From f0f84513f8efe533b6ee670a6f1a0c074387b2ec Mon Sep 17 00:00:00 2001 From: Volpeon Date: Wed, 13 Aug 2025 12:01:46 +0200 Subject: Make use of SASS modules --- src/bem/_element.scss | 565 +++++++++++++++++++++++++------------------------- 1 file changed, 280 insertions(+), 285 deletions(-) (limited to 'src/bem/_element.scss') diff --git a/src/bem/_element.scss b/src/bem/_element.scss index 64862b0..9f108fe 100644 --- a/src/bem/_element.scss +++ b/src/bem/_element.scss @@ -4,6 +4,11 @@ /// @access public //// +@use 'sass:list'; +@use 'sass:map'; +@use 'sass:meta'; +@use 'sass:selector'; +@use 'sass:string'; @use './validators'; @use './vars'; @use '../functions'; @@ -92,22 +97,22 @@ /// } /// @mixin elem($name, $names...) { - $result: elem($name, $names...); - $selector: nth($result, 1); - $context: nth($result, 2); - - @include validators.validate( - 'element', - (name: $name, names: $names), - $selector, - $context - ); - - @include contexts.push(vars.$context-id, $context...); - @at-root #{$selector} { - @content; - } - @include contexts.pop(vars.$context-id); + $result: elem($name, $names...); + $selector: list.nth($result, 1); + $context: list.nth($result, 2); + + @include validators.validate( + 'element', + (name: $name, names: $names), + $selector, + $context + ); + + @include contexts.push(vars.$context-id, $context...); + @at-root #{$selector} { + @content; + } + @include contexts.pop(vars.$context-id); } /// @@ -118,99 +123,93 @@ /// @see {mixin} element /// @function elem($name, $names...) { - $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth); - $noop: contexts.assert-stack-must-contain(vars.$context-id, 'block'); - - $parent-context: contexts.get(vars.$context-id, 'block' 'element'); - - $selector: (); - $parts-data: (); - - @if nth($parent-context, 1) == 'element' { - @if vars.$element-nesting-policy == 'disallow' { - @error 'Element nesting is forbidden.'; - } - - @if vars.$element-nesting-policy == 'append' { - $element-selector: map-get(nth($parent-context, 2), 'selector'); - - @if not functions.selector-suffix-match(&, $element-selector) { - @error 'A nested element must be an immediate children of the parent element.'; - } - - // - // Possible outcomes: - // - {e}__element - // - [manual selector] {e}__element - // - - @each $name in join($name, $names) { - $sel: selector-append(&, vars.$element-separator + $name); - $selector: join($selector, $sel, comma); - $parts-data: append( - $parts-data, ( - 'name': $name, - 'selector': $sel - ) - ); - } - } - - $parent-context: contexts.get(vars.$context-id, 'block'); - } - - @if length($selector) == 0 { - $parent-selector: map-get(nth($parent-context, 2), 'selector'); - - @if functions.selector-suffix-match(&, $parent-selector) { - // - // Possible outcomes: - // - {b}__element - // - [manual selector] {b}__element - // - - @each $name in join($name, $names) { - $sel: selector-append(&, vars.$element-separator + $name); - $selector: join($selector, $sel, comma); - $parts-data: append( - $parts-data, ( - 'name': $name, - 'selector': $sel - ) - ); - } - } @else { - // - // Possible outcomes: - // - {b} [manual selector] {b}__element - // - {e,m,s} ([manual selector]) {b}__element - // - - @if nth($parent-context, 1) != 'block' { - $parent-context: contexts.get(vars.$context-id, 'block'); - } - - $block-base-selector: map-get(nth($parent-context, 2), 'base-selector'); - - @each $name in join($name, $names) { - $sel: selector-nest(&, selector-append($block-base-selector, vars.$element-separator + $name)); - $selector: join($selector, $sel, comma); - $parts-data: append( - $parts-data, ( - 'name': $name, - 'selector': $sel - ) - ); - } - } - } - - $context: 'element', ( - 'parts': $parts-data, - 'selector': $selector - ); - - @return $selector $context; + $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth); + $noop: contexts.assert-stack-must-contain(vars.$context-id, 'block'); + + $parent-context: contexts.get(vars.$context-id, 'block' 'element'); + + $selector: (); + $parts-data: (); + + @if list.nth($parent-context, 1) == 'element' { + @if vars.$element-nesting-policy == 'disallow' { + @error 'Element nesting is forbidden.'; + } + + @if vars.$element-nesting-policy == 'append' { + $element-selector: map.get(list.nth($parent-context, 2), 'selector'); + + @if not functions.selector-suffix-match(&, $element-selector) { + @error 'A nested element must be an immediate children of the parent element.'; + } + + // + // Possible outcomes: + // - {e}__element + // - [manual selector] {e}__element + // + + @each $name in list.join($name, $names) { + $sel: selector.append(&, vars.$element-separator + $name); + $selector: list.join($selector, $sel, comma); + $parts-data: list.append($parts-data, ( + 'name': $name, + 'selector': $sel + )); + } + } + + $parent-context: contexts.get(vars.$context-id, 'block'); + } + + @if list.length($selector) == 0 { + $parent-selector: map.get(list.nth($parent-context, 2), 'selector'); + + @if functions.selector-suffix-match(&, $parent-selector) { + // + // Possible outcomes: + // - {b}__element + // - [manual selector] {b}__element + // + + @each $name in list.join($name, $names) { + $sel: selector.append(&, vars.$element-separator + $name); + $selector: list.join($selector, $sel, comma); + $parts-data: list.append($parts-data, ( + 'name': $name, + 'selector': $sel + )); + } + } @else { + // + // Possible outcomes: + // - {b} [manual selector] {b}__element + // - {e,m,s} ([manual selector]) {b}__element + // + + @if list.nth($parent-context, 1) != 'block' { + $parent-context: contexts.get(vars.$context-id, 'block'); + } + + $block-base-selector: map.get(list.nth($parent-context, 2), 'base-selector'); + + @each $name in list.join($name, $names) { + $sel: selector.nest(&, selector.append($block-base-selector, vars.$element-separator + $name)); + $selector: list.join($selector, $sel, comma); + $parts-data: list.append($parts-data, ( + 'name': $name, + 'selector': $sel + )); + } + } + } + + $context: 'element', ( + 'parts': $parts-data, + 'selector': $selector + ); + + @return $selector $context; } /// @@ -291,22 +290,22 @@ /// } /// @mixin related-elem($sign, $name, $names...) { - $result: related-elem($sign, $name, $names...); - $selector: nth($result, 1); - $context: nth($result, 2); - - @include validators.validate( - 'related-element', - (sign: $sign, name: $name, names: $names), - $selector, - $context - ); - - @include contexts.push(vars.$context-id, $context...); - @at-root #{$selector} { - @content; - } - @include contexts.pop(vars.$context-id); + $result: related-elem($sign, $name, $names...); + $selector: list.nth($result, 1); + $context: list.nth($result, 2); + + @include validators.validate( + 'related-element', + (sign: $sign, name: $name, names: $names), + $selector, + $context + ); + + @include contexts.push(vars.$context-id, $context...); + @at-root #{$selector} { + @content; + } + @include contexts.pop(vars.$context-id); } /// @@ -318,44 +317,42 @@ /// @see {mixin} related-element /// @function related-elem($sign, $name, $names...) { - // - // Generating this selector is simple: Take the latest block context, use it - // to generate the element part, and insert it at the end of the current selector. - // Possible outcomes: - // - {e} ({m,s}) ([manual selector]) + {e} - // - {e} ({m,s}) ([manual selector]) ~ {e} - // - - $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth); - $noop: contexts.assert-stack-must-contain(vars.$context-id, 'element'); - - @if $sign != '+' and $sign != '~' { - @error 'Invalid relationship sign #{inspect($sign)}.'; - } - - $block-context: contexts.get(vars.$context-id, 'block'); - $block-base-selector: map-get(nth($block-context, 2), 'base-selector'); - - $selector: (); - $parts-data: (); - - @each $name in join($name, $names) { - $sel: selector-nest(&, $sign, selector-append($block-base-selector, vars.$element-separator + $name)); - $selector: join($selector, $sel, comma); - $parts-data: append( - $parts-data, ( - 'name': $name, - 'selector': $sel - ) - ); - } - - $context: 'element', ( - 'parts': $parts-data, - 'selector': $selector - ); - - @return $selector $context; + // + // Generating this selector is simple: Take the latest block context, use it + // to generate the element part, and insert it at the end of the current selector. + // Possible outcomes: + // - {e} ({m,s}) ([manual selector]) + {e} + // - {e} ({m,s}) ([manual selector]) ~ {e} + // + + $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth); + $noop: contexts.assert-stack-must-contain(vars.$context-id, 'element'); + + @if $sign != '+' and $sign != '~' { + @error 'Invalid relationship sign #{inspect($sign)}.'; + } + + $block-context: contexts.get(vars.$context-id, 'block'); + $block-base-selector: map.get(list.nth($block-context, 2), 'base-selector'); + + $selector: (); + $parts-data: (); + + @each $name in list.join($name, $names) { + $sel: selector.nest(&, $sign, selector.append($block-base-selector, vars.$element-separator + $name)); + $selector: list.join($selector, $sel, comma); + $parts-data: list.append($parts-data, ( + 'name': $name, + 'selector': $sel + )); + } + + $context: 'element', ( + 'parts': $parts-data, + 'selector': $selector + ); + + @return $selector $context; } /// @@ -369,9 +366,9 @@ /// @content /// @mixin sibling-elem($name, $names...) { - @include related-elem('~', $name, $names...) { - @content; - } + @include related-elem('~', $name, $names...) { + @content; + } } /// @@ -383,7 +380,7 @@ /// @see {mixin} sibling-element /// @function sibling-elem($name, $names...) { - @return related-elem('~', $name, $names...); + @return related-elem('~', $name, $names...); } /// @@ -397,9 +394,9 @@ /// @content /// @mixin next-elem($name, $names...) { - @include related-elem('+', $name, $names...) { - @content; - } + @include related-elem('+', $name, $names...) { + @content; + } } /// @@ -411,7 +408,7 @@ /// @see {mixin} next-element /// @function next-elem($name, $names...) { - @return related-elem('+', $name, $names...); + @return related-elem('+', $name, $names...); } /// @@ -467,22 +464,22 @@ /// } /// @mixin related-twin-elem($sign) { - $result: related-twin-elem($sign); - $selector: nth($result, 1); - $context: nth($result, 2); - - @include validators.validate( - 'next-twin-elem', - (), - $selector, - $context - ); - - @include contexts.push(vars.$context-id, $context...); - @at-root #{$selector} { - @content; - } - @include contexts.pop(vars.$context-id); + $result: related-twin-elem($sign); + $selector: list.nth($result, 1); + $context: list.nth($result, 2); + + @include validators.validate( + 'next-twin-elem', + (), + $selector, + $context + ); + + @include contexts.push(vars.$context-id, $context...); + @at-root #{$selector} { + @content; + } + @include contexts.pop(vars.$context-id); } /// @@ -494,96 +491,94 @@ /// @see {mixin} next-twin-elem /// @function related-twin-elem($sign) { - $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth); - $noop: contexts.assert-stack-must-contain(vars.$context-id, 'element'); - - $element-context: contexts.get(vars.$context-id, 'element'); - $element-selector: map-get(nth($element-context, 2), 'selector'); - - $block-context: contexts.get(vars.$context-id, 'block'); - $block-base-selector: map-get(nth($block-context, 2), 'base-selector'); - - $selector: (); - $parts-data: (); - - // - // To determine the twin for each element, iterate the sub-selectors from the current selector - // and check if it contains the currently inspected element. This has to be done with string - // comparison since none of Sass selector functions is of use here. - // Finally, the current twin will be appended to the extracted sub-selector as a successor - // element. - // - @each $part-data in map-get(nth($element-context, 2), 'parts') { - $part-selector: map-get($part-data, 'selector'); - $part-name: map-get($part-data, 'name'); - - $sel: (); - @if functions.selector-suffix-match(&, $element-selector) { - // - // This mixin is included in the selector the last element mixin created. - // Possible outcomes: - // - {e} + {e} - // - [manual selector] {e} + {e} - // - - @each $s in & { - @each $ps in $part-selector { - @if nth($s, -1) == nth($ps, -1) { - $sel-ent: selector-nest($s, $sign, selector-append($block-base-selector, vars.$element-separator + $part-name)); - $sel: join($sel, $sel-ent, comma); - } - } - } - } @else { - // - // This mixin is NOT included in the selector the last element mixin created. - // Possible outcomes: - // - {e} {m,s} + {e} - // - {e} [manual selector] + {e} - // - {e} {m,s} [manual selector] + {e} - // - - @each $s in & { - @each $ps in $part-selector { - @if str-index(inspect($s), inspect($ps)) { - $char-index: str-length(inspect($ps)) + 1; - $match: index(' ' ':' ',', str-slice(inspect($s), $char-index, $char-index)) != null; - - @if not $match { - @each $separator in vars.$element-separator vars.$modifier-separator vars.$suffix-separator { - @if str-slice(inspect($s), $char-index, $char-index + str-length($separator) - 1) == $separator { - $match: true; - } - } - } - - @if $match { - $sel-ent: selector-nest($s, '+', selector-append($block-base-selector, vars.$element-separator + $part-name)); - $sel: join($sel, $sel-ent, comma); - } - } - } - } - } - @if length($sel) != length($part-selector) { - @error 'Could not generate twin element selector.'; - } - - $selector: join($selector, $sel, comma); - $parts-data: append( - $parts-data, ( - 'name': $part-name, - 'selector': $sel - ) - ); - } - - $context: 'element', ( - 'parts': $parts-data, - 'selector': $selector - ); - - @return $selector $context; + $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth); + $noop: contexts.assert-stack-must-contain(vars.$context-id, 'element'); + + $element-context: contexts.get(vars.$context-id, 'element'); + $element-selector: map.get(list.nth($element-context, 2), 'selector'); + + $block-context: contexts.get(vars.$context-id, 'block'); + $block-base-selector: map.get(list.nth($block-context, 2), 'base-selector'); + + $selector: (); + $parts-data: (); + + // + // To determine the twin for each element, iterate the sub-selectors from the current selector + // and check if it contains the currently inspected element. This has to be done with string + // comparison since none of Sass selector functions is of use here. + // Finally, the current twin will be appended to the extracted sub-selector as a successor + // element. + // + @each $part-data in map.get(list.nth($element-context, 2), 'parts') { + $part-selector: map.get($part-data, 'selector'); + $part-name: map.get($part-data, 'name'); + + $sel: (); + @if functions.selector-suffix-match(&, $element-selector) { + // + // This mixin is included in the selector the last element mixin created. + // Possible outcomes: + // - {e} + {e} + // - [manual selector] {e} + {e} + // + + @each $s in & { + @each $ps in $part-selector { + @if list.nth($s, -1) == list.nth($ps, -1) { + $sel-ent: selector.nest($s, $sign, selector.append($block-base-selector, vars.$element-separator + $part-name)); + $sel: list.join($sel, $sel-ent, comma); + } + } + } + } @else { + // + // This mixin is NOT included in the selector the last element mixin created. + // Possible outcomes: + // - {e} {m,s} + {e} + // - {e} [manual selector] + {e} + // - {e} {m,s} [manual selector] + {e} + // + + @each $s in & { + @each $ps in $part-selector { + @if string.index(meta.inspect($s), meta.inspect($ps)) { + $char-index: string.length(meta.inspect($ps)) + 1; + $match: list.index(' ' ':' ',', string.slice(meta.inspect($s), $char-index, $char-index)) != null; + + @if not $match { + @each $separator in vars.$element-separator vars.$modifier-separator vars.$suffix-separator { + @if string.slice(meta.inspect($s), $char-index, $char-index + string.length($separator) - 1) == $separator { + $match: true; + } + } + } + + @if $match { + $sel-ent: selector.nest($s, '+', selector.append($block-base-selector, vars.$element-separator + $part-name)); + $sel: list.join($sel, $sel-ent, comma); + } + } + } + } + } + @if list.length($sel) != list.length($part-selector) { + @error 'Could not generate twin element selector.'; + } + + $selector: list.join($selector, $sel, comma); + $parts-data: list.append($parts-data, ( + 'name': $part-name, + 'selector': $sel + )); + } + + $context: 'element', ( + 'parts': $parts-data, + 'selector': $selector + ); + + @return $selector $context; } /// @@ -594,9 +589,9 @@ /// @content /// @mixin sibling-twin-element { - @include related-twin-elem('~') { - @content; - } + @include related-twin-elem('~') { + @content; + } } /// @@ -608,7 +603,7 @@ /// @see {mixin} sibling-twin-element /// @function sibling-twin-elem() { - @return related-twin-elem('~'); + @return related-twin-elem('~'); } /// @@ -619,9 +614,9 @@ /// @content /// @mixin next-twin-elem { - @include related-twin-elem('+') { - @content; - } + @include related-twin-elem('+') { + @content; + } } /// @@ -633,5 +628,5 @@ /// @see {mixin} next-twin-elem /// @function next-twin-elem() { - @return related-twin-elem('+'); + @return related-twin-elem('+'); } -- cgit v1.2.3-70-g09d2