Less.js: Permitir que os guardas trabalhem com variáveis ​​indefinidas

Criado em 4 jul. 2013  ·  46Comentários  ·  Fonte: less/less.js

.guard () when (@variable) {
  a {
    color: red;
  }
}

A variável não está definida, portanto, não há saída.

<strong i="8">@variable</strong>: true;

.guard () when (@variable) {
  a {
    color: red;
  }
}

A variável é definida, portanto, a saída:

a {
  color: red;
}
feature request low priority support as plugin

Comentários muito úteis

Houve algum movimento sobre este pedido? Estou ansioso por uma verificação de variável indefinida há algum tempo. No momento, tenho um trabalho em torno que está longe de ser o ideal...

p {
  & when not (@body-text-color = null) {
    color: @body-text-color;
  }
}

mas, para que isso funcione, a variável deve existir e ser definida como null por padrão. Isso seria muito mais eficaz/flexível se eu pudesse apenas verificar se a variável existe em primeiro lugar.

Todos 46 comentários

+1
Isso também pode ser usado para definir cores básicas em bibliotecas.
Imagine o seguinte caso:
Você tem um arquivo menor para um aplicativo específico, por exemplo, o menu principal de um site.
Você importa esse arquivo less no arquivo less principal, onde suas cores básicas são definidas como variáveis.
Agora, se você usar essas cores básicas no arquivo menor do menu principal, elas dependem do arquivo principal.
Se você pudesse verificar a existência das variáveis, poderia retornar às cores definidas pelo módulo.
Aqui está um exemplo de como eu imagino o menu menos arquivo:

@menu-base-color: white;
@menu-base-color: @base-color when (@base-color);
...or...
@menu-base-color: @base-color when isset(@base-color);

+1, já temos istext , isnumber tendo isdefined e isundefined vai ajudar bastante na temática com menos.

Ou seja, use value ou userValue se isdefined

Estou enfrentando muito esse problema ao tentar configurar convenções de temas para UI semântica com MENOS.

+1 nisso - seria muito bom.

+1 nisso para mim também

Não é grande coisa adicionar a função is-defined (espero que apareça em um dos primeiros plugins assim que a v2 for lançada). Embora, para ser honesto, o triste é que aqueles que desejam esse recurso realmente perdem o fato de que ambos os casos de uso acima são solucionáveis ​​(em muitos casos, mesmo com código mais compacto) sem qualquer recurso como esse.

Caso de uso do @InitArt :

// library.less
<strong i="9">@variable</strong>: false;

a when (@variable) {
    color: red;
}

// ......................................
// user.less
<strong i="10">@import</strong> "library.less"
<strong i="11">@variable</strong>: true;

Caso de uso de @nemesis13 :

// library.less
@base-color: green;
@menu-base-color: @base-color;

.menu {
    background-color: @menu-base-color;
}

// ......................................
// user.less
<strong i="16">@import</strong> "library.less"
@base-color: white;
// or:
@menu-base-color: black;
// or whatever...

Ou seja, em resumo: em vez de testar se uma variável está definida, apenas forneça um valor padrão para essa variável, pois os valores das variáveis ​​podem ser substituídos livremente.

Alguém pode responder por que o exemplo de max não é possível para eles? Eu posso apenas
suponha que é porque você não sabe se a variável está definida acima ou
abaixo da importação? Ou é outra coisa?
Não vamos considerar algo que não tenha casos de uso.
A desvantagem de fazer isso é que as pessoas digitando incorretamente uma variável não verão um
erro. Podemos contornar isso com um aviso, mas se você fizer isso em
propósito, você não gostaria de um aviso .. esse é o meu único problema com isso
sugestão.

Isso é útil especialmente ao definir temas para reduzir o tamanho do CSS resultante. Ao criar temas em geral, gostaria de habilitar a configuração de muitas propriedades. No entanto, os usuários podem querer definir apenas alguns deles, deixando o restante das propriedades não definidas. É claro que configurá-los para os valores padrão funcionaria em geral, no entanto, isso sobrecarrega o CSS final.

Considere o seguinte tema base (modelo):

.my-class {
    color: @text-color;
    background-color: @background-color;
    border-color: @border-color;
}

O usuário do tema pode querer definir apenas a cor do texto deixando o restante das propriedades indefinidas. Claro que é possível definir os valores para "inicial", "transparente", "herdar", etc., no entanto, isso aumenta o tamanho do CSS final. Os temas tendem a ter centenas de tais propriedades, de modo que o tamanho pode aumentar consideravelmente.

@JechoJekov

Acho que você está perdendo o ponto desta solicitação de recurso, observe que, para o seu caso de uso, você ainda precisará colocar guarda para cada uma dessas propriedades (daí o raciocínio que sugeri nos dois posts acima ainda se aplica). Portanto, não acho que seu caso de uso adicione nada de novo ao #1400 (basicamente parece que você está sugerindo um recurso diferente como "ignorar propriedade se todos os seus valores estiverem definidos com variáveis ​​indefinidas" ou algo assim, mas é outra grande história).

@seven-phases-max

Talvez seja melhor se for possível definir uma variável para um valor "indefinido" ou "não definido". Definir uma propriedade com esse valor significaria que a propriedade não é renderizada no CSS final. Isso simplifica a sintaxe e permite validar que a variável está declarada.

@JechoJekov

Talvez seja melhor se for possível definir uma variável para um valor "indefinido" ou "não definido".

Como mencionei, esse é outro recurso que precisa de seu próprio ticket (com sua própria discussão).

Apenas alguns esclarecimentos para o roteiro: isso está marcado como "ReadyForImplementation", mas não parece haver consenso neste tópico e parece que @seven-phases-max sugeriu uma alternativa decente. @lukeapage @seven-phases-max, pronto para implementação deve ser removido?

@matthew-dean Não faço ideia. Suponho que aqui "ReadyForImplementation" ainda seja válido, significando algo como "esse recurso parece não ser realmente necessário, mas se alguém vier com um PR implementando tal coisa, não haverá resistência" ou uma espécie de :)

Como é isso? (Sob consideração)

Para mim, "Pronto para Implementação" significa que há um consenso geral (mas não necessariamente unânime) de que _deveria_ ser abordado ou implementado. Acho que "sob consideração" atende à sua definição.

Ainda enfrentando esse problema ao usar o escopo

<strong i="6">@foo</strong>: 'bar';
& { 
  // not sure if <strong i="7">@foo</strong> is defined
}

Adoraria poder usar isDefined

Embora algo como is-defined faça sentido (como um açúcar sintático), não é realmente necessário. Apenas _sempre_ defina @foo (com qualquer valor padrão que você precisar), afinal se você estiver usando uma variável dentro de seus estilos - você _pode_ prever que precisa defini-la, por exemplo:

// in a far far away galaxy of your library default variables:
<strong i="8">@foo</strong>: undefined;

// the code where you need it
& when not(<strong i="9">@foo</strong> = undefined) { 
    // <strong i="10">@foo</strong> is defined by user    
}

// some user code
<strong i="11">@foo</strong>: bar;

Embora eu ache que já escrevi isso nos exemplos acima (só estou tentando me certificar de que a falta desse recurso não pode ser um impedimento para nada. Se você conhece algum caso de uso em que ele esteja o único, por favor, compartilhe).

Estou tentando configurar visualizações de temas no navegador.

Isso significa que eu tenho que usar less.js e substituir a variável @theme padrão com configurações de tempo de execução.

Ala

window.less.modifyVars({
  theme: 'someOtherValue'
})

No meu LESS estou importando um arquivo global theme.less que inclui

<strong i="14">@theme</strong>: undefined;

.setTheme {
  <strong i="15">@theme</strong> : 'default';
}
.setTheme;

Eu também tentei apenas

<strong i="19">@theme</strong>: 'default';

No primeiro caso, o valor de saída é undefined no segundo caso é sempre default independentemente do que está definido em modifyVars

Clique no menu suspenso do tema aqui para obter um exemplo.

A solução atual (que está no site ao vivo) é pegar manualmente o caminho de importação da variável e analisá-lo usando regexp e importar todas essas variáveis ​​com modifyVars , como você pode imaginar, isso é bastante confuso

@seven-phases-max Eu reduzi para um caso de teste muito específico com @import causando problemas.

Em ambos os casos assuma:

Javascript

window.less.modifyVars({
  theme: 'changedValue'
});

No primeiro caso em theme.less, outer está corretamente definido como changedValue

sem componente

<strong i="17">@import</strong> 'theme.less';

sem tema

<strong i="22">@theme</strong>: 'default';
.outer {
  value: @theme; // value is set to changedValue
}

Neste caso de falha, em theme.less @theme é definido como default e não changedValue

sem componente

<strong i="32">@import</strong> 'theme.less';

sem tema

<strong i="37">@theme</strong>: 'default';
<strong i="38">@import</strong> "@{theme}"; // theme is set to default not changedValue

No caso de "var in mixin", o trecho se transforma em:

// defaults:
.setTheme {
   <strong i="6">@theme</strong>: undefined;
}

// custom:
.setTheme {
  <strong i="7">@theme</strong>: 'default';
}
.setTheme;

Embora para modifyVars deve sempre resultar 'someOtherValue' para qualquer trecho que você tentou (porque modifyVars funciona simplesmente anexando uma linha simples <strong i="14">@theme</strong>: 'someOtherValue'; no final do compilado Código fonte). Portanto, se modifyVars não estiver funcionando, o problema provavelmente está em outro lugar.
Eu olhei para o código da página button , mas é muito complexo para dizer rapidamente o que poderia estar errado. À primeira vista, não consigo ver (ou talvez simplesmente não consiga entender) como o código Less é recompilado e o CSS resultante é reaplicado após modifyVars (em snippets simples, é feito via less.refreshStyles seguindo modifyVars ).


Aliás, por precaução, você tem certeza de que não está planejando usar is-default como algo como:

.setTheme when not(is-defined(@theme)) {
  <strong i="26">@theme</strong>: 'default';
}
.setTheme;

? Nesse caso, o problema é que esse código violará diretamente o princípio da avaliação lenta ( is-defined deve retornar false se a variável não estiver definida, mas como _será_ definida nesse escopo imediatamente , is-defined também tem para retornar true -> recursão insolúvel (para mais detalhes sobre por que redefinições condicionais do tipo imperativo não podem ser permitidas com segurança em Less, veja #2072).
Ou seja, poderia realmente funcionar como esperado (a menos que is-defined seja codificado para detectar e resgatar explicitamente tal recursão), mas apenas como um tipo de comportamento não especificado/indefinido, sem consistência com regras normais de visibilidade variável, criando assim mais bagunça.

@jlukic

<strong i="8">@theme</strong>: 'default';
<strong i="9">@import</strong> "@{theme}"; // theme is set to default not changedValue

Sim, essa era uma das minhas suspeitas também (Infelizmente não posso testar isso agora, nem posso adivinhar como isso pode acontecer, mas sim, é possível porque o uso de variáveis ​​em importações é uma verdadeira magia negra. emitir relatório se este for o problema).

Tenho quase certeza de que tem algo a ver com aquela maravilhosa magia negra.

Eu tenho trabalhado com menos arquivos muito simples para depurar, porém ainda não tive a chance de criar um repositório de casos de teste.

Em meus testes, a falha é específica para usar valores {@variable} na importação, mas não nas propriedades css.

Realmente gostando de temas com menos, adoraria superar esses problemas. Aqui está um exemplo de tema divertido com LESS que acabei de mostrar no Meteor DevShop na semana passada (clique no ícone do balde de tinta no canto superior direito)

@jlukic Eu testei e modifyVars funciona para vars em importações como esperado tanto com lessc quanto com less.js (veja codepen demo , é um código um pouco complicado lá, pois teve que import do url assustador da essência , mas suponho que seja igual ao que suas páginas devem executar ... Então, na verdade, parece que o problema está em outro lugar).

Obrigado por dedicar um tempo para produzir o caso de teste para mim. Isso é bem claro. Não tenho certeza do que está acontecendo comigo.. Vou ter que investigar

Houve algum movimento sobre este pedido? Estou ansioso por uma verificação de variável indefinida há algum tempo. No momento, tenho um trabalho em torno que está longe de ser o ideal...

p {
  & when not (@body-text-color = null) {
    color: @body-text-color;
  }
}

mas, para que isso funcione, a variável deve existir e ser definida como null por padrão. Isso seria muito mais eficaz/flexível se eu pudesse apenas verificar se a variável existe em primeiro lugar.

+1

Então, em resumo: a implementação de uma função is-defined(name) (e/ou get-value-of(var-name, fallback-value-if-not-defined) ) dentro de um plugin precisa de menos de 15 linhas de código. Então, faça isso se você for corajoso o suficiente.

+1 para isso. Existe algum progresso nisso como um plugin? Quero dizer, @seven-phases-max você conhece melhor o código do que a maioria dos contribuidores, eu acho. Se requer cerca de 15 linhas de código, por que ainda não foi feito?

Se requer cerca de 15 linhas de código, por que ainda não foi feito?

Porque ninguém está realmente interessado? Cada recurso é legal e "extremamente útil" quando alguém faz isso por você...

você conhece melhor o código do que a maioria dos contribuidores

Isso não significa que eu tenha que colocar algo de meu próprio interesse depois de qualquer coisa de interesse de outra pessoa, desculpe (especialmente se isso for algo encorajador que eu pessoalmente trato como uso indevido/estilo de código ruim).

Para não parecer vazio, para o caso de uso de @ashenden : aqui está a maneira correta de tornar qualquer um de seu conjunto de regras personalizável por "desconhecidos" (obviamente assumindo um caso em que a substituição normal de CSS não é aplicável por algum motivo) sem a falha idéia de "mover-cega-cada-valor-CSS-para-uma-variável-mais-usar-algo-que-você-não-usa" (não vou entrar em detalhes porque está quebrado, pois é uma história para uma longa postagem no blog).

Há um conceito geralmente chamado de "ganchos", então o seguinte "não pode me decidir festival":

// .............................................
// base code:

p {
    & when not (is-defined(@body-text-color)) {
        color: @body-text-color;
    }
    & when not (is-defined(@body-text-color)) {
        background-color: @body-back-color;
    }
}

span {
    & when not (is-defined(@body-text-color)) {
        color: @body-text-color;
    }
    & when not (is-defined(@body-text-color)) {
        background-color: @body-back-color;
    }
}

// .............................................
// customization:

@body-back-color: blue; // how about gradient?

deve ser substituído por:

// .............................................
// base code:

p {
    .body-text();
    .body-back();
    // ^ it's actually better to group all this into a singe entity, e.g. .p()
    // so that as you don't know WHAT or HOW to be customized
    // you don't pre-enforce any limitations and/or hardcoded approach
    // for something YOU do not even write
}

span {
    .body-text();
    .body-back();
}

.body-text() {}
.body-back() {}

// .............................................
// customization:

.body-back() {background-color: blue}

Conte as linhas, conte as possibilidades, conte as limitações.
Ou seja, se você não usa algumas propriedades CSS, por favor, deixe-as em paz para aqueles que irão .

Pessoal, aqui é onde me deparei com isso, por favor, dê uma olhada neste exemplo e me diga se é um caso válido e não pode ser corrigido sem is-undefined.

Eu quero carregar temas em uma biblioteca como um módulo assim:

No meu aplicativo principal, defino a variável @theme : 'yellow' e importo menos a biblioteca.

E aqui está o que menos biblioteca poderia parecer neste caso:

@import 'themes/@{theme}';

corpo {
fundo: @primaryColor;
}

Dessa forma eu poderia ter yellow.less e default.less com @primaryColor : yellow e @primaryColor : red.

No final, posso escrever minha biblioteca usando nomes de variáveis ​​semânticas como primaryColor e fornecer um conjunto de temas com essas variáveis ​​definidas. E o usuário da biblioteca apenas definiria o nome do tema em seu aplicativo e importaria os estilos da biblioteca.

Ok, não importa, parece que entendi, só preciso de um mixin de importação assim:

<strong i="6">@theme</strong>: 'default';

.imports(@theme) when (<strong i="7">@theme</strong> = 'yellow') {
    <strong i="8">@import</strong> "themes/yellow";
}
.imports(@theme) when (<strong i="9">@theme</strong> = 'default') {
    <strong i="10">@import</strong> "themes/default";
}
.imports(@theme);

@waterplea Você pode simplificar seu código para:

<strong i="7">@theme</strong>: default;

.imports(yellow) {<strong i="8">@import</strong> "themes/yellow";}
.imports(default) {<strong i="9">@import</strong> "themes/default";}
.imports(@theme);

observe também as citações redundantes removidas.

Embora para ser honesto eu não consigo ver como <strong i="13">@theme</strong>: yellow; (+ todo o código mixins) é melhor do que apenas a linha explícita <strong i="15">@import</strong> "themes/yellow"; . Ou seja, em primeiro lugar, você está ciente da substituição ? Ou seja, você não precisa ocultar o <strong i="18">@import</strong> "themes/default"; padrão, se o usuário da sua biblioteca precisar aplicar yellow coisas - ela apenas importa seu tema amarelo ( em qualquer lugar após o arquivo da biblioteca principal e, novamente, é o mesma linha) e voilà - sua biblioteca usa valores de variáveis ​​especificados no arquivo amarelo.

Acabei com uma importação opcional de um arquivo que substitui o tema ou alterna para um tema predefinido. Em um projeto que usa a biblioteca, configuramos o webpack para usar um alias para esse arquivo como um módulo de nó se o projeto precisar de um tema diferente. O que escrevi antes ou o que você escreveu não funcionaria no nosso caso, pois estamos construindo um kit de interface do usuário para o Angular e é altamente modular. Podemos importar apenas um botão ou selecionar o controle e ele deve estar ciente do tema que estamos usando com todos os seus estilos de encapsulamento. A solução é fácil de configurar, então estou feliz com isso.

o que você escreveu não funcionaria no nosso caso, pois estamos construindo um kit de interface do usuário para o Angular e é altamente modular.

Eu já vi isso infinitas vezes antes - é um dos problemas mais tristes de Less (des)compreensão - as pessoas simplesmente perdem o princípio fundamental de avaliação Less lazy e, em vez de usar a substituição natural Less, constroem bibliotecas com injeção de arquivos pesada - padrões de customização baseados em hábitos de linguagens com semânticas variáveis ​​diretamente opostas (como PHP, Sass etc.). Deve ser algo mais do que apenas um cara gritando continuamente "Gente, você fez isso errado!"

Eu adoraria acertar, não estou afirmando que entendi tudo :) Importa-se de explicar sua sugestão no meu exemplo? Aqui está o que temos:

Aqui está um exemplo estrutural mínimo:

Biblioteca:

  • import.less
    <strong i="10">@import</strong> 'theme-default';
    <strong i="13">@import</strong> 'mixins';
  • mixins.less
    some mixins here like resetting default browser button styles
  • theme-default.less
    <strong i="20">@primary</strong>: red;
    <strong i="23">@secondary</strong>: blue;
  • tema-secundário.less
    <strong i="27">@primary</strong>: green;
    <strong i="30">@secondary</strong>: yellow;
  • botãoComponente
    <strong i="34">@import</strong> 'import';
    .button { color: @primary; }

Projeto 1 (deve usar o tema padrão):

  • componente
    <strong i="42">@import</strong> 'import';
    body { background: @secondary; }

Projeto 2 (deve usar tema secundário):

  • componente
    <strong i="50">@import</strong> 'import';
    body { background: @secondary; }

Nossa biblioteca é adicionada como um módulo de nó e dentro dos projetos importamos apenas o componente botão da biblioteca. Como é um módulo Angular para esse botão com seu próprio arquivo menor, ele é compilado separadamente do resto do Projeto 1 ou Projeto 2.

Eu estive pensando sobre isso por um tempo e não consegui encontrar uma maneira de organizar menos arquivos para permitir que o Projeto 1 ou o Projeto 2 definam qual arquivo de tema usar ao compilar os componentes e o próprio código do Projetos. Não vejo uma maneira de substituir estilos para o componente de botão de dentro do código do Project.

Então, basicamente, aqui está como eu escrevi import.less:
<strong i="58">@import</strong> 'theme-default';
<strong i="61">@import</strong> 'mixins';
<strong i="64">@import</strong> (optional) '~custom-theme';

E dentro de Projetos, se eu quiser substituir as variáveis ​​de tema para o código do Projetos e os componentes da biblioteca, alias esse arquivo como módulo usando webpack. Este arquivo pode ter uma substituição explícita de, digamos, @primary ou apenas uma mudança para o tema secundário:
<strong i="69">@import</strong> '~myLibrary/styles/theme-secondary';

Então, eu sei sobre a substituição, o problema é - o usuário da biblioteca não tem como entrar entre "qualquer lugar após o arquivo da biblioteca principal" e o código do componente da biblioteca. Eu entendo que o que você diz funcionaria para frameworks sem encapsulamento de estilos, mas para Angular não funcionaria ou eu entendi mal algo em sua solução.

Entendo. Então estamos falando da mesma coisa (apenas em palavras diferentes). Assim que buttonComponent (ou qualquer outro componente) for compilado separadamente, "o mestre de personalização do projeto" continua sendo o arquivo import.less e você fez exatamente o que eu sugeri. (enquanto Project 1 e Project 2 passam a ser apenas mais um componente "qualquer coisa" (ou "tudo em um"?) do mesmo nível que buttonComponent e não bastante "projetos").

Ou seja, desta forma eu diria que você "fez certo" ao meu gosto.

Este problema foi marcado automaticamente como obsoleto porque não teve atividade recente. Será fechado se não ocorrer mais nenhuma atividade. Obrigado por suas contribuições.

@stale Ping.

Tecnicamente, um plugin que implementa o recurso está disponível desde 2015 . Mas, pessoalmente, eu nunca testei, então, por favor, não me culpe se algo der errado (é apenas FYI).

Este problema foi marcado automaticamente como obsoleto porque não teve atividade recente. Será fechado se não ocorrer mais nenhuma atividade. Obrigado por suas contribuições.

Então... Eu dei uma olhada neste tópico e não posso realmente determinar se isso é principalmente casos de pessoas que entendem mal Menos escopo ou não.

Eu realmente não vejo a necessidade de verificar se vars estão definidos. As vars devem ser definidas se forem consumidas.

<strong i="7">@import</strong> "library";  // contains @library-color: blue;

@library-color: red;

.box {
  color: @library-color;
}

Não entendo a necessidade de definir condicionalmente uma propriedade com base no valor _change_. Se a propriedade estiver configurada para uma variável, basta alterar o valor da variável.

Ou seja, se library.less tiver isso:

@library-color: blue;
.box {
  color: @library-color;
}

Tudo o que alguém precisaria fazer para definir sua saída:

<strong i="16">@import</strong> "library";
@library-color: red;

Isso geraria:

.box {
  color: red;
}

O OP e outros nesse segmento entendem esse comportamento?

Eu acho que o caso de uso de "o var existe" é uma boa maneira de fazer sinalizadores. Ou seja, eu não acho que exista um "falso" conceitualmente direto. Ou melhor, é incomum que true seja o único valor "verdadeiro". Em outras palavras, acho que é uma questão de educação, e não de comportamento.

Acho que devemos considerar fechar esse problema, pois isso geralmente é corrigido com uma linha declarando o valor da variável padrão. Embora com Less v3.5 se movendo em direção a um acordo mais permissivo, talvez as variáveis ​​dentro dos guardas sejam avaliadas de forma mais permissiva?

Mas meu voto é não. @the-variable: false; parece ser tudo o que o OP precisa adicionar.


Para quem tiver dúvidas sobre como resolver um problema específico que sinta estar relacionado a isso, sinta-se à vontade para postar no less gitter e no @ me.

Ok fechando, obrigado @calvinjuarez.

O que também aconteceu desde que isso foi aberto é a função if() . Então você pode fazer color: if((@variable), green, red);

@matthew-dean Muito obrigado por escrever com esta dica. Vi a adição de if() nas notas da versão 3.0, mas não fiz a conexão com esse problema, para o qual tive um caso único (mas não o tempo para fazer uma versão reduzida). Novamente, muito apreciado. Bem feito, MENOS equipe.

@kbav Sem problemas! Outra dica em cima dessa dica: quando if() foi introduzido pela primeira vez, exigia parênteses em torno das condições porque estava basicamente "incorporando" um quando guarda (como na parte após when em when (@variable) ). No entanto, isso foi corrigido a partir do Less 3.6, então o exemplo acima você pode escrever sem parênteses (se compilar com a versão mais recente):

color: if(<strong i="10">@variable</strong>, green, red);
Esta página foi útil?
0 / 5 - 0 avaliações