Destructuring assignment of SASS list - sass

I'm trying to destructuring list of values in SCSS in this way, which works, but I want to get leaner way to do that.
#function update-list($list) {
$name: nth($list, 1);
$dur: nth($list, 2);
$func: nth($list, 3);
#debug "list values: #{$name} #{$dur} #{$func}";
#return $name $dur $func;
}
So, I want to replace this part:
$name: nth($list, 1);
$dur: nth($list, 2);
$func: nth($list, 3);
with something like JS do:
[a, b, c] = [10, 20, 30];
UPD:
I have space-separated list $list witch transmitted to function update-list.
Also, I've added debug results of this list:
$list: prop1 prop2 prop3;
#debug "list data: #{type-of($list)} #{length($list)} #{list-separator($list)}";
// DEBUG: list data: list 3 space
$list-upd: update-list($list);

You can't destructure like in JS, however you can spread the list params you pass to your function:
#function update-list($name, $dur, $func) {
#debug "list values: #{$name} #{$dur} #{$func}";
#return $name $dur $func;
}
$list: prop1 prop2 prop3;
$list-upd: update-list($list...);

Related

Getting module from string parameter

I'm trying to get a module name from a string. Here is an example of what I imagine it would look like:
#use 'dark'; /* _dark.scss, with a $color variable */
#use 'light'; /* _light.scss, with a $color variable */
#mixin theme($name) {
body {
color: #{$name}.$color;
}
}
If I passed in "dark" as an argument, I would want this result:
...
color: dark.$color;
...
If I passed in "light" as an argument, I would want this result:
...
color: light.$color;
...

How to remove whitespace in SASS string?

Is there a string function that remove whitespaces in a string in SASS?
For an instance, I'd like to use a variable (string with spaces) to specify a resource image file (name without spaces).
Something like:
$str-var: "The White Lion";
#mixin bg-img($name) {
background-image: url("#{$name}.jpg");
}
.image-cover {
#include bg-img(str-remove-whitespace($str-var));
}
Expected result:
.image-cover {
background-image: url("TheWhiteLion.jpg");
}
There is no such built-in function, but it can be implemented by searching for spaces into string and cutting them out. Something like this should work:
#function str-remove-whitespace($str) {
#while (str-index($str, ' ') != null) {
$index: str-index($str, ' ');
$str: "#{str-slice($str, 0, $index - 1)}#{str-slice($str, $index + 1)}";
}
#return $str;
}
List of available functions you can see into SASS documentation.

Isolate nested sass map into new map

How can I isolate nested sass map into a new map? For example I have sass map like this:
$susy-setting: (
s: (
'columns': 4,
'gutters': 30px,
),
m: (
'columns': 8,
'gutters': 30px,
),
l: (
'columns': 12,
'gutters': 30px,
)
);
Then I need to isolate each map inside after the loop, because my other mixin need map for its parameter.
#each $setting in $susy-setting{
#include susy-settings-block($setting) { // This mixin need map
#for $i from 1 through map-get($setting, 'columns') {
#content;
}
}
}
To answer your primary question, you'll need to get both the key and value in your loop: #each $size, $setting in $susy-setting. The $size variable will store the key (e.g. s, m, l) while the $setting variable stores the map value assigned to that key.
EDIT: I saw your comment on my gist, which provides a bit more context. Try this:
#mixin susy-settings-block(
$name,
$config: $susy-settings
) {
// store the old settings
$global: $susy;
// Get the new settings by name
$new: map-get($config, $name);
// apply the new settings
$susy: map-merge($susy, $new) !global;
// allow nested styles, using new settings
#content;
// return to the initial settings
$susy: $global !global;
}

Sass won't compile when using Libsass (argument must not be empty)

So I'm using some pretty complicated Sass to remove every other selector from "&"
.test, .test-2, .test-3, .test-4 {
$selectors: (&);
#if length(&) != 1 {
$selectors: (); // Works only with Ruby Sass
$i: 1;
#each $item in (&) {
$i: $i + 1;
#if $i % 2 == 0 {
$i: 0;
}#else{
$selectors: append($selectors, $item);
}
}
$selectors: to-string($selectors, ", ");
content: "#{$selectors}";
}
}
My expected result for the content attribute is:
content: ".test-2, .test-4";
When using Ruby Sass, this is precisely what I get. When using Libsass, I get this error:
argument `$list` of `nth($list, $n)` must not be empty
This error is referring to code within the custom "to-string" function I'm using:
#function to-string($list, $glue: '', $is-nested: false, $recursive: false) {
$result: null;
#for $i from 1 through length($list) {
$e: nth($list, $i);
#if type-of($e) == list and $recursive {
$result: $result#{to-string($e, $glue, true)};
}
#else {
$result: if(
$i != length($list) or $is-nested,
$result#{$e}#{$glue}, $result#{$e}
);
}
}
#return $result;
}
More specifically, this line:
$e: nth($list, $i);
It appears that the value I am passing to the to-string function (which is the $selectors variable) is empty, when it shouldn't be. I initially define it as being empty ($selectors: ();), but then I append every other item from &. So I'm unsure why it's empty at this point.
Here is a Sassmesiter demonstrating the issue: http://sassmeister.com/gist/332dae9a27983edd9d15
Change the compiler in the above demo to Libsass to see it error.
I'm unsure if this is an issue with my code or with Libsass. I don't want to open an issue on Libsass unless I know for sure it is a Libsass problem.
Thanks
Ok I got to the bottom of this. Fairly certain it's a Libsass issue.
It looks like you can't loop through each item in & directly, but if I store & in a variable, it proceeds to work.
So to clarify, changing this:
#each $item in & {
...
}
to this:
$this: &;
#each $item in $this {
...
}
Solves the issue.

SASS Index in nested list

I'm trying to get the index of an item in a nested SASS list — by the first property. But the only way i can get a result is to include both properties in the item.
Is is doable with native SASS, or would it require a mixin/function? And any input to how i would do that?
The code i got:
$icons : (
'arrow--down--full' '\e806', /* '' */
'cog' '\e805', /* '' */
'info' '\e807', /* '' */
'arrow--down' '\e800', /* '' */
'arrow--left' '\e801', /* '' */
'arrow--right' '\e802', /* '' */
'arrow--up' '\e803', /* '' */
'close' '\e804', /* '' */
'search' '\e804', /* '' */
'spin' '\e809' /* '' */
);
And my lookup
//Working
index($icons, 'search' '\e804');
//Not working, but what i want to achieve
index($icons, 'search');
It sounds like what you're talking about is a hash or lookup table, which Sass does not currently have. However, you can easily work around that in a variety of ways. Here are some examples.
You could structure your list a little differently:
$icons : (
'arrow--down--full', '\e806', /* '' */
'cog', '\e805', /* '' */
'info', '\e807', /* '' */
...
);
I've added a comma after each item. Now to look it up you'd write a function like
#function lookup($list, $key) {
#return nth( $list, ( ( index($list, $key) ) + 1) );
}
And call it like so
lookup($icons, 'cog'); // => '\e805'
You could push this a little further by making 2 different lists and then associating them via a similar function:
$icon-keys: ('arrow--down--full', 'cog', 'info' ... );
$icon-values: ('\e806', '\e805', '\e807' ... );
I've lined up the values with whitespace only to make them more legible to me so that they appear a bit like an actual table, but there are tons of ways of writing Sass lists, and you may prefer another. Then the function that associates them:
#function lookup($lookup-key, $all-keys, $all-values) {
#return nth($all-values, index($all-keys, $lookup-key));
}
And using it:
lookup('cog', $icon-keys, $icon-values); // => '\e805'
For my tastes, these are both a bit clunky so I'd make a shortcut function to make it a bit more legible:
for the first variation:
#function icons($lookup-key) {
#return lookup($icons, $lookup-key);
}
for the second:
#function icons($lookup-key, $types: $icon-keys, $values: $icon-values) {
#return lookup($lookup-key, $types, $values);
}
so you could just in either case call
icons('cog');
You'd probably want to put a little more logic in your lookup functions to error catch, and you could also expand it to both accept and return a list rather than a single value, but this is just a basic example.
#cimmanon answered this: https://stackoverflow.com/a/17004655/786123
#function find-value($list, $key) {
#each $item in $list {
#if ($key == nth($item, 1)) {
#return nth($items, 2);
}
}
#return false;
}

Resources