Ecossistema de íconesO eQuantic.UI oferece um ecossistema de ícones abrangente e extensível. Ele suporta vários conjuntos de ícones populares prontos para uso, todos integrados por um modelo de componente unificado.
Para usar ícones no seu projeto, instale o pacote do conjunto desejado e o pacote de componentes.
dotnet add package eQuantic.UI.Components
dotnet add package eQuantic.UI.Lucide
Nada para registrar. Um pacote é um catálogo de IconGlyph: você nomeia o glifo que quer e o compilador embute só aquele:
using eQuantic.UI.Lucide;
Glyph(LucideIcons.Search)
Glyph(LucideIcons.Check, size: 20)
Os tamanhos são a lista branca do §07 (16 / 20 / 24 / 32). Para um tamanho fora dessa lista, o Vector(glyph, size) desenha os mesmos caminhos sem o contrato de ícone, e o Vector(glyph, size, height: h) dá a uma figura não quadrada o aspecto próprio dela (desde a 0.2.0-preview.28; o conector de um diagrama foi o caso). O Icon(...) é a fábrica do enum Icons curado do próprio framework. Veja DeclarativeSurface para entender por que os dois têm nomes diferentes. Como você nomeia o glifo, nada resolve em tempo de execução e dois pacotes nunca podem discordar sobre um nome: não há busca a se perder.
eQuantic.UI.Lucide
Ícones bonitos e consistentes (conjunto padrão).
eQuantic.UI.Heroicons
Ícones SVG feitos à mão pelos criadores do Tailwind CSS.
eQuantic.UI.RadixIcons
Um conjunto nítido de ícones 15×15 desenhado pelo time do Radix UI.
eQuantic.UI.TablerIcons
Mais de 4200 ícones pixel-perfect para design web.
eQuantic.UI.FontAwesome6.Solid
O conjunto de ícones mais popular do mundo (variante Solid).
eQuantic.UI.FontAwesome6.Regular
Ícones Font Awesome 6 Regular.
eQuantic.UI.FontAwesome6.Brands
Ícones de marcas do Font Awesome 6.
eQuantic.UI.Phosphor
Uma família flexível de ícones para interfaces, diagramas e mais.
eQuantic.UI.SimpleIcons
Mais de 3000 ícones SVG de marcas populares.
eQuantic.UI.BootstrapIcons
Biblioteca livre, de alta qualidade e open source, com mais de 2.000 ícones.
eQuantic.UI.Iconoir
Um conjunto de alta qualidade com mais de 1500 ícones open source.
eQuantic.UI.MaterialSymbols
Os Material Symbols do Google: 16.284 glifos, o maior pacote aqui.
Glifos de pacote, write-once
Um pacote de ícones é um catálogo de IconGlyph: dados de caminho neutros em relação ao alvo, então a mesma entrada serve o realizador web como SVG inline e o atlas nativo como geometria de glifo. Desenhe um com Glyph:
using eQuantic.UI.MaterialSymbols;
Glyph(MaterialSymbolsIcons.PlayArrowRounded)
Glyph(MaterialSymbolsIcons.ExpandMore, size: 20)
Glyph, não Icon: o Icon é a fábrica do enum Icons curado do próprio framework, e a superfície declarativa não tem sobrecargas. Veja DeclarativeSurface. Os tamanhos são a lista branca do §07 (16 / 20 / 24 / 32); para qualquer coisa fora dela, o Vector(glyph, size) desenha os mesmos caminhos sem o contrato de ícone, e um argumento height: liberta a caixa do quadrado. Você paga pelo que nomeia. O eqc embute apenas os glifos que uma página realmente referencia: uma página que usa dois glifos do Material Symbols emite um módulo de 627 bytes, de um catálogo cuja fonte tem ~8 MB. O tamanho do pacote é um custo de build, não de download.
Desde 0.2.0-preview.7
Uma nota sobre o Material Symbols
O Google publica o Material Symbols como uma fonte variável com os eixos FILL, wght, GRAD e opsz, e os três cortes (outlined, rounded, sharp) são o que aqueles eixos resolvem. Uma display list de GPU não sabe desenhar uma fonte, então este pacote carrega os caminhos já resolvidos: o corte vive no nome do ícone (Home, HomeRounded, HomeSharp) e não há eixo FILL, então X e XOutline são duas entradas. Os nomes são os do Iconify em PascalCase, então material-symbols:play-arrow-rounded é PlayArrowRounded.
Ele também é o pacote mais pesado que o framework publica (~13 MB de assembly contra os 8 do Phosphor) — o que era um conselho sobre qual pacote escolher, e já não é. Veja abaixo.
Um pacote entrega os glifos que a sua app nomeia
Uma app publicada carrega os ícones que USA, não o pacote que referenciou. Medido no maior deles: o assembly do pacote passa de 14.558.720 bytes para 7.680, e um publish que o carregava passa de 35.256 KB para 20.952 KB — 41% mais pequeno, para uma app que desenha um punhado de glifos de entre 16.284.
Nada para configurar e nada para nomear. O que mudou foi a FORMA em que os pacotes são gerados: um inicializador de campo compila para um único construtor estático da classe inteira, e um construtor estático não é removível — portanto um pacote de campos viajava inteiro, todo ele, para desenhar cinco glifos. Uma propriedade de corpo-expressão é um corpo de método separado, e o trimmer de IL descarta os que ninguém chama:
// antes: um construtor estático, 16.284 glifos, não removível
public static readonly IconGlyph Camera = new("camera", "M14.5 4h-5L7 7", …);
// agora: um corpo de método por glifo, e o trimmer guarda os que você nomeia
public static IconGlyph Camera => new("camera", "M14.5 4h-5L7 7", …);
Precisa do PublishTrimmed, que é o que faz o trimmer correr de todo. Sem ele o pacote tem o tamanho que sempre teve, portanto "escolha um pacote menor" continua a ser o conselho para um publish sem trimming. Com ele, o tamanho do pacote deixa de ser razão para escolher um conjunto em vez de outro — escolha os ícones que ficam bem.
Desde 0.2.0-preview.47
Arte a partir de um arquivo .svg
Desde 0.2.0-preview.29
Um ícone é uma path num tint só. Um LOGO são várias formas, cada uma na cor que o designer escolheu: isso é o Drawing, e é o nó que você aponta para um arquivo.
Ponha o arquivo na pasta Assets do app e ele vira um membro tipado:
// Assets/mark.svg → Vectors.Mark
column.Add(new Drawing(Vectors.Mark, width: 240, label: "eQuantic"));
A leitura acontece em tempo de build: nada lê arquivo em runtime em nenhum dos alvos, porque o browser não consegue e um app nativo estaria carregando a arte duas vezes. Um membro tipado em vez de uma string de caminho, pelo mesmo motivo que o resto deste SDK gera superfícies: Vectors.Mark é erro de compilação quando está errado, e Drawing("mark.svg") é um espaço em branco em runtime.
Um número só mantém o aspecto da própria arte (um ícone assume quadrado porque é isso que um ícone é; um logo espremido é um logo errado). Dê os dois para letterbox ou esticar de propósito.
Na largura da coluna. A largura é um SizeValue, então SizeValue.Fill toma a largura que o pai oferece e o aspecto da arte decide a altura: um mapa numa coluna responsiva, que nenhum número diz para toda janela. A web escreve width: 100% e aspect-ratio; o Photon resolve a largura como resolve a de um canvas e pinta na caixa que diagramou. Uma altura que você der continua vencendo. Um fill numa largura que o pai dimensiona pelo próprio conteúdo não tem o que preencher e fica com largura zero, como um canvas, e um desenho recusa SizeValue.Hug, já que não tem conteúdo para abraçar.
column.Add(new Drawing(Vectors.Mark, SizeValue.Fill, label: "eQuantic"));
Desde 0.2.0-preview.61
Tingir. Uma forma que o arquivo deixou como currentColor é a arte pedindo para ser tingida, então passe tint: e ela segue, enquanto toda forma que escolheu a própria cor ignora. Na web a palavra currentColor chega ao markup e o tint vai como color do CSS, então a cascata pode responder; no Photon cada forma é rasterizada e pintada na cor dela.
new Drawing(Vectors.Mark, 240, tint: theme.TextPrimary) // a metade monocromática segue o tema
O subset, nomeado como o do Markdown. Entendidos: viewBox, path, rect (com rx/ry), circle, ellipse, line, polyline, polygon, g aninhado, transform (translate / scale / rotate / skew / matrix, assado na path data em vez de carregado), fill, stroke, stroke-width, fill-rule, as três opacidades, as mesmas propriedades escritas num atributo style, e cores como #rgb / #rrggbb / #rrggbbaa / rgb() / rgba() / none / currentColor / os nomes básicos. Descartados, de propósito: patterns, clipPath, mask, filter, use, image e text: cada um precisa de um paint server que este modelo não comporta, ou de uma fonte. Essas formas chegam FALTANDO em vez de preenchidas com uma cor que ninguém escolheu, e o build diz qual arquivo pulou.
Desde 0.2.0-preview.31
linearGradient e radialGradient são entendidos, com as paradas deles, em qualquer unidade (o objectBoundingBox, que é o padrão, é uma FRAÇÃO da caixa da própria forma; o userSpaceOnUse mede na grade do viewBox), e o href é seguido quando dois gradientes compartilham a paleta. Os defs são lidos numa passagem própria, então um preenchimento pode nomear um gradiente que o arquivo declara depois dele, que é o que metade dos exportadores do mundo escreve.
Cada alvo pinta a corrida do jeito que ele sabe nativamente. No web a arte carrega os próprios defs e cada forma nomeia um com url(#…), então o browser interpola no tamanho em que a caixa acabar; o id é um hash do próprio gradiente, então uma corrida usada por duas formas é declarada uma vez e o servidor e o gêmeo cliente chegam na mesma string. No Photon a mesma máscara de alpha que o rasterizador já fez é preenchida com a paint de gradiente do engine, que é o caminho que o TEXTO em degradê já usava: nenhum comando de desenho novo e nenhum ponto de entrada novo no shader.
Duas cercas: uma corrida atravessa com duas paradas no Photon (a primeira e a última, porque a paint do engine interpola entre um par; multi-parada é fatia dele), e o gradientTransform, o spreadMethod e o ponto focal de um radial são descartados. Um paint server que o leitor não entende continua não pintando NADA, que é o que todo paint server fazia antes disto.
Como um ícone chega ao ecrã
Um ícone é um VALOR, não um serviço. O Icons.Heart é um IconGlyph — um nome, um path, e se é traçado ou preenchido — e o nó Icon desenha-o. O realizador web escreve um <svg> inline; o realizador Photon rasteriza o path uma vez e mete-o em cache. Não há registo nenhum a consultar nem nada para registar.
Já foi uma interface de provedor (IIconProvider em eQuantic.UI.Core, a resolver nomes para componentes através de DI), e isso foi-se com o modelo de componentes pré-write-once na 0.2.0-preview.48. Um provedor só sabe responder no alvo para que foi escrito, e um ícone tem de ser o mesmo ícone em três.
Duas formas, ambas sem provedor:
•
Um IconGlyph que você constrói, quando já tem os dados do path:
public static class MeusIcones
// Uma propriedade e não um campo, de propósito: um pacote de campos compila para um único
// construtor estático e viaja inteiro, que é do que trata a nota sobre trimming acima.
public static IconGlyph Logo => new("logo", "M12 2L2 7l10 5 10-5-10-5z", IconGlyphStyle.Fill);
Icon(MeusIcones.Logo, size: IconSize.Lg)