diff options
| author | Volpeon <git@volpeon.ink> | 2024-10-18 13:02:18 +0200 |
|---|---|---|
| committer | Volpeon <git@volpeon.ink> | 2024-10-18 13:02:18 +0200 |
| commit | 1257a534af5ebda9f167b5bfd283e0ade8ed7b80 (patch) | |
| tree | 123762e4d1aa6e773190c10a1185081d41e5aff2 /src | |
| parent | Fix lint (diff) | |
| download | iro-sass-1257a534af5ebda9f167b5bfd283e0ade8ed7b80.tar.gz iro-sass-1257a534af5ebda9f167b5bfd283e0ade8ed7b80.tar.bz2 iro-sass-1257a534af5ebda9f167b5bfd283e0ade8ed7b80.zip | |
New CSS variable management
Diffstat (limited to 'src')
| -rw-r--r-- | src/_props.scss | 454 |
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); | ||
