Skip to content

Commit

Permalink
+ element mixin
Browse files Browse the repository at this point in the history
+ selector-trim function
+ selector-trim-combinators function
  • Loading branch information
seyd committed Jun 15, 2022
1 parent c67a146 commit 535ccba
Show file tree
Hide file tree
Showing 41 changed files with 522 additions and 70 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "spotcss",
"version": "1.0.22",
"version": "2.0.0",
"description": "SPOT CSS Framework { Single Place Of Truth Methodology }",
"main": "index.scss",
"scripts": {
Expand Down Expand Up @@ -45,7 +45,7 @@
"bugs": {
"url": "https://github.com/seyd/spot-css/issues"
},
"homepage": "https://www.spotcss.page",
"homepage": "https://www.spotcss.io",
"dependencies": {},
"devDependencies": {
"cli-color": "^2.0.0",
Expand Down
41 changes: 36 additions & 5 deletions src/_private.mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,20 @@
$component: component();
$component-to-extend: self();
$is-internal-state: $modifier-name=='internal-state';
$is-super-modifier: str-index($modifier-name, 'super-')==1;
$second-param-is-number: length($args)==2 and type-of(nth($args, 2))=='number';
$second-param-index: '';
$super-component-index: 0;
@if $second-param-is-number {
$second-param-index: nth($args, 2);
$super-component-index: $second-param-index;
}

// for all non "super-" modifiers clear this index
@if not $is-super-modifier {
// clear this global variable
$_super-component-index: null !global;
}

@if $is-internal-state {
// for internal-state is relevant whole selector (every sub-child)
Expand All @@ -176,14 +190,20 @@
@else if _is-modifier-context-like($modifier-name) {
$component: top-component();
}
@else if str-index($modifier-name, 'super-')==1 {
@else {
// convert super-state and super-variant into modifier state and variant
$modifier-name: str-replace($modifier-name, 'super-', '');
$sub-component-stack: $_current-components;
@if not $is-super-modifier {
// changing logic to parse as super-modifier also state and variant, we add extra (useless) item here
$sub-component-stack: append($_current-components, 'PLACEHOLDER_TO_REMOVE', 'space');
}

$found: false;
$sub-component-name-for-error: '';
// just find component only for first identifier (all others are expected on the same component)
$identifier: first($args);

// find $identifier in parent component
@while not $found and length($sub-component-stack)>1 {
@if $_super-component-index != null {
Expand All @@ -192,14 +212,19 @@
$sub-component-stack: pop($sub-component-stack);
$component: #{$sub-component-stack};
// if no index is given or current super component is given index
@if $_super-component-index == null or $_super-component-index == 0 {
@if $_super-component-index == null or $_super-component-index == 0 {
$sub-component-name-for-error: $component;
$component-with-identifier: _get-component-identifier($component, $identifier);
$component-with-modifier: str-replace(_get-component-modifier($component, $modifier-name), ' &', '');
$modifiers: map-get($_registered-modifiers, $component-with-modifier);
$is-alias: index($_registered-aliases, str-replace($component-with-identifier, ' &', ''));
@if $is-alias or ($modifiers!=null and map-has-key($modifiers, $identifier)) {
$found: true;
@if $is-alias or ($modifiers!=null and map-has-key($modifiers, $identifier)) {
@if $super-component-index < 0 {
$super-component-index: $super-component-index + 1;
}
@else {
$found: true;
}
}
}
}
Expand Down Expand Up @@ -230,6 +255,10 @@
@error $SPOT_ERROR_PREFIX + $original-modifier-name+"/default-is-missing - the "+$original-modifier-name+" '"+inspect($args)+"' in component '"+$component+"' was used but default mixin wasn't used before it. Add 'default' mixin before all modifiers at first (even if it stays empty).";
}

@if $second-param-is-number {
$args: pop($args);
}

$current-modifier: $modifier-name + '(' + $args + ')';
// add current modifier to the stack
$current-modifier-stack: map-get($_current-modifiers-stack, $component); // for this component
Expand Down Expand Up @@ -327,9 +356,11 @@

// check all selectors if wasn't used already
$i: 0;
// for super-state|variant add index string to distinct already used state
@if $second-param-index!='' { $second-param-index: '[['+$second-param-index+']]'; }
@each $single-selector in $selectors {
$i: $i + 1;
$single-selector: _wrap_into_media_selector( str-replace($single-selector+'', '&', &) );
$single-selector: _wrap_into_media_selector( str-replace($single-selector+'', '&', &) )+$second-param-index;
@if index($_already-used-selectors, $single-selector) {
@error $SPOT_ERROR_PREFIX + $original-modifier-name+"/already-used-modifier - the "+$original-modifier-name+"('" + nth($args,$i) + "') is already used somewhere higher! Do not break the SPOT rule and use just single place for every variant "+$modifier-name+".";
}
Expand Down
31 changes: 27 additions & 4 deletions src/_public.functions.scss
Original file line number Diff line number Diff line change
Expand Up @@ -378,13 +378,13 @@

@function str-replace($string, $search, $replace: '') {
$index: str-index($string, $search);

@if $index {
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
}

@return $string;
}
}


@function str-trim-left($s) {
Expand Down Expand Up @@ -423,6 +423,29 @@
@return $selector;
}

// replace $search from the beginning or from the end of given single selector
@function selector-trim($selector, $search) {
$selector: str-trim($selector);
$index: str-index($selector, $search);
$max: str-length($selector);

@while $index==1 or $index==$max {
$selector: str-trim(str-slice($selector, 1, $index - 1) + str-slice($selector, $index + str-length($search)));
$index: str-index($selector, $search);
$max: str-length($selector);
}

@return $selector;
}

// it replaces '>', '~', '+' from the beginning or from the end of single selector
@function selector-trim-combinators($selector) {
$selector: selector-trim($selector, '>');
$selector: selector-trim($selector, '~');
$selector: selector-trim($selector, '+');
@return $selector;
}

// It has to be simple selector (not contain comma separated selectors)
// @return How many selector levels given selector contains.
// @example:
Expand Down
146 changes: 146 additions & 0 deletions src/_public.mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@
///
/// 2) Components can be nested one into another.
///
/// @deprecated from version 2
/// @group Component
/// @access public
/// @deprecated from version 2+, instead use +element() mixin.
/// @param { String } $selector - single selector of sub-component
/// @content
/// @throw SPOT CSS: sub-component/no-selector - selector was expected but nothing passed.
Expand Down Expand Up @@ -177,8 +179,10 @@
///
/// 2) Components can be nested one into another.
///
/// @deprecated from version 2
/// @group Component
/// @access public
/// @deprecated from version 2+, instead use +element() mixin.
/// @param { String } $selector - single selector of sub-component
/// @content
/// @throw SPOT CSS: sub-component/no-selector - selector was expected but nothing passed.
Expand Down Expand Up @@ -399,6 +403,7 @@
///
/// @group Component
/// @access public
/// @deprecated from version 2+, use just +state('identifier',-1) or other negative index.
/// @param { String } $args... - selector extension(s) = state names
/// @content
/// @throw See errors of mixin _super-modifier and _apply-modifier.
Expand All @@ -418,6 +423,7 @@
///
/// @group Component
/// @access public
/// @deprecated from version 2+, use just +variant('identifier',-1) or other negative index.
/// @param { String } $args... - selector extension(s) = variant names
/// @content
/// @throw See errors of mixin _super-modifier and _apply-modifier.
Expand Down Expand Up @@ -495,9 +501,148 @@
}


/// Element mixin to define sub elements in component.
///
/// Context, responsive and browser modifiers are inherited from parent component but element can register also its own.
///
/// 1) It checks if same element is defined multiple times. It is not allowed because Single Place Of Truth.
///
/// 2) Elements can be nested one into another.
///
/// @group Component
/// @access public
/// @since from version 2.0.0
/// @param { String } $selector - single selector of sub element of component. It has to be single and simple selector.
/// @content
/// @throw SPOT CSS: element/no-selectors - at least one selector is expected.
/// @throw SPOT CSS: element/no-component - you cannot use mixin element outside of component.
/// @throw SPOT CSS: element/in-modifier - it is not allowed to use element in modifier '{current_modifier}'.
/// @throw SPOT CSS: element/in-default - it is not allowed to call element mixin in default mixin (in component '{component}).
/// @throw SPOT CSS: element/missing-separator - there is sub-element separator mixin (_______) missing before element mixin (in component '{component}').
/// @throw SPOT CSS: element/already-registered - trying to register already registered element '{selector}' for component '{component}'.
/// @throw SPOT CSS: element/unknown-unregistered - given element '{selector}' is not registered in direct parent component ('{component}').
/// @throw SPOT CSS: element/duplicate - given element selector is already used in current block ('{selector}').";
/// @throw SPOT CSS: element/no-selector - selector was expected but nothing passed.
/// @throw SPOT CSS: element/single-selector - given selector is not a single selector, because contains comma(s): '{selector}'.
/// @output Given selector with given content.
@mixin element($selectors...) {
@if length($selectors)==0 {
@error $SPOT_ERROR_PREFIX + "element/no-selectors - at least one selector is expected.";
}

@if component()=='' {
@error $SPOT_ERROR_PREFIX + "element/no-component - you cannot use mixin element outside of component.";
}

// do not allow in modifier
@if $_is-modifier-mode {
@error $SPOT_ERROR_PREFIX + "element/in-modifier - it is not allowed to use element in modifier '"+$_current_modifier+"'.";
}

// do not allow in default mutation
@if $_is-default-mode {
@error $SPOT_ERROR_PREFIX + "element/in-default - it is not allowed to call element mixin in default mixin (in component '"+component()+"').";
}

// check for separator ____________________________
@if not $_is-register-mode and not $_was-used-sub-element-separator {
@error $SPOT_ERROR_PREFIX + "element/missing-separator - there is sub-element separator mixin (_______) missing before element mixin in component ('"+component()+"').";
}
// reset flag for next (sub)element
$_was-used-sub-element-separator: false !global;

// test for not empty and single selector
@each $selector in $selectors {
@if not $selector or $selector=="" {
@error $SPOT_ERROR_PREFIX + "element/no-selector - selector was expected but nothing passed.";
}
$parts: selector-parse($selector);
@if length($parts) > 1 {
@error $SPOT_ERROR_PREFIX + "element/single-selector - given selector is not a single selector, because contains comma(s): '"+$selector+"'.";
}

@if length-of-simple-selector(selector-trim-combinators($selector)) > 1 {
@error $SPOT_ERROR_PREFIX + "element/simple-selector - given selector has to be simple selector withou spaces, '>' and '~' but is: '"+$selector+"'.";
}
}

@if $_is-register-mode {
// TODOS
// {alias} and selector(s)

@each $selector in $selectors {
$sub-component-selector: #{ & + ' ' + $selector };

// guarding duplicity of sub-selectors
@if index($_already-used-selectors, $sub-component-selector) or index($_registered-sub-components, $sub-component-selector) {
@error $SPOT_ERROR_PREFIX + "element/already-registered - trying to register already registered element '"+$selector+"' for component '"+component()+"'.";
}
// store unique sub-component
$_already-used-selectors: append($_already-used-selectors, $sub-component-selector, 'comma') !global;
$_registered-sub-components: append($_registered-sub-components, $sub-component-selector, 'comma') !global;

// store current component selector (into stack)
$_current-components: append($_current-components, $selector, 'space') !global;

$_is-child-component-mode: true !global;

@at-root
#{$sub-component-selector} {
@content;
}

$_is-child-component-mode: false !global;

// remove current component selector (from stack)
$_current-components: pop($_current-components) !global;
}
}
@else {
// store appling sub element in current selector to avoid calling state, variant, etc. after element section (see error "/invalid-order")
@if not index($_selectors-with-sub-elements, &) {
$_selectors-with-sub-elements: append($_selectors-with-sub-elements, &) !global;
}

@each $selector in $selectors {
$sub-component-selector: #{ & + ' ' + $selector };
// check if element is registered
@if not index($_already-used-selectors, $sub-component-selector) {
@error $SPOT_ERROR_PREFIX + "element/unknown-unregistered - given element '"+$selector+"' is not registered in direct parent component ('"+component()+"').";
}
// 1)
// if already registred
@if index($_registered-components, $sub-component-selector)!=null {
@error $SPOT_ERROR_PREFIX + "element/duplicate - given element selector is already used in current block ('"+$sub-component-selector+"').";
}
@else {
// store component selector if is not stored yet
$_registered-components: append($_registered-components, $sub-component-selector, 'comma') !global;
}

// 2)
// store current component selector (into stack)
$_current-components: append($_current-components, $selector, 'space') !global;

$_is-child-component-mode: true !global;

@at-root
#{$sub-component-selector} {
@content;
}

$_is-child-component-mode: false !global;

// remove current component selector (from stack)
$_current-components: pop($_current-components) !global;
}
}
}


/// Declares direct child block in current component selector.
/// @group Component
/// @access public
/// @deprecated from version 2+, instead use +element() mixin.
/// @param { String } $selectors... - One or more selectors of direct childs. Each selector as a single selector in separate argument.
/// @content
/// @throw SPOT CSS: child-element/no-selectors - at least one child selector is expected.
Expand All @@ -520,6 +665,7 @@
/// Note: It is strongly recomended to use <b>child-element</b> mixin everytime it is possible!
/// @group Component
/// @access public
/// @deprecated from version 2+, instead use +element() mixin.
/// @param { String } $selectors... - One or more selectors. Each selector as a single selector in separate argument.
/// @content
/// @throw SPOT CSS: sub-element/no-selectors - at least one selector is expected.
Expand Down
2 changes: 1 addition & 1 deletion src/_spot-css.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// @author Johnny Seyd (johnnyseyd@gmail.com)
/// @link https://github.com/seyd/spot-css
///
$SPOT-CSS-FRAMEWORK: '1.0.22';
$SPOT-CSS-FRAMEWORK: '2.0.0';
//
// Usage:
// @import "~spotcss";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Error SPOT CSS: element/already-registered */
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.component {
property: value;
}

.component:hover {
property: value-2;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Error SPOT CSS: element/duplicate */
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Error SPOT CSS: element/in-default */
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Error SPOT CSS: element/in-modifier */
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Error SPOT CSS: element/missing-separator */
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Error SPOT CSS: element/no-component */
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Error SPOT CSS: element/no-selector */
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Error SPOT CSS: element/no-selectors */
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Error SPOT CSS: element/no-selectors */
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Error SPOT CSS: element/simple-selector */
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Error SPOT CSS: element/single-selector */
Loading

0 comments on commit 535ccba

Please sign in to comment.