/** * Imported ionic string functions for SCSS * ---------------------------------------------------------------------------- * Extracted from * https://github.com/ionic-team/ionic-framework/blob/main/core/src/themes/ionic.functions.string.scss */ // String Utility Functions // -------------------------------------------------------------------------------- // String Replace Function // -------------------------------------------------------------------------------- @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 $string; } // String Split Function // -------------------------------------------------------------------------------- @function str-split($string, $separator) { // empty array/list $split-arr: (); // first index of separator in string $index: str-index($string, $separator); // loop through string @while $index != null { // get the substring from the first character to the separator $item: str-slice($string, 1, $index - 1); // push item to array $split-arr: append($split-arr, $item); // remove item and separator from string $string: str-slice($string, $index + 1); // find new index of separator $index: str-index($string, $separator); } // add the remaining string to list (the last item) $split-arr: append($split-arr, $string); @return $split-arr; } // String Extract Function // -------------------------------------------------------------------------------- @function str-extract($string, $start, $end) { $start-index: str-index($string, $start); @if $start-index { $post: str-slice($string, $start-index + str-length($start)); $end-index: str-index($post, $end); @if $end-index { @return str-slice($post, 1, $end-index - 1); } } @return null; } // String Contains Function // -------------------------------------------------------------------------------- @function str-contains($string, $needle) { @if (type-of($string) == string) { @return str-index($string, $needle) != null; } @return false; } // URL Encode Function // -------------------------------------------------------------------------------- @function url-encode($val) { $spaces: str-replace($val, " ", "%20"); $encoded: str-replace($spaces, "#", "%23"); @return $encoded; } // Add Root Selector // -------------------------------------------------------------------------------- // Adds a root selector using host based on the selector passed // $root: The selector that needs to be updated to include the $addHostSelector. // - Example: ion-button // $addHostSelector: The selector that is used to add the host to the $root selector. // - Example: [dir=rtl] // $useHostContext: Whether to use host-context or not. Defaults to true. // -------------------------------------------------------------------------------- @function add-root-selector($root, $addHostSelector, $useHostContext: true) { $selectors: str-split($root, ","); $list: (); @each $selector in $selectors { // If the selector contains :host( it means it is targeting a class on the host // element so we need to change how we target it: // Example with `useHostContext=true` // @include add-root-selector(":host(.fixed)", "[dir=rtl]") // --> :host-context([dir=rtl]):host(.fixed) // --> :host-context([dir=rtl]).fixed // --- // Example with `useHostContext=false` // @include add-root(":host(.fixed)", ":dir(rtl)", false) // --> :host(.fixed:dir(rtl)) @if str-contains($selector, ":host(") { @if $useHostContext { // @include add-root-selector(":host(.fixed)", "[dir=rtl]") // --> :host-context([dir=rtl]):host(.fixed) $shadow-element: str-replace($selector, ":host(", ":host-context(#{$addHostSelector}):host("); $list: append($list, $shadow-element, comma); } $new-element: (); $elements: str-split($selector, " "); @each $element in $elements { @if str-contains($element, ":host(") { $scoped-element: $element; // Replace the :host( and ) so all we have left is the class // inside of it: // :host(.fixed) -> .fixed $scoped-element: str-replace($scoped-element, ")", ""); $scoped-element: str-replace($scoped-element, ":host(", ""); // Add the class back inside of host with the addHostSelector: @if $useHostContext { // .fixed -> :host-context([dir=rtl]).fixed $scoped-element: str-replace($scoped-element, $scoped-element, ":host-context(#{$addHostSelector})#{$scoped-element}"); } @else { // .fixed -> :host(.fixed:dir(rtl)) $scoped-element: str-replace($scoped-element, $scoped-element, ":host(#{$scoped-element}#{$addHostSelector})"); } // @include add-root-selector(":host(.fixed)", "[dir=rtl]") // --> :host-context([dir=rtl]).fixed // @include add-root(":host(.fixed)", ":dir(rtl)", false) // --> :host(.fixed:dir(rtl)) $new-element: append($new-element, $scoped-element, space); } @else { // Add back any selectors that followed the host // after transforming the first selector: // @include add-root-selector(":host(.fixed) ::slotted(ion-icon)", "[dir=rtl]") // --> :host-context([dir=rtl]):host(.fixed) ::slotted(ion-icon) // --> :host-context([dir=rtl]).fixed ::slotted(ion-icon) // @include add-root(":host(.fixed) ::slotted(ion-icon)", ":dir(rtl)", false) // --> :host(.fixed:dir(rtl)) ::slotted(ion-icon) $new-element: append($new-element, $element, space); } } $list: append($list, $new-element, comma); // If the selector contains :host without a parantheses // it means it is targeting just the host // element so we can change it to look for host-context // @include add-root-selector(":host", "[dir=rtl]") // --> :host-context([dir=rtl]) // @include add-root(":host", ":dir(rtl)", false) // --> :host(:dir(rtl)) } @else if str-contains($selector, ":host") { $new-element: (); $elements: str-split($selector, " "); @each $element in $elements { @if str-contains($element, ":host") { $updated-element: ''; // Replace the :host with the addHostSelector: @if $useHostContext { // :host -> :host-context([dir=rtl]) $updated-element: str-replace($element, ":host", ":host-context(#{$addHostSelector})"); } @else { // :host -> :host(:dir(rtl)) $updated-element: str-replace($element, ":host", ":host(#{$addHostSelector})"); } // Add the final selector after all transformations: // @include add-root-selector(":host", "[dir=rtl]") // --> :host-context([dir=rtl]) // @include add-root(":host", ":dir(rtl)", false) // --> :host(:dir(rtl)) $new-element: append($new-element, $updated-element, space); } @else { // Add back any selectors that followed the host // after transforming the first selector: // @include add-root-selector(":host ::slotted(ion-icon)", "[dir=rtl]") // --> :host-context([dir=rtl]) ::slotted(ion-icon) // @include add-root(":host ::slotted(ion-icon)", ":dir(rtl)", false) // --> :host(:dir(rtl)) ::slotted(ion-icon) $new-element: append($new-element, $element, space); } } $list: append($list, $new-element, comma); // If the selector does not contain host at all it is either a shadow // or normal element so append both the addHostSelector and host-context // @include add-root-selector("ion-component", "[dir=rtl]") // --> :host-context([dir=rtl]) ion-component // --> [dir=rtl] ion-component // @include add-root("ion-component", ":dir(rtl)", false) // --> ion-component:dir(rtl) } @else { @if ($useHostContext) { $list: append($list, ":host-context(#{$addHostSelector}) #{$selector}", comma); $list: append($list, "#{$addHostSelector} #{$selector}", comma); } @else { $list: append($list, "#{$selector}#{$addHostSelector}", comma); } } } @return $list; }