Статьи

Дополнительные функции карты в Sass

Sass карты потрясающие. Они делают много вещей, которые раньше были невозможны, ну, возможно. Например, у них есть умный способ определения динамических переменных. Они также являются идеальным контейнером для сложной конфигурации, для каркаса или системы сетки.

К счастью для нас, Sass предоставляет несколько функций для управления картами. Тем не менее, если вы продолжите работать с Sass, у вас может возникнуть крайний случай, который не охватывается встроенным API.

Возможно, вам понадобится способ получить ключ, который вкладывается в несколько карт? Что делать, если вы хотите обновить этот ключ? Или расширить вложенные карты, как $.extend из jQuery? Несколько проблем тут и там, которые можно легко исправить с помощью функций, которые мы будем проверять сегодня.

Карта Deep Get

Когда вам нужно получить доступ к ключу, принадлежащему карте, которая оказывается вложенной в другую карту (или несколько), вы больше не можете полагаться на map-get . По крайней мере, не так, как сейчас.

 /// Fetch nested keys /// @param {Map} $map - Map /// @param {Arglist} $keys - Keys to fetch /// @return {*} @function map-deep-get($map, $keys...) { @each $key in $keys { $map: map-get($map, $key); } @return $map; } 

пример

 $grid-configuration: ( 'columns': 12, 'layouts': ( 'small': 800px, 'medium': 1000px, 'large': 1200px, ), ); // Without `map-deep-get` $medium: map-get(map-get($grid-configuration, 'layouts'), 'medium'); // With `map-deep-get` $medium: map-deep-get($grid-configuration, 'layouts', 'medium'); 

Карта Deep Set

Как видите, создание функции для получения ключа из вложенных карт не слишком сложно для написания. С другой стороны, функцию для установки значения в конце цепочки ключей не так просто создать … Из-за сложности мы не будем слишком углубляться в код для этого.

Тем не менее, мы внимательно рассмотрим подпись, потому что я немного схожу с ума от этого. Чтобы наша функция работала, нам нужно 3 вещи:

  • карта;
  • цепочка ключей, которая может содержать от одного ключа до базиллиона;
  • новое значение для последнего ключа из цепочки ключей.

Мы видели в предыдущей статье, почему лучше использовать arglist при работе с неизвестным количеством аргументов, что в точности соответствует тому, что мы имеем здесь с $keys . Проблема в том, что после arglist не может быть лишних аргументов, поэтому чтобы исправить это, у нас есть два способа написания нашей функции.

Либо мы можем поставить новое значение, затем цепочку ключей, но иметь новое значение перед тем, как обновляется, выглядит немного странно для меня.

 @function map-deep-set($map, $value, $keys...) {} 

Или у нас есть только два фактических аргумента, и мы считаем последнее значение из $keys нашим новым значением.

 @function map-deep-set($map, $keys.../*, $value */) {} 

Это то, что я решил пойти с.

  

пример

 $grid-configuration: ( 'columns': 12, 'layouts': ( 'small': 800px, 'medium': 1000px, 'large': 1200px, ), ); // Without `map-deep-set` $grid-configuration: map-merge($grid-configuration, map-merge(map-get($grid-configuration, 'layouts'), ('large': 1300px))); // With `map-deep-set` $medium: map-deep-set($grid-configuration, 'layouts', 'medium', 1300px); 

Глубина карты

В статье об отладке карты Sass мы увидели, как можно построить функцию для извлечения максимальной глубины карты Sass. Позвольте мне опубликовать это здесь снова для потомков:

 /// Compute the maximum depth of a map /// @param {Map} $map /// @return {Number} max depth of `$map` @function map-depth($map) { $level: 1; @each $key, $value in $map { @if type-of($value) == "map" { $level: max(map-depth($value) + 1, $level); } } @return $level; } 

пример

 $grid-configuration: ( 'columns': 12, 'layouts': ( 'small': 800px, 'medium': 1000px, 'large': 1200px, ), ); // Maximum depth $depth: map-depth($grid-configuration); // -> 2 

Карта имеет ключи

Sass предоставляет функцию, чтобы проверить, есть ли у карты данный ключ. Мы могли бы расширить эту функцию, чтобы проверить, есть ли у Sass несколько клавиш, что вы скажете? Это на самом деле очень легко сделать, как вы можете видеть.

  

пример

 $grid-configuration: ( 'columns': 12, 'layouts': ( 'small': 800px, 'medium': 1000px, 'large': 1200px, ), ); $depth: map-has-keys($grid-configuration, 'columns', 'layouts'); // -> true $depth: map-has-keys($grid-configuration, 'columns', 'options'); // -> false 

Карта имеет вложенные ключи

В том же map-has-key функция map-has-key по умолчанию map-has-key может быть использована при работе с вложенными картами, так как она углубляется только на один уровень. Давайте закатать рукава, чтобы построить глубокий map-has-key .

  

пример

 $grid-configuration: ( 'columns': 12, 'layouts': ( 'small': 800px, 'medium': 1000px, 'large': 1200px, ), ); $depth: map-has-keys($grid-configuration, 'layouts', 'medium'); // -> true $depth: map-has-keys($grid-configuration, 'layouts', 'huge'); // -> false 

Карта Zip

Это то, что я хотел сделать более одного раза, не имея встроенного способа сделать это: объединение двух списков для построения карты, где первый список будет служить ключами, а второй список будет служить значениями.

В случае, если оба списка не имеют одинаковую длину, дополнительные значения будут отброшены, и функция выдаст предупреждение, чтобы объяснить, что происходит.

 /// An equivalent of `zip` function but for maps. /// Takes two lists, the first for keys, second for values. /// @param {List} $keys - Keys for map /// @param {List} $values - Values for map /// @return {Map} Freshly created map /// @see http://sass-lang.com/documentation/Sass/Script/Functions.html#zip-instance_method @function map-zip($keys, $values) { $l-keys: length($keys); $l-values: length($values); $min: min($l-keys, $l-values); $map: (); @if $l-keys != $l-values { @warn "There are #{$l-keys} key(s) for #{$l-values} value(s) in the map for `map-zip`. " + "Resulting map will only have #{$min} pairs."; } @if $min == 0 { @return $map; } @for $i from 1 through $min { $map: map-merge($map, (nth($keys, $i): nth($values, $i))); } @return $map; } 

пример

 $layout-names: 'small', 'medium', 'large', 'huge'; $layout-values: 600px, 900px, 1200px, 1500px; $breakpoints: map-zip($layout-names, $layout-values); // -> ('small': 600px, 'medium': 900px, 'large': 1200px, 'huge': 1500px) 

Карта Расширить

Возможно, вы знаете, что map-merge имеет две цели: добавить ключ к карте и объединить две карты вместе. На сегодняшний день я вижу, что в map-merge возможно два недостатка:

  • он не рекурсивный, то есть работает с вложенными картами;
  • это займет всего две карты, не более.

Конечно, мы можем сделать лучше, верно? Позвольте мне представить функцию map-extend , ala jQuery . Что касается map-deep-set , мы используем хак с сигнатурой, чтобы в качестве последнего значения из нашей цепочки $maps указан параметр $deep .

 /// jQuery-style extend function /// About `map-merge()`: /// * only takes 2 arguments /// * is not recursive /// @param {Map} $map - first map /// @param {ArgList} $maps - other maps /// @param {Bool} $deep - recursive mode /// @return {Map} @function map-extend($map, $maps.../*, $deep */) { $last: nth($maps, -1); $deep: $last == true; $max: if($deep, length($maps) - 1, length($maps)); // Loop through all maps in $maps... @for $i from 1 through $max { // Store current map $current: nth($maps, $i); // If not in deep mode, simply merge current map with map @if not $deep { $map: map-merge($map, $current); } @else { // If in deep mode, loop through all tuples in current map @each $key, $value in $current { // If value is a nested map and same key from map is a nested map as well @if type-of($value) == "map" and type-of(map-get($map, $key)) == "map" { // Recursive extend $value: map-extend(map-get($map, $key), $value, true); } // Merge current tuple with map $map: map-merge($map, ($key: $value)); } } } @return $map; } 

пример

 $grid-configuration-default: ( 'columns': 12, 'layouts': ( 'small': 800px, 'medium': 1000px, 'large': 1200px, ), ); $grid-configuration-custom: ( 'layouts': ( 'large': 1300px, 'huge': 1500px ), ); $grid-configuration-user: ( 'direction': 'ltr', 'columns': 16, 'layouts': ( 'large': 1300px, 'huge': 1500px ), ); // Not deep $grid-configuration: map-extend($grid-configuration-default, $grid-configuration-custom, $grid-configuration-user); // -> ("columns": 16, "layouts": (("large": 1300px, "huge": 1500px)), "direction": "ltr") // Deep $grid-configuration: map-extend($grid-configuration-default, $grid-configuration-custom, $grid-configuration-user, true); // -> ("columns": 16, "layouts": (("small": 800px, "medium": 1000px, "large": 1300px, "huge": 1500px)), "direction": "ltr") 

Последние мысли

Вот и все, ребята! Очевидно, вы не будете использовать их в своей повседневной таблице стилей, но время от времени такие функции могут пригодиться. По крайней мере, вы будете знать, где их найти. 😉