Skip to content

Instantly share code, notes, and snippets.

@myakura
Last active June 30, 2018 00:54
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save myakura/9433723 to your computer and use it in GitHub Desktop.
Save myakura/9433723 to your computer and use it in GitHub Desktop.
Sass list extra functions
@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