@softize/opus 19.0.0 → 20.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 +239 -0
- package/bin/cli.mjs +5 -0
- package/bin/lib/check.mjs +198 -10
- package/bin/lib/copy.mjs +29 -0
- package/bin/lib/mcp.mjs +1 -1
- package/docs/adr/0020-icon-tiles-are-explicitly-composed.md +8 -0
- package/docs/adr/0021-content-carries-structure-and-semantic-element.md +7 -0
- package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +19 -1
- package/docs/adr/0024-fill-height-propagates-by-context.md +118 -0
- package/docs/adr/0025-content-frame-treatment-does-not-change-semantics.md +134 -0
- package/docs/data-products.md +15 -9
- package/docs/protocol.md +3 -4
- package/docs/releasing.md +2 -2
- package/package.json +2 -2
- package/registry/skills/create-opus-action/scripts/scaffold.mjs +2 -1
- package/registry/templates/app/package.json +1 -1
- package/src/core/runtime.ts +96 -8
- package/src/core/types.ts +5 -2
- package/src/ui/components/patterns/action-form-card.tsx +11 -1
- package/src/ui/components/patterns/content-header.tsx +109 -18
- package/src/ui/components/patterns/fill-region.ts +150 -0
- package/src/ui/components/patterns/list.tsx +91 -92
- package/src/ui/components/patterns/page.tsx +20 -4
- package/src/ui/components/patterns/presentation.tsx +52 -37
- package/src/ui/components/patterns/split.tsx +44 -4
- package/src/ui/components/patterns/surface-anatomy.ts +35 -8
- package/src/ui/components/patterns/surface-header.tsx +58 -25
- package/src/ui/components/patterns/trigger.tsx +12 -2
- package/src/ui/components/primitives/alert.tsx +9 -1
- package/src/ui/components/primitives/avatar.tsx +7 -3
- package/src/ui/components/primitives/card.tsx +1 -1
- package/src/ui/components/primitives/carousel.tsx +34 -5
- package/src/ui/components/primitives/checkbox.tsx +11 -3
- package/src/ui/components/primitives/command.tsx +2 -1
- package/src/ui/components/primitives/copyable.tsx +120 -10
- package/src/ui/components/primitives/dialog.tsx +42 -11
- package/src/ui/components/primitives/drawer.tsx +10 -2
- package/src/ui/components/primitives/empty.tsx +1 -1
- package/src/ui/components/primitives/icon-picker.tsx +6 -2
- package/src/ui/components/primitives/icon-tile.tsx +7 -0
- package/src/ui/components/primitives/input-group.tsx +9 -2
- package/src/ui/components/primitives/input-otp.tsx +1 -1
- package/src/ui/components/primitives/kbd.tsx +1 -1
- package/src/ui/components/primitives/label.tsx +3 -0
- package/src/ui/components/primitives/menu.tsx +5 -0
- package/src/ui/components/primitives/popover.tsx +1 -1
- package/src/ui/components/primitives/progress.tsx +9 -1
- package/src/ui/components/primitives/slider.tsx +62 -11
- package/src/ui/components/primitives/truncate.tsx +10 -1
- package/src/ui/docs/content/accordion.md +74 -27
- package/src/ui/docs/content/action-form-card.md +24 -15
- package/src/ui/docs/content/action-form-dialog.md +17 -7
- package/src/ui/docs/content/action-form.md +92 -77
- package/src/ui/docs/content/action-list-dialog.md +75 -38
- package/src/ui/docs/content/action-list.md +141 -58
- package/src/ui/docs/content/action-trigger.md +27 -10
- package/src/ui/docs/content/action-view.md +14 -14
- package/src/ui/docs/content/actions.md +161 -66
- package/src/ui/docs/content/ai.md +43 -44
- package/src/ui/docs/content/alert.md +83 -29
- package/src/ui/docs/content/ask.md +51 -26
- package/src/ui/docs/content/aspect-ratio.md +22 -24
- package/src/ui/docs/content/audit.md +23 -19
- package/src/ui/docs/content/auth.md +38 -19
- package/src/ui/docs/content/avatar.md +54 -5
- package/src/ui/docs/content/badge.md +29 -9
- package/src/ui/docs/content/breadcrumb.md +21 -8
- package/src/ui/docs/content/button.md +73 -43
- package/src/ui/docs/content/calendar.md +134 -26
- package/src/ui/docs/content/card.md +41 -15
- package/src/ui/docs/content/carousel.md +37 -6
- package/src/ui/docs/content/chat.md +118 -37
- package/src/ui/docs/content/checkbox.md +73 -7
- package/src/ui/docs/content/cli.md +94 -55
- package/src/ui/docs/content/collapsible.md +43 -19
- package/src/ui/docs/content/command.md +30 -8
- package/src/ui/docs/content/composer.md +23 -8
- package/src/ui/docs/content/content.md +119 -27
- package/src/ui/docs/content/copyable.md +39 -8
- package/src/ui/docs/content/customization.md +55 -46
- package/src/ui/docs/content/cycle.md +33 -18
- package/src/ui/docs/content/data-products.md +115 -0
- package/src/ui/docs/content/data-state.md +20 -6
- package/src/ui/docs/content/data.md +20 -19
- package/src/ui/docs/content/detail.md +12 -5
- package/src/ui/docs/content/dialog.md +107 -24
- package/src/ui/docs/content/dictionary-value.md +4 -4
- package/src/ui/docs/content/dock.md +54 -19
- package/src/ui/docs/content/dot.md +35 -5
- package/src/ui/docs/content/drawer.md +63 -19
- package/src/ui/docs/content/empty-value.md +20 -6
- package/src/ui/docs/content/empty.md +24 -3
- package/src/ui/docs/content/events.md +76 -16
- package/src/ui/docs/content/executions.md +9 -9
- package/src/ui/docs/content/field.md +21 -6
- package/src/ui/docs/content/getting-started.md +44 -34
- package/src/ui/docs/content/icon-picker.md +19 -13
- package/src/ui/docs/content/icon-tile.md +38 -15
- package/src/ui/docs/content/input-otp.md +58 -7
- package/src/ui/docs/content/input.md +22 -3
- package/src/ui/docs/content/item.md +53 -6
- package/src/ui/docs/content/kbd.md +5 -1
- package/src/ui/docs/content/label.md +13 -4
- package/src/ui/docs/content/log.md +5 -5
- package/src/ui/docs/content/markdown.md +5 -4
- package/src/ui/docs/content/mcp.md +49 -23
- package/src/ui/docs/content/menu.md +95 -10
- package/src/ui/docs/content/metric-card.md +12 -4
- package/src/ui/docs/content/observability.md +10 -9
- package/src/ui/docs/content/page.md +118 -12
- package/src/ui/docs/content/pagination.md +13 -5
- package/src/ui/docs/content/pane.md +55 -5
- package/src/ui/docs/content/popover.md +42 -5
- package/src/ui/docs/content/presentation.md +91 -47
- package/src/ui/docs/content/progress.md +27 -14
- package/src/ui/docs/content/queue.md +39 -21
- package/src/ui/docs/content/radio-group.md +26 -4
- package/src/ui/docs/content/router.md +29 -10
- package/src/ui/docs/content/runtime.md +90 -54
- package/src/ui/docs/content/scheduler.md +53 -21
- package/src/ui/docs/content/scroll-area.md +42 -35
- package/src/ui/docs/content/select.md +78 -33
- package/src/ui/docs/content/semantic-context.md +21 -2
- package/src/ui/docs/content/separator.md +15 -9
- package/src/ui/docs/content/sidebar.md +3 -2
- package/src/ui/docs/content/skeleton.md +6 -1
- package/src/ui/docs/content/slider.md +37 -9
- package/src/ui/docs/content/spinner.md +4 -3
- package/src/ui/docs/content/split.md +19 -0
- package/src/ui/docs/content/storage.md +8 -9
- package/src/ui/docs/content/switch.md +10 -7
- package/src/ui/docs/content/table.md +46 -8
- package/src/ui/docs/content/tabs.md +14 -4
- package/src/ui/docs/content/testing.md +76 -31
- package/src/ui/docs/content/textarea.md +5 -4
- package/src/ui/docs/content/toast.md +48 -7
- package/src/ui/docs/content/toggle.md +3 -3
- package/src/ui/docs/content/tokens.md +15 -26
- package/src/ui/docs/content/tooltip.md +18 -2
- package/src/ui/docs/content/truncate.md +36 -6
- package/src/ui/docs/content/ui.md +74 -22
- package/src/ui/docs/content/upgrading.md +45 -36
- package/src/ui/docs/plugin.ts +36 -2
- package/src/ui/docs/registry.tsx +6 -0
- package/src/ui/drivers/react.tsx +16 -8
- package/src/ui/meta.ts +32 -32
- package/src/ui/react.tsx +1 -0
- package/src/vite/design.ts +16 -4
|
@@ -6,14 +6,17 @@ recursos e envia o estado correspondente no input. Filtros avançados aparecem n
|
|
|
6
6
|
os filtros aplicados permanecem visíveis como chips removíveis.
|
|
7
7
|
|
|
8
8
|
A tabela derivada não cria uma moldura própria. A superfície que recebe o `ActionList` define os
|
|
9
|
-
limites visuais da coleção; use `Table` diretamente quando a composição precisar da
|
|
10
|
-
`framed`.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
limites visuais da coleção; use [`Table`](/ui/table) diretamente quando a composição precisar da
|
|
10
|
+
variante `framed`. Carregamento, erro e vazio usam o mesmo [`DataState`](/ui/data-state) das
|
|
11
|
+
demais coleções.
|
|
12
|
+
|
|
13
|
+
A barra mantém em uma única linha os controles declarados como `inline`: ela não quebra e não move
|
|
14
|
+
filtros para o modal por falta de espaço. Seletores de valor único acompanham o valor escolhido até
|
|
15
|
+
`20rem` e truncam o excedente; a busca varia entre `8rem` e `16rem` conforme a largura
|
|
16
|
+
disponível. Quando a barra ficar longa demais, declare como `advanced` os filtros que não precisam
|
|
17
|
+
ficar sempre à vista, como descreve a seção “Onde cada filtro aparece”. Recarregar e
|
|
18
|
+
exibição formam um `ButtonGroup` espaçado; `toolbarActions` acrescenta, depois deles, operações
|
|
19
|
+
ligadas ao recorte atual.
|
|
17
20
|
|
|
18
21
|
```tsx preview col
|
|
19
22
|
render(
|
|
@@ -31,8 +34,9 @@ render(
|
|
|
31
34
|
|
|
32
35
|
## Coleção nomeada
|
|
33
36
|
|
|
34
|
-
Em uma página, envolva a listagem com `Content
|
|
35
|
-
cabeçalho da coleção; busca, filtros e atualização permanecem na toolbar do
|
|
37
|
+
Em uma página, envolva a listagem com [`Content`](/ui/content). O título e a criação ocupam
|
|
38
|
+
extremos opostos do cabeçalho da coleção; busca, filtros e atualização permanecem na toolbar do
|
|
39
|
+
`ActionList`.
|
|
36
40
|
|
|
37
41
|
```tsx
|
|
38
42
|
<Content
|
|
@@ -46,7 +50,8 @@ cabeçalho da coleção; busca, filtros e atualização permanecem na toolbar do
|
|
|
46
50
|
```
|
|
47
51
|
|
|
48
52
|
Use `toolbarActions` somente quando a operação depender do recorte visível, como exportar os
|
|
49
|
-
resultados filtrados.
|
|
53
|
+
resultados filtrados. A propriedade recebe os nós já prontos: em uma ação somente com ícone, forneça
|
|
54
|
+
você mesmo o `aria-label` e o `Tooltip` com o nome da operação.
|
|
50
55
|
|
|
51
56
|
## Barra de filtros compartilhada
|
|
52
57
|
|
|
@@ -64,9 +69,9 @@ superfícies que não são listagens, como relatórios. O estado é controlado p
|
|
|
64
69
|
/>
|
|
65
70
|
```
|
|
66
71
|
|
|
67
|
-
Seletores inline de valor único
|
|
72
|
+
Seletores inline de valor único acompanham o valor selecionado até `20rem`. Valores maiores são
|
|
68
73
|
truncados no controle, enquanto o dropdown pode crescer além dele para preservar a opção.
|
|
69
|
-
Multiseletores mantêm `
|
|
74
|
+
Multiseletores mantêm `13rem`; no modal de filtros avançados, o seletor ocupa toda a largura.
|
|
70
75
|
`filterIcons` associa um ícone leading ao nome do filtro quando a superfície precisa reforçar
|
|
71
76
|
dimensões recorrentes, sem substituir o rótulo. Busca, filtros e período aparecem somente quando
|
|
72
77
|
declarados.
|
|
@@ -79,7 +84,7 @@ simples usam o valor vazio como padrão; por isso, opções como “Todos” tam
|
|
|
79
84
|
Use `periodPosition` para manter o período antes ou depois dos filtros inline. Quando ele precisa
|
|
80
85
|
apenas aparecer depois de um filtro específico, use `{ afterFilter: 'dateBasis' }`. Se tipo de data
|
|
81
86
|
e período formam uma única decisão, declare `period={{ basisFilter: 'dateBasis' }}`: o Opus os reúne
|
|
82
|
-
|
|
87
|
+
em um só controle, com ícone no início e uma única ação de limpar no fim. Quando a pessoa muda o tipo
|
|
83
88
|
ou o período padrão, essa ação aparece e restaura os dois valores. Se a âncora de `periodPosition`
|
|
84
89
|
não existir, o período permanece visível no fim dos filtros inline.
|
|
85
90
|
|
|
@@ -91,10 +96,11 @@ apresentação para o consumidor, como cards de status acima da lista. No modal,
|
|
|
91
96
|
`section` formam um grupo, e `advancedFilters.columns` distribui os grupos em até três colunas.
|
|
92
97
|
Use `advancedFilters.sectionIcons` quando os grupos representarem dimensões recorrentes; a chave é
|
|
93
98
|
o mesmo rótulo de `section`, e cada grupo usa `Content as="fieldset"` para manter uma legenda
|
|
94
|
-
nativa.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
99
|
+
nativa. O ícone ocupa a mídia do grupo, antes do título, e recebe a moldura padrão: passe só o
|
|
100
|
+
glifo. Um `IconTile` é adotado pela mídia com o que declara, e um ícone `null` não desenha nada.
|
|
101
|
+
Quando os grupos precisarem de limites mais explícitos, `advancedFilters.sectionVariant: 'framed'`
|
|
102
|
+
envolve cada seção e pede ao `Content` o acabamento `frameVariant="legend"`, integrando visualmente
|
|
103
|
+
a legenda à borda sem atribuir esse design à semântica de `fieldset`.
|
|
98
104
|
Um filtro `select` pode declarar `searchable: true` quando a lista fechada for longa; a opção geral
|
|
99
105
|
continua disponível e participa da busca. Campos de data avançados usam calendário. A antiga `advanced: true` continua aceita durante a
|
|
100
106
|
migração e equivale a `placement: 'advanced'`.
|
|
@@ -133,8 +139,8 @@ filters: {
|
|
|
133
139
|
columns: 2,
|
|
134
140
|
sectionVariant: 'framed',
|
|
135
141
|
sectionIcons: {
|
|
136
|
-
Relacionamento: <
|
|
137
|
-
Datas: <
|
|
142
|
+
Relacionamento: <UserRound />,
|
|
143
|
+
Datas: <CalendarDays />,
|
|
138
144
|
},
|
|
139
145
|
}}
|
|
140
146
|
/>
|
|
@@ -177,7 +183,7 @@ render(
|
|
|
177
183
|
action={docWorkspaceList}
|
|
178
184
|
input={{}}
|
|
179
185
|
cells={{
|
|
180
|
-
name: (w) => (
|
|
186
|
+
name: (w: { name: string }) => (
|
|
181
187
|
<a
|
|
182
188
|
href="#"
|
|
183
189
|
onClick={(e) => e.preventDefault()}
|
|
@@ -218,6 +224,65 @@ disponíveis e a quantidade de itens. Alterar busca, filtro, ordenação ou per
|
|
|
218
224
|
página. Durante a primeira consulta, `DataState` apresenta o carregamento; em atualizações
|
|
219
225
|
posteriores, a lista permanece visível com `aria-busy`.
|
|
220
226
|
|
|
227
|
+
## Ocupar a altura disponível
|
|
228
|
+
|
|
229
|
+
Por padrão, a lista cresce com os resultados e a rolagem acontece na janela ou no contêiner que a
|
|
230
|
+
hospeda. Quando a coleção é a região principal de uma tela de altura fixa, a barra de filtros e o
|
|
231
|
+
rodapé podem continuar visíveis enquanto só os resultados rolam. Para isso, declare
|
|
232
|
+
`layout="fill"` na [`Page`](/ui/page) e componha a lista com `PageBody` e
|
|
233
|
+
[`Content`](/ui/content), como no exemplo abaixo. Role a tabela para ver o cabeçalho fixo.
|
|
234
|
+
|
|
235
|
+
```tsx preview col
|
|
236
|
+
render(
|
|
237
|
+
<DocBrowserActionProvider>
|
|
238
|
+
<div className="h-80 w-full overflow-x-auto overflow-y-hidden rounded-lg border border-border">
|
|
239
|
+
<Page layout="fill">
|
|
240
|
+
<PageBody>
|
|
241
|
+
<Content level={1} variant="page">
|
|
242
|
+
<ContentHeader>
|
|
243
|
+
<ContentTitle>Workspaces</ContentTitle>
|
|
244
|
+
<ContentActions>
|
|
245
|
+
<Button>Criar workspace</Button>
|
|
246
|
+
</ContentActions>
|
|
247
|
+
</ContentHeader>
|
|
248
|
+
<ContentBody>
|
|
249
|
+
<ActionList action={docWorkspaceList} input={{}} />
|
|
250
|
+
</ContentBody>
|
|
251
|
+
</Content>
|
|
252
|
+
</PageBody>
|
|
253
|
+
</Page>
|
|
254
|
+
</div>
|
|
255
|
+
</DocBrowserActionProvider>,
|
|
256
|
+
);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
A página limita a própria altura à do contêiner que a hospeda, normalmente o shell, e `PageBody`,
|
|
260
|
+
`Content` e `ContentBody` repassam essa altura até a lista, que passa a usar `layout="fill"` sem que
|
|
261
|
+
você o declare. Em uma [`Presentation`](/ui/presentation) de lista, basta `layout="fill"` no
|
|
262
|
+
próprio `Presentation`. Só as regiões que contêm a lista mudam: uma seção irmã, como um resumo acima
|
|
263
|
+
da coleção, mantém a altura do próprio conteúdo. Os demais filhos de cada região que repassa a
|
|
264
|
+
altura se esticam à largura dela; dê `self-start` a um botão que deva manter a largura do conteúdo.
|
|
265
|
+
|
|
266
|
+
Com a altura limitada, a barra de filtros, o `beforeContent` e o rodapé mantêm a própria altura. A
|
|
267
|
+
região de resultados ocupa o espaço restante e rola nos dois eixos; o rodapé se mede pelo conteúdo
|
|
268
|
+
e mantém a mesma altura com e sem paginador. Na tabela, o cabeçalho fica fixo no topo da região que
|
|
269
|
+
rola, sobre a cor da superfície e com uma linha inferior, e a moldura dos estados de carregamento,
|
|
270
|
+
erro e vazio ocupa a altura disponível. Em outras visualizações e em `children`, a rolagem interna
|
|
271
|
+
continua, mas não existe cabeçalho fixo.
|
|
272
|
+
|
|
273
|
+
A lista só assume `fill` sozinha quando é filha direta de `PageBody`, `ContentBody` ou do corpo de
|
|
274
|
+
uma `Presentation`, e a altura só chega até ela se não houver um contêiner próprio entre esses
|
|
275
|
+
componentes no caminho. Nos demais casos, a decisão é sua:
|
|
276
|
+
|
|
277
|
+
- Para a lista manter a altura dos resultados, como em uma seção secundária cuja rolagem pertence ao
|
|
278
|
+
`PageBody`, declare `layout="flow"`.
|
|
279
|
+
- Um contêiner próprio entre `ContentBody` e a lista, como um `TabsContent`, não repassa a altura, e
|
|
280
|
+
a lista volta a `flow`. Declare `layout="fill"` na lista e dê ao contêiner as classes
|
|
281
|
+
`flex min-h-0 flex-1 flex-col`.
|
|
282
|
+
- Dentro de [`Card`](/ui/card), [`Dialog`](/ui/dialog), [`Drawer`](/ui/drawer) ou
|
|
283
|
+
[`Pane`](/ui/pane), a lista não herda o `fill` da página: a altura do corpo pertence à
|
|
284
|
+
superfície.
|
|
285
|
+
|
|
221
286
|
## Ações em lote
|
|
222
287
|
|
|
223
288
|
`batch` acrescenta seleção à tabela. `can(item)` determina quais linhas podem participar: itens
|
|
@@ -250,6 +315,14 @@ fornece um `render`; `ActionList` preserva a mesma fonte de dados, filtros e est
|
|
|
250
315
|
“Tabela” aparece quando existem colunas.
|
|
251
316
|
|
|
252
317
|
```tsx preview col
|
|
318
|
+
type Workspace = {
|
|
319
|
+
id: string;
|
|
320
|
+
name: string;
|
|
321
|
+
client: string;
|
|
322
|
+
status: "active" | "onboarding" | "paused";
|
|
323
|
+
agents: number;
|
|
324
|
+
};
|
|
325
|
+
|
|
253
326
|
render(
|
|
254
327
|
<DocBrowserActionProvider>
|
|
255
328
|
<div className="w-full">
|
|
@@ -260,24 +333,20 @@ render(
|
|
|
260
333
|
gallery: {
|
|
261
334
|
label: "Galeria",
|
|
262
335
|
icon: <LayoutGrid />,
|
|
263
|
-
render: (workspaces) => (
|
|
336
|
+
render: (workspaces: Workspace[]) => (
|
|
264
337
|
<div className="grid grid-cols-2 gap-3">
|
|
265
338
|
{workspaces.map((w) => (
|
|
266
|
-
<
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
<
|
|
273
|
-
dict={docWorkspaceStatus}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
<p className="mt-1 text-xs text-muted-foreground">
|
|
278
|
-
{w.agents} agentes.
|
|
279
|
-
</p>
|
|
280
|
-
</div>
|
|
339
|
+
<Card key={w.id}>
|
|
340
|
+
<CardHeader>
|
|
341
|
+
<CardTitle>{w.name}</CardTitle>
|
|
342
|
+
<CardDescription>
|
|
343
|
+
{w.agents === 1 ? "1 agente" : `${w.agents} agentes`}
|
|
344
|
+
</CardDescription>
|
|
345
|
+
<CardAction>
|
|
346
|
+
<DictionaryValue dict={docWorkspaceStatus} value={w.status} />
|
|
347
|
+
</CardAction>
|
|
348
|
+
</CardHeader>
|
|
349
|
+
</Card>
|
|
281
350
|
))}
|
|
282
351
|
</div>
|
|
283
352
|
),
|
|
@@ -334,28 +403,42 @@ Convenção compacta (defaults omitidos): `?q=…&sort=chave:dir&status=…&view
|
|
|
334
403
|
|
|
335
404
|
## Composição dos itens
|
|
336
405
|
|
|
337
|
-
Passe `children` para substituir a tabela por uma composição própria
|
|
338
|
-
estados de carregamento, erro e vazio continuam sendo
|
|
406
|
+
Passe `children` para substituir a tabela por uma composição própria, como uma lista de
|
|
407
|
+
[`Item`](/ui/item). A barra de filtros e os estados de carregamento, erro e vazio continuam sendo
|
|
408
|
+
tratados por `ActionList`. O contrato não leva o tipo do item até o componente: anote o parâmetro de
|
|
409
|
+
`children` (ou de `render`, em uma visualização) ou declare os genéricos, como em
|
|
410
|
+
`<ActionList<Input, Workspace>>`. Sem uma das duas formas, os itens chegam como `unknown`.
|
|
339
411
|
|
|
340
412
|
```tsx preview col
|
|
413
|
+
type Workspace = {
|
|
414
|
+
id: string;
|
|
415
|
+
name: string;
|
|
416
|
+
client: string;
|
|
417
|
+
status: "active" | "onboarding" | "paused";
|
|
418
|
+
agents: number;
|
|
419
|
+
};
|
|
420
|
+
|
|
341
421
|
render(
|
|
342
422
|
<DocBrowserActionProvider>
|
|
343
423
|
<div className="w-full">
|
|
344
424
|
<ActionList action={docWorkspaceList} input={{}}>
|
|
345
|
-
{(workspaces) => (
|
|
346
|
-
<
|
|
347
|
-
{workspaces.map((w) => (
|
|
348
|
-
<
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
<
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
425
|
+
{(workspaces: Workspace[]) => (
|
|
426
|
+
<ItemGroup variant="framed">
|
|
427
|
+
{workspaces.map((w, index) => (
|
|
428
|
+
<React.Fragment key={w.id}>
|
|
429
|
+
{index > 0 && <ItemSeparator />}
|
|
430
|
+
<Item>
|
|
431
|
+
<ItemHeader>
|
|
432
|
+
<ItemTitle>{w.name}</ItemTitle>
|
|
433
|
+
<ItemDescription>Cliente {w.client}.</ItemDescription>
|
|
434
|
+
</ItemHeader>
|
|
435
|
+
<ItemActions>
|
|
436
|
+
<DictionaryValue dict={docWorkspaceStatus} value={w.status} />
|
|
437
|
+
</ItemActions>
|
|
438
|
+
</Item>
|
|
439
|
+
</React.Fragment>
|
|
357
440
|
))}
|
|
358
|
-
</
|
|
441
|
+
</ItemGroup>
|
|
359
442
|
)}
|
|
360
443
|
</ActionList>
|
|
361
444
|
</div>
|
|
@@ -403,9 +486,9 @@ chamar a action.
|
|
|
403
486
|
|
|
404
487
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
405
488
|
| ----------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
406
|
-
| `action` | `
|
|
407
|
-
| `input` | `TInput` | | O
|
|
408
|
-
| `layout` | `'flow' \| 'fill'` | `'flow'`
|
|
489
|
+
| `action` | `ListActionLike` | | A ListAction do Opus (kind list, output = item). A paginação envia `limit`/`page` e usa o `total` devolvido. |
|
|
490
|
+
| `input` | `TInput` | | O escopo base, como `{ workspaceId }`. A toolbar acrescenta busca, filtros e paginação sem sobrescrever essas chaves. |
|
|
491
|
+
| `layout` | `'flow' \| 'fill'` | `'fill'` como filha direta de `PageBody`, `ContentBody` ou do corpo da `Presentation` em uma página `fill`; `'flow'` nos demais casos | Em `fill`, ocupa a altura de um ancestral limitado e rola somente a região de resultados; na tabela, o cabeçalho fica fixo. Sem ancestral limitado, equivale a `flow`. |
|
|
409
492
|
| `cells` | `Record<string, (item) => ReactNode>` | | Células custom por cima das colunas do contrato (chave = column.key). |
|
|
410
493
|
| `filterOptions` | `Record<string, SelectOption[]>` | | Opções de runtime para os filtros select/lookup (chave = nome do filtro). |
|
|
411
494
|
| `filterDefaults` | `Record<string, string>` | | Valores de repouso aplicados à consulta; o `X` só aparece quando o filtro diverge deles e retorna a eles. |
|
|
@@ -415,7 +498,7 @@ chamar a action.
|
|
|
415
498
|
| `period` | `{ basisFilter: string }` | | Reúne o período e o filtro inline indicado em um único controle. |
|
|
416
499
|
| `views` | `Record<string, ActionListView<TItem>>` | | Visualizações alternativas `{ label, icon?, render }`; o seletor alterna entre a tabela e estas. |
|
|
417
500
|
| `beforeContent` | `ReactNode` | | Conteúdo entre a toolbar e o corpo, como um resumo que reage ao recorte atual. |
|
|
418
|
-
| `columns` | `ActionListColumn<TItem>[]` | | Tabela
|
|
501
|
+
| `columns` | `ActionListColumn<TItem>[]` | | Tabela explícita, que substitui as colunas declaradas no contrato. |
|
|
419
502
|
| `children` | `(items, refetch) => ReactNode` | | Modo composição: layout livre; a toolbar segue. Tem precedência sobre columns. |
|
|
420
503
|
| `batch` | `ActionListBatchAction[]` | | Ações em lote: `{ label, can?, run, confirm?: { title, message? }, destructive? }` — liga a multi-seleção. |
|
|
421
504
|
| `rowId` | `(item) => string` | `item.id` | Identidade da linha para seleção. |
|
|
@@ -427,5 +510,5 @@ chamar a action.
|
|
|
427
510
|
| `errorMessage` | `string` | `'Não foi possível carregar.'` | Título do aviso de erro; a ação de tentar de novo refaz a consulta. |
|
|
428
511
|
| `retryLabel` | `string` | `'Tentar de novo'` | Nome acessível e tooltip da ação de recuperação. |
|
|
429
512
|
| `onRowClick` | `(item: TItem) => void` | | Clique na linha (ex.: navegar para o detalhe) — só na tabela. |
|
|
430
|
-
| `toolbarActions` | `ReactNode` | | Operações ligadas ao recorte atual, no fim da barra e separadas do grupo de controles.
|
|
431
|
-
| `rowActions` | `(item: TItem) => ReactNode` | | Ações por linha (coluna final, à direita). A partir de duas, entram
|
|
513
|
+
| `toolbarActions` | `ReactNode` | | Operações ligadas ao recorte atual, no fim da barra e separadas do grupo de controles. Os nós são renderizados como recebidos: nome acessível e tooltip de uma ação somente com ícone ficam com o consumidor. |
|
|
514
|
+
| `rowActions` | `(item: TItem) => ReactNode` | | Ações por linha (coluna final, à direita). A partir de duas, entram em um grupo com intervalo compacto; uma só fica solta, sem grupo. Cliques ali não disparam o `onRowClick`. |
|
|
@@ -13,9 +13,11 @@ de tradução ou estiver ausente, o botão usa `action.name`.
|
|
|
13
13
|
|
|
14
14
|
## Confirmação declarada no contrato
|
|
15
15
|
|
|
16
|
-
Quando a action declara `confirm`, o componente monta o diálogo
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
Quando a action declara `confirm`, o componente monta o diálogo antes de executar. Com
|
|
17
|
+
`confirm.destructive: true` no contrato, o gatilho e o botão de confirmação usam o contexto
|
|
18
|
+
`danger`. Use a propriedade `confirm` somente quando esta ocorrência precisar de uma mensagem
|
|
19
|
+
diferente da declarada no contrato; ela substitui os textos, mas o caráter destrutivo continua
|
|
20
|
+
vindo do contrato.
|
|
19
21
|
|
|
20
22
|
```tsx preview
|
|
21
23
|
<DocBrowserActionProvider>
|
|
@@ -41,20 +43,35 @@ confirm: {
|
|
|
41
43
|
Com `icon`, o botão exibe somente o ícone no quadrado `icon-xs` da escala (1.5rem, a ação que mora
|
|
42
44
|
dentro de uma linha ou card) e usa `label`, ou `action.label`, no tooltip e no nome acessível. Uma
|
|
43
45
|
composição que peça mais presença, como a barra do `PageHeader`, declara `size="icon"`. O clique
|
|
44
|
-
não aciona o item clicável ao redor.
|
|
46
|
+
não aciona o item clicável ao redor.
|
|
47
|
+
|
|
48
|
+
Em uma lista, várias linhas repetem a mesma ação. Inclua o alvo em `label`, como `Excluir Unidade
|
|
49
|
+
Centro`, para que cada botão tenha um nome acessível distinto. `itemLabel` identifica o registro na
|
|
50
|
+
pergunta de confirmação.
|
|
45
51
|
|
|
46
52
|
```tsx
|
|
53
|
+
// No contrato:
|
|
54
|
+
confirm: {
|
|
55
|
+
title: 'Excluir a unidade?',
|
|
56
|
+
message: 'Os agendamentos desta unidade também serão removidos.',
|
|
57
|
+
confirmLabel: 'Excluir',
|
|
58
|
+
destructive: true,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Na linha da lista:
|
|
47
62
|
<ActionTrigger
|
|
48
63
|
action={unitDeleteContract}
|
|
49
64
|
input={{ id: unit.id }}
|
|
50
65
|
icon={<Trash2 />}
|
|
51
|
-
label=
|
|
66
|
+
label={`Excluir ${unit.name}`}
|
|
52
67
|
itemLabel={unit.name}
|
|
53
68
|
/>
|
|
54
69
|
```
|
|
55
70
|
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
Sem `confirmLabel` no contrato ou `actionLabel` na propriedade `confirm`, o botão de confirmação
|
|
72
|
+
usa o `label` da action, como `Excluir`. O nome longo passado em `label` serve à linha; na pergunta,
|
|
73
|
+
o alvo já aparece por `itemLabel`. O `label` do gatilho só nomeia o botão de confirmação quando a
|
|
74
|
+
action não declara `label`.
|
|
58
75
|
|
|
59
76
|
## Erro que a pessoa entende
|
|
60
77
|
|
|
@@ -80,9 +97,9 @@ um atalho, um arrastar, um item de menu.
|
|
|
80
97
|
|---|---|---|---|
|
|
81
98
|
| `action` | `SimpleContract<TInput, TData>` | | A SimpleAction do Opus — label, messages e confirm vêm do contrato. |
|
|
82
99
|
| `input` | `TInput` | | O que a action recebe — geralmente { id }. |
|
|
83
|
-
| `label` | `string` | `action.label` quando é texto; senão `action.name` |
|
|
84
|
-
| `context / variant / size` | `do Button` | gatilho: `primary`/`solid`; `danger` quando
|
|
85
|
-
| `confirm` | `{ title, body?, actionLabel?, cancelLabel? }` | | Confirmação
|
|
100
|
+
| `label` | `string` | `action.label` quando é texto; senão `action.name` | Texto do botão ou, com `icon`, seu nome acessível e tooltip. Só vira o botão de confirmação quando a action não declara `label` e nem o contrato nem `confirm` declaram outro rótulo. |
|
|
101
|
+
| `context / variant / size` | `do Button` | gatilho: `primary`/`solid`; `danger` quando `action.confirm.destructive` é `true`; no modo ícone, `ghost` e `neutral` (ou `danger` se destrutiva) / `default`, ou `icon-xs` com `icon` | Visual do gatilho; `context` explícito vence. O botão de confirmar é sempre `solid`: `danger` quando `action.confirm.destructive` é `true`, senão a prop `context` do gatilho (ou `primary`). |
|
|
102
|
+
| `confirm` | `{ title, body?, actionLabel?, cancelLabel? }` | | Confirmação para esta ocorrência; substitui os textos do `confirm` do contrato. Sem `actionLabel`, o botão de confirmar usa `action.label`. O contexto `danger` continua dependendo de `action.confirm.destructive`. |
|
|
86
103
|
| `disabled` | `boolean` | `false` | Desabilita o botão independentemente da execução em andamento. |
|
|
87
104
|
| `onSuccess` | `(data: TData) => void` | | Pós-sucesso (cache já invalidado pelo action.invalidates). |
|
|
88
105
|
| `onLoadingChange` | `(loading: boolean) => void` | | Informa início e fim da execução a quem coordena bloqueio entre várias actions. |
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Use `ActionView` para carregar um recurso por uma `ViewAction` e manter carregamento e erro no
|
|
4
4
|
mesmo fluxo. No exemplo, alterne entre os workspaces para observar o carregamento e a recuperação de
|
|
5
|
-
erro. O consumidor compõe somente o conteúdo disponível.
|
|
5
|
+
erro. O consumidor compõe somente o conteúdo disponível, aqui com [`Detail`](/ui/detail). Para uma
|
|
6
|
+
coleção, use [`ActionList`](/ui/action-list).
|
|
6
7
|
|
|
7
8
|
```tsx preview col
|
|
8
9
|
const [id, setId] = useState('empresa-x')
|
|
@@ -10,26 +11,25 @@ const [id, setId] = useState('empresa-x')
|
|
|
10
11
|
render(
|
|
11
12
|
<DocBrowserActionProvider>
|
|
12
13
|
<div className="w-full space-y-3">
|
|
13
|
-
<
|
|
14
|
+
<ButtonGroup mode="spaced">
|
|
14
15
|
<Button variant="outline" size="sm" onClick={() => setId('empresa-x')}>Empresa X</Button>
|
|
15
16
|
<Button variant="outline" size="sm" onClick={() => setId('empresa-y')}>Empresa Y</Button>
|
|
16
17
|
<Button variant="outline" size="sm" onClick={() => setId('sessao-fantasma')}>Inexistente (erro)</Button>
|
|
17
|
-
</
|
|
18
|
-
<
|
|
18
|
+
</ButtonGroup>
|
|
19
|
+
<Card className="p-4">
|
|
19
20
|
<ActionView action={docWorkspaceView} input={{ id }}>
|
|
20
21
|
{(ws) => (
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
</div>
|
|
22
|
+
<DetailGroup columns={3}>
|
|
23
|
+
<DetailField label="Workspace" value={ws.name} />
|
|
24
|
+
<DetailField label="Cliente" value={ws.client} />
|
|
25
|
+
<DetailField
|
|
26
|
+
label="Status"
|
|
27
|
+
value={<DictionaryValue dict={docWorkspaceStatus} value={ws.status} />}
|
|
28
|
+
/>
|
|
29
|
+
</DetailGroup>
|
|
30
30
|
)}
|
|
31
31
|
</ActionView>
|
|
32
|
-
</
|
|
32
|
+
</Card>
|
|
33
33
|
</div>
|
|
34
34
|
</DocBrowserActionProvider>,
|
|
35
35
|
)
|