@softize/opus 26.0.0 → 26.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/bin/lib/check.mjs +9 -32
- package/bin/lib/copy.mjs +7 -4
- package/docs/adr/0014-structural-headers-do-not-carry-description.md +1 -1
- package/docs/adr/0018-modal-header-only-names-the-surface.md +1 -1
- package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +4 -0
- package/docs/adr/0057-doc-browser-permite-busca-no-chrome-do-consumidor.md +27 -0
- package/docs/adr/0059-subheadings-orient-without-carrying-business-rules.md +51 -0
- package/docs/adr/README.md +2 -0
- package/package.json +1 -1
- package/registry/skills/build-opus-ui/references/evaluations.md +8 -0
- package/registry/skills/build-opus-ui/references/ui-patterns.md +14 -4
- package/src/core/types.ts +3 -2
- package/src/ui/components/patterns/action-list-dialog.tsx +14 -7
- package/src/ui/components/patterns/form-dialog.tsx +18 -7
- package/src/ui/components/patterns/form.tsx +36 -8
- package/src/ui/components/patterns/page.tsx +22 -0
- package/src/ui/components/patterns/sidebar.tsx +17 -3
- package/src/ui/components/patterns/surface-anatomy.ts +1 -1
- package/src/ui/components/primitives/dialog.tsx +28 -1
- package/src/ui/components/primitives/drawer.tsx +30 -2
- package/src/ui/docs/DocBrowser.tsx +128 -81
- package/src/ui/docs/content/action-form-card.md +1 -1
- package/src/ui/docs/content/action-form-dialog.md +8 -0
- package/src/ui/docs/content/action-form.md +18 -5
- package/src/ui/docs/content/action-list-dialog.md +8 -0
- package/src/ui/docs/content/dialog.md +24 -2
- package/src/ui/docs/content/documentation-portal.md +45 -2
- package/src/ui/docs/content/drawer.md +24 -2
- package/src/ui/docs/content/page.md +23 -1
- package/src/ui/docs/content/sidebar.md +23 -0
- package/src/ui/docs/content/surface.md +8 -2
- package/src/ui/docs/types.ts +2 -0
- package/src/ui/lib/modal-layers.ts +27 -3
- package/src/ui/react.tsx +3 -0
|
@@ -84,12 +84,21 @@ do mesmo peso visual. Nesse caso, o cancelamento muda para `outline`, salvo esco
|
|
|
84
84
|
`cancelVariant`. Para apresentar o formulário como uma seção delimitada da página, use
|
|
85
85
|
[`ActionFormCard`](/ui/action-form-card). As propriedades de cada wrapper estão na página dele.
|
|
86
86
|
|
|
87
|
-
##
|
|
87
|
+
## Hint visível ou ícone de help
|
|
88
88
|
|
|
89
|
-
`FieldSpec.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
`FieldDescription`
|
|
89
|
+
Use `FieldSpec.hint` abaixo do controle quando a pessoa precisar da informação para preencher
|
|
90
|
+
corretamente ou prever o efeito da escolha. Exemplo: “Sem áreas selecionadas, a pasta aparece em
|
|
91
|
+
todas as áreas.” O texto permanece visível e participa do `aria-describedby` do controle, junto
|
|
92
|
+
do erro quando houver. Em uma composição própria, use `FieldDescription` com a mesma associação.
|
|
93
|
+
|
|
94
|
+
Use `FieldSpec.help` no ícone junto à label para detalhes complementares, definições e exemplos
|
|
95
|
+
opcionais. A pergunta é: sem abrir essa ajuda, a pessoa ainda consegue escolher corretamente?
|
|
96
|
+
Se não, o trecho necessário deve ser hint. Não use o comprimento da frase como critério nem
|
|
97
|
+
esconda uma condição obrigatória no tooltip. Omita texto que apenas repete a label.
|
|
98
|
+
|
|
99
|
+
Hint e help podem coexistir se tiverem funções diferentes, sem duplicar a mesma frase. Um erro
|
|
100
|
+
explica uma entrada inválida e sua correção; não substitui uma orientação necessária antes de
|
|
101
|
+
preencher. Uma regra que afeta vários campos pertence ao corpo ou ao grupo correspondente.
|
|
93
102
|
|
|
94
103
|
`LabelHelp` é esse mesmo ícone, exportado para um formulário sem contrato mostrar a ajuda no mesmo
|
|
95
104
|
lugar:
|
|
@@ -185,3 +194,7 @@ ciclo de vida que o pattern não cobre; validação e execução continuam iguai
|
|
|
185
194
|
| Propriedade | Tipo | Descrição |
|
|
186
195
|
| ----------- | -------- | ------------------------------------------------------------ |
|
|
187
196
|
| `help` | `string` | O texto do tooltip. Vazio ou ausente, o ícone não renderiza. |
|
|
197
|
+
|
|
198
|
+
O `hint` aceita texto ou `I18nRef` e permanece visível; o contexto do formulário entrega o texto
|
|
199
|
+
resolvido aos campos personalizados. O botão de `help` participa do Tab e abre por foco ou hover.
|
|
200
|
+
Dialog e Drawer evitam que a ajuda seja o primeiro foco automático quando há outro controle.
|
|
@@ -96,6 +96,13 @@ const roleOptions = roles.items.map((role) => ({ value: role.id, label: role.nam
|
|
|
96
96
|
</ActionListDialog>;
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
## Orientação e contexto
|
|
100
|
+
|
|
101
|
+
`description` apresenta a finalidade no cabeçalho, como “Escolha onde a pasta e suas subpastas
|
|
102
|
+
aparecem.”. É opcional; não repita o título. `intro` apresenta no corpo informações necessárias
|
|
103
|
+
à decisão que não pertencem a um campo específico. Regras, condições e consequências não ficam
|
|
104
|
+
somente no subheading. Quando ambos são fornecidos, ambos participam de `aria-describedby`.
|
|
105
|
+
|
|
99
106
|
## Propriedades de ActionListDialog
|
|
100
107
|
|
|
101
108
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
@@ -103,6 +110,7 @@ const roleOptions = roles.items.map((role) => ({ value: role.id, label: role.nam
|
|
|
103
110
|
| `action / input` | `ListAction / TInput` | `input`: `{}` | O contrato e o escopo base da consulta. Uma mudança em `input` refaz a consulta; as invalidações declaradas por forms e triggers também. |
|
|
104
111
|
| `open / onOpenChange` | `boolean / (open) => void` | | Estado controlado do modal. |
|
|
105
112
|
| `title` | `string` | | Nome da lista exibido no cabeçalho fixo. |
|
|
113
|
+
| `description` | `ReactNode` | | Orientação opcional sob o título; não recebe regras de negócio. |
|
|
106
114
|
| `intro` | `ReactNode` | | Contexto relevante no início do corpo, como texto ou `Alert`. |
|
|
107
115
|
| `children` | `(items, refetch) => ReactNode` | | A composição dos itens. Carregamento, erro e vazio já são tratados pelo `ActionList`. |
|
|
108
116
|
| `note` | `ReactNode` | | Contexto curto à esquerda da linha de ações. O total de itens já aparece no rodapé do `ActionList`. |
|
|
@@ -150,8 +150,21 @@ Voltar e ações do cabeçalho usam `Button` `ghost` no tamanho `icon` (2.25rem)
|
|
|
150
150
|
[`Presentation`](/ui/presentation) usa no voltar. O botão de fechar é menor: um `CloseButton` no
|
|
151
151
|
quadrado `icon-sm` (1.75rem) por padrão.
|
|
152
152
|
|
|
153
|
-
O cabeçalho
|
|
154
|
-
|
|
153
|
+
O cabeçalho aceita `DialogDescription` como subheading opcional para orientar sobre a finalidade.
|
|
154
|
+
Omita quando repetir o título. Condições, regras de negócio e consequências necessárias à decisão
|
|
155
|
+
ficam junto dos controles ou em `DialogBody`. Uma frase curta também pode ser regra: o critério é se
|
|
156
|
+
pular o texto compromete a decisão, não sua extensão.
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
<DialogHeader>
|
|
160
|
+
<DialogTitle>Organizar pasta</DialogTitle>
|
|
161
|
+
<DialogDescription>Escolha onde a pasta e suas subpastas aparecem.</DialogDescription>
|
|
162
|
+
</DialogHeader>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Na composição direta, associe o `id` do texto por `aria-describedby` em `DialogContent` quando ele
|
|
166
|
+
descrever a superfície para tecnologias assistivas. A regra sobre seleção vazia pertence ao
|
|
167
|
+
campo de áreas e permanece fora do subheading.
|
|
155
168
|
|
|
156
169
|
## Identificar a tarefa com um ícone
|
|
157
170
|
|
|
@@ -634,3 +647,12 @@ conteúdo no primeiro bloco de `DialogBody`. `DialogIntro` exige um `DialogTitle
|
|
|
634
647
|
|
|
635
648
|
No formato compacto, mantenha `DialogTitle` e `DialogActions` no próprio `DialogHeader`. Para
|
|
636
649
|
comparar as duas formas em cada `layout`, abra [Composições](/ui/compositions).
|
|
650
|
+
|
|
651
|
+
## Propriedades de DialogDescription
|
|
652
|
+
|
|
653
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
654
|
+
| --- | --- | --- | --- |
|
|
655
|
+
| `children` | `ReactNode` | | Orientação opcional sob o título em `DialogHeader` ou `DialogIntro`. |
|
|
656
|
+
| `id` | `string` | | Identificador para associação acessível explícita. |
|
|
657
|
+
| `className` | `string` | | Ajusta a apresentação do parágrafo. |
|
|
658
|
+
| `…props` | `ComponentProps<"p">` | | Atributos nativos. |
|
|
@@ -46,6 +46,36 @@ conceitos relacionados, não para reproduzir automaticamente a árvore de arquiv
|
|
|
46
46
|
tem `slug`, `title` e `render`; `searchText` acrescenta conteúdo à busca e `navigationTitle` permite
|
|
47
47
|
um rótulo lateral curto sem mudar o título completo.
|
|
48
48
|
|
|
49
|
+
Um grupo pode conter páginas próprias e subgrupos em `groups`. Por exemplo, uma documentação
|
|
50
|
+
por domínio pode reunir as conexões externas em Integrações e manter um grupo por sistema:
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
const sections: DocSection[] = [{
|
|
54
|
+
label: 'Domínios',
|
|
55
|
+
groups: [{
|
|
56
|
+
label: 'Integrações',
|
|
57
|
+
pages: [{ slug: 'integrations', title: 'Integrações', render: () => <p>Conexões com sistemas externos.</p> }],
|
|
58
|
+
groups: [{
|
|
59
|
+
label: 'Insider',
|
|
60
|
+
pages: [{ slug: 'integrations/insider', title: 'Insider', render: () => <p>Envio de atributos e eventos.</p> }],
|
|
61
|
+
}],
|
|
62
|
+
}],
|
|
63
|
+
}]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
As páginas próprias vêm antes dos subgrupos na sequência de leitura. Um grupo com `pages: []`
|
|
67
|
+
pode reunir somente subgrupos; ramos sem páginas em nenhum nível não aparecem na lateral.
|
|
68
|
+
O contexto editorial usa o grupo nomeado mais próximo. A busca revela os ancestrais dos
|
|
69
|
+
resultados e, no modo `sidebarInitiallyCollapsed`, restaura os ramos abertos manualmente
|
|
70
|
+
quando a consulta é limpa. No modo expandido padrão, os ancestrais da página ativa são
|
|
71
|
+
reabertos para mantê-la visível. `groups` é opcional:
|
|
72
|
+
as declarações existentes continuam válidas. Essa hierarquia é uma escolha de leitura do host,
|
|
73
|
+
não exige mover arquivos nem muda a descoberta por pastas.
|
|
74
|
+
|
|
75
|
+
Se o host também percorre as páginas para montar índices ou resolver links, esse percurso
|
|
76
|
+
precisa visitar `group.groups` recursivamente. Achatar somente `section.groups[].pages`
|
|
77
|
+
não inclui as páginas dos subgrupos.
|
|
78
|
+
|
|
49
79
|
Com `editorial`, a leitura recebe contexto, sumário dos títulos renderizados e links de página
|
|
50
80
|
anterior e seguinte. `additionalPages` preserva destinos diretos que não entram nessa sequência.
|
|
51
81
|
A busca documental do topo mantém seu rótulo, valor e função ao navegar para catálogos e detalhes;
|
|
@@ -64,6 +94,19 @@ o contrato desktop a partir de `64rem`. O aplicativo organiza a moldura do porta
|
|
|
64
94
|
|
|
65
95
|
ADRs e changelog mantêm o mapa dos guias, sem acrescentar uma árvore para cada registro. Use `sidebarLinks` para oferecer entradas
|
|
66
96
|
no mesmo nível dos grupos, após os capítulos, sem incluí-las na sequência anterior/próxima.
|
|
97
|
+
Destinos com URL HTTP(S) absoluta usam navegação nativa, inclusive para outro aplicativo;
|
|
98
|
+
destinos relativos usam o roteamento compartilhado.
|
|
99
|
+
|
|
100
|
+
Para abrir uma ferramenta sem substituir a leitura atual, declare `newTab` no destino:
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
<DocBrowser sections={sections}
|
|
104
|
+
sidebarLinks={[{ label: 'Lente', href: '/lens', newTab: true }]} />
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
O link exibe um ícone de nova aba, informa a abertura no nome acessível e usa
|
|
108
|
+
`rel="noopener noreferrer"`. Sem `newTab`, mantém a navegação na aba atual.
|
|
109
|
+
|
|
67
110
|
`DocCatalog` recebe registros derivados dos Markdown e uma consulta local independente do cabeçalho.
|
|
68
111
|
Passe o catálogo em `content` para preservar a lateral:
|
|
69
112
|
|
|
@@ -148,7 +191,7 @@ não define as rotas nem a autorização do aplicativo.
|
|
|
148
191
|
|
|
149
192
|
| Propriedade | Tipo | Padrão | Uso |
|
|
150
193
|
| --- | --- | --- | --- |
|
|
151
|
-
| `sections` | `DocSection[]` | Obrigatória na entrada leve | Seções, grupos e páginas na ordem de leitura. |
|
|
194
|
+
| `sections` | `DocSection[]` | Obrigatória na entrada leve | Seções, grupos, subgrupos opcionais e páginas na ordem de leitura. |
|
|
152
195
|
| `basePath` | `string` | `/docs` | Prefixo das rotas. |
|
|
153
196
|
| `path` | `string` | Rota compartilhada | Caminho controlado por um host externo. |
|
|
154
197
|
| `redirects` | `Record<string, string>` | `{}` | Slugs anteriores e seus destinos canônicos. |
|
|
@@ -160,7 +203,7 @@ não define as rotas nem a autorização do aplicativo.
|
|
|
160
203
|
| `navigationLabel` | `string` | `Documentação` | Nome da área no contexto editorial; por exemplo, `UI` para o catálogo de componentes. |
|
|
161
204
|
| `additionalPages` | `DocEntry[]` | `[]` | Destinos diretos fora da árvore e da sequência. |
|
|
162
205
|
| `content` | `ReactNode` | — | Conteúdo do host no lugar da página documental, mantendo a lateral. O host resolve rota e título; `null` deixa a leitura vazia. |
|
|
163
|
-
| `sidebarLinks` | `{ label: string; href: string; active?: boolean }[]` | `[]` | Destinos globais fora dos capítulos; `active` marca o catálogo atual. |
|
|
206
|
+
| `sidebarLinks` | `{ label: string; href: string; active?: boolean; newTab?: boolean }[]` | `[]` | Destinos globais fora dos capítulos; `active` marca o catálogo atual e `newTab` abre em outra aba com ícone e descrição acessível. |
|
|
164
207
|
| `sidebarInitiallyCollapsed` | `boolean` | `false` | Seções e grupos nomeados começam recolhidos, inclusive em links diretos. A busca revela resultados temporariamente sem alterar os ramos abertos pela pessoa. |
|
|
165
208
|
|
|
166
209
|
A entrada histórica `@softize/opus/ui/docs` inclui o catálogo e oferece suas páginas quando
|
|
@@ -148,8 +148,21 @@ Voltar e ações do cabeçalho usam `Button` `ghost` no tamanho `icon` (2.25rem)
|
|
|
148
148
|
[`Presentation`](/ui/presentation) usa no voltar. O botão de fechar é menor: um `CloseButton` no
|
|
149
149
|
quadrado `icon-sm` (1.75rem) por padrão.
|
|
150
150
|
|
|
151
|
-
O cabeçalho
|
|
152
|
-
|
|
151
|
+
O cabeçalho aceita `DrawerDescription` como subheading opcional para orientar sobre a finalidade.
|
|
152
|
+
Omita quando repetir o título. Condições, regras de negócio e consequências necessárias à decisão
|
|
153
|
+
ficam junto dos controles ou em `DrawerBody`. Uma frase curta também pode ser regra: o critério é se
|
|
154
|
+
pular o texto compromete a decisão, não sua extensão.
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
<DrawerHeader>
|
|
158
|
+
<DrawerTitle>Organizar pasta</DrawerTitle>
|
|
159
|
+
<DrawerDescription>Escolha onde a pasta e suas subpastas aparecem.</DrawerDescription>
|
|
160
|
+
</DrawerHeader>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Na composição direta, associe o `id` do texto por `aria-describedby` em `DrawerContent` quando ele
|
|
164
|
+
descrever a superfície para tecnologias assistivas. A regra sobre seleção vazia pertence ao
|
|
165
|
+
campo de áreas e permanece fora do subheading.
|
|
153
166
|
|
|
154
167
|
## Impedir o fechamento durante uma operação
|
|
155
168
|
|
|
@@ -301,3 +314,12 @@ Assim o título acompanha o conteúdo rolável enquanto o chrome permanece está
|
|
|
301
314
|
|
|
302
315
|
No formato compacto, mantenha `DrawerTitle` e `DrawerActions` no próprio `DrawerHeader`. Para
|
|
303
316
|
comparar as duas formas em cada `layout`, abra [Composições](/ui/compositions).
|
|
317
|
+
|
|
318
|
+
## Propriedades de DrawerDescription
|
|
319
|
+
|
|
320
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
321
|
+
| --- | --- | --- | --- |
|
|
322
|
+
| `children` | `ReactNode` | | Orientação opcional sob o título em `DrawerHeader` ou `DrawerIntro`. |
|
|
323
|
+
| `id` | `string` | | Identificador para associação acessível explícita. |
|
|
324
|
+
| `className` | `string` | | Ajusta a apresentação do parágrafo. |
|
|
325
|
+
| `…props` | `ComponentProps<"p">` | | Atributos nativos. |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
## Esqueleto de página
|
|
2
2
|
|
|
3
3
|
Use `Page` para manter cabeçalho, estado e conteúdo na mesma anatomia. `PageHeader` é a única
|
|
4
|
-
introdução estrutural: reúne navegação contextual opcional, título e ações. O container é
|
|
4
|
+
introdução estrutural: reúne navegação contextual opcional, título, orientação opcional e ações. O container é
|
|
5
5
|
centralizado e ocupa a largura disponível até `80rem` (`max-w-7xl`). Use `className` somente quando
|
|
6
6
|
a composição pedir outro teto ou largura total.
|
|
7
7
|
|
|
@@ -41,6 +41,19 @@ render(
|
|
|
41
41
|
);
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
## Orientar sobre a finalidade
|
|
45
|
+
|
|
46
|
+
Use `description` na forma curta ou `PageDescription` em `PageHeader` quando uma frase breve
|
|
47
|
+
ajudar a reconhecer a finalidade da página. Omita texto que só repita o título. Regras de negócio,
|
|
48
|
+
limites e consequências ficam nos campos, ações ou corpo em que são necessários. Pular o
|
|
49
|
+
subheading não deve comprometer a decisão nem a conclusão correta da tarefa.
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<Page title="Conhecimento" description="Consulte os materiais de apoio da sua área.">
|
|
53
|
+
<KnowledgeList />
|
|
54
|
+
</Page>
|
|
55
|
+
```
|
|
56
|
+
|
|
44
57
|
## Retorno para a página pai
|
|
45
58
|
|
|
46
59
|
Use `PageBack` em uma subpágina simples. O destino é explícito para continuar correto após refresh ou
|
|
@@ -311,6 +324,7 @@ estado na própria seção com [`DataState`](/ui/data-state), [`ActionView`](/ui
|
|
|
311
324
|
|
|
312
325
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
313
326
|
| ----------- | -------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
327
|
+
| `description` | `ReactNode` | | Orientação opcional sob o título na forma curta. |
|
|
314
328
|
| `title` | `ReactNode` | | O h1 da página. |
|
|
315
329
|
| `actions` | `ReactNode` | | Ações contextuais no extremo oposto do cabeçalho. |
|
|
316
330
|
| `layout` | `'flow' \| 'fill'` | `'flow'` | Ocupação de altura, não anatomia: `fill` limita a página ao shell para que uma região interna role e leva essa altura até uma `ActionList` composta em `PageBody`. |
|
|
@@ -414,3 +428,11 @@ ocupa a altura.
|
|
|
414
428
|
| `retryLabel` | `string` | `'Tentar de novo'` | Texto do botão de recuperação. |
|
|
415
429
|
| `children` | `ReactNode` | | Conteúdo renderizado somente em `ready`. |
|
|
416
430
|
| `className` | `string` | | Classes da superfície de estado. |
|
|
431
|
+
|
|
432
|
+
## Propriedades de PageDescription
|
|
433
|
+
|
|
434
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
435
|
+
| --- | --- | --- | --- |
|
|
436
|
+
| `children` | `ReactNode` | | Orientação breve e opcional; filho direto de `PageHeader`. |
|
|
437
|
+
| `className` | `string` | | Ajusta a apresentação do parágrafo. |
|
|
438
|
+
| `…props` | `HTMLAttributes<HTMLParagraphElement>` | | Atributos nativos. |
|
|
@@ -73,6 +73,10 @@ diretamente quando a composição precisar de controles, badges ou hierarquia pa
|
|
|
73
73
|
Quando o host usa um roteador, forneça também `onClick`: somente o clique simples é interceptado;
|
|
74
74
|
cliques com modificadores preservam o comportamento do link. Sem `href`, o item continua sendo um botão.
|
|
75
75
|
|
|
76
|
+
Para manter a página atual aberta ao visitar outro destino, declare `newTab` junto de `href`.
|
|
77
|
+
O link usa `target="_blank"` e `rel="noopener noreferrer"`, não chama `onClick` e informa
|
|
78
|
+
“abre em nova aba” no nome acessível, inclusive no rail recolhido. Sem `href`, `newTab` não tem efeito.
|
|
79
|
+
|
|
76
80
|
## Colapso
|
|
77
81
|
|
|
78
82
|
`collapsed` informa o estado compacto aos itens. Forneça labels acessíveis mesmo quando somente os
|
|
@@ -99,3 +103,22 @@ render(
|
|
|
99
103
|
- `Sidebar` oferece a coluna de navegação.
|
|
100
104
|
- `SidebarNav`, `SidebarItem` e elementos nativos organizam o conteúdo interno.
|
|
101
105
|
- `Surface` oferece anatomia e tratamento visual quando a região não é uma sidebar.
|
|
106
|
+
|
|
107
|
+
## Propriedades de SidebarItem
|
|
108
|
+
|
|
109
|
+
| Propriedade | Tipo | Padrão | Uso |
|
|
110
|
+
| --- | --- | --- | --- |
|
|
111
|
+
| `label` | `string` | Obrigatória | Nome do destino, também usado no modo recolhido. |
|
|
112
|
+
| `href` | `string` | — | Renderiza um link nativo quando o item está habilitado; sem ela, renderiza um botão. |
|
|
113
|
+
| `newTab` | `boolean` | `false` | Com `href`, abre em outra aba, informa a abertura no nome acessível e não chama `onClick`. |
|
|
114
|
+
| `onClick` | `() => void` | — | Ativa o botão ou intercepta o clique simples do link, exceto com `newTab`. |
|
|
115
|
+
| `icon`, `badge` | `ReactNode` | — | Ícone e complemento visual da linha. O modo recolhido mostra somente o ícone. |
|
|
116
|
+
| `active` | `boolean` | `false` | Destaca o destino atual e declara `aria-current="page"`. |
|
|
117
|
+
| `disabled` | `boolean` | `false` | Desabilita o alvo principal e impede que seja um link. |
|
|
118
|
+
| `actions` | `ReactNode` | — | Controles irmãos do alvo principal, no modo aberto. |
|
|
119
|
+
| `onToggle` | `() => void` | — | Exibe um controle separado para expandir ou recolher descendentes no modo aberto. |
|
|
120
|
+
| `expanded` | `boolean` | `false` | Estado visual e acessível do controle de expansão. |
|
|
121
|
+
| `dropPosition` | `'before' \| 'inside' \| 'after'` | — | Indicação visual de posição durante arraste, no modo aberto. |
|
|
122
|
+
| `dragProps` | `HTMLAttributes<HTMLDivElement>` | — | Eventos e atributos de arraste aplicados à linha. |
|
|
123
|
+
| `tooltipHint` | `ReactNode` | — | Complementa o tooltip do modo recolhido. |
|
|
124
|
+
| `className` | `string` | — | Classes da linha nos dois modos. |
|
|
@@ -131,12 +131,11 @@ render(
|
|
|
131
131
|
as="section"
|
|
132
132
|
framed
|
|
133
133
|
title="Plano atual"
|
|
134
|
-
description="Renova em 12 de maio."
|
|
135
134
|
level={3}
|
|
136
135
|
>
|
|
137
136
|
<SurfaceBody>
|
|
138
137
|
<p className="text-sm text-muted-foreground">
|
|
139
|
-
Até 10 workspaces e 50 agentes.
|
|
138
|
+
Renova em 12 de maio. Até 10 workspaces e 50 agentes.
|
|
140
139
|
</p>
|
|
141
140
|
</SurfaceBody>
|
|
142
141
|
<SurfaceFooter>
|
|
@@ -511,3 +510,10 @@ um wrapper filho declara relações locais mais compactas.
|
|
|
511
510
|
| ----------- | ----------- | ------ | ---------------------------------------------------- |
|
|
512
511
|
| `children` | `ReactNode` | | Ações persistentes, alinhadas no fim da linha. |
|
|
513
512
|
| `className` | `string` | | Ajusta o rodapé, inclusive a distribuição das ações. |
|
|
513
|
+
|
|
514
|
+
## Subheading e regras de negócio
|
|
515
|
+
|
|
516
|
+
`description` e `SurfaceDescription` apresentam brevemente a finalidade da região. A orientação
|
|
517
|
+
é opcional e não repete o título. A mesma regra vale para uma página (`level={1}`), uma seção ou
|
|
518
|
+
um card: condições, limites e consequências necessários à decisão ficam no corpo ou junto do
|
|
519
|
+
controle correspondente. Pular o subheading não deve comprometer a tarefa.
|
package/src/ui/docs/types.ts
CHANGED
|
@@ -143,6 +143,21 @@ function syncHosts(): void {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/** A ajuda permanece no Tab, mas não toma o foco inicial do campo ao abrir um modal. */
|
|
147
|
+
export function skipHelpOnAutoFocus(event: Event): void {
|
|
148
|
+
if (event.defaultPrevented || !(event.target instanceof HTMLElement)) return;
|
|
149
|
+
const candidates = tabbables(event.target).filter(
|
|
150
|
+
(element) => element.tagName !== "A",
|
|
151
|
+
);
|
|
152
|
+
if (candidates[0]?.dataset.slot !== "label-help") return;
|
|
153
|
+
const control = candidates.find(
|
|
154
|
+
(element) => element.dataset.slot !== "label-help",
|
|
155
|
+
);
|
|
156
|
+
if (control === undefined) return;
|
|
157
|
+
control.focus();
|
|
158
|
+
if (document.activeElement === control) event.preventDefault();
|
|
159
|
+
}
|
|
160
|
+
|
|
146
161
|
/** Elementos alcançáveis pelo Tab dentro de `root`, na ordem do documento. */
|
|
147
162
|
function tabbables(root: HTMLElement): HTMLElement[] {
|
|
148
163
|
const nodes: HTMLElement[] = [];
|
|
@@ -178,7 +193,10 @@ function tabbables(root: HTMLElement): HTMLElement[] {
|
|
|
178
193
|
function focusFromKeyboard(element: HTMLElement): void {
|
|
179
194
|
element.focus();
|
|
180
195
|
// O Tab do navegador seleciona o texto do campo que recebe o foco; a passagem manual imita.
|
|
181
|
-
if (
|
|
196
|
+
if (
|
|
197
|
+
element instanceof HTMLInputElement &&
|
|
198
|
+
typeof element.select === "function"
|
|
199
|
+
) {
|
|
182
200
|
element.select();
|
|
183
201
|
}
|
|
184
202
|
}
|
|
@@ -342,7 +360,10 @@ export function registerFloatingLayer(
|
|
|
342
360
|
// podem tratar o Tab e marcá-lo com `preventDefault`.
|
|
343
361
|
const onKeyDown = (event: KeyboardEvent): void => {
|
|
344
362
|
if (!isTabKey(event) || event.defaultPrevented) return;
|
|
345
|
-
if (
|
|
363
|
+
if (
|
|
364
|
+
!layerExemptionsActive() ||
|
|
365
|
+
layerOf(document.activeElement) !== record
|
|
366
|
+
) {
|
|
346
367
|
return;
|
|
347
368
|
}
|
|
348
369
|
const candidates = tabbables(host);
|
|
@@ -361,7 +382,10 @@ export function registerFloatingLayer(
|
|
|
361
382
|
if (layerWherePersonIs() === record) escape()?.(event);
|
|
362
383
|
};
|
|
363
384
|
const observer = new MutationObserver(() => {
|
|
364
|
-
if (
|
|
385
|
+
if (
|
|
386
|
+
pointerAnchor !== null &&
|
|
387
|
+
pointerAnchor.parentNode !== pointerLayer?.host
|
|
388
|
+
) {
|
|
365
389
|
// O painel apertado saiu da camada, como quando é fechado por código e vira o FAB.
|
|
366
390
|
pointerAnchor = null;
|
|
367
391
|
pointerLayer = null;
|
package/src/ui/react.tsx
CHANGED
|
@@ -173,6 +173,7 @@ export {
|
|
|
173
173
|
DialogBody,
|
|
174
174
|
DialogFooter,
|
|
175
175
|
DialogTitle,
|
|
176
|
+
DialogDescription,
|
|
176
177
|
} from "./components/primitives/dialog.tsx";
|
|
177
178
|
export type {
|
|
178
179
|
DialogProps,
|
|
@@ -326,6 +327,7 @@ export {
|
|
|
326
327
|
DrawerNavigation,
|
|
327
328
|
DrawerBody,
|
|
328
329
|
DrawerTitle,
|
|
330
|
+
DrawerDescription,
|
|
329
331
|
DrawerTrigger,
|
|
330
332
|
} from "./components/primitives/drawer.tsx";
|
|
331
333
|
export {
|
|
@@ -509,6 +511,7 @@ export {
|
|
|
509
511
|
PageNavigation,
|
|
510
512
|
PageBack,
|
|
511
513
|
PageTitle,
|
|
514
|
+
PageDescription,
|
|
512
515
|
PageActions,
|
|
513
516
|
PageActionsTarget,
|
|
514
517
|
PageBody,
|