@softize/opus 12.11.0 → 13.0.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 +29 -0
- package/bin/lib/check.mjs +2 -7
- package/bin/lib/copy.mjs +1 -5
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +93 -10
- package/docs/adr/0007-toast-actions-form-an-ordered-collection.md +63 -0
- package/docs/adr/0008-hierarchical-navigation-is-composed-at-the-consumer-boundary.md +71 -0
- package/docs/radius-scale.md +1 -1
- package/package.json +1 -1
- package/registry/skills/maintain-opus-docs/SKILL.md +83 -0
- package/registry/skills/maintain-opus-docs/agents/openai.yaml +4 -0
- package/registry/skills/maintain-opus-docs/references/editorial-standard.md +85 -0
- package/registry/skills/maintain-opus-docs/references/evaluations.md +34 -0
- package/registry/skills/maintain-opus-docs/scripts/audit-docs.mjs +81 -0
- package/src/ui/components/patterns/confirm.tsx +140 -40
- package/src/ui/components/patterns/list.tsx +35 -40
- package/src/ui/components/patterns/page-state.tsx +2 -2
- package/src/ui/components/patterns/sidebar.tsx +26 -26
- package/src/ui/components/patterns/trigger.tsx +25 -22
- package/src/ui/components/primitives/alert.tsx +3 -3
- package/src/ui/components/primitives/dialog.tsx +196 -39
- package/src/ui/components/primitives/drawer.tsx +8 -5
- package/src/ui/components/primitives/empty.tsx +3 -3
- package/src/ui/components/primitives/item.tsx +3 -3
- package/src/ui/components/primitives/sonner.tsx +187 -8
- package/src/ui/docs/DocBrowser.tsx +102 -23
- package/src/ui/docs/content/accordion.md +22 -16
- package/src/ui/docs/content/action-form-card.md +8 -8
- package/src/ui/docs/content/action-form-dialog.md +9 -9
- package/src/ui/docs/content/action-form.md +28 -34
- package/src/ui/docs/content/action-list-dialog.md +11 -6
- package/src/ui/docs/content/action-list.md +64 -39
- package/src/ui/docs/content/action-trigger.md +21 -14
- package/src/ui/docs/content/action-view.md +8 -8
- package/src/ui/docs/content/actions.md +9 -9
- package/src/ui/docs/content/ai.md +3 -3
- package/src/ui/docs/content/alert.md +14 -12
- package/src/ui/docs/content/aspect-ratio.md +4 -4
- package/src/ui/docs/content/audit.md +2 -2
- package/src/ui/docs/content/auth.md +3 -3
- package/src/ui/docs/content/avatar.md +34 -14
- package/src/ui/docs/content/badge.md +3 -3
- package/src/ui/docs/content/breadcrumb.md +13 -8
- package/src/ui/docs/content/button.md +81 -6
- package/src/ui/docs/content/calendar.md +5 -5
- package/src/ui/docs/content/card.md +1 -1
- package/src/ui/docs/content/carousel.md +16 -11
- package/src/ui/docs/content/chat.md +3 -3
- package/src/ui/docs/content/checkbox.md +7 -7
- package/src/ui/docs/content/cli.md +5 -5
- package/src/ui/docs/content/collapsible.md +8 -8
- package/src/ui/docs/content/command.md +16 -8
- package/src/ui/docs/content/composer.md +2 -2
- package/src/ui/docs/content/content.md +2 -2
- package/src/ui/docs/content/copyable.md +4 -3
- package/src/ui/docs/content/customization.md +5 -5
- package/src/ui/docs/content/cycle.md +3 -3
- package/src/ui/docs/content/data-state.md +11 -12
- package/src/ui/docs/content/data.md +26 -33
- package/src/ui/docs/content/detail.md +3 -3
- package/src/ui/docs/content/dialog.md +339 -31
- package/src/ui/docs/content/dictionary-value.md +8 -8
- package/src/ui/docs/content/dock.md +3 -3
- package/src/ui/docs/content/drawer.md +27 -14
- package/src/ui/docs/content/empty-value.md +2 -2
- package/src/ui/docs/content/empty.md +19 -12
- package/src/ui/docs/content/events.md +4 -4
- package/src/ui/docs/content/field.md +34 -12
- package/src/ui/docs/content/getting-started.md +1 -1
- package/src/ui/docs/content/icon-picker.md +8 -4
- package/src/ui/docs/content/input-otp.md +20 -12
- package/src/ui/docs/content/input.md +121 -9
- package/src/ui/docs/content/item.md +27 -13
- package/src/ui/docs/content/kbd.md +19 -11
- package/src/ui/docs/content/label.md +5 -3
- package/src/ui/docs/content/log.md +4 -4
- package/src/ui/docs/content/markdown.md +7 -6
- package/src/ui/docs/content/mcp.md +13 -15
- package/src/ui/docs/content/menu.md +34 -16
- package/src/ui/docs/content/observability.md +2 -2
- package/src/ui/docs/content/page.md +51 -6
- package/src/ui/docs/content/pagination.md +22 -17
- package/src/ui/docs/content/popover.md +16 -8
- package/src/ui/docs/content/progress.md +7 -5
- package/src/ui/docs/content/queue.md +5 -5
- package/src/ui/docs/content/radio-group.md +20 -12
- package/src/ui/docs/content/router.md +11 -6
- package/src/ui/docs/content/scheduler.md +4 -5
- package/src/ui/docs/content/scroll-area.md +12 -7
- package/src/ui/docs/content/select.md +42 -29
- package/src/ui/docs/content/separator.md +5 -5
- package/src/ui/docs/content/sidebar.md +323 -54
- package/src/ui/docs/content/skeleton.md +3 -2
- package/src/ui/docs/content/slider.md +8 -7
- package/src/ui/docs/content/spinner.md +8 -8
- package/src/ui/docs/content/split.md +8 -5
- package/src/ui/docs/content/storage.md +6 -8
- package/src/ui/docs/content/switch.md +8 -7
- package/src/ui/docs/content/table.md +13 -3
- package/src/ui/docs/content/tabs.md +28 -14
- package/src/ui/docs/content/testing.md +9 -11
- package/src/ui/docs/content/textarea.md +5 -4
- package/src/ui/docs/content/toast.md +47 -13
- package/src/ui/docs/content/toggle.md +75 -7
- package/src/ui/docs/content/tokens.md +3 -3
- package/src/ui/docs/content/tooltip.md +19 -11
- package/src/ui/docs/content/truncate.md +7 -8
- package/src/ui/docs/content/ui.md +10 -9
- package/src/ui/docs/content/upgrading.md +7 -8
- package/src/ui/docs/registry.tsx +20 -37
- package/src/ui/meta.ts +64 -94
- package/src/ui/react.tsx +15 -16
- package/src/ui/theme.css +50 -0
- package/src/ui/components/primitives/alert-dialog.tsx +0 -192
- package/src/ui/docs/content/alert-dialog.md +0 -73
- package/src/ui/docs/content/button-group.md +0 -71
- package/src/ui/docs/content/confirm.md +0 -120
- package/src/ui/docs/content/input-group.md +0 -79
- package/src/ui/docs/content/page-state.md +0 -45
- package/src/ui/docs/content/toggle-group.md +0 -81
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Escolha em lista
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `Select` para escolhas em lista. Declare as opções como dados em `options`; `label` é usado no
|
|
4
|
+
campo, na busca e na leitura assistiva. Associe `id` ao `htmlFor` de `Label` em formulários.
|
|
4
5
|
|
|
5
6
|
```tsx preview col md
|
|
6
7
|
const [role, setRole] = useState('')
|
|
@@ -23,9 +24,10 @@ render(
|
|
|
23
24
|
)
|
|
24
25
|
```
|
|
25
26
|
|
|
26
|
-
##
|
|
27
|
+
## Lista pesquisável
|
|
27
28
|
|
|
28
|
-
`searchable`
|
|
29
|
+
Use `searchable` quando a quantidade ou os rótulos dificultarem encontrar uma opção. O próprio
|
|
30
|
+
campo passa a filtrar `label`, `hint` e `value` com a busca do cmdk.
|
|
29
31
|
|
|
30
32
|
```tsx preview col md
|
|
31
33
|
const [issue, setIssue] = useState('')
|
|
@@ -49,9 +51,11 @@ render(
|
|
|
49
51
|
)
|
|
50
52
|
```
|
|
51
53
|
|
|
52
|
-
##
|
|
54
|
+
## Seleção múltipla
|
|
53
55
|
|
|
54
|
-
`multiple
|
|
56
|
+
Com `multiple`, `value` e `onChange` usam `string[]`. As opções escolhidas aparecem como chips
|
|
57
|
+
removíveis e a lista permanece aberta para escolhas sucessivas. “Selecionar tudo” fica disponível
|
|
58
|
+
somente quando todas as opções estão carregadas localmente e a busca está vazia.
|
|
55
59
|
|
|
56
60
|
```tsx preview col md
|
|
57
61
|
const [skills, setSkills] = useState<string[]>(['clean-code'])
|
|
@@ -77,9 +81,10 @@ render(
|
|
|
77
81
|
)
|
|
78
82
|
```
|
|
79
83
|
|
|
80
|
-
##
|
|
84
|
+
## Controle nativo
|
|
81
85
|
|
|
82
|
-
`native`
|
|
86
|
+
Use `native` para uma lista curta e conhecida, como prioridade ou ambiente. O sistema operacional
|
|
87
|
+
controla a interação do `<select>`. Busca, seleção múltipla e conteúdo rico exigem o modo padrão.
|
|
83
88
|
|
|
84
89
|
```tsx preview col md
|
|
85
90
|
const [env, setEnv] = useState('preview')
|
|
@@ -104,7 +109,8 @@ render(
|
|
|
104
109
|
|
|
105
110
|
## Grupos
|
|
106
111
|
|
|
107
|
-
`group`
|
|
112
|
+
`group` reúne opções sob um título nos dois modos. Use grupos quando a lista combinar conjuntos com
|
|
113
|
+
origens ou papéis distintos.
|
|
108
114
|
|
|
109
115
|
```tsx preview col md
|
|
110
116
|
const [reviewer, setReviewer] = useState('')
|
|
@@ -124,9 +130,10 @@ render(
|
|
|
124
130
|
)
|
|
125
131
|
```
|
|
126
132
|
|
|
127
|
-
## Busca
|
|
133
|
+
## Busca no servidor
|
|
128
134
|
|
|
129
|
-
Com `onSearch
|
|
135
|
+
Com `onSearch`, o consumidor consulta as opções ao abrir e ao digitar, com debounce. Atualize
|
|
136
|
+
`options` com a resposta e use `loading` durante a consulta. Esse modo já habilita o campo de busca.
|
|
130
137
|
|
|
131
138
|
```tsx preview col md
|
|
132
139
|
const ASSIGNEES = [
|
|
@@ -163,7 +170,8 @@ render(
|
|
|
163
170
|
|
|
164
171
|
## Ícone dentro do campo
|
|
165
172
|
|
|
166
|
-
`icon`
|
|
173
|
+
`icon` posiciona um ícone decorativo antes do texto ou dos chips. Use-o para reforçar a identidade do
|
|
174
|
+
campo sem criar um elemento separado ao lado do controle.
|
|
167
175
|
|
|
168
176
|
```tsx preview col-start
|
|
169
177
|
const [task, setTask] = useState('')
|
|
@@ -182,9 +190,10 @@ render(
|
|
|
182
190
|
)
|
|
183
191
|
```
|
|
184
192
|
|
|
185
|
-
##
|
|
193
|
+
## Tamanho
|
|
186
194
|
|
|
187
|
-
O
|
|
195
|
+
O tamanho padrão acompanha `Input` e `Button`. Use `size="sm"` em barras de filtros e outras
|
|
196
|
+
composições densas.
|
|
188
197
|
|
|
189
198
|
```tsx preview col-start
|
|
190
199
|
const [a, setA] = useState('gra-2')
|
|
@@ -203,9 +212,10 @@ render(
|
|
|
203
212
|
)
|
|
204
213
|
```
|
|
205
214
|
|
|
206
|
-
## Limpar
|
|
215
|
+
## Limpar a seleção
|
|
207
216
|
|
|
208
|
-
`clearable`
|
|
217
|
+
`clearable` acrescenta uma ação que limpa o valor sem abrir a lista. No modo simples, o valor volta
|
|
218
|
+
a vazio; no múltiplo, volta a `[]`. Os filtros de `ActionList` já habilitam esse comportamento.
|
|
209
219
|
|
|
210
220
|
```tsx preview col md
|
|
211
221
|
function Demo() {
|
|
@@ -229,9 +239,10 @@ function Demo() {
|
|
|
229
239
|
render(<Demo />)
|
|
230
240
|
```
|
|
231
241
|
|
|
232
|
-
##
|
|
242
|
+
## Conteúdo rico nas opções
|
|
233
243
|
|
|
234
|
-
`label`
|
|
244
|
+
Mantenha `label` como string para busca e leitura assistiva. Use `content` para acrescentar ícone,
|
|
245
|
+
avatar ou texto complementar à opção apresentada na lista.
|
|
235
246
|
|
|
236
247
|
```tsx preview col md
|
|
237
248
|
const [agent, setAgent] = useState('developer')
|
|
@@ -271,9 +282,10 @@ render(
|
|
|
271
282
|
)
|
|
272
283
|
```
|
|
273
284
|
|
|
274
|
-
##
|
|
285
|
+
## Select na barra do Composer
|
|
275
286
|
|
|
276
|
-
`variant="ghost"`
|
|
287
|
+
Use `variant="ghost"` para integrar o seletor à barra de ações de `Composer`. `triggerLabel` pode
|
|
288
|
+
encurtar somente o texto do gatilho; a lista continua exibindo o `label` completo.
|
|
277
289
|
|
|
278
290
|
```tsx preview col-start
|
|
279
291
|
const [task, setTask] = useState('GB-42')
|
|
@@ -292,9 +304,10 @@ render(
|
|
|
292
304
|
)
|
|
293
305
|
```
|
|
294
306
|
|
|
295
|
-
## Ação no fim do campo
|
|
307
|
+
## Ação no fim do campo
|
|
296
308
|
|
|
297
|
-
`trailing`
|
|
309
|
+
`trailing` posiciona uma ação relacionada ao valor antes do chevron. Interagir com essa região não
|
|
310
|
+
abre a lista.
|
|
298
311
|
|
|
299
312
|
```tsx preview col-start
|
|
300
313
|
const [task, setTask] = useState('gra-2')
|
|
@@ -318,9 +331,9 @@ render(
|
|
|
318
331
|
)
|
|
319
332
|
```
|
|
320
333
|
|
|
321
|
-
##
|
|
334
|
+
## Propriedades de Select
|
|
322
335
|
|
|
323
|
-
|
|
|
336
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
324
337
|
|---|---|---|---|
|
|
325
338
|
| `options` | `SelectOption[]` | | As opções: `{ value, label, hint?, content?, triggerLabel?, group?, disabled? }`. |
|
|
326
339
|
| `value` | `string \| string[]` | | O selecionado: string no single, string[] no multiple. |
|
|
@@ -328,16 +341,16 @@ render(
|
|
|
328
341
|
| `native` | `boolean` | `false` | Renderiza o `<select>` do sistema. Exclui busca, multi e ghost (o browser é quem desenha a lista). |
|
|
329
342
|
| `searchable` | `boolean` | `false` | O campo vira busca: filtra a lista enquanto digita. |
|
|
330
343
|
| `multiple` | `boolean` | `false` | Chips removíveis, lista que permanece aberta e Selecionar tudo. |
|
|
331
|
-
| `variant` | `'default' \| 'ghost'` | `'default'` | `ghost` = sem moldura,
|
|
344
|
+
| `variant` | `'default' \| 'ghost'` | `'default'` | `ghost` = sem moldura, para barra do composer. |
|
|
332
345
|
| `placeholder` | `string` | `'Selecione…'` | Texto do campo vazio. |
|
|
333
|
-
| `searchPlaceholder` | `string` | | Placeholder enquanto busca; cai
|
|
346
|
+
| `searchPlaceholder` | `string` | | Placeholder enquanto busca; cai para o `placeholder` se ausente. |
|
|
334
347
|
| `emptyText` | `string` | `'Nada encontrado.'` | Mensagem quando a busca não acha nada. |
|
|
335
348
|
| `onSearch` | `(query: string) => void` | | Busca server-side (debounced, ao abrir e ao digitar): desliga o filtro do cmdk — o pai atualiza `options`. |
|
|
336
349
|
| `loading` | `boolean` | | Mostra Buscando… enquanto o fetch corre (use com `onSearch`). |
|
|
337
|
-
| `clearable` | `boolean` | `false` | X no controle quando há seleção — limpa
|
|
350
|
+
| `clearable` | `boolean` | `false` | X no controle quando há seleção — limpa em um clique. |
|
|
338
351
|
| `icon` | `React.ReactNode` | | Ícone leading DENTRO do controle (decorativo) — herda `size-4` e o tom muted. |
|
|
339
352
|
| `trailing` | `React.ReactNode` | | Ação custom no FIM do controle (antes do chevron) — o clique não abre a lista. |
|
|
340
|
-
| `size` | `'default' \| 'sm'` | `'default'` | Altura: default (h-9, a do Input e do Button) ou sm (h-8)
|
|
353
|
+
| `size` | `'default' \| 'sm'` | `'default'` | Altura: default (h-9, a do Input e do Button) ou sm (h-8) para toolbar densa. |
|
|
341
354
|
| `disabled` | `boolean` | `false` | Esmaece e trava o controle. |
|
|
342
|
-
| `id` | `string` | | Vai
|
|
355
|
+
| `id` | `string` | | Vai para o campo — para parear com o `htmlFor` do Label. |
|
|
343
356
|
| `className` | `string` | | Classes da raiz do controle, incluindo campo, ícones e ações, em todos os modos. |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Separar blocos empilhados
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use a orientação horizontal entre blocos empilhados. A linha ocupa toda a largura disponível.
|
|
4
4
|
|
|
5
5
|
```tsx preview col
|
|
6
6
|
<div>
|
|
@@ -13,7 +13,7 @@ Divide blocos empilhados — ocupa a largura toda do contêiner.
|
|
|
13
13
|
|
|
14
14
|
## Vertical
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Use a orientação vertical entre itens lado a lado. A linha herda a altura do contêiner.
|
|
17
17
|
|
|
18
18
|
```tsx preview
|
|
19
19
|
<div className="flex h-4 items-center gap-3 text-sm">
|
|
@@ -25,9 +25,9 @@ Entre itens de uma linha. O vertical herda a altura do contêiner — dê altura
|
|
|
25
25
|
</div>
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
##
|
|
28
|
+
## Propriedades de Separator
|
|
29
29
|
|
|
30
|
-
|
|
|
30
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
31
31
|
|---|---|---|---|
|
|
32
32
|
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da linha. Vertical precisa de altura vinda do contêiner. |
|
|
33
33
|
| `decorative` | `boolean` | `true` | Decorativa some da árvore de acessibilidade. Use false quando a divisão é semântica. |
|
|
@@ -1,79 +1,348 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Navegação dentro de um layout
|
|
2
2
|
|
|
3
|
-
`Sidebar`
|
|
3
|
+
Use `Sidebar` para apresentar navegação global ou contextual em uma coluna lateral. O componente
|
|
4
|
+
organiza a superfície e compartilha o estado de colapso; posição e largura pertencem a um `Pane`
|
|
5
|
+
dentro de `Split`.
|
|
6
|
+
|
|
7
|
+
O exemplo canônico mantém a largura inicial em `rem`, deixa a navegação rolar e preserva o
|
|
8
|
+
cabeçalho e o rodapé nas extremidades.
|
|
4
9
|
|
|
5
10
|
```tsx preview
|
|
6
11
|
render(
|
|
7
|
-
<div className="h-
|
|
8
|
-
<Split
|
|
9
|
-
<Pane initialSize=
|
|
10
|
-
<Sidebar
|
|
11
|
-
<PaneHeader className="px-4 py-3 text-sm font-semibold">
|
|
12
|
+
<div className="h-80 overflow-hidden rounded-lg border border-border">
|
|
13
|
+
<Split>
|
|
14
|
+
<Pane initialSize="16rem" inset="none">
|
|
15
|
+
<Sidebar className="w-full">
|
|
16
|
+
<PaneHeader className="px-4 py-3 text-sm font-semibold">
|
|
17
|
+
Projeto
|
|
18
|
+
</PaneHeader>
|
|
12
19
|
<PaneBody>
|
|
13
20
|
<SidebarNav
|
|
14
|
-
groups={[
|
|
15
|
-
|
|
21
|
+
groups={[
|
|
22
|
+
{
|
|
23
|
+
label: 'Workspace',
|
|
24
|
+
items: [
|
|
25
|
+
{ id: 'overview', label: 'Visão geral', icon: <LayoutDashboard /> },
|
|
26
|
+
{ id: 'sessions', label: 'Sessões', icon: <MessagesSquare /> },
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
]}
|
|
30
|
+
activeId="overview"
|
|
16
31
|
onSelect={() => {}}
|
|
17
32
|
/>
|
|
18
33
|
</PaneBody>
|
|
19
|
-
<PaneFooter
|
|
34
|
+
<PaneFooter>
|
|
35
|
+
<SidebarItem label="Configurações" icon={<Settings />} onClick={() => {}} />
|
|
36
|
+
</PaneFooter>
|
|
20
37
|
</Sidebar>
|
|
21
38
|
</Pane>
|
|
22
|
-
<Pane grow inset="lg"
|
|
39
|
+
<Pane grow inset="lg">
|
|
40
|
+
<p className="text-sm text-muted-foreground">Conteúdo da página.</p>
|
|
41
|
+
</Pane>
|
|
23
42
|
</Split>
|
|
24
43
|
</div>,
|
|
25
44
|
)
|
|
26
45
|
```
|
|
27
46
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
Use `resizable` no `Split` somente quando ajustar a largura fizer parte da tarefa. Nesse caso, o
|
|
48
|
+
`Pane` também declara `minSize`, e `divider={false}` evita que `Sidebar` desenhe uma segunda
|
|
49
|
+
divisória ao lado do handle.
|
|
50
|
+
|
|
51
|
+
## Anatomia e responsabilidades
|
|
52
|
+
|
|
53
|
+
Cada parte possui uma responsabilidade única. Essa separação permite reutilizar os mesmos slots em
|
|
54
|
+
uma sidebar fixa, redimensionável ou recolhida.
|
|
55
|
+
|
|
56
|
+
| Parte | Responsabilidade |
|
|
57
|
+
|---|---|
|
|
58
|
+
| `Split` | Define a relação espacial e, opcionalmente, o redimensionamento. |
|
|
59
|
+
| `Pane` | Define largura, limites e espaçamento interno da coluna. |
|
|
60
|
+
| `Sidebar` | Desenha a superfície lateral e fornece o estado de colapso. |
|
|
61
|
+
| `PaneHeader` | Mantém identidade, contexto ou ações no topo. |
|
|
62
|
+
| `PaneBody` | Ocupa o espaço restante e concentra a rolagem vertical. |
|
|
63
|
+
| `PaneFooter` | Mantém ações persistentes no rodapé. |
|
|
64
|
+
| `SidebarNav` ou `ShellNav` | Apresenta e controla os destinos de navegação. |
|
|
65
|
+
|
|
66
|
+
`PaneContent` permanece como alias temporário de `PaneBody` durante a versão 12. Código novo usa
|
|
67
|
+
`PaneBody`.
|
|
68
|
+
|
|
69
|
+
## Escolher a navegação
|
|
70
|
+
|
|
71
|
+
Use `SidebarNav` para grupos planos de destinos. Ele recebe dados e produz `SidebarItem` com a mesma
|
|
72
|
+
semântica usada na composição manual.
|
|
73
|
+
|
|
74
|
+
```tsx preview
|
|
75
|
+
<div className="h-72 w-64 overflow-hidden rounded-lg border border-border">
|
|
76
|
+
<Sidebar className="w-full">
|
|
77
|
+
<PaneBody>
|
|
78
|
+
<SidebarNav
|
|
79
|
+
groups={[
|
|
80
|
+
{
|
|
81
|
+
label: 'Configurações',
|
|
82
|
+
items: [
|
|
83
|
+
{ id: 'users', label: 'Usuários', icon: <Users /> },
|
|
84
|
+
{ id: 'roles', label: 'Papéis', icon: <ShieldCheck /> },
|
|
85
|
+
{ id: 'imports', label: 'Importações', icon: <Database /> },
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
]}
|
|
89
|
+
activeId="users"
|
|
90
|
+
onSelect={() => {}}
|
|
91
|
+
/>
|
|
92
|
+
</PaneBody>
|
|
93
|
+
</Sidebar>
|
|
94
|
+
</div>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Compor uma árvore
|
|
98
|
+
|
|
99
|
+
Uma árvore não pertence ao contrato de `SidebarNav`: profundidade, carregamento e expansão variam
|
|
100
|
+
por produto. Componha cada nó com `SidebarItem`, envolva seus descendentes em `SidebarTreeGroup` e
|
|
101
|
+
mantenha o estado no consumidor. O grupo aplica o recuo e desenha a guia vertical; o destino
|
|
102
|
+
principal e o controle de expansão permanecem irmãos acessíveis.
|
|
103
|
+
|
|
104
|
+
```tsx preview
|
|
105
|
+
const [open, setOpen] = useState(true)
|
|
106
|
+
|
|
107
|
+
render(
|
|
108
|
+
<div className="h-72 w-64 overflow-hidden rounded-lg border border-border">
|
|
109
|
+
<Sidebar className="w-full">
|
|
110
|
+
<PaneBody className="p-2">
|
|
111
|
+
<SidebarGroupLabel>Documentação</SidebarGroupLabel>
|
|
112
|
+
<SidebarItem
|
|
113
|
+
label="Componentes"
|
|
114
|
+
icon={<Folder />}
|
|
115
|
+
expanded={open}
|
|
116
|
+
onToggle={() => setOpen((current) => !current)}
|
|
117
|
+
onClick={() => setOpen((current) => !current)}
|
|
118
|
+
/>
|
|
119
|
+
{open && (
|
|
120
|
+
<SidebarTreeGroup>
|
|
121
|
+
<SidebarItem label="Layout" icon={<Folder />} expanded onToggle={() => {}} onClick={() => {}} />
|
|
122
|
+
<SidebarTreeGroup>
|
|
123
|
+
<SidebarItem label="Sidebar" icon={<PanelLeft />} active onClick={() => {}} />
|
|
124
|
+
<SidebarItem label="Page" icon={<PanelsTopLeft />} onClick={() => {}} />
|
|
125
|
+
</SidebarTreeGroup>
|
|
126
|
+
</SidebarTreeGroup>
|
|
127
|
+
)}
|
|
128
|
+
</PaneBody>
|
|
129
|
+
</Sidebar>
|
|
130
|
+
</div>,
|
|
131
|
+
)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Esse é o padrão usado pelo `DocBrowser`: seções são rótulos, grupos nomeados são nós expansíveis e
|
|
135
|
+
páginas são folhas. Um grupo começa aberto e volta a abrir quando contém a página ativa.
|
|
136
|
+
|
|
137
|
+
Use `ShellNav` para uma navegação plana que precisa de cabeçalho próprio e grupos ancorados no
|
|
138
|
+
rodapé. Ele gerencia a rolagem internamente e se adapta quando estiver dentro de uma `Sidebar`
|
|
139
|
+
recolhida.
|
|
52
140
|
|
|
53
141
|
```tsx
|
|
54
142
|
<Sidebar collapsed={collapsed}>
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
143
|
+
<ShellNav
|
|
144
|
+
heading={<ShellNavHeading title="Relatórios" action={<CreateReportButton />} />}
|
|
145
|
+
groups={reportGroups}
|
|
146
|
+
footer={settingsGroups}
|
|
147
|
+
activeId={activeId}
|
|
148
|
+
onSelect={navigateToReport}
|
|
149
|
+
/>
|
|
58
150
|
</Sidebar>
|
|
59
151
|
```
|
|
60
152
|
|
|
61
|
-
##
|
|
153
|
+
## Recolher a coluna
|
|
62
154
|
|
|
63
|
-
`
|
|
155
|
+
`collapsed` pertence à `Sidebar`. Nesse estado, `SidebarItem`, `SidebarNav` e `ShellNav` mantêm
|
|
156
|
+
somente os ícones e expõem os rótulos em tooltips. Por isso, todo destino que aparece no modo
|
|
157
|
+
recolhido precisa de um ícone reconhecível e de um `label` completo.
|
|
64
158
|
|
|
65
|
-
```tsx
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
/>
|
|
159
|
+
```tsx preview
|
|
160
|
+
const [collapsed, setCollapsed] = useState(false)
|
|
161
|
+
|
|
162
|
+
render(
|
|
163
|
+
<div className="flex h-64 overflow-hidden rounded-lg border border-border">
|
|
164
|
+
<Sidebar collapsed={collapsed}>
|
|
165
|
+
<PaneHeader className="flex h-12 items-center justify-center">
|
|
166
|
+
<Button
|
|
167
|
+
variant="ghost"
|
|
168
|
+
size="icon-sm"
|
|
169
|
+
aria-label={collapsed ? 'Expandir navegação' : 'Recolher navegação'}
|
|
170
|
+
onClick={() => setCollapsed((current) => !current)}
|
|
171
|
+
>
|
|
172
|
+
<PanelLeftClose className={collapsed ? 'rotate-180' : ''} />
|
|
173
|
+
</Button>
|
|
174
|
+
</PaneHeader>
|
|
175
|
+
<PaneBody>
|
|
176
|
+
<SidebarNav
|
|
177
|
+
groups={[
|
|
178
|
+
{
|
|
179
|
+
items: [
|
|
180
|
+
{ id: 'home', label: 'Início', icon: <House /> },
|
|
181
|
+
{ id: 'reports', label: 'Relatórios', icon: <ChartNoAxesColumn /> },
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
]}
|
|
185
|
+
activeId="home"
|
|
186
|
+
onSelect={() => {}}
|
|
187
|
+
/>
|
|
188
|
+
</PaneBody>
|
|
189
|
+
</Sidebar>
|
|
190
|
+
<div className="flex-1 p-4 text-sm text-muted-foreground">
|
|
191
|
+
O conteúdo ocupa o espaço liberado pela sidebar.
|
|
192
|
+
</div>
|
|
193
|
+
</div>,
|
|
194
|
+
)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
O consumidor controla o estado e decide onde colocar o gatilho. A transição de largura pertence à
|
|
198
|
+
`Sidebar`; a aplicação não precisa trocar a árvore de componentes.
|
|
199
|
+
|
|
200
|
+
## Itens com expansão e ações
|
|
201
|
+
|
|
202
|
+
`SidebarItem` mantém o destino principal, a expansão e as ações como controles irmãos. Essa
|
|
203
|
+
estrutura evita botões aninhados e permite que cada controle receba foco de forma independente.
|
|
204
|
+
|
|
205
|
+
```tsx preview
|
|
206
|
+
<div className="w-72 rounded-lg border border-border p-2">
|
|
207
|
+
<SidebarItem
|
|
208
|
+
label="Contratos"
|
|
209
|
+
icon={<Folder />}
|
|
210
|
+
expanded
|
|
211
|
+
onToggle={() => {}}
|
|
212
|
+
actions={
|
|
213
|
+
<Button variant="ghost" size="icon-xs" aria-label="Mais ações">
|
|
214
|
+
<Ellipsis />
|
|
215
|
+
</Button>
|
|
216
|
+
}
|
|
217
|
+
onClick={() => {}}
|
|
218
|
+
/>
|
|
219
|
+
<SidebarItem
|
|
220
|
+
label="Criar workspace"
|
|
221
|
+
icon={<FilePlus2 />}
|
|
222
|
+
className="pl-6"
|
|
223
|
+
onClick={() => {}}
|
|
224
|
+
/>
|
|
225
|
+
</div>
|
|
79
226
|
```
|
|
227
|
+
|
|
228
|
+
Quando o rótulo não cabe, ele termina em um gradiente. As ações aparecem ao passar o ponteiro, ao
|
|
229
|
+
receber foco visível ou enquanto um menu permanece aberto; como são sobrepostas, não reduzem o
|
|
230
|
+
espaço disponível para o texto.
|
|
231
|
+
|
|
232
|
+
Para árvores arrastáveis, `dropPosition` comunica o destino visual sem reservar espaço:
|
|
233
|
+
`before` e `after` desenham uma linha na fronteira, enquanto `inside` realça a superfície do item.
|
|
234
|
+
O driver de drag-and-drop continua externo e fornece seus atributos por `dragProps`.
|
|
235
|
+
|
|
236
|
+
## Acessibilidade
|
|
237
|
+
|
|
238
|
+
- `activeId` resulta em `aria-current="page"` no destino atual.
|
|
239
|
+
- `navLabel` nomeia a landmark de navegação.
|
|
240
|
+
- O controle de expansão informa `aria-expanded` e recebe um rótulo baseado no item.
|
|
241
|
+
- Itens desabilitados continuam visíveis, mas não respondem à interação.
|
|
242
|
+
- O foco visível usa o anel semântico do tema no destino, nas ações e no controle de expansão.
|
|
243
|
+
- No modo recolhido, o tooltip preserva o nome que deixou de aparecer visualmente.
|
|
244
|
+
|
|
245
|
+
## Propriedades de Sidebar
|
|
246
|
+
|
|
247
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
248
|
+
|---|---|---|---|
|
|
249
|
+
| `collapsed` | `boolean` | `false` | Recolhe a coluna e publica esse estado para as navegações descendentes. |
|
|
250
|
+
| `divider` | `boolean` | `true` | Desenha a borda na lateral. Desative quando o `Split` já fornecer o handle. |
|
|
251
|
+
| `className` | `string` | | Ajusta a raiz. A largura externa deve continuar pertencendo ao `Pane`. |
|
|
252
|
+
| `children` | `ReactNode` | | Cabeçalho, corpo, rodapé ou uma navegação que gerencie a própria estrutura. |
|
|
253
|
+
|
|
254
|
+
## Propriedades de PaneHeader
|
|
255
|
+
|
|
256
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
257
|
+
|---|---|---|---|
|
|
258
|
+
| `className` | `string` | | Ajusta a faixa fixa e seu espaçamento. |
|
|
259
|
+
| `children` | `ReactNode` | | Identidade, contexto ou ações apresentadas no topo. |
|
|
260
|
+
|
|
261
|
+
## Propriedades de PaneBody
|
|
262
|
+
|
|
263
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
264
|
+
|---|---|---|---|
|
|
265
|
+
| `className` | `string` | | Ajusta a região flexível e rolável. |
|
|
266
|
+
| `children` | `ReactNode` | | Conteúdo que ocupa o espaço entre cabeçalho e rodapé. |
|
|
267
|
+
|
|
268
|
+
## Propriedades de PaneFooter
|
|
269
|
+
|
|
270
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
271
|
+
|---|---|---|---|
|
|
272
|
+
| `className` | `string` | | Ajusta a faixa fixa e seu espaçamento. |
|
|
273
|
+
| `children` | `ReactNode` | | Navegação ou ações que precisam permanecer acessíveis no rodapé. |
|
|
274
|
+
|
|
275
|
+
## Propriedades de SidebarNav
|
|
276
|
+
|
|
277
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
278
|
+
|---|---|---|---|
|
|
279
|
+
| `groups` | `SidebarNavGroup[]` | | Grupos e itens apresentados na ordem recebida. Grupos vazios são omitidos. |
|
|
280
|
+
| `activeId` | `string` | | Identificador do destino atual. |
|
|
281
|
+
| `onSelect` | `(id: string) => void` | | Recebe o identificador selecionado; roteamento permanece com o consumidor. |
|
|
282
|
+
| `navLabel` | `string` | `'Navegação'` | Nome acessível da landmark `nav`. |
|
|
283
|
+
| `className` | `string` | | Ajusta a raiz da navegação. |
|
|
284
|
+
|
|
285
|
+
### Estrutura de SidebarNavGroup
|
|
286
|
+
|
|
287
|
+
| Campo | Tipo | Descrição |
|
|
288
|
+
|---|---|---|
|
|
289
|
+
| `label` | `string` | Rótulo opcional do grupo. Rótulos vazios não reservam espaço. |
|
|
290
|
+
| `items` | `SidebarNavItem[]` | Itens diretos do grupo. |
|
|
291
|
+
|
|
292
|
+
`SidebarNavItem` acrescenta `id` às propriedades de `SidebarItem`. Para mais níveis, componha
|
|
293
|
+
`SidebarItem` e `SidebarTreeGroup` diretamente.
|
|
294
|
+
|
|
295
|
+
## Propriedades de SidebarItem
|
|
296
|
+
|
|
297
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
298
|
+
|---|---|---|---|
|
|
299
|
+
| `label` | `string` | | Nome visível do destino e conteúdo do tooltip no modo recolhido. |
|
|
300
|
+
| `icon` | `ReactNode` | | Ícone apresentado antes do rótulo e preservado no modo recolhido. |
|
|
301
|
+
| `badge` | `ReactNode` | | Informação curta no fim da linha, como contagem ou estado. |
|
|
302
|
+
| `active` | `boolean` | `false` | Destaca o destino e aplica `aria-current="page"`. |
|
|
303
|
+
| `disabled` | `boolean` | `false` | Mantém o item visível sem permitir interação. |
|
|
304
|
+
| `actions` | `ReactNode` | | Controles relacionados exibidos no fim da linha. |
|
|
305
|
+
| `onToggle` | `() => void` | | Adiciona o controle independente de expansão. |
|
|
306
|
+
| `expanded` | `boolean` | `false` | Define o estado acessível e a rotação do controle de expansão. |
|
|
307
|
+
| `dropPosition` | `'before' \| 'inside' \| 'after'` | | Indica visualmente onde um item arrastado será solto. |
|
|
308
|
+
| `dragProps` | `HTMLAttributes<HTMLDivElement>` | | Encaminha atributos fornecidos pelo driver de drag-and-drop para a linha. |
|
|
309
|
+
| `tooltipHint` | `ReactNode` | | Acrescenta contexto ao tooltip do modo recolhido. |
|
|
310
|
+
| `className` | `string` | | Ajusta a linha nos estados aberto e recolhido. |
|
|
311
|
+
| `onClick` | `() => void` | | Executa a navegação principal do item. |
|
|
312
|
+
|
|
313
|
+
## Propriedades de SidebarGroupLabel
|
|
314
|
+
|
|
315
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
316
|
+
|---|---|---|---|
|
|
317
|
+
| `className` | `string` | | Ajusta o rótulo discreto do grupo. |
|
|
318
|
+
| `children` | `ReactNode` | | Nome do conjunto de destinos. |
|
|
319
|
+
|
|
320
|
+
## Propriedades de SidebarTreeGroup
|
|
321
|
+
|
|
322
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
323
|
+
|---|---|---|---|
|
|
324
|
+
| `className` | `string` | | Ajusta o grupo sem substituir o recuo, o espaçamento e a guia vertical padrão. |
|
|
325
|
+
| `children` | `ReactNode` | | Itens ou grupos que descendem do nó anterior. |
|
|
326
|
+
|
|
327
|
+
## Propriedades de ShellNav
|
|
328
|
+
|
|
329
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
330
|
+
|---|---|---|---|
|
|
331
|
+
| `groups` | `ShellNavGroup[]` | | Grupos planos exibidos na região rolável. Grupos vazios são omitidos. |
|
|
332
|
+
| `activeId` | `string` | | Identificador do destino atual. |
|
|
333
|
+
| `onSelect` | `(id: string) => void` | | Recebe o identificador selecionado. |
|
|
334
|
+
| `heading` | `ReactNode` | | Conteúdo fixo acima da navegação; fica oculto no modo recolhido. |
|
|
335
|
+
| `footer` | `ShellNavGroup[]` | | Grupos ancorados abaixo da região rolável. |
|
|
336
|
+
| `navLabel` | `string` | `'Navegação'` | Nome acessível da landmark `nav`. |
|
|
337
|
+
| `className` | `string` | | Ajusta a coluna da navegação. A largura vem do contêiner. |
|
|
338
|
+
|
|
339
|
+
Cada `ShellNavGroup` recebe `label` opcional e `items`. Um `ShellNavItem` declara `id`, `label`,
|
|
340
|
+
`icon`, `badge` e `disabled`.
|
|
341
|
+
|
|
342
|
+
## Propriedades de ShellNavHeading
|
|
343
|
+
|
|
344
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
345
|
+
|---|---|---|---|
|
|
346
|
+
| `title` | `ReactNode` | | Título da navegação. |
|
|
347
|
+
| `action` | `ReactNode` | | Ação relacionada apresentada no extremo oposto. |
|
|
348
|
+
| `className` | `string` | | Ajusta a faixa de cabeçalho. |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
## Linha de lista com avatar
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `Skeleton` para preservar a forma do conteúdo durante o carregamento. Combine círculos para
|
|
4
|
+
avatares e barras com dimensões próximas às linhas de texto esperadas.
|
|
4
5
|
|
|
5
6
|
```tsx preview col
|
|
6
7
|
<div className="flex items-center gap-3">
|
|
@@ -14,7 +15,7 @@ O skeleton imita a forma do conteúdo: círculo pro avatar (rounded-full), barra
|
|
|
14
15
|
|
|
15
16
|
## Card em carregamento
|
|
16
17
|
|
|
17
|
-
O esqueleto reproduz o layout final do card — título, descrição, conteúdo e ações —
|
|
18
|
+
O esqueleto reproduz o layout final do card — título, descrição, conteúdo e ações — para tela não pular quando os dados chegarem.
|
|
18
19
|
|
|
19
20
|
```tsx preview col
|
|
20
21
|
<Card>
|