-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mixin overloads from detached ruleset caller scope aren't applied #2556
Comments
The result is expected, since within a DR, the parent scope (incl. global) has higher precedence than the caller scope. Just the same way as it is with ordinal rulesets and parametric mixins (yes, it's the same #1316, #2435 etc. story), e.g.: @foo: global;
@dr: {
dr: @foo;
};
.ruleset {
ruleset: @foo;
}
.mixin() {
mixin: @foo;
}
div {
@foo: caller;
@dr(); // global
.ruleset(); // global
.mixin(); // global
} I.e. in you example Actually from your example it looks like you tried to overcome "parent > caller" thing for the variable ( .Method() {
content: 'global';
}
.Foo() {
.Method();
}
.Call() {
.Method() {
content: 'caller';
}
.Method(); // -> 'caller' only (local > global)
.Foo(); // -> 'global' only (global > caller)
// but in both cases only one definition is used
}
div {
.Call();
} |
I'm marking this as "consider closing" since this is currently expected behaviour and possible improvements to be a subject for another ticket. Feel free to enlighten more details about your specific use-case though - maybe we could come up with a workaround. I would offer something but the example is too abstract to try to approach it with different method. For instance it's not so evident why .Method() {
content: 'Not inverted';
}
.CallInverted() {
.Method();
.Method() {
content: 'Inverted';
}
}
.Call(@callback) {
@callback();
}
a {
.CallInverted();
}
b {
.Call({.Method});
}
c {
.Method();
} |
This doesn't work because invertCombinator doesn't pass through to the inner callback. See less/less.js#2556
Here is I'm actually trying to do. I'm implementing logic gates in CSS, using LESS mixins to generate CSS selectors for chained operations. The problem is that implementing XOR requires that I invert each argument ( I worked around the initial problem described in this issue (making the first level invert) by using a separate overload for the top-level call (the one that doesn't have |
Here is a minimal example of what I'm currently stuck at: .Method(root) {
@invert: false;
.Method();
}
.Method() when not (@invert) {
content: 'Not inverted';
}
.Method() when (@invert) {
content: 'Inverted';
}
.CallInverted(@callback) {
@invert: true;
@callback();
}
.Call(@callback) {
@invert: false;
@callback();
}
a {
.CallInverted({
.Method();
inner {
.Call ({ .Method(); });
}
});
}
b {
.Call({
.Method();
inner {
.CallInverted({ .Method(); });
}
});
}
c {
.Method(root);
} Actual: a {
content: 'Inverted';
}
a inner {
content: 'Inverted';
}
b {
content: 'Not inverted';
}
b inner {
content: 'Not inverted';
}
c {
content: 'Not inverted';
} Desired: a {
content: 'Inverted';
}
a inner {
content: 'Not inverted';
}
b {
content: 'Not inverted';
}
b inner {
content: 'Inverted';
}
c {
content: 'Not inverted';
} |
I suppose I could work around this by passing a nesting level to each |
Here is a simpler interesting impossible example that would help: .StartChain(@inner) {
@index: 0;
@inner();
}
.Chain(@inner, @i: @index) {
@index: @i + 1;
@inner();
}
a {
.StartChain({
i: @index;
b {
.Chain({
i: @index;
c {
.Chain({
i: @index;
});
}
});
}
});
} |
A completely different approach I thought of for my actual problem is to pass nested lists instead of DRs: .op(
(#a:checked, xor, #b:checked), xor, (#c:checked, and, #d:checked));
{ #result { ... } }
); The implementation would look like this: #private {
.call(@combinator, @selector, @content) when (isList(@selector)) {
// isList() doesn't exist?
&@{combinator} {
.op(extract(@selector, 1), extract(@selector, 2), extract(@selector, 3));
// I wish I could just expand the list into the arguments like JS apply()
}
}
} This would essentially create a DSL. However, I can't actually create nested lists like that. (?) |
Also, if you make DRs support parameters (#2270), this entire problem will be instantly solved. |
I see. Well, yes, that's pretty complicated stuff to suggest something w/o understanding all aspects of it.
Sure, but notice #2270 (comment). .StartChain(@inner) {
@inner(0);
}
.Chain(@inner, @i: @index) {
@inner(@i + 1);
}
a {
.StartChain(???(@index) {
i: @index;
b {
.Chain(???(@index) {
i: @index;
c {
.Chain(???(@index) {
i: @index;
});
}
});
}
});
} Now taking the mentioned trick (that has essentially equal-to-2270-feature syntax except extra redundant pair of .StartChain(@inner) {
@inner();
.func(0);
}
.Chain(@inner) {
@inner();
.func(@i + 1);
}
a {
.StartChain({.func(@i) {
i: @i;
b {
.Chain({.func(@i) {
i: @i;
c {
.Chain({.func(@i) {
i: @i;
}});
}
}});
}
}});
} |
You can actually, in simple case you can use whitespace separated lists, see for example. .op(
~'#a:checked' xor ~'#b:checked', xor, ~'#c:checked' and ~'#d:checked';
{ #result { ... } }
); Alternatively you can use something like this to be able to define an arbitrary-level nested lists with a single statement. |
That's (adding more The problem is that I will probably add a second variable passed in this fashion, and I don't want to have to modify every callsite each time I add a variable. I'd like to just pass a stack depth, and use variable variables to form a call stack with "array"s of each variable I use. However, it appears to be impossible to set a variable variable? @invertCombinator: %("invertCombinator%s", @i); // Syntax error :(
@@invertCombinator: true;
...
when (@@invertCombinator) |
Re: Your second comment, I have more than two layers of nesting, and I cannot easily use plugins. |
No, strictly speaking, because of Less's declarative nature, variables can't be set at all. They are all constants actually, and each time you write In other words, if there's a need for some counter it should use anything but the variable name... |
I know; that's exactly what I want. I want to define a new constant (which must have a unique name) for each level of the call stack. |
Sad... because after looking at all that code I started to think that doing all this via plain classical functions would be just enormously more clean and simple than all these DRs... |
I finished implementing the workaround; everything works now. |
OK, closing since the initial problem is an expected behaviour and the ticket for related improvements is #2270 (as well as other tied features in other threads). |
Source:
Current Output:
Desired Output:
Context: I want to make a set of mixins that are sometimes called in callbacks, and I want to make each one use a different definition from some callsites of the callbacks (the
.CallInverted()
mixin).I can't find any other way to do this, because the guard variable must be defined when called in global scope, and I can't override its value when called from a mixin.
The text was updated successfully, but these errors were encountered: