Skip to content

Commit

Permalink
Merge pull request #3263 from matthew-dean/feature/each-2
Browse files Browse the repository at this point in the history
Fixes #2270 - Adds each() function to Less functions
  • Loading branch information
matthew-dean authored Jul 11, 2018
2 parents 36ec7b8 + c974d4e commit 3f82ef5
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 20 deletions.
1 change: 1 addition & 0 deletions lib/less/functions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = function(environment) {
require('./color');
require('./color-blending');
require('./data-uri')(environment);
require('./list');
require('./math');
require('./number');
require('./string');
Expand Down
101 changes: 101 additions & 0 deletions lib/less/functions/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
var Dimension = require('../tree/dimension'),
Declaration = require('../tree/declaration'),
Ruleset = require('../tree/ruleset'),
Selector = require('../tree/selector'),
Element = require('../tree/element'),
functionRegistry = require('./function-registry');

var getItemsFromNode = function(node) {
// handle non-array values as an array of length 1
// return 'undefined' if index is invalid
var items = Array.isArray(node.value) ?
node.value : Array(node);

return items;
};

functionRegistry.addMultiple({
_SELF: function(n) {
return n;
},
extract: function(values, index) {
index = index.value - 1; // (1-based index)

return getItemsFromNode(values)[index];
},
length: function(values) {
return new Dimension(getItemsFromNode(values).length);
},
each: function(list, rs) {
var i = 0, rules = [], newRules, iterator;

if (list.value) {
if (Array.isArray(list.value)) {
iterator = list.value;
} else {
iterator = [list.value];
}
} else if (list.ruleset) {
iterator = list.ruleset.rules;
} else if (Array.isArray(list)) {
iterator = list;
} else {
iterator = [list];
}

var valueName = '@value',
keyName = '@key',
indexName = '@index';

if (rs.params) {
valueName = rs.params[0] && rs.params[0].name;
keyName = rs.params[1] && rs.params[1].name;
indexName = rs.params[2] && rs.params[2].name;
rs = rs.rules;
} else {
rs = rs.ruleset;
}

iterator.forEach(function(item) {
i = i + 1;
var key, value;
if (item instanceof Declaration) {
key = typeof item.name === 'string' ? item.name : item.name[0].value;
value = item.value;
} else {
key = new Dimension(i);
value = item;
}

newRules = rs.rules.slice(0);
if (valueName) {
newRules.push(new Declaration(valueName,
value,
false, false, this.index, this.currentFileInfo));
}
if (indexName) {
newRules.push(new Declaration(indexName,
new Dimension(i),
false, false, this.index, this.currentFileInfo));
}
if (keyName) {
newRules.push(new Declaration(keyName,
key,
false, false, this.index, this.currentFileInfo));
}

rules.push(new Ruleset([ new(Selector)([ new Element("", '&') ]) ],
newRules,
rs.strictImports,
rs.visibilityInfo()
));
});

return new Ruleset([ new(Selector)([ new Element("", '&') ]) ],
rules,
rs.strictImports,
rs.visibilityInfo()
).eval(this.context);

}
});
20 changes: 1 addition & 19 deletions lib/less/functions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,8 @@ var isa = function (n, Type) {
throw { type: 'Argument', message: 'Second argument to isunit should be a unit or a string.' };
}
return (n instanceof Dimension) && n.unit.is(unit) ? Keyword.True : Keyword.False;
},
getItemsFromNode = function(node) {
// handle non-array values as an array of length 1
// return 'undefined' if index is invalid
var items = Array.isArray(node.value) ?
node.value : Array(node);

return items;
};

functionRegistry.addMultiple({
isruleset: function (n) {
return isa(n, DetachedRuleset);
Expand Down Expand Up @@ -77,16 +70,5 @@ functionRegistry.addMultiple({
},
'get-unit': function (n) {
return new Anonymous(n.unit);
},
extract: function(values, index) {
index = index.value - 1; // (1-based index)

return getItemsFromNode(values)[index];
},
length: function(values) {
return new Dimension(getItemsFromNode(values).length);
},
_SELF: function(n) {
return n;
}
});
23 changes: 23 additions & 0 deletions lib/less/parser/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1377,10 +1377,33 @@ var Parser = function Parser(context, imports, fileInfo) {
},

detachedRuleset: function() {
var argInfo, params, variadic;

parserInput.save();
if (parserInput.$re(/^[.#]\(/)) {
/**
* DR args currently only implemented for each() function, and not
* yet settable as `@dr: #(@arg) {}`
* This should be done when DRs are merged with mixins.
* See: https://github.com/less/less-meta/issues/16
*/
argInfo = this.mixin.args(false);
params = argInfo.args;
variadic = argInfo.variadic;
if (!parserInput.$char(')')) {
parserInput.restore();
return;
}
}
var blockRuleset = this.blockRuleset();
if (blockRuleset) {
parserInput.forget();
if (params) {
return new tree.mixin.Definition(null, params, blockRuleset, null, variadic);
}
return new tree.DetachedRuleset(blockRuleset);
}
parserInput.restore();
},

//
Expand Down
2 changes: 1 addition & 1 deletion lib/less/tree/mixin-definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ var Selector = require('./selector'),
utils = require('../utils');

var Definition = function (name, params, rules, condition, variadic, frames, visibilityInfo) {
this.name = name;
this.name = name || 'anonymous mixin';
this.selectors = [new Selector([new Element(null, name, false, this._index, this._fileInfo)])];
this.params = params;
this.condition = condition;
Expand Down
48 changes: 48 additions & 0 deletions test/css/functions-each.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.sel-blue {
a: b;
}
.sel-green {
a: b;
}
.sel-red {
a: b;
}
.each {
index: 1, 2, 3, 4;
item1: a;
item2: b;
item3: c;
item4: d;
nest-1-1: 10px 1;
nest-2-1: 15px 2;
nest-1-2: 20px 1;
nest-2-2: 25px 2;
padding: 10px 20px 30px 40px;
}
.each .nest-anon {
nest-1-1: a c;
nest-1-2: a d;
nest-2-1: b c;
nest-2-2: b d;
}
.set {
one: blue;
two: green;
three: red;
}
.set-2 {
one-1: blue;
two-2: green;
three-3: red;
}
.single {
val: true;
val2: 2;
val3: 4;
}
.box {
-less-log: a;
-less-log: b;
-less-log: c;
-less-log: d;
}
81 changes: 81 additions & 0 deletions test/less/functions-each.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
@selectors: blue, green, red;
@list: a b c d;

each(@selectors, {
.sel-@{value} {
a: b;
}
});

.each {
each(@list, {
index+: @index;
item@{index}: @value;
});

// nested each
each(10px 15px, 20px 25px; {
// demonstrates nesting of each()
each(@value; #(@v, @k, @i) {
nest-@{i}-@{index}: @v @k;
});
});

// nested anonymous mixin
.nest-anon {
each(a b, .(@v;@i) {
each(c d, .(@vv;@ii) {
nest-@{i}-@{ii}: @v @vv;
});
});
}

// vector math
each(1 2 3 4, {
padding+_: (@value * 10px);
});
}

@set: {
one: blue;
two: green;
three: red;
}
.set {
each(@set, {
@{key}: @value;
});
}
.set-2() {
one: blue;
two: green;
three: red;
}
.set-2 {
each(.set-2(), .(@v, @k, @i) {
@{k}-@{i}: @v;
});
}

.pick(@a) when (@a = 4) {
val3: @a;
}
.single {
each(true, {
val: @value;
});
@exp: 1 + 1;
each(@exp, {
val2: @value;
});
each(1 2 3 4, {
.pick(@value);
});
}

@list: a b c d;
.box {
each(@list, {
-less-log: extract(@list, @index);
})
}

0 comments on commit 3f82ef5

Please sign in to comment.