From 8942c24908a972b308db4471f291edb829849850 Mon Sep 17 00:00:00 2001 From: Awjin Ahn Date: Wed, 7 Oct 2020 17:05:20 -0500 Subject: [PATCH] Nested maps documentations (#484) Closes #476 --- source/documentation/modules/map.html.md.erb | 385 +++++++++++++++++-- source/documentation/values/maps.html.md.erb | 71 ++-- 2 files changed, 379 insertions(+), 77 deletions(-) diff --git a/source/documentation/modules/map.html.md.erb b/source/documentation/modules/map.html.md.erb index d40c921bd..eca404c83 100644 --- a/source/documentation/modules/map.html.md.erb +++ b/source/documentation/modules/map.html.md.erb @@ -4,21 +4,194 @@ title: sass:map <%= partial '../snippets/built-in-module-status' %> -<% function 'map.get($map, $key)', 'map-get($map, $key)' do %> - Returns the value in `$map` associated with `$key`. +<% fun_fact do %> + Sass libraries and design systems tend to share and override configurations + that are represented as nested maps (maps that contain maps that contain + maps). - If `$map` doesn't have a value associated with `$key`, returns [`null`][]. + To help you work with nested maps, some map functions support deep + operations. For example, if you pass multiple keys to `map.get()`, it will + follow those keys to find the desired nested map: - [`null`]: ../values/null + <% example(autogen_css: false) do %> + $config: (a: (b: (c: d))); + @debug map.get($config, a, b, c)); // d + === + $config: (a: (b: (c: d))) + @debug map.get($config, a, b, c)) // d + <% end %> +<% end %> + + +<% function 'map.deep-merge($map1, $map2)', returns: 'map' do %> + Identical to [`map.merge()`](#merge), except that nested map values are *also* + recursively merged. + + <% example(autogen_css: false) do %> + $helvetica-light: ( + "weights": ( + "lightest": 100, + "light": 300 + ) + ); + $helvetica-heavy: ( + "weights": ( + "medium": 500, + "bold": 700 + ) + ); + + @debug map.deep-merge($helvetica-light, $helvetica-heavy); + // ( + // "weights": ( + // "lightest": 100, + // "light": 300, + // "medium": 500, + // "bold": 700 + // ) + // ) + @debug map.merge($helvetica-light, $helvetica-heavy); + // ( + // "weights": ( + // "medium: 500, + // "bold": 700 + // ) + // ) + === + $helvetica-light: ("weights": ("lightest": 100, "light": 300)) + $helvetica-heavy: ("weights": ("medium": 500, "bold": 700)) + + @debug map.deep-merge($helvetica-light, $helvetica-heavy) + // ( + // "weights": ( + // "lightest": 100, + // "light": 300, + // "medium": 500, + // "bold": 700 + // ) + // ) + @debug map.merge($helvetica-light, $helvetica-heavy); + // ( + // "weights": ( + // "medium: 500, + // "bold": 700 + // ) + // ) + <% end %> +<% end %> + + +<% function 'map.deep-remove($map, $key, $keys...)', + returns: 'map' do %> + If `$keys` is empty, returns a copy of `$map` without a value associated with + `$key`. + + <% example(autogen_css: false) do %> + $font-weights: ("regular": 400, "medium": 500, "bold": 700); + + @debug map.deep-remove($font-weights, "regular"); + // ("medium": 500, "bold": 700) + === + $font-weights: ("regular": 400, "medium": 500, "bold": 700) + + @debug map.deep-remove($font-weights, "regular") + // ("medium": 500, "bold": 700) + <% end %> + + --- + + If `$keys` is not empty, follows the set of keys including `$key` and + excluding the last key in `$keys`, from left to right, to find the nested map + targeted for updating. + + Returns a copy of `$map` where the targeted map does not have a value + associated with the last key in `$keys`. + + <% example(autogen_css: false) do %> + $fonts: ( + "Helvetica": ( + "weights": ( + "regular": 400, + "medium": 500, + "bold": 700 + ) + ) + ); + + @debug map.deep-remove($fonts, "Helvetica", "weights", "regular"); + // ( + // "Helvetica": ( + // "weights: ( + // "medium": 500, + // "bold": 700 + // ) + // ) + // ) + === + $fonts: ("Helvetica": ("weights": ("regular": 400, "medium": 500, "bold": 700))) + + @debug map.deep-remove($fonts, "Helvetica", "weights", "regular") + // ( + // "Helvetica": ( + // "weights: ( + // "medium": 500, + // "bold": 700 + // ) + // ) + // ) + <% end %> +<% end %> + + +<% function 'map.get($map, $key, $keys...)', + 'map-get($map, $key, $keys...)' do %> + If `$keys` is empty, returns the value in `$map` associated with `$key`. + + If `$map` doesn't have a value associated with `$key`, returns [`null`][]. <%= partial 'code-snippets/example-map-get' %> + + --- + + If `$keys` is not empty, follows the set of keys including `$key` and + excluding the last key in `$keys`, from left to right, to find the nested map + targeted for searching. + + Returns the value in the targeted map associated with the last key in `$keys`. + + Returns [`null`][] if the map does not have a value associated with the key, + or if any key in `$keys` is missing from a map or references a value that is + not a map. + + <% example(autogen_css: false) do %> + $fonts: ( + "Helvetica": ( + "weights": ( + "regular": 400, + "medium": 500, + "bold": 700 + ) + ) + ); + + @debug map.get($fonts, "Helvetica", "weights", "regular"); // 400 + @debug map.get($fonts, "Helvetica", "colors"); // null + === + $fonts: ("Helvetica": ("weights": ("regular": 400, "medium": 500, "bold": 700))) + + @debug map.get($fonts, "Helvetica", "weights", "regular") // 400 + @debug map.get($fonts, "Helvetica", "colors") // null + <% end %> + + [`null`]: ../values/null <% end %> -<% function 'map.has-key($map, $key)', - 'map-has-key($map, $key)', +<% function 'map.has-key($map, $key, $keys...)', + 'map-has-key($map, $key, $keys...)', returns: 'boolean' do %> - Returns whether `$map` contains a value associated with `$key`. + If `$keys` is empty, returns whether `$map` contains a value associated with + `$key`. <% example(autogen_css: false) do %> $font-weights: ("regular": 400, "medium": 500, "bold": 700); @@ -28,8 +201,40 @@ title: sass:map === $font-weights: ("regular": 400, "medium": 500, "bold": 700) - @debug map.has-key($font-weights, "regular") // true - @debug map.has-key($font-weights, "bolder") // false + @debug map.has-key($font-weights, "regular") // true + @debug map.has-key($font-weights, "bolder") // false + <% end %> + + --- + + If `$keys` is not empty, follows the set of keys including `$key` and + excluding the last key in `$keys`, from left to right, to find the nested map + targeted for searching. + + Returns true if the targeted map contains a value associated with the last key + in `$keys`. + + Returns false if it does not, or if any key in `$keys` is missing from a map + or references a value that is not a map. + + <% example(autogen_css: false) do %> + $fonts: ( + "Helvetica": ( + "weights": ( + "regular": 400, + "medium": 500, + "bold": 700 + ) + ) + ); + + @debug map.has-key($fonts, "Helvetica", "weights", "regular"); // true + @debug map.has-key($fonts, "Helvetica", "colors"); // false + === + $fonts: ("Helvetica": ("weights": ("regular": 400, "medium": 500, "bold": 700))) + + @debug map.has-key($fonts, "Helvetica", "weights", "regular") // true + @debug map.has-key($fonts, "Helvetica", "colors") // false <% end %> <% end %> @@ -51,55 +256,84 @@ title: sass:map <% function 'map.merge($map1, $map2)', 'map-merge($map1, $map2)', + 'map.merge($map1, $keys..., $map2)', + 'map-merge($map1, $keys..., $map2)', returns: 'map' do %> - Returns a new map with all the keys and values from both `$map1` and `$map2`. + <% heads_up do %> + In practice, the actual arguments to `map.merge($map1, $keys..., $map2)` + are passed as `map.set($map1, $args...). They are described here as `$map1, + $keys..., $map2` for explanation purposes only. + <% end %> - This can also be used to add a new value or overrwrite a value in `$map1`, by - passing a single key/value pair as `$map2`. + If no `$keys` are passed, returns a new map with all the keys and values from + both `$map1` and `$map2`. - If both `$map1` and `$map2` have the same key, `$map2`'s value takes precedence. + If both `$map1` and `$map2` have the same key, `$map2`'s value takes + precedence. - All keys in the returned map that also appear in `$map1` have the same order as - in `$map1`. New keys from `$map2` appear at the end of the map. + All keys in the returned map that also appear in `$map1` have the same order + as in `$map1`. New keys from `$map2` appear at the end of the map. <% example(autogen_css: false) do %> $light-weights: ("lightest": 100, "light": 300); $heavy-weights: ("medium": 500, "bold": 700); @debug map.merge($light-weights, $heavy-weights); - // ( - // "lightest": 100, - // "light": 300, - // "medium": 500, - // "bold": 700 - // ) - - // map.merge() can be used to add a single key/value pair to a map. - @debug map.merge($light-weights, ("lighter": 200)); - // ("lightest": 100, "light": 300, "lighter": 200) - - // It can also be used to overwrite a value in a map. - @debug map.merge($light-weights, ("light": 200)); - // ("lightest": 100, "light": 200) + // ("lightest": 100, "light": 300, "medium": 500, "bold": 700) === $light-weights: ("lightest": 100, "light": 300) $heavy-weights: ("medium": 500, "bold": 700) @debug map.merge($light-weights, $heavy-weights) + // ("lightest": 100, "light": 300, "medium": 500, "bold": 700) + <% end %> + + --- + + If `$keys` is not empty, follows the `$keys` to find the nested map targeted + for merging. If any key in `$keys` is missing from a map or references a value + that is not a map, sets the value at that key to an empty map. + + Returns a copy of `$map1` where the targeted map is replaced by a new map that + contains all the keys and values from both the targeted map and `$map2`. + + <% example(autogen_css: false) do %> + $fonts: ( + "Helvetica": ( + "weights": ( + "lightest": 100, + "light": 300 + ) + ) + ); + $heavy-weights: ("medium": 500, "bold": 700); + + @debug map.merge($fonts, "Helvetica", "weights", $heavy-weights); // ( - // "lightest": 100, - // "light": 300, - // "medium": 500, - // "bold": 700 + // "Helvetica": ( + // "weights": ( + // "lightest": 100, + // "light": 300, + // "medium": 500, + // "bold": 700 + // ) + // ) // ) + === + $fonts: ("Helvetica": ("weights": ("lightest": 100, "light": 300))) + $heavy-weights: ("medium": 500, "bold": 700) - // map.merge() can be used to add a single key/value pair to a map. - @debug map.merge($light-weights, ("lighter": 200)) - // ("lightest": 100, "light": 300, "lighter": 200) - - // It can also be used to overwrite a value in a map. - @debug map.merge($light-weights, ("light": 200)) - // ("lightest": 100, "light": 200) + @debug map.merge($fonts, "Helvetica", "weights", $heavy-weights) + // ( + // "Helvetica": ( + // "weights": ( + // "lightest": 100, + // "light": 300, + // "medium": 500, + // "bold": 700 + // ) + // ) + // ) <% end %> <% end %> @@ -129,6 +363,77 @@ title: sass:map <% end %> +<% function 'map.set($map, $key, $value)', + 'map.set($map, $keys..., $key, $value)', + returns: 'map' do %> + <% heads_up do %> + In practice, the actual arguments to `map.set($map, $keys..., $key, $value)` + are passed as `map.set($map, $args...). They are described here as `$map, + $keys..., $key, $value` for explanation purposes only. + <% end %> + + If `$keys` are not passed, returns a copy of `$map` with the value at `$key` + set to `$value`. + + <% example(autogen_css: false) do %> + $font-weights: ("regular": 400, "medium": 500, "bold": 700); + + @debug map.set($font-weights, "regular", 300); + // ("regular": 300, "medium": 500, "bold": 700) + === + $font-weights: ("regular": 400, "medium": 500, "bold": 700) + + @debug map.set($font-weights, "regular", 300) + // ("regular": 300, "medium": 500, "bold": 700) + <% end %> + + --- + + If `$keys` are passed, follows the `$keys` to find the nested map targeted for + updating. If any key in `$keys` is missing from a map or references a value + that is not a map, sets the value at that key to an empty map. + + Returns a copy of `$map` with the targeted map's value at `$key` set to + `$value`. + + <% example(autogen_css: false) do %> + $fonts: ( + "Helvetica": ( + "weights": ( + "regular": 400, + "medium": 500, + "bold": 700 + ) + ) + ); + + @debug map.set($fonts, "Helvetica", "weights", "regular", 300); + // ( + // "Helvetica": ( + // "weights": ( + // "regular": 300, + // "medium": 500, + // "bold": 700 + // ) + // ) + // ) + === + $fonts: ("Helvetica": ("weights": ("regular": 400, "medium": 500, "bold": 700))) + + @debug map.set($fonts, "Helvetica", "weights", "regular", 300) + // ( + // "Helvetica": ( + // "weights": ( + // "regular": 300, + // "medium": 500, + // "bold": 700 + // ) + // ) + // ) + <% end %> +<% end %> + + <% function 'map.values($map)', 'map-values($map)', returns: 'list' do %> Returns a comma-separated list of all the values in `$map`. diff --git a/source/documentation/values/maps.html.md.erb b/source/documentation/values/maps.html.md.erb index 0b6a3ac9f..7d089f941 100644 --- a/source/documentation/values/maps.html.md.erb +++ b/source/documentation/values/maps.html.md.erb @@ -68,55 +68,52 @@ easily be accessed in the block. ### Add to a Map It's also useful to add new pairs to a map, or to replace the value for an -existing key. The [`map.merge($map1, $map2)` function][] does this: it returns a -new map that contains all the key/value pairs in *both* arguments. +existing key. The [`map.set($map, $key, $value)` function][] does this: it +returns a copy of `$map` with the value at `$key` set to `$value`. -[`map.merge($map1, $map2)` function]: ../modules/map#merge +[`map.set($map, $key, $value)` function]: ../modules/map#set <% example(autogen_css: false) do %> @use "sass:map"; - $light-weights: ("lightest": 100, "light": 300); - $heavy-weights: ("medium": 500, "bold": 700); + $font-weights: ("regular": 400, "medium": 500, "bold": 700); - @debug map.merge($light-weights, $heavy-weights); - // ( - // "lightest": 100, - // "light": 300, - // "medium": 500, - // "bold": 700 - // ) + @debug map.set($font-weights, "extra-bold", 900); + // ("regular": 400, "medium": 500, "bold": 700, "extra-bold": 900) + @debug map.set($font-weights, "bold", 900); + // ("regular": 400, "medium": 500, "bold": 900) === @use "sass:map" - $light-weights: ("lightest": 100, "light": 300) - $heavy-weights: ("medium": 500, "bold": 700) + $font-weights: ("regular": 400, "medium": 500, "bold": 700) - @debug map.merge($light-weights, $heavy-weights) - // ( - // "lightest": 100, - // "light": 300, - // "medium": 500, - // "bold": 700 - // ) + @debug map.set($font-weights, "extra-bold": 900) + // ("regular": 400, "medium": 500, "bold": 700, "extra-bold": 900) + @debug map.set($font-weights, "bold", 900) + // ("regular": 400, "medium": 500, "bold": 900) <% end %> -`map.merge()` is often used with an inline map to add a single key/value pair. +Instead of setting values one-by-one, you can also merge two existing maps using +[`map.merge($map1, $map2)`][]. + +[`map.merge($map1, $map2)` function]: ../modules/map#merge <% example(autogen_css: false) do %> @use "sass:map"; - $font-weights: ("regular": 400, "medium": 500, "bold": 700); + $light-weights: ("lightest": 100, "light": 300); + $heavy-weights: ("medium": 500, "bold": 700); - @debug map.merge($font-weights, ("extra-bold": 900)); - // ("regular": 400, "medium": 500, "bold": 700, "extra-bold": 900) + @debug map.merge($light-weights, $heavy-weights); + // ("lightest": 100, "light": 300, "medium": 500, "bold": 700) === @use "sass:map" - $font-weights: ("regular": 400, "medium": 500, "bold": 700) + $light-weights: ("lightest": 100, "light": 300) + $heavy-weights: ("medium": 500, "bold": 700) - @debug map.merge($font-weights, ("extra-bold": 900)) - // ("regular": 400, "medium": 500, "bold": 700, "extra-bold": 900) + @debug map.merge($light-weights, $heavy-weights) + // ("lightest": 100, "light": 300, "medium": 500, "bold": 700) <% end %> If both maps have the same keys, the second map's values are used in the map @@ -125,21 +122,21 @@ that gets returned. <% example(autogen_css: false) do %> @use "sass:map"; - $font-weights: ("regular": 400, "medium": 500, "bold": 700); + $weights: ("light": 300, "medium": 500); - @debug map.merge($font-weights, ("medium": 600)); - // ("regular": 400, "medium": 600, "bold": 700) + @debug map.merge($weights, ("medium": 700)); + // ("light": 300, "medium": 700) === - @use "sass:map" + @use "sass:map"; - $font-weights: ("regular": 400, "medium": 500, "bold": 700) + $weights: ("light": 300, "medium": 500) - @debug map.merge($font-weights, ("medium": 600)) - // ("regular": 400, "medium": 600, "bold": 700) + @debug map.merge($weights, ("medium": 700)) + // ("light": 300, "medium": 700) <% end %> -Note that because Sass maps are [immutable][], `map.merge()` doesn't modify the -original list. +Note that because Sass maps are [immutable][], `map.set()` and `map.merge()` do +not modify the original list. [immutable]: #immutability