-
Notifications
You must be signed in to change notification settings - Fork 1
Components pt BR
🌐 Esta página em: English · Português
A biblioteca de componentes (eQuantic.UI.Components) é write-once: cada componente é autorado
uma vez em C# contra o vocabulário abstrato do eQuantic.UI.Primitives, e realizado por alvo, DOM +
CSS no web, pixels de GPU pelo Photon no nativo. Não são "duas APIs parecidas", é
literalmente a mesma classe. A arquitetura (camadas, realizadores, paridade de hidratação) está
documentada em Componentes write-once.
| Tipo | Descrição |
|---|---|
StatelessComponent |
Depende só das propriedades dele. Sobrescreva Build(ComponentContext) para devolver a árvore. |
StatefulComponent |
Guarda estado interno persistente; o SetState dispara uma reconstrução. |
O ComponentContext é a porta sem modos por onde entra tudo que depende do alvo: o tema, a
Densidade, a medição de texto (MeasureText/MonoAdvance) e os serviços de
capacidade tomados por construtor (IThemeController, ITextClipboard, capacidades de dispositivo).
Os componentes autoram tokens, nunca cores resolvidas, então uma árvore construída realiza em
claro ou escuro.
Os eventos são propriedades tipadas (OnPressed, OnChanged, OnSelectionChanged, …) carregando
delegates C#; o mesmo handler roda nos dois alvos.
A camada web de baixo nível (
HtmlElement,ClassName,DynamicElement) continua existindo para as saídas de emergência só-web. Veja Estilo.
Ações e entradas: Button, IconButton, TextInput, SearchField, Checkbox, Switch,
RadioGroup, Select, Slider, Stepper, SegmentedControl.
Superfícies e exibição: Card, Divider, Badge, Chip, Avatar, Banner, ProgressBar,
EmptyState, Skeleton.
Navegação: Tabs, AppBar, BottomNavigation, NavigationRail, Breadcrumb, Pagination,
PageIndicator, Menu, Drawer. A barra e a rail são os MESMOS destinos para duas larguras de
janela: entregue uma lista de NavItem às duas dentro de um AdaptiveNode e o shell acompanha a
janela, barra embaixo no telefone e rail na borda inicial no tablet, sem nenhum listener e sem um
segundo estado. A barra aceita de 3 a 5 destinos, a rail de 3 a 7 (ela é mais alta do que a barra é
larga); passando disso a resposta é um Drawer nas duas.
Desde 0.2.0-preview.29
Os destinos do rail ficam onde o Alignment disser (o topo do Material por padrão, o centro quando
há um header). Ele recebe o MainAlign do próprio vocabulário em vez de um enum com cara de rail, e
esperava por uma fatia do transpilador, não por uma de design: um componente que repassa a
propriedade enum dele para um slot do vocabulário precisava que essa propriedade chegasse ao gêmeo
como UNIÃO e não como string pelada.
Desde 0.2.0-preview.31
O ListDetail é a terceira forma, e o primeiro componente do catálogo que é ele próprio ADAPTATIVO:
uma lista ao lado de um detalhe numa janela larga, um painel de cada vez num telefone.
Desde 0.2.0-preview.31
O app é dono dos DADOS: um nó de lista, e um nó de detalhe quando algo é escolhido. O componente é dono da REGRA, que é a parte que todo app ia escrever de novo, e ia escrever duas vezes, porque as duas larguras parecem duas telas diferentes e não são:
new ListDetail(
list: Inbox.List(_selected, i => SetState(() => _selected = i)),
detail: _selected is { } chosen ? Inbox.Detail(chosen) : null,
onBack: () => SetState(() => _selected = null))
{
ListTitle = "Inbox",
Title = _selected is { } open ? Inbox.Items[open].Title : null,
Placeholder = EmptyState(Icons.Mail, "Escolha uma mensagem"),
}Um campo responde pelas duas formas. Num telefone, Detail nulo é um LUGAR: a lista é a tela, e o
voltar desescolhe; acima do TwoPaneFrom (840dp, a classe Expanded da spec) é apenas uma ausência,
preenchida pelo Placeholder. A afordância de voltar é desenhada SÓ no painel compacto: na largura,
os dois painéis estão visíveis, e um controle que te devolve para algo que você já vê é exatamente
como um layout adaptativo passa a parecer quebrado. O
dotnet new equantic-native --shell list-detail escafolda exatamente a chamada acima.
Cercas honestas, as duas sobre a troca de painel no compacto, que é uma navegação e não só um
re-layout: o foco do teclado fica onde estava em vez de ir para o painel que chegou (o vocabulário
ainda não tem um "foque esta subárvore" neutro de alvo), e o gesto de voltar da plataforma não está
ligado ao OnBack (uma questão do host, por shell).
Overlays: Dialog, Toast, BottomSheet (arrastar para dispensar), Popover, Tooltip
(revelado por hover, só ponteiro, zero JS).
Listas e dados: List/ListItem, ListView (com reciclagem, abaixo), Accordion, Table,
DataTable, Spreadsheet (abaixo).
Uma linha tem 52 / 68 / 88 de altura para uma, duas e três linhas (o SubtitleLines = 2 pede a
terceira, que existe para um subtítulo de configuração poder ocupar uma segunda frase), e a List
recua cada divisor até onde a linha DE CIMA começa o texto dela: 16 + leading + gap, para que o fio
nunca corte um ícone. Ela deriva esse número do slot: um Icon carrega o tamanho dele e um Avatar
o degrau dele, e só um slot que o app construiu por conta própria precisa declarar LeadingWidth.
Desde 0.2.0-preview.31
Uma cerca, nomeada porque é lacuna e não preferência: uma lista SELECIONÁVEL ainda é uma sequência de
paradas de Tab individuais, não uma parada única com ↑/↓ e Espaço. Por isso o ListItem.Selected
declara aria-current="page" (destino de navegação) e não aria-selected num listbox: a parada
que caminha pertence ao contêiner, e até a List compô-la, uma linha option sairia da ordem de Tab
sem nada para recolocá-la.
Interação por toque: PullToRefresh, SwipeableRow.
Código: CodeBlock (só leitura) e CodeEditor (cursor, seleção, teclado). O modelo completo e as
duas superfícies estão documentados na página do Editor de código.
Cada componente vem com pinos do realizador web, imagens golden nativas (claro + escuro), fixtures
transpiladas pinadas executadas no vitest, e o showroom vivo (/ e /shared no DefaultUIDashboard:
SSR + identidade de hidratação + interação verificados de ponta a ponta pela suíte do Playwright). Os
sistemas que vêm junto com a biblioteca: o sistema de movimento por transição de estado (entrada/saída
de Presence), o pipeline de ponteiro (hover, arrastar para dispensar com folga/cancelamento/deslize), o
compositor de rolagem (fixação Sticky de verdade), os overlays ancorados, e a gestão de foco modal
(abaixo).
O contrato de interação do design system (spec §10) é implementado pelos realizadores, então cada componente o herda em vez de improvisar por conta. O que segue é o comportamento publicado.
Desde 0.2.0-preview.24
Não existe um sexto slot de cor: o preenchimento de hover de um controle preenchido é derivado (o
ponto médio de canal de Base → Pressed, exposto como VariantColors.Hover), e as variantes
discretas (Outline, Ghost, IconButton Standard) fazem hover em SurfaceSubtle, o preenchimento que o
estado pressionado delas já usa. O Button e o IconButton autoram isso; no web, toda regra :hover
que o motor atômico emite vive atrás de @media (hover: hover), então o hover grudento de toque de um
browser não acha regra nenhuma, e no Photon um movimento marcado como toque pula o hover inteiramente. O
pressionado sempre vence o hover, e a entrada/saída de hover pega o mesmo degrau de 100 ms do feedback
de pressionado.
Desde 0.2.0-preview.24
O RadioGroup e o SegmentedControl seguem o contrato nativo do <input type=radio>: o grupo é um
único role="radiogroup" focável carregando o nome do grupo, as setas movem a seleção e dão a volta nas
pontas (Baixo é próximo num radiogroup: ordem de leitura, não valor de slider), e cada opção é um
role="radio" declarando aria-checked, fora da ordem de tabulação, ainda pressionável por ponteiro. O
vocabulário carrega isso como PressableRole.Radio, e a forma foi adotada pelos compostos desde
então: os itens do Tabs são role="tab" declarando aria-selected (uma aba é escolhida, não
marcada) sob a parada única da tira, e as linhas de Menu/Select são menuitem/option (abaixo).
Desde 0.2.0-preview.24
Um Overlay modal e aberto (que é sobre o que o Dialog, o Drawer e o BottomSheet são
construídos) é um diálogo no web: role="dialog", aria-modal="true" (a página atrás fica inerte para tecnologia
assistiva), e tabindex="-1" para que a própria camada seja o fallback de foco inicial (uma folha sem
nada focável foca a folha, conforme a spec). Enquanto está aberta, Tab e Shift+Tab circulam dentro dela e
uma guarda de focusin puxa de volta qualquer coisa que escape por outra rota; quando ela fecha
(removida, fechada mantendo a montagem pelo Overlay.Motion, ou no meio da animação de saída) o foco
volta ao elemento que a abriu, em todo caminho de fechamento. Diálogos empilham em último a entrar,
primeiro a sair. O Toast nunca prende (ele é a camada não modal), e o Popover deliberadamente não é
preso: um popover é uma superfície não modal, então o Tab sai dele.
Desde 0.2.0-preview.29
A camada também diz o NOME dela (Overlay.Label, já que o Dialog passa o título como aria-label),
uma confirmação destrutiva é um role="alertdialog" anunciado assertivamente (Overlay.Alert), e
o foco inicial prefere a ação SEGURA: o Dialog marca o botão Ghost dele (Button.InitialFocus),
então Enter apertado por reflexo cancela em vez de destruir.
Desde 0.2.0-preview.24
O TextInput passa o rótulo dele à entrada como nome acessível, a legenda de ajuda/erro como
descrição acessível (associada por aria-describedby, anunciada educadamente quando ela vira erro), e
a condição de erro como aria-invalid. O que um leitor de tela ouve é exatamente a string que a pessoa
que enxerga lê. O SearchField e a barra de busca do editor de código promovem o placeholder deles a um
nome de verdade: um placeholder some sob o texto, então ele nunca substitui um nome.
Desde 0.2.0-preview.29
O Checkbox é um role="checkbox" e o Switch um role="switch", cada um declarando o estado
como aria-checked, com "mixed" para o checkbox indeterminado, e só nele, regra do próprio ARIA. O
NOME acessível é para que o controle serve, e nunca muda quando o estado muda (um nome que alterna
lê como outro controle aparecendo). No nativo a árvore de semântica carrega a mesma resposta
(SemanticCheck Off/On/Mixed), que o macOS mapeia no valor numérico do AXCheckBox, e o VoiceOver
anuncia o estado na língua do usuário, e foi por isso que as antigas strings "Marcado"/"Ativado"
saíram do SDK por inteiro.
Desde 0.2.0-preview.31
Uma barra anunciava os três destinos dela de forma idêntica: "Atividade, botão", "Busca, botão", "Perfil, botão", porque o ativo era uma pílula e um tom de cor, que não é absolutamente nada para um leitor de tela. É a única informação de estado que uma navegação existe para carregar, e nenhuma das três grafias que o vocabulário tinha conseguia dizê-la: um destino não é PRESSIONADO (ele não alterna), não é MARCADO (não se está escolhendo nada) e não é SELECIONADO (uma aba troca um painel; um destino muda ONDE você está).
Então o PressableRole.Destination entrou no conjunto, e o Link.Current ao lado dele para o idioma
de navegação do próprio web. O BottomNavigation, o NavigationRail e um ListItem marcado como
Selected declaram isso por você; uma barra lateral feita à mão com Links diz Current = true
naquele que casa com a rota.
-
Web:
aria-current="page", e só no atual. Oaria-current="false"é legal, inútil e lido em voz alta, então nove links anunciando "não atual" é pior que silêncio. -
Nativo: a árvore de semântica carrega o bit, e cada ponte o reporta com a palavra mais próxima
que a plataforma dela tem: o trait
Selectedno iOS, oaccessibilitySelectedno macOS e oSelecteddo node info no Android. - Ordem de Tab: um destino MANTÉM a parada de Tab dele, ao contrário de uma aba ou de um radio. Uma navegação é uma lista de links, não um composto com uma entrada só, e quem chega na barra pelo teclado espera percorrê-la.
Desde 0.2.0-preview.29
O foco de teclado revela um Tooltip exatamente como o hover (:has(:focus-visible),
focus-visible e não focus-within, porque tooltip abrindo a cada clique de mouse atrapalha), a
pílula é um role="tooltip" de verdade que o gatilho aponta com aria-describedby (o leitor de
tela ouve a dica depois do nome do controle sem nada estar revelado), e o Escape esconde um
tooltip revelado SEM mover ponteiro nem foco, que é a cláusula de dispensa do WCAG 1.4.13. Sair do
gatilho limpa a supressão, então a próxima visita revela de novo: Esc é um "agora não", nunca um
"nunca mais".
Desde 0.2.0-preview.29
Um chevron rodado é pintura, e pintura não diz nada à tecnologia assistiva. O Pressable.Expanded
declara a divulgação: aria-expanded no web, o bit expandido do nó de semântica nativo (o
"expanded"/"collapsed" do VoiceOver no macOS). E null traça a mesma linha do Selected: "não
controla divulgação nenhuma" é resposta diferente de "controla uma, fechada agora". Os cabeçalhos
do Accordion, o campo do Select e o gatilho do Menu declaram todos.
Desde 0.2.0-preview.29
Um Menu aberto é um role="menu" de linhas menuitem (comandos desabilitados incluídos,
anunciados esmaecidos em vez de escondidos), com o gatilho carregando aria-haspopup/aria-controls.
Um Select aberto é o padrão combobox completo: o CAMPO é o role="combobox", o painel um
role="listbox" de linhas option numeradas declarando aria-selected. E nos dois, o destaque
de teclado que as setas movem, que de outro modo seria só pintura, é declarado como
aria-activedescendant no gatilho, então um leitor de tela seguindo o teclado ouve cada passo. O
foco em si nunca sai do gatilho, que é o arranjo do próprio padrão combobox.
Desde 0.2.0-preview.24
Nenhum componente fixa uma string voltada ao usuário: as afordâncias de dispensar, o placeholder
de busca e os rótulos da barra de busca leem todos o SdkStrings, a costura respaldada por resx
que o trilho de localização preenche por cultura. O conjunto ENCOLHEU no
preview.29: os antigos anúncios de alternância ("Marcado", "Ativado") saíram quando o estado de um
check virou aria-checked, porque a plataforma anuncia o estado na língua do usuário, o que nenhum resx
nosso alcançaria.
Uma lista que só CONSTRÓI as linhas que você consegue ver: dê a ela uma contagem, uma extensão fixa por item e um construtor por índice, e ela materializa a janela visível mais uma margem de sobrevarredura. O resto da lista são dois espaçadores, então o layout (e a barra de rolagem) veem a altura de conteúdo verdadeira enquanto dez mil linhas custam o que uma tela cheia custa. Write-once: o mesmo C# roda no Photon e no web.
new ListView(count: 10_000, itemExtent: 44, itemBuilder: i => RowFor(i))
{
Width = SizeValue.Fill,
Height = SizeValue.Fill, // a lista é uma JANELA, dê uma a ela
}A janela converge em vez de bloquear: o primeiro frame constrói contra um palpite de viewport, o frame
realizado reporta o viewport e o deslocamento verdadeiros pelos canais de saída do ScrollView
(OnScrolled / OnViewportChanged, ligados nos dois alvos), e o frame seguinte constrói a janela
corrigida. Os pixels rolam sem reconstrução; o estado só muda quando uma linha cruza a margem de
sobrevarredura. Escopo (v1): listas verticais com ItemExtent fixo; extensões variáveis não estão
incluídas.
Regras de layout que fazem janelas funcionarem em todo lugar (invariantes do SDK, não tarefas do app):
- O quadro
#appdo shell tem altura EXATA de viewport (height: 100dvh, grid, filhosmin-height: 0): uma página de APP (raizHeight = Fill) resolve para exatamente um viewport e rola internamente; uma página de DOCUMENTO (raiz de altura automática) transborda o quadro e o body rola como um documento sempre rola. - Um
ScrollViewsemHeightexplícito assumeheight: 100%no web, porque uma view de rolagem É a janela que o pai dá a ela, nunca o conteúdo dela (paridade nativa: o realizador entrega a ela os limites de layout e corta sempre). - A hidratação adota os marcadores
data-eq-*do framework a partir da árvore do cliente, porque o SSR não pode conhecer identidades do lado do cliente, e as varreduras da passada posterior acham os elementos delas por eles.
Uma planilha com usabilidade de Excel, construída sobre o mesmo princípio do Editor de código: o controller carrega tudo, os pixels são aritmética. Um modelo C# + um componente C# renderizam no Photon e no DOM; o build do browser transpila exatamente as mesmas fontes.
O modelo (eQuantic.UI.Primitives/Sheet, compartilhado verbatim):
-
SheetDocument: células esparsas (vazia é ausente), tamanhos por linha/coluna, extensão lógica. OSetCell/SetRowHeight/SetColWidthé o caminho de carga/preview, deliberadamente não desfazível. -
CellRef/SheetRange: endereçamento A1; seleção âncora/foco em 2D. As células indexam por um int empacotado (linha × 16384 + coluna) porque um record struct é uma chave inútil num Map do JS. -
SheetController: a semântica do Excel como métodos. Setas/Shift estendem, Ctrl+seta salta para a borda dos dados, Tab/Enter caminham (e dão a volta) dentro de uma seleção enquanto o retângulo fica parado (célula ativa ≠ foco da seleção, deliberadamente), seleções pela faixa de cabeçalho, inserir/excluir/redimensionar linha/coluna com desfazer/refazer esparso baseado em inverso, copiar/colar TSV falando a citação do Excel, e o rascunho de edição na célula:BeginEdit/TypeIntoDraft/CommitEditvivem no controller, então os dois alvos editam de forma idêntica e um rascunho comitado desfaz como um passo só. -
SheetKeymap: O teclado ÚNICO. Digitar substitui (a entrada rápida do Excel), F2 edita no lugar, Enter/Tab comitam e avançam, Escape descarta, as setas comitam e movem, ⌘A/⌘Z. O host nativo e o rebaixamento web delegam os dois aqui; o dialeto não pode bifurcar.
O componente (eQuantic.UI.Components/Spreadsheet.cs):
- Cabeçalhos de coluna fixos no topo; cabeçalhos de linha rolam COM as linhas; a janela visível de
células é embrulhada numa
SheetSurfaceque vive DENTRO da rolagem, então as marcas transladam com o conteúdo e um clique numa grade rolada seleciona a célula que de fato está sob o ponteiro, por construção. - As linhas virtualizam do jeito do ListView (janela + espaçadores); a faixa de seleção, o anel da célula ativa e o rascunho+cursor de edição pintam SOBRE as células, no componente, então os dois alvos são visualmente idênticos porque não há nada do lado do realizador para divergir.
- Seleção no núcleo do Excel: cliques em cabeçalho selecionam a linha/coluna inteira deles (o canto seleciona tudo, e os cabeçalhos sombreiam quando a faixa da seleção os cruza); shift+clique estica a partir da âncora sem mover a célula ativa; um arrasto simples varre um intervalo nos dois alvos.
-
A alça de preenchimento, o quadradinho do Excel no canto inferior direito da seleção, arrasta um
preview pelo eixo DOMINANTE e despeja o bloco de origem por ele ao soltar, ladrilhando com volta em
fase nas quatro direções, como UM passo de desfazer. ⌘D/⌘R despejam para baixo/direita pelo mesmo
motor no keymap compartilhado. A semântica do gesto (
BeginFill/UpdateFill/CommitFill,Fill,FillDown,FillRight,SelectTo) vive no controller, então os dois alvos preenchem de forma idêntica. -
Redimensionar por arrasto: a aresta final de cada cabeçalho carrega uma pega
Draggableinvisível de 6dp (Stack+Positioned,Follows = false). Os movimentos fazem preview direto no documento (SetColWidth/SetRowHeight); a soltura rebobina o preview e assenta o gesto inteiro como UMController.Resizedesfazível. Os pisos (MinColWidth24,MinRowHeight14) mantêm uma lasca agarrável, limitados no componente, não noMin/Maxdo nó (esses reconstroem no meio do arrasto). Dentro do ScrollView a pega vence a rolagem: uma superfície de arrasto é o gesto mais específico (regra do host,PressDown). - Ligação da interação: o host nativo roteia cliques/seleção por arrasto/teclas/área de transferência
TSV/texto seguro para IME ao controller; o rebaixamento web (
lowerSheetSurface) é uma div focável comrole="grid"euser-select: none(um arrasto de seleção pinta a faixa, não a varredura azul do browser), keydown → oSheetKeymaptranspilado, duplo clique edita, copiar/recortar/colar pegam carona nos eventos de área de transferência do próprio browser, em TSV. -
Cursores de ponteiro são vocabulário (
BoxStyle.Cursor, o espelho docursordo CSS): o web emite a declaração, o Photon registra umaCursorRegionque oCursorAtdo host responde do topo para baixo, e o shell do macOS mapeia paraNSCursor. As pegas de redimensionamento dizem col-resize/row-resize e a alça de preenchimento diz crosshair, nos dois alvos, a partir do mesmo código de componente.
Escopo (v1): sem motor de fórmulas, sem formatação por célula, sem mesclas, sem painéis congelados. A virtualização é só vertical (as colunas materializam), e não há rolagem 2D, então colunas além da largura do painel são cortadas. As suítes de controller, edição e componente dirigem os dois eixos de redimensionamento pelo host; o gêmeo transpilado roda os mesmos movimentos sob o vitest, mais specs de superfície web com eventos DOM de verdade.
O Positioned é um contrato com o PAI: como um peso de flex, ele não significa nada em nenhum outro
lugar. Ele tem que ser filho de um Stack, e até a 0.2.0-preview.18 isso significava filho direto: um
componente cujo Build devolvia um não era reconhecido, degradava para o filho dele, e entrava no fluxo.
Um botão de canto renderizava acima da laje a que ele pertencia, colado na aresta esquerda, nos dois
alvos e em silêncio.
Os dois realizadores agora resolvem através do componente, então isto funciona:
Stack(children: [
CodeSlab(code),
CopyAction(code), // o Build dele devolve Positioned(top: 0, end: 0)
])Um filho posicionado continua não participando do tamanho de CONTEÚDO do stack, ou um selo de canto faria crescer a laje sobre a qual ele se apoia.
Desde 0.2.0-preview.20
InView(Heading(section.Title), visible => SetState(() => _active = visible ? section.Id : _active))A pergunta que um índice de conteúdo de fato faz é para qual título o leitor está olhando, e o único
jeito de responder isso era a posição de rolagem da página, que vive num ScrollView, então o artigo
tinha que ser embrulhado num. Isso muda o modelo de rolagem da página inteira: o cabeçalho sticky para de
grudar na janela, a restauração de rolagem do próprio browser para de funcionar, e a barra de endereço
para de se esconder num celular.
Um deslocamento cru também não teria ajudado muito. Ele é um número nas coordenadas da página, e responder "este título já passou dele" precisa da posição de cada título, uma medição que um componente write-once não tem por que fazer, e não consegue fazer do mesmo jeito nos dois alvos. Presença é a pergunta, então presença é o que isto reporta.
- Dispara só nas transições: verdadeiro quando o filho entra em vista, falso quando ele sai. Nunca uma vez por frame enquanto ele fica ali.
- O
Threshold(0–1) pede mais que uma lasca: um título metade fora do topo não é aquele em que o leitor está. - Não visível é o estado assumido, então um componente que monta com o filho fora da tela não ouve nada. Trinta títulos não anunciam trinta ausências.
- Transparente para o layout: ele não ocupa espaço e não desenha nada.
No web ele é um IntersectionObserver: sem listener de rolagem, então sem trabalho nos frames em que
nada cruza, e a medição acontece fora da thread principal. O Photon compara os limites com a superfície
uma vez por frame, num percurso que ele já está fazendo.
Os dois cortam. Uma linha rolada para fora de um ScrollView, ou de um Box que corta, não está na
tela. No Photon o que conta como visível se estreita a cada corte, do mesmo jeito que pintura e entrada
já se estreitam; no web o observador corta contra todo ancestral sozinho. Então uma lista em janela só
carrega o que o leitor consegue ver, que é o trabalho que isto torna barato. Corte estreitado no Photon
desde a 0.2.0-preview.21.
Duas coisas tinham que ser verdade no web antes de qualquer disso reportar algo, e as duas eram falsas quando isto foi publicado (corrigido na 0.2.0-preview.21):
-
O marcador vai no FILHO, nunca no embrulho transparente para o layout. O
display: contentsnão gera caixa, então um observador apontado ao embrulho fica olhando um retângulo 0×0 na origem. Ele reporta fielmente, sobre um lugar onde o título nunca está. - Os observadores anexam depois que a passada foi ESCRITA. Uma passada produz uma árvore; o gerente de render a escreve assim que a passada retorna. Comitar dentro da passada procurava elementos que ainda não existiam, não achava nenhum, e limpava as declarações, então uma página recém-hidratada carregava todo nó marcado sem observador, e só um re-render posterior os anexava. Numa página onde mais nada muda, isso é nunca.
Desde 0.2.0-preview.23
Link("/docs/Storage", Text("Storage")) { KeepsPosition = true }Uma navegação começa a nova página no topo dela, o que está certo para um link que te leva a outro lugar e errado para um que troca um painel ao lado de uma lista em que você estava na metade. Uma barra lateral de documentação é o caso que dá nome a isso: o shell é preservado e só umas duas centenas de nós são remendados, e aí tudo salta para o topo, o que lê, para quem está olhando, exatamente como se a página tivesse recarregado.
Chrome que mantém a PRÓPRIA rolagem nunca teve o problema e não precisa disto. Isto é para os layouts em que a lista e o conteúdo dividem uma rolagem.
Desde 0.2.0-preview.16
InFlow(Dialog("Delete this?", "It cannot be undone.", [new DialogAction("Delete")]))
InFlow(Drawer(content)) // sem precisar de `open: true`, veja abaixo
InFlow(BottomSheet(content))
InFlow(Popover(trigger, panel))Um diálogo, uma gaveta, uma folha e um popover só existem depois que algo os abre, e quando isso acontece eles tomam o viewport inteiro: um véu sobre a página, a superfície centrada ou fixada à frente dela, um atalho de Escape, uma animação de entrada. Uma página que quer MOSTRAR um deles (documentação, uma revisão de design, uma suíte de regressão visual) quer a superfície e nada disso. Cinco componentes renderizavam em branco.
O InFlow declara a intenção; cada overlay a responde construindo o painel dele e nada mais, porque o
componente é a única coisa que sabe qual é a superfície dele.
No fluxo não há aberto ou fechado: o painel está posicionado, portanto ele está lá. O Open governa
se a CAMADA existe, e não há camada. Uma gaveta se aperta ao conteúdo dela em vez de preencher o viewport
(altura cheia pertence à aresta em que ela está fixada, e ela não tem aresta); um popover desenha o painel
dele sem o gatilho; um PullToRefresh descansa com o indicador descoberto, já que não há o que puxar.
Não é um modo de preview: a superfície é real, com ações reais. O que é descartado é a apresentação. E a intenção termina no nó: um diálogo aberto de verdade ao lado de um previsualizado continua tomando o viewport.
Desde 0.2.0-preview.15
Simulated(SimulatedState.Hovered, Button("Save"))
Simulated(SimulatedState.Pressed | SimulatedState.Focused, IconButton(Icons.Check))Um botão pressionado não pode ser entregue a um construtor: pressionado é algo que o host observa, não algo que uma árvore declara. Então uma galeria de documentação, uma revisão de design e uma suíte de regressão visual só conseguiam mostrar o estado de repouso de um controle, e os três estados que um leitor mais quer comparar eram os três que página nenhuma conseguia desenhar.
Um nó em vez de uma propriedade em todo componente interativo: ele compõe, cobre os componentes que ainda não existem, e funciona para um controle aninhado três níveis dentro de um cartão sendo previsualizado. Previews aninhados se COMBINAM: um cartão em hover segurando um botão pressionado são dois nós, e o interno não desliga o hover do externo.
Ele muda só o que é DESENHADO. Nada é invocado, nenhum estado é entrado, nenhum handler roda, e o estado termina no nó: o controle ao lado se comporta normalmente.
No web, :hover e :focus-visible não podem ser forçados pelo CSS (nada pode, que é o ponto de uma
pseudo-classe), então os diffs que essas regras carregam são dobrados na regra base. As mesmas
declarações, então o preview é a coisa real em vez de uma aproximação dela. Um toque simulado reusa o
próprio corpo de seletor que um toque real usa, que é o que impede os dois de divergirem.
- Componentes write-once: a arquitetura compartilhada sobre a qual estes componentes andam
- Design System: tokens, densidade, tematização, o motor de estilo atômico
-
Editor de código: o modelo do editor, o
CodeBlocke oCodeEditor - Integração com o servidor: SSR, SEO e configuração do servidor
🌐 English · Português
🏁 Comece aqui
📱 Write-once
- Componentes write-once
- Superfície declarativa
- Motor Photon
- Design System
- Capacidades
- Armazenamento
- Formulários
- Editor de código
- Markdown
- Mermaid
- Renderização de Email
🏗️ Arquitetura
⚙️ Compilação
- Compilador
- Avaliação em tempo de compilação
- Recursos C# suportados
- Resolução de tipos externos
- Fluxo de build
- Diagnósticos
⚡ Runtime
🔌 Servidor
🎨 Ecossistema
🚀 Desenvolvimento