From 44194280b23ffbe3454092cbde04ac3adcb0c2f9 Mon Sep 17 00:00:00 2001 From: Xu Yiming Date: Thu, 28 Mar 2019 13:49:47 +0800 Subject: [PATCH] [fix] Make BEM more robust --- src/mixins/function.scss | 55 +++++++--------- src/mixins/mixins.scss | 132 ++++++++++++++++++++------------------- 2 files changed, 91 insertions(+), 96 deletions(-) diff --git a/src/mixins/function.scss b/src/mixins/function.scss index 26a79b2e..e3c7b177 100644 --- a/src/mixins/function.scss +++ b/src/mixins/function.scss @@ -2,43 +2,34 @@ /* BEM support Func -------------------------- */ -@function selectorToString($selector) { - $selector: inspect($selector); - $selector: str-slice($selector, 2, -2); - @return $selector; -} - -@function containsModifier($selector) { - $selector: selectorToString($selector); - - @if str-index($selector, $modifier-separator) { - @return true; - } @else { - @return false; - } -} +/// See [Combinators](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors#Combinators "CSS selectors") +$_selector-combinators: ("+", "~", ">", "||"); -@function containWhenFlag($selector) { - $selector: selectorToString($selector); +@function bem-extend($selectors, $extendee, $extender) { + $returns: (); - @if str-index($selector, '.' + $state-prefix) { - @return true; - } @else { - @return false; + @if null == $selectors { + $selectors: selector-parse(""); } -} -@function containPseudoClass($selector) { - $selector: selectorToString($selector); - - @if str-index($selector, ':') { - @return true; - } @else { - @return false; + @each $selector in $selectors { + $len: length($selector); + + @if nth($selector, $len) == $extendee { + @if $len >= 2 { + @if index($_selector-combinators, nth($selector, $len - 1)) == null { + $returns: append($returns, set-nth($selector, $len, $extender), "comma"); + } @else { + $returns: append($returns, append($selector, $extender), "comma"); + } + } @else { + $returns: append($returns, ($extender), "comma"); + } + } @else { + $returns: append($returns, append($selector, $extender), "comma"); + } } -} -@function hitAllSpecialNestRule($selector) { - @return containsModifier($selector) or containWhenFlag($selector) or containPseudoClass($selector); + @return $returns; } diff --git a/src/mixins/mixins.scss b/src/mixins/mixins.scss index 8d2b56d1..7486b311 100644 --- a/src/mixins/mixins.scss +++ b/src/mixins/mixins.scss @@ -67,98 +67,102 @@ /* BEM -------------------------- */ + +$_BEM: (null, null, null); + @mixin b($block) { - $B: $namespace+'-'+$block !global; + $lastBEM: $_BEM; + $_BEM: ($block, null, null) !global; - .#{$B} { + .#{$namespace}-#{$block} { @content; } + + $_BEM: $lastBEM !global; } -@mixin e($element) { - $E: $element !global; - $selector: &; - $currentSelector: ""; - @each $unit in $element { - $currentSelector: #{$currentSelector + "." + $B + $element-separator + $unit + ","}; - } +@mixin e($elements) { + $block: nth($_BEM, 1); - @if hitAllSpecialNestRule($selector) { - @at-root { - #{$selector} { - #{$currentSelector} { - @content; - } - } - } + @if null == $block { + @error "Base-level rules cannot contain an element mixin"; } @else { - @at-root { - #{$currentSelector} { - @content; - } - } - } -} + $selects: &; -@mixin m($modifier) { - $selector: &; - $currentSelector: ""; - @each $unit in $modifier { - $currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","}; - } + @if (null != nth($_BEM, 2)) { + $selects: (); - @at-root { - #{$currentSelector} { - @content; + @each $old-select in & { + $selects: append($selects, set-nth($old-select, length($old-select), ".#{$namespace}-#{$block}"), "comma") + } } - } -} -@mixin configurable-m($modifier, $E-flag: false) { - $selector: &; - $interpolation: ''; + $lastBEM: $_BEM; + $_BEM: ($block, $elements, null) !global; - @if $E-flag { - $interpolation: $element-separator + $E-flag; - } + $parent: ".#{$namespace}-#{$block}"; + $current: (); - @at-root { - #{$selector} { - .#{$B+$interpolation+$modifier-separator+$modifier} { + @each $element in $elements { + $current: append( + $current, + bem-extend($selects, $parent, "#{$parent}#{$element-separator}#{$element}"), + "comma", + ); + } + + @at-root { + #{$current} { @content; } } + + $_BEM: $lastBEM !global; } } -@mixin spec-selector($specSelector: '', $element: $E, $modifier: false, $block: $B) { - $modifierCombo: ''; +@mixin m($modifiers) { + $block: nth($_BEM, 1); - @if $modifier { - $modifierCombo: $modifier-separator + $modifier; - } + @if null == $block { + @error "Base-level rules cannot contain a modifier mixin"; + } @else if null != nth($_BEM, 3) { + @error "Modifier-level rules cannot contain another modifier mixin"; + } @else { + $elements: nth($_BEM, 2); - @at-root { - #{&}#{$specSelector}.#{$block+$element-separator+$element+$modifierCombo} { - @content; + @if (null == $elements) { + $elements: (null); } - } -} -@mixin meb($modifier: false, $element: $E, $block: $B) { - $selector: &; - $modifierCombo: ''; + $lastBEM: $_BEM; + $_BEM: ($block, $elements, $modifiers) !global; - @if $modifier { - $modifierCombo: $modifier-separator + $modifier; - } + @each $element in $elements { + $parent: ".#{$namespace}-#{$block}"; - @at-root { - #{$selector} { - .#{$block+$element-separator+$element+$modifierCombo} { - @content; + @if (null != $element) { + $parent: "#{$parent}#{$element-separator}#{$element}"; + } + + $current: (); + + @each $modifier in $modifiers { + $current: append( + $current, + bem-extend(&, $parent, "#{$parent}#{$modifier-separator}#{$modifier}"), + "comma", + ); + } + + @at-root { + #{$current} { + @content; + } } } + + $_BEM: $lastBEM !global; } }