@softize/opus 17.2.0 → 18.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 +55 -1
- package/bin/lib/check.mjs +212 -45
- package/docs/adr/0011-page-shell-coordinates-persistent-page-chrome.md +4 -0
- package/docs/adr/0013-presentation-is-a-portable-action-oriented-artifact.md +14 -2
- package/docs/adr/0016-list-collection-header-belongs-to-content.md +80 -0
- package/package.json +1 -1
- package/registry/skills/build-opus-ui/SKILL.md +30 -20
- package/registry/skills/build-opus-ui/references/evaluations.md +9 -3
- package/registry/skills/build-opus-ui/references/ui-patterns.md +29 -17
- package/src/core/presentation.ts +223 -24
- package/src/core/runtime.ts +3 -0
- package/src/core/types.ts +2 -0
- package/src/mcp/index.ts +1 -0
- package/src/ui/components/patterns/action-form-card.tsx +8 -1
- package/src/ui/components/patterns/confirm.tsx +194 -157
- package/src/ui/components/patterns/content-header.tsx +17 -2
- package/src/ui/components/patterns/form-dialog.tsx +28 -14
- package/src/ui/components/patterns/form.tsx +340 -222
- package/src/ui/components/patterns/list.tsx +43 -44
- package/src/ui/components/patterns/page-heading-context.tsx +34 -0
- package/src/ui/components/patterns/page-state.tsx +2 -0
- package/src/ui/components/patterns/page.tsx +164 -50
- package/src/ui/components/patterns/presentation.tsx +140 -84
- package/src/ui/components/patterns/surface-header.tsx +5 -6
- package/src/ui/components/patterns/trigger.tsx +112 -82
- package/src/ui/components/primitives/button.tsx +2 -2
- package/src/ui/components/primitives/chat.tsx +19 -5
- package/src/ui/components/primitives/control.ts +9 -3
- package/src/ui/components/primitives/dialog.tsx +16 -9
- package/src/ui/components/primitives/drawer.tsx +9 -6
- package/src/ui/docs/content/action-form-card.md +9 -8
- package/src/ui/docs/content/action-form-dialog.md +11 -12
- package/src/ui/docs/content/action-form.md +25 -25
- package/src/ui/docs/content/action-list.md +101 -70
- package/src/ui/docs/content/chat.md +4 -4
- package/src/ui/docs/content/content.md +29 -13
- package/src/ui/docs/content/dialog.md +27 -21
- package/src/ui/docs/content/drawer.md +8 -6
- package/src/ui/docs/content/page.md +43 -50
- package/src/ui/docs/content/presentation.md +39 -28
- package/src/ui/docs/doc-client.tsx +1 -1
- package/src/ui/meta.ts +4 -4
|
@@ -41,7 +41,7 @@ invalidação continuam sob responsabilidade de `ActionForm`.
|
|
|
41
41
|
<DocBrowserActionProvider>
|
|
42
42
|
<ActionForm
|
|
43
43
|
action={docWorkspaceCreate}
|
|
44
|
-
defaultValues={{ name:
|
|
44
|
+
defaultValues={{ name: "Empresa X", status: "active" }}
|
|
45
45
|
submitLabel="Atualizar"
|
|
46
46
|
onCancel={() => undefined}
|
|
47
47
|
/>
|
|
@@ -70,23 +70,23 @@ ciclo de vida que o pattern não cobre; validação e execução continuam iguai
|
|
|
70
70
|
|
|
71
71
|
## Propriedades de ActionForm
|
|
72
72
|
|
|
73
|
-
| Propriedade
|
|
74
|
-
|
|
75
|
-
| `action`
|
|
76
|
-
| `defaultValues`
|
|
77
|
-
| `onSuccess`
|
|
78
|
-
| `submitLabel / cancelLabel / onCancel` | `string / string / () => void` | `'Salvar' / 'Cancelar'` | Rodapé do form — o Cancelar só aparece com onCancel. |
|
|
79
|
-
| `fieldOptions`
|
|
80
|
-
| `className / body / footer`
|
|
81
|
-
| `children`
|
|
73
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
74
|
+
| ------------------------------------------------------ | ----------------------------------------------------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
75
|
+
| `action` | `FormContract<TInput, TData>` | | O contrato da FormAction — dele saem campos (input Zod + fields), mensagens e invalidação de cache. |
|
|
76
|
+
| `defaultValues` | `Partial<TInput>` | | Valores iniciais — o modo edição de um update/patch. |
|
|
77
|
+
| `onSuccess` | `(data: TData) => void` | | Pós-sucesso (o toast e a invalidação de cache já aconteceram). |
|
|
78
|
+
| `submitLabel / cancelLabel / cancelVariant / onCancel` | `string / string / 'ghost' \| 'outline' / () => void` | `'Salvar' / 'Cancelar' / 'ghost'` | Rodapé do form — o Cancelar só aparece com onCancel. Use `outline` quando ele dividir o footer com a ação principal. |
|
|
79
|
+
| `fieldOptions` | `Record<string, SelectOption[]>` | | Opções de runtime por campo (ex.: ids de skills) — sobrepõe as inferidas do z.enum. |
|
|
80
|
+
| `className / body / footer` | `string / (node) => node / (node) => node` | | className = classes do `<form>`. body/footer = slots: recebem os campos / os botões e escolhem o invólucro — é como o ActionFormDialog injeta DialogBody/DialogFooter (scroll + faixa). |
|
|
81
|
+
| `children` | `ReactNode` | | Modo composição: diagrame com `<ActionFormField name />`. Sem children, o automático monta todos os campos na ordem do contrato. |
|
|
82
82
|
|
|
83
83
|
## Propriedades de ActionFormField
|
|
84
84
|
|
|
85
|
-
| Propriedade | Tipo
|
|
86
|
-
|
|
87
|
-
| `name`
|
|
88
|
-
| `options`
|
|
89
|
-
| `className` | `string`
|
|
85
|
+
| Propriedade | Tipo | Descrição |
|
|
86
|
+
| ----------- | ---------------- | ------------------------------------------------------------------------------ |
|
|
87
|
+
| `name` | `string` | O campo do contrato (chave em `fields`/schema). Fora do schema, não renderiza. |
|
|
88
|
+
| `options` | `SelectOption[]` | Opções por id de runtime — sobrepõe o fieldOptions do form e o z.enum. |
|
|
89
|
+
| `className` | `string` | Classes do invólucro (ex.: `col-span-2` em uma grid). |
|
|
90
90
|
|
|
91
91
|
## Ajuda na label
|
|
92
92
|
|
|
@@ -110,9 +110,9 @@ lugar:
|
|
|
110
110
|
|
|
111
111
|
### Propriedades de LabelHelp
|
|
112
112
|
|
|
113
|
-
| Propriedade | Tipo
|
|
114
|
-
|
|
115
|
-
| `help`
|
|
113
|
+
| Propriedade | Tipo | Descrição |
|
|
114
|
+
| ----------- | -------- | ------------------------------------------------------------ |
|
|
115
|
+
| `help` | `string` | O texto do tooltip. Vazio ou ausente, o ícone não renderiza. |
|
|
116
116
|
|
|
117
117
|
## Origem das opções de seleção
|
|
118
118
|
|
|
@@ -137,25 +137,25 @@ Quando nenhum widget atender ao campo, use `useActionFormContext` no modo de com
|
|
|
137
137
|
próprio participa da mesma validação e execução sem abandonar o `ActionForm`:
|
|
138
138
|
|
|
139
139
|
```tsx
|
|
140
|
-
import { useActionFormContext } from
|
|
140
|
+
import { useActionFormContext } from "@softize/opus/ui/react";
|
|
141
141
|
|
|
142
142
|
function PermissionGrid() {
|
|
143
|
-
const { form } = useActionFormContext()
|
|
144
|
-
const perms = (form.watch(
|
|
143
|
+
const { form } = useActionFormContext();
|
|
144
|
+
const perms = (form.watch("permissions") as string[] | undefined) ?? [];
|
|
145
145
|
const toggle = (p: string) =>
|
|
146
146
|
form.setValue(
|
|
147
|
-
|
|
147
|
+
"permissions",
|
|
148
148
|
perms.includes(p) ? perms.filter((x) => x !== p) : [...perms, p],
|
|
149
149
|
{ shouldValidate: true, shouldDirty: true },
|
|
150
|
-
)
|
|
151
|
-
const error = form.formState.errors[
|
|
150
|
+
);
|
|
151
|
+
const error = form.formState.errors["permissions"];
|
|
152
152
|
// …sua grade; zod-resolver, erro inline e submit continuam do contrato.
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
<ActionForm action={roleUpdate}>
|
|
156
156
|
<ActionFormField name="name" />
|
|
157
157
|
<PermissionGrid />
|
|
158
|
-
</ActionForm
|
|
158
|
+
</ActionForm>;
|
|
159
159
|
```
|
|
160
160
|
|
|
161
161
|
O contexto também expõe `shape` (Zod por campo), `fields` (FieldSpec) e `fieldOptions`.
|
|
@@ -7,34 +7,43 @@ os filtros aplicados permanecem visíveis como chips removíveis.
|
|
|
7
7
|
|
|
8
8
|
A barra se adapta ao espaço disponível. A busca cede largura primeiro e filtros que deixam de caber
|
|
9
9
|
migram para o modal; a linha só quebra quando nenhum controle restante puder ceder espaço.
|
|
10
|
-
`toolbarActions` acrescenta
|
|
11
|
-
`ButtonGroup` espaçado; as ações do consumidor vêm depois, com um intervalo
|
|
12
|
-
hierarquia entre
|
|
10
|
+
`toolbarActions` acrescenta operações ligadas ao recorte atual no fim da barra. Recarregar e
|
|
11
|
+
exibição formam um `ButtonGroup` espaçado; as ações do consumidor vêm depois, com um intervalo
|
|
12
|
+
maior para preservar a hierarquia entre controles e operações do recorte.
|
|
13
13
|
|
|
14
14
|
```tsx preview col
|
|
15
15
|
render(
|
|
16
16
|
<DocBrowserActionProvider>
|
|
17
17
|
<div className="w-full">
|
|
18
|
-
<ActionList
|
|
18
|
+
<ActionList
|
|
19
|
+
action={docWorkspaceList}
|
|
20
|
+
input={{}}
|
|
21
|
+
emptyMessage="Nenhum workspace."
|
|
22
|
+
/>
|
|
19
23
|
</div>
|
|
20
24
|
</DocBrowserActionProvider>,
|
|
21
|
-
)
|
|
25
|
+
);
|
|
22
26
|
```
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
## Coleção nomeada
|
|
29
|
+
|
|
30
|
+
Em uma página, envolva a listagem com `Content`. O título e a criação ocupam extremos opostos do
|
|
31
|
+
cabeçalho da coleção; busca, filtros e atualização permanecem na toolbar do `ActionList`.
|
|
25
32
|
|
|
26
33
|
```tsx
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
/>
|
|
34
|
+
<Content
|
|
35
|
+
title="Workspaces"
|
|
36
|
+
level={1}
|
|
37
|
+
variant="page"
|
|
38
|
+
actions={<Button>Criar workspace</Button>}
|
|
39
|
+
>
|
|
40
|
+
<ActionList action={workspaceList} input={{}} />
|
|
41
|
+
</Content>
|
|
36
42
|
```
|
|
37
43
|
|
|
44
|
+
Use `toolbarActions` somente quando a operação depender do recorte visível, como exportar os
|
|
45
|
+
resultados filtrados. Uma ação icon-only nessa região mantém o nome no tooltip e no `aria-label`.
|
|
46
|
+
|
|
38
47
|
## Barra de filtros compartilhada
|
|
39
48
|
|
|
40
49
|
`ActionFilterBar` expõe a mesma linguagem declarativa de busca, filtros e período para
|
|
@@ -89,7 +98,11 @@ render(
|
|
|
89
98
|
input={{}}
|
|
90
99
|
cells={{
|
|
91
100
|
name: (w) => (
|
|
92
|
-
<a
|
|
101
|
+
<a
|
|
102
|
+
href="#"
|
|
103
|
+
onClick={(e) => e.preventDefault()}
|
|
104
|
+
className="font-medium underline-offset-4 hover:underline"
|
|
105
|
+
>
|
|
93
106
|
{w.name}
|
|
94
107
|
</a>
|
|
95
108
|
),
|
|
@@ -97,7 +110,7 @@ render(
|
|
|
97
110
|
/>
|
|
98
111
|
</div>
|
|
99
112
|
</DocBrowserActionProvider>,
|
|
100
|
-
)
|
|
113
|
+
);
|
|
101
114
|
```
|
|
102
115
|
|
|
103
116
|
## Período obrigatório
|
|
@@ -109,10 +122,10 @@ como `?period=2026-07-01..2026-07-07`, e chega ao handler pelos parâmetros `fro
|
|
|
109
122
|
|
|
110
123
|
```tsx
|
|
111
124
|
periods: [
|
|
112
|
-
{ value:
|
|
113
|
-
{ value:
|
|
114
|
-
{ value:
|
|
115
|
-
]
|
|
125
|
+
{ value: "today", label: "Hoje" },
|
|
126
|
+
{ value: "last7", label: "Últimos 7 dias" },
|
|
127
|
+
{ value: "thisMonth", label: "Este mês" },
|
|
128
|
+
];
|
|
116
129
|
// Presets computáveis: today · yesterday · last7 · last30 · thisMonth · lastMonth.
|
|
117
130
|
// presetRange('last7') → { from: 'YYYY-MM-DD', to: 'YYYY-MM-DD' } — o mesmo cálculo do pattern.
|
|
118
131
|
```
|
|
@@ -133,13 +146,19 @@ apenas essa seleção. Com `confirm`, o componente pede confirmação antes de e
|
|
|
133
146
|
seleção é limpa e a consulta é refeita.
|
|
134
147
|
|
|
135
148
|
```tsx
|
|
136
|
-
<ActionList
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
149
|
+
<ActionList
|
|
150
|
+
action={runList}
|
|
151
|
+
input={{}}
|
|
152
|
+
batch={[
|
|
153
|
+
{
|
|
154
|
+
label: "Reprocessar",
|
|
155
|
+
can: (run) => run.status === "failed",
|
|
156
|
+
confirm: { title: "Reprocessar as execuções?" },
|
|
157
|
+
run: async (runs) => {
|
|
158
|
+
await Promise.all(runs.map((r) => api.retry(r.id)));
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
]}
|
|
143
162
|
/>
|
|
144
163
|
```
|
|
145
164
|
|
|
@@ -159,17 +178,25 @@ render(
|
|
|
159
178
|
input={{}}
|
|
160
179
|
views={{
|
|
161
180
|
gallery: {
|
|
162
|
-
label:
|
|
181
|
+
label: "Galeria",
|
|
163
182
|
icon: <LayoutGrid />,
|
|
164
183
|
render: (workspaces) => (
|
|
165
184
|
<div className="grid grid-cols-2 gap-3">
|
|
166
185
|
{workspaces.map((w) => (
|
|
167
|
-
<div
|
|
186
|
+
<div
|
|
187
|
+
key={w.id}
|
|
188
|
+
className="rounded-lg border border-border p-3"
|
|
189
|
+
>
|
|
168
190
|
<div className="flex items-center gap-2">
|
|
169
191
|
<span className="text-sm font-semibold">{w.name}</span>
|
|
170
|
-
<DictionaryValue
|
|
192
|
+
<DictionaryValue
|
|
193
|
+
dict={docWorkspaceStatus}
|
|
194
|
+
value={w.status}
|
|
195
|
+
/>
|
|
171
196
|
</div>
|
|
172
|
-
<p className="mt-1 text-xs text-muted-foreground">
|
|
197
|
+
<p className="mt-1 text-xs text-muted-foreground">
|
|
198
|
+
{w.agents} agentes.
|
|
199
|
+
</p>
|
|
173
200
|
</div>
|
|
174
201
|
))}
|
|
175
202
|
</div>
|
|
@@ -179,7 +206,7 @@ render(
|
|
|
179
206
|
/>
|
|
180
207
|
</div>
|
|
181
208
|
</DocBrowserActionProvider>,
|
|
182
|
-
)
|
|
209
|
+
);
|
|
183
210
|
```
|
|
184
211
|
|
|
185
212
|
## Filtros dependentes e opções remotas
|
|
@@ -217,7 +244,9 @@ podem ser serializados na query string pelos helpers do componente:
|
|
|
217
244
|
action={contract}
|
|
218
245
|
input={{}}
|
|
219
246
|
state={listParamsToState(params, contract)}
|
|
220
|
-
onStateChange={(next) =>
|
|
247
|
+
onStateChange={(next) =>
|
|
248
|
+
navigate(`/rota${listStateToParams(next, contract)}`)
|
|
249
|
+
}
|
|
221
250
|
/>
|
|
222
251
|
```
|
|
223
252
|
|
|
@@ -241,7 +270,9 @@ render(
|
|
|
241
270
|
<span className="text-sm font-semibold">{w.name}</span>
|
|
242
271
|
<DictionaryValue dict={docWorkspaceStatus} value={w.status} />
|
|
243
272
|
</div>
|
|
244
|
-
<p className="mt-1 text-xs text-muted-foreground">
|
|
273
|
+
<p className="mt-1 text-xs text-muted-foreground">
|
|
274
|
+
{w.agents} agentes.
|
|
275
|
+
</p>
|
|
245
276
|
</div>
|
|
246
277
|
))}
|
|
247
278
|
</div>
|
|
@@ -249,31 +280,31 @@ render(
|
|
|
249
280
|
</ActionList>
|
|
250
281
|
</div>
|
|
251
282
|
</DocBrowserActionProvider>,
|
|
252
|
-
)
|
|
283
|
+
);
|
|
253
284
|
```
|
|
254
285
|
|
|
255
286
|
## Propriedades de ActionFilterBar
|
|
256
287
|
|
|
257
|
-
| Propriedade
|
|
258
|
-
|
|
259
|
-
| `action`
|
|
260
|
-
| `state`
|
|
261
|
-
| `onStateChange` | `(next) => void`
|
|
262
|
-
| `filterOptions` | `Record<string, SelectOption[]>`
|
|
263
|
-
| `onRefresh`
|
|
264
|
-
| `refreshing`
|
|
265
|
-
| `controls`
|
|
266
|
-
| `actions`
|
|
288
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
289
|
+
| --------------- | ---------------------------------------------------- | ------- | ---------------------------------------------------------------------------------- |
|
|
290
|
+
| `action` | `Pick<ListAction, 'filters' \| 'text' \| 'periods'>` | | Declara busca, filtros e períodos disponíveis. |
|
|
291
|
+
| `state` | `ActionFilterState` | | Estado atual da barra. |
|
|
292
|
+
| `onStateChange` | `(next) => void` | | Recebe o estado completo depois de cada alteração. |
|
|
293
|
+
| `filterOptions` | `Record<string, SelectOption[]>` | | Fornece opções de runtime para filtros select e lookup. |
|
|
294
|
+
| `onRefresh` | `() => Promise<void> \| void` | | Exibe a ação de recarregar e executa a consulta do consumidor. |
|
|
295
|
+
| `refreshing` | `boolean` | `false` | Desabilita e anima a ação de recarregar durante a consulta. |
|
|
296
|
+
| `controls` | `ReactNode` | | Controles auxiliares agrupados com recarregar em um `ButtonGroup` espaçado. |
|
|
297
|
+
| `actions` | `ReactNode` | | Ações do consumidor exibidas depois do grupo de controles, com um intervalo maior. |
|
|
267
298
|
|
|
268
299
|
## Declaração na ListAction
|
|
269
300
|
|
|
270
|
-
| Chave
|
|
271
|
-
|
|
272
|
-
| `columns` | `{ key, label, type?, sortable?, fit?, hidden?, dateFormat?, dictionary?, empty? }` — a tabela. `hidden` fica fora (base do futuro column picker); `dictionary` nomeia o dicionário do provider; `empty` dá o significado da ausência.
|
|
301
|
+
| Chave | O que declara |
|
|
302
|
+
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
303
|
+
| `columns` | `{ key, label, type?, sortable?, fit?, hidden?, dateFormat?, dictionary?, empty? }` — a tabela. `hidden` fica fora (base do futuro column picker); `dictionary` nomeia o dicionário do provider; `empty` dá o significado da ausência. |
|
|
273
304
|
| `filters` | `{ [nome]: { label, type, options?, multiple?, advanced?, depends?, … } }` — a toolbar. `advanced` vai para o modal; `depends` desabilita/cascateia; options `kind: 'lookup'` busca em uma action. Valor aplicado entra no input com o mesmo nome. |
|
|
274
|
-
| `text`
|
|
275
|
-
| `sort`
|
|
276
|
-
| `periods` | `{ value, label }[]` — o controle de período (presets + Personalizado com calendário); materializa em `from`/`to` no input.
|
|
305
|
+
| `text` | `{ fields }` — liga a busca; convenção: param `q` no input. Com período/filtros no contrato ela fica à direita; sendo a ÚNICA forma de recorte, abre a linha. |
|
|
306
|
+
| `sort` | `{ fields, default }` — ordenação inicial; header ordenável escreve `sort: 'chave:dir'`. |
|
|
307
|
+
| `periods` | `{ value, label }[]` — o controle de período (presets + Personalizado com calendário); materializa em `from`/`to` no input. |
|
|
277
308
|
|
|
278
309
|
## useListAction
|
|
279
310
|
|
|
@@ -285,21 +316,21 @@ chamar a action.
|
|
|
285
316
|
|
|
286
317
|
## Propriedades de ActionList
|
|
287
318
|
|
|
288
|
-
| Propriedade
|
|
289
|
-
|
|
290
|
-
| `action`
|
|
291
|
-
| `input`
|
|
292
|
-
| `cells`
|
|
293
|
-
| `filterOptions`
|
|
294
|
-
| `columns`
|
|
295
|
-
| `children`
|
|
296
|
-
| `batch`
|
|
297
|
-
| `rowId`
|
|
298
|
-
| `pageSize`
|
|
299
|
-
| `state / onStateChange` | `ActionListState / (next) => void`
|
|
300
|
-
| `emptyMessage`
|
|
301
|
-
| `errorMessage`
|
|
302
|
-
| `retryLabel`
|
|
303
|
-
| `onRowClick`
|
|
304
|
-
| `toolbarActions`
|
|
305
|
-
| `rowActions`
|
|
319
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
320
|
+
| ----------------------- | ------------------------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
321
|
+
| `action` | `ListAction<TInput, TItem>` | | A ListAction do Opus (kind list, output = item, paginate cursor). |
|
|
322
|
+
| `input` | `TInput` | | O ESCOPO BASE (ex.: { workspaceId }) — a toolbar soma por cima, nunca sobrescreve. |
|
|
323
|
+
| `cells` | `Record<string, (item) => ReactNode>` | | Células custom por cima das colunas do contrato (chave = column.key). |
|
|
324
|
+
| `filterOptions` | `Record<string, SelectOption[]>` | | Opções de runtime para os filtros select/lookup (chave = nome do filtro). |
|
|
325
|
+
| `columns` | `ActionListColumn<TItem>[]` | | Tabela EXPLÍCITA — sobrepõe as colunas do contrato (escape hatch). |
|
|
326
|
+
| `children` | `(items, refetch) => ReactNode` | | Modo composição: layout livre; a toolbar segue. Tem precedência sobre columns. |
|
|
327
|
+
| `batch` | `ActionListBatchAction[]` | | Ações em lote: `{ label, can?, run, confirm?, destructive? }` — liga a multi-seleção. |
|
|
328
|
+
| `rowId` | `(item) => string` | `item.id` | Identidade da linha para seleção. |
|
|
329
|
+
| `pageSize` | `number` | `50` | Itens por página padrão (vira `limit`/`page` no input; handler devolve `total`). O usuário troca no popover de exibição. |
|
|
330
|
+
| `state / onStateChange` | `ActionListState / (next) => void` | interno | Estado da toolbar controlado — para quem embala sincronizar com a URL. |
|
|
331
|
+
| `emptyMessage` | `string` | `'Nenhum resultado.'` | Frase do estado vazio (o `Empty` do `DataState`). |
|
|
332
|
+
| `errorMessage` | `string` | `'Não foi possível carregar.'` | Título do aviso de erro; a ação de tentar de novo refaz a consulta. |
|
|
333
|
+
| `retryLabel` | `string` | `'Tentar de novo'` | Nome acessível e tooltip da ação de recuperação. |
|
|
334
|
+
| `onRowClick` | `(item: TItem) => void` | | Clique na linha (ex.: navegar para o detalhe) — só na tabela. |
|
|
335
|
+
| `toolbarActions` | `ReactNode` | | Operações ligadas ao recorte atual, no fim da barra e separadas do grupo de controles. |
|
|
336
|
+
| `rowActions` | `(item: TItem) => ReactNode` | | Ações por linha (coluna final, à direita). A partir de duas, entram num grupo com intervalo compacto; uma só fica solta, sem grupo. Cliques ali não disparam o `onRowClick`. |
|
|
@@ -93,9 +93,9 @@ componente ao trocar de conversa. O rótulo do indicador é customizável por
|
|
|
93
93
|
`humanizeTool={(name) => '…'}`.
|
|
94
94
|
|
|
95
95
|
Itens controlados podem carregar `id` e `createdAt`. O Chat os projeta no elemento da
|
|
96
|
-
mensagem e oferece `renderMessageActions` para ações contextuais discretas
|
|
97
|
-
busca nem conhece detalhes: o app deve carregar
|
|
98
|
-
interação da pessoa e aplicar novamente sua autorização.
|
|
96
|
+
mensagem e oferece `renderMessageActions` para ações contextuais discretas tanto nas mensagens
|
|
97
|
+
enviadas quanto nas recebidas. O slot não busca nem conhece detalhes: o app deve carregar
|
|
98
|
+
informação complementar somente após a interação da pessoa e aplicar novamente sua autorização.
|
|
99
99
|
|
|
100
100
|
## Propriedades de Chat
|
|
101
101
|
|
|
@@ -105,7 +105,7 @@ interação da pessoa e aplicar novamente sua autorização.
|
|
|
105
105
|
| `messages` | `ChatTranscriptItem[]` | | Modo controlado: o transcript vem do app; com ele, `onSend`, `busy` e `activity` assumem. |
|
|
106
106
|
| `onSend` | `(text: string) => void \| boolean \| Promise<void \| boolean>` | | Modo controlado: recebe o texto enviado; retornar `false` devolve o texto ao composer. |
|
|
107
107
|
| `busy` | `boolean` | | Modo controlado: trava o composer enquanto o turno corre. |
|
|
108
|
-
| `activity` | `string \| null` | `undefined` | Indicador vivo: `null` mostra “Pensando…”, string mostra o rótulo; `undefined` esconde. |
|
|
108
|
+
| `activity` | `string \| null` | `undefined` | Indicador vivo: `null` mostra “Pensando…”, string mostra o rótulo, `''` centraliza somente os pontos; `undefined` esconde. |
|
|
109
109
|
| `notice` | `ReactNode` | | Aviso do app acima do composer (credencial, agente desatualizado…). |
|
|
110
110
|
| `composerActions` | `ReactNode` | | Seletores discretos na barra do composer (agente, app, escopo). |
|
|
111
111
|
| `composerClassName` | `string` | | Ajusta o contêiner externo do composer sem alcançar o DOM interno. |
|
|
@@ -13,9 +13,11 @@ render(
|
|
|
13
13
|
description="Sessões com acesso à sua conta."
|
|
14
14
|
actions={<Button variant="outline">Encerrar outras sessões</Button>}
|
|
15
15
|
>
|
|
16
|
-
<div className="rounded-lg border border-border p-4">
|
|
16
|
+
<div className="rounded-lg border border-border p-4">
|
|
17
|
+
MacBook Para o · ativo agora
|
|
18
|
+
</div>
|
|
17
19
|
</Content>,
|
|
18
|
-
)
|
|
20
|
+
);
|
|
19
21
|
```
|
|
20
22
|
|
|
21
23
|
## Composição explícita
|
|
@@ -32,28 +34,42 @@ render(
|
|
|
32
34
|
<ContentTitle>Dispositivos conectados</ContentTitle>
|
|
33
35
|
<ContentMeta>3</ContentMeta>
|
|
34
36
|
<ContentDescription>Sessões com acesso à sua conta.</ContentDescription>
|
|
35
|
-
<ContentActions
|
|
37
|
+
<ContentActions>
|
|
38
|
+
<Button variant="outline">Atualizar</Button>
|
|
39
|
+
</ContentActions>
|
|
36
40
|
</ContentHeader>
|
|
37
41
|
<ContentBody>
|
|
38
|
-
<div className="rounded-lg border border-border p-4">
|
|
42
|
+
<div className="rounded-lg border border-border p-4">
|
|
43
|
+
MacBook Para o · ativo agora
|
|
44
|
+
</div>
|
|
39
45
|
</ContentBody>
|
|
40
46
|
</Content>,
|
|
41
|
-
)
|
|
47
|
+
);
|
|
42
48
|
```
|
|
43
49
|
|
|
44
50
|
As duas formas geram os mesmos elementos, estilos e `data-slot`. `opus check` reprova slots fora
|
|
45
51
|
do pai correto, filhos estruturais indiretos e a mistura de shorthand com composição explícita.
|
|
46
52
|
|
|
53
|
+
## Coleção nomeada
|
|
54
|
+
|
|
55
|
+
Quando a região principal da página é uma coleção, `Content` nomeia e governa essa coleção, enquanto
|
|
56
|
+
`ActionList` executa a consulta. Coloque criar, importar e outras ações sobre o conjunto completo em
|
|
57
|
+
`ContentActions`. Busca, filtros, atualização e operações dependentes do recorte atual permanecem na
|
|
58
|
+
toolbar da lista. Essa divisão aproxima cada comando do objeto que ele afeta sem criar uma família
|
|
59
|
+
paralela de componentes `List*`. Na variante `page`, as ações ficam no extremo oposto ao título. A
|
|
60
|
+
criação usa um botão textual `default`, sem ícone, nomeado `Criar recurso`; o diálogo aberto pelo
|
|
61
|
+
gatilho repete esse título e a edição usa `Editar recurso`.
|
|
62
|
+
|
|
47
63
|
## Propriedades de Content
|
|
48
64
|
|
|
49
|
-
| Propriedade
|
|
50
|
-
|
|
51
|
-
| `title`
|
|
52
|
-
| `count`
|
|
53
|
-
| `description` | `ReactNode`
|
|
54
|
-
| `actions`
|
|
55
|
-
| `level`
|
|
56
|
-
| `variant`
|
|
65
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
66
|
+
| ------------- | ---------------------------- | ----------- | -------------------------------------------------------------------------------------------------------- |
|
|
67
|
+
| `title` | `ReactNode` | | Forma curta: título da região (vira o heading ligado à `section`). |
|
|
68
|
+
| `count` | `number` | | Forma curta: total de itens ao lado do título da região. |
|
|
69
|
+
| `description` | `ReactNode` | | Forma curta: frase de apoio sob o título. |
|
|
70
|
+
| `actions` | `ReactNode` | | Forma curta: ações sobre a região inteira; em `page`, ficam no extremo oposto ao título. |
|
|
71
|
+
| `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | herdado | Nível semântico do heading, independente do destaque visual. |
|
|
72
|
+
| `variant` | `'page' \| 'section'` | `'section'` | Hierarquia visual: `page` reproduz o cabeçalho de `Page`; `section` é a região dentro de uma superfície. |
|
|
57
73
|
|
|
58
74
|
Na composição explícita, `ContentHeader` recebe `ContentTitle`, `ContentMeta`, `ContentDescription` e
|
|
59
75
|
`ContentActions`, e `ContentBody` recebe o conteúdo; nenhum desses slots aceita `title` ou `level`
|
|
@@ -21,27 +21,31 @@ borda semântica, raio `xl` e elevação para permanecer distinta da página.
|
|
|
21
21
|
```tsx preview
|
|
22
22
|
<Dialog>
|
|
23
23
|
<DialogTrigger asChild>
|
|
24
|
-
<Button
|
|
24
|
+
<Button>Criar workspace</Button>
|
|
25
25
|
</DialogTrigger>
|
|
26
26
|
<DialogContent>
|
|
27
27
|
<DialogHeader>
|
|
28
|
-
<DialogTitle>
|
|
28
|
+
<DialogTitle>Criar workspace</DialogTitle>
|
|
29
29
|
</DialogHeader>
|
|
30
30
|
<DialogBody className="space-y-2">
|
|
31
31
|
<Label htmlFor="workspace-name">Nome</Label>
|
|
32
32
|
<Input id="workspace-name" placeholder="Ex.: Empresa X" />
|
|
33
33
|
</DialogBody>
|
|
34
34
|
<DialogFooter>
|
|
35
|
-
<
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
<ButtonGroup mode="spaced" distribution="equal">
|
|
36
|
+
<DialogClose asChild>
|
|
37
|
+
<Button variant="outline">Cancelar</Button>
|
|
38
|
+
</DialogClose>
|
|
39
|
+
<Button>Criar workspace</Button>
|
|
40
|
+
</ButtonGroup>
|
|
39
41
|
</DialogFooter>
|
|
40
42
|
</DialogContent>
|
|
41
43
|
</Dialog>
|
|
42
44
|
```
|
|
43
45
|
|
|
44
46
|
O corpo cresce até o limite da janela e passa a rolar; cabeçalho e rodapé permanecem visíveis.
|
|
47
|
+
O `ButtonGroup` divide o espaço entre decisões equivalentes no rodapé. O `DialogFooter` organiza
|
|
48
|
+
a faixa, mas não decide a distribuição nem a aparência dos botões.
|
|
45
49
|
`DialogClose` encerra o modal sem exigir estado controlado. Use `open` e `onOpenChange` quando outra
|
|
46
50
|
parte da interface também precisar controlar a abertura.
|
|
47
51
|
|
|
@@ -207,16 +211,18 @@ function ResponseDialogDemo() {
|
|
|
207
211
|
O contrato possui alterações que ainda não foram salvas.
|
|
208
212
|
</p>
|
|
209
213
|
</DialogBody>
|
|
210
|
-
<DialogFooter
|
|
211
|
-
<
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
<
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
<
|
|
219
|
-
|
|
214
|
+
<DialogFooter>
|
|
215
|
+
<ButtonGroup mode="spaced" distribution="equal">
|
|
216
|
+
<DialogClose result="continue" initialFocus asChild>
|
|
217
|
+
<Button variant="outline">Continuar editando</Button>
|
|
218
|
+
</DialogClose>
|
|
219
|
+
<DialogClose result="discard" asChild>
|
|
220
|
+
<Button variant="outline">Descartar</Button>
|
|
221
|
+
</DialogClose>
|
|
222
|
+
<DialogClose result="save" asChild>
|
|
223
|
+
<Button>Salvar e fechar</Button>
|
|
224
|
+
</DialogClose>
|
|
225
|
+
</ButtonGroup>
|
|
220
226
|
</DialogFooter>
|
|
221
227
|
</DialogContent>
|
|
222
228
|
</Dialog>
|
|
@@ -286,10 +292,10 @@ botão. O texto informa o efeito real, como `Excluir`, `Revogar acesso` ou `Ence
|
|
|
286
292
|
|
|
287
293
|
## Propriedades de DialogFooter
|
|
288
294
|
|
|
289
|
-
| Propriedade | Tipo | Padrão | Descrição
|
|
290
|
-
| ----------- | ----------- | ------ |
|
|
291
|
-
| `className` | `string` | | Ajusta a faixa do modo padrão ou
|
|
292
|
-
| `children` | `ReactNode` | | Ações secundárias e principal.
|
|
295
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
296
|
+
| ----------- | ----------- | ------ | ------------------------------------------- |
|
|
297
|
+
| `className` | `string` | | Ajusta a faixa do modo padrão ou do alerta. |
|
|
298
|
+
| `children` | `ReactNode` | | Ações secundárias e principal. |
|
|
293
299
|
|
|
294
300
|
## Propriedades de DialogTitle
|
|
295
301
|
|
|
@@ -380,5 +386,5 @@ imperativa.
|
|
|
380
386
|
| `label` | `ReactNode` | | Rótulo que descreve o resultado da ação. |
|
|
381
387
|
| `initialFocus` | `boolean` | `false` | Marca a única saída segura que recebe o foco inicial. |
|
|
382
388
|
| `context` | `ButtonContext` | Última ação: `primary`; demais: `neutral` | Define o significado semântico. |
|
|
383
|
-
| `variant` | `ButtonVariant` | Última ação: `solid`; demais: `
|
|
389
|
+
| `variant` | `ButtonVariant` | Última ação: `solid`; demais: `outline` | Define o tratamento visual. |
|
|
384
390
|
| `disabled` | `boolean` | `false` | Impede a escolha desta ação. |
|
|
@@ -25,8 +25,8 @@ entra pela direita e pode ser fechado por Esc, pelo overlay ou pelo botão de fe
|
|
|
25
25
|
|
|
26
26
|
`side` aceita `top`, `right`, `bottom` e `left`. `DrawerFooter` mantém as ações no rodapé;
|
|
27
27
|
`DrawerClose` fecha o painel sem exigir controle manual de estado. Como no `Dialog`, o cabeçalho
|
|
28
|
-
recebe um divisor inferior e o rodapé usa divisor superior
|
|
29
|
-
|
|
28
|
+
recebe um divisor inferior e o rodapé usa um divisor superior. O corpo preserva o mesmo alinhamento
|
|
29
|
+
horizontal entre título, conteúdo e ações.
|
|
30
30
|
|
|
31
31
|
```tsx preview
|
|
32
32
|
<Drawer>
|
|
@@ -42,10 +42,12 @@ mesmo alinhamento horizontal entre título, conteúdo e ações.
|
|
|
42
42
|
<Input id="ws-nome" defaultValue="Empresa X" />
|
|
43
43
|
</DrawerBody>
|
|
44
44
|
<DrawerFooter>
|
|
45
|
-
<
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
<ButtonGroup mode="spaced" distribution="equal">
|
|
46
|
+
<DrawerClose asChild>
|
|
47
|
+
<Button variant="outline">Cancelar</Button>
|
|
48
|
+
</DrawerClose>
|
|
49
|
+
<Button>Salvar</Button>
|
|
50
|
+
</ButtonGroup>
|
|
49
51
|
</DrawerFooter>
|
|
50
52
|
</DrawerContent>
|
|
51
53
|
</Drawer>
|