@softize/opus 18.5.0 → 20.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 +275 -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/0023-surfaces-share-one-anatomy-and-two-layouts.md +298 -0
- package/docs/adr/0024-fill-height-propagates-by-context.md +118 -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 +16 -12
- package/src/ui/components/patterns/fill-region.ts +150 -0
- package/src/ui/components/patterns/heading-level.ts +16 -0
- package/src/ui/components/patterns/list.tsx +103 -73
- package/src/ui/components/patterns/page.tsx +20 -4
- package/src/ui/components/patterns/pane.tsx +294 -0
- package/src/ui/components/patterns/presentation.tsx +81 -54
- package/src/ui/components/patterns/sidebar.tsx +13 -72
- package/src/ui/components/patterns/split.tsx +51 -38
- package/src/ui/components/patterns/surface-anatomy.ts +246 -0
- package/src/ui/components/patterns/surface-header.tsx +51 -6
- package/src/ui/components/patterns/surface-media.tsx +69 -0
- 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 +91 -29
- 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 +14 -5
- package/src/ui/components/primitives/copyable.tsx +120 -10
- package/src/ui/components/primitives/detail.tsx +10 -13
- package/src/ui/components/primitives/dialog.tsx +179 -45
- package/src/ui/components/primitives/drawer.tsx +131 -24
- 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/metric-card.tsx +98 -39
- 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 +134 -52
- 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 +252 -35
- 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 +37 -7
- 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 +219 -28
- 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 +193 -22
- 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 +28 -5
- 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 +68 -21
- package/src/ui/docs/content/observability.md +10 -9
- package/src/ui/docs/content/page.md +125 -11
- package/src/ui/docs/content/pagination.md +13 -5
- package/src/ui/docs/content/pane.md +300 -0
- 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 +25 -45
- package/src/ui/docs/content/skeleton.md +13 -4
- 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 +34 -20
- 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 +14 -2
- package/src/ui/drivers/react.tsx +16 -8
- package/src/ui/meta.ts +38 -32
- package/src/ui/react.tsx +14 -0
- package/src/vite/design.ts +16 -4
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
## Lista de altura fixa
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `ScrollArea` quando um bloco precisa manter a própria altura e rolar o conteúdo que passar dela.
|
|
4
|
+
Dê a altura ao `ScrollArea`, como `h-48`, e coloque o conteúdo dentro. A barra vertical já vem
|
|
5
|
+
incluída; não é preciso acrescentar `ScrollBar`.
|
|
4
6
|
|
|
5
7
|
```tsx preview col
|
|
6
|
-
const
|
|
7
|
-
{ id: '
|
|
8
|
-
{ id: '
|
|
9
|
-
{ id: '
|
|
10
|
-
{ id: '
|
|
11
|
-
{ id: '
|
|
12
|
-
{ id: '
|
|
13
|
-
{ id: '
|
|
14
|
-
{ id: '
|
|
8
|
+
const orders = [
|
|
9
|
+
{ id: 'OS-1842', title: 'Revisão dos 20.000 km', vehicle: 'ABC-1D23' },
|
|
10
|
+
{ id: 'OS-1839', title: 'Troca das pastilhas de freio', vehicle: 'BRA-2E19' },
|
|
11
|
+
{ id: 'OS-1835', title: 'Alinhamento e balanceamento', vehicle: 'RIO-4F56' },
|
|
12
|
+
{ id: 'OS-1830', title: 'Diagnóstico de ruído na suspensão', vehicle: 'SPO-7G88' },
|
|
13
|
+
{ id: 'OS-1827', title: 'Troca de óleo e filtro', vehicle: 'ABC-1D23' },
|
|
14
|
+
{ id: 'OS-1821', title: 'Substituição da bateria', vehicle: 'CWB-3H45' },
|
|
15
|
+
{ id: 'OS-1818', title: 'Higienização do ar-condicionado', vehicle: 'BRA-2E19' },
|
|
16
|
+
{ id: 'OS-1814', title: 'Revisão dos 40.000 km', vehicle: 'POA-9J12' },
|
|
15
17
|
]
|
|
16
18
|
|
|
17
19
|
render(
|
|
18
20
|
<ScrollArea className="h-48 w-full rounded-md border border-border">
|
|
19
21
|
<div className="p-3">
|
|
20
|
-
{
|
|
21
|
-
<div key={
|
|
22
|
-
<p className="font-medium">{
|
|
23
|
-
<p className="text-xs text-muted-foreground">{
|
|
22
|
+
{orders.map((order) => (
|
|
23
|
+
<div key={order.id} className="rounded-md px-2 py-1.5 text-sm hover:bg-muted">
|
|
24
|
+
<p className="font-medium">{order.title}</p>
|
|
25
|
+
<p className="text-xs text-muted-foreground">{order.id} · {order.vehicle}</p>
|
|
24
26
|
</div>
|
|
25
27
|
))}
|
|
26
28
|
</div>
|
|
@@ -30,17 +32,18 @@ render(
|
|
|
30
32
|
|
|
31
33
|
## Trilho horizontal
|
|
32
34
|
|
|
33
|
-
Para rolar na horizontal, acrescente
|
|
35
|
+
Para rolar na horizontal, acrescente `<ScrollBar orientation="horizontal" />` como filho e mantenha
|
|
36
|
+
o conteúdo em uma linha que não quebra, com `flex` e `w-max`.
|
|
34
37
|
|
|
35
38
|
```tsx preview col
|
|
36
|
-
const
|
|
39
|
+
const services = ['Troca de óleo', 'Filtro de ar', 'Alinhamento', 'Balanceamento', 'Pastilhas de freio', 'Velas de ignição']
|
|
37
40
|
|
|
38
41
|
render(
|
|
39
42
|
<ScrollArea className="w-full rounded-md border border-border whitespace-nowrap">
|
|
40
43
|
<div className="flex w-max gap-2 p-3">
|
|
41
|
-
{
|
|
42
|
-
<span key={
|
|
43
|
-
{
|
|
44
|
+
{services.map((service) => (
|
|
45
|
+
<span key={service} className="rounded-md border border-border bg-muted px-3 py-1.5 text-sm">
|
|
46
|
+
{service}
|
|
44
47
|
</span>
|
|
45
48
|
))}
|
|
46
49
|
</div>
|
|
@@ -49,28 +52,30 @@ render(
|
|
|
49
52
|
)
|
|
50
53
|
```
|
|
51
54
|
|
|
52
|
-
##
|
|
55
|
+
## Histórico com divisórias
|
|
53
56
|
|
|
54
|
-
O conteúdo é livre
|
|
57
|
+
O conteúdo interno é livre. Neste exemplo, o histórico de uma ordem de serviço usa `Separator`
|
|
58
|
+
entre as linhas. A barra só aparece quando o conteúdo passa da altura e o ponteiro está sobre a
|
|
59
|
+
área; para mantê-la sempre visível, passe `type="always"`.
|
|
55
60
|
|
|
56
61
|
```tsx preview col
|
|
57
|
-
const
|
|
58
|
-
'
|
|
59
|
-
'
|
|
60
|
-
'
|
|
61
|
-
'
|
|
62
|
-
'
|
|
63
|
-
'
|
|
64
|
-
'
|
|
65
|
-
'
|
|
66
|
-
'12:
|
|
62
|
+
const history = [
|
|
63
|
+
'08:02 Veículo recebido na oficina',
|
|
64
|
+
'08:15 Checklist de entrada concluído',
|
|
65
|
+
'08:40 Diagnóstico iniciado',
|
|
66
|
+
'09:10 Orçamento enviado ao cliente',
|
|
67
|
+
'09:32 Orçamento aprovado',
|
|
68
|
+
'10:05 Peças separadas no estoque',
|
|
69
|
+
'11:20 Serviço concluído',
|
|
70
|
+
'11:45 Teste de rodagem aprovado',
|
|
71
|
+
'12:00 Veículo liberado para retirada',
|
|
67
72
|
]
|
|
68
73
|
|
|
69
74
|
render(
|
|
70
75
|
<ScrollArea className="h-40 w-full rounded-md border border-border">
|
|
71
76
|
<div className="p-3 text-sm">
|
|
72
|
-
{
|
|
73
|
-
<div key={
|
|
77
|
+
{history.map((line, i) => (
|
|
78
|
+
<div key={line}>
|
|
74
79
|
{i > 0 && <Separator className="my-1.5" />}
|
|
75
80
|
<p className="text-muted-foreground">{line}</p>
|
|
76
81
|
</div>
|
|
@@ -84,11 +89,13 @@ render(
|
|
|
84
89
|
|
|
85
90
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
86
91
|
|---|---|---|---|
|
|
87
|
-
| `className` | `string` | |
|
|
88
|
-
| `
|
|
92
|
+
| `className` | `string` | | Define as dimensões da janela rolável. Sem uma dimensão limitada, não há conteúdo a recortar. |
|
|
93
|
+
| `type` | `'hover' \| 'scroll' \| 'auto' \| 'always'` | `'hover'` | Quando a barra aparece: sob o ponteiro, durante a rolagem, sempre que houver excedente ou sempre. |
|
|
94
|
+
| `children` | `ReactNode` | | Conteúdo da janela, incluindo uma `ScrollBar` horizontal quando necessária. |
|
|
89
95
|
|
|
90
96
|
## Propriedades de ScrollBar
|
|
91
97
|
|
|
92
98
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
93
99
|
|---|---|---|---|
|
|
94
100
|
| `orientation` | `'vertical' \| 'horizontal'` | `'vertical'` | Direção da barra. A barra vertical já faz parte de `ScrollArea`. |
|
|
101
|
+
| `className` | `string` | | Ajusta o trilho da barra. |
|
|
@@ -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. |
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
## Navegação dentro de um layout
|
|
2
2
|
|
|
3
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
|
|
5
|
-
dentro de `Split
|
|
4
|
+
organiza a superfície e compartilha o estado de colapso; posição e largura pertencem a um
|
|
5
|
+
[`Pane`](/ui/pane) dentro de [`Split`](/ui/split).
|
|
6
6
|
|
|
7
7
|
O exemplo canônico mantém a largura inicial em `rem`, deixa a navegação rolar e preserva o
|
|
8
8
|
cabeçalho e o rodapé nas extremidades.
|
|
@@ -13,8 +13,8 @@ render(
|
|
|
13
13
|
<Split>
|
|
14
14
|
<Pane initialSize="16rem" inset="none">
|
|
15
15
|
<Sidebar className="w-full">
|
|
16
|
-
<PaneHeader
|
|
17
|
-
Projeto
|
|
16
|
+
<PaneHeader>
|
|
17
|
+
<PaneTitle>Projeto</PaneTitle>
|
|
18
18
|
</PaneHeader>
|
|
19
19
|
<PaneBody>
|
|
20
20
|
<SidebarNav
|
|
@@ -50,22 +50,17 @@ divisória ao lado do handle.
|
|
|
50
50
|
|
|
51
51
|
## Anatomia e responsabilidades
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
A `Sidebar` não tem regiões próprias: o cabeçalho, o corpo rolável e o rodapé são os slots do
|
|
54
|
+
`Pane` que a hospeda, com a anatomia e as propriedades descritas na
|
|
55
|
+
[página da família](/ui/pane).
|
|
55
56
|
|
|
56
57
|
| Parte | Responsabilidade |
|
|
57
58
|
|---|---|
|
|
58
|
-
| `Split` | Define a relação espacial e, opcionalmente, o redimensionamento. |
|
|
59
|
-
| `Pane` | Define largura
|
|
59
|
+
| [`Split`](/ui/split) | Define a relação espacial e, opcionalmente, o redimensionamento. |
|
|
60
|
+
| [`Pane`](/ui/pane) | Define largura e limites da coluna e estrutura suas regiões. |
|
|
60
61
|
| `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
62
|
| `SidebarNav` | Apresenta e controla os destinos de navegação. |
|
|
65
63
|
|
|
66
|
-
`PaneContent` continua exportado como alias depreciado de `PaneBody`. Código novo usa
|
|
67
|
-
`PaneBody`.
|
|
68
|
-
|
|
69
64
|
## Escolher a navegação
|
|
70
65
|
|
|
71
66
|
Use `SidebarNav` para grupos planos de destinos. Ele recebe dados e produz `SidebarItem` com a mesma
|
|
@@ -151,15 +146,17 @@ const [collapsed, setCollapsed] = useState(false)
|
|
|
151
146
|
render(
|
|
152
147
|
<div className="flex h-64 overflow-hidden rounded-lg border border-border">
|
|
153
148
|
<Sidebar collapsed={collapsed}>
|
|
154
|
-
<PaneHeader
|
|
155
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
149
|
+
<PaneHeader>
|
|
150
|
+
<PaneActions>
|
|
151
|
+
<Button
|
|
152
|
+
variant="ghost"
|
|
153
|
+
size="icon-sm"
|
|
154
|
+
aria-label={collapsed ? 'Expandir navegação' : 'Recolher navegação'}
|
|
155
|
+
onClick={() => setCollapsed((current) => !current)}
|
|
156
|
+
>
|
|
157
|
+
<PanelLeftClose className={collapsed ? 'rotate-180' : ''} />
|
|
158
|
+
</Button>
|
|
159
|
+
</PaneActions>
|
|
163
160
|
</PaneHeader>
|
|
164
161
|
<PaneBody>
|
|
165
162
|
<SidebarNav
|
|
@@ -241,26 +238,8 @@ O driver de drag-and-drop continua externo e fornece seus atributos por `dragPro
|
|
|
241
238
|
| `className` | `string` | | Ajusta a raiz. A largura externa deve continuar pertencendo ao `Pane`. |
|
|
242
239
|
| `children` | `ReactNode` | | Cabeçalho, corpo, rodapé ou uma navegação que gerencie a própria estrutura. |
|
|
243
240
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
| Propriedade | Tipo | Padrão | Descrição |
|
|
247
|
-
|---|---|---|---|
|
|
248
|
-
| `className` | `string` | | Ajusta a faixa fixa e seu espaçamento. |
|
|
249
|
-
| `children` | `ReactNode` | | Identidade, contexto ou ações apresentadas no topo. |
|
|
250
|
-
|
|
251
|
-
## Propriedades de PaneBody
|
|
252
|
-
|
|
253
|
-
| Propriedade | Tipo | Padrão | Descrição |
|
|
254
|
-
|---|---|---|---|
|
|
255
|
-
| `className` | `string` | | Ajusta a região flexível e rolável. |
|
|
256
|
-
| `children` | `ReactNode` | | Conteúdo que ocupa o espaço entre cabeçalho e rodapé. |
|
|
257
|
-
|
|
258
|
-
## Propriedades de PaneFooter
|
|
259
|
-
|
|
260
|
-
| Propriedade | Tipo | Padrão | Descrição |
|
|
261
|
-
|---|---|---|---|
|
|
262
|
-
| `className` | `string` | | Ajusta a faixa fixa e seu espaçamento. |
|
|
263
|
-
| `children` | `ReactNode` | | Navegação ou ações que precisam permanecer acessíveis no rodapé. |
|
|
241
|
+
`PaneHeader`, `PaneBody` e `PaneFooter` são os slots da família `Pane`; a referência deles está na
|
|
242
|
+
[página do Pane](/ui/pane).
|
|
264
243
|
|
|
265
244
|
## Propriedades de SidebarNav
|
|
266
245
|
|
|
@@ -279,8 +258,9 @@ O driver de drag-and-drop continua externo e fornece seus atributos por `dragPro
|
|
|
279
258
|
| `label` | `string` | Rótulo opcional do grupo. Rótulos vazios não reservam espaço. |
|
|
280
259
|
| `items` | `SidebarNavItem[]` | Itens diretos do grupo. |
|
|
281
260
|
|
|
282
|
-
`SidebarNavItem`
|
|
283
|
-
`
|
|
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.
|
|
284
264
|
|
|
285
265
|
## Propriedades de SidebarItem
|
|
286
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">
|
|
@@ -15,15 +20,19 @@ avatares e barras com dimensões próximas às linhas de texto esperadas.
|
|
|
15
20
|
|
|
16
21
|
## Card em carregamento
|
|
17
22
|
|
|
18
|
-
O esqueleto reproduz o layout final do card — título,
|
|
23
|
+
O esqueleto reproduz o layout final do card — título, conteúdo e ações — para a tela não pular
|
|
24
|
+
quando os dados chegarem. O título entra pelo slot do cabeçalho com `asChild`, porque o
|
|
25
|
+
`CardHeader` só aceita os próprios slots.
|
|
19
26
|
|
|
20
27
|
```tsx preview col
|
|
21
28
|
<Card>
|
|
22
29
|
<CardHeader>
|
|
23
|
-
<
|
|
24
|
-
|
|
30
|
+
<CardTitle asChild>
|
|
31
|
+
<Skeleton className="h-5 w-40" />
|
|
32
|
+
</CardTitle>
|
|
25
33
|
</CardHeader>
|
|
26
34
|
<CardBody className="space-y-2">
|
|
35
|
+
<Skeleton className="h-4 w-56" />
|
|
27
36
|
<Skeleton className="h-4 w-full" />
|
|
28
37
|
<Skeleton className="h-4 w-3/4" />
|
|
29
38
|
</CardBody>
|