Skip to content
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

Idea: Support declarative list expansion into selectors (syntax not decided yet) #1694

Closed
dantman opened this issue Nov 30, 2013 · 7 comments

Comments

@dantman
Copy link

dantman commented Nov 30, 2013

Looking at the new docs I see that list variables are supported.

@list: apple, satsuma, banana, pear;

I expect one of the most common use cases for lists is outputting a series of rules, such as these.

.fruit-apple {
  content: "Just a test.";
}
.fruit-satsuma {
  content: "Just a test.";
}
.fruit-banana {
  content: "Just a test.";
}
.fruit-pear {
  content: "Just a test.";
}

The current LESS syntax to do this however, is completely insane. ((Though I do understand it was an unintentional "feature"))

@list: apple, satsuma, banana, pear;

.loop(@index) when (@index <= length(@list)) {
    @item: extract(@list, @index);
    .fruit-@{item} {
        content: "Just a test.";
    }
    .loop(@index + 1);
}
.loop(1);

I think we should add some nice clean declarative syntaxes to handle the common cases where you'd think you want a for loop. ie: Something declarative like the @when idea was, rather than the if/else ideas which weren't.

I haven't thought of the best syntax for this yet, and I'd like other ideas too.

The first and simplest possible syntax would be to auto-expand lists inside of selectors:

@list: apple, satsuma, banana, pear;

.fruit-@{list} {
  content: "Just a test.";
}

However this isn't that great because there's no obvious way to use the current value inside of the selector (ie: It would be an ugly hack to support content: "Just a test @{item}"; or content: "Just a test @{fruit}";) and it's rather non-obvious that this selector expands to four different rules instead of one.

Another idea might be a syntax like this:

@list: apple, satsuma, banana, pear;

@expand @fruit in @list {
  .fruit-@{fruit} {
    content: "Just a test @{fruit}";
  }
}

This one is rather straightforward, it properly supports an item variable, while also becoming flexible enough to do more than just .fruit-@{fruit}. Though it needs a bit of bike-shedding over whether to call it @expand or something like @for, @foreach, or @each, whether it should be @fruit in @list or @list as @fruit.

Or maybe given the current style of when() maybe a syntax like this:

@list: apple, satsuma, banana, pear;

& each (@list as @fruit) {
  .fruit-@{fruit} {
    content: "Just a test @{fruit}";
  }
}

You could also make it a little more like when (...) by putting it directly on the .fruit-* selector though that might read a little backwards (since the variable in the selector is declared in the each that comes 'after'.

@list: apple, satsuma, banana, pear;

.fruit-@{fruit} each (@list as @fruit) {
  content: "Just a test @{fruit}";
}
@seven-phases-max
Copy link
Member

Well, personally I don't see anything "insane" there, "verbose" maybe. Actually we can get more syntax sugar by abstracting the whole thing into a generic mixin like this. Which can also be vastly improved if a feature like passing a block into a mixin via variable was added into LESS.

So if it takes into considering of how this or similar (e.g. for etc.) features could be improved, I'd rather prefer to do it via #965 and abstracting a particular construction via a mixin rather than going a "feature -> build-in directive" way, just because a single "generic callback support" feature (i.e. this is what #965 essentially is) opens a whole new world of higher level abstraction possibilities via mixins for many other language constructions and use-cases (i.e. not only things like .for and .for-each, but also things like "normal" @keyframes, more customizable @media, more generic conditional mixins etc. etc.). While still keeping the implementation modest.


Regardless of all above, the last syntax you've proposed is actually quite heavy from an implementation point of view, notice that it not only introduces two new keywords (each and as ) but also adds a new concept of implicit variable expansion into the outer scope (i.e. @fruit in .fruit-@{fruit}).

Also see #1421, #1465, #1481, #869 etc.

@lukeapage
Copy link
Member

I mostly agree with @seven-phases-max though I also don't mind the idea of

@list: apple, satsuma, banana, pear;
.fruit-@{list} {
  content: "Just a test.";
}

though having said that its a breaking change. it goes to

.fruit-apple, satsuma, banana, pear {
  content: "Just a test.";
}

at the moment.

@seven-phases-max
Copy link
Member

Btw, I also like the idea of .fruit-@{list} especially since it's already partially supported, e.g.:

apple, satsuma, banana, pear {
    .fruit-& {
        content: "Just a test.";
    }
}

But indeed there's not too much use of this in a real project since we cannot get these values into a variable/property (so it can only generate rulesets with the same content). But if we could, that also may open some interesting possibilities, taking certain selectors tricks into account (for example like this or this)

@lukeapage
Copy link
Member

I've been thinking about this and I would really like this in 2.0.0

@list: apple, satsuma, banana, pear;

.fruit-@{fruit} {
  content: "Just a test @{fruit-item}";
}

along with

list: numeric-list(1, 5);
// output
list: 1, 2, 3, 4, 5;

and some kind of selector parsing so you can do

list: $(.mysel, div > span);

and then a) any comma seperated list that gets passed to selector interpolation, it duplicates the ruleset n times and provides a variable "@listname-item".

I think this is far more declarative and easy to use and worth the breaking change of people who maybe are doing

a: div, span;
@{a} {
  color: red;
}

but I really don't think too many people will be doing that

@matthew-dean
Copy link
Member

Agreed at least with the premise that we should eliminate more loop use cases and the general principle of this request. Not sure about syntax, though.

@seven-phases-max
Copy link
Member

Closing as duplicate of #1421.

@matthew-dean
Copy link
Member

FYI - This is now supported by this PR - #3217

Works in the form of:

@list: apple, satsuma, banana, pear;
@{list} {
  .fruit-& {
    content: "Just a test.";
  }
}

Outputs:

.fruit-apple,
.fruit-satsuma,
.fruit-banana,
.fruit-pear {
  content: "Just a test.";
}

matthew-dean added a commit that referenced this issue Jun 16, 2018
…3217)

* Adds passing test from #3098
* Added passing test example from #1817
* Allow lists to be re-evaluated as selectors (Fixes #1694)
matthew-dean added a commit that referenced this issue Jun 25, 2018
…no.2) (#3227)

* Fix element to selector list conversion, passing all tests!
* Add passing test from #3098
* Added passing test example from #1817
* Allow lists to be re-evaluated as selectors (Fixes #1694)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants