//// /// Various functions. /// /// This file contains various and mostly unrelated functions. Some of which /// are used in this framework, while others are just there and may be used. /// /// @group Functions /// /// @access public //// @use 'sass:map'; @use 'sass:math'; @use './vars'; /// /// Replace a substring with a new string. /// /// @param {string} $string - String to search /// @param {string} $search - Substring that gets replaced /// @param {string} $replace - String that replaces $search /// /// @return {string} A string with all instances of $search replaced with $replace /// @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; } /// /// Concatenate all items from $list. /// /// @param {list} $list /// @param {number} $glue - Delimiter /// /// @return {string} /// @function str-implode($list, $glue: '') { $result: ''; @each $item in $list { $result: $result + if(length($item) > 1, str-implode($item, $glue), $item); @if $item != nth($list, length($list)) { $result: $result + $glue; } } @return $result; } /// /// Extract a subset from the given list. /// /// @param {list} $list /// @param {number} $start [1] - Indices before this value will be discarded /// @param {number} $end [length($list)] - Indices starting after this value will be discarded /// /// @return {list} A slice of the list /// @function list-slice($list, $start: 1, $end: length($list)) { $result: (); @if $end >= $start { @for $i from $start through $end { @if $i != 0 { $result: append($result, nth($list, $i), list-separator($list)); } } } @return $result; } /// /// Add a new item to the beginning of a list. /// /// @param {list} $list /// @param {number} $value /// /// @return {list} A list with $value at the beginning, followed by the other items /// @function list-prepend($list, $value) { $result: append((), $value, list-separator($list)); @if length($list) > 0 { @for $i from 1 through length($list) { $result: append($result, nth($list, $i), list-separator($list)); } } @return $result; } /// /// Reverse the order of items in a list. /// /// @param {list} $list /// /// @return {list} Teh reversed list /// @function list-reverse($list) { @if length($list) == 0 { @return $list; } $result: (); @for $i from length($list) * -1 through -1 { $result: append($result, nth($list, abs($i))); } @return $result; } /// /// Sort numeric items in a list. /// /// The implementation is based on the algorithm on the German Wikipedia article /// about quicksort: https://de.wikipedia.org/wiki/Quicksort#Pseudocode /// /// @param {list} $l /// /// @return {list} Sorted list /// @function quicksort($l, $left: 1, $right: length($l)) { @if $left < $right { $pvr: quicksort-partition($l, $left, $right); $pivot: nth($pvr, 1); $l: nth($pvr, 2); $l: quicksort($l, $left, $pivot); $l: quicksort($l, $pivot + 1, $right); } @return $l; } /// /// @access private /// @function quicksort-partition($l, $left, $right) { $start: true; $i: $left; $j: $right - 1; $pivot: nth($l, $right); @while ($i < $j) or $start { @while (nth($l, $i) < $pivot) and ($i < $right - 1) { $i: $i + 1; } @while (nth($l, $j)>= $pivot) and ($j > $left) { $j: $j - 1; } @if $i < $j { $i-val: nth($l, $i); $l: set-nth($l, $i, nth($l, $j)); $l: set-nth($l, $j, $i-val); } $start: false; } @if nth($l, $i) > $pivot { $i-val: nth($l, $i); $l: set-nth($l, $i, nth($l, $right)); $l: set-nth($l, $right, $i-val); } @return $i $l; } /// /// Try to get the value for the given key from the given map. If it doesn't contain that key, /// return the provided default value instead. /// /// @param {map} $map /// @param {string} $key /// @param {any} $default /// /// @return {any} Either the value assigned to $key or $default /// @function map-get-default($map, $key, $keys...) { $default: nth($keys, length($keys)); $keys: list-slice($keys, 1, length($keys) - 1); @return if(map-has-key($map, $key, $keys...), map-get($map, $key, $keys...), $default); } /// /// Get the contents of a map as a user-friendly string representation. /// /// @param {map} $map /// /// @return {string} /// @function map-print($map) { $output: ''; @each $key, $value in $map { $value-str: ''; @if type-of($value) == map { $value-str: '[ ' + map-print($value) + ' ]'; } @else if type-of($value) == list { $value-str: '[ ' + str-implode($value, ', ') + ' ]'; } @else if type-of($value) == string { $value-str: '\'' + $value + '\''; } @else { $value-str: $value; } @if $output == '' { $output: $key + ': ' + $value-str; } @else { $output: $output + ', ' + $key + ': ' + $value-str; } } @return $output; } /// /// Check if the given selector ends with one of the provided suffixes. /// /// @param {selector} $selector /// @param {selector} $suffixes /// /// @return {bool} `true` if the selector matches at least one suffix, otherwise `false`. /// @function selector-suffix-match($selector, $suffixes) { $match: true; @each $sel in $selector { @if $match { $sel-match: false; @each $suffix in $suffixes { @if not $sel-match { $suf-match: true; @for $i from 1 through length($suffix) { @if $suf-match and (nth($sel, -$i) != nth($suffix, -$i)) { $suf-match: false; } } @if $suf-match { $sel-match: true; } } } @if not $sel-match { $match: false; } } } @return $match; } /// /// Remove the unit from any variable. /// /// @param {any} $n /// /// @return {number} Unit-less variable /// @function strip-unit($n) { @return math.div($n, $n * 0 + 1); } /// /// Convert a pixel value to a rem value. /// /// @param {number} $size - Pixel value to convert /// @param {number} $base [vars.$root-size] - Reference base font size used for conversion /// /// @return {number} Pixel value converted to rem /// @function px-to-rem($size, $base: vars.$root-size) { @return math.div($size, $base) * 1rem; } /// /// A mixin with the sole purpose of letting you use temporary variables without polluting the global namespace. /// /// @content /// @mixin execute { @content; }