@softize/opus 27.1.0 → 29.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 +113 -0
- package/bin/cli.mjs +68 -3
- package/bin/lib/check.mjs +139 -30
- package/bin/lib/copy.mjs +505 -67
- package/bin/lib/materialize.mjs +99 -1
- package/docs/adr/0015-action-size-follows-interaction-density.md +2 -0
- package/docs/adr/0028-schedules-run-actions-as-the-system-by-action-opt-in.md +1 -1
- package/docs/adr/0029-page-header-is-the-single-page-introduction.md +3 -1
- package/docs/adr/0037-design-data-lives-outside-the-contract.md +1 -1
- package/docs/adr/0060-surface-composition-separates-chrome-and-intro.md +161 -0
- package/docs/adr/0061-claude-md-imports-agents-md.md +41 -0
- package/docs/adr/README.md +4 -0
- package/docs/code-style.md +47 -8
- package/docs/protocol.md +1 -1
- package/docs/releasing.md +7 -0
- package/package.json +1 -1
- package/registry/instructions/opus.md +3 -1
- package/registry/skills/build-opus-ui/SKILL.md +16 -14
- package/registry/skills/build-opus-ui/references/evaluations.md +10 -0
- package/registry/skills/build-opus-ui/references/ui-patterns.md +17 -15
- package/registry/skills/maintain-opus-docs/SKILL.md +2 -1
- package/registry/skills/maintain-opus-docs/references/editorial-standard.md +9 -0
- package/registry/skills/maintain-opus-docs/references/evaluations.md +8 -0
- package/registry/templates/app/pnpm-workspace.yaml +1 -1
- package/src/core/system-actor.ts +1 -1
- package/src/ui/components/patterns/list.tsx +63 -54
- package/src/ui/components/patterns/page.tsx +146 -38
- package/src/ui/components/patterns/presentation.tsx +44 -59
- package/src/ui/components/patterns/structural-header.tsx +42 -6
- package/src/ui/components/patterns/surface-anatomy.ts +52 -22
- package/src/ui/components/patterns/surface.tsx +2 -3
- package/src/ui/components/primitives/control.ts +2 -2
- package/src/ui/components/primitives/dialog.tsx +7 -2
- package/src/ui/components/primitives/drawer.tsx +7 -2
- package/src/ui/components/primitives/icon-picker.tsx +271 -34
- package/src/ui/components/primitives/pagination.tsx +5 -4
- package/src/ui/components/primitives/tabs.tsx +15 -2
- package/src/ui/components/primitives/toggle.tsx +1 -1
- package/src/ui/docs/content/action-list.md +12 -3
- package/src/ui/docs/content/actions.md +1 -1
- package/src/ui/docs/content/auth.md +3 -3
- package/src/ui/docs/content/button.md +7 -7
- package/src/ui/docs/content/cli.md +6 -0
- package/src/ui/docs/content/communication.md +10 -8
- package/src/ui/docs/content/compositions.md +35 -23
- package/src/ui/docs/content/dialog.md +18 -6
- package/src/ui/docs/content/drawer.md +17 -5
- package/src/ui/docs/content/events.md +13 -13
- package/src/ui/docs/content/icon-picker.md +37 -19
- package/src/ui/docs/content/input.md +1 -1
- package/src/ui/docs/content/installation.md +4 -0
- package/src/ui/docs/content/log.md +1 -1
- package/src/ui/docs/content/page.md +169 -69
- package/src/ui/docs/content/pagination.md +7 -8
- package/src/ui/docs/content/presentation.md +4 -3
- package/src/ui/docs/content/queue.md +2 -2
- package/src/ui/docs/content/router.md +1 -1
- package/src/ui/docs/content/scheduler.md +15 -15
- package/src/ui/docs/content/surface.md +8 -5
- package/src/ui/docs/content/tabs.md +12 -4
- package/src/ui/docs/content/testing.md +4 -4
- package/src/ui/meta.ts +1 -1
- package/src/ui/react.tsx +9 -1
- package/src/vite/design.ts +1 -1
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
## Esqueleto de página
|
|
2
2
|
|
|
3
|
-
Use `Page` para manter cabeçalho, estado e conteúdo na mesma anatomia. `PageHeader`
|
|
4
|
-
|
|
5
|
-
centralizado e ocupa a largura disponível até `80rem` (`max-w-7xl`).
|
|
6
|
-
|
|
3
|
+
Use `Page` para manter cabeçalho, estado e conteúdo na mesma anatomia. `PageHeader` recebe navegação e ações da superfície;
|
|
4
|
+
`PageIntro` abre o corpo com título e orientação. Na composição compacta, o título fica no header e a intro é omitida. O conteúdo interno do body é
|
|
5
|
+
centralizado e ocupa a largura disponível até `80rem` (`max-w-7xl`). O header e o viewport
|
|
6
|
+
de rolagem ocupam toda a largura do painel.
|
|
7
7
|
|
|
8
|
-
A forma curta é o padrão para páginas comuns e cria `
|
|
9
|
-
exige exatamente um heading principal. Use `PageTitle` no cabeçalho, `Surface level={1}` no body,
|
|
8
|
+
A forma curta é o padrão para páginas comuns e cria `PageIntro` dentro de `PageBody` e um `PageHeader` quando existem controles. A forma explícita
|
|
9
|
+
exige exatamente um heading principal. Use `PageTitle` na intro ou no cabeçalho, `Surface level={1}` no body,
|
|
10
10
|
ou deixe um `PageState` ativo assumir esse papel. O cabeçalho pode ser omitido quando o próprio
|
|
11
11
|
conteúdo nomeia a página. Contadores e outros indicadores pertencem ao conteúdo que os explica.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
Use `surfaceLayout="sectioned"` (padrão) para separar as regiões por bordas ou
|
|
14
|
+
`surfaceLayout="continuous"` para uma superfície contínua. O `layout` decide a ocupação de altura:
|
|
15
|
+
`flow`, o padrão, mantém a rolagem vertical em `PageBody`; `fill` permite que uma região interna,
|
|
16
|
+
como uma tabela longa ou um split, assuma a rolagem. Header e footer ficam fora desse viewport.
|
|
17
|
+
|
|
18
|
+
Nos botões da barra superior, declare `size="sm"` para texto e `size="icon-sm"` para ícones.
|
|
19
|
+
`PageHeader` reserva uma linha mínima de `2rem`, mesmo sem ações; conteúdo maior pode expandi-la.
|
|
20
|
+
`PageBack` já usa `icon-sm`. A mesma escala vale para ações nos headers de Dialog, Drawer e Surface, inclusive criação de coleções.
|
|
19
21
|
|
|
20
22
|
`PageShell` não inclui sidebar nem inventa breadcrumb. Ele ocupa o painel principal já delimitado e
|
|
21
23
|
entrega ao `PageHeader` descendente a navegação e as ações conhecidas pela aplicação, sem criar uma
|
|
@@ -31,7 +33,11 @@ render(
|
|
|
31
33
|
<div className="w-full overflow-hidden rounded-lg border border-border">
|
|
32
34
|
<Page
|
|
33
35
|
title="Workspaces"
|
|
34
|
-
actions={
|
|
36
|
+
actions={
|
|
37
|
+
<Button variant="outline" size="sm">
|
|
38
|
+
Exportar
|
|
39
|
+
</Button>
|
|
40
|
+
}
|
|
35
41
|
>
|
|
36
42
|
<div className="rounded-lg border border-dashed border-border p-10 text-center text-sm text-muted-foreground">
|
|
37
43
|
O conteúdo da página (tabela, cards, grid…).
|
|
@@ -43,13 +49,16 @@ render(
|
|
|
43
49
|
|
|
44
50
|
## Orientar sobre a finalidade
|
|
45
51
|
|
|
46
|
-
Use `description` na forma curta ou `PageDescription` em `PageHeader` quando uma frase breve
|
|
52
|
+
Use `description` na forma curta ou `PageDescription` junto ao título em `PageIntro` ou `PageHeader` quando uma frase breve
|
|
47
53
|
ajudar a reconhecer a finalidade da página. Omita texto que só repita o título. Regras de negócio,
|
|
48
54
|
limites e consequências ficam nos campos, ações ou corpo em que são necessários. Pular o
|
|
49
55
|
subheading não deve comprometer a decisão nem a conclusão correta da tarefa.
|
|
50
56
|
|
|
51
57
|
```tsx
|
|
52
|
-
<Page
|
|
58
|
+
<Page
|
|
59
|
+
title="Conhecimento"
|
|
60
|
+
description="Consulte os materiais de apoio da sua área."
|
|
61
|
+
>
|
|
53
62
|
<KnowledgeList />
|
|
54
63
|
</Page>
|
|
55
64
|
```
|
|
@@ -65,9 +74,13 @@ da linha do título como ícone; o nome acessível e a dica nativa do navegador
|
|
|
65
74
|
<Page>
|
|
66
75
|
<PageHeader>
|
|
67
76
|
<PageBack href="/customers">Clientes</PageBack>
|
|
68
|
-
<PageTitle>Qualidade da base</PageTitle>
|
|
69
77
|
</PageHeader>
|
|
70
|
-
<PageBody>
|
|
78
|
+
<PageBody>
|
|
79
|
+
<PageIntro>
|
|
80
|
+
<PageTitle>Qualidade da base</PageTitle>
|
|
81
|
+
</PageIntro>
|
|
82
|
+
Conteúdo da análise.
|
|
83
|
+
</PageBody>
|
|
71
84
|
</Page>
|
|
72
85
|
```
|
|
73
86
|
|
|
@@ -89,9 +102,13 @@ ocupa a mesma posição introdutória sem transformar a trilha em ação.
|
|
|
89
102
|
</BreadcrumbList>
|
|
90
103
|
</Breadcrumb>
|
|
91
104
|
</PageNavigation>
|
|
92
|
-
<PageTitle>Carla Souza</PageTitle>
|
|
93
105
|
</PageHeader>
|
|
94
|
-
<PageBody>
|
|
106
|
+
<PageBody>
|
|
107
|
+
<PageIntro>
|
|
108
|
+
<PageTitle>Carla Souza</PageTitle>
|
|
109
|
+
</PageIntro>
|
|
110
|
+
Dados do cliente.
|
|
111
|
+
</PageBody>
|
|
95
112
|
</Page>
|
|
96
113
|
```
|
|
97
114
|
|
|
@@ -99,7 +116,7 @@ ocupa a mesma posição introdutória sem transformar a trilha em ação.
|
|
|
99
116
|
|
|
100
117
|
Use `PageShell` quando o shell conhece o breadcrumb e a rota conhece ações globais ou uma
|
|
101
118
|
introdução. Não crie um cabeçalho paralelo no layout nem esconda slots de `Page` com CSS. O Opus compõe a
|
|
102
|
-
navegação e as ações do shell no `PageHeader`
|
|
119
|
+
navegação e as ações do shell no `PageHeader` estável da página ativa.
|
|
103
120
|
|
|
104
121
|
```tsx preview col
|
|
105
122
|
<PageShell
|
|
@@ -113,7 +130,14 @@ navegação e as ações do shell no `PageHeader` rolável da página ativa.
|
|
|
113
130
|
</Breadcrumb>
|
|
114
131
|
}
|
|
115
132
|
>
|
|
116
|
-
<Page
|
|
133
|
+
<Page
|
|
134
|
+
title="Clientes"
|
|
135
|
+
actions={
|
|
136
|
+
<Button variant="outline" size="sm">
|
|
137
|
+
Exportar
|
|
138
|
+
</Button>
|
|
139
|
+
}
|
|
140
|
+
>
|
|
117
141
|
Conteúdo da listagem.
|
|
118
142
|
</Page>
|
|
119
143
|
</PageShell>
|
|
@@ -122,7 +146,7 @@ navegação e as ações do shell no `PageHeader` rolável da página ativa.
|
|
|
122
146
|
Durante `PageState`, o cabeçalho inteiro desaparece e o título do estado assume o heading principal.
|
|
123
147
|
Se uma ação não puder ser executada sem o conteúdo, a própria rota deve omiti-la naquele estado.
|
|
124
148
|
|
|
125
|
-
Na composição explícita,
|
|
149
|
+
Na composição explícita, separe navegação e ações da superfície do título no corpo:
|
|
126
150
|
|
|
127
151
|
```tsx preview col
|
|
128
152
|
<PageShell
|
|
@@ -138,12 +162,16 @@ Na composição explícita, combine navegação, título e ações no mesmo cabe
|
|
|
138
162
|
>
|
|
139
163
|
<Page>
|
|
140
164
|
<PageHeader>
|
|
141
|
-
<PageTitle>Clientes</PageTitle>
|
|
142
165
|
<PageActions>
|
|
143
|
-
<Button>Exportar</Button>
|
|
166
|
+
<Button size="sm">Exportar</Button>
|
|
144
167
|
</PageActions>
|
|
145
168
|
</PageHeader>
|
|
146
|
-
<PageBody>
|
|
169
|
+
<PageBody>
|
|
170
|
+
<PageIntro>
|
|
171
|
+
<PageTitle>Clientes</PageTitle>
|
|
172
|
+
</PageIntro>
|
|
173
|
+
Conteúdo da listagem.
|
|
174
|
+
</PageBody>
|
|
147
175
|
</Page>
|
|
148
176
|
</PageShell>
|
|
149
177
|
```
|
|
@@ -157,12 +185,17 @@ forma curta com `PageHeader` ou `PageBody`.
|
|
|
157
185
|
render(
|
|
158
186
|
<Page>
|
|
159
187
|
<PageHeader>
|
|
160
|
-
<PageTitle>Workspaces</PageTitle>
|
|
161
188
|
<PageActions>
|
|
162
|
-
<Button variant="outline">
|
|
189
|
+
<Button variant="outline" size="sm">
|
|
190
|
+
Exportar
|
|
191
|
+
</Button>
|
|
163
192
|
</PageActions>
|
|
164
193
|
</PageHeader>
|
|
165
194
|
<PageBody>
|
|
195
|
+
<PageIntro>
|
|
196
|
+
<PageTitle>Workspaces</PageTitle>
|
|
197
|
+
</PageIntro>
|
|
198
|
+
|
|
166
199
|
<div className="rounded-lg border border-dashed border-border p-10 text-center">
|
|
167
200
|
O conteúdo da página.
|
|
168
201
|
</div>
|
|
@@ -185,10 +218,12 @@ no fim da linha.
|
|
|
185
218
|
|
|
186
219
|
```tsx preview col
|
|
187
220
|
<Page>
|
|
188
|
-
<
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
|
|
221
|
+
<PageBody>
|
|
222
|
+
<PageIntro>
|
|
223
|
+
<PageTitle>Importar clientes</PageTitle>
|
|
224
|
+
</PageIntro>
|
|
225
|
+
Revise os 128 registros antes de confirmar.
|
|
226
|
+
</PageBody>
|
|
192
227
|
<PageFooter>
|
|
193
228
|
<ButtonGroup mode="spaced">
|
|
194
229
|
<Button variant="ghost">Cancelar</Button>
|
|
@@ -209,10 +244,11 @@ rolagem, como o `PageBody` do exemplo.
|
|
|
209
244
|
render(
|
|
210
245
|
<div className="h-72 overflow-hidden rounded-lg border border-border">
|
|
211
246
|
<Page layout="fill">
|
|
212
|
-
<PageHeader>
|
|
213
|
-
<PageTitle>Ordens de serviço</PageTitle>
|
|
214
|
-
</PageHeader>
|
|
215
247
|
<PageBody className="min-h-0 flex-1 overflow-y-auto">
|
|
248
|
+
<PageIntro>
|
|
249
|
+
<PageTitle>Ordens de serviço</PageTitle>
|
|
250
|
+
</PageIntro>
|
|
251
|
+
|
|
216
252
|
<div className="space-y-2">
|
|
217
253
|
{[
|
|
218
254
|
"OS-1842",
|
|
@@ -263,7 +299,11 @@ function Example() {
|
|
|
263
299
|
<PageActionsTarget target={target}>
|
|
264
300
|
<Page
|
|
265
301
|
title="Clientes"
|
|
266
|
-
actions={
|
|
302
|
+
actions={
|
|
303
|
+
<Button variant="outline" size="sm">
|
|
304
|
+
Exportar
|
|
305
|
+
</Button>
|
|
306
|
+
}
|
|
267
307
|
>
|
|
268
308
|
Conteúdo da listagem.
|
|
269
309
|
</Page>
|
|
@@ -322,31 +362,35 @@ estado na própria seção com [`DataState`](/ui/data-state), [`ActionView`](/ui
|
|
|
322
362
|
|
|
323
363
|
## Propriedades de Page
|
|
324
364
|
|
|
325
|
-
| Propriedade
|
|
326
|
-
|
|
|
327
|
-
| `description` | `ReactNode`
|
|
328
|
-
| `title`
|
|
329
|
-
| `actions`
|
|
330
|
-
| `layout`
|
|
331
|
-
| `className`
|
|
332
|
-
| `children`
|
|
333
|
-
| demais
|
|
365
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
366
|
+
| ------------- | -------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
367
|
+
| `description` | `ReactNode` | | Orientação opcional sob o título na forma curta. |
|
|
368
|
+
| `title` | `ReactNode` | | O h1 da página. |
|
|
369
|
+
| `actions` | `ReactNode` | | Ações contextuais no extremo oposto do cabeçalho. |
|
|
370
|
+
| `layout` | `'flow' \| 'fill'` | `'flow'` | Ocupação de altura, não anatomia: `fill` limita a página ao shell para que uma região interna role e leva essa altura até uma `ActionList` composta em `PageBody`. |
|
|
371
|
+
| `className` | `string` | | Classes do container externo. Para largura interna, use `contentClassName`. |
|
|
372
|
+
| `children` | `ReactNode` | | O body da página — espaçamento e diagramação são seus. |
|
|
373
|
+
| demais | Atributos de `HTMLElement` | | Atributos nativos repassados ao elemento `main`, como `id` ou `aria-label`. `className` vai para a moldura da página. |
|
|
334
374
|
|
|
335
375
|
## Propriedades de PageShell
|
|
336
376
|
|
|
337
|
-
| Propriedade | Tipo | Descrição
|
|
338
|
-
| ------------ | ----------------------------- |
|
|
339
|
-
| `navigation` | `ReactNode` | Navegação contextual composta
|
|
340
|
-
| `actions` | `ReactNode` | Ações conhecidas pelo shell, antes das ações declaradas pela `Page`.
|
|
341
|
-
| `children` | `ReactNode` | Rota que renderiza uma `Page` descendente.
|
|
342
|
-
| `className` | `string` | Classes adicionais da moldura.
|
|
343
|
-
| demais | Atributos de `HTMLDivElement` | Atributos nativos repassados à moldura.
|
|
377
|
+
| Propriedade | Tipo | Descrição |
|
|
378
|
+
| ------------ | ----------------------------- | ------------------------------------------------------------------------------------------ |
|
|
379
|
+
| `navigation` | `ReactNode` | Navegação contextual composta na linha de controles do header no `PageHeader` descendente. |
|
|
380
|
+
| `actions` | `ReactNode` | Ações conhecidas pelo shell, antes das ações declaradas pela `Page`. |
|
|
381
|
+
| `children` | `ReactNode` | Rota que renderiza uma `Page` descendente. |
|
|
382
|
+
| `className` | `string` | Classes adicionais da moldura. |
|
|
383
|
+
| demais | Atributos de `HTMLDivElement` | Atributos nativos repassados à moldura. |
|
|
384
|
+
|
|
385
|
+
`surfaceLayout?: 'sectioned' | 'continuous'` escolhe os divisores; o padrão é `sectioned`.
|
|
386
|
+
`contentClassName?: string` configura largura e alinhamento do conteúdo. O ritmo e os recuos
|
|
387
|
+
continuam sob responsabilidade do body; não use essa prop para substituir padding ou gap.
|
|
344
388
|
|
|
345
389
|
## Propriedades de PageHeader
|
|
346
390
|
|
|
347
391
|
| Propriedade | Tipo | Descrição |
|
|
348
392
|
| ----------- | ----------------------------- | ------------------------------------------------------------------------ |
|
|
349
|
-
| `className` | `string` | Classes adicionais do cabeçalho
|
|
393
|
+
| `className` | `string` | Classes adicionais do cabeçalho estável. |
|
|
350
394
|
| `children` | `ReactNode` | `PageBack` ou `PageNavigation`, um `PageTitle` opcional e `PageActions`. |
|
|
351
395
|
| demais | Atributos de `HTMLDivElement` | Atributos nativos repassados ao cabeçalho. |
|
|
352
396
|
|
|
@@ -370,11 +414,11 @@ estado na própria seção com [`DataState`](/ui/data-state), [`ActionView`](/ui
|
|
|
370
414
|
|
|
371
415
|
## Propriedades de PageTitle
|
|
372
416
|
|
|
373
|
-
| Propriedade | Tipo | Descrição
|
|
374
|
-
| ----------- | --------------------------------- |
|
|
375
|
-
| `children` | `ReactNode` | Título principal `h1`
|
|
376
|
-
| `className` | `string` | Classes adicionais do título.
|
|
377
|
-
| demais | Atributos de `HTMLHeadingElement` | Atributos nativos repassados ao heading.
|
|
417
|
+
| Propriedade | Tipo | Descrição |
|
|
418
|
+
| ----------- | --------------------------------- | ----------------------------------------------------- |
|
|
419
|
+
| `children` | `ReactNode` | Título principal `h1` na intro ou no header compacto. |
|
|
420
|
+
| `className` | `string` | Classes adicionais do título. |
|
|
421
|
+
| demais | Atributos de `HTMLHeadingElement` | Atributos nativos repassados ao heading. |
|
|
378
422
|
|
|
379
423
|
## Propriedades de PageActions
|
|
380
424
|
|
|
@@ -394,14 +438,15 @@ estado na própria seção com [`DataState`](/ui/data-state), [`ActionView`](/ui
|
|
|
394
438
|
|
|
395
439
|
## Propriedades de PageBody
|
|
396
440
|
|
|
397
|
-
`
|
|
398
|
-
`Page`. Uma grade, lista compacta ou linha horizontal fica dentro de um wrapper próprio.
|
|
441
|
+
`contentClassName?: string` configura largura e alinhamento do container interno.
|
|
399
442
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
|
403
|
-
|
|
|
404
|
-
|
|
|
443
|
+
`PageBody` separa seus blocos diretos por `1rem` e fornece os recuos internos. Uma grade, lista compacta ou linha horizontal fica dentro de um wrapper próprio.
|
|
444
|
+
|
|
445
|
+
| Propriedade | Tipo | Descrição |
|
|
446
|
+
| ----------- | ----------------------------- | --------------------------------------------------- |
|
|
447
|
+
| `children` | `ReactNode` | Blocos principais, separados pelo ritmo estrutural. |
|
|
448
|
+
| `className` | `string` | Classes do viewport: ocupação e overflow. |
|
|
449
|
+
| demais | Atributos de `HTMLDivElement` | Atributos nativos repassados ao container. |
|
|
405
450
|
|
|
406
451
|
Em uma `Page` com `layout="fill"`, o `PageBody` vira coluna flexível sozinho quando contém uma `ActionList` que
|
|
407
452
|
ocupa a altura.
|
|
@@ -431,8 +476,63 @@ ocupa a altura.
|
|
|
431
476
|
|
|
432
477
|
## Propriedades de PageDescription
|
|
433
478
|
|
|
434
|
-
| Propriedade | Tipo
|
|
435
|
-
|
|
|
436
|
-
| `children`
|
|
437
|
-
| `className` | `string`
|
|
438
|
-
| `…props`
|
|
479
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
480
|
+
| ----------- | -------------------------------------- | ------ | ------------------------------------------------------------------------- |
|
|
481
|
+
| `children` | `ReactNode` | | Orientação breve e opcional; filho direto da intro ou do header compacto. |
|
|
482
|
+
| `className` | `string` | | Ajusta a apresentação do parágrafo. |
|
|
483
|
+
| `…props` | `HTMLAttributes<HTMLParagraphElement>` | | Atributos nativos. |
|
|
484
|
+
|
|
485
|
+
## Composição compacta
|
|
486
|
+
|
|
487
|
+
Quando o cabeçalho também identifica o conteúdo, mantenha o título nele e omita a intro.
|
|
488
|
+
Não declare o mesmo título nas duas regiões.
|
|
489
|
+
|
|
490
|
+
```tsx preview col
|
|
491
|
+
<Page>
|
|
492
|
+
<PageHeader>
|
|
493
|
+
<PageTitle>Preferências</PageTitle>
|
|
494
|
+
</PageHeader>
|
|
495
|
+
<PageBody>Configurações da conta.</PageBody>
|
|
496
|
+
</Page>
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## Propriedades de PageIntro
|
|
500
|
+
|
|
501
|
+
Aceita atributos HTML de `div` e exige um `PageTitle`. Recebe opcionalmente `PageDescription` e
|
|
502
|
+
`PageIntroActions`. Deve abrir o `PageBody`; navegação e ações da superfície ficam no header.
|
|
503
|
+
|
|
504
|
+
| Propriedade | Tipo | Descrição |
|
|
505
|
+
| ----------- | ----------------------------- | ------------------------------------------------------------------------ |
|
|
506
|
+
| `children` | `ReactNode` | Um `PageTitle` e, opcionalmente, `PageDescription` e `PageIntroActions`. |
|
|
507
|
+
| `className` | `string` | Classes adicionais da introdução. |
|
|
508
|
+
| demais | Atributos de `HTMLDivElement` | Repassados à região introdutória. |
|
|
509
|
+
|
|
510
|
+
## Propriedades de PageIntroActions
|
|
511
|
+
|
|
512
|
+
Agrupa ações locais do conteúdo no extremo oposto ao título da intro.
|
|
513
|
+
|
|
514
|
+
| Propriedade | Tipo | Descrição |
|
|
515
|
+
| ----------- | ----------------------------- | ----------------------------------------------- |
|
|
516
|
+
| `children` | `ReactNode` | Controles relacionados ao conteúdo apresentado. |
|
|
517
|
+
| `className` | `string` | Classes adicionais do agrupamento. |
|
|
518
|
+
| demais | Atributos de `HTMLDivElement` | Repassados ao agrupamento. |
|
|
519
|
+
|
|
520
|
+
O header ocupa toda a largura disponível. O limite de 80rem e o recuo pertencem ao conteúdo
|
|
521
|
+
interno do Body; o viewport ocupa a largura da página para manter sua barra de rolagem na extremidade.
|
|
522
|
+
|
|
523
|
+
### Divisão visual das regiões
|
|
524
|
+
|
|
525
|
+
Use `surfaceLayout="sectioned"` (padrão) para separar header, body e footer com divisores.
|
|
526
|
+
Use `surfaceLayout="continuous"` para uma composição sem divisores. O header vazio continua
|
|
527
|
+
omitido. Essa escolha é independente de `layout="flow" | "fill"`, que controla a altura e a rolagem.
|
|
528
|
+
|
|
529
|
+
## Largura e rolagem
|
|
530
|
+
|
|
531
|
+
`Page` e `PageBody` aceitam `contentClassName` para o container interno de leitura.
|
|
532
|
+
Use `contentClassName="max-w-none"` para largura total ou `max-w-5xl` para uma leitura
|
|
533
|
+
mais estreita, mantendo o header e a barra de rolagem na extremidade. `className` em Page
|
|
534
|
+
controla a moldura externa; em PageBody, controla o viewport.
|
|
535
|
+
|
|
536
|
+
PageShell permite rolagem de contingência quando a rota renderiza um fallback ou um
|
|
537
|
+
wrapper sem altura definida. Para manter o header fixo, a rota deve preservar a cadeia
|
|
538
|
+
flexível de altura até Page. Uma Page diretamente no shell já segue esse contrato.
|
|
@@ -33,9 +33,8 @@ a largura cresce quando o número precisa de mais espaço.
|
|
|
33
33
|
## Números longos
|
|
34
34
|
|
|
35
35
|
A largura mínima é quadrada e cresce conforme o conteúdo. Na escala densa de um rodapé, as setas e a
|
|
36
|
-
elipse usam `size="icon-sm"`, o quadrado de `
|
|
37
|
-
números
|
|
38
|
-
`className`, como no exemplo.
|
|
36
|
+
elipse usam `size="icon-sm"`, o quadrado de `2rem` da escala única, com ícone de `0.875rem`. Os
|
|
37
|
+
números usam `size="sm"`, com a mesma altura e largura mínima, crescendo para acomodar os dígitos.
|
|
39
38
|
|
|
40
39
|
```tsx preview
|
|
41
40
|
<Pagination>
|
|
@@ -44,16 +43,16 @@ números não têm um degrau textual de `1.75rem` na escala e, por isso, recebem
|
|
|
44
43
|
<PaginationPrevious href="#" iconOnly size="icon-sm" />
|
|
45
44
|
</PaginationItem>
|
|
46
45
|
<PaginationItem>
|
|
47
|
-
<PaginationLink href="#" page={1} className="
|
|
46
|
+
<PaginationLink href="#" page={1} size="sm" className="text-xs" />
|
|
48
47
|
</PaginationItem>
|
|
49
48
|
<PaginationItem>
|
|
50
49
|
<PaginationEllipsis size="icon-sm" />
|
|
51
50
|
</PaginationItem>
|
|
52
51
|
<PaginationItem>
|
|
53
|
-
<PaginationLink href="#" page={5726} className="
|
|
52
|
+
<PaginationLink href="#" page={5726} size="sm" className="text-xs" />
|
|
54
53
|
</PaginationItem>
|
|
55
54
|
<PaginationItem>
|
|
56
|
-
<PaginationLink href="#" page={5727} isActive className="
|
|
55
|
+
<PaginationLink href="#" page={5727} isActive size="sm" className="text-xs" />
|
|
57
56
|
</PaginationItem>
|
|
58
57
|
<PaginationItem>
|
|
59
58
|
<PaginationNext href="#" iconOnly size="icon-sm" />
|
|
@@ -135,7 +134,7 @@ render(
|
|
|
135
134
|
| `page` | `number` | | Número usado como conteúdo, quando `children` não é informado, e no nome acessível “Página N”. |
|
|
136
135
|
| `isActive` | `boolean` | `false` | Marca a página atual com `aria-current="page"` e tratamento `outline`. |
|
|
137
136
|
| `href` | `string` | | Renderiza um `<a>`. Sem `href`, o componente usa `<button>` e aceita `onClick` e `disabled`. |
|
|
138
|
-
| `size` | `ButtonProps['size']` | `'default'` | A escala única, via `Button`. Os `icon-*` são quadrados
|
|
137
|
+
| `size` | `ButtonProps['size']` | `'default'` | A escala única, via `Button`. Os `icon-*` são quadrados; os tamanhos textuais preservam a mesma altura, com largura mínima quadrada que cresce para números longos. |
|
|
139
138
|
|
|
140
139
|
## Propriedades de PaginationPrevious e PaginationNext
|
|
141
140
|
|
|
@@ -143,7 +142,7 @@ render(
|
|
|
143
142
|
|---|---|---|---|
|
|
144
143
|
| `label` | `string` | `'Página anterior'` ou `'Próxima página'` | Nome acessível e texto visível da ação. |
|
|
145
144
|
| `iconOnly` | `boolean` | `false` | Exibe somente a seta; `label` continua disponível para leitura assistiva. |
|
|
146
|
-
| `size` | `ButtonProps['size']` | `'default'`; `'icon'` com `iconOnly` | A escala única; `icon-sm` para o rodapé denso.
|
|
145
|
+
| `size` | `ButtonProps['size']` | `'default'`; `'icon'` com `iconOnly` | A escala única; `icon-sm` para o rodapé denso. Com ou sem `iconOnly`, a altura acompanha o tamanho escolhido, como em `PaginationLink`. |
|
|
147
146
|
| `iconClassName` | `string` | | Classes aplicadas ao ícone da seta, quando o glifo da escala não servir. |
|
|
148
147
|
|
|
149
148
|
## Propriedades de PaginationEllipsis
|
|
@@ -89,14 +89,15 @@ fundo sustenta o modal, mas não é uma etapa para onde voltar. Formulários mod
|
|
|
89
89
|
rodapé pelo conteúdo, com `Cancelar` em `ghost` e a ação principal sólida.
|
|
90
90
|
|
|
91
91
|
Dentro de [`PageShell`](/ui/page), a superfície `page` usa a anatomia real da aplicação. O
|
|
92
|
-
`PageHeader` reúne
|
|
93
|
-
|
|
92
|
+
`PageHeader` reúne navegação e ações globais; o título abre o corpo na intro, e `PageFooter`
|
|
93
|
+
recebe ações de rodapé. Comandos textuais nos headers de todas as superfícies usam `sm`; retorno e assistente
|
|
94
|
+
usam `icon-sm`. O rodapé preserva `default`.
|
|
94
95
|
|
|
95
96
|
Uma Presentation de listagem exibida como página muda essa distribuição para manter a criação perto
|
|
96
97
|
da coleção. O `PageBody` recebe um [`Surface`](/ui/surface) com a
|
|
97
98
|
[`ActionList`](/ui/action-list), e o cabeçalho desse `Surface` recebe o título e os comandos
|
|
98
99
|
declarados com `placement: "header"`, no extremo oposto ao título. Os comandos de cabeçalho usam o
|
|
99
|
-
tamanho `
|
|
100
|
+
tamanho `sm` e permanecem textuais, mesmo quando a action declara um ícone: o que abre outra
|
|
100
101
|
Presentation é um `Button` sólido, e uma action `simple` vira um
|
|
101
102
|
[`ActionTrigger`](/ui/action-trigger) `ghost`. O rótulo vem do `label` da action, como `Criar workspace`.
|
|
102
103
|
|
|
@@ -59,11 +59,11 @@ const runtime = createRuntime({
|
|
|
59
59
|
queue,
|
|
60
60
|
})
|
|
61
61
|
|
|
62
|
-
export const
|
|
62
|
+
export const reportGenerate = bindAction(reportGenerateContract, {
|
|
63
63
|
background: { enabled: true, retry: { attempts: 3 }, progress: true },
|
|
64
64
|
handler: async (ctx, input, loaded, progress) => {
|
|
65
65
|
await progress?.report({ percent: 50, message: 'Consultando dados' })
|
|
66
|
-
return
|
|
66
|
+
return generateReport(input)
|
|
67
67
|
},
|
|
68
68
|
})
|
|
69
69
|
```
|
|
@@ -76,5 +76,5 @@ o erro de uso.
|
|
|
76
76
|
|
|
77
77
|
`navigate` espera um caminho da própria origem e não valida destinos. Quando o destino vem de fora,
|
|
78
78
|
como o `?redirect=` de um pós-login, valide-o antes de navegar: resolva com
|
|
79
|
-
`new URL(
|
|
79
|
+
`new URL(target, origin)` e compare o `origin` do resultado, em vez de conferir apenas o prefixo do
|
|
80
80
|
texto.
|
|
@@ -47,26 +47,26 @@ Declare o schedule com `defineSchedule` e registre-o no domínio, ao lado da act
|
|
|
47
47
|
|
|
48
48
|
```ts
|
|
49
49
|
import { createRuntime, defineDomain, defineSchedule } from '@softize/opus/core'
|
|
50
|
-
import {
|
|
50
|
+
import { ledgerClose } from './ledger-close.ts' // a action 'ledger.close', preparada abaixo
|
|
51
51
|
|
|
52
|
-
const
|
|
53
|
-
name: '
|
|
54
|
-
action: '
|
|
52
|
+
const closeDay = defineSchedule({
|
|
53
|
+
name: 'ledger.close-day',
|
|
54
|
+
action: 'ledger.close',
|
|
55
55
|
cron: '0 23 * * *',
|
|
56
56
|
timezone: 'America/Sao_Paulo',
|
|
57
57
|
})
|
|
58
58
|
|
|
59
|
-
export const
|
|
60
|
-
name: '
|
|
61
|
-
actions: {
|
|
62
|
-
schedules: [
|
|
59
|
+
export const ledgerDomain = defineDomain({
|
|
60
|
+
name: 'ledger',
|
|
61
|
+
actions: { ledgerClose },
|
|
62
|
+
schedules: [closeDay],
|
|
63
63
|
})
|
|
64
64
|
|
|
65
65
|
const runtime = createRuntime({
|
|
66
66
|
// …server/data…
|
|
67
67
|
scheduler,
|
|
68
68
|
})
|
|
69
|
-
runtime.register([
|
|
69
|
+
runtime.register([ledgerDomain])
|
|
70
70
|
await runtime.start()
|
|
71
71
|
```
|
|
72
72
|
|
|
@@ -91,18 +91,18 @@ recusa o sistema até que o contrato dela aceite essa execução com `automatabl
|
|
|
91
91
|
```ts
|
|
92
92
|
import { defineContract, isSystemActor } from '@softize/opus/core'
|
|
93
93
|
|
|
94
|
-
export const
|
|
95
|
-
name: '
|
|
94
|
+
export const ledgerCloseContract = defineContract({
|
|
95
|
+
name: 'ledger.close',
|
|
96
96
|
kind: 'simple',
|
|
97
97
|
input: z.object({}),
|
|
98
|
-
output: z.object({
|
|
98
|
+
output: z.object({ closedEntries: z.number() }),
|
|
99
99
|
automatable: true,
|
|
100
100
|
})
|
|
101
101
|
|
|
102
|
-
export const
|
|
102
|
+
export const ledgerClose = bindAction(ledgerCloseContract, {
|
|
103
103
|
// O sistema não tem permissões: aceite-o de forma explícita ao lado da regra das pessoas.
|
|
104
|
-
authorize: (ctx) => isSystemActor(ctx) || ctx.can('
|
|
105
|
-
handler: async (ctx) => ({
|
|
104
|
+
authorize: (ctx) => isSystemActor(ctx) || ctx.can('ledger:close'),
|
|
105
|
+
handler: async (ctx) => ({ closedEntries: await ledger.closeDay() }),
|
|
106
106
|
})
|
|
107
107
|
```
|
|
108
108
|
|
|
@@ -16,7 +16,7 @@ render(
|
|
|
16
16
|
title="Dispositivos conectados"
|
|
17
17
|
count={3}
|
|
18
18
|
description="Sessões com acesso à sua conta."
|
|
19
|
-
actions={<Button variant="outline">Encerrar outras sessões</Button>}
|
|
19
|
+
actions={<Button variant="outline" size="sm">Encerrar outras sessões</Button>}
|
|
20
20
|
>
|
|
21
21
|
<p className="text-sm text-muted-foreground">MacBook Pro · ativo agora</p>
|
|
22
22
|
</Surface>,
|
|
@@ -50,7 +50,10 @@ Três propriedades independentes definem a aparência:
|
|
|
50
50
|
- `layout` escolhe entre o ritmo contínuo e as faixas com divisores.
|
|
51
51
|
|
|
52
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é.
|
|
53
|
+
escreve classes de padding no header, no corpo ou no rodapé. O header delimitado usa
|
|
54
|
+
`1rem` em todos os lados e linha mínima de `2rem`, como Page, Dialog e Drawer.
|
|
55
|
+
Declare `sm` para botões textuais e `icon-sm` para ícones; Pane usa a mesma geometria
|
|
56
|
+
quando hospeda Surface. Seções sem moldura e legendas recortadas não ganham padding de barra.
|
|
54
57
|
|
|
55
58
|
```tsx preview col
|
|
56
59
|
render(
|
|
@@ -400,7 +403,7 @@ render(
|
|
|
400
403
|
as="section"
|
|
401
404
|
level={1}
|
|
402
405
|
title="Clientes"
|
|
403
|
-
actions={<Button>Criar cliente</Button>}
|
|
406
|
+
actions={<Button size="sm">Criar cliente</Button>}
|
|
404
407
|
>
|
|
405
408
|
<p className="text-sm text-muted-foreground">
|
|
406
409
|
A lista de clientes ocupa o corpo.
|
|
@@ -409,7 +412,7 @@ render(
|
|
|
409
412
|
);
|
|
410
413
|
```
|
|
411
414
|
|
|
412
|
-
A criação usa um `Button` textual na variante padrão e no tamanho `
|
|
415
|
+
A criação usa um `Button` textual na variante padrão e no tamanho `sm`, sem ícone, nomeado
|
|
413
416
|
`Criar {recurso}`; o diálogo aberto por ele repete esse título, e a edição usa `Editar {recurso}`.
|
|
414
417
|
|
|
415
418
|
Em uma [`Page`](/ui/page) com `layout="fill"`, a coleção ocupa a altura da página e rola só os
|
|
@@ -495,7 +498,7 @@ As demais propriedades são as nativas do elemento escolhido em `as`.
|
|
|
495
498
|
|
|
496
499
|
## Propriedades de SurfaceBody
|
|
497
500
|
|
|
498
|
-
Os filhos diretos recebem o ritmo estrutural de `
|
|
501
|
+
Os filhos diretos recebem o ritmo estrutural de `1rem`. O layout da `Surface` decide o inset;
|
|
499
502
|
um wrapper filho declara relações locais mais compactas.
|
|
500
503
|
|
|
501
504
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
@@ -38,25 +38,30 @@ mostrar somente o ícone, declare `aria-label` para preservar seu nome acessíve
|
|
|
38
38
|
</Tabs>
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
A barra e o painel ficam separados por `1rem`. O painel organiza seus blocos diretos com
|
|
42
|
+
o mesmo intervalo. Sem moldura, não há padding inferior: o hospedeiro fornece o recuo externo.
|
|
43
|
+
Não adicione margem ou padding para repetir esses intervalos.
|
|
44
|
+
|
|
41
45
|
## Emoldurar abas e painéis
|
|
42
46
|
|
|
43
47
|
Use `framed` quando a navegação e seus painéis precisarem formar um único bloco. A propriedade
|
|
44
48
|
reúne borda, raio e recorte no próprio `Tabs`, mas não aplica fundo: o bloco herda a
|
|
45
49
|
superfície em que foi composto. Por isso, não envolva `Tabs` em um `Surface` somente para obter essa
|
|
46
|
-
moldura.
|
|
50
|
+
moldura. O painel horizontal recebe `1rem` nas laterais e embaixo; o intervalo da barra
|
|
51
|
+
fornece o espaço superior. Na orientação vertical, a moldura recebe `1rem` ao redor da composição.
|
|
47
52
|
|
|
48
53
|
```tsx preview col
|
|
49
|
-
<Tabs defaultValue="overview" framed
|
|
54
|
+
<Tabs defaultValue="overview" framed>
|
|
50
55
|
<TabsList>
|
|
51
56
|
<TabsListItem value="overview">Visão geral</TabsListItem>
|
|
52
57
|
<TabsListItem value="history">Histórico</TabsListItem>
|
|
53
58
|
</TabsList>
|
|
54
|
-
<TabsPanel value="overview"
|
|
59
|
+
<TabsPanel value="overview">
|
|
55
60
|
<p className="text-sm text-muted-foreground">
|
|
56
61
|
Resumo do workspace Empresa X.
|
|
57
62
|
</p>
|
|
58
63
|
</TabsPanel>
|
|
59
|
-
<TabsPanel value="history"
|
|
64
|
+
<TabsPanel value="history">
|
|
60
65
|
<p className="text-sm text-muted-foreground">
|
|
61
66
|
Alterações recentes do workspace.
|
|
62
67
|
</p>
|
|
@@ -196,3 +201,6 @@ do gatilho.
|
|
|
196
201
|
| ------------ | -------- | ------ | ----------------------------------------------------------------------- |
|
|
197
202
|
| `value` | `string` | | Identificador do painel, igual ao `value` do `TabsListItem` que o abre. |
|
|
198
203
|
| `forceMount` | `true` | | Mantém o painel montado mesmo quando outra aba está ativa. |
|
|
204
|
+
|
|
205
|
+
Painéis com `forceMount` continuam montados quando inativos, mas ficam ocultos. Isso preserva
|
|
206
|
+
o estado de editores sem exibir simultaneamente o conteúdo de abas diferentes.
|