@softize/opus 19.0.0 → 20.1.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 +239 -0
- package/bin/cli.mjs +5 -0
- package/bin/lib/check.mjs +198 -10
- package/bin/lib/copy.mjs +29 -0
- package/bin/lib/mcp.mjs +1 -1
- package/docs/adr/0020-icon-tiles-are-explicitly-composed.md +8 -0
- package/docs/adr/0021-content-carries-structure-and-semantic-element.md +7 -0
- package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +19 -1
- package/docs/adr/0024-fill-height-propagates-by-context.md +118 -0
- package/docs/adr/0025-content-frame-treatment-does-not-change-semantics.md +134 -0
- package/docs/data-products.md +15 -9
- package/docs/protocol.md +3 -4
- package/docs/releasing.md +2 -2
- package/package.json +2 -2
- package/registry/skills/create-opus-action/scripts/scaffold.mjs +2 -1
- package/registry/templates/app/package.json +1 -1
- package/src/core/runtime.ts +96 -8
- package/src/core/types.ts +5 -2
- package/src/ui/components/patterns/action-form-card.tsx +11 -1
- package/src/ui/components/patterns/content-header.tsx +109 -18
- package/src/ui/components/patterns/fill-region.ts +150 -0
- package/src/ui/components/patterns/list.tsx +91 -92
- package/src/ui/components/patterns/page.tsx +20 -4
- package/src/ui/components/patterns/presentation.tsx +52 -37
- package/src/ui/components/patterns/split.tsx +44 -4
- package/src/ui/components/patterns/surface-anatomy.ts +35 -8
- package/src/ui/components/patterns/surface-header.tsx +58 -25
- package/src/ui/components/patterns/trigger.tsx +12 -2
- package/src/ui/components/primitives/alert.tsx +9 -1
- package/src/ui/components/primitives/avatar.tsx +7 -3
- package/src/ui/components/primitives/card.tsx +1 -1
- package/src/ui/components/primitives/carousel.tsx +34 -5
- package/src/ui/components/primitives/checkbox.tsx +11 -3
- package/src/ui/components/primitives/command.tsx +2 -1
- package/src/ui/components/primitives/copyable.tsx +120 -10
- package/src/ui/components/primitives/dialog.tsx +42 -11
- package/src/ui/components/primitives/drawer.tsx +10 -2
- package/src/ui/components/primitives/empty.tsx +1 -1
- package/src/ui/components/primitives/icon-picker.tsx +6 -2
- package/src/ui/components/primitives/icon-tile.tsx +7 -0
- package/src/ui/components/primitives/input-group.tsx +9 -2
- package/src/ui/components/primitives/input-otp.tsx +1 -1
- package/src/ui/components/primitives/kbd.tsx +1 -1
- package/src/ui/components/primitives/label.tsx +3 -0
- package/src/ui/components/primitives/menu.tsx +5 -0
- package/src/ui/components/primitives/popover.tsx +1 -1
- package/src/ui/components/primitives/progress.tsx +9 -1
- package/src/ui/components/primitives/slider.tsx +62 -11
- package/src/ui/components/primitives/truncate.tsx +10 -1
- package/src/ui/docs/content/accordion.md +74 -27
- package/src/ui/docs/content/action-form-card.md +24 -15
- package/src/ui/docs/content/action-form-dialog.md +17 -7
- package/src/ui/docs/content/action-form.md +92 -77
- package/src/ui/docs/content/action-list-dialog.md +75 -38
- package/src/ui/docs/content/action-list.md +141 -58
- package/src/ui/docs/content/action-trigger.md +27 -10
- package/src/ui/docs/content/action-view.md +14 -14
- package/src/ui/docs/content/actions.md +161 -66
- package/src/ui/docs/content/ai.md +43 -44
- package/src/ui/docs/content/alert.md +83 -29
- package/src/ui/docs/content/ask.md +51 -26
- package/src/ui/docs/content/aspect-ratio.md +22 -24
- package/src/ui/docs/content/audit.md +23 -19
- package/src/ui/docs/content/auth.md +38 -19
- package/src/ui/docs/content/avatar.md +54 -5
- package/src/ui/docs/content/badge.md +29 -9
- package/src/ui/docs/content/breadcrumb.md +21 -8
- package/src/ui/docs/content/button.md +73 -43
- package/src/ui/docs/content/calendar.md +134 -26
- package/src/ui/docs/content/card.md +41 -15
- package/src/ui/docs/content/carousel.md +37 -6
- package/src/ui/docs/content/chat.md +118 -37
- package/src/ui/docs/content/checkbox.md +73 -7
- package/src/ui/docs/content/cli.md +94 -55
- package/src/ui/docs/content/collapsible.md +43 -19
- package/src/ui/docs/content/command.md +30 -8
- package/src/ui/docs/content/composer.md +23 -8
- package/src/ui/docs/content/content.md +119 -27
- package/src/ui/docs/content/copyable.md +39 -8
- package/src/ui/docs/content/customization.md +55 -46
- package/src/ui/docs/content/cycle.md +33 -18
- package/src/ui/docs/content/data-products.md +115 -0
- package/src/ui/docs/content/data-state.md +20 -6
- package/src/ui/docs/content/data.md +20 -19
- package/src/ui/docs/content/detail.md +12 -5
- package/src/ui/docs/content/dialog.md +107 -24
- package/src/ui/docs/content/dictionary-value.md +4 -4
- package/src/ui/docs/content/dock.md +54 -19
- package/src/ui/docs/content/dot.md +35 -5
- package/src/ui/docs/content/drawer.md +63 -19
- package/src/ui/docs/content/empty-value.md +20 -6
- package/src/ui/docs/content/empty.md +24 -3
- package/src/ui/docs/content/events.md +76 -16
- package/src/ui/docs/content/executions.md +9 -9
- package/src/ui/docs/content/field.md +21 -6
- package/src/ui/docs/content/getting-started.md +44 -34
- package/src/ui/docs/content/icon-picker.md +19 -13
- package/src/ui/docs/content/icon-tile.md +38 -15
- package/src/ui/docs/content/input-otp.md +58 -7
- package/src/ui/docs/content/input.md +22 -3
- package/src/ui/docs/content/item.md +53 -6
- package/src/ui/docs/content/kbd.md +5 -1
- package/src/ui/docs/content/label.md +13 -4
- package/src/ui/docs/content/log.md +5 -5
- package/src/ui/docs/content/markdown.md +5 -4
- package/src/ui/docs/content/mcp.md +49 -23
- package/src/ui/docs/content/menu.md +95 -10
- package/src/ui/docs/content/metric-card.md +12 -4
- package/src/ui/docs/content/observability.md +10 -9
- package/src/ui/docs/content/page.md +118 -12
- package/src/ui/docs/content/pagination.md +13 -5
- package/src/ui/docs/content/pane.md +55 -5
- package/src/ui/docs/content/popover.md +42 -5
- package/src/ui/docs/content/presentation.md +91 -47
- package/src/ui/docs/content/progress.md +27 -14
- package/src/ui/docs/content/queue.md +39 -21
- package/src/ui/docs/content/radio-group.md +26 -4
- package/src/ui/docs/content/router.md +29 -10
- package/src/ui/docs/content/runtime.md +90 -54
- package/src/ui/docs/content/scheduler.md +53 -21
- package/src/ui/docs/content/scroll-area.md +42 -35
- package/src/ui/docs/content/select.md +78 -33
- package/src/ui/docs/content/semantic-context.md +21 -2
- package/src/ui/docs/content/separator.md +15 -9
- package/src/ui/docs/content/sidebar.md +3 -2
- package/src/ui/docs/content/skeleton.md +6 -1
- package/src/ui/docs/content/slider.md +37 -9
- package/src/ui/docs/content/spinner.md +4 -3
- package/src/ui/docs/content/split.md +19 -0
- package/src/ui/docs/content/storage.md +8 -9
- package/src/ui/docs/content/switch.md +10 -7
- package/src/ui/docs/content/table.md +46 -8
- package/src/ui/docs/content/tabs.md +14 -4
- package/src/ui/docs/content/testing.md +76 -31
- package/src/ui/docs/content/textarea.md +5 -4
- package/src/ui/docs/content/toast.md +48 -7
- package/src/ui/docs/content/toggle.md +3 -3
- package/src/ui/docs/content/tokens.md +15 -26
- package/src/ui/docs/content/tooltip.md +18 -2
- package/src/ui/docs/content/truncate.md +36 -6
- package/src/ui/docs/content/ui.md +74 -22
- package/src/ui/docs/content/upgrading.md +45 -36
- package/src/ui/docs/plugin.ts +36 -2
- package/src/ui/docs/registry.tsx +6 -0
- package/src/ui/drivers/react.tsx +16 -8
- package/src/ui/meta.ts +32 -32
- package/src/ui/react.tsx +1 -0
- package/src/vite/design.ts +16 -4
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
## Escolha em lista
|
|
2
2
|
|
|
3
|
-
Use `Select`
|
|
4
|
-
|
|
3
|
+
Use `Select` quando a pessoa precisar escolher um valor em uma lista que não cabe, inteira, na tela.
|
|
4
|
+
Para poucas opções que devem ficar visíveis ao mesmo tempo, use [`RadioGroup`](/ui/radio-group).
|
|
5
|
+
Declare as opções como dados em `options`; `label` é usado no campo, na busca e na leitura
|
|
6
|
+
assistiva. Associe `id` ao `htmlFor` de `Label` em formulários.
|
|
5
7
|
|
|
6
8
|
```tsx preview col md
|
|
7
9
|
const [role, setRole] = useState('')
|
|
@@ -16,7 +18,7 @@ render(
|
|
|
16
18
|
placeholder="Selecione o papel"
|
|
17
19
|
options={[
|
|
18
20
|
{ value: 'designer', label: 'Designer' },
|
|
19
|
-
{ value: 'developer', label: '
|
|
21
|
+
{ value: 'developer', label: 'Desenvolvedor' },
|
|
20
22
|
{ value: 'reviewer', label: 'Revisor' },
|
|
21
23
|
]}
|
|
22
24
|
/>
|
|
@@ -24,9 +26,10 @@ render(
|
|
|
24
26
|
)
|
|
25
27
|
```
|
|
26
28
|
|
|
27
|
-
O gatilho respeita a largura definida pelo contexto.
|
|
28
|
-
e pode crescer até `32rem` para
|
|
29
|
-
toolbar.
|
|
29
|
+
O gatilho respeita a largura definida pelo contexto. Na variante padrão, a lista aberta usa essa
|
|
30
|
+
largura como mínimo e pode crescer até `32rem` para mostrar rótulos maiores sem alterar o layout do
|
|
31
|
+
formulário ou da toolbar. Nas variantes `outline` e `ghost`, a lista tem largura própria, entre
|
|
32
|
+
`14rem` e `20rem`. Rótulos que ultrapassarem o limite são truncados.
|
|
30
33
|
|
|
31
34
|
## Lista pesquisável
|
|
32
35
|
|
|
@@ -67,7 +70,7 @@ const [skills, setSkills] = useState<string[]>(['clean-code'])
|
|
|
67
70
|
|
|
68
71
|
render(
|
|
69
72
|
<div className="grid gap-2">
|
|
70
|
-
<Label htmlFor="agent-skills">
|
|
73
|
+
<Label htmlFor="agent-skills">Habilidades do agente</Label>
|
|
71
74
|
<Select
|
|
72
75
|
multiple
|
|
73
76
|
searchable
|
|
@@ -128,7 +131,7 @@ render(
|
|
|
128
131
|
options={[
|
|
129
132
|
{ value: 'joao', label: 'João Campos', group: 'Membros' },
|
|
130
133
|
{ value: 'rafa', label: 'Rafa', group: 'Membros' },
|
|
131
|
-
{ value: 'developer', label: '
|
|
134
|
+
{ value: 'developer', label: 'Desenvolvedor', group: 'Agentes' },
|
|
132
135
|
{ value: 'reviewer', label: 'Revisor', group: 'Agentes' },
|
|
133
136
|
]}
|
|
134
137
|
/>,
|
|
@@ -144,7 +147,7 @@ Com `onSearch`, o consumidor consulta as opções ao abrir e ao digitar, com deb
|
|
|
144
147
|
const ASSIGNEES = [
|
|
145
148
|
{ value: 'joao', label: 'João Campos', hint: 'humano' },
|
|
146
149
|
{ value: 'designer', label: 'Designer', hint: 'IA' },
|
|
147
|
-
{ value: 'developer', label: '
|
|
150
|
+
{ value: 'developer', label: 'Desenvolvedor', hint: 'IA' },
|
|
148
151
|
{ value: 'reviewer', label: 'Revisor', hint: 'IA' },
|
|
149
152
|
]
|
|
150
153
|
|
|
@@ -188,7 +191,7 @@ render(
|
|
|
188
191
|
onChange={setTask}
|
|
189
192
|
placeholder="Vincular tarefa"
|
|
190
193
|
options={[
|
|
191
|
-
{ value: 'gra-2', label: 'Nova tela anotações', hint: 'GRA-2' },
|
|
194
|
+
{ value: 'gra-2', label: 'Nova tela de anotações', hint: 'GRA-2' },
|
|
192
195
|
{ value: 'gra-7', label: 'Filtro por filial', hint: 'GRA-7' },
|
|
193
196
|
]}
|
|
194
197
|
/>,
|
|
@@ -210,7 +213,7 @@ render(
|
|
|
210
213
|
icon={<Ticket />}
|
|
211
214
|
value={a}
|
|
212
215
|
onChange={setA}
|
|
213
|
-
options={[{ value: 'gra-2', label: 'Nova tela anotações', hint: 'GRA-2' }]}
|
|
216
|
+
options={[{ value: 'gra-2', label: 'Nova tela de anotações', hint: 'GRA-2' }]}
|
|
214
217
|
/>
|
|
215
218
|
<Button size="sm" variant="outline">Filtrar</Button>
|
|
216
219
|
</div>,
|
|
@@ -220,7 +223,8 @@ render(
|
|
|
220
223
|
## Limpar a seleção
|
|
221
224
|
|
|
222
225
|
`clearable` acrescenta uma ação que limpa o valor sem abrir a lista. No modo simples, o valor volta
|
|
223
|
-
a vazio; no múltiplo, volta a `[]`. Os filtros de `ActionList` já habilitam
|
|
226
|
+
a vazio; no múltiplo, volta a `[]`. Os filtros de [`ActionList`](/ui/action-list) já habilitam
|
|
227
|
+
esse comportamento.
|
|
224
228
|
|
|
225
229
|
```tsx preview col md
|
|
226
230
|
function Demo() {
|
|
@@ -260,12 +264,12 @@ render(
|
|
|
260
264
|
options={[
|
|
261
265
|
{
|
|
262
266
|
value: 'developer',
|
|
263
|
-
label: '
|
|
267
|
+
label: 'Desenvolvedor',
|
|
264
268
|
content: (
|
|
265
269
|
<span className="flex items-center gap-2">
|
|
266
270
|
<Bot className="size-4 text-muted-foreground" />
|
|
267
271
|
<span>
|
|
268
|
-
|
|
272
|
+
Desenvolvedor <span className="text-muted-foreground">· implementa</span>
|
|
269
273
|
</span>
|
|
270
274
|
</span>
|
|
271
275
|
),
|
|
@@ -289,21 +293,22 @@ render(
|
|
|
289
293
|
|
|
290
294
|
## Select na barra do Composer
|
|
291
295
|
|
|
292
|
-
Use `variant="ghost"` para integrar o seletor à barra de ações de `Composer
|
|
293
|
-
encurtar somente o texto do gatilho; a lista continua exibindo o `label`
|
|
296
|
+
Use `variant="ghost"` para integrar o seletor à barra de ações de [`Composer`](/ui/composer).
|
|
297
|
+
`triggerLabel` pode encurtar somente o texto do gatilho; a lista continua exibindo o `label`
|
|
298
|
+
completo.
|
|
294
299
|
|
|
295
300
|
```tsx preview col-start
|
|
296
|
-
const [
|
|
301
|
+
const [order, setOrder] = useState('OS-1842')
|
|
297
302
|
|
|
298
303
|
render(
|
|
299
304
|
<Select
|
|
300
305
|
variant="ghost"
|
|
301
|
-
value={
|
|
302
|
-
onChange={
|
|
306
|
+
value={order}
|
|
307
|
+
onChange={setOrder}
|
|
303
308
|
options={[
|
|
304
|
-
{ value: '', label: 'Sem
|
|
305
|
-
{ value: '
|
|
306
|
-
{ value: '
|
|
309
|
+
{ value: '', label: 'Sem ordem de serviço' },
|
|
310
|
+
{ value: 'OS-1842', label: 'Revisão dos 20.000 km', hint: 'OS-1842', triggerLabel: 'OS-1842' },
|
|
311
|
+
{ value: 'OS-1839', label: 'Troca das pastilhas de freio', hint: 'OS-1839', triggerLabel: 'OS-1839' },
|
|
307
312
|
]}
|
|
308
313
|
/>,
|
|
309
314
|
)
|
|
@@ -324,7 +329,7 @@ render(
|
|
|
324
329
|
onChange={setTask}
|
|
325
330
|
className="w-72"
|
|
326
331
|
options={[
|
|
327
|
-
{ value: 'gra-2', label: 'Nova tela anotações', hint: 'GRA-2' },
|
|
332
|
+
{ value: 'gra-2', label: 'Nova tela de anotações', hint: 'GRA-2' },
|
|
328
333
|
{ value: 'gra-7', label: 'Filtro por filial', hint: 'GRA-7' },
|
|
329
334
|
]}
|
|
330
335
|
trailing={
|
|
@@ -336,26 +341,66 @@ render(
|
|
|
336
341
|
)
|
|
337
342
|
```
|
|
338
343
|
|
|
344
|
+
## Estados
|
|
345
|
+
|
|
346
|
+
`disabled` esmaece o controle e impede a abertura da lista. `aria-invalid` aplica o contexto de
|
|
347
|
+
erro; associe a mensagem por `aria-describedby`.
|
|
348
|
+
|
|
349
|
+
```tsx preview col md
|
|
350
|
+
const [unit, setUnit] = useState('')
|
|
351
|
+
|
|
352
|
+
render(
|
|
353
|
+
<div className="grid gap-4">
|
|
354
|
+
<Field data-invalid="true">
|
|
355
|
+
<FieldLabel htmlFor="unit">Unidade</FieldLabel>
|
|
356
|
+
<Select
|
|
357
|
+
id="unit"
|
|
358
|
+
value={unit}
|
|
359
|
+
onChange={setUnit}
|
|
360
|
+
placeholder="Selecione a unidade"
|
|
361
|
+
aria-invalid
|
|
362
|
+
aria-describedby="unit-error"
|
|
363
|
+
options={[
|
|
364
|
+
{ value: 'centro', label: 'Centro' },
|
|
365
|
+
{ value: 'norte', label: 'Zona Norte' },
|
|
366
|
+
]}
|
|
367
|
+
/>
|
|
368
|
+
<FieldError id="unit-error">Escolha a unidade responsável.</FieldError>
|
|
369
|
+
</Field>
|
|
370
|
+
<Field data-disabled="true">
|
|
371
|
+
<FieldLabel htmlFor="region">Região</FieldLabel>
|
|
372
|
+
<Select
|
|
373
|
+
id="region"
|
|
374
|
+
value="sudeste"
|
|
375
|
+
onChange={() => undefined}
|
|
376
|
+
disabled
|
|
377
|
+
options={[{ value: 'sudeste', label: 'Sudeste' }]}
|
|
378
|
+
/>
|
|
379
|
+
</Field>
|
|
380
|
+
</div>,
|
|
381
|
+
)
|
|
382
|
+
```
|
|
383
|
+
|
|
339
384
|
## Propriedades de Select
|
|
340
385
|
|
|
341
386
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
342
387
|
|---|---|---|---|
|
|
343
388
|
| `options` | `SelectOption[]` | | As opções: `{ value, label, hint?, content?, triggerLabel?, group?, disabled? }`. |
|
|
344
389
|
| `value` | `string \| string[]` | | O selecionado: string no single, string[] no multiple. |
|
|
345
|
-
| `onChange` | `(value: string) => void \| (value: string[]) => void` | | Chamado ao escolher (e ao remover chip, no multiple) — a assinatura segue o modo. |
|
|
390
|
+
| `onChange` | `((value: string) => void) \| ((value: string[]) => void)` | | Chamado ao escolher (e ao remover chip, no multiple) — a assinatura segue o modo. |
|
|
346
391
|
| `native` | `boolean` | `false` | Renderiza o `<select>` do sistema. Exclui busca, seleção múltipla e variantes visuais (o browser é quem desenha a lista). |
|
|
347
392
|
| `searchable` | `boolean` | `false` | Acrescenta uma linha de busca no topo da lista, que filtra as opções enquanto a pessoa digita. O gatilho continua exibindo a escolha. |
|
|
348
|
-
| `multiple` | `boolean` | `false` |
|
|
349
|
-
| `variant` | `'default' \| 'outline' \| 'ghost'` | `'default'` | `outline`
|
|
350
|
-
| `placeholder` | `string` | `'Selecione…'` | Texto do campo vazio. |
|
|
393
|
+
| `multiple` | `boolean` | `false` | Seleção de vários valores: chips removíveis, lista que permanece aberta e a opção “Selecionar tudo”. |
|
|
394
|
+
| `variant` | `'default' \| 'outline' \| 'ghost'` | `'default'` | `outline` é compacto e mantém a borda; `ghost` é compacto, sem borda nem fundo, para a barra do `Composer`. |
|
|
395
|
+
| `placeholder` | `string` | `'Selecione…'`; sem padrão com `native` | Texto do campo vazio. Com `native`, só existe quando declarado e vira uma opção desabilitada no início da lista. |
|
|
351
396
|
| `searchPlaceholder` | `string` | `'Buscar…'` | Placeholder da linha de busca dentro da lista. |
|
|
352
397
|
| `emptyText` | `string` | `'Nada encontrado.'` | Mensagem quando a busca não acha nada. |
|
|
353
|
-
| `onSearch` | `(query: string) => void` | | Busca
|
|
354
|
-
| `loading` | `boolean` | | Mostra Buscando
|
|
355
|
-
| `clearable` | `boolean` | `false` |
|
|
356
|
-
| `icon` | `React.ReactNode` | | Ícone
|
|
357
|
-
| `trailing` | `React.ReactNode` | | Ação
|
|
358
|
-
| `size` | `'default' \| 'sm'` | `'default'` | Altura: default (
|
|
398
|
+
| `onSearch` | `(query: string) => void` | | Busca no servidor, chamada ao abrir a lista e depois de uma breve pausa na digitação. Desliga o filtro local; o consumidor atualiza `options` com a resposta. |
|
|
399
|
+
| `loading` | `boolean` | | Mostra “Buscando…” enquanto a consulta de `onSearch` está em andamento. |
|
|
400
|
+
| `clearable` | `boolean` | `false` | Mostra, quando há seleção, uma ação que limpa o valor sem abrir a lista. |
|
|
401
|
+
| `icon` | `React.ReactNode` | | Ícone decorativo no início do controle, em `1rem` e no tom atenuado. |
|
|
402
|
+
| `trailing` | `React.ReactNode` | | Ação própria no fim do controle, antes do chevron; interagir com ela não abre a lista. |
|
|
403
|
+
| `size` | `'default' \| 'sm'` | `'default'` | Altura na escala dos controles: `default` (`2.25rem`, a de `Input` e `Button`) ou `sm` (`2rem`) para toolbars densas. |
|
|
359
404
|
| `shape` | `'default' \| 'pill'` | `'default'` | Geometria do controle; `pill` arredonda as extremidades e preserva a variante visual. |
|
|
360
405
|
| `disabled` | `boolean` | `false` | Esmaece e trava o controle. |
|
|
361
406
|
| `id` | `string` | | Vai para o campo — para parear com o `htmlFor` do Label. |
|
|
@@ -41,10 +41,29 @@ aceita o subconjunto coerente com seu papel.
|
|
|
41
41
|
</div>
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
## Controles sem contexto
|
|
45
|
+
|
|
46
|
+
Alguns controles também têm `variant`, mas não têm `context`. Neles, `variant` não está ligado a um
|
|
47
|
+
significado: descreve o formato do próprio controle.
|
|
48
|
+
|
|
49
|
+
O par `context` + `variant` vale para componentes que comunicam significado, como
|
|
50
|
+
[`Button`](/ui/button), [`Badge`](/ui/badge), [`Alert`](/ui/alert), [`Dot`](/ui/dot) e
|
|
51
|
+
[`IconTile`](/ui/icon-tile). Os controles abaixo não aceitam `context`:
|
|
52
|
+
|
|
53
|
+
| Componente | Valores | O que muda |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| [`Select`](/ui/select) | `default`, `outline`, `ghost` | `default` é o campo completo; `outline` é compacto com borda, e `ghost`, compacto sem borda nem fundo. |
|
|
56
|
+
| [`Toggle`](/ui/toggle) | `default`, `outline` | `default` não tem borda nem fundo em repouso; o fundo aparece no hover e quando ativo. `outline` acrescenta a borda. |
|
|
57
|
+
| [`TabsList`](/ui/tabs) | `default`, `line` | `default` agrupa as abas em uma superfície preenchida; `line` usa uma linha-base e marca nela a aba ativa. |
|
|
58
|
+
|
|
59
|
+
O mesmo nome pode aparecer nos dois grupos. Num `Badge`, `outline` é um tratamento do significado
|
|
60
|
+
declarado em `context`; num `Select`, é apenas o formato compacto com borda.
|
|
61
|
+
|
|
44
62
|
## Dicionários
|
|
45
63
|
|
|
46
|
-
Status e estágios declaram `context` na entrada. A tela usa
|
|
47
|
-
escolhe a
|
|
64
|
+
Status e estágios declaram `context` na entrada. A tela usa
|
|
65
|
+
[`DictionaryValue`](/ui/dictionary-value) ou [`ActionList`](/ui/action-list) e não escolhe a
|
|
66
|
+
aparência novamente.
|
|
48
67
|
|
|
49
68
|
```ts
|
|
50
69
|
const status = t.dict(
|
|
@@ -1,27 +1,32 @@
|
|
|
1
1
|
## Separar blocos empilhados
|
|
2
2
|
|
|
3
|
-
Use a
|
|
3
|
+
Use `Separator` para marcar a fronteira entre blocos de conteúdo que não pertencem a uma superfície
|
|
4
|
+
com regiões. A orientação horizontal fica entre blocos empilhados e ocupa toda a largura disponível.
|
|
4
5
|
|
|
5
6
|
```tsx preview col
|
|
6
7
|
<div>
|
|
7
|
-
<h4 className="text-sm font-medium">
|
|
8
|
-
<p className="text-sm text-muted-foreground">
|
|
8
|
+
<h4 className="text-sm font-medium">Oficina Centro</h4>
|
|
9
|
+
<p className="text-sm text-muted-foreground">Rua das Flores, 120.</p>
|
|
9
10
|
</div>
|
|
10
11
|
<Separator />
|
|
11
|
-
<p className="text-sm text-muted-foreground"
|
|
12
|
+
<p className="text-sm text-muted-foreground">Próximo horário livre hoje, às 14h.</p>
|
|
12
13
|
```
|
|
13
14
|
|
|
15
|
+
Não use `Separator` entre cabeçalho, corpo e rodapé de [`Card`](/ui/card), [`Dialog`](/ui/dialog) ou
|
|
16
|
+
[`Drawer`](/ui/drawer). Nessas superfícies, os divisores são derivados das regiões presentes: use
|
|
17
|
+
`layout="sectioned"` no componente raiz.
|
|
18
|
+
|
|
14
19
|
## Vertical
|
|
15
20
|
|
|
16
21
|
Use a orientação vertical entre itens lado a lado. A linha herda a altura do contêiner.
|
|
17
22
|
|
|
18
23
|
```tsx preview
|
|
19
24
|
<div className="flex h-4 items-center gap-3 text-sm">
|
|
20
|
-
<span>
|
|
25
|
+
<span>4 boxes</span>
|
|
21
26
|
<Separator orientation="vertical" />
|
|
22
|
-
<span>
|
|
27
|
+
<span>2 consultores</span>
|
|
23
28
|
<Separator orientation="vertical" />
|
|
24
|
-
<span>6
|
|
29
|
+
<span>6 revisões hoje</span>
|
|
25
30
|
</div>
|
|
26
31
|
```
|
|
27
32
|
|
|
@@ -29,5 +34,6 @@ Use a orientação vertical entre itens lado a lado. A linha herda a altura do c
|
|
|
29
34
|
|
|
30
35
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
31
36
|
|---|---|---|---|
|
|
32
|
-
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da linha.
|
|
33
|
-
| `decorative` | `boolean` | `true` |
|
|
37
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da linha. A vertical precisa de altura vinda do contêiner. |
|
|
38
|
+
| `decorative` | `boolean` | `true` | Uma linha decorativa fica fora da árvore de acessibilidade. Use `false` quando a divisão tiver significado e precisar ser anunciada. |
|
|
39
|
+
| `className` | `string` | | Ajusta a linha, como o espaçamento ao redor. |
|
|
@@ -258,8 +258,9 @@ O driver de drag-and-drop continua externo e fornece seus atributos por `dragPro
|
|
|
258
258
|
| `label` | `string` | Rótulo opcional do grupo. Rótulos vazios não reservam espaço. |
|
|
259
259
|
| `items` | `SidebarNavItem[]` | Itens diretos do grupo. |
|
|
260
260
|
|
|
261
|
-
`SidebarNavItem`
|
|
262
|
-
`
|
|
261
|
+
`SidebarNavItem` recebe as propriedades de `SidebarItem`, exceto `active` e `onClick`, e acrescenta
|
|
262
|
+
`id`. O destaque vem de `activeId` e a seleção chega por `onSelect`, ambos no `SidebarNav`. Para mais
|
|
263
|
+
níveis, componha `SidebarItem` e `SidebarTreeGroup` diretamente.
|
|
263
264
|
|
|
264
265
|
## Propriedades de SidebarItem
|
|
265
266
|
|
|
@@ -1,7 +1,12 @@
|
|
|
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
|
+
avatares e barras com dimensões próximas às linhas de texto esperadas. Quando a forma do conteúdo
|
|
5
|
+
não é conhecida, use [`Spinner`](/ui/spinner); para os estados completos de uma consulta, incluindo
|
|
6
|
+
erro e vazio, use [`DataState`](/ui/data-state).
|
|
7
|
+
|
|
8
|
+
O `Skeleton` é só visual e não anuncia a espera. Quando a região inteira estiver carregando, marque
|
|
9
|
+
o contêiner com `role="status"` e um nome, como `aria-label="Carregando sessões"`.
|
|
5
10
|
|
|
6
11
|
```tsx preview col
|
|
7
12
|
<div className="flex items-center gap-3">
|
|
@@ -3,52 +3,77 @@
|
|
|
3
3
|
Use `Slider` para escolher um valor dentro de uma faixa. `defaultValue` recebe um array; `[n]`
|
|
4
4
|
representa um único controle. `min`, `max` e `step` delimitam os valores disponíveis.
|
|
5
5
|
|
|
6
|
+
Quem recebe o foco e anuncia o valor é o controle arrastável, chamado de thumb. Sem rótulo
|
|
7
|
+
visível, passe `aria-label` ao `Slider`: ele repassa o nome ao thumb.
|
|
8
|
+
|
|
6
9
|
```tsx preview col
|
|
7
|
-
<Slider defaultValue={[40]} min={0} max={100} step={5} />
|
|
10
|
+
<Slider aria-label="Opacidade" defaultValue={[40]} min={0} max={100} step={5} />
|
|
8
11
|
```
|
|
9
12
|
|
|
10
13
|
## Controlado
|
|
11
14
|
|
|
12
15
|
`onValueChange` recebe o array atualizado. Para um único controle, leia `value[0]`.
|
|
13
16
|
|
|
17
|
+
Com um rótulo visível, ligue-o ao `Slider` por `aria-labelledby`. O `htmlFor` do `Label` não
|
|
18
|
+
alcança o thumb, porque ele não é um campo nativo.
|
|
19
|
+
|
|
14
20
|
```tsx preview col
|
|
15
21
|
const [parallelism, setParallelism] = useState([4])
|
|
16
22
|
|
|
17
23
|
render(
|
|
18
|
-
<div
|
|
24
|
+
<div className="grid w-full gap-3">
|
|
19
25
|
<div className="flex items-center justify-between">
|
|
20
26
|
<Label id="parallelism-label">Sessões em paralelo</Label>
|
|
21
27
|
<span className="text-sm text-muted-foreground">{parallelism[0]}</span>
|
|
22
28
|
</div>
|
|
23
|
-
<Slider
|
|
29
|
+
<Slider
|
|
30
|
+
aria-labelledby="parallelism-label"
|
|
31
|
+
value={parallelism}
|
|
32
|
+
onValueChange={setParallelism}
|
|
33
|
+
min={1}
|
|
34
|
+
max={8}
|
|
35
|
+
step={1}
|
|
36
|
+
/>
|
|
24
37
|
</div>,
|
|
25
38
|
)
|
|
26
39
|
```
|
|
27
40
|
|
|
28
41
|
## Intervalo
|
|
29
42
|
|
|
30
|
-
Dois números em `value` criam um intervalo selecionável entre dois
|
|
43
|
+
Dois números em `value` criam um intervalo selecionável entre dois thumbs, e cada um precisa de um
|
|
44
|
+
nome próprio. Passe os nomes em `thumbLabels`, na ordem dos valores. Sem `thumbLabels`, o nome comum
|
|
45
|
+
se compõe com a posição: com `aria-label`, “Custo, mínimo” e “Custo, máximo”; com
|
|
46
|
+
`aria-labelledby`, o texto do rótulo seguido de “Mínimo” ou “Máximo”. Sem nome algum, o leitor de
|
|
47
|
+
tela anuncia só “Mínimo” e “Máximo”, sem dizer do quê.
|
|
31
48
|
|
|
32
49
|
```tsx preview col
|
|
33
50
|
const [budget, setBudget] = useState([20, 60])
|
|
34
51
|
|
|
35
52
|
render(
|
|
36
|
-
<div
|
|
53
|
+
<div className="grid w-full gap-3">
|
|
37
54
|
<div className="flex items-center justify-between">
|
|
38
|
-
<Label
|
|
55
|
+
<Label>Custo estimado (US$)</Label>
|
|
39
56
|
<span className="text-sm text-muted-foreground">{budget[0]} – {budget[1]}</span>
|
|
40
57
|
</div>
|
|
41
|
-
<Slider
|
|
58
|
+
<Slider
|
|
59
|
+
thumbLabels={['Custo mínimo', 'Custo máximo']}
|
|
60
|
+
value={budget}
|
|
61
|
+
onValueChange={setBudget}
|
|
62
|
+
min={0}
|
|
63
|
+
max={100}
|
|
64
|
+
step={5}
|
|
65
|
+
/>
|
|
42
66
|
</div>,
|
|
43
67
|
)
|
|
44
68
|
```
|
|
45
69
|
|
|
46
70
|
## Desabilitado
|
|
47
71
|
|
|
48
|
-
disabled esmaece o trilho e o thumb e bloqueia o arraste
|
|
72
|
+
`disabled` esmaece o trilho e o thumb e bloqueia o arraste. O valor fica congelado no
|
|
73
|
+
`defaultValue`.
|
|
49
74
|
|
|
50
75
|
```tsx preview col
|
|
51
|
-
<Slider defaultValue={[70]} min={0} max={100} disabled />
|
|
76
|
+
<Slider aria-label="Opacidade" defaultValue={[70]} min={0} max={100} disabled />
|
|
52
77
|
```
|
|
53
78
|
|
|
54
79
|
## Propriedades de Slider
|
|
@@ -63,3 +88,6 @@ disabled esmaece o trilho e o thumb e bloqueia o arraste — o valor fica congel
|
|
|
63
88
|
| `step` | `number` | `1` | Incremento entre valores possíveis. |
|
|
64
89
|
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção do trilho — vertical exige altura no container. |
|
|
65
90
|
| `disabled` | `boolean` | `false` | Esmaece e bloqueia o arraste. |
|
|
91
|
+
| `aria-label` | `string` | | Nome do thumb quando não há rótulo visível. Em intervalo, prefira `thumbLabels`. |
|
|
92
|
+
| `aria-labelledby` | `string` | | `id` do rótulo visível que nomeia o thumb. |
|
|
93
|
+
| `thumbLabels` | `readonly string[]` | | Um nome por thumb, na ordem de `value`. Prevalece sobre `aria-label` e `aria-labelledby`. |
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Use `Spinner` quando a duração ou o progresso da espera não forem conhecidos. `size` usa os nomes da
|
|
4
4
|
escala dos controles e mede o glifo do controle homônimo: `sm` é o ícone de um `Button` `sm`, `lg` o
|
|
5
5
|
de um `lg`. O ícone é decorativo; quem nomeia a espera é o contêiner (`role="status"`, como fazem
|
|
6
|
-
`DataState` e `PageState`) ou o texto ao lado.
|
|
6
|
+
[`DataState`](/ui/data-state) e `PageState`) ou o texto ao lado.
|
|
7
7
|
|
|
8
8
|
```tsx preview
|
|
9
9
|
<Spinner size="xs" />
|
|
@@ -14,7 +14,7 @@ de um `lg`. O ícone é decorativo; quem nomeia a espera é o contêiner (`role=
|
|
|
14
14
|
|
|
15
15
|
## No botão
|
|
16
16
|
|
|
17
|
-
Durante uma ação, use `busy` no `Button` com um rótulo que descreva o andamento. O botão fica
|
|
17
|
+
Durante uma ação, use `busy` no [`Button`](/ui/button) com um rótulo que descreva o andamento. O botão fica
|
|
18
18
|
desabilitado e o spinner ocupa o lugar do ícone, no glifo do tamanho do botão.
|
|
19
19
|
|
|
20
20
|
```tsx preview
|
|
@@ -24,7 +24,8 @@ desabilitado e o spinner ocupa o lugar do ícone, no glifo do tamanho do botão.
|
|
|
24
24
|
## Em carga de conteúdo
|
|
25
25
|
|
|
26
26
|
Espera curta sem forma definida; o contêiner nomeia o estado. Para uma consulta inteira, `DataState`
|
|
27
|
-
já monta esse contêiner. Quando o conteúdo tem forma conhecida
|
|
27
|
+
já monta esse contêiner. Quando o conteúdo tem forma conhecida, como uma lista ou um card, prefira
|
|
28
|
+
[`Skeleton`](/ui/skeleton).
|
|
28
29
|
|
|
29
30
|
```tsx preview
|
|
30
31
|
<div role="status" className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
@@ -21,6 +21,19 @@ render(
|
|
|
21
21
|
);
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
Escreva cada `Pane` diretamente dentro do `Split`; um fragment pode agrupá-los, como em uma
|
|
25
|
+
renderização condicional que devolve dois panes. O `Split` lê o tamanho de cada `Pane` antes de
|
|
26
|
+
renderizá-lo, então não enxerga um `Pane` devolvido por outro componente: nesse caso, escreva o
|
|
27
|
+
`Pane` no `Split` e passe o componente como filho dele. Qualquer outro filho interrompe a
|
|
28
|
+
renderização com um erro que aponta o nó recebido, em vez de sumir da tela sem aviso.
|
|
29
|
+
|
|
30
|
+
O [`opus check`](/docs/cli) antecipa esse erro com a regra `ui-structure-split`: ele aponta o filho
|
|
31
|
+
direto de um `Split` que o código mostra não ser `Pane`, como um elemento HTML, um texto, outro
|
|
32
|
+
componente do Opus ou um componente declarado no mesmo arquivo. Panes em fragment ou em condicional,
|
|
33
|
+
espaços e comentários são aceitos. Expressões dinâmicas, como `{children}` ou `{items.map(...)}`, e
|
|
34
|
+
componentes importados de outros módulos ficam fora do check; nesses casos, só o erro em runtime
|
|
35
|
+
revela um filho inválido.
|
|
36
|
+
|
|
24
37
|
O `Split` responde pela relação espacial e pelo arraste. O que existe dentro de cada área — o
|
|
25
38
|
cabeçalho, o corpo rolável, o rodapé, o recuo e a escolha entre regiões seccionadas e contínuas —
|
|
26
39
|
pertence ao [`Pane`](/ui/pane) e está documentado na página da família.
|
|
@@ -139,6 +152,9 @@ render(<Example />);
|
|
|
139
152
|
| `id` | `string` | | Identidade estável do grupo redimensionável. |
|
|
140
153
|
| `defaultLayout` | `Record<string, number>` | | Layout percentual restaurado, indexado pelos ids dos panes. |
|
|
141
154
|
| `onLayoutChanged` | `(layout, { isUserInteraction }) => void` | | Chamado ao concluir uma mudança de layout; persista onde fizer sentido. |
|
|
155
|
+
| `className` | `string` | | Ajusta o contêiner do grupo. |
|
|
156
|
+
| `data-slot` | `string` | `'split'` | Identificador semântico para testes e composição de patterns. |
|
|
157
|
+
| `children` | `ReactNode` | | Os `Pane`, diretos ou agrupados em fragment; outro filho gera erro. |
|
|
142
158
|
|
|
143
159
|
## Propriedades de SurfaceAssistant
|
|
144
160
|
|
|
@@ -151,3 +167,6 @@ render(<Example />);
|
|
|
151
167
|
| `initialSize` | `PaneSize` | `28rem` | Largura inicial do painel. |
|
|
152
168
|
| `minSize` | `PaneSize` | `20rem` | Largura mínima durante o redimensionamento. |
|
|
153
169
|
| `maxSize` | `PaneSize` | `40rem` | Largura máxima durante o redimensionamento. |
|
|
170
|
+
| `className` | `string` | | Ajusta o contêiner do split. |
|
|
171
|
+
| `contentClassName` | `string` | | Ajusta a coluna do conteúdo principal. |
|
|
172
|
+
| `assistantClassName` | `string` | | Ajusta a coluna do painel de assistência. |
|
|
@@ -4,8 +4,7 @@ title: Armazenamento
|
|
|
4
4
|
|
|
5
5
|
# Armazenamento
|
|
6
6
|
|
|
7
|
-
> **Experimental.**
|
|
8
|
-
> reincidência de caso real, não por especulação.
|
|
7
|
+
> **Experimental.** A superfície é mínima e cresce conforme casos reais a exigem.
|
|
9
8
|
|
|
10
9
|
Use `StorageAdapter` para upload, download, remoção e geração de URL de arquivos. A chave é um
|
|
11
10
|
caminho lógico, como `clientes/123/contrato.pdf`; cada driver decide onde o conteúdo é armazenado.
|
|
@@ -31,10 +30,11 @@ import { fsStorage } from '@softize/opus/storage/fs'
|
|
|
31
30
|
import { s3Storage } from '@softize/opus/storage/s3'
|
|
32
31
|
import { S3Client } from '@aws-sdk/client-s3'
|
|
33
32
|
|
|
34
|
-
//
|
|
33
|
+
// Desenvolvimento ou um único host: arquivos num diretório local; url() exige baseUrl,
|
|
34
|
+
// e o app serve esse diretório.
|
|
35
35
|
const dev = fsStorage({ root: './var/arquivos', baseUrl: 'http://localhost:5173/arquivos' })
|
|
36
36
|
|
|
37
|
-
// Produção: S3 e compatíveis (MinIO, R2…). O
|
|
37
|
+
// Produção: S3 e compatíveis (MinIO, R2…). O app cria o client com região, endpoint e credenciais.
|
|
38
38
|
const prod = s3Storage({
|
|
39
39
|
client: new S3Client({ region: 'us-east-1' }),
|
|
40
40
|
bucket: 'meu-app',
|
|
@@ -42,8 +42,8 @@ const prod = s3Storage({
|
|
|
42
42
|
})
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
`@aws-sdk/client-s3` e, para `url()`, `@aws-sdk/s3-request-presigner` são dependências opcionais,
|
|
46
|
+
carregadas só quando o driver é usado.
|
|
47
47
|
|
|
48
48
|
## No runtime
|
|
49
49
|
|
|
@@ -62,6 +62,5 @@ handler: async (ctx, input) => {
|
|
|
62
62
|
|
|
63
63
|
## Limites atuais
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
régua do Opus: comportamento vira peça na reincidência.
|
|
65
|
+
O conteúdo trafega inteiro em memória, como `Uint8Array`, sem streams; arquivos muito grandes ainda
|
|
66
|
+
não são suportados. Também não há `list` nem metadados além de `contentType`.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
## Alternar uma preferência
|
|
2
2
|
|
|
3
|
-
Use `Switch` para uma preferência booleana que entra em vigor imediatamente.
|
|
4
|
-
|
|
3
|
+
Use `Switch` para uma preferência booleana que entra em vigor imediatamente. Quando a escolha só vale
|
|
4
|
+
ao salvar um formulário, use [`Checkbox`](/ui/checkbox). Associe o controle a um [`Label`](/ui/label);
|
|
5
|
+
`defaultChecked` define o estado inicial no modo não controlado.
|
|
5
6
|
|
|
6
7
|
```tsx preview
|
|
7
8
|
<div className="flex items-center gap-2">
|
|
@@ -31,7 +32,8 @@ render(
|
|
|
31
32
|
|
|
32
33
|
## Tamanho sm
|
|
33
34
|
|
|
34
|
-
Use `size="sm"` em linhas de lista e outras composições compactas.
|
|
35
|
+
Use `size="sm"` em linhas de lista e outras composições compactas. O tamanho segue o degrau do ícone
|
|
36
|
+
na escala dos controles, e não a altura da linha: a chave `sm` acompanha o ícone de um botão `sm`.
|
|
35
37
|
|
|
36
38
|
```tsx preview col-start
|
|
37
39
|
<div className="flex items-center gap-2">
|
|
@@ -46,7 +48,8 @@ Use `size="sm"` em linhas de lista e outras composições compactas.
|
|
|
46
48
|
|
|
47
49
|
## Desabilitado
|
|
48
50
|
|
|
49
|
-
disabled esmaece e bloqueia a chave
|
|
51
|
+
`disabled` esmaece e bloqueia a chave, ligada ou desligada. O `Label` escrito logo depois da chave,
|
|
52
|
+
no mesmo contêiner, esmaece junto.
|
|
50
53
|
|
|
51
54
|
```tsx preview col-start
|
|
52
55
|
<div className="flex items-center gap-2">
|
|
@@ -63,8 +66,8 @@ disabled esmaece e bloqueia a chave — ligada ou desligada — e o rótulo em p
|
|
|
63
66
|
|
|
64
67
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
65
68
|
|---|---|---|---|
|
|
66
|
-
| `checked` | `boolean` | |
|
|
69
|
+
| `checked` | `boolean` | | Estado no modo controlado. Use com `onCheckedChange`. |
|
|
67
70
|
| `onCheckedChange` | `(checked: boolean) => void` | | Chamado a cada alternância, com o novo estado. |
|
|
68
71
|
| `defaultChecked` | `boolean` | `false` | Estado inicial no modo não controlado. |
|
|
69
|
-
| `size` | `'sm' \| 'default'` | `'default'` | Tamanho da chave, com os nomes da escala única
|
|
70
|
-
| `disabled` | `boolean` | `false` | Esmaece e bloqueia
|
|
72
|
+
| `size` | `'sm' \| 'default'` | `'default'` | Tamanho da chave, com os nomes da escala única; `sm` para linhas de lista densas. |
|
|
73
|
+
| `disabled` | `boolean` | `false` | Esmaece e bloqueia a chave; o `Label` irmão posterior esmaece junto. |
|