Last active
June 30, 2018 00:54
-
-
Save myakura/9433723 to your computer and use it in GitHub Desktop.
Sass list extra functions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@charset "UTF-8"; | |
// Sass list extra functions v0.0.1 | masataka yakura | MIT License | |
// determines whether an argument is a list type | |
// @return {boolean} | |
@function is-list($arg) { | |
@return type-of($arg) == 'list' or type-of($arg) == 'arglist'; | |
// ISSUE: "false negative" with a single value wrapped in parens | |
// since "Sass doesn't use () as list delimiter characters." | |
// (see https://github.com/nex3/sass/issues/679 ) | |
} | |
// determines if a value is a member of a list | |
// @return {boolean} | |
@function contains($list, $value) { | |
@if not is-list($list) { | |
@warn 'argument error: #{$list}'; | |
@return 'error'; | |
} | |
// index() returns either the index number or false if not found | |
// in Sass number doesn't automatically converts to boolean... | |
// so here it use type-of() to see if the return value is a number | |
@return type-of(index($list, $value)) == 'number'; | |
} | |
// an alternate form for contains() | |
// can be used with keyword arguments to make the code slightly readable | |
// -> e.g. has('grape', $in: $fruits) | |
// @return {boolean} | |
@function has($value, $in) { | |
@return contains($in, $value); | |
} | |
// returns which separator the list uses | |
// @return {string} | |
@function separator($list) { | |
// not a list: just a value | |
@if length($list) == 1 { | |
@return null; | |
} | |
$list_comma: (); | |
$list_space: (); | |
@each $item in $list { | |
// append() doesn't flatten lists | |
$list_comma: append($list_comma, $item, comma); | |
$list_space: append($list_space, $item, space); | |
} | |
@if $list == $list_comma { | |
@return 'comma'; | |
} | |
@else if $list == $list_space { | |
@return 'space'; | |
} | |
@else { | |
@return 'error'; // something goes wrong in the code... | |
} | |
} | |
// make a copy of a list in reversed order | |
// @return {list} | |
@function reverse($list, $separator: auto) { | |
// not a list: just a value | |
@if not is-list($list) { | |
@return $list; | |
} | |
// choose separator for the reversed list | |
// user preference is the highest, then the original separator, lastly space | |
@if not has($separator, (space comma)) { | |
$separator_orig: separator($list); | |
@if has($separator_orig, (space comma)) { | |
$separator: $separator_orig; | |
} | |
@else { | |
$separator: space; | |
} | |
} | |
$i: length($list); | |
$result: (); | |
@while $i > 0 { | |
$result: append($result, nth($list, $i), $separator); | |
$i: $i - 1; | |
} | |
@return $result; | |
} | |
// returns the item at given index | |
// same as nth() except it supports negative index | |
// @return {object} | |
@function at($list, $index) { | |
$idx: if($index < 0, length($list) + 1 + $index, $index); | |
@return nth($list, $idx); | |
} | |
// prepend an item to the list | |
// @return {list} | |
@function prepend($list, $value, $separator: auto) { | |
@if not has($separator, (space comma)) { | |
$separator_orig: separator($list); | |
@if has($separator_orig, (space comma)) { | |
$separator: $separator_orig; | |
} | |
@else { | |
$separator: space; | |
} | |
} | |
// if $value is a list containing more than one item | |
@if length($value) > 1 { | |
$value: append((), $value); | |
} | |
@return join($value, $list, $separator); | |
} | |
// delete the item at given index, which can be nagative | |
// @return {list} | |
@function delete-at($list, $index) { | |
$length: length($list); | |
// convert negative index to positive index | |
$idx: if($index < 0, $length + 1 + $index, $index); | |
// index out of range | |
@if $idx <= 0 or $idx > $length { | |
@warn 'index out of range: #{$index}'; | |
@return $list; | |
} | |
// create a new list | |
$result: (); | |
$i: 1; | |
@while $i <= $length { | |
@if $i != $index { | |
$result: append($result, nth($list, $i)); | |
} | |
$i: $i + 1; | |
} | |
@return $result; | |
} | |
// ----- Experimental ----- | |
// form a list by its arguments | |
// can take space-separated values | |
// @return {list} | |
@function list($args...) { | |
// here, $args is an arglist | |
// to make it a normal list, join with an empty list | |
$result: join((), $args); | |
// in case of space-separated items are passed | |
// the length of $result is 1, which is not useful | |
@if length($args) == 1 { | |
@return nth($result, 1); | |
} | |
@else { | |
@return $result; | |
} | |
} | |
// returns a list of numbers in a certain range | |
// @return {list} | |
@function range($first, $second: null, $third: null, $separator: auto) { | |
$result: (); | |
$start: 0 !default; | |
$stop: null; | |
$step: 1 !default; | |
// ISSUE: no way to do like range(x, null, 2) | |
// todo: resolve separator | |
// only one argument passed | |
@if type-of($first) == 'number' and $second == null and $third == null { | |
$stop: $first; | |
} | |
// two or three arguments passed | |
@if type-of($first) == 'number' and type-of($second) == 'number' { | |
// start and stop | |
$start: $first; | |
$stop: $second; | |
// if the step argument passed | |
@if type-of($third) == 'number' and $third != 0 { | |
$step: $third; | |
} | |
} | |
$i: $start; | |
@while $i < $stop { | |
$result: append($result, $i, $separator); | |
$i: $i + $step; | |
} | |
@return $result; | |
} | |
// ----- working on ... ----- | |
// slices at given index/indices, optionally a step can be passed | |
// indices can be negative | |
// @return {list} | |
@function slice($list, $start: 1, $stop: length($list) + 1, $step: 1, $separator: null) { | |
// todo: | |
// ✔ 1. support step (positive) | |
// ✔ 2. support separator | |
// 3. work on negative start and stop indices so do negative step | |
$length: length($list); | |
$result: (); | |
// for supporting negative index we need to resolve indices | |
$r_start: false; | |
$r_stop: false; | |
$r_step: false; | |
@if not has($separator, (space comma)) { | |
$separator_orig: separator($list); | |
@if has($separator_orig, (space comma)) { | |
$separator: $separator_orig; | |
} | |
@else { | |
$separator: space; | |
} | |
} | |
// start index | |
@if $start < 0 { | |
$start: $length + 1 + $start; | |
} | |
@if $start > $length { | |
$start: $length + 1; | |
} | |
@if $start == null { | |
$start: 1; | |
} | |
// stop index | |
@if $stop < 0 { | |
$stop: $length + 1 + $stop; | |
} | |
@if $stop > $length { | |
$stop: $length + 1; | |
} | |
@if $stop == null { | |
$stop: $length + 1; | |
} | |
// step | |
@if $step == 0 { | |
@warn 'step cannot be zero: #{$step}'; | |
@return 'error'; | |
} | |
@if $step == null { | |
$step: 1; | |
} | |
// define a direction | |
@if $step > 0 and $start < $stop { | |
$i: $start; | |
@while $i < $stop { | |
$result: append($result, nth($list, $i), $separator); | |
$i: $i + $step; | |
} | |
} | |
@if $step < 0 and $start > $stop { | |
$i: $stop; | |
@while $i < $start { | |
$result: append($result, nth($list, $i), $separator); | |
$i: $i - $step; | |
} | |
} | |
// check if the start, stop, step are right | |
// @if ($stop - $start) / $step < 0 { | |
// @warn 'error'; | |
// @return $result; | |
// } | |
// $list: (0 1 2 3 4 5 6 7 8 9) | |
// 0, 9, 2 -> ok, 0 9, (0 2 4 6 8) | |
// 0, -5, 4 -> ok, 0 5, (0 4) | |
// 3, 8, -1 -> ng | |
// -2, -7, 3 -> ng, 8 3 | |
// -2, -7, -3 -> ok, 8 3, (8 5) | |
// -2, 2, -4 -> ok, 8 2, (8 4) | |
// -2, 2, 1 -> ng, 8 2 | |
// -8, 5, 1 -> ok, 2 5, (2 3 4) | |
// okay if | |
// * step(+) and start(+) < stop(+) | |
// * step(+) and start(r) < stop(r) // r means resolved | |
// The slice of s from i to j with step k is defined as the sequence of items with | |
// index x = i + n*k such that 0 <= n < (j-i)/k. | |
// In other words, the indices are i, i+k, i+2*k, i+3*k and so on | |
// stopping when j is reached (but never including j). | |
// ✔ If i or j is greater than len(s), use len(s). | |
// If i or j are omitted or None, they become “end” values (which end depends on the sign of k). | |
// ✔ Note, k cannot be zero. If k is None, it is treated like 1. | |
// all set | |
// @if $_start < $_stop { | |
// $i: $_start; | |
// @while $i < $_stop { | |
// $result: append($result, nth($list, $i), $separator); | |
// $i: $i + $_step; | |
// } | |
// } | |
@return $result; | |
} | |
// returns each type of an item in the list | |
// @return {list} | |
@function types($list) { | |
$result: (); | |
@each $item in $list { | |
$result: append($result, type-of($item)); | |
} | |
@return $result; | |
} | |
// // determines if all items in list are the same to the value | |
// // @return {boolean} | |
// @function are-all($list, $value) { | |
// $result: true; | |
// @each $item in $list { | |
// $result: $result and ($item == $value); | |
// } | |
// @return $result; | |
// } | |
// // determines if one or more items in the list are the same to the value | |
// // @return {boolean} | |
// @function are-some($list, $value) { | |
// $result: false; | |
// @each $item in $list { | |
// $result: $result or ($item == $value); | |
// } | |
// @return $result; | |
// } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment