@softize/opus 12.11.0 → 13.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +76 -0
- package/PROMOTED.md +46 -0
- package/README.md +28 -19
- package/bin/cli.mjs +87 -216
- package/bin/lib/check.mjs +2 -7
- package/bin/lib/cli-shared.mjs +131 -0
- package/bin/lib/copy.mjs +1 -5
- package/bin/lib/db.mjs +16 -74
- package/bin/lib/gen-openapi.mjs +3 -3
- package/bin/lib/gen-runner.mjs +1 -1
- package/bin/lib/gen.mjs +14 -69
- package/bin/lib/mcp.mjs +3 -1
- package/bin/lib/seed.mjs +5 -62
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +93 -10
- package/docs/adr/0007-toast-actions-form-an-ordered-collection.md +63 -0
- package/docs/adr/0008-hierarchical-navigation-is-composed-at-the-consumer-boundary.md +71 -0
- package/docs/ownership-vs-shadcn-lock.md +2 -3
- package/docs/protocol.md +7 -7
- package/docs/radius-scale.md +1 -1
- package/docs/releasing.md +8 -2
- package/package.json +7 -3
- package/registry/skills/maintain-opus-docs/SKILL.md +83 -0
- package/registry/skills/maintain-opus-docs/agents/openai.yaml +4 -0
- package/registry/skills/maintain-opus-docs/references/editorial-standard.md +85 -0
- package/registry/skills/maintain-opus-docs/references/evaluations.md +34 -0
- package/registry/skills/maintain-opus-docs/scripts/audit-docs.mjs +81 -0
- package/registry/templates/app/package.json +1 -1
- package/registry/templates/app/src/main.tsx +4 -4
- package/src/audit/drivers/console.ts +1 -0
- package/src/auth/drivers/better-auth.ts +1 -0
- package/src/auth/drivers/jwt.ts +1 -0
- package/src/cache/drivers/memory.ts +1 -0
- package/src/client/drivers/fetch.ts +2 -1
- package/src/core/actions.ts +6 -1
- package/src/core/audit.ts +9 -3
- package/src/core/contracts.ts +7 -0
- package/src/core/domain.ts +1 -1
- package/src/core/errors.ts +18 -15
- package/src/core/index.ts +4 -2
- package/src/core/package-version.ts +26 -0
- package/src/core/reactions.ts +1 -1
- package/src/core/runtime.ts +33 -23
- package/src/core/schedules.ts +1 -1
- package/src/core/types.ts +2 -2
- package/src/dsl/eval.ts +2 -2
- package/src/dsl/kysely.ts +2 -2
- package/src/dsl/loads.ts +1 -1
- package/src/dsl/parser.ts +5 -5
- package/src/events/drivers/mitt.ts +1 -0
- package/src/mcp/index.ts +2 -1
- package/src/observability/drivers/opentelemetry.ts +1 -0
- package/src/queue/drivers/bullmq.ts +3 -3
- package/src/scheduler/drivers/node-cron.ts +3 -2
- package/src/scheduler/every.ts +7 -7
- package/src/schema/openapi.ts +3 -3
- package/src/seed/index.ts +29 -0
- package/src/server/drivers/fastify.ts +5 -2
- package/src/server/drivers/node.ts +9 -6
- package/src/server/index.ts +3 -1
- package/src/storage/drivers/fs.ts +1 -0
- package/src/testing/index.ts +3 -3
- package/src/ui/components/patterns/confirm.tsx +142 -42
- package/src/ui/components/patterns/content-header.tsx +7 -1
- package/src/ui/components/patterns/data-state.tsx +1 -1
- package/src/ui/components/patterns/dock.tsx +20 -3
- package/src/ui/components/patterns/form.tsx +12 -8
- package/src/ui/components/patterns/list.tsx +36 -41
- package/src/ui/components/patterns/page-state.tsx +2 -2
- package/src/ui/components/patterns/page.tsx +19 -1
- package/src/ui/components/patterns/shell-nav.tsx +10 -3
- package/src/ui/components/patterns/sidebar.tsx +43 -32
- package/src/ui/components/patterns/trigger.tsx +39 -38
- package/src/ui/components/patterns/view.tsx +26 -17
- package/src/ui/components/primitives/alert.tsx +14 -8
- package/src/ui/components/primitives/ask.tsx +3 -3
- package/src/ui/components/primitives/badge.tsx +11 -6
- package/src/ui/components/primitives/breadcrumb.tsx +2 -2
- package/src/ui/components/primitives/button.tsx +16 -3
- package/src/ui/components/primitives/calendar.tsx +28 -2
- package/src/ui/components/primitives/carousel.tsx +3 -3
- package/src/ui/components/primitives/chat.tsx +1 -1
- package/src/ui/components/primitives/checkbox.tsx +1 -1
- package/src/ui/components/primitives/command.tsx +2 -2
- package/src/ui/components/primitives/control.ts +12 -0
- package/src/ui/components/primitives/copyable.tsx +1 -1
- package/src/ui/components/primitives/dialog.tsx +202 -40
- package/src/ui/components/primitives/dot.tsx +5 -0
- package/src/ui/components/primitives/drawer.tsx +18 -8
- package/src/ui/components/primitives/empty.tsx +3 -3
- package/src/ui/components/primitives/field.tsx +3 -3
- package/src/ui/components/primitives/icon-picker.tsx +3 -1
- package/src/ui/components/primitives/input-group.tsx +1 -1
- package/src/ui/components/primitives/input-otp.tsx +1 -1
- package/src/ui/components/primitives/input.tsx +2 -2
- package/src/ui/components/primitives/item.tsx +3 -3
- package/src/ui/components/primitives/progress.tsx +32 -3
- package/src/ui/components/primitives/radio-group.tsx +1 -1
- package/src/ui/components/primitives/resizable.tsx +3 -1
- package/src/ui/components/primitives/select.tsx +5 -5
- package/src/ui/components/primitives/slider.tsx +5 -1
- package/src/ui/components/primitives/sonner.tsx +190 -8
- package/src/ui/components/primitives/switch.tsx +1 -0
- package/src/ui/components/primitives/tabs.tsx +1 -0
- package/src/ui/components/primitives/textarea.tsx +1 -1
- package/src/ui/components/primitives/toggle.tsx +1 -1
- package/src/ui/components/primitives/tooltip.tsx +1 -0
- package/src/ui/docs/DocBrowser.tsx +102 -23
- package/src/ui/docs/changelog.tsx +1 -1
- package/src/ui/docs/content/accordion.md +22 -16
- package/src/ui/docs/content/action-form-card.md +8 -8
- package/src/ui/docs/content/action-form-dialog.md +9 -9
- package/src/ui/docs/content/action-form.md +37 -36
- package/src/ui/docs/content/action-list-dialog.md +11 -6
- package/src/ui/docs/content/action-list.md +73 -39
- package/src/ui/docs/content/action-trigger.md +29 -15
- package/src/ui/docs/content/action-view.md +17 -10
- package/src/ui/docs/content/actions.md +9 -9
- package/src/ui/docs/content/ai.md +3 -3
- package/src/ui/docs/content/alert.md +14 -12
- package/src/ui/docs/content/ask.md +11 -0
- package/src/ui/docs/content/aspect-ratio.md +4 -4
- package/src/ui/docs/content/audit.md +2 -2
- package/src/ui/docs/content/auth.md +3 -3
- package/src/ui/docs/content/avatar.md +34 -14
- package/src/ui/docs/content/badge.md +3 -3
- package/src/ui/docs/content/breadcrumb.md +13 -8
- package/src/ui/docs/content/button.md +81 -6
- package/src/ui/docs/content/calendar.md +18 -5
- package/src/ui/docs/content/card.md +27 -1
- package/src/ui/docs/content/carousel.md +16 -11
- package/src/ui/docs/content/chat.md +23 -3
- package/src/ui/docs/content/checkbox.md +7 -7
- package/src/ui/docs/content/cli.md +74 -22
- package/src/ui/docs/content/collapsible.md +8 -8
- package/src/ui/docs/content/command.md +16 -8
- package/src/ui/docs/content/composer.md +17 -2
- package/src/ui/docs/content/content.md +17 -2
- package/src/ui/docs/content/copyable.md +12 -3
- package/src/ui/docs/content/customization.md +5 -5
- package/src/ui/docs/content/cycle.md +3 -3
- package/src/ui/docs/content/data-state.md +11 -12
- package/src/ui/docs/content/data.md +26 -33
- package/src/ui/docs/content/detail.md +22 -4
- package/src/ui/docs/content/dialog.md +339 -31
- package/src/ui/docs/content/dictionary-value.md +17 -10
- package/src/ui/docs/content/dock.md +11 -3
- package/src/ui/docs/content/dot.md +8 -0
- package/src/ui/docs/content/drawer.md +27 -14
- package/src/ui/docs/content/empty-value.md +2 -2
- package/src/ui/docs/content/empty.md +19 -12
- package/src/ui/docs/content/events.md +4 -4
- package/src/ui/docs/content/field.md +34 -12
- package/src/ui/docs/content/getting-started.md +3 -3
- package/src/ui/docs/content/icon-picker.md +19 -4
- package/src/ui/docs/content/input-otp.md +20 -12
- package/src/ui/docs/content/input.md +121 -9
- package/src/ui/docs/content/item.md +27 -13
- package/src/ui/docs/content/kbd.md +19 -11
- package/src/ui/docs/content/label.md +12 -3
- package/src/ui/docs/content/log.md +4 -4
- package/src/ui/docs/content/markdown.md +7 -6
- package/src/ui/docs/content/mcp.md +13 -15
- package/src/ui/docs/content/menu.md +40 -16
- package/src/ui/docs/content/metric-card.md +13 -0
- package/src/ui/docs/content/observability.md +2 -2
- package/src/ui/docs/content/page.md +59 -6
- package/src/ui/docs/content/pagination.md +22 -17
- package/src/ui/docs/content/popover.md +22 -8
- package/src/ui/docs/content/progress.md +15 -16
- package/src/ui/docs/content/queue.md +5 -5
- package/src/ui/docs/content/radio-group.md +20 -12
- package/src/ui/docs/content/router.md +11 -6
- package/src/ui/docs/content/scheduler.md +4 -5
- package/src/ui/docs/content/scroll-area.md +12 -7
- package/src/ui/docs/content/select.md +47 -34
- package/src/ui/docs/content/semantic-context.md +2 -2
- package/src/ui/docs/content/separator.md +5 -5
- package/src/ui/docs/content/sidebar.md +329 -54
- package/src/ui/docs/content/skeleton.md +9 -2
- package/src/ui/docs/content/slider.md +8 -7
- package/src/ui/docs/content/spinner.md +8 -8
- package/src/ui/docs/content/split.md +29 -5
- package/src/ui/docs/content/storage.md +6 -8
- package/src/ui/docs/content/switch.md +8 -7
- package/src/ui/docs/content/table.md +13 -3
- package/src/ui/docs/content/tabs.md +28 -14
- package/src/ui/docs/content/testing.md +9 -11
- package/src/ui/docs/content/textarea.md +12 -4
- package/src/ui/docs/content/toast.md +47 -13
- package/src/ui/docs/content/toggle.md +75 -7
- package/src/ui/docs/content/tokens.md +7 -7
- package/src/ui/docs/content/tooltip.md +19 -11
- package/src/ui/docs/content/truncate.md +15 -8
- package/src/ui/docs/content/ui.md +24 -9
- package/src/ui/docs/content/upgrading.md +7 -8
- package/src/ui/docs/doc-client.tsx +5 -5
- package/src/ui/docs/doc.tsx +26 -14
- package/src/ui/docs/registry.tsx +25 -42
- package/src/ui/docs/standalone.tsx +2 -2
- package/src/ui/drivers/react.tsx +17 -12
- package/src/ui/lib/action-errors.ts +45 -0
- package/src/ui/lib/zod-pt-br.ts +31 -4
- package/src/ui/meta.ts +65 -95
- package/src/ui/react.tsx +17 -16
- package/src/ui/theme.css +60 -8
- package/src/vite/design.ts +6 -18
- package/src/ui/components/primitives/alert-dialog.tsx +0 -192
- package/src/ui/docs/content/alert-dialog.md +0 -73
- package/src/ui/docs/content/button-group.md +0 -71
- package/src/ui/docs/content/confirm.md +0 -120
- package/src/ui/docs/content/input-group.md +0 -79
- package/src/ui/docs/content/page-state.md +0 -45
- package/src/ui/docs/content/toggle-group.md +0 -81
|
@@ -4,15 +4,15 @@ title: Camada de dados
|
|
|
4
4
|
|
|
5
5
|
# Camada de dados
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
As entidades descrevem o armazenamento que o manifest projeta, enquanto o Kysely mantém as
|
|
8
|
+
consultas tipadas. No código, nomes usam camelCase; no banco, snake_case. `CamelCasePlugin` faz essa
|
|
9
|
+
conversão nos dois sentidos.
|
|
10
10
|
|
|
11
11
|
## Entidade e manifest
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
`defineEntity` declara campos e tipos lógicos `t.*`. A `description` de cada entidade e action
|
|
14
|
+
registra o significado de negócio projetado no manifest por `opus gen`. Use `opus db check` para
|
|
15
|
+
detectar divergências entre a entidade e o banco.
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
18
|
import { defineEntity } from '@softize/opus/schema'
|
|
@@ -30,16 +30,12 @@ export const SkillEntity = defineEntity({
|
|
|
30
30
|
})
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
## Código camelCase, banco
|
|
33
|
+
## Código em camelCase, banco em snake_case
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
O schema tipado do Kysely e **toda** query (select/insert/update/where) usam camelCase
|
|
35
|
+
O schema tipado do Kysely e todas as consultas usam camelCase
|
|
38
36
|
(`workspaceId`, `ghRepo`). O banco é snake (a DDL no schema idempotente — `db/schema.sql`).
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
Única exceção: um sink escrito por fora do Kysely-com-plugin (ex.: o `audit_log` do Opus, pelo
|
|
42
|
-
pool) fica snake.
|
|
37
|
+
`CamelCasePlugin` converte nomes nas consultas e nos resultados. Um sink que não usa o Kysely com o
|
|
38
|
+
plugin, como `audit_log`, mantém os nomes do banco.
|
|
43
39
|
|
|
44
40
|
```ts
|
|
45
41
|
import { CamelCasePlugin, Kysely, PostgresDialect } from 'kysely'
|
|
@@ -52,31 +48,28 @@ const db = new Kysely<AdminDB>({
|
|
|
52
48
|
await db.selectFrom('agents').select(['isDefault', 'roleId']).where('workspaceId', '=', id).execute()
|
|
53
49
|
```
|
|
54
50
|
|
|
55
|
-
## Migrações
|
|
51
|
+
## Migrações, preparação e seeds
|
|
56
52
|
|
|
57
|
-
|
|
53
|
+
Cada etapa possui um papel diferente:
|
|
58
54
|
|
|
59
55
|
- `opus db migrate` — aplica o **schema idempotente** (`config.schema`, um script SQL
|
|
60
56
|
evolutivo: `IF NOT EXISTS` + guards cobrem nascer do zero e upgrade no mesmo artefato)
|
|
61
57
|
e roda o drift-check entidade ↔ banco na sequência (exit ≠ 0 se divergir).
|
|
62
58
|
- `prepare` — backfill estrutural, prod-safe e idempotente; roda no deploy (depois do migrate).
|
|
63
|
-
- `seed` —
|
|
64
|
-
|
|
65
|
-
## Campo `t.json()`: objeto entra, objeto sai
|
|
59
|
+
- `seed` — cria dados de desenvolvimento e teste. Não é executado em produção.
|
|
66
60
|
|
|
67
|
-
|
|
68
|
-
> no caminho do `kyselyRepo`.
|
|
61
|
+
## Campos JSON
|
|
69
62
|
|
|
70
|
-
|
|
63
|
+
`kyselyRepo` serializa campos `t.json()` na escrita (`insert` e `update`), guiado pela
|
|
71
64
|
declaração. Sem isso, o pg até stringifica objeto plano — mas **array vira literal de
|
|
72
|
-
array do PG** (errado
|
|
65
|
+
array do PG** (errado para jsonb) sem quebrar typecheck. String passa direto (quem já
|
|
73
66
|
mandava pré-serializado segue valendo); na leitura o pg devolve objeto. Query à mão
|
|
74
67
|
(fora do repo) continua responsável pelo próprio stringify.
|
|
75
68
|
|
|
76
|
-
## SQL
|
|
69
|
+
## SQL gerado por modelo
|
|
77
70
|
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
Uma action `ai: true` que executa SQL livre precisa de quatro proteções no banco. Validar o texto
|
|
72
|
+
com expressão regular não substitui nenhuma delas:
|
|
80
73
|
|
|
81
74
|
```ts
|
|
82
75
|
import { Pool } from 'pg'
|
|
@@ -86,14 +79,14 @@ const bi = readOnlyContextPool({ pool: new Pool({ connectionString, max: 4 }) })
|
|
|
86
79
|
const { rows } = await bi.query(sqlDoLlm, { role: roleFor(ctx) })
|
|
87
80
|
```
|
|
88
81
|
|
|
89
|
-
1. **Pool com
|
|
90
|
-
conexões do
|
|
82
|
+
1. **Pool com limite** (`max` e/ou `maxConcurrent`) — impede que as consultas consumam todas as
|
|
83
|
+
conexões do aplicativo.
|
|
91
84
|
2. **`BEGIN TRANSACTION READ ONLY`** — o servidor rejeita tentativas de escrita;
|
|
92
|
-
3. **`SET LOCAL ROLE` por transação** — o alcance é
|
|
85
|
+
3. **`SET LOCAL ROLE` por transação** — o alcance é definido pelo contexto (crie os roles com grants
|
|
93
86
|
default-fechado nas suas migrações: sem isso, `sales_read` leria `hr_employees`);
|
|
94
|
-
4. **Protocolo estendido** — o SQL
|
|
87
|
+
4. **Protocolo estendido** — o SQL sempre recebe os valores separadamente; múltiplas instruções
|
|
95
88
|
(`SELECT 1; DROP …`) é recusada pelo próprio protocolo.
|
|
96
89
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
Ao terminar, `DISCARD ALL` limpa a conexão. Se a limpeza falhar, a conexão é descartada em vez de
|
|
91
|
+
voltar ao pool. `statement_timeout`, aplicado por transação, limita consultas demoradas; o padrão é
|
|
92
|
+
15 segundos.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
DetailGroup
|
|
2
|
-
DetailField representa cada par e aceita conteúdo React
|
|
3
|
-
|
|
1
|
+
Use `DetailGroup` para organizar dados somente leitura como pares de rótulo e valor.
|
|
2
|
+
`DetailField` representa cada par e aceita conteúdo React em `label` e `value`. Para entrada e
|
|
3
|
+
validação, use `Field`.
|
|
4
4
|
|
|
5
5
|
```tsx preview col
|
|
6
6
|
<DetailGroup columns={2}>
|
|
@@ -9,7 +9,7 @@ para entrada e validação; use DetailField quando a pessoa apenas consulta um v
|
|
|
9
9
|
label="Estágio"
|
|
10
10
|
value={
|
|
11
11
|
<DictionaryValue
|
|
12
|
-
dict={{ keys: ['prospect', 'customer'], entries: { prospect: { label: '
|
|
12
|
+
dict={{ keys: ['prospect', 'customer'], entries: { prospect: { label: 'Prospecto' }, customer: { label: 'Cliente', context: 'success' } }, presentation: 'stage' }}
|
|
13
13
|
value="prospect"
|
|
14
14
|
/>
|
|
15
15
|
}
|
|
@@ -56,3 +56,21 @@ mais espaço para os rótulos, ajuste a variável no grupo, por exemplo com
|
|
|
56
56
|
/>
|
|
57
57
|
</DetailGroup>
|
|
58
58
|
```
|
|
59
|
+
|
|
60
|
+
## Propriedades de DetailGroup
|
|
61
|
+
|
|
62
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| `variant` | `'plain' \| 'framed'` | `'plain'` | `framed` aplica a superfície e a moldura canônicas ao conjunto. |
|
|
65
|
+
| `dividers` | `boolean` | `false` | Hairlines somente entre os campos, sem exigir moldura externa. |
|
|
66
|
+
| `columns` | `1 \| 2 \| 3 \| 4 \| 'auto'` | `1` | Colunas responsivas ou distribuição automática por largura mínima. |
|
|
67
|
+
| `orientation` | `'vertical' \| 'horizontal'` | `'vertical'` | Chave sobre o valor ou ao lado dele em cada campo. |
|
|
68
|
+
|
|
69
|
+
## Propriedades de DetailField
|
|
70
|
+
|
|
71
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
72
|
+
|---|---|---|---|
|
|
73
|
+
| `label` | `ReactNode` | | A chave do par. |
|
|
74
|
+
| `value` | `ReactNode` | | O valor; `null`, `undefined` e string vazia renderizam a ausência, `0` e `false` seguem como valores. |
|
|
75
|
+
| `icon` | `ReactNode` | | Ícone decorativo antes do par chave/valor. |
|
|
76
|
+
| `empty` | `ReactNode` | `“Não informado”` | O que a ausência significa neste campo: um rótulo ou um nó próprio. |
|
|
@@ -1,6 +1,22 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Escolher a forma do diálogo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use a família `Dialog` quando a pessoa precisar concluir uma tarefa ou responder sem perder o
|
|
4
|
+
contexto da página. A forma escolhida depende do tipo de interrupção:
|
|
5
|
+
|
|
6
|
+
| Necessidade | Escolha |
|
|
7
|
+
|---|---|
|
|
8
|
+
| Exibir uma tarefa ou conteúdo modal que pode ser fechado. | Componha `Dialog`. |
|
|
9
|
+
| Exigir uma resposta e impedir o fechamento pelo clique externo. | Componha `Dialog mode="alert"`. |
|
|
10
|
+
| Pedir reconhecimento, confirmação binária ou uma string sem montar a superfície. | Use `dialog.alert`, `dialog.confirm` ou `dialog.prompt`. |
|
|
11
|
+
| Escolher entre vários resultados simples. | Use `dialog.choose`. |
|
|
12
|
+
|
|
13
|
+
Para conteúdo ancorado e não modal, use `Popover`. Para uma lista de ações, use `Menu`.
|
|
14
|
+
|
|
15
|
+
## Compor uma tarefa modal
|
|
16
|
+
|
|
17
|
+
`DialogContent` delimita a superfície. `DialogHeader`, `DialogBody` e `DialogFooter` organizam
|
|
18
|
+
título, conteúdo rolável e ações com o espaçamento da família. A superfície usa o fundo base,
|
|
19
|
+
borda semântica, raio `xl` e elevação para permanecer distinta da página.
|
|
4
20
|
|
|
5
21
|
```tsx preview
|
|
6
22
|
<Dialog>
|
|
@@ -13,48 +29,340 @@ Content é superfície pura; o espaço mora nos slots (body p-5; header/footer p
|
|
|
13
29
|
<DialogDescription>O workspace agrupa os repositórios e agentes do cliente.</DialogDescription>
|
|
14
30
|
</DialogHeader>
|
|
15
31
|
<DialogBody className="space-y-2">
|
|
16
|
-
<Label htmlFor="
|
|
17
|
-
<Input id="
|
|
32
|
+
<Label htmlFor="workspace-name">Nome</Label>
|
|
33
|
+
<Input id="workspace-name" placeholder="Ex.: Empresa X" />
|
|
18
34
|
</DialogBody>
|
|
19
35
|
<DialogFooter>
|
|
20
36
|
<DialogClose asChild>
|
|
21
|
-
<Button variant="ghost"
|
|
37
|
+
<Button variant="ghost">Cancelar</Button>
|
|
22
38
|
</DialogClose>
|
|
23
|
-
<Button
|
|
39
|
+
<Button>Criar workspace</Button>
|
|
24
40
|
</DialogFooter>
|
|
25
41
|
</DialogContent>
|
|
26
42
|
</Dialog>
|
|
27
43
|
```
|
|
28
44
|
|
|
29
|
-
|
|
45
|
+
O corpo cresce até o limite da janela e passa a rolar; cabeçalho e rodapé permanecem visíveis.
|
|
46
|
+
`DialogClose` encerra o modal sem exigir estado controlado. Use `open` e `onOpenChange` quando outra
|
|
47
|
+
parte da interface também precisar controlar a abertura.
|
|
48
|
+
|
|
49
|
+
## Pedir uma resposta pela API imperativa
|
|
30
50
|
|
|
31
|
-
|
|
51
|
+
Use `dialog.alert`, `dialog.confirm`, `dialog.prompt` e `dialog.choose` quando a resposta couber em
|
|
52
|
+
um contrato pronto. As chamadas são assíncronas e resolvem somente depois da resposta.
|
|
32
53
|
|
|
33
54
|
```tsx preview
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
function Demo() {
|
|
56
|
+
const [result, setResult] = useState('Nenhuma resposta.')
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
60
|
+
<DialogHost />
|
|
61
|
+
<Button variant="outline" size="sm" onClick={async () => {
|
|
62
|
+
await dialog.alert({
|
|
63
|
+
title: 'Sessão expirada',
|
|
64
|
+
description: 'Entre novamente para continuar.',
|
|
65
|
+
})
|
|
66
|
+
setResult('Aviso reconhecido.')
|
|
67
|
+
}}>
|
|
68
|
+
Abrir aviso
|
|
69
|
+
</Button>
|
|
70
|
+
<Button variant="outline" size="sm" onClick={async () => {
|
|
71
|
+
const accepted = await dialog.confirm({
|
|
72
|
+
title: 'Excluir sessão?',
|
|
73
|
+
description: 'O worktree e o preview desta sessão serão removidos.',
|
|
74
|
+
action: 'Excluir',
|
|
75
|
+
context: 'danger',
|
|
76
|
+
})
|
|
77
|
+
setResult(accepted ? 'Sessão excluída.' : 'Sessão mantida.')
|
|
78
|
+
}}>
|
|
79
|
+
Confirmar exclusão
|
|
80
|
+
</Button>
|
|
81
|
+
<Button variant="outline" size="sm" onClick={async () => {
|
|
82
|
+
const name = await dialog.prompt({
|
|
83
|
+
title: 'Renomear snapshot',
|
|
84
|
+
action: 'Renomear',
|
|
85
|
+
placeholder: 'Ex.: versão final',
|
|
86
|
+
})
|
|
87
|
+
setResult(name === null ? 'Nome mantido.' : `Novo nome: ${name}`)
|
|
88
|
+
}}>
|
|
89
|
+
Renomear snapshot
|
|
90
|
+
</Button>
|
|
91
|
+
<Button variant="outline" size="sm" onClick={async () => {
|
|
92
|
+
const choice = await dialog.choose({
|
|
93
|
+
title: 'Fechar editor?',
|
|
94
|
+
description: 'O contrato possui alterações que ainda não foram salvas.',
|
|
95
|
+
actions: [
|
|
96
|
+
{ result: 'continue', label: 'Continuar editando', initialFocus: true },
|
|
97
|
+
{ result: 'discard', label: 'Descartar', variant: 'outline' },
|
|
98
|
+
{ result: 'save', label: 'Salvar e fechar' },
|
|
99
|
+
],
|
|
100
|
+
})
|
|
101
|
+
setResult(choice === null ? 'Editor mantido.' : `Resultado: ${choice}`)
|
|
102
|
+
}}>
|
|
103
|
+
Fechar editor
|
|
104
|
+
</Button>
|
|
105
|
+
<span className="text-sm text-muted-foreground">{result}</span>
|
|
106
|
+
</div>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
render(<Demo />)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`dialog.alert` devolve `Promise<void>`, `dialog.confirm` devolve `Promise<boolean>`,
|
|
114
|
+
`dialog.prompt` devolve `Promise<string | null>` e `dialog.choose` infere a união dos resultados
|
|
115
|
+
declarados, acrescida de `null`. Fechar ou cancelar resolve a resposta negativa de cada operação,
|
|
116
|
+
em vez de deixar a promise pendente. No aviso com uma ação, o botão ocupa toda a largura do rodapé;
|
|
117
|
+
confirmações e prompts distribuem confirmar e cancelar em duas colunas.
|
|
118
|
+
|
|
119
|
+
Em `dialog.choose`, declare pelo menos uma ação e marque exatamente uma ação habilitada com
|
|
120
|
+
`initialFocus`. Essa ação representa a saída segura. Os valores de `result` precisam ser únicos.
|
|
121
|
+
Por padrão, a última ação recebe contexto primário e variante sólida; as anteriores usam contexto
|
|
122
|
+
neutro e variante ghost. Defina `context` ou `variant` na própria ação para substituir o padrão.
|
|
123
|
+
|
|
124
|
+
### Montar o host
|
|
125
|
+
|
|
126
|
+
Monte um `DialogHost` no shell do aplicativo, ao lado do `Toaster`:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
<>
|
|
130
|
+
{routes}
|
|
131
|
+
<Toaster />
|
|
132
|
+
<DialogHost />
|
|
133
|
+
</>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Sem o host, a chamada lança um erro imediatamente. Solicitações concorrentes entram em uma fila e
|
|
137
|
+
aparecem uma por vez, preservando a resposta de cada `await`.
|
|
138
|
+
|
|
139
|
+
### Adicionar conteúdo próprio
|
|
140
|
+
|
|
141
|
+
Use `body` quando a descrição não for suficiente, como em uma lista de recursos afetados:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
const accepted = await dialog.confirm({
|
|
145
|
+
title: 'Encerrar três sessões?',
|
|
146
|
+
description: 'Os agentes vinculados serão interrompidos.',
|
|
147
|
+
body: <AffectedSessions />,
|
|
148
|
+
action: 'Encerrar sessões',
|
|
149
|
+
})
|
|
53
150
|
```
|
|
54
151
|
|
|
55
|
-
|
|
152
|
+
Use `ActionTrigger` quando a confirmação já pertencer ao contrato de uma action. Use
|
|
153
|
+
`ActionFormDialog` para formulários com validação ou vários campos. `dialog.prompt` atende somente
|
|
154
|
+
uma string livre. Componha `Dialog` quando uma ação precisar manter a superfície
|
|
155
|
+
aberta para exibir carregamento, validação ou falha antes do fechamento.
|
|
156
|
+
|
|
157
|
+
## Compor uma resposta personalizada
|
|
158
|
+
|
|
159
|
+
`Dialog mode="alert"` deriva `role="alertdialog"`, mantém o foco dentro da superfície e não fecha
|
|
160
|
+
com o clique externo. Prefira a API imperativa quando a resposta couber em `alert`, `confirm`,
|
|
161
|
+
`prompt` ou `choose`. Componha o componente quando a resposta depender de estado próprio ou de um
|
|
162
|
+
layout específico.
|
|
163
|
+
|
|
164
|
+
```tsx preview
|
|
165
|
+
function ResponseDialogDemo() {
|
|
166
|
+
const [result, setResult] = useState('Nenhuma resposta.')
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<div className="space-y-3">
|
|
170
|
+
<Dialog mode="alert" onResult={setResult}>
|
|
171
|
+
<DialogTrigger asChild>
|
|
172
|
+
<Button variant="outline">Fechar editor</Button>
|
|
173
|
+
</DialogTrigger>
|
|
174
|
+
<DialogContent>
|
|
175
|
+
<DialogHeader>
|
|
176
|
+
<DialogTitle>Fechar editor?</DialogTitle>
|
|
177
|
+
<DialogDescription>
|
|
178
|
+
O contrato possui alterações que ainda não foram salvas.
|
|
179
|
+
</DialogDescription>
|
|
180
|
+
</DialogHeader>
|
|
181
|
+
<DialogFooter className="grid-cols-3">
|
|
182
|
+
<DialogClose result="continue" initialFocus asChild>
|
|
183
|
+
<Button variant="ghost">Continuar editando</Button>
|
|
184
|
+
</DialogClose>
|
|
185
|
+
<DialogClose result="discard" asChild>
|
|
186
|
+
<Button variant="outline">Descartar</Button>
|
|
187
|
+
</DialogClose>
|
|
188
|
+
<DialogClose result="save" asChild>
|
|
189
|
+
<Button>Salvar e fechar</Button>
|
|
190
|
+
</DialogClose>
|
|
191
|
+
</DialogFooter>
|
|
192
|
+
</DialogContent>
|
|
193
|
+
</Dialog>
|
|
194
|
+
<p className="text-sm text-muted-foreground">{result}</p>
|
|
195
|
+
</div>
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
render(<ResponseDialogDemo />)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`DialogClose` fecha a superfície sem escolher a aparência. Use `asChild` e componha um `Button`;
|
|
203
|
+
contexto, variante e tamanho pertencem ao botão. `result` identifica a resposta entregue a
|
|
204
|
+
`Dialog onResult`. Marque com `initialFocus` a saída segura — normalmente `Cancelar`; em um aviso
|
|
205
|
+
com uma única ação, marque essa ação. Para uma confirmação destrutiva, use `context="danger"` no
|
|
206
|
+
botão. O texto informa o efeito real, como `Excluir`, `Revogar acesso` ou `Encerrar sessões`.
|
|
207
|
+
|
|
208
|
+
## Acessibilidade
|
|
209
|
+
|
|
210
|
+
- `DialogTitle` e `DialogDescription` nomeiam e descrevem a superfície para tecnologias assistivas.
|
|
211
|
+
- O modo padrão permite fechamento externo; `mode="alert"` exige uma resposta explícita.
|
|
212
|
+
- O comportamento selecionado por `mode` determina o papel de acessibilidade; `role` não configura
|
|
213
|
+
o comportamento.
|
|
214
|
+
- O foco permanece dentro do diálogo aberto e retorna ao gatilho após o fechamento.
|
|
215
|
+
- `DialogHost` apresenta uma solicitação por vez para que cada resposta tenha um alvo inequívoco.
|
|
216
|
+
|
|
217
|
+
## Propriedades de Dialog
|
|
218
|
+
|
|
219
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
220
|
+
|---|---|---|---|
|
|
221
|
+
| `open` | `boolean` | | Controla a abertura. |
|
|
222
|
+
| `defaultOpen` | `boolean` | `false` | Define a abertura inicial no modo não controlado. |
|
|
223
|
+
| `onOpenChange` | `(open: boolean) => void` | | Recebe mudanças de abertura. |
|
|
224
|
+
| `mode` | `'default' \| 'alert'` | `'default'` | Exige resposta explícita em `alert`; o componente deriva o papel de acessibilidade. |
|
|
225
|
+
| `onResult` | `(result: string) => void` | | Recebe o resultado do `DialogClose` acionado. |
|
|
226
|
+
| `modal` | `boolean` | `true` | No modo padrão, mantém foco e interação restritos à superfície aberta. |
|
|
227
|
+
|
|
228
|
+
## Propriedades de DialogTrigger
|
|
229
|
+
|
|
230
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
231
|
+
|---|---|---|---|
|
|
232
|
+
| `asChild` | `boolean` | `false` | Usa o filho como gatilho sem criar outro elemento. |
|
|
233
|
+
| `children` | `ReactNode` | | Controle que abre o diálogo. |
|
|
234
|
+
|
|
235
|
+
## Propriedades de DialogContent
|
|
236
|
+
|
|
237
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
238
|
+
|---|---|---|---|
|
|
239
|
+
| `showCloseButton` | `boolean` | `true` | No modo padrão, exibe o botão de fechamento no canto; o modo de alerta exige uma resposta identificável. |
|
|
240
|
+
| `className` | `string` | | Ajusta a superfície. |
|
|
241
|
+
| `children` | `ReactNode` | | Cabeçalho, corpo, rodapé ou conteúdo próprio. |
|
|
242
|
+
|
|
243
|
+
## Propriedades de DialogHeader
|
|
244
|
+
|
|
245
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
246
|
+
|---|---|---|---|
|
|
247
|
+
| `className` | `string` | | Ajusta a faixa superior. |
|
|
248
|
+
| `children` | `ReactNode` | | Título, descrição e mídia opcional. |
|
|
249
|
+
|
|
250
|
+
## Propriedades de DialogBody
|
|
251
|
+
|
|
252
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
253
|
+
|---|---|---|---|
|
|
254
|
+
| `className` | `string` | | Ajusta o corpo rolável do modo padrão ou o conteúdo próprio do alerta. |
|
|
255
|
+
| `children` | `ReactNode` | | Conteúdo principal da tarefa. |
|
|
256
|
+
|
|
257
|
+
## Propriedades de DialogFooter
|
|
258
|
+
|
|
259
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
260
|
+
|---|---|---|---|
|
|
261
|
+
| `className` | `string` | | Ajusta a faixa do modo padrão ou a grade de respostas do alerta. |
|
|
262
|
+
| `children` | `ReactNode` | | Ações secundárias e principal. |
|
|
263
|
+
|
|
264
|
+
## Propriedades de DialogTitle
|
|
265
|
+
|
|
266
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
267
|
+
|---|---|---|---|
|
|
268
|
+
| `className` | `string` | | Ajusta o título. |
|
|
269
|
+
| `children` | `ReactNode` | | Nome da tarefa ou do conteúdo modal. |
|
|
270
|
+
|
|
271
|
+
## Propriedades de DialogDescription
|
|
272
|
+
|
|
273
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
274
|
+
|---|---|---|---|
|
|
275
|
+
| `className` | `string` | | Ajusta a descrição. |
|
|
276
|
+
| `children` | `ReactNode` | | Contexto, efeito ou consequência relevante. |
|
|
277
|
+
|
|
278
|
+
## Propriedades de DialogMedia
|
|
279
|
+
|
|
280
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
281
|
+
|---|---|---|---|
|
|
282
|
+
| `className` | `string` | | Ajusta a moldura quadrada da mídia. |
|
|
283
|
+
| `children` | `ReactNode` | | Ícone que reforça o contexto da resposta. |
|
|
284
|
+
|
|
285
|
+
## Propriedades de DialogClose
|
|
56
286
|
|
|
57
|
-
|
|
|
287
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
58
288
|
|---|---|---|---|
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
289
|
+
| `initialFocus` | `boolean` | `false` | No modo de alerta, marca o controle como destino inicial seguro do foco. |
|
|
290
|
+
| `result` | `string` | | Entrega esta resposta a `Dialog onResult` antes de fechar. |
|
|
291
|
+
| `asChild` | `boolean` | `false` | Aplica o fechamento ao filho; use com `Button`. |
|
|
292
|
+
| `children` | `ReactNode` | | Controle que fecha o diálogo. |
|
|
293
|
+
|
|
294
|
+
## Propriedades de DialogOverlay
|
|
295
|
+
|
|
296
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
297
|
+
|---|---|---|---|
|
|
298
|
+
| `className` | `string` | | Ajusta a camada que cobre a página. |
|
|
299
|
+
|
|
300
|
+
## Propriedades de DialogPortal
|
|
301
|
+
|
|
302
|
+
`DialogContent` já monta portal e overlay. Use `DialogPortal` diretamente somente em uma composição
|
|
303
|
+
avançada; ele recebe `children` e `container` conforme o portal do Radix.
|
|
304
|
+
|
|
305
|
+
## Propriedades de DialogHost
|
|
306
|
+
|
|
307
|
+
`DialogHost` não recebe propriedades. Monte uma instância no shell para atender toda a API
|
|
308
|
+
imperativa. `ConfirmHost` permanece como alias de migração.
|
|
309
|
+
|
|
310
|
+
## Opções de dialog.alert
|
|
311
|
+
|
|
312
|
+
| Opção | Tipo | Padrão | Descrição |
|
|
313
|
+
|---|---|---|---|
|
|
314
|
+
| `title` | `ReactNode` | | Nomeia o aviso que exige reconhecimento. |
|
|
315
|
+
| `description` | `ReactNode` | | Explica efeito, alcance ou próximo passo. |
|
|
316
|
+
| `body` | `ReactNode` | | Acrescenta conteúdo próprio abaixo da descrição. |
|
|
317
|
+
| `media` | `ReactNode` | | Exibe um ícone na moldura de `DialogMedia`. |
|
|
318
|
+
| `action` | `string` | `'OK'` | Define o rótulo de reconhecimento. |
|
|
319
|
+
|
|
320
|
+
## Opções de dialog.confirm
|
|
321
|
+
|
|
322
|
+
| Opção | Tipo | Padrão | Descrição |
|
|
323
|
+
|---|---|---|---|
|
|
324
|
+
| `title` | `ReactNode` | | Nomeia a decisão. |
|
|
325
|
+
| `description` | `ReactNode` | | Explica a consequência necessária para decidir. |
|
|
326
|
+
| `body` | `ReactNode` | | Acrescenta conteúdo próprio abaixo da descrição. |
|
|
327
|
+
| `media` | `ReactNode` | | Exibe um ícone na moldura de `DialogMedia`. |
|
|
328
|
+
| `action` | `string` | `'Confirmar'` | Define o rótulo do resultado afirmativo. |
|
|
329
|
+
| `cancel` | `string` | `'Cancelar'` | Define o rótulo da resposta negativa. |
|
|
330
|
+
| `context` | `'primary' \| 'danger'` | `'primary'` | Define o contexto da ação afirmativa. |
|
|
331
|
+
|
|
332
|
+
## Opções de dialog.prompt
|
|
333
|
+
|
|
334
|
+
| Opção | Tipo | Padrão | Descrição |
|
|
335
|
+
|---|---|---|---|
|
|
336
|
+
| `title` | `ReactNode` | | Nomeia a informação solicitada. |
|
|
337
|
+
| `description` | `ReactNode` | | Explica como a informação será usada. |
|
|
338
|
+
| `body` | `ReactNode` | | Acrescenta conteúdo próprio abaixo da descrição. |
|
|
339
|
+
| `media` | `ReactNode` | | Exibe um ícone na moldura de `DialogMedia`. |
|
|
340
|
+
| `action` | `string` | `'Confirmar'` | Define o rótulo do resultado afirmativo. |
|
|
341
|
+
| `cancel` | `string` | `'Cancelar'` | Define o rótulo da resposta negativa. |
|
|
342
|
+
| `context` | `'primary' \| 'danger'` | `'primary'` | Define o contexto da ação afirmativa. |
|
|
343
|
+
| `placeholder` | `string` | | Orienta o formato esperado quando um exemplo ajuda. |
|
|
344
|
+
| `defaultValue` | `string` | | Define e seleciona o valor inicial do campo. |
|
|
345
|
+
|
|
346
|
+
## Opções de dialog.choose
|
|
347
|
+
|
|
348
|
+
| Opção | Tipo | Padrão | Descrição |
|
|
349
|
+
|---|---|---|---|
|
|
350
|
+
| `title` | `ReactNode` | | Nomeia a escolha. |
|
|
351
|
+
| `description` | `ReactNode` | | Explica o contexto necessário para escolher. |
|
|
352
|
+
| `body` | `ReactNode` | | Acrescenta conteúdo próprio antes das ações. |
|
|
353
|
+
| `media` | `ReactNode` | | Exibe um ícone na moldura de `DialogMedia`. |
|
|
354
|
+
| `actions` | `readonly DialogChoice[]` | | Declara os resultados disponíveis. |
|
|
355
|
+
|
|
356
|
+
## Propriedades de DialogChoice
|
|
357
|
+
|
|
358
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
359
|
+
|---|---|---|---|
|
|
360
|
+
| `result` | `string` | | Valor único devolvido quando a ação for escolhida. |
|
|
361
|
+
| `label` | `ReactNode` | | Rótulo que descreve o resultado da ação. |
|
|
362
|
+
| `initialFocus` | `boolean` | `false` | Marca a única saída segura que recebe o foco inicial. |
|
|
363
|
+
| `context` | `ButtonContext` | Última ação: `primary`; demais: `neutral` | Define o significado semântico. |
|
|
364
|
+
| `variant` | `ButtonVariant` | Última ação: `solid`; demais: `ghost` | Define o tratamento visual. |
|
|
365
|
+
| `disabled` | `boolean` | `false` | Impede a escolha desta ação. |
|
|
366
|
+
|
|
367
|
+
`confirm()` e `ConfirmHost` continuam disponíveis apenas como aliases de migração para
|
|
368
|
+
`dialog.confirm()` e `DialogHost`.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
ícone nunca comunicam sozinhos.
|
|
1
|
+
Use `DictionaryValue` para apresentar um valor de `t.dict` de acordo com o papel declarado pelo
|
|
2
|
+
próprio dicionário. A tela fornece o dicionário e o código; o componente escolhe texto, badge e ícone
|
|
3
|
+
sem perder o rótulo visível.
|
|
5
4
|
|
|
6
5
|
```tsx preview
|
|
7
6
|
<DictionaryValue
|
|
@@ -9,7 +8,7 @@ aplica os defaults da apresentação declarada. O rótulo está sempre presente
|
|
|
9
8
|
value="pj"
|
|
10
9
|
/>
|
|
11
10
|
<DictionaryValue
|
|
12
|
-
dict={{ keys: ['prospect', 'customer'], entries: { prospect: { label: '
|
|
11
|
+
dict={{ keys: ['prospect', 'customer'], entries: { prospect: { label: 'Prospecto' }, customer: { label: 'Cliente', context: 'success' } }, presentation: 'stage' }}
|
|
13
12
|
value="customer"
|
|
14
13
|
/>
|
|
15
14
|
<DictionaryValue
|
|
@@ -38,7 +37,7 @@ export const customerKindDict = t.dict(
|
|
|
38
37
|
|
|
39
38
|
export const customerStageDict = t.dict(
|
|
40
39
|
{
|
|
41
|
-
prospect: { label: '
|
|
40
|
+
prospect: { label: 'Prospecto', description: 'Relacionamento ainda em prospecção.' },
|
|
42
41
|
customer: { label: 'Cliente', context: 'success' },
|
|
43
42
|
},
|
|
44
43
|
{ doc: 'Estágio comercial atual da parte.', presentation: 'stage' },
|
|
@@ -73,9 +72,10 @@ export const customerStageDict = t.dict(
|
|
|
73
72
|
/>
|
|
74
73
|
```
|
|
75
74
|
|
|
76
|
-
##
|
|
75
|
+
## Substituição explícita
|
|
77
76
|
|
|
78
|
-
Os
|
|
77
|
+
Os padrões vêm do dicionário. Use as propriedades de apresentação somente quando esta ocorrência
|
|
78
|
+
precisar de um tratamento diferente e a decisão estiver explícita na tela.
|
|
79
79
|
|
|
80
80
|
```tsx preview
|
|
81
81
|
<DictionaryValue
|
|
@@ -106,9 +106,16 @@ dicionário registrado no provider: `{ key: 'source', label: 'Fonte', dictionary
|
|
|
106
106
|
Dimensões independentes (tipo e estágio, por exemplo) ficam em colunas distintas; não empilhar
|
|
107
107
|
uma sob a outra como texto secundário.
|
|
108
108
|
|
|
109
|
-
##
|
|
109
|
+
## useDicts
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
`useDicts()` devolve os dicionários registrados em `OpusProvider` (`dicts`), chaveados pela `ref`.
|
|
112
|
+
É o que `ActionList`, `ActionForm` e `DictionaryValue` consultam para resolver
|
|
113
|
+
`options: { kind: 'dictionary', ref }` e colunas nomeadas por `dictionary`; fora do provider, o
|
|
114
|
+
resultado é vazio e a resolução cai na meta do `t.dict` que viaja no schema.
|
|
115
|
+
|
|
116
|
+
## Propriedades de DictionaryValue
|
|
117
|
+
|
|
118
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
112
119
|
|---|---|---|---|
|
|
113
120
|
| `dict` | `DictType \| LogicalTypeMeta \| DictionaryDescriptor` | — | O dicionário (`t.dict`), a meta lida do schema ou um descritor normalizado. |
|
|
114
121
|
| `value` | `string \| null \| undefined` | — | O código. Vazio renderiza `fallback`. |
|
|
@@ -32,11 +32,11 @@ esquerda.
|
|
|
32
32
|
|
|
33
33
|
A divisória entre grupos pertence ao componente: o consumidor declara `DockGroup` e a linha
|
|
34
34
|
aparece entre grupos consecutivos, nunca antes do primeiro. Agrupe por intenção — modo, criação,
|
|
35
|
-
IA, publicação — em vez de espalhar ícones
|
|
35
|
+
IA, publicação — em vez de espalhar ícones em uma fileira única.
|
|
36
36
|
|
|
37
37
|
## Estado da superfície
|
|
38
38
|
|
|
39
|
-
Estado não é ferramenta, e por isso não mora na barra: `SurfaceStatus` flutua
|
|
39
|
+
Estado não é ferramenta, e por isso não mora na barra: `SurfaceStatus` flutua em um canto da mesma
|
|
40
40
|
superfície — `top-right` por padrão — e recebe salvamento, versão publicada, execução percorrida.
|
|
41
41
|
A região é `role="status"` com `aria-live="polite"`, então a mudança é anunciada sem roubar o foco.
|
|
42
42
|
Separar os dois preserva a barra como toolbar navegável e dá ao estado um lugar estável, que não
|
|
@@ -63,7 +63,15 @@ A barra é uma `toolbar`: as setas andam entre as ações e Home/End vão às po
|
|
|
63
63
|
por tooltip, então `<TooltipProvider>` precisa existir na raiz do app — o esqueleto do `opus create`
|
|
64
64
|
já monta.
|
|
65
65
|
|
|
66
|
-
|
|
|
66
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
67
67
|
| --- | --- | --- | --- |
|
|
68
68
|
| `position` | `'bottom' \| 'bottom-left' \| 'bottom-right'` | `'bottom'` | Aresta do contêiner onde a barra se ancora. |
|
|
69
69
|
| `label` | `string` | | Nome acessível da barra. |
|
|
70
|
+
|
|
71
|
+
## Propriedades de SurfaceStatus
|
|
72
|
+
|
|
73
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
74
|
+
| --- | --- | --- | --- |
|
|
75
|
+
| `position` | `'top-right' \| 'top-left'` | `'top-right'` | Canto da superfície onde o estado se ancora. |
|
|
76
|
+
| `context` | `'neutral' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | O que o estado comunica; tinge borda e texto pela família semântica. |
|
|
77
|
+
| `actions` | `ReactNode` | | Ações do recurso aberto, fora da região viva. |
|
|
@@ -15,3 +15,11 @@ acessível. Sem `label`, o ponto é decorativo.
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Use `Badge` quando o estado precisar permanecer legível sem depender do contexto ao redor.
|
|
18
|
+
|
|
19
|
+
## Propriedades de Dot
|
|
20
|
+
|
|
21
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
22
|
+
|---|---|---|---|
|
|
23
|
+
| `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | O significado da cor. |
|
|
24
|
+
| `variant` | `'solid' \| 'outline'` | `'solid'` | Ponto preenchido ou só contornado. |
|
|
25
|
+
| `label` | `string` | | Nome acessível quando a cor comunica estado; sem ele o ponto é decorativo (`aria-hidden`). |
|