Building of a string, which depends on variable number of parameters - css

My question is: how to build a string in Less, which depends on variable number of parameters. For instance, I would like to make a mixin, which helps me to write #font-face CSS rules. So I need to build src:... fonts property for arbitrary number of formats (.eot, .ttf, .oft, .woff, .woff2, .svg) of my font. Here is my Less loop to process all font formats in list:
// #some-types - it is my font formats list, just smth. like 'otf', 'eot',...
// #old-src-value - it is string with src for my font from previous loop
// #counter - it is my loop counter
.make-font-src(#some-types; #old-src-value; #counter) when (#counter <= length(#some-types)) {
// Here we get next font format from #some-types
#font-type: extract(#some-types, #counter);
// Used for building 'format("opentype")' - like part of src value string
.get-font-format(#font-type);
// Building a part of src value string for this iteration
#src-value: e('#{old-src-value}, url("#{font-path}#{font-filename}.#{font-type}") format("#{font-format}")');
// Recursive call of this mixin for looping
.make-font-src(#some-types; #src-value; (#counter + 1));
}
So I'm stuck in how to fetch complete src value string, when all font formats will be processed in the loop? Also please refer to this codepen demo.

As mentioned in my comment, this would not cause a recursive definition error because you have assigned the value to a different variable and then used it. However, it seems like Less is processing the property-value setting line as soon as the first iteration of the loop is completed. You can verify this by changing the counter value for the first iteration itself to 2 or more.
One solution (a better approach to the problem in my opinion) would be to use the property merging with comma feature and set the property-value pair directly like in the below snippet:
.make-font-src(#some-types; #counter) when (#counter <= length(#some-types)) {
#font-path: 'some/test/path/';
#font-filename: 'Arial';
#font-type: extract(#some-types, #counter);
src+: e('url("#{font-path}#{font-filename}.#{font-type}") format("#{font-type}")');
.make-font-src(#some-types; (#counter + 1));
}
div.test {
.make-font-src('eot', 'woff', 'svg'; 1);
}
This when compiled would produce the following output:
div.test {
src: url("some/test/path/Arial.eot") format("eot"),
url("some/test/path/Arial.woff") format("woff"),
url("some/test/path/Arial.svg") format("svg");
}

Finally, I found my own solution: if we add special 'getter' mixin with guard, which triggered on last iteration of the loop, we can get full src value from our loop mixin.
.getter(#cond; #list) when (#cond = length(#list)) {
#font-src-full: #src-value;
}
Here is a fiddle with demo

Related

Coloring cells in Vaadin Grid that uses OpenCSV

I am attempting conditional coloring/formatting for a Vaadin Grid that sources its data from a file (using OpenCSV) rather than a POJO. I was looking at Change color of cell in Grid (Vaadin) to see if this could be modified to support CSV instead, but have so far come up empty.
Can this be done with the cell style generator method? I am referencing https://vaadin.com/blog/read-and-display-a-csv-file-in-java for the reading of the csv data.
You are looking at the questions/blogs from different versions; the SO
question is about Vaadin 8 - the blog about Vaadin Flow (10 and up, but
most likely v14 in this case).
Styling rows and/or columns is documented in the Grid:
https://vaadin.com/docs/latest/components/grid/#styling-rows-and-columns
You can style individual cells based on the data, for example, to
highlight changes or important information.
...
grid.setClassNameGenerator(person -> {
if (person.getRating() >= 8)
return "high-rating";
if (person.getRating() <= 4)
return "low-rating";
return null;
});
Note: there is also setClassNameGenerator for Column.
Going off the CSVReader and Vaadin documentation examples with the generic Person class, you can do something like:
grid.addColumn(str->str[columnIndex])
.setKey(header)
.setHeader(header)
.setClassNameGenerator(str -> {
if (Integer.valueOf(str[columnIndex]) > 8){
return "condition1";
}
if (Integer.valueOf(str[columnIndex]) < 8){
return "condition2";
}
return null;
});
Similar to what is done in the cookbook example, you can then define those conditions in the corresponding css file.

Stylus: how to create a CSS variable with a Mixin interpolation

How can a Stylus Mixin interpolate for both sides of the definition of a variable, such as:
--MyVariable: MyVariable
In the compiled CSS, --MyVariable should remain as the variable name, whereas the second variable should be the numerical value computed for this Stylus variable defined elsewhere.
I will have many such pairs of a CSS variable and a Stylus variable. Now, even though I could just manually write them down in the Stylus file, I would like to have a Mixin that allows me to create them by writing the shared part of the names just once as the Mixin's single argument, such as:
VariablePair(MyVariable-1) // --MyVariable-1: MyVariable-1
VariablePair(MyVariable-2) // --MyVariable-2: MyVariable-2
VariablePair(MyVariable-3) // --MyVariable-3: MyVariable-3
I tried:
VariablePair(VariableName)
--{VariableName}: VariableName
VariablePair(MyVariable)
It didn't parse.
I realized that MyVariable without '' would come as an actual value that couldn't be affixed to --. But, adding '' to it as VariablePair('MyVariable') would result in the right side of the definition itself becoming a string instead of the Stylus variable for computation.
I tried the different combinations of the presence/lack of the brackets and '' and $ as well as concatenations, but none of them seem to work. '--' + VariableName + ': ' + VariableName (with/without the brackets/$) isn't working, it either doesn't parse or does parse but without creating a line in the compiled CSS.
Is there a solution to this?
unquote works for this:
VariablePair(name, val)
{unquote('--' + name)}: val
.selector
VariablePair('fontcolor', red)
color: var(--fontcolor)
will output
.selector {
--fontcolor: #f00;
color: var(--fontcolor);
}

Is there a way to print SASS value to output?

Suppose we have:-
$value: 13.37;
$length: $value + 0em;
Now i wanted to check the value of $length.
Is there anything similar to Javascript's Console.log?
If you want it to appear on the page itself, I believe you could attach it as the value of a pseudo-element. Something like:
body::before{ content: "#{$length}"}
Additionally, sass includes #error, #warn, #debug directives that will log things to the terminal to verying degrees of of noisiness.
#error "the value of $length is `#{$length}`"
More info on those can be found here

SASS: How to ignore a loop

I have two SCSS arrays ($color_options and $object_options).
I want to use SCSS to loop through both arrays to combine values from both of them.
I have created a function to do this and everything works fine. However, there are times when I want to only to loop through the colour array and ignore the object array. How can I do this?
Here is my code(*):
#function generic-avatar-hidden($color_options: $color_options, $object_options: $object_options) {
$bg: compact();
#each $ov in $object_options {
$o-css-selector: nth($ov, 1);
#each $cv in $color_options {
$c-css-selector: nth($cv, 1);
$assetname: $o-css-selector + $c-css-selector;
$bg: join($bg, image-url("#{$root-directory}/#{$brand}/#{$product}/#{$type}/#{$product}-#{$type}-#{$path-pre}#{$assetname}#{$path-post}.#{$ext}"), comma);
}
// Close CV
}
// Close OV
#return $bg;
}
Steps I have taken:
I have tried wrapping all the code to do with the object_options in #if $object_options != null.
This works, but when $object_options is supposed to be use, the function will only loop through the last item in the $object_options array.
Code Examples
(*) This is a cut down version of my code. The full examples can be found here:
Full version which doesn't use #if $object_options != null
Updated version with #if $object_options != null
**Expected Output **
(This is based on the full version of the code linked above)
http://pastebin.com/gVykec8X
Basically, $color_options & $object_options all have 5 elements inside of them.
The SCSS code takes elements from $color_options and $object_options and combines them. However, if $object_options is set to null, then there will be no combination and only elements from $color_options will be used.
sass doesn't support break or continue statements. You should use #if to implement branching.

How can I search CSS with Perl?

First question from a long time user.
I'm writing a Perl script that will go through a number of HTML files, search them line-by-line for instances of "color:" or "background-color:" (the CSS tags) and print the entire line when it comes across one of these instances. This is fairly straightforward.
Now I'll admit I'm still a beginning programmer, so this next part may be extremely obvious, but that's why I came here :).
What I want it to do is when it finds an instance of "color:" or "background-color:" I want it to trace back and find the name of the element, and print that as well. For example:
If my document contained the following CSS:
.css_class {
font-size: 18px;
font-weight: bold;
color: #FFEFA1;
font-family: Arial, Helvetica, sans-serif;
}
I would want the script to output something like:
css_class,#FFEFA1
Ideally it would output this as a text file.
I would greatly appreciate any advice that could be given to me regarding this!
Here is my script in full thus far:
$color = "color:";
open (FILE, "index.html");
#document = `<FILE>`;
close (FILE);
foreach $line (#document){
if($line =~ /$color/){
print $line;
}
}
Since you asked for advice (and this isn't a coding service) I'll offer just that.
Always use strictures and warnings:
use strict;
use warnings;
Always check the return value of open calls:
open(FILE, 'filename') or die "Can't read file 'filename' [$!]\n";
Use the three-arg form of open and lexical filehandles instead of globs:
open(my $fh, '<', 'filename') or die "Can't read file 'filename' [$!]\n";
Don't slurp when line-by-line processing will do:
while (my $line = <$fh>) {
# do something with $line
}
Use backreferences to retrieve data from regex matches:
if ($line =~ /color *: *(#[0-9a-fA-F]{6})/) {
# color value is in $1
}
Save the class name in a temporary variable so that you have it when you match a color:
if ($line =~ /^.(\w+) *\{/) {
$class = $1;
}
Well, this is not as simple as it seems.
CSS classes can be defined in many ways. For example,
.classy {
color: black;
}
Good luck using a line-by-line approach for parsing that.
Actually, my first approach would be searching CPAN. This looks promising:
CSS - Object oriented access to Cascading Style Sheets (CSS)
Edit:
I installed HTML::TreeBuilder and CSS modules from CPAN and concocted the following aberration:
use strict;
use HTML::TreeBuilder;
use CSS;
foreach my $file_name (#ARGV) {
my $tree = HTML::TreeBuilder->new; # empty tree
$tree->parse_file($file_name);
my $styles = $tree->find('style');
if ($styles) {
foreach my $style ($styles) {
# This is an insane hack, not guarantee
# to work in the future.
my $css = CSS->new;
$css->read_string(join "\n", #{$style->{_content}});
print $css->output;
}
}
$tree = $tree->delete;
}
This thing only prints all the CSS selectors from list of HTML files, but nicely formatted so you should be able to continue from here.
For yet another way to do it, you can ask perl to read from the file in sections other than lines, for example by using the "}" as a record separator.
my $color = "color:";
open (my $fh, '<', "index.html") || die "Can't open file: $!";
{
local $/ = "}";
while( my $section = <$fh>) {
if($section =~ /$color(.*)/) {
my ($selector) = $line =~ /(.*){/;
print "$selector, $section\n";
}
}
Untested! Also, this of course assumes that your CSS neatly ends its sections with a } on a line on it's own.
I'm not having problems with the regex's but rather with the capture of data. Since CSS elements are typically multi-line, I need to figure out how to create an array between the { and } with each linebreak as a delimiter for list items.
No, you don't.
For the problem as stated, the only lines of interest will be those containing either a class name or a color definition, and possibly also lines containing } to mark the end of a class. All other lines can be ignored, so there's no need to put them into an array.
Since class specifications cannot be nested[1], the last seen set of class names will always be the active set of classes. Therefore, you need only record the last seen set of class names and, when a color specification is encountered, print those class names.
There are still some potential difficulties handling cases in which a specification block is shared by multiple classes (.foo, .bar, .baz { ... }), which may or may not be spread across multiple lines, or if multiple attributes are defined on the same line, but dealing with those should follow fairly easily from what I've already laid out. Depending on your input data, you may also need to include a basic state engine to keep track of whether you're in comments or not.
[1] i.e., Although you can have semantically-nested classes, such as .foo and .foo .bar, they have to be specified in the CSS file as
.foo {
...
}
.foo .bar {
...
}
and cannot be
.foo {
...
.bar {
...
}
}
Although I have not tested the code below, but something like this should work:
if ($line =~ m/\.(.*?) \{(.*?)color:(.*?);(.*)/) {
print "$1,$3\n";
}
You should invest some time learning regular expressions for Perl.

Resources