aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/_props.scss454
1 files changed, 62 insertions, 392 deletions
diff --git a/src/_props.scss b/src/_props.scss
index 14295bd..c80c18b 100644
--- a/src/_props.scss
+++ b/src/_props.scss
@@ -1,434 +1,104 @@
1////
2/// Property trees.
3///
4/// Property trees allow you to organize properties in a tree structure (internally nested maps).
5/// The intended use is to store all your properties at the beginning and for the rest of the
6/// stylesheet you just get them.
7///
8/// @group Property trees
9///
10/// @access public
11////
12
13@use 'sass:map';
14@use 'sass:list'; 1@use 'sass:list';
15@use 'sass:string'; 2@use 'sass:map';
16@use 'sass:meta'; 3@use 'sass:meta';
17@use './functions'; 4@use 'functions';
18@use './contexts';
19
20///
21/// The maximum depth of resolved iro-prop-ref() references.
22///
23/// @type number
24///
25$native-assign-max-depth: 2 !default;
26
27///
28/// Indicate if property names must start with two dashes (--).
29/// This is required if property trees are also used for native CSS custom properties.
30///
31/// @type bool
32///
33$enforce-double-dashes: true !default;
34
35///
36/// Default tree name to use if no name is specified.
37///
38/// @type string
39///
40$default-tree: 'default' !default;
41
42///
43/// List of all created property trees.
44///
45/// @type list
46///
47/// @access private
48///
49$trees: ();
50
51///
52/// Default context name used for the namespace context.
53///
54/// @type string
55///
56$namespace-context-id: 'namespace' !default;
57
58///
59/// Declare a namespace, meaning that all variables declared and accessed.
60///
61/// @param {string} $name - Name of the namespace
62///
63@mixin namespace($name) {
64 $key: '--#{$name}';
65
66 $ns-key: get-ns-key();
67
68 @if $ns-key != null {
69 $key: list.append($ns-key, $key);
70 } @else {
71 $key: ($key);
72 }
73
74 @include contexts.push($namespace-context-id, 'namespace', (
75 'name': $name,
76 'key': $key
77 ));
78
79 @content;
80
81 @include contexts.pop($namespace-context-id);
82}
83
84///
85/// Get the current namespace name.
86///
87@function namespace() {
88 $noop: contexts.assert-stack-must-contain($namespace-context-id, 'namespace');
89
90 $data: list.nth(contexts.get($namespace-context-id, 'namespace'), 2);
91 $name: map.get($data, 'name');
92
93 @return $name;
94}
95
96///
97/// Save a property tree. If a tree with the sane name already exists, the trees
98/// will be merged.
99///
100/// @param {map} $map - Map containing properties
101/// @param {string} $tree [$default-tree] - ID the map is saved as
102/// @param {bool} $merge [true] - If a tree named $tree already exists and this value is set to true, they will be merged. Otherwise an error will be emitted.
103///
104@mixin store($map, $tree: $default-tree, $merge: true, $global: false) {
105 $noop: store($map, $tree, $merge, $global);
106}
107
108///
109/// Save a property tree.
110///
111/// @param {map} $map - Map containing properties
112/// @param {string} $tree [$default-tree] - ID the map is saved as
113/// @param {bool} $merge [true] - If a tree named $tree already exists and this value is set to true, they will be merged. Otherwise an error will be emitted.
114///
115@function store($map, $tree: $default-tree, $merge: true, $global: false) {
116 $prop-map: null;
117 5
118 @if $enforce-double-dashes { 6@function is-prop-ref($value) {
119 @if not validate($map) { 7 @if meta.type-of($value) != 'list' {
120 @error 'Property tree keys must start with two dashes (--). If you don\'t use property trees for native CSS custom properties, set $enforce-double-dashes to false.'; 8 @return false;
121 }
122 } 9 }
123 10 @if list.length($value) != 4 {
124 @if not $global { 11 @return false;
125 $ns-key: get-ns-key();
126
127 @if $ns-key != null {
128 $map: ($ns-key: $map);
129 }
130 } 12 }
131 13 @if list.nth($value, 1) != 'prop-ref' {
132 @if map.has-key($trees, $tree) { 14 @return false;
133 @if $merge {
134 $map: map.deep-merge(map.get($trees, $tree), $map);
135 } @else {
136 @error 'Property tree #{inspect($tree)} does already exist.';
137 }
138 } 15 }
139 16 @return true;
140 $trees: map.merge($trees, ($tree: $map)) !global;
141
142 @return null;
143} 17}
144 18
145/// 19@function def($name, $value: (), $metadata: null) {
146/// Delete a property tree. 20 @return ('prop-ref' $name $value $metadata);
147///
148/// @param {string} $tree [$default-tree] - ID of the tree to be deleted
149///
150@mixin clear($tree: $default-tree) {
151 $noop: clear($tree);
152} 21}
153 22
154/// 23@function merge($ref, $value) {
155/// Delete a property tree. 24 @if not is-prop-ref($ref) {
156/// 25 @return $ref;
157/// @param {string} $tree [$default-tree] - ID of the tree to be deleted
158///
159/// @throw If the property tree does not exist
160///
161@function clear($tree: $default-tree) {
162 @if not map.has-key($trees, $tree) {
163 @error 'Property tree "#{inspect($tree)}" does not exist.';
164 } 26 }
165 27
166 $trees: map.remove($trees, $tree) !global; 28 $v: list.nth($ref, 3);
167 29 $ref: list.set-nth($ref, 3, map.deep-merge($v, $value));
168 @return null; 30 @return $ref;
169} 31}
170 32
171/// 33@function get-deep($name, $value, $key: null, $keys...) {
172/// Access a whole property or a subsection (i.e. value) of it. 34 @if is-prop-ref($value) {
173/// 35 @return get($value, $key, $keys);
174/// @param {string | list} $key [null] - Key of the property to read. If this is a list of keys, the map will be traversed in that order.
175/// @param {string} $tree [$default-tree] - ID of the property tree to use
176/// @param {any} $default [null] - Default value to return of no match was found. If null, this function will throw an error instead.
177///
178/// @return {any} Value assigned to property or $default
179///
180/// @throw If there was no match for $key and $default is null
181///
182@function get-static($key: (), $tree: $default-tree, $default: null, $global: false) {
183 @if not map.has-key($trees, $tree) {
184 @error 'Unknown tree "#{$tree}".';
185 }
186
187 $result: map.get($trees, $tree);
188
189 @if not $global {
190 $ns-key: get-ns-key();
191
192 @if $ns-key != null {
193 $orig-key: $key;
194 $key: $ns-key;
195
196 @if meta.type-of($orig-key) == list {
197 @each $subkey in $orig-key {
198 $key: list.append($key, $subkey);
199 }
200 } @else {
201 $key: list.append($key, $orig-key);
202 }
203 }
204 }
205
206 @if meta.type-of($key) == list {
207 $stop: false;
208
209 @each $k in $key {
210 @if not $stop and map.has-key($result, $k) {
211 $result: map.get($result, $k);
212
213 @if meta.type-of($result) == list and list.nth($result, 1) == 'iro-prop-ref' {
214 @if list.length($result) == 2 {
215 $result: get-static($tree: list.nth($result, 2), $global: true);
216 } @else {
217 $result: get-static(list.nth($result, 3), nth($result, 2), $global: true);
218 }
219 }
220 } @else {
221 $stop: true;
222 }
223 }
224
225 @if $stop {
226 $result: null;
227 }
228 } @else {
229 $result: map.get($result, $key);
230
231 @if meta.type-of($result) == list and list.nth($result, 1) == 'iro-prop-ref' {
232 @if list.length($result) == 2 {
233 $result: get-static($tree: list.nth($result, 2), $global: true);
234 } @else {
235 $result: get-static(list.nth($result, 3), nth($result, 2), $global: true);
236 }
237 }
238 } 36 }
239 37 @if meta.type-of($value) == 'map' and $key != null {
240 @if $result == null { 38 @return get-deep(#{$name}#{$key}, map.get($value, $key), $keys...);
241 @if $default == null {
242 @error '"#{$key}" is null.';
243 } @else {
244 @return $default;
245 }
246 } 39 }
247 40 @return $name $value;
248 @return $result;
249} 41}
250 42
251/// 43@function map-to-vars($name, $map) {
252/// Generate a var() function call to get native CSS custom property. 44 @if meta.type-of($map) != 'map' {
253/// 45 @return var($name);
254/// @param {string | list} $key - Key of the property to read. If this is a list of keys, the map will be traversed in that order.
255/// @param {string | null} $tree [null] - Optional tree to check if the property actually exists.
256/// @param {any} $default [null] - Default value to return of no match was found.
257///
258/// @return {string} var()
259///
260@function get($key, $tree: $default-tree, $default: null, $global: false) {
261 @if $tree != null {
262 $value: get-static($key, $tree, $default, $global);
263
264 @if meta.type-of($value) == map {
265 $result: ();
266 @each $k in map.keys($value) {
267 $result: map.set($result, $k, get(list.append($key, $k), $tree, $default, $global));
268 }
269 @return $result;
270 }
271 }
272
273 @if not $global {
274 $ns-key: get-ns-key();
275
276 @if $ns-key != null {
277 $orig-key: $key;
278 $key: $ns-key;
279
280 @if meta.type-of($orig-key) == list {
281 @each $subkey in $orig-key {
282 $key: list.append($key, $subkey);
283 }
284 } @else {
285 $key: list.append($key, $orig-key);
286 }
287 }
288 } 46 }
289 47
290 $native-var: ''; 48 $out: ();
291 49
292 @if meta.type-of($key) == list { 50 @each $key, $value in $map {
293 @each $subkey in $key { 51 $out: map.set($out, $key, map-to-vars(#{$name}#{$key}, $value));
294 $native-var: $native-var + $subkey;
295 }
296 } @else {
297 $native-var: $key;
298 } 52 }
299 53
300 @if $default == null { 54 @return $out;
301 @return var(#{$native-var});
302 } @else {
303 @return var(#{$native-var}, #{$default});
304 }
305} 55}
306 56
307/// 57@function get($ref, $key: null, $keys...) {
308/// Generate assignments for native CSS custom properties with the values from the specified tree. 58 @if not is-prop-ref($ref) {
309/// 59 @return $ref;
310/// @param {string} $tree [$default-tree] - ID of the property tree to use
311/// @param {string} $root [()] - Sub-tree to use for assignment
312///
313@mixin assign($tree: $default-tree, $root: (), $skip: (), $prefix: $root, $global: false) {
314 $map: get-static($root, $tree, $global: $global);
315 $map: map.remove($map, $skip...);
316
317 @if meta.type-of($prefix) == list {
318 $prefix: functions.str-implode($prefix);
319 } 60 }
320 61
321 @if not $global { 62 $name: list.nth($ref, 2);
322 $ns-key: get-ns-key(); 63 $value: get(list.nth($ref, 3));
323 64
324 @if $ns-key != null { 65 @if meta.type-of($value) == 'map' {
325 $prefix: $prefix + functions.str-implode($ns-key); 66 $res: get-deep($name, $value, $key, $keys...);
67 $name: list.nth($res, 1);
68 $value: list.nth($res, 2);
69 } @else if meta.type-of($value) == 'list' {
70 $i: 1;
71 @each $item in $value {
72 $value: list.set-nth($value, $i, get($item));
73 $i: $i + 1;
326 } 74 }
327 } 75 }
328 76
329 @include assign-internal($map, $prefix); 77 @return map-to-vars($name, $value);
330} 78}
331 79
332/// 80@mixin materialize-helper($name, $value) {
333/// @access private 81 @if meta.type-of($value) == 'map' {
334/// 82 @each $key, $value in $value {
335@mixin assign-internal($map, $prefix: '', $ref-depth: $native-assign-max-depth) { 83 @include materialize-helper(#{$name}#{$key}, $value);
336 @each $key, $value in $map {
337 $rd: $ref-depth;
338 @if meta.type-of($value) == list and list.length($value) > 0 and list.nth($value, 1) == 'iro-prop-ref' {
339 @if $ref-depth != 0 {
340 $rd: $rd - 1;
341 @if list.length($value) == 2 {
342 $value: get-static($tree: list.nth($value, 2));
343 } @else {
344 $value: get-static(list.nth($value, 3), nth($value, 2));
345 }
346 } @else {
347 $value: null;
348 }
349 }
350 @if meta.type-of($value) != map and $value != () {
351 #{$prefix + $key}: #{$value};
352 } @else {
353 @include assign-internal($value, $prefix + $key, $rd);
354 } 84 }
85 } @else {
86 #{$name}: #{$value};
355 } 87 }
356} 88}
357 89
358/// 90@mixin materialize($ref, $match-meta: null) {
359/// Validate property names. 91 @if is-prop-ref($ref) {
360/// 92 $name: list.nth($ref, 2);
361/// @access private 93 $value: get(list.nth($ref, 3));
362/// 94 $meta: get(list.nth($ref, 4));
363@function validate($map) {
364 @each $key, $value in $map {
365 @if string.index($key, '--') != 1 {
366 @return false;
367 }
368 95
369 @if meta.type-of($value) == map { 96 @if $meta == $match-meta {
370 @if not validate($value) { 97 @include materialize-helper($name, $value);
371 @return false;
372 }
373 } 98 }
374 } 99 } @else if meta.type-of($ref) == 'list' {
375 100 @each $r in $ref {
376 @return true; 101 @include materialize($r);
377}
378
379///
380/// Generate a reference to another tree. Dereferencing is lazy, so you may specify a tree that hasn't been created yet.
381///
382/// @param {string} $tree [$default-tree] - ID of the property tree to use
383/// @param {string | list} $key - Key of the property to read. If this is a list of keys, the map will be traversed in that order.
384///
385/// @return {list} A special list that let's Ignis know that this is a lazy value.
386///
387/// @throw If there was no match for $key and $default is null
388///
389@function ref($tree: $default-tree, $key: null, $global: false) {
390 @if not $global {
391 $ns-key: get-ns-key();
392
393 @if $ns-key != null {
394 $orig-key: $key;
395 $key: $ns-key;
396
397 @if $orig-key != null {
398 @if meta.type-of($orig-key) == list {
399 @each $subkey in $orig-key {
400 $key: list.append($key, $subkey);
401 }
402 } @else {
403 $key: list.append($key, $orig-key);
404 }
405 }
406 } 102 }
407 } 103 }
408
409 @if $key == null {
410 @return ('iro-prop-ref' $tree);
411 } @else {
412 @return ('iro-prop-ref' $tree $key);
413 }
414} 104}
415
416///
417/// Get the current namespace key.
418///
419/// @access private
420///
421@function get-ns-key() {
422 $ctx: contexts.get($namespace-context-id, 'namespace');
423
424 @if $ctx == null {
425 @return null;
426 }
427
428 $data: list.nth($ctx, 2);
429 $key: map.get($data, 'key');
430
431 @return $key;
432}
433
434@include contexts.create($namespace-context-id);