Skip to content

Commit 3f82ef5

Browse files
authored
Merge pull request #3263 from matthew-dean/feature/each-2
Fixes #2270 - Adds each() function to Less functions
2 parents 36ec7b8 + c974d4e commit 3f82ef5

File tree

7 files changed

+256
-20
lines changed

7 files changed

+256
-20
lines changed

lib/less/functions/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = function(environment) {
1010
require('./color');
1111
require('./color-blending');
1212
require('./data-uri')(environment);
13+
require('./list');
1314
require('./math');
1415
require('./number');
1516
require('./string');

lib/less/functions/list.js

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
var Dimension = require('../tree/dimension'),
2+
Declaration = require('../tree/declaration'),
3+
Ruleset = require('../tree/ruleset'),
4+
Selector = require('../tree/selector'),
5+
Element = require('../tree/element'),
6+
functionRegistry = require('./function-registry');
7+
8+
var getItemsFromNode = function(node) {
9+
// handle non-array values as an array of length 1
10+
// return 'undefined' if index is invalid
11+
var items = Array.isArray(node.value) ?
12+
node.value : Array(node);
13+
14+
return items;
15+
};
16+
17+
functionRegistry.addMultiple({
18+
_SELF: function(n) {
19+
return n;
20+
},
21+
extract: function(values, index) {
22+
index = index.value - 1; // (1-based index)
23+
24+
return getItemsFromNode(values)[index];
25+
},
26+
length: function(values) {
27+
return new Dimension(getItemsFromNode(values).length);
28+
},
29+
each: function(list, rs) {
30+
var i = 0, rules = [], newRules, iterator;
31+
32+
if (list.value) {
33+
if (Array.isArray(list.value)) {
34+
iterator = list.value;
35+
} else {
36+
iterator = [list.value];
37+
}
38+
} else if (list.ruleset) {
39+
iterator = list.ruleset.rules;
40+
} else if (Array.isArray(list)) {
41+
iterator = list;
42+
} else {
43+
iterator = [list];
44+
}
45+
46+
var valueName = '@value',
47+
keyName = '@key',
48+
indexName = '@index';
49+
50+
if (rs.params) {
51+
valueName = rs.params[0] && rs.params[0].name;
52+
keyName = rs.params[1] && rs.params[1].name;
53+
indexName = rs.params[2] && rs.params[2].name;
54+
rs = rs.rules;
55+
} else {
56+
rs = rs.ruleset;
57+
}
58+
59+
iterator.forEach(function(item) {
60+
i = i + 1;
61+
var key, value;
62+
if (item instanceof Declaration) {
63+
key = typeof item.name === 'string' ? item.name : item.name[0].value;
64+
value = item.value;
65+
} else {
66+
key = new Dimension(i);
67+
value = item;
68+
}
69+
70+
newRules = rs.rules.slice(0);
71+
if (valueName) {
72+
newRules.push(new Declaration(valueName,
73+
value,
74+
false, false, this.index, this.currentFileInfo));
75+
}
76+
if (indexName) {
77+
newRules.push(new Declaration(indexName,
78+
new Dimension(i),
79+
false, false, this.index, this.currentFileInfo));
80+
}
81+
if (keyName) {
82+
newRules.push(new Declaration(keyName,
83+
key,
84+
false, false, this.index, this.currentFileInfo));
85+
}
86+
87+
rules.push(new Ruleset([ new(Selector)([ new Element("", '&') ]) ],
88+
newRules,
89+
rs.strictImports,
90+
rs.visibilityInfo()
91+
));
92+
});
93+
94+
return new Ruleset([ new(Selector)([ new Element("", '&') ]) ],
95+
rules,
96+
rs.strictImports,
97+
rs.visibilityInfo()
98+
).eval(this.context);
99+
100+
}
101+
});

lib/less/functions/types.js

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,8 @@ var isa = function (n, Type) {
2020
throw { type: 'Argument', message: 'Second argument to isunit should be a unit or a string.' };
2121
}
2222
return (n instanceof Dimension) && n.unit.is(unit) ? Keyword.True : Keyword.False;
23-
},
24-
getItemsFromNode = function(node) {
25-
// handle non-array values as an array of length 1
26-
// return 'undefined' if index is invalid
27-
var items = Array.isArray(node.value) ?
28-
node.value : Array(node);
29-
30-
return items;
3123
};
24+
3225
functionRegistry.addMultiple({
3326
isruleset: function (n) {
3427
return isa(n, DetachedRuleset);
@@ -77,16 +70,5 @@ functionRegistry.addMultiple({
7770
},
7871
'get-unit': function (n) {
7972
return new Anonymous(n.unit);
80-
},
81-
extract: function(values, index) {
82-
index = index.value - 1; // (1-based index)
83-
84-
return getItemsFromNode(values)[index];
85-
},
86-
length: function(values) {
87-
return new Dimension(getItemsFromNode(values).length);
88-
},
89-
_SELF: function(n) {
90-
return n;
9173
}
9274
});

lib/less/parser/parser.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,10 +1377,33 @@ var Parser = function Parser(context, imports, fileInfo) {
13771377
},
13781378

13791379
detachedRuleset: function() {
1380+
var argInfo, params, variadic;
1381+
1382+
parserInput.save();
1383+
if (parserInput.$re(/^[.#]\(/)) {
1384+
/**
1385+
* DR args currently only implemented for each() function, and not
1386+
* yet settable as `@dr: #(@arg) {}`
1387+
* This should be done when DRs are merged with mixins.
1388+
* See: https://github.com/less/less-meta/issues/16
1389+
*/
1390+
argInfo = this.mixin.args(false);
1391+
params = argInfo.args;
1392+
variadic = argInfo.variadic;
1393+
if (!parserInput.$char(')')) {
1394+
parserInput.restore();
1395+
return;
1396+
}
1397+
}
13801398
var blockRuleset = this.blockRuleset();
13811399
if (blockRuleset) {
1400+
parserInput.forget();
1401+
if (params) {
1402+
return new tree.mixin.Definition(null, params, blockRuleset, null, variadic);
1403+
}
13821404
return new tree.DetachedRuleset(blockRuleset);
13831405
}
1406+
parserInput.restore();
13841407
},
13851408

13861409
//

lib/less/tree/mixin-definition.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ var Selector = require('./selector'),
88
utils = require('../utils');
99

1010
var Definition = function (name, params, rules, condition, variadic, frames, visibilityInfo) {
11-
this.name = name;
11+
this.name = name || 'anonymous mixin';
1212
this.selectors = [new Selector([new Element(null, name, false, this._index, this._fileInfo)])];
1313
this.params = params;
1414
this.condition = condition;

test/css/functions-each.css

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
.sel-blue {
2+
a: b;
3+
}
4+
.sel-green {
5+
a: b;
6+
}
7+
.sel-red {
8+
a: b;
9+
}
10+
.each {
11+
index: 1, 2, 3, 4;
12+
item1: a;
13+
item2: b;
14+
item3: c;
15+
item4: d;
16+
nest-1-1: 10px 1;
17+
nest-2-1: 15px 2;
18+
nest-1-2: 20px 1;
19+
nest-2-2: 25px 2;
20+
padding: 10px 20px 30px 40px;
21+
}
22+
.each .nest-anon {
23+
nest-1-1: a c;
24+
nest-1-2: a d;
25+
nest-2-1: b c;
26+
nest-2-2: b d;
27+
}
28+
.set {
29+
one: blue;
30+
two: green;
31+
three: red;
32+
}
33+
.set-2 {
34+
one-1: blue;
35+
two-2: green;
36+
three-3: red;
37+
}
38+
.single {
39+
val: true;
40+
val2: 2;
41+
val3: 4;
42+
}
43+
.box {
44+
-less-log: a;
45+
-less-log: b;
46+
-less-log: c;
47+
-less-log: d;
48+
}

test/less/functions-each.less

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
@selectors: blue, green, red;
2+
@list: a b c d;
3+
4+
each(@selectors, {
5+
.sel-@{value} {
6+
a: b;
7+
}
8+
});
9+
10+
.each {
11+
each(@list, {
12+
index+: @index;
13+
item@{index}: @value;
14+
});
15+
16+
// nested each
17+
each(10px 15px, 20px 25px; {
18+
// demonstrates nesting of each()
19+
each(@value; #(@v, @k, @i) {
20+
nest-@{i}-@{index}: @v @k;
21+
});
22+
});
23+
24+
// nested anonymous mixin
25+
.nest-anon {
26+
each(a b, .(@v;@i) {
27+
each(c d, .(@vv;@ii) {
28+
nest-@{i}-@{ii}: @v @vv;
29+
});
30+
});
31+
}
32+
33+
// vector math
34+
each(1 2 3 4, {
35+
padding+_: (@value * 10px);
36+
});
37+
}
38+
39+
@set: {
40+
one: blue;
41+
two: green;
42+
three: red;
43+
}
44+
.set {
45+
each(@set, {
46+
@{key}: @value;
47+
});
48+
}
49+
.set-2() {
50+
one: blue;
51+
two: green;
52+
three: red;
53+
}
54+
.set-2 {
55+
each(.set-2(), .(@v, @k, @i) {
56+
@{k}-@{i}: @v;
57+
});
58+
}
59+
60+
.pick(@a) when (@a = 4) {
61+
val3: @a;
62+
}
63+
.single {
64+
each(true, {
65+
val: @value;
66+
});
67+
@exp: 1 + 1;
68+
each(@exp, {
69+
val2: @value;
70+
});
71+
each(1 2 3 4, {
72+
.pick(@value);
73+
});
74+
}
75+
76+
@list: a b c d;
77+
.box {
78+
each(@list, {
79+
-less-log: extract(@list, @index);
80+
})
81+
}

0 commit comments

Comments
 (0)