How do I convert number to words while looping in LESS? - less

I am exploring a way to convert numbers in words while looping through LESS.
Right now I have following code
.margin(#n, #i:1) when (#i <= #n) {
.space-#{i} {
margin: 0rem+ #i ;
}
.space-#{i}-top {
margin-top: 0rem + #i ;
}
.space-#{i}-bottom {
margin-top: 0rem + #i ;
}
.margin(#n, (#i+1));
}
.margin(2);
Which produces:
.space-1 {
margin: 1rem;
}
.space-1-top {
margin-top: 1rem;
}
.space-1-bottom {
margin-top: 1rem;
}
.space-2 {
margin: 2rem;
}
.space-2-top {
margin-top: 2rem;
}
.space-2-bottom {
margin-top: 2rem;
}
However, I wanted to achieve class names as space-one instead space-1 and so on.
How do I utilize array in LESS? Thanks.

See extract function. E.g.
#names:
one,
two,
three,
four,
five;
.margin(3);
.margin(#i: 1) when (#i > 0) {
.margin(#i - 1);
#name: extract(#names, #i);
.space-#{name} {
margin: #i * 1rem;
}
}

Related

LESS array loop through loop: double loop case

How to double loop in LESS to generate a set of mixins such as:
.p_0_0 {
padding: 0rem 0rem;
}
.p_1_0 {
padding: 1rem 0rem;
}
.p_0_1 {
padding: 0rem 1rem;
}
.p_1_1 {
padding: 1rem 1rem;
}
It is better to use built-in tools...
Since LESS 3.x version LESS has each function, see more here. In this case, we can use this function to its fullest:
#steps: {
0: 0rem;
1: 1rem;
};
each(#steps, .(#valueX, #keyX, #indexX) {
each(#steps, .(#valueY, #keyY, #indexY) {
.p_#{keyY}_#{keyX} {
#{valueP}: #valueY #valueX;
}
})
})
Which generates:
.p_0_0 {
padding: 0rem 0rem;
}
.p_1_0 {
padding: 1rem 0rem;
}
.p_0_1 {
padding: 0rem 1rem;
}
.p_1_1 {
padding: 1rem 1rem;
}

Merge two or more rules in different files in Less

I'm relatively new to Less (and precompilers).
I have three files, defined as this:
//global.less
html, body {
height: 100%;
}
body {
margin: 0;
padding: 0;
}
//...
//global-flex.less
body {
display: flex;
flex-direction: column;
}
main {
flex-grow: 1;
}
//...
And a third file (main.less) where I import both of those files.
#import "global.less";
#import "global-flex.less";
//Other imports and files also happens, but they are not relevant to this question.
When i compile main.less, my output is:
html, body {
height: 100%;
}
body {
margin: 0;
padding: 0;
}
body {
display: flex;
flex-direction: column;
}
main {
flex-grow: 1;
}
I want the output to "merge" the two matching "body" rules, like this:
html, body {
height: 100%;
}
body {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
}
main {
flex-grow: 1;
}
How can I achieve this?

Nested loops in Less

I have some nested loops in Less.
It should create css rules for padding with 3 sizes and 4 direction.
Code sample:
#sizes: normal, small, large;
#size-normal: 1em;
#size-small: 0.8 * #size-normal;
#size-large: 1.2 * #size-normal;
.l-padding {
#directions: top, left, right, bottom;
.s(#i: length(#sizes)) when (#i > 0) {
.s((#i - 1));
.d(#j: length(#directions)) when (#j > 0) {
.d((#j - 1));
#dir: extract(#directions, #j);
#s: extract(#sizes, #i);
#size: "size-#{s}";
&_#{dir}_#{s} {
.l-padding-mixin(#dir, ##size);
}
}
.d();
}
.s();
}
I don't know where is the problem but it compile to many duplacates. However each independet loop do their job.
.l-padding_top_normal {
padding-top: 1em;
}
.l-padding_left_normal {
padding-left: 1em;
}
.l-padding_right_normal {
padding-right: 1em;
}
.l-padding_bottom_normal {
padding-bottom: 1em;
}
.l-padding_top_normal {
padding-top: 1em;
}
.l-padding_left_normal {
padding-left: 1em;
}
.l-padding_right_normal {
padding-right: 1em;
}
.l-padding_bottom_normal {
padding-bottom: 1em;
}
.l-padding_top_normal {
padding-top: 1em;
}
...
.l-padding_top_normal {
padding-top: 1em;
}
.l-padding_top_small {
padding-top: 0.8em;
}
.l-padding_left_small {
padding-left: 0.8em;
}
.l-padding_right_small {
padding-right: 0.8em;
}
.l-padding_bottom_small {
padding-bottom: 0.8em;
}
.l-padding_top_normal {
padding-top: 1em;
}
.l-padding_left_normal {
padding-left: 1em;
}
.l-padding_right_normal {
padding-right: 1em;
}
.l-padding_bottom_normal {
padding-bottom: 1em;
}
...
To many duplicates. Can any one help with this?
UPD
thanks to #Harry find solution:
.l-padding-mixin(#direction, #size: 1em) {
padding-#{direction}: #size;
}
#sizes: normal, small, large;
#size-normal: 1em;
#size-small: 0.8 * #size-normal;
#size-large: 1.2 * #size-normal;
//==== layouts ====
.l-padding {
#directions: top, left, right, bottom;
#i: length(#sizes);
#j: length(#directions);
.d(#j) when (#j > 0) {
.d((#j - 1));
#dir: extract(#directions, #j);
#s: extract(#sizes, #i);
#size: "size-#{s}";
&_#{dir}_#{s} {
.l-padding-mixin(#dir, ##size);
}
}
.-(#i) when (#i > 0) {
.-((#i - 1));
.d(#j);
} .-(#i);
}

Use computed value in element using less.js

Have the following bit of code:
generate-margins(4);
.generate-margins(#n, #i: 1) when (#i =< #n) {
.marginleft#{i} {
margin-left: (5px * #i);
}
.marginright(5*#{i}) {
margin-right: (5px * #i);
}
.generate-margins(#n, (#i + 1));
}
Which gives me:
.marginleft1 {
margin-left: 5px;
}
.marginright(5*1) {
margin-right: 5px;
}
.marginleft2 {
margin-left: 10px;
}
.marginright(5*2) {
margin-right: 10px;
}
.marginleft3 {
margin-left: 15px;
}
.marginright(5*3) {
margin-right: 15px;
}
.marginleft4 {
margin-left: 20px;
}
.marginright(5*4) {
margin-right: 20px;
}
What I wanted was:
.marginright5 {... }
.marginright10 {... }
...
How can use computed value in element name? I tried using all string functions with no luck.
Did some more testing and came up with this:
.generate-margins(#n, #i: 1) when (#i =< #n) {
#l : #i*5;
.marginleft#{l} {
margin-left: (5px * #i);
}
.marginright#{l} {
margin-right: (5px * #i);
}
.generate-margins(#n, (#i + 1));
}
Would still like to know if there is another way. Cheers.

Streamline LESS mixin

I have some LESS for making margins based on the side being passed (top, right, bottom, left or all):
.margin(#px,#side) when (#side = top) {
(){ margin-top: ~"#{px}px"; }
}
.margin(#px,#side) when (#side = right) {
(){ margin-right: ~"#{px}px"; }
}
.margin(#px,#side) when (#side = bottom) {
(){ margin-bottom: ~"#{px}px"; }
}
.margin(#px,#side) when (#side = left) {
(){ margin-left: ~"#{px}px"; }
}
.margin(#px,#side) when (#side = all) {
(){ margin-top: ~"#{px}px";
margin-right: ~"#{px}px";
margin-bottom: ~"#{px}px";
margin-left: ~"#{px}px"; }
}
My question is that if I have an ID like this:
#testfeature {
.margin(10px,l);
.margin(10px,r);
}
Then LESS compiles it like this:
#testfeature {
margin-left:10px;
}
#testfeature {
margin-right:10px;
}
How do I get it to compile like this:
#testfeature {
margin-left:10px;
margin-right:10px;
}
Get rid of the unnecessary () { ... }'s that are wrapping all of your mixin styles. They're causing the selectors to be interpreted oddly and separating them into different selections, rather than joining everything under one selection:
.margin(#px,#side) when (#side = top) {
margin-top: ~"#{px}px";
}
.margin(#px,#side) when (#side = right) {
margin-right: ~"#{px}px";
}
.margin(#px,#side) when (#side = bottom) {
margin-bottom: ~"#{px}px";
}
.margin(#px,#side) when (#side = left) {
margin-left: ~"#{px}px";
}
.margin(#px,#side) when (#side = all) {
margin-top: ~"#{px}px";
margin-right: ~"#{px}px";
margin-bottom: ~"#{px}px";
margin-left: ~"#{px}px";
}
You could probably also drop the ~"#{px}px" in favor of simply #px, also the last mixin should probably be:
.margin(#px, #side) when (#side = all) {
margin: #px;
}
To get them to "group" you need to create a nested and grouped mixin. Below is a grouped mixin function, "streamlined" for margin setting.
It takes anywhere from 1 to 8 parameters; always in pairs of position then value (except as noted below; it can accept any order of position and values of auto or inherit are allowed).
Passing a single "position" just sets a 0px margin on that position.
Defaults non-unit numbers to px, but keeps explicitly set units as passed.
Passing a single parameter that is number value will set all the margins to that number equally.
LESS Mixin
.setMargins(#s1: ~'', #v1: 0, #s2: ~'', #v2: 0, #s3: ~'', #v3: 0, #s4: ~'', #v4: 0) {
.setPos(top, #T) {
.setNum() when (isnumber(#T)) {
margin-top: #T * 1px;
}
.setNum() when not (isnumber(#T)){
margin-top: #T;
}
.setNum();
}
.setPos(right, #R) {
.setNum() when (isnumber(#R)) {
margin-right: #R * 1px;
}
.setNum() when not (isnumber(#R)) {
margin-right: #R;
}
.setNum();
}
.setPos(bottom, #B) {
.setNum() when (isnumber(#B)) {
margin-bottom: #B * 1px;
}
.setNum() when not(isnumber(#B)) {
margin-bottom: #B;
}
.setNum();
}
.setPos(left, #L) {
.setNum() when (isnumber(#L)) {
margin-left: #L * 1px;
}
.setNum() when not (isnumber(#L)) {
margin-left: #L;
}
.setNum();
}
//allows all to be called with one number or value
.setPos(#A, 0) when (isnumber(#A)) {
margin: #A * 1px;
}
.setPos(auto, 0) {
margin: auto;
}
.setPos(inherit, 0) {
margin: inherit;
}
//default null if no valid side given
.setPos(#other, 0) {}
//call all possible positions
.setPos(#s1, #v1);
.setPos(#s2, #v2);
.setPos(#s3, #v3);
.setPos(#s4, #v4);
}
Examples
.setMargins(right); produces margin-right: 0px;
.setMargins(right, 15); produces margin-right: 15px;
.setMargins(left, 10em); produces margin-left: 10em;
.setMargins(top, 12, right, 10); produces: margin-top: 12px; margin-right: 10px;
.setMargins(25); produces: margin: 25px;
.setMargins(auto); produces: margin: auto;
So you use it in a selector:
LESS
#testfeature {
.setMargins(left, 10, right, 10);
}
CSS Output
#testfeature {
margin-left: 10px;
margin-right: 10px;
}