@softize/opus 23.0.0 → 24.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 +81 -0
- package/bin/cli.mjs +2 -0
- package/bin/lib/check.mjs +241 -42
- package/bin/lib/copy.mjs +16 -34
- package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +6 -0
- package/docs/adr/0039-filter-presentation-is-independent-from-filter-type.md +3 -3
- package/docs/adr/0040-tabs-use-a-single-navigation-presentation.md +5 -6
- package/docs/adr/0042-toggle-group-shares-the-grouped-control-grammar.md +79 -0
- package/docs/adr/0043-neutral-surfaces-and-interactions-use-opaque-role-tokens.md +73 -0
- package/docs/adr/0044-neutral-appearance-derives-from-bounded-axes-per-mode.md +133 -0
- package/docs/adr/0045-surface-replaces-content-and-card.md +230 -0
- package/docs/adr/0046-structural-body-owns-content-rhythm.md +103 -0
- package/docs/adr/0047-pane-is-only-a-split-layout-unit.md +71 -0
- package/docs/adr/0048-modal-intro-separates-content-from-chrome.md +34 -0
- package/package.json +5 -1
- package/registry/skills/build-opus-ui/SKILL.md +11 -11
- package/registry/skills/build-opus-ui/references/evaluations.md +10 -9
- package/registry/skills/build-opus-ui/references/ui-patterns.md +17 -16
- package/registry/templates/app/src/App.tsx +24 -12
- package/src/ui/appearance.ts +149 -0
- package/src/ui/components/patterns/action-form-card.tsx +46 -33
- package/src/ui/components/patterns/action-list-dialog.tsx +2 -4
- package/src/ui/components/patterns/confirm.tsx +3 -1
- package/src/ui/components/patterns/fill-region.ts +8 -7
- package/src/ui/components/patterns/form-dialog.tsx +1 -3
- package/src/ui/components/patterns/form.tsx +8 -7
- package/src/ui/components/patterns/list.tsx +15 -19
- package/src/ui/components/patterns/page.tsx +15 -11
- package/src/ui/components/patterns/pane.tsx +9 -240
- package/src/ui/components/patterns/presentation.tsx +14 -14
- package/src/ui/components/patterns/sidebar.tsx +7 -17
- package/src/ui/components/patterns/split.tsx +111 -59
- package/src/ui/components/patterns/{surface-header.tsx → structural-header.tsx} +50 -19
- package/src/ui/components/patterns/structural-media.tsx +26 -0
- package/src/ui/components/patterns/surface-anatomy.ts +71 -61
- package/src/ui/components/patterns/surface-assistant.tsx +2 -4
- package/src/ui/components/patterns/surface.tsx +620 -0
- package/src/ui/components/patterns/trigger.tsx +11 -9
- package/src/ui/components/primitives/ask.tsx +155 -83
- package/src/ui/components/primitives/button.tsx +11 -9
- package/src/ui/components/primitives/checkbox.tsx +8 -8
- package/src/ui/components/primitives/command.tsx +1 -1
- package/src/ui/components/primitives/composer.tsx +47 -45
- package/src/ui/components/primitives/detail.tsx +9 -2
- package/src/ui/components/primitives/dialog.tsx +60 -26
- package/src/ui/components/primitives/drawer.tsx +58 -12
- package/src/ui/components/primitives/icon-picker.tsx +90 -73
- package/src/ui/components/primitives/input-group.tsx +35 -28
- package/src/ui/components/primitives/input-otp.tsx +16 -16
- package/src/ui/components/primitives/input.tsx +20 -10
- package/src/ui/components/primitives/item.tsx +9 -12
- package/src/ui/components/primitives/menu.tsx +44 -65
- package/src/ui/components/primitives/metric-card.tsx +75 -59
- package/src/ui/components/primitives/radio-group.tsx +9 -9
- package/src/ui/components/primitives/select.tsx +344 -209
- package/src/ui/components/primitives/switch.tsx +10 -10
- package/src/ui/components/primitives/table.tsx +29 -24
- package/src/ui/components/primitives/tabs.tsx +4 -2
- package/src/ui/components/primitives/textarea.tsx +6 -6
- package/src/ui/components/primitives/toggle-group-context.ts +24 -0
- package/src/ui/components/primitives/toggle-group.tsx +116 -74
- package/src/ui/components/primitives/toggle.tsx +82 -16
- package/src/ui/docs/DocBrowser.tsx +77 -69
- package/src/ui/docs/content/action-form-card.md +9 -9
- package/src/ui/docs/content/action-list.md +76 -70
- package/src/ui/docs/content/action-view.md +33 -18
- package/src/ui/docs/content/button.md +1 -1
- package/src/ui/docs/content/communication.md +1 -1
- package/src/ui/docs/content/customization.md +64 -11
- package/src/ui/docs/content/detail.md +8 -6
- package/src/ui/docs/content/dialog.md +100 -58
- package/src/ui/docs/content/drawer.md +63 -19
- package/src/ui/docs/content/field.md +14 -15
- package/src/ui/docs/content/icon-tile.md +14 -13
- package/src/ui/docs/content/page.md +34 -26
- package/src/ui/docs/content/pane.md +41 -290
- package/src/ui/docs/content/presentation.md +3 -3
- package/src/ui/docs/content/separator.md +6 -6
- package/src/ui/docs/content/sidebar.md +68 -266
- package/src/ui/docs/content/skeleton.md +20 -18
- package/src/ui/docs/content/split.md +14 -14
- package/src/ui/docs/content/surface.md +513 -0
- package/src/ui/docs/content/tabs.md +20 -20
- package/src/ui/docs/content/toggle.md +117 -65
- package/src/ui/docs/content/tokens.md +124 -40
- package/src/ui/docs/registry.tsx +4 -6
- package/src/ui/index.ts +30 -27
- package/src/ui/meta.ts +15 -27
- package/src/ui/react.tsx +40 -43
- package/src/ui/theme.css +311 -64
- package/src/ui/components/patterns/content-header.tsx +0 -528
- package/src/ui/components/patterns/surface-media.tsx +0 -69
- package/src/ui/components/primitives/card.tsx +0 -191
- package/src/ui/docs/content/card.md +0 -292
- package/src/ui/docs/content/content.md +0 -275
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
## Região nomeada
|
|
2
|
+
|
|
3
|
+
Use `Surface` para delimitar e nomear um trecho da tela: uma seção da página, um grupo de campos, um
|
|
4
|
+
registro, um item de uma grade ou um bloco clicável. Para uma janela modal, use
|
|
5
|
+
[`Dialog`](/ui/dialog); para um painel lateral, [`Drawer`](/ui/drawer); para uma coluna de layout,
|
|
6
|
+
[`Pane`](/ui/pane).
|
|
7
|
+
|
|
8
|
+
O caso mais comum é uma seção com título dentro de uma página. Declare `as="section"` e as
|
|
9
|
+
propriedades do header na própria `Surface`; os filhos formam o corpo. `count` mostra o total de
|
|
10
|
+
itens ao lado do título.
|
|
11
|
+
|
|
12
|
+
```tsx preview col
|
|
13
|
+
render(
|
|
14
|
+
<Surface
|
|
15
|
+
as="section"
|
|
16
|
+
title="Dispositivos conectados"
|
|
17
|
+
count={3}
|
|
18
|
+
description="Sessões com acesso à sua conta."
|
|
19
|
+
actions={<Button variant="outline">Encerrar outras sessões</Button>}
|
|
20
|
+
>
|
|
21
|
+
<p className="text-sm text-muted-foreground">MacBook Pro · ativo agora</p>
|
|
22
|
+
</Surface>,
|
|
23
|
+
);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Sem moldura e sem fundo, a superfície não aplica recuo: título e corpo ficam alinhados ao conteúdo
|
|
27
|
+
ao redor. A `section` é ligada ao título por `aria-labelledby`, então leitores de tela anunciam a
|
|
28
|
+
região pelo nome.
|
|
29
|
+
|
|
30
|
+
## Escolher o elemento
|
|
31
|
+
|
|
32
|
+
`as` decide o que a superfície é para o documento. A aparência não muda com o elemento.
|
|
33
|
+
|
|
34
|
+
| `as` | Quando usar | Efeito |
|
|
35
|
+
| -------------- | -------------------------------------------------------------------------------------- | --------------------------------------- |
|
|
36
|
+
| `div` (padrão) | Agrupamento visual sem papel próprio no documento. | Nenhum nome acessível é atribuído. |
|
|
37
|
+
| `section` | Região da página identificada por um título. | `aria-labelledby` aponta para o título. |
|
|
38
|
+
| `article` | Conteúdo que faz sentido sozinho, como um registro ou um item de uma lista de cartões. | `aria-labelledby` aponta para o título. |
|
|
39
|
+
| `fieldset` | Grupo de campos de formulário. | O header vira a `legend` nativa. |
|
|
40
|
+
|
|
41
|
+
Quando `aria-label` é informado, ele substitui a ligação com o título. Para um bloco clicável, use
|
|
42
|
+
`asChild` em vez de `as`, como mostra a seção “Superfície clicável”.
|
|
43
|
+
|
|
44
|
+
## Moldura, fundo e anatomia
|
|
45
|
+
|
|
46
|
+
Três propriedades independentes definem a aparência:
|
|
47
|
+
|
|
48
|
+
- `framed` acrescenta borda e raio.
|
|
49
|
+
- `elevation="raised"` dá à superfície o fundo de um elemento elevado sobre a página.
|
|
50
|
+
- `layout` escolhe entre o ritmo contínuo e as faixas com divisores.
|
|
51
|
+
|
|
52
|
+
Uma superfície com moldura ou com fundo passa a aplicar o recuo de `1rem` nas suas regiões; você não
|
|
53
|
+
escreve classes de padding no header, no corpo ou no rodapé.
|
|
54
|
+
|
|
55
|
+
```tsx preview col
|
|
56
|
+
render(
|
|
57
|
+
<div className="grid grid-cols-3 gap-4">
|
|
58
|
+
<Surface as="section" framed title="Com moldura" level={3}>
|
|
59
|
+
<p className="text-sm text-muted-foreground">
|
|
60
|
+
Mostra o fundo de quem a hospeda.
|
|
61
|
+
</p>
|
|
62
|
+
</Surface>
|
|
63
|
+
<Surface as="section" elevation="raised" title="Com fundo" level={3}>
|
|
64
|
+
<p className="text-sm text-muted-foreground">Fundo próprio, sem borda.</p>
|
|
65
|
+
</Surface>
|
|
66
|
+
<Surface
|
|
67
|
+
as="section"
|
|
68
|
+
framed
|
|
69
|
+
elevation="raised"
|
|
70
|
+
title="Com moldura e fundo"
|
|
71
|
+
level={3}
|
|
72
|
+
>
|
|
73
|
+
<p className="text-sm text-muted-foreground">
|
|
74
|
+
O registro destacado sobre a página.
|
|
75
|
+
</p>
|
|
76
|
+
</Surface>
|
|
77
|
+
</div>,
|
|
78
|
+
);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Use a moldura para separar blocos vizinhos e o fundo elevado para destacar um registro ou um item
|
|
82
|
+
sobre a página. Quando a hierarquia pedir sombra, declare a intensidade em `className`, por exemplo
|
|
83
|
+
`shadow-sm`.
|
|
84
|
+
|
|
85
|
+
## Cabeçalho, corpo e rodapé
|
|
86
|
+
|
|
87
|
+
Quando a superfície precisa de rodapé ou de controle sobre cada região, componha os slots.
|
|
88
|
+
`SurfaceHeader` monta a linha sozinho: mídia, título com contador e descrição de um lado, ações no
|
|
89
|
+
extremo oposto. A ordem em que os slots são escritos não altera a posição.
|
|
90
|
+
|
|
91
|
+
```tsx preview col
|
|
92
|
+
render(
|
|
93
|
+
<Surface as="article" framed elevation="raised" level={3}>
|
|
94
|
+
<SurfaceHeader>
|
|
95
|
+
<SurfaceTitle>Oficina Centro</SurfaceTitle>
|
|
96
|
+
<SurfaceDescription>
|
|
97
|
+
4 boxes · 2 consultores de plantão.
|
|
98
|
+
</SurfaceDescription>
|
|
99
|
+
<SurfaceActions>
|
|
100
|
+
<Button size="sm" variant="ghost">
|
|
101
|
+
Editar
|
|
102
|
+
</Button>
|
|
103
|
+
</SurfaceActions>
|
|
104
|
+
</SurfaceHeader>
|
|
105
|
+
<SurfaceBody>
|
|
106
|
+
<p className="text-sm text-muted-foreground">
|
|
107
|
+
Próximo horário livre hoje, às 14h.
|
|
108
|
+
</p>
|
|
109
|
+
</SurfaceBody>
|
|
110
|
+
<SurfaceFooter>
|
|
111
|
+
<Button size="sm" variant="outline">
|
|
112
|
+
Ver agenda
|
|
113
|
+
</Button>
|
|
114
|
+
<Button size="sm">Agendar revisão</Button>
|
|
115
|
+
</SurfaceFooter>
|
|
116
|
+
</Surface>,
|
|
117
|
+
);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Cada região é opcional, e o ritmo acompanha as que estão presentes: a última fecha a superfície com
|
|
121
|
+
o mesmo respiro do topo. O rodapé alinha as ações no fim da linha e vale para qualquer superfície,
|
|
122
|
+
inclusive uma região de página sem moldura.
|
|
123
|
+
|
|
124
|
+
A forma curta e a composição explícita produzem os mesmos elementos. Na forma curta, os filhos
|
|
125
|
+
também podem ser `SurfaceBody` e `SurfaceFooter`, o que permite declarar o header por propriedades e
|
|
126
|
+
ainda ter rodapé:
|
|
127
|
+
|
|
128
|
+
```tsx preview col
|
|
129
|
+
render(
|
|
130
|
+
<Surface
|
|
131
|
+
as="section"
|
|
132
|
+
framed
|
|
133
|
+
title="Plano atual"
|
|
134
|
+
description="Renova em 12 de maio."
|
|
135
|
+
level={3}
|
|
136
|
+
>
|
|
137
|
+
<SurfaceBody>
|
|
138
|
+
<p className="text-sm text-muted-foreground">
|
|
139
|
+
Até 10 workspaces e 50 agentes.
|
|
140
|
+
</p>
|
|
141
|
+
</SurfaceBody>
|
|
142
|
+
<SurfaceFooter>
|
|
143
|
+
<Button size="sm" variant="outline">
|
|
144
|
+
Alterar plano
|
|
145
|
+
</Button>
|
|
146
|
+
</SurfaceFooter>
|
|
147
|
+
</Surface>,
|
|
148
|
+
);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Composições que interrompem a renderização
|
|
152
|
+
|
|
153
|
+
A `Surface` reprova as composições que deixariam a estrutura ambígua:
|
|
154
|
+
|
|
155
|
+
- slots estruturais misturados com filhos livres: envolva o conteúdo em `SurfaceBody`;
|
|
156
|
+
- mais de um `SurfaceHeader`, `SurfaceBody` ou `SurfaceFooter`;
|
|
157
|
+
- propriedades da forma curta junto com `SurfaceHeader`;
|
|
158
|
+
- `media`, `count`, `description` ou `actions` sem `title`;
|
|
159
|
+
- um nó dentro de `SurfaceHeader` que não seja um dos seus slots;
|
|
160
|
+
- um `SurfaceHeader` de `fieldset` sem `SurfaceTitle`, necessário para nomear o grupo.
|
|
161
|
+
|
|
162
|
+
O `opus check` aponta os mesmos problemas antes da execução.
|
|
163
|
+
|
|
164
|
+
## Contínuo ou seccionado
|
|
165
|
+
|
|
166
|
+
`layout` declara a anatomia da superfície inteira. No padrão, `continuous`, o espaço separa as
|
|
167
|
+
regiões. Em `sectioned`, header e rodapé viram faixas e uma linha separa as regiões vizinhas.
|
|
168
|
+
|
|
169
|
+
```tsx preview col
|
|
170
|
+
render(
|
|
171
|
+
<div className="grid grid-cols-2 gap-4">
|
|
172
|
+
<Surface as="section" framed elevation="raised" title="Contínuo" level={3}>
|
|
173
|
+
<SurfaceBody>
|
|
174
|
+
<p className="text-sm text-muted-foreground">
|
|
175
|
+
Um assunto, lido de cima a baixo.
|
|
176
|
+
</p>
|
|
177
|
+
</SurfaceBody>
|
|
178
|
+
<SurfaceFooter>
|
|
179
|
+
<Button size="sm" variant="outline">
|
|
180
|
+
Abrir
|
|
181
|
+
</Button>
|
|
182
|
+
</SurfaceFooter>
|
|
183
|
+
</Surface>
|
|
184
|
+
<Surface
|
|
185
|
+
as="section"
|
|
186
|
+
framed
|
|
187
|
+
elevation="raised"
|
|
188
|
+
layout="sectioned"
|
|
189
|
+
title="Seccionado"
|
|
190
|
+
level={3}
|
|
191
|
+
>
|
|
192
|
+
<SurfaceBody>
|
|
193
|
+
<p className="text-sm text-muted-foreground">
|
|
194
|
+
Regiões com pesos diferentes.
|
|
195
|
+
</p>
|
|
196
|
+
</SurfaceBody>
|
|
197
|
+
<SurfaceFooter>
|
|
198
|
+
<Button size="sm" variant="outline">
|
|
199
|
+
Abrir
|
|
200
|
+
</Button>
|
|
201
|
+
</SurfaceFooter>
|
|
202
|
+
</Surface>
|
|
203
|
+
</div>,
|
|
204
|
+
);
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Use `continuous` para um bloco de leitura e `sectioned` para uma superfície de operação, com lista
|
|
208
|
+
ou tabela no corpo e ações persistentes no rodapé. `sectioned` exige `framed` ou
|
|
209
|
+
`elevation="raised"`: os divisores precisam de um limite para alcançar.
|
|
210
|
+
|
|
211
|
+
Os divisores são derivados das regiões presentes. Um header sozinho não desenha linha, e um header
|
|
212
|
+
seguido direto de um rodapé produz uma linha só. Por isso, não escreva `border-b` nem `border-t`
|
|
213
|
+
nas regiões, e use `layout="continuous"` para uma superfície sem divisores.
|
|
214
|
+
|
|
215
|
+
### Corpo de ponta a ponta
|
|
216
|
+
|
|
217
|
+
Quando o filho do corpo já controla o próprio recuo, como `DetailGroup` com divisores, uma lista ou
|
|
218
|
+
uma tabela, declare `flush` em `SurfaceBody`. O corpo entrega a largura inteira ao filho e a moldura
|
|
219
|
+
recorta os cantos.
|
|
220
|
+
|
|
221
|
+
```tsx preview col
|
|
222
|
+
render(
|
|
223
|
+
<Surface
|
|
224
|
+
as="section"
|
|
225
|
+
framed
|
|
226
|
+
layout="sectioned"
|
|
227
|
+
title="Veículo"
|
|
228
|
+
media={
|
|
229
|
+
<IconTile size="xs">
|
|
230
|
+
<CarFront />
|
|
231
|
+
</IconTile>
|
|
232
|
+
}
|
|
233
|
+
>
|
|
234
|
+
<SurfaceBody flush>
|
|
235
|
+
<DetailGroup dividers columns={2} orientation="horizontal">
|
|
236
|
+
<DetailField label="Marca" value="Toyota" />
|
|
237
|
+
<DetailField label="Modelo" value="Yaris XLS" />
|
|
238
|
+
<DetailField label="Ano/modelo" value="2021" />
|
|
239
|
+
<DetailField label="Placa" value="EVT0012" />
|
|
240
|
+
</DetailGroup>
|
|
241
|
+
</SurfaceBody>
|
|
242
|
+
</Surface>,
|
|
243
|
+
);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Mídia
|
|
247
|
+
|
|
248
|
+
`SurfaceMedia`, ou `media` na forma curta, reserva a posição que abre a linha do header. O slot não
|
|
249
|
+
desenha moldura e não altera o que recebe: o conteúdo entra como foi escrito.
|
|
250
|
+
|
|
251
|
+
- Para um ícone com moldura, componha [`IconTile`](/ui/icon-tile) e escolha nele `size`, `context` e
|
|
252
|
+
`variant`.
|
|
253
|
+
- Para uma pessoa ou uma marca, passe [`Avatar`](/ui/avatar) ou `img` diretamente.
|
|
254
|
+
- Um glifo solto aparece sem moldura, no tamanho de `1rem`; uma classe `size-*` no ícone prevalece.
|
|
255
|
+
|
|
256
|
+
```tsx preview col
|
|
257
|
+
render(
|
|
258
|
+
<div className="grid grid-cols-3 gap-4">
|
|
259
|
+
<Surface as="article" framed elevation="raised" level={3}>
|
|
260
|
+
<SurfaceHeader>
|
|
261
|
+
<SurfaceMedia>
|
|
262
|
+
<IconTile size="sm" context="primary">
|
|
263
|
+
<CarFront />
|
|
264
|
+
</IconTile>
|
|
265
|
+
</SurfaceMedia>
|
|
266
|
+
<SurfaceTitle>Revisão dos 20.000 km</SurfaceTitle>
|
|
267
|
+
<SurfaceDescription>12 de maio, às 9h.</SurfaceDescription>
|
|
268
|
+
</SurfaceHeader>
|
|
269
|
+
</Surface>
|
|
270
|
+
<Surface as="article" framed elevation="raised" level={3}>
|
|
271
|
+
<SurfaceHeader>
|
|
272
|
+
<SurfaceMedia>
|
|
273
|
+
<Avatar>
|
|
274
|
+
<AvatarFallback>MA</AvatarFallback>
|
|
275
|
+
</Avatar>
|
|
276
|
+
</SurfaceMedia>
|
|
277
|
+
<SurfaceTitle>Marina Alves</SurfaceTitle>
|
|
278
|
+
<SurfaceDescription>Consultora responsável.</SurfaceDescription>
|
|
279
|
+
</SurfaceHeader>
|
|
280
|
+
</Surface>
|
|
281
|
+
<Surface as="article" framed elevation="raised" level={3}>
|
|
282
|
+
<SurfaceHeader>
|
|
283
|
+
<SurfaceMedia>
|
|
284
|
+
<ShieldCheck aria-hidden="true" />
|
|
285
|
+
</SurfaceMedia>
|
|
286
|
+
<SurfaceTitle>Garantia ativa</SurfaceTitle>
|
|
287
|
+
<SurfaceDescription>Até março de 2027.</SurfaceDescription>
|
|
288
|
+
</SurfaceHeader>
|
|
289
|
+
</Surface>
|
|
290
|
+
</div>,
|
|
291
|
+
);
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
`media` com `null` ou `false` não abre o slot.
|
|
295
|
+
|
|
296
|
+
## Grupo de campos
|
|
297
|
+
|
|
298
|
+
Quando a superfície nomear um conjunto de campos relacionados, use `as="fieldset"`. O título vira a
|
|
299
|
+
`legend` nativa e as propriedades de `fieldset`, como `disabled`, continuam disponíveis. A legenda
|
|
300
|
+
aceita somente título e mídia; contador, descrição e ações não fazem parte do nome do grupo. A
|
|
301
|
+
mídia da legenda é decorativa e fica oculta dos leitores de tela.
|
|
302
|
+
|
|
303
|
+
```tsx preview col
|
|
304
|
+
render(
|
|
305
|
+
<Surface
|
|
306
|
+
as="fieldset"
|
|
307
|
+
framed
|
|
308
|
+
title="Proprietário"
|
|
309
|
+
media={
|
|
310
|
+
<IconTile size="xs">
|
|
311
|
+
<UserRound />
|
|
312
|
+
</IconTile>
|
|
313
|
+
}
|
|
314
|
+
>
|
|
315
|
+
<FieldGroup>
|
|
316
|
+
<Field>
|
|
317
|
+
<FieldLabel htmlFor="owner-name">Nome</FieldLabel>
|
|
318
|
+
<Input id="owner-name" />
|
|
319
|
+
</Field>
|
|
320
|
+
<Field>
|
|
321
|
+
<FieldLabel htmlFor="owner-document">CPF/CNPJ</FieldLabel>
|
|
322
|
+
<Input id="owner-document" />
|
|
323
|
+
</Field>
|
|
324
|
+
</FieldGroup>
|
|
325
|
+
</Surface>,
|
|
326
|
+
);
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Com `framed`, a legenda ocupa a borda do grupo, como em um `fieldset` nativo. Com
|
|
330
|
+
`layout="sectioned"`, ela passa a ser a faixa do header, dentro da moldura. Sem moldura, o grupo
|
|
331
|
+
flui alinhado ao formulário.
|
|
332
|
+
|
|
333
|
+
## Superfície clicável
|
|
334
|
+
|
|
335
|
+
Quando o bloco inteiro leva a outro lugar ou dispara uma ação, use `asChild`: o filho — um `button`
|
|
336
|
+
ou um link — recebe a moldura e o fundo e mantém a própria semântica. `asChild` não combina com
|
|
337
|
+
`as` nem com a forma curta; componha os slots dentro do filho.
|
|
338
|
+
|
|
339
|
+
```tsx preview col
|
|
340
|
+
render(
|
|
341
|
+
<Surface
|
|
342
|
+
asChild
|
|
343
|
+
framed
|
|
344
|
+
elevation="raised"
|
|
345
|
+
level={3}
|
|
346
|
+
className="w-full cursor-pointer text-left transition-colors hover:bg-muted"
|
|
347
|
+
>
|
|
348
|
+
<button type="button">
|
|
349
|
+
<SurfaceHeader>
|
|
350
|
+
<SurfaceTitle>Camada de dados</SurfaceTitle>
|
|
351
|
+
<SurfaceDescription>
|
|
352
|
+
Entidades, consultas e migrações.
|
|
353
|
+
</SurfaceDescription>
|
|
354
|
+
<SurfaceActions>
|
|
355
|
+
<ArrowRight
|
|
356
|
+
aria-hidden="true"
|
|
357
|
+
className="size-4 text-muted-foreground"
|
|
358
|
+
/>
|
|
359
|
+
</SurfaceActions>
|
|
360
|
+
</SurfaceHeader>
|
|
361
|
+
</button>
|
|
362
|
+
</Surface>,
|
|
363
|
+
);
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Os estados de hover e de foco pertencem ao elemento interativo e são declarados em `className`. Não
|
|
367
|
+
coloque outro controle interativo dentro de uma superfície clicável.
|
|
368
|
+
|
|
369
|
+
## Caixa simples
|
|
370
|
+
|
|
371
|
+
Sem título e sem slots, a `Surface` é uma caixa: aplica somente o que `framed` e `elevation`
|
|
372
|
+
declaram, sem recuo nem espaçamento. O conteúdo e o recuo são seus.
|
|
373
|
+
|
|
374
|
+
```tsx preview col
|
|
375
|
+
<Surface framed elevation="raised" className="p-4">
|
|
376
|
+
<p className="text-sm text-muted-foreground">
|
|
377
|
+
3 veículos aguardando retirada.
|
|
378
|
+
</p>
|
|
379
|
+
</Surface>
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Hierarquia do título
|
|
383
|
+
|
|
384
|
+
`level` escolhe o heading, de `1` a `6`, com `2` como padrão. Ajuste o nível pela estrutura do
|
|
385
|
+
documento: uma superfície dentro de uma seção que já tem `h2` usa `level={3}`. Os níveis de 2 a 6
|
|
386
|
+
têm a mesma aparência.
|
|
387
|
+
|
|
388
|
+
O nível 1 é diferente: identifica o conteúdo principal da página e usa a escala do título de
|
|
389
|
+
[`Page`](/ui/page). É o caso da página centrada em uma coleção.
|
|
390
|
+
|
|
391
|
+
## Coleção nomeada
|
|
392
|
+
|
|
393
|
+
Quando a região principal da página é uma coleção, a `Surface` nomeia essa coleção e a
|
|
394
|
+
[`ActionList`](/ui/action-list) executa a consulta. Coloque criar, importar e outras ações sobre o
|
|
395
|
+
conjunto completo nas ações da superfície. Busca, filtros, atualização e operações que dependem do
|
|
396
|
+
recorte atual permanecem na toolbar da lista.
|
|
397
|
+
|
|
398
|
+
```tsx preview col
|
|
399
|
+
render(
|
|
400
|
+
<Surface
|
|
401
|
+
as="section"
|
|
402
|
+
level={1}
|
|
403
|
+
title="Clientes"
|
|
404
|
+
actions={<Button>Criar cliente</Button>}
|
|
405
|
+
>
|
|
406
|
+
<p className="text-sm text-muted-foreground">
|
|
407
|
+
A lista de clientes ocupa o corpo.
|
|
408
|
+
</p>
|
|
409
|
+
</Surface>,
|
|
410
|
+
);
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
A criação usa um `Button` textual na variante padrão e no tamanho `default`, sem ícone, nomeado
|
|
414
|
+
`Criar {recurso}`; o diálogo aberto por ele repete esse título, e a edição usa `Editar {recurso}`.
|
|
415
|
+
|
|
416
|
+
Em uma [`Page`](/ui/page) com `layout="fill"`, a coleção ocupa a altura da página e rola só os
|
|
417
|
+
resultados. `Surface` e `SurfaceBody` repassam a altura até a `ActionList` sem classes adicionais;
|
|
418
|
+
uma superfície irmã, sem lista, mantém a altura do próprio conteúdo. Uma superfície com
|
|
419
|
+
`elevation="raised"` não repassa a altura: ela mantém a altura do próprio conteúdo. A seção
|
|
420
|
+
“Ocupar a altura disponível” da `ActionList` mostra a composição completa.
|
|
421
|
+
|
|
422
|
+
## Fundo e aninhamento
|
|
423
|
+
|
|
424
|
+
`elevation="none"` não pinta fundo: a superfície mostra o fundo de quem a hospeda, em qualquer nível
|
|
425
|
+
de aninhamento. `elevation="raised"` pinta o fundo de superfície elevada e o publica na variável CSS
|
|
426
|
+
`--surface-background`, que `Dialog` e `Drawer` também declaram.
|
|
427
|
+
|
|
428
|
+
Um elemento seu que precise de uma cor opaca igual à do fundo visível — um cabeçalho fixo ou uma
|
|
429
|
+
faixa que cobre o conteúdo ao rolar — usa `bg-[var(--surface-background,var(--background))]`. Na
|
|
430
|
+
página, vale `--background`; dentro de uma superfície elevada, o fundo dela. Um contêiner da
|
|
431
|
+
aplicação que pinte outro fundo declara a mesma variável com a cor que usa.
|
|
432
|
+
|
|
433
|
+
## Propriedades de Surface
|
|
434
|
+
|
|
435
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
436
|
+
| ------------- | ----------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------ |
|
|
437
|
+
| `as` | `'div' \| 'section' \| 'article' \| 'fieldset'` | `'div'` | Elemento da superfície. Não combina com `asChild`. |
|
|
438
|
+
| `asChild` | `boolean` | `false` | Entrega a superfície ao único filho, como um `button` ou um link. |
|
|
439
|
+
| `framed` | `boolean` | `false` | Acrescenta borda e raio, e recorta o conteúdo seccionado ou sem recuo. |
|
|
440
|
+
| `elevation` | `'none' \| 'raised'` | `'none'` | `raised` pinta o fundo de superfície elevada e mantém a altura do próprio conteúdo. |
|
|
441
|
+
| `layout` | `'continuous' \| 'sectioned'` | `'continuous'` | Anatomia das regiões. `sectioned` exige `framed` ou `elevation="raised"`. |
|
|
442
|
+
| `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `2` | Nível do heading do título. `1` identifica o conteúdo principal da página. Não se aplica a `fieldset`. |
|
|
443
|
+
| `title` | `ReactNode` | | Forma curta: título da superfície. |
|
|
444
|
+
| `media` | `ReactNode` | | Forma curta: conteúdo do slot de mídia, antes do título. Exige `title`. |
|
|
445
|
+
| `count` | `number` | | Forma curta: total de itens ao lado do título. Exige `title`; indisponível em `fieldset`. |
|
|
446
|
+
| `description` | `ReactNode` | | Forma curta: frase de apoio sob o título. Exige `title`; indisponível em `fieldset`. |
|
|
447
|
+
| `actions` | `ReactNode` | | Forma curta: ações sobre a superfície inteira. Exige `title`; indisponível em `fieldset`. |
|
|
448
|
+
| `className` | `string` | | Compõe sobre a superfície. Na caixa simples, é onde entra o recuo. |
|
|
449
|
+
| `children` | `ReactNode` | | Filhos livres, que formam o corpo, ou os slots `SurfaceHeader`, `SurfaceBody` e `SurfaceFooter`. |
|
|
450
|
+
|
|
451
|
+
As demais propriedades são as nativas do elemento escolhido em `as`.
|
|
452
|
+
|
|
453
|
+
## Propriedades de SurfaceHeader
|
|
454
|
+
|
|
455
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
456
|
+
| ----------- | ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
457
|
+
| `children` | `ReactNode` | | No máximo um de cada: `SurfaceMedia`, `SurfaceTitle`, `SurfaceMeta`, `SurfaceDescription` e `SurfaceActions`. Em `fieldset`, o `SurfaceTitle` é obrigatório e somente ele e `SurfaceMedia` são aceitos. |
|
|
458
|
+
| `className` | `string` | | Ajusta a região do header. |
|
|
459
|
+
|
|
460
|
+
`SurfaceHeader` só existe como filho direto de `Surface`.
|
|
461
|
+
|
|
462
|
+
## Propriedades de SurfaceMedia
|
|
463
|
+
|
|
464
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
465
|
+
| ----------- | ----------- | ------ | ----------------------------------------------------------------------- |
|
|
466
|
+
| `children` | `ReactNode` | | `IconTile`, `Avatar`, `img` ou um glifo. O slot não acrescenta moldura. |
|
|
467
|
+
| `className` | `string` | | Ajusta o alinhamento do slot. |
|
|
468
|
+
|
|
469
|
+
## Propriedades de SurfaceTitle
|
|
470
|
+
|
|
471
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
472
|
+
| ----------- | ----------- | ------ | ------------------------------------------------------------------- |
|
|
473
|
+
| `children` | `ReactNode` | | Nome da superfície. O heading usa o `level` declarado em `Surface`. |
|
|
474
|
+
| `className` | `string` | | Ajusta o título. |
|
|
475
|
+
|
|
476
|
+
## Propriedades de SurfaceMeta
|
|
477
|
+
|
|
478
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
479
|
+
| ----------- | ----------- | ------ | -------------------------------------------------------- |
|
|
480
|
+
| `children` | `ReactNode` | | Metadado curto ao lado do título, como o total de itens. |
|
|
481
|
+
| `className` | `string` | | Ajusta o metadado. |
|
|
482
|
+
|
|
483
|
+
## Propriedades de SurfaceDescription
|
|
484
|
+
|
|
485
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
486
|
+
| ----------- | ----------- | ------ | ---------------------------- |
|
|
487
|
+
| `children` | `ReactNode` | | Frase de apoio sob o título. |
|
|
488
|
+
| `className` | `string` | | Ajusta a descrição. |
|
|
489
|
+
|
|
490
|
+
## Propriedades de SurfaceActions
|
|
491
|
+
|
|
492
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
493
|
+
| ----------- | ----------- | ------ | ---------------------------------------------------------------- |
|
|
494
|
+
| `children` | `ReactNode` | | Controles no extremo oposto ao título, como um botão ou um menu. |
|
|
495
|
+
| `className` | `string` | | Ajusta o agrupamento das ações. |
|
|
496
|
+
|
|
497
|
+
## Propriedades de SurfaceBody
|
|
498
|
+
|
|
499
|
+
Os filhos diretos recebem o ritmo estrutural de `1.5rem`. O layout da `Surface` decide o inset;
|
|
500
|
+
um wrapper filho declara relações locais mais compactas.
|
|
501
|
+
|
|
502
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
503
|
+
| ----------- | ----------- | ------- | ---------------------------------------------------------------------- |
|
|
504
|
+
| `flush` | `boolean` | `false` | Remove o recuo do corpo para um filho que já controla o próprio recuo. |
|
|
505
|
+
| `children` | `ReactNode` | | Blocos principais, separados pelo ritmo estrutural. |
|
|
506
|
+
| `className` | `string` | | Ajusta ocupação, overflow, alinhamento ou tipografia. |
|
|
507
|
+
|
|
508
|
+
## Propriedades de SurfaceFooter
|
|
509
|
+
|
|
510
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
511
|
+
| ----------- | ----------- | ------ | ---------------------------------------------------- |
|
|
512
|
+
| `children` | `ReactNode` | | Ações persistentes, alinhadas no fim da linha. |
|
|
513
|
+
| `className` | `string` | | Ajusta o rodapé, inclusive a distribuição das ações. |
|
|
@@ -42,12 +42,12 @@ mostrar somente o ícone, declare `aria-label` para preservar seu nome acessíve
|
|
|
42
42
|
|
|
43
43
|
Use `framed` quando a navegação e seus painéis precisarem formar um único bloco. A propriedade
|
|
44
44
|
reúne borda, raio e recorte no próprio `Tabs`, mas não aplica fundo: o bloco herda a
|
|
45
|
-
superfície em que foi composto. Por isso, não envolva `Tabs` em um `
|
|
45
|
+
superfície em que foi composto. Por isso, não envolva `Tabs` em um `Surface` somente para obter essa
|
|
46
46
|
moldura.
|
|
47
47
|
|
|
48
48
|
```tsx preview col
|
|
49
49
|
<Tabs defaultValue="overview" framed className="gap-0">
|
|
50
|
-
<TabsList
|
|
50
|
+
<TabsList>
|
|
51
51
|
<TabsListItem value="overview">Visão geral</TabsListItem>
|
|
52
52
|
<TabsListItem value="history">Histórico</TabsListItem>
|
|
53
53
|
</TabsList>
|
|
@@ -92,8 +92,8 @@ essa linha. A linha respeita o recuo do contêiner; dentro de `DrawerBody` e `Di
|
|
|
92
92
|
acompanha o alinhamento do restante do corpo sem alcançar as laterais da superfície.
|
|
93
93
|
Cada item contém uma área interativa com a mesma altura de um Button do tamanho correspondente. A
|
|
94
94
|
área permanece transparente e recebe fundo apenas no hover; a aba ativa é indicada pela linha
|
|
95
|
-
primária inferior. Um respiro de `0.375rem` separa o controle dessa linha
|
|
96
|
-
|
|
95
|
+
primária inferior. Um respiro de `0.375rem` separa o controle dessa linha. Em `Tabs framed`, a
|
|
96
|
+
lista recebe `0.25rem` nas laterais e no topo, e a mesma medida separa o controle da linha.
|
|
97
97
|
|
|
98
98
|
```tsx preview col
|
|
99
99
|
<Tabs defaultValue="agents">
|
|
@@ -165,34 +165,34 @@ do gatilho.
|
|
|
165
165
|
|
|
166
166
|
## Propriedades de Tabs
|
|
167
167
|
|
|
168
|
-
| Propriedade | Tipo | Padrão | Descrição
|
|
169
|
-
| --------------- | ---------------------------- | -------------- |
|
|
170
|
-
| `defaultValue` | `string` | | Aba inicial no modo não controlado.
|
|
171
|
-
| `value` | `string` | | Aba ativa no modo controlado. Use com `onValueChange`.
|
|
172
|
-
| `onValueChange` | `(value: string) => void` | | Chamado quando a pessoa seleciona outra aba.
|
|
173
|
-
| `size` | `'default' \| 'sm'` | `'default'` | Altura da área interativa na escala única dos controles (2.25 e 2rem), aplicada aos gatilhos.
|
|
174
|
-
| `distribution` | `'content' \| 'equal'` | `'content'` | Preserva a largura dos rótulos ou divide igualmente a largura disponível entre os gatilhos.
|
|
175
|
-
| `framed` | `boolean` | `false` | Aplica borda, raio e recorte ao conjunto sem definir fundo ou foreground.
|
|
176
|
-
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da lista de abas.
|
|
168
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
169
|
+
| --------------- | ---------------------------- | -------------- | --------------------------------------------------------------------------------------------- |
|
|
170
|
+
| `defaultValue` | `string` | | Aba inicial no modo não controlado. |
|
|
171
|
+
| `value` | `string` | | Aba ativa no modo controlado. Use com `onValueChange`. |
|
|
172
|
+
| `onValueChange` | `(value: string) => void` | | Chamado quando a pessoa seleciona outra aba. |
|
|
173
|
+
| `size` | `'default' \| 'sm'` | `'default'` | Altura da área interativa na escala única dos controles (2.25 e 2rem), aplicada aos gatilhos. |
|
|
174
|
+
| `distribution` | `'content' \| 'equal'` | `'content'` | Preserva a largura dos rótulos ou divide igualmente a largura disponível entre os gatilhos. |
|
|
175
|
+
| `framed` | `boolean` | `false` | Aplica borda, raio e recorte ao conjunto sem definir fundo ou foreground. |
|
|
176
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da lista de abas. |
|
|
177
177
|
|
|
178
178
|
## Propriedades de TabsList
|
|
179
179
|
|
|
180
|
-
| Propriedade | Tipo | Padrão | Descrição
|
|
181
|
-
| ------------ | -------- | ------ |
|
|
180
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
181
|
+
| ------------ | -------- | ------ | ----------------------------------------------------------------- |
|
|
182
182
|
| `aria-label` | `string` | | Nomeia a navegação quando não houver um rótulo visível associado. |
|
|
183
|
-
| `className` | `string` | | Ajusta a composição externa sem substituir a linha de navegação.
|
|
183
|
+
| `className` | `string` | | Ajusta a composição externa sem substituir a linha de navegação. |
|
|
184
184
|
|
|
185
185
|
## Propriedades de TabsListItem
|
|
186
186
|
|
|
187
187
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
188
188
|
| ----------- | ----------- | ------- | ----------------------------------------------------------------------------------------------- |
|
|
189
|
-
| `value` | `string` | | Identificador que associa o gatilho ao `TabsPanel` correspondente.
|
|
189
|
+
| `value` | `string` | | Identificador que associa o gatilho ao `TabsPanel` correspondente. |
|
|
190
190
|
| `icon` | `ReactNode` | | Ícone decorativo antes do rótulo, em `1rem`. Em uma aba sem texto, declare também `aria-label`. |
|
|
191
191
|
| `disabled` | `boolean` | `false` | Impede a seleção da aba. |
|
|
192
192
|
|
|
193
193
|
## Propriedades de TabsPanel
|
|
194
194
|
|
|
195
|
-
| Propriedade | Tipo | Padrão | Descrição
|
|
196
|
-
| ------------ | -------- | ------ |
|
|
195
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
196
|
+
| ------------ | -------- | ------ | ----------------------------------------------------------------------- |
|
|
197
197
|
| `value` | `string` | | Identificador do painel, igual ao `value` do `TabsListItem` que o abre. |
|
|
198
|
-
| `forceMount` | `true` | | Mantém o painel montado mesmo quando outra aba está ativa.
|
|
198
|
+
| `forceMount` | `true` | | Mantém o painel montado mesmo quando outra aba está ativa. |
|