@softize/opus 12.10.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 +56 -0
- package/bin/lib/check.mjs +1098 -310
- package/bin/lib/copy.mjs +12 -5
- package/docs/adr/0003-dictionary-presentation-is-declared.md +3 -0
- package/docs/adr/0004-page-content-state-is-composed.md +65 -0
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +180 -0
- package/docs/adr/0006-semantic-context-precedes-visual-variant.md +182 -0
- 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/instructions/opus.md +5 -0
- package/registry/skills/build-opus-ui/SKILL.md +27 -16
- package/registry/skills/build-opus-ui/references/evaluations.md +16 -5
- package/registry/skills/build-opus-ui/references/ui-patterns.md +38 -15
- 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/registry/skills/model-opus-dictionary/SKILL.md +4 -2
- package/registry/skills/model-opus-dictionary/references/evaluations.md +4 -3
- package/registry/templates/app/src/App.tsx +1 -1
- package/src/core/dictionary.ts +52 -14
- package/src/core/index.ts +10 -0
- package/src/core/ui-context.ts +29 -0
- package/src/schema/drivers/zod.ts +17 -8
- package/src/ui/components/patterns/action-form-card.tsx +18 -12
- package/src/ui/components/patterns/confirm.tsx +163 -40
- package/src/ui/components/patterns/content-header.tsx +335 -61
- package/src/ui/components/patterns/data-state.tsx +23 -10
- package/src/ui/components/patterns/list.tsx +1097 -783
- package/src/ui/components/patterns/page-state.tsx +115 -0
- package/src/ui/components/patterns/page.tsx +231 -41
- package/src/ui/components/patterns/sidebar.tsx +357 -83
- package/src/ui/components/patterns/trigger.tsx +37 -30
- package/src/ui/components/patterns/view.tsx +7 -11
- package/src/ui/components/primitives/alert.tsx +298 -110
- package/src/ui/components/primitives/ask.tsx +2 -1
- package/src/ui/components/primitives/badge.tsx +91 -30
- package/src/ui/components/primitives/button.tsx +99 -60
- package/src/ui/components/primitives/calendar.tsx +39 -39
- package/src/ui/components/primitives/card.tsx +96 -23
- package/src/ui/components/primitives/detail.tsx +2 -2
- package/src/ui/components/primitives/dialog.tsx +196 -39
- package/src/ui/components/primitives/dictionary-value.tsx +9 -14
- package/src/ui/components/primitives/dot.tsx +74 -21
- package/src/ui/components/primitives/drawer.tsx +40 -24
- package/src/ui/components/primitives/empty.tsx +3 -3
- package/src/ui/components/primitives/item.tsx +135 -79
- package/src/ui/components/primitives/menu.tsx +11 -3
- package/src/ui/components/primitives/metric-card.tsx +133 -0
- package/src/ui/components/primitives/sonner.tsx +187 -8
- package/src/ui/components/primitives/table.tsx +2 -2
- package/src/ui/docs/DocBrowser.tsx +104 -25
- package/src/ui/docs/changelog.tsx +1 -1
- 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 +54 -28
- 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 +21 -22
- package/src/ui/docs/content/breadcrumb.md +13 -8
- package/src/ui/docs/content/button.md +93 -15
- package/src/ui/docs/content/calendar.md +5 -5
- package/src/ui/docs/content/card.md +6 -6
- 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 +44 -0
- package/src/ui/docs/content/copyable.md +4 -3
- package/src/ui/docs/content/customization.md +7 -7
- 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 +8 -5
- package/src/ui/docs/content/dialog.md +339 -31
- package/src/ui/docs/content/dictionary-value.md +19 -18
- package/src/ui/docs/content/dock.md +3 -3
- package/src/ui/docs/content/dot.md +7 -7
- package/src/ui/docs/content/drawer.md +32 -16
- 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 +64 -24
- 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 +36 -17
- package/src/ui/docs/content/metric-card.md +41 -0
- package/src/ui/docs/content/observability.md +2 -2
- package/src/ui/docs/content/page.md +93 -10
- 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/semantic-context.md +63 -0
- package/src/ui/docs/content/separator.md +5 -5
- package/src/ui/docs/content/sidebar.md +325 -56
- package/src/ui/docs/content/skeleton.md +5 -4
- 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 +16 -6
- 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 +31 -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/doc-client.tsx +2 -2
- package/src/ui/docs/registry.tsx +580 -229
- package/src/ui/lib/semantic-context.ts +30 -0
- package/src/ui/meta.ts +278 -286
- package/src/ui/react.tsx +377 -111
- package/src/ui/theme.css +116 -0
- package/src/ui/components/primitives/alert-dialog.tsx +0 -190
- 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 -78
- package/src/ui/docs/content/toggle-group.md +0 -81
|
@@ -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
|
-
|
|
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>
|
|
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
|
-
</
|
|
19
|
-
<PaneFooter
|
|
33
|
+
</PaneBody>
|
|
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>
|
|
@@ -22,10 +23,10 @@ O esqueleto reproduz o layout final do card — título, descrição, conteúdo
|
|
|
22
23
|
<Skeleton className="h-5 w-40" />
|
|
23
24
|
<Skeleton className="h-4 w-56" />
|
|
24
25
|
</CardHeader>
|
|
25
|
-
<
|
|
26
|
+
<CardBody className="space-y-2">
|
|
26
27
|
<Skeleton className="h-4 w-full" />
|
|
27
28
|
<Skeleton className="h-4 w-3/4" />
|
|
28
|
-
</
|
|
29
|
+
</CardBody>
|
|
29
30
|
<CardFooter className="gap-2">
|
|
30
31
|
<Skeleton className="h-8 w-28" />
|
|
31
32
|
<Skeleton className="h-8 w-24" />
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Um valor na faixa
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `Slider` para escolher um valor dentro de uma faixa. `defaultValue` recebe um array; `[n]`
|
|
4
|
+
representa um único controle. `min`, `max` e `step` delimitam os valores disponíveis.
|
|
4
5
|
|
|
5
6
|
```tsx preview col
|
|
6
7
|
<Slider defaultValue={[40]} min={0} max={100} step={5} />
|
|
@@ -8,7 +9,7 @@ defaultValue é um array — [n] pra um thumb. min, max e step delimitam a faixa
|
|
|
8
9
|
|
|
9
10
|
## Controlado
|
|
10
11
|
|
|
11
|
-
onValueChange recebe o array atualizado
|
|
12
|
+
`onValueChange` recebe o array atualizado. Para um único controle, leia `value[0]`.
|
|
12
13
|
|
|
13
14
|
```tsx preview col
|
|
14
15
|
const [parallelism, setParallelism] = useState([4])
|
|
@@ -26,7 +27,7 @@ render(
|
|
|
26
27
|
|
|
27
28
|
## Intervalo
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
Dois números em `value` criam um intervalo selecionável entre dois controles.
|
|
30
31
|
|
|
31
32
|
```tsx preview col
|
|
32
33
|
const [budget, setBudget] = useState([20, 60])
|
|
@@ -50,11 +51,11 @@ disabled esmaece o trilho e o thumb e bloqueia o arraste — o valor fica congel
|
|
|
50
51
|
<Slider defaultValue={[70]} min={0} max={100} disabled />
|
|
51
52
|
```
|
|
52
53
|
|
|
53
|
-
##
|
|
54
|
+
## Propriedades de Slider
|
|
54
55
|
|
|
55
|
-
|
|
|
56
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
56
57
|
|---|---|---|---|
|
|
57
|
-
| `value` | `number[]` | | O valor no modo controlado — [n]
|
|
58
|
+
| `value` | `number[]` | | O valor no modo controlado — [n] para um thumb, [a, b] para um intervalo. Pareie com onValueChange. |
|
|
58
59
|
| `onValueChange` | `(value: number[]) => void` | | Chamado a cada arraste, com o array atualizado. |
|
|
59
60
|
| `defaultValue` | `number[]` | | Valor inicial no modo não controlado. |
|
|
60
61
|
| `min` | `number` | `0` | Limite inferior da faixa. |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Carregamento sem progresso conhecido
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Use `Spinner` quando a duração ou o progresso da espera não forem conhecidos. `sm` atende ações
|
|
4
|
+
compactas; `lg`, estados mais amplos. O componente já possui o nome acessível “Carregando”.
|
|
5
5
|
|
|
6
6
|
```tsx preview
|
|
7
7
|
<Spinner size="sm" />
|
|
@@ -11,8 +11,8 @@ Divergência da casa: o shadcn atual só tem size-4 fixo; mantivemos sm/default/
|
|
|
11
11
|
|
|
12
12
|
## No botão
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
Durante uma ação, combine o spinner compacto com o estado desabilitado e um rótulo que descreva o
|
|
15
|
+
andamento.
|
|
16
16
|
|
|
17
17
|
```tsx preview
|
|
18
18
|
<Button disabled><Spinner size="sm" /> Publicando…</Button>
|
|
@@ -30,8 +30,8 @@ Skeleton.
|
|
|
30
30
|
</div>
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
##
|
|
33
|
+
## Propriedades de Spinner
|
|
34
34
|
|
|
35
|
-
|
|
|
35
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
36
36
|
|---|---|---|---|
|
|
37
|
-
| `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | O tamanho — sm
|
|
37
|
+
| `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | O tamanho — sm para dentro de botão, lg para estados de página. |
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Dividir uma área
|
|
2
2
|
|
|
3
|
-
`Split`
|
|
3
|
+
Use `Split` para organizar áreas lado a lado ou empilhadas. A ordem dos `Pane` define a posição. Com
|
|
4
|
+
`resizable`, a pessoa pode ajustar as divisórias por ponteiro ou teclado; sem essa propriedade, o
|
|
5
|
+
layout permanece estático.
|
|
4
6
|
|
|
5
7
|
```tsx preview
|
|
6
8
|
render(
|
|
@@ -17,11 +19,12 @@ render(
|
|
|
17
19
|
)
|
|
18
20
|
```
|
|
19
21
|
|
|
20
|
-
##
|
|
22
|
+
## Espaçamento interno
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
`Pane` usa `inset="md"` por padrão. Use `none` para navegação e conteúdo que já controla o próprio
|
|
25
|
+
espaçamento; `sm` e `lg` atendem outras densidades.
|
|
23
26
|
|
|
24
|
-
##
|
|
27
|
+
## Largura inicial
|
|
25
28
|
|
|
26
29
|
Quando uma área precisa continuar legível em telas largas, use uma unidade absoluta para o tamanho inicial ou mínimo. Números continuam representando porcentagens; strings aceitam `%`, `rem`, `em`, `vh`, `vw` e `px`. Prefira `rem` para acompanhar a escala tipográfica configurada pela aplicação.
|
|
27
30
|
|
|
@@ -7,10 +7,8 @@ title: Armazenamento
|
|
|
7
7
|
> **Experimental.** Superfície mínima, sem consumidor interno ainda — cresce por
|
|
8
8
|
> reincidência de caso real, não por especulação.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
(`StorageAdapter`): a key é um **caminho lógico** (`clientes/123/contrato.pdf`) e o
|
|
13
|
-
driver resolve onde ela mora.
|
|
10
|
+
Use `StorageAdapter` para upload, download, remoção e geração de URL de arquivos. A chave é um
|
|
11
|
+
caminho lógico, como `clientes/123/contrato.pdf`; cada driver decide onde o conteúdo é armazenado.
|
|
14
12
|
|
|
15
13
|
## O contrato
|
|
16
14
|
|
|
@@ -23,8 +21,8 @@ interface StorageAdapter {
|
|
|
23
21
|
}
|
|
24
22
|
```
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
`normalizeKey` aplica a mesma validação a todos os drivers: chaves são relativas e não aceitam
|
|
25
|
+
`..` nem `\`. Assim, tentativas de escapar do diretório são bloqueadas antes do armazenamento.
|
|
28
26
|
|
|
29
27
|
## Drivers
|
|
30
28
|
|
|
@@ -44,7 +42,7 @@ const prod = s3Storage({
|
|
|
44
42
|
})
|
|
45
43
|
```
|
|
46
44
|
|
|
47
|
-
Os peers do S3 são **opcionais** (`@aws-sdk/client-s3`; `s3-request-presigner`
|
|
45
|
+
Os peers do S3 são **opcionais** (`@aws-sdk/client-s3`; `s3-request-presigner` para o
|
|
48
46
|
`url()`): quem não usa o driver não os instala — importados sob demanda.
|
|
49
47
|
|
|
50
48
|
## No runtime
|
|
@@ -62,7 +60,7 @@ handler: async (ctx, input) => {
|
|
|
62
60
|
}
|
|
63
61
|
```
|
|
64
62
|
|
|
65
|
-
## Limites
|
|
63
|
+
## Limites atuais
|
|
66
64
|
|
|
67
65
|
Bytes em memória (`Uint8Array`), sem streams — arquivo gigante ainda não é o caso;
|
|
68
66
|
sem `list`, sem metadata rica. Cada um entra quando um projeto real cobrar — é a
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Alternar uma preferência
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `Switch` para uma preferência booleana que entra em vigor imediatamente. Associe o controle a
|
|
4
|
+
`Label`; `defaultChecked` define o estado inicial no modo não controlado.
|
|
4
5
|
|
|
5
6
|
```tsx preview
|
|
6
7
|
<div className="flex items-center gap-2">
|
|
@@ -11,7 +12,7 @@ Sempre em par com Label (htmlFor↔id) — clicar no texto alterna a chave. defa
|
|
|
11
12
|
|
|
12
13
|
## Controlado
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
Use `checked` e `onCheckedChange` quando o estado pertencer ao consumidor.
|
|
15
16
|
|
|
16
17
|
```tsx preview
|
|
17
18
|
const [autoReview, setAutoReview] = useState(true)
|
|
@@ -30,7 +31,7 @@ render(
|
|
|
30
31
|
|
|
31
32
|
## Tamanho sm
|
|
32
33
|
|
|
33
|
-
size=sm
|
|
34
|
+
Use `size="sm"` em linhas de lista e outras composições compactas.
|
|
34
35
|
|
|
35
36
|
```tsx preview col-start
|
|
36
37
|
<div className="flex items-center gap-2">
|
|
@@ -58,12 +59,12 @@ disabled esmaece e bloqueia a chave — ligada ou desligada — e o rótulo em p
|
|
|
58
59
|
</div>
|
|
59
60
|
```
|
|
60
61
|
|
|
61
|
-
##
|
|
62
|
+
## Propriedades de Switch
|
|
62
63
|
|
|
63
|
-
|
|
|
64
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
64
65
|
|---|---|---|---|
|
|
65
66
|
| `checked` | `boolean` | | O estado, no modo controlado — parear com onCheckedChange. |
|
|
66
67
|
| `onCheckedChange` | `(checked: boolean) => void` | | Chamado a cada alternância, com o novo estado. |
|
|
67
68
|
| `defaultChecked` | `boolean` | `false` | Estado inicial no modo não controlado. |
|
|
68
|
-
| `size` | `'sm' \| 'default'` | `'default'` | Tamanho da chave — sm
|
|
69
|
+
| `size` | `'sm' \| 'default'` | `'default'` | Tamanho da chave — sm para densidade em linha de lista. |
|
|
69
70
|
| `disabled` | `boolean` | `false` | Esmaece e bloqueia — o Label em par esmaece junto (peer-disabled). |
|