@softize/opus 30.0.0 → 31.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/bin/lib/check.mjs +109 -101
  3. package/bin/lib/copy.mjs +2973 -1801
  4. package/docs/adr/0043-neutral-surfaces-and-interactions-use-opaque-role-tokens.md +52 -0
  5. package/docs/adr/0044-neutral-appearance-derives-from-bounded-axes-per-mode.md +27 -2
  6. package/docs/adr/0060-surface-composition-separates-chrome-and-intro.md +142 -0
  7. package/docs/adr/0064-action-list-com-itens-e-fontes-externas.md +47 -0
  8. package/docs/adr/0065-presentation-composes-settings-sections.md +295 -0
  9. package/docs/adr/0066-civil-date-input-and-calendar-list.md +28 -0
  10. package/docs/adr/README.md +6 -0
  11. package/package.json +1 -1
  12. package/registry/skills/build-opus-ui/SKILL.md +21 -19
  13. package/registry/skills/build-opus-ui/references/evaluations.md +13 -11
  14. package/registry/skills/build-opus-ui/references/ui-patterns.md +24 -27
  15. package/src/core/presentation.ts +938 -32
  16. package/src/core/types.ts +2 -0
  17. package/src/ui/components/patterns/form.tsx +22 -1
  18. package/src/ui/components/patterns/grouped-list.ts +2 -2
  19. package/src/ui/components/patterns/list-calendar.tsx +129 -0
  20. package/src/ui/components/patterns/list.tsx +612 -382
  21. package/src/ui/components/patterns/page-heading-context.tsx +12 -5
  22. package/src/ui/components/patterns/page.tsx +227 -197
  23. package/src/ui/components/patterns/presentation-fields.tsx +246 -0
  24. package/src/ui/components/patterns/presentation-sections.tsx +494 -0
  25. package/src/ui/components/patterns/presentation.tsx +1142 -229
  26. package/src/ui/components/patterns/structural-header.tsx +23 -32
  27. package/src/ui/components/patterns/surface-anatomy.ts +27 -217
  28. package/src/ui/components/patterns/surface.tsx +19 -29
  29. package/src/ui/components/patterns/trigger.tsx +10 -2
  30. package/src/ui/components/primitives/calendar.tsx +39 -18
  31. package/src/ui/components/primitives/close-button.tsx +11 -3
  32. package/src/ui/components/primitives/command.tsx +2 -8
  33. package/src/ui/components/primitives/date-input.tsx +276 -0
  34. package/src/ui/components/primitives/detail.tsx +1 -1
  35. package/src/ui/components/primitives/dialog.tsx +19 -81
  36. package/src/ui/components/primitives/drawer.tsx +17 -79
  37. package/src/ui/components/primitives/field.tsx +1 -1
  38. package/src/ui/components/primitives/pagination.tsx +3 -2
  39. package/src/ui/components/primitives/table.tsx +1 -1
  40. package/src/ui/components/primitives/tabs.tsx +13 -2
  41. package/src/ui/docs/content/action-form.md +6 -1
  42. package/src/ui/docs/content/action-list.md +156 -68
  43. package/src/ui/docs/content/action-trigger.md +5 -0
  44. package/src/ui/docs/content/button.md +5 -6
  45. package/src/ui/docs/content/calendar-month-view.md +50 -0
  46. package/src/ui/docs/content/calendar.md +6 -1
  47. package/src/ui/docs/content/command.md +62 -50
  48. package/src/ui/docs/content/compositions.md +129 -459
  49. package/src/ui/docs/content/date-input.md +67 -0
  50. package/src/ui/docs/content/dialog.md +44 -112
  51. package/src/ui/docs/content/drawer.md +44 -103
  52. package/src/ui/docs/content/field.md +2 -1
  53. package/src/ui/docs/content/input.md +3 -0
  54. package/src/ui/docs/content/page.md +204 -412
  55. package/src/ui/docs/content/presentation.md +371 -23
  56. package/src/ui/docs/content/separator.md +1 -1
  57. package/src/ui/docs/content/surface.md +65 -120
  58. package/src/ui/docs/content/tokens.md +18 -4
  59. package/src/ui/docs/registry.tsx +38 -9
  60. package/src/ui/drivers/context.tsx +11 -4
  61. package/src/ui/drivers/react.tsx +27 -7
  62. package/src/ui/meta.ts +17 -6
  63. package/src/ui/react.tsx +41 -8
  64. package/src/ui/theme.css +44 -33
@@ -1,491 +1,161 @@
1
- ---
2
- title: Composições
3
- ---
1
+ ## Uma estrutura para cada superfície
4
2
 
5
- # Composições
3
+ Page, Dialog, Drawer e Surface compartilham cabeçalho, corpo e rodapé. Escolha a superfície
4
+ pelo contexto da tarefa: Page ocupa a área principal, Dialog interrompe para uma tarefa,
5
+ Drawer mantém o detalhe lateral e Surface agrupa uma região dentro do conteúdo.
6
6
 
7
- Use esta página para decidir como montar uma tela antes de escolher as propriedades de cada
8
- componente. `Page`, `Dialog`, `Drawer` e `Surface` se organizam da mesma forma: um cabeçalho, um
9
- corpo e um rodapé com a decisão da etapa. Cada família admite as ações e a navegação indicadas na
10
- tabela abaixo. Em `Page`, `Dialog` e `Drawer`, o corpo também pode começar por uma intro que leva o
11
- título. `Pane` fica fora dessa organização: ele só reserva, dentro de um `Split`, o espaço em que
12
- uma `Surface` aparece.
7
+ | Região | Page | Dialog | Drawer | Surface |
8
+ | ------------ | ----------- | ------------- | ------------- | -------------- |
9
+ | Cabeçalho | PageHeader | DialogHeader | DrawerHeader | SurfaceHeader |
10
+ | Título | PageTitle | DialogTitle | DrawerTitle | SurfaceTitle |
11
+ | Metadados | PageMeta | DialogMeta | DrawerMeta | SurfaceMeta |
12
+ | Ações gerais | PageActions | DialogActions | DrawerActions | SurfaceActions |
13
+ | Corpo | PageBody | DialogBody | DrawerBody | SurfaceBody |
14
+ | Rodapé | PageFooter | DialogFooter | DrawerFooter | SurfaceFooter |
13
15
 
14
- Todos os exemplos mostram a mesma tarefa, editar uma ordem de serviço, em superfícies diferentes.
15
- As propriedades de cada componente ficam nas páginas da família: [`Page`](/ui/page),
16
- [`Dialog`](/ui/dialog), [`Drawer`](/ui/drawer), [`Surface`](/ui/surface) e [`Pane`](/ui/pane).
17
-
18
- ## As regiões de cada superfície
19
-
20
- Cada família dá um nome próprio a cada região. A tabela mostra a correspondência entre elas.
21
-
22
- | Região | Page | Dialog | Drawer | Surface |
23
- | -------------------- | ------------------------------------------- | ----------------------------- | ----------------------------- | ----------------------------- |
24
- | Navegação | `PageNavigation` ou `PageBack` | `DialogNavigation` | `DrawerNavigation` | Não existe |
25
- | Título no cabeçalho | `PageTitle` | `DialogTitle` | `DrawerTitle` | `SurfaceTitle` |
26
- | Ações do cabeçalho | `PageActions` | `DialogActions` | `DrawerActions` | `SurfaceActions` |
27
- | Intro | `PageIntro` no início do `PageBody` | `DialogIntro` | `DrawerIntro` | Não existe |
28
- | Ações da intro | `PageIntroActions` | `DialogIntroActions` | `DrawerIntroActions` | Não existe |
29
- | Corpo | `PageBody` | `DialogBody` | `DrawerBody` | `SurfaceBody` |
30
- | Rodapé | `PageFooter` | `DialogFooter` | `DrawerFooter` | `SurfaceFooter` |
31
- | Divisores | Antes do `PageFooter` | `sectioned` (padrão) | `sectioned` (padrão) | `sectioned` |
32
- | Sem divisores | Cabeçalho e corpo | `continuous` | `continuous` | `continuous` (padrão) |
33
-
34
- `layout` escolhe entre as duas anatomias em `DialogContent`, `DrawerContent` e `Surface`. Em
35
- `sectioned`, uma linha separa as regiões vizinhas; em `continuous`, o espaço faz essa separação. Na
36
- `Surface`, `sectioned` exige `framed` ou `elevation="raised"`, para que a linha tenha um limite. A
37
- `Page` não oferece anatomia `sectioned`: cabeçalho e corpo são contínuos, enquanto o `PageFooter`
38
- é uma faixa própria, separada por sua borda superior. O `layout` da página decide apenas a ocupação
39
- de altura.
16
+ O título pertence ao cabeçalho. Descrição opcional e metadados têm slots próprios. Cabeçalho
17
+ e rodapé ficam fora da rolagem do corpo em Page e modais; a Surface acompanha o fluxo do conteúdo.
18
+ Os divisores são automáticos, sem escolha de anatomia contínua ou compacta.
40
19
 
41
20
  ## Onde cada ação fica
42
21
 
43
- A posição de uma ação informa a que ela se refere. Escolha o lugar pelo alcance da ação, não pelo
44
- espaço livre na tela.
45
-
46
- | Lugar | Use para |
47
- | ---------- | ---------------------------------------------------------------------------------------------------------- |
48
- | Navegação | Sair ou voltar na estrutura: a trilha da página, a etapa anterior de um fluxo modal ou recolher o painel. |
49
- | Cabeçalho | Ações que valem para a superfície inteira, como abrir uma conversa sobre o registro. |
50
- | Intro | Ações ligadas ao título e ao conteúdo, como ver o histórico do registro. |
51
- | Rodapé | A decisão da etapa: **Cancelar** em `ghost` e a ação principal sólida, alinhadas à direita. |
52
-
53
- Em `Dialog` e `Drawer`, a navegação abre a linha do cabeçalho e fica junto do título, a `0.5rem`
54
- dele. As ações ficam no extremo oposto, antes do botão de fechar, e mantêm a distância maior da
55
- família até o título. Na `Page`, a navegação fica acima do título; quando o título está na intro,
56
- ela divide a linha com as ações da página.
57
-
58
- Em `Dialog` e `Drawer`, cabeçalho e rodapé permanecem visíveis enquanto o corpo rola; a intro rola
59
- junto com o conteúdo.
60
-
61
- ## Título no cabeçalho ou na intro
62
-
63
- O título pode ficar em dois lugares, e essa escolha define a composição:
22
+ | Ação | Região |
23
+ | ----------------------------------------------------- | ------------------------- |
24
+ | Retorno, histórico ou configuração do recurso inteiro | Ações do cabeçalho |
25
+ | Criar, importar, exportar ou operar a coleção | ActionList.toolbarActions |
26
+ | Operar os registros selecionados | ActionList.batchActions |
27
+ | Operar um registro da lista | ActionList.rowActions |
28
+ | Salvar ou cancelar uma etapa | Rodapé |
64
29
 
65
- - **Com intro:** o cabeçalho guarda só a navegação, as ações da superfície e o fechamento. O título
66
- abre o corpo junto das ações do conteúdo. Use quando esses controles da superfície precisam
67
- continuar estáveis e separados do registro aberto.
68
- - **Compacta:** título, ações do conteúdo e ações da superfície dividem a linha do cabeçalho, e a
69
- intro é omitida. Use quando uma linha basta para nomear a tarefa e agir sobre ela.
30
+ O botão de criação mostra **Criar**. Seu nome acessível e o título do formulário identificam
31
+ o recurso. Menus com vários tipos de criação mantêm nomes específicos.
70
32
 
71
- Na composição compacta, coloque as ações do conteúdo antes das ações da superfície, no mesmo slot de
72
- ações. Em `Dialog` e `Drawer`, o título no cabeçalho trunca quando não cabe na linha; na intro, ele
73
- usa uma escala maior e quebra em mais de uma linha.
33
+ ## Página
74
34
 
75
- ## Dialog
35
+ A página usa a largura disponível. Para uma lista preencher a altura restante, declare
36
+ `layout="fill"`; para conteúdo que rola no corpo, mantenha `flow`.
76
37
 
77
- Abra cada combinação para comparar. As linhas trocam o `layout` de `DialogContent`; as colunas
78
- trocam o lugar do título. O campo e o rodapé são os mesmos nos quatro casos.
79
-
80
- ```tsx preview
81
- function OrderField() {
82
- const id = useId();
83
- return (
84
- <div className="space-y-2">
85
- <Label htmlFor={id}>Responsável</Label>
86
- <Input id={id} defaultValue="Marina Alves" />
87
- </div>
88
- );
89
- }
90
-
91
- function DialogWithIntro({ layout, label }) {
92
- return (
93
- <Dialog>
94
- <DialogTrigger asChild>
95
- <Button variant="outline">{label}</Button>
96
- </DialogTrigger>
97
- <DialogContent layout={layout}>
98
- <DialogHeader>
99
- <DialogActions>
100
- <Button variant="ghost" size="icon-sm" aria-label="Abrir conversa">
101
- <MessageCircle />
102
- </Button>
103
- </DialogActions>
104
- </DialogHeader>
105
- <DialogBody>
106
- <DialogIntro>
107
- <DialogTitle>Editar ordem de serviço</DialogTitle>
108
- <DialogIntroActions>
109
- <Button variant="outline" size="sm">
110
- Ver histórico
111
- </Button>
112
- </DialogIntroActions>
113
- </DialogIntro>
114
- <OrderField />
115
- </DialogBody>
116
- <DialogFooter>
117
- <ButtonGroup mode="spaced">
118
- <DialogClose asChild>
119
- <Button variant="ghost">Cancelar</Button>
120
- </DialogClose>
121
- <Button>Salvar</Button>
122
- </ButtonGroup>
123
- </DialogFooter>
124
- </DialogContent>
125
- </Dialog>
126
- );
127
- }
128
-
129
- function CompactDialog({ layout, label }) {
130
- return (
131
- <Dialog>
132
- <DialogTrigger asChild>
133
- <Button variant="outline">{label}</Button>
134
- </DialogTrigger>
135
- <DialogContent layout={layout}>
136
- <DialogHeader>
137
- <DialogMedia>
138
- <Wrench />
139
- </DialogMedia>
140
- <DialogTitle>Editar ordem de serviço</DialogTitle>
141
- <DialogActions>
142
- <Button variant="outline" size="sm">
143
- Ver histórico
144
- </Button>
145
- <Button variant="ghost" size="icon-sm" aria-label="Abrir conversa">
146
- <MessageCircle />
147
- </Button>
148
- </DialogActions>
149
- </DialogHeader>
150
- <DialogBody>
151
- <OrderField />
152
- </DialogBody>
153
- <DialogFooter>
154
- <ButtonGroup mode="spaced">
155
- <DialogClose asChild>
156
- <Button variant="ghost">Cancelar</Button>
157
- </DialogClose>
158
- <Button>Salvar</Button>
159
- </ButtonGroup>
160
- </DialogFooter>
161
- </DialogContent>
162
- </Dialog>
163
- );
164
- }
165
-
166
- render(
167
- <div className="grid grid-cols-2 gap-3">
168
- <DialogWithIntro layout="sectioned" label="Seccionado com intro" />
169
- <CompactDialog layout="sectioned" label="Seccionado compacto" />
170
- <DialogWithIntro layout="continuous" label="Contínuo com intro" />
171
- <CompactDialog layout="continuous" label="Contínuo compacto" />
172
- </div>,
173
- );
38
+ ```tsx preview col
39
+ <Page>
40
+ <PageHeader>
41
+ <PageTitle>Evento</PageTitle>
42
+ <PageActions>
43
+ <PageBack href="/events">Eventos</PageBack>
44
+ <Button variant="ghost" size="icon" aria-label="Histórico">
45
+ <History />
46
+ </Button>
47
+ </PageActions>
48
+ </PageHeader>
49
+ <PageBody>
50
+ <p>Dados do evento.</p>
51
+ </PageBody>
52
+ <PageFooter>
53
+ <ButtonGroup mode="spaced">
54
+ <Button variant="ghost">Cancelar</Button>
55
+ <Button>Salvar</Button>
56
+ </ButtonGroup>
57
+ </PageFooter>
58
+ </Page>
174
59
  ```
175
60
 
176
- Prefira `continuous` para uma tarefa curta, em que a linha entre as regiões seria ruído. O `Dialog`
177
- não recebe navegação neste exemplo: use `DialogNavigation` quando o modal trocar de etapa e a pessoa
178
- precisar voltar à anterior.
61
+ `PageShell` fornece ações conhecidas pelo shell sem criar outra barra. Subpáginas usam
62
+ `PageBack` como primeiro ícone das ações, com destino explícito.
179
63
 
180
- ## Drawer
64
+ ## Diálogo
181
65
 
182
- O `Drawer` segue a mesma grade. A navegação aparece aqui como o controle de recolher o painel: um
183
- `DrawerClose` dentro de `DrawerNavigation`, com `showCloseButton={false}` para que o painel não tenha
184
- dois controles de fechamento.
66
+ O diálogo preserva a mesma ordem das regiões. Fechar, Escape e o clique fora dispensam
67
+ uma tarefa comum; use `closeDisabled` durante uma operação que não possa ser interrompida.
185
68
 
186
69
  ```tsx preview
187
- function OrderField() {
188
- const id = useId();
189
- return (
190
- <div className="space-y-2">
191
- <Label htmlFor={id}>Responsável</Label>
192
- <Input id={id} defaultValue="Marina Alves" />
193
- </div>
194
- );
195
- }
196
-
197
- function CollapseButton() {
198
- return (
199
- <DrawerClose asChild>
200
- <Button variant="ghost" size="icon-sm" aria-label="Recolher painel">
201
- <ChevronsRight />
202
- </Button>
203
- </DrawerClose>
204
- );
205
- }
206
-
207
- function DrawerWithIntro({ layout, label }) {
208
- return (
209
- <Drawer>
210
- <DrawerTrigger asChild>
211
- <Button variant="outline">{label}</Button>
212
- </DrawerTrigger>
213
- <DrawerContent layout={layout} showCloseButton={false}>
214
- <DrawerHeader>
215
- <DrawerNavigation>
216
- <CollapseButton />
217
- </DrawerNavigation>
218
- <DrawerActions>
219
- <Button variant="ghost" size="icon-sm" aria-label="Abrir conversa">
220
- <MessageCircle />
221
- </Button>
222
- </DrawerActions>
223
- </DrawerHeader>
224
- <DrawerBody>
225
- <DrawerIntro>
226
- <DrawerTitle>Editar ordem de serviço</DrawerTitle>
227
- <DrawerIntroActions>
228
- <Button variant="outline" size="sm">
229
- Ver histórico
230
- </Button>
231
- </DrawerIntroActions>
232
- </DrawerIntro>
233
- <OrderField />
234
- </DrawerBody>
235
- <DrawerFooter>
236
- <ButtonGroup mode="spaced">
237
- <DrawerClose asChild>
238
- <Button variant="ghost">Cancelar</Button>
239
- </DrawerClose>
240
- <Button>Salvar</Button>
241
- </ButtonGroup>
242
- </DrawerFooter>
243
- </DrawerContent>
244
- </Drawer>
245
- );
246
- }
247
-
248
- function CompactDrawer({ layout, label }) {
249
- return (
250
- <Drawer>
251
- <DrawerTrigger asChild>
252
- <Button variant="outline">{label}</Button>
253
- </DrawerTrigger>
254
- <DrawerContent layout={layout} showCloseButton={false}>
255
- <DrawerHeader>
256
- <DrawerNavigation>
257
- <CollapseButton />
258
- </DrawerNavigation>
259
- <DrawerTitle>Editar ordem de serviço</DrawerTitle>
260
- <DrawerActions>
261
- <Button variant="outline" size="sm">
262
- Ver histórico
263
- </Button>
264
- <Button variant="ghost" size="icon-sm" aria-label="Abrir conversa">
265
- <MessageCircle />
266
- </Button>
267
- </DrawerActions>
268
- </DrawerHeader>
269
- <DrawerBody>
270
- <OrderField />
271
- </DrawerBody>
272
- <DrawerFooter>
273
- <ButtonGroup mode="spaced">
274
- <DrawerClose asChild>
275
- <Button variant="ghost">Cancelar</Button>
276
- </DrawerClose>
277
- <Button>Salvar</Button>
278
- </ButtonGroup>
279
- </DrawerFooter>
280
- </DrawerContent>
281
- </Drawer>
282
- );
283
- }
284
-
285
- render(
286
- <div className="grid grid-cols-2 gap-3">
287
- <DrawerWithIntro layout="sectioned" label="Seccionado com intro" />
288
- <CompactDrawer layout="sectioned" label="Seccionado compacto" />
289
- <DrawerWithIntro layout="continuous" label="Contínuo com intro" />
290
- <CompactDrawer layout="continuous" label="Contínuo compacto" />
291
- </div>,
292
- );
70
+ <Dialog>
71
+ <DialogTrigger asChild>
72
+ <Button variant="outline">Abrir diálogo</Button>
73
+ </DialogTrigger>
74
+ <DialogContent aria-describedby={undefined}>
75
+ <DialogHeader>
76
+ <DialogTitle>Editar evento</DialogTitle>
77
+ <DialogActions>
78
+ <Button variant="ghost" size="icon" aria-label="Histórico">
79
+ <History />
80
+ </Button>
81
+ </DialogActions>
82
+ </DialogHeader>
83
+ <DialogBody>
84
+ <p>Dados do evento.</p>
85
+ </DialogBody>
86
+ <DialogFooter>
87
+ <ButtonGroup mode="spaced">
88
+ <DialogClose asChild>
89
+ <Button variant="ghost">Cancelar</Button>
90
+ </DialogClose>
91
+ <Button>Salvar</Button>
92
+ </ButtonGroup>
93
+ </DialogFooter>
94
+ </DialogContent>
95
+ </Dialog>
293
96
  ```
294
97
 
295
- Mantenha `DrawerNavigation`, `DrawerTitle` e `DrawerActions` como filhos diretos de
296
- `DrawerHeader`: o cabeçalho reconhece os slots pelo componente e interrompe a renderização diante de
297
- qualquer outro nó, inclusive um componente local que devolva um slot. Por isso, o exemplo extrai
298
- apenas o botão de recolher. Sem o botão de fechar, Esc e o clique no overlay continuam fechando o
299
- painel.
300
-
301
- ## Page
98
+ `Dialog mode="alert"` conserva sua composição de resposta obrigatória. É uma diferença de
99
+ comportamento, não uma opção de densidade do formulário.
302
100
 
303
- Na composição com intro, o `PageHeader` guarda a navegação e as ações da
304
- página, e uma `PageIntro` abre o `PageBody` com o título e as ações do conteúdo. A página
305
- precisa de exatamente um heading principal, por isso o `PageHeader` dessa composição não leva
306
- `PageTitle`.
307
-
308
- ```tsx preview col
309
- render(
310
- <div className="w-full overflow-hidden rounded-lg border border-border">
311
- <Page>
312
- <PageHeader>
313
- <PageNavigation>
314
- <Breadcrumb>
315
- <BreadcrumbList>
316
- <BreadcrumbItem>Oficina</BreadcrumbItem>
317
- <BreadcrumbSeparator />
318
- <BreadcrumbItem>Ordens de serviço</BreadcrumbItem>
319
- <BreadcrumbSeparator />
320
- <BreadcrumbPage>OS-1842</BreadcrumbPage>
321
- </BreadcrumbList>
322
- </Breadcrumb>
323
- </PageNavigation>
324
- <PageActions>
325
- <Button variant="ghost" size="icon-sm" aria-label="Abrir conversa">
326
- <MessageCircle />
327
- </Button>
328
- </PageActions>
329
- </PageHeader>
330
- <PageBody>
331
- <PageIntro>
332
- <PageTitle>Editar ordem de serviço</PageTitle>
333
- <PageIntroActions><Button variant="outline" size="sm">Ver histórico</Button></PageIntroActions>
334
- </PageIntro>
335
- <div className="space-y-2">
336
- <Label htmlFor="page-intro-owner">Responsável</Label>
337
- <Input id="page-intro-owner" defaultValue="Marina Alves" />
338
- </div>
339
- </PageBody>
340
- <PageFooter>
341
- <ButtonGroup mode="spaced">
342
- <Button variant="ghost">Cancelar</Button>
343
- <Button>Salvar</Button>
344
- </ButtonGroup>
345
- </PageFooter>
346
- </Page>
347
- </div>,
348
- );
349
- ```
101
+ ## Painel lateral
350
102
 
351
- Na composição compacta, `PageTitle` entra no `PageHeader` e as ações do conteúdo passam para
352
- `PageActions`, antes das ações da página. A forma curta, `<Page title actions>`, produz esse mesmo
353
- cabeçalho; nela, a navegação vem do [`PageShell`](/ui/page).
103
+ O drawer usa o mesmo cabeçalho e mantém seu corpo rolável até a altura disponível.
354
104
 
355
- ```tsx preview col
356
- render(
357
- <div className="w-full overflow-hidden rounded-lg border border-border">
358
- <Page>
359
- <PageHeader>
360
- <PageNavigation>
361
- <Breadcrumb>
362
- <BreadcrumbList>
363
- <BreadcrumbItem>Oficina</BreadcrumbItem>
364
- <BreadcrumbSeparator />
365
- <BreadcrumbItem>Ordens de serviço</BreadcrumbItem>
366
- <BreadcrumbSeparator />
367
- <BreadcrumbPage>OS-1842</BreadcrumbPage>
368
- </BreadcrumbList>
369
- </Breadcrumb>
370
- </PageNavigation>
371
- <PageTitle>Editar ordem de serviço</PageTitle>
372
- <PageActions>
373
- <Button variant="outline" size="sm">
374
- Ver histórico
375
- </Button>
376
- <Button variant="ghost" size="icon-sm" aria-label="Abrir conversa">
377
- <MessageCircle />
378
- </Button>
379
- </PageActions>
380
- </PageHeader>
381
- <PageBody>
382
- <div className="space-y-2">
383
- <Label htmlFor="page-compact-owner">Responsável</Label>
384
- <Input id="page-compact-owner" defaultValue="Marina Alves" />
385
- </div>
386
- </PageBody>
387
- <PageFooter>
388
- <ButtonGroup mode="spaced">
105
+ ```tsx preview
106
+ <Drawer>
107
+ <DrawerTrigger asChild>
108
+ <Button variant="outline">Abrir painel</Button>
109
+ </DrawerTrigger>
110
+ <DrawerContent aria-describedby={undefined}>
111
+ <DrawerHeader>
112
+ <DrawerTitle>Evento</DrawerTitle>
113
+ <DrawerActions>
114
+ <Button variant="ghost" size="icon" aria-label="Histórico">
115
+ <History />
116
+ </Button>
117
+ </DrawerActions>
118
+ </DrawerHeader>
119
+ <DrawerBody>
120
+ <p>Dados do evento.</p>
121
+ </DrawerBody>
122
+ <DrawerFooter>
123
+ <ButtonGroup mode="spaced">
124
+ <DrawerClose asChild>
389
125
  <Button variant="ghost">Cancelar</Button>
390
- <Button>Salvar</Button>
391
- </ButtonGroup>
392
- </PageFooter>
393
- </Page>
394
- </div>,
395
- );
126
+ </DrawerClose>
127
+ <Button>Salvar</Button>
128
+ </ButtonGroup>
129
+ </DrawerFooter>
130
+ </DrawerContent>
131
+ </Drawer>
396
132
  ```
397
133
 
398
- Use `PageFooter` quando a página for uma etapa com decisão própria, como esta edição.
399
-
400
134
  ## Pane com Surface
401
135
 
402
- `Pane` declara uma coluna de um [`Split`](/ui/split) e não desenha nada: não tem cabeçalho, corpo,
403
- rodapé, recuo, borda nem fundo. Quando a coluna precisa dessas regiões, componha uma `Surface`
404
- dentro dela. A `Surface` não tem intro, então o título fica no `SurfaceHeader`, como na composição
405
- compacta.
136
+ Pane distribui espaço; Surface fornece a apresentação. Moldura e elevação continuam
137
+ independentes da composição do cabeçalho, corpo e rodapé.
406
138
 
407
139
  ```tsx preview col
408
- render(
409
- <div className="h-80 w-full overflow-hidden rounded-lg border border-border">
410
- <Split>
411
- <Pane initialSize="14rem">
412
- <Sidebar className="w-full">
413
- <SidebarNav
414
- navLabel="Ordens de serviço"
415
- activeId="os-1842"
416
- groups={[
417
- {
418
- items: [
419
- { id: "os-1842", label: "OS-1842" },
420
- { id: "os-1839", label: "OS-1839" },
421
- { id: "os-1835", label: "OS-1835" },
422
- ],
423
- },
424
- ]}
425
- />
426
- </Sidebar>
427
- </Pane>
428
- <Pane grow>
429
- <Surface
430
- as="section"
431
- framed
432
- elevation="raised"
433
- layout="sectioned"
434
- className="flex h-full flex-col"
435
- >
436
- <SurfaceHeader>
437
- <SurfaceMedia>
438
- <Wrench />
439
- </SurfaceMedia>
440
- <SurfaceTitle>Editar ordem de serviço</SurfaceTitle>
441
- <SurfaceActions>
442
- <Button variant="outline" size="sm">
443
- Ver histórico
444
- </Button>
445
- <Button variant="ghost" size="icon-sm" aria-label="Abrir conversa">
446
- <MessageCircle />
447
- </Button>
448
- </SurfaceActions>
449
- </SurfaceHeader>
450
- <SurfaceBody className="min-h-0 flex-1 overflow-y-auto">
451
- <div className="space-y-2">
452
- <Label htmlFor="pane-owner">Responsável</Label>
453
- <Input id="pane-owner" defaultValue="Marina Alves" />
454
- </div>
455
- </SurfaceBody>
456
- <SurfaceFooter>
457
- <ButtonGroup mode="spaced">
458
- <Button variant="ghost">Cancelar</Button>
459
- <Button>Salvar</Button>
460
- </ButtonGroup>
461
- </SurfaceFooter>
462
- </Surface>
463
- </Pane>
464
- </Split>
465
- </div>,
466
- );
140
+ <Pane>
141
+ <Surface framed title="Evento">
142
+ <SurfaceBody>
143
+ <p>Dados do evento.</p>
144
+ </SurfaceBody>
145
+ <SurfaceFooter>
146
+ <Button>Salvar</Button>
147
+ </SurfaceFooter>
148
+ </Surface>
149
+ </Pane>
467
150
  ```
468
151
 
469
- Os dois panes recebem conteúdos diferentes sem mudar de componente: o primeiro hospeda uma
470
- [`Sidebar`](/ui/sidebar), que já tem anatomia própria, e o segundo, uma `Surface` completa. A
471
- rolagem também pertence ao conteúdo: a `Surface` ocupa a altura do pane com `flex h-full flex-col`,
472
- e o `SurfaceBody` declara `min-h-0 flex-1 overflow-y-auto` para rolar entre o cabeçalho e o rodapé.
473
-
474
- ## Ritmo entre regiões e blocos
475
-
476
- Surface (incluindo cards e seções), Page, Dialog, Drawer e Tabs organizam o conteúdo
477
- com intervalos de `1rem`. Os bodies separam seus blocos diretos; grupos internos podem
478
- ter densidade própria. DetailGroup e Separator não criam margens externas.
479
-
480
- Sem delimitação, Surface e Tabs não adicionam padding inferior. Uma Surface com moldura
481
- ou elevação, assim como Dialog e Drawer, reserva `1rem` até a borda. No layout contínuo,
482
- o intervalo vem da região anterior. No seccionado, há `1rem` entre cada divisória e o
483
- conteúdo adjacente. Não some padding e gap para expressar o mesmo intervalo.
152
+ ## Abas e espaçamento
484
153
 
485
- Page conserva os recuos laterais e a largura de leitura. Seu body não acrescenta padding
486
- inferior quando termina a página; antes de um footer seccionado, reserva `1rem` até a linha.
487
- O modo `flush` remove recuos do body para uma lista ou tabela controlar sua própria borda,
488
- mas mantém o intervalo entre blocos.
154
+ O header de cada família aceita `tabs`, com uma `TabsList` governada por `Tabs` ancestral.
155
+ Os painéis ficam no body. Ações de listagem permanecem dentro do painel correspondente.
489
156
 
490
- Ao migrar, remova `space-y`, `gap`, `pt` e `pb` locais que apenas reconstruíam esses defaults
491
- nos slots. Preserve classes de layout internas, como uma grade ou um grupo de campos compacto.
157
+ Superfícies delimitadas usam recuo de `1rem` em cada região. A linha de cabeçalho reserva
158
+ `2,25rem`, com ou sem ações, e recebe botões `default` ou `icon`. O corpo separa blocos diretos
159
+ por `1rem`; grupos com ritmo próprio ficam em um wrapper filho. `flush` permite que um conteúdo
160
+ controle seu próprio recuo. Surface sem moldura ou elevação mantém alinhamento lateral com
161
+ o hospedeiro, sem padding inferior extra. Não reescreva essas medidas em cada tela.