@softize/opus 13.1.0 → 15.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 +97 -0
- package/bin/cli.mjs +2 -0
- package/bin/lib/check.mjs +33 -5
- package/bin/lib/cli-shared.mjs +30 -1
- package/bin/lib/copy.mjs +279 -6
- package/bin/lib/db.mjs +2 -0
- package/docs/adr/0004-page-content-state-is-composed.md +39 -5
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +9 -3
- package/docs/adr/0009-page-title-does-not-carry-a-counter.md +57 -0
- package/docs/adr/0010-page-header-owns-page-chrome.md +73 -0
- package/docs/code-style.md +4 -1
- package/docs/data-layer.md +9 -0
- package/package.json +1 -1
- package/registry/instructions/opus.md +3 -3
- package/registry/skills/build-opus-ui/SKILL.md +3 -2
- package/registry/skills/build-opus-ui/references/ui-patterns.md +17 -6
- package/registry/templates/app/src/App.tsx +11 -6
- package/src/core/types.ts +3 -4
- package/src/ui/components/patterns/action-list-dialog.tsx +10 -3
- package/src/ui/components/patterns/confirm.tsx +2 -31
- package/src/ui/components/patterns/content-header.tsx +42 -141
- package/src/ui/components/patterns/data-state.tsx +42 -68
- package/src/ui/components/patterns/form.tsx +15 -15
- package/src/ui/components/patterns/list.tsx +55 -34
- package/src/ui/components/patterns/page-state.tsx +81 -51
- package/src/ui/components/patterns/page.tsx +228 -97
- package/src/ui/components/patterns/state-surface.tsx +262 -0
- package/src/ui/components/patterns/surface-header.tsx +204 -0
- package/src/ui/components/patterns/trigger.tsx +9 -10
- package/src/ui/components/patterns/view.tsx +14 -16
- package/src/ui/components/primitives/alert.tsx +1 -33
- package/src/ui/components/primitives/avatar.tsx +15 -5
- package/src/ui/components/primitives/badge.tsx +2 -43
- package/src/ui/components/primitives/button-group.tsx +34 -8
- package/src/ui/components/primitives/button.tsx +31 -35
- package/src/ui/components/primitives/control.ts +69 -0
- package/src/ui/components/primitives/dot.tsx +1 -30
- package/src/ui/components/primitives/input-group.tsx +11 -8
- package/src/ui/components/primitives/item.tsx +3 -1
- package/src/ui/components/primitives/menu.tsx +1 -7
- package/src/ui/components/primitives/pagination.tsx +16 -8
- package/src/ui/components/primitives/select.tsx +2 -2
- package/src/ui/components/primitives/spinner.tsx +13 -16
- package/src/ui/components/primitives/switch.tsx +4 -1
- package/src/ui/components/primitives/tabs.tsx +5 -3
- package/src/ui/components/primitives/toggle.tsx +9 -4
- package/src/ui/docs/content/action-form.md +26 -0
- package/src/ui/docs/content/action-list-dialog.md +2 -2
- package/src/ui/docs/content/action-list.md +35 -2
- package/src/ui/docs/content/action-trigger.md +5 -4
- package/src/ui/docs/content/action-view.md +3 -2
- package/src/ui/docs/content/alert.md +16 -4
- package/src/ui/docs/content/avatar.md +7 -3
- package/src/ui/docs/content/button.md +48 -17
- package/src/ui/docs/content/communication.md +36 -0
- package/src/ui/docs/content/content.md +5 -4
- package/src/ui/docs/content/data-state.md +17 -13
- package/src/ui/docs/content/dialog.md +1 -4
- package/src/ui/docs/content/input.md +1 -1
- package/src/ui/docs/content/item.md +1 -1
- package/src/ui/docs/content/page.md +160 -37
- package/src/ui/docs/content/pagination.md +11 -9
- package/src/ui/docs/content/semantic-context.md +3 -2
- package/src/ui/docs/content/sidebar.md +2 -42
- package/src/ui/docs/content/spinner.md +9 -6
- package/src/ui/docs/content/switch.md +1 -1
- package/src/ui/docs/content/tabs.md +1 -1
- package/src/ui/docs/content/toggle.md +1 -1
- package/src/ui/drivers/react.tsx +1 -6
- package/src/ui/meta.ts +8 -8
- package/src/ui/react.tsx +16 -18
- package/src/ui/components/patterns/shell-nav.tsx +0 -154
|
@@ -7,6 +7,9 @@ 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 ações do consumidor ao fim da barra. Recarregar e exibição formam um
|
|
11
|
+
`ButtonGroup` espaçado; as ações do consumidor vêm depois, com um intervalo maior para preservar a
|
|
12
|
+
hierarquia entre ferramentas e a ação principal.
|
|
10
13
|
|
|
11
14
|
```tsx preview col
|
|
12
15
|
render(
|
|
@@ -18,6 +21,20 @@ render(
|
|
|
18
21
|
)
|
|
19
22
|
```
|
|
20
23
|
|
|
24
|
+
Uma ação icon-only mantém o nome no tooltip e no `aria-label`:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
<ActionList
|
|
28
|
+
action={workspaceList}
|
|
29
|
+
input={{}}
|
|
30
|
+
toolbarActions={
|
|
31
|
+
<Button context="primary" size="icon" aria-label="Novo workspace">
|
|
32
|
+
<Plus />
|
|
33
|
+
</Button>
|
|
34
|
+
}
|
|
35
|
+
/>
|
|
36
|
+
```
|
|
37
|
+
|
|
21
38
|
## Barra de filtros compartilhada
|
|
22
39
|
|
|
23
40
|
`ActionFilterBar` expõe a mesma linguagem declarativa de busca, filtros e período para
|
|
@@ -235,6 +252,19 @@ render(
|
|
|
235
252
|
)
|
|
236
253
|
```
|
|
237
254
|
|
|
255
|
+
## Propriedades de ActionFilterBar
|
|
256
|
+
|
|
257
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
258
|
+
|---|---|---|---|
|
|
259
|
+
| `action` | `Pick<ListAction, 'filters' \| 'text' \| 'periods'>` | | Declara busca, filtros e períodos disponíveis. |
|
|
260
|
+
| `state` | `ActionFilterState` | | Estado atual da barra. |
|
|
261
|
+
| `onStateChange` | `(next) => void` | | Recebe o estado completo depois de cada alteração. |
|
|
262
|
+
| `filterOptions` | `Record<string, SelectOption[]>` | | Fornece opções de runtime para filtros select e lookup. |
|
|
263
|
+
| `onRefresh` | `() => Promise<void> \| void` | | Exibe a ação de recarregar e executa a consulta do consumidor. |
|
|
264
|
+
| `refreshing` | `boolean` | `false` | Desabilita e anima a ação de recarregar durante a consulta. |
|
|
265
|
+
| `controls` | `ReactNode` | | Controles auxiliares agrupados com recarregar em um `ButtonGroup` espaçado. |
|
|
266
|
+
| `actions` | `ReactNode` | | Ações do consumidor exibidas depois do grupo de controles, com um intervalo maior. |
|
|
267
|
+
|
|
238
268
|
## Declaração na ListAction
|
|
239
269
|
|
|
240
270
|
| Chave | O que declara |
|
|
@@ -267,6 +297,9 @@ chamar a action.
|
|
|
267
297
|
| `rowId` | `(item) => string` | `item.id` | Identidade da linha para seleção. |
|
|
268
298
|
| `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. |
|
|
269
299
|
| `state / onStateChange` | `ActionListState / (next) => void` | interno | Estado da toolbar controlado — para quem embala sincronizar com a URL. |
|
|
270
|
-
| `emptyMessage` | `string` | `'Nenhum resultado.'` |
|
|
300
|
+
| `emptyMessage` | `string` | `'Nenhum resultado.'` | Frase do estado vazio (o `Empty` do `DataState`). |
|
|
301
|
+
| `errorMessage` | `string` | `'Não foi possível carregar.'` | Título do aviso de erro; a ação de tentar de novo refaz a consulta. |
|
|
302
|
+
| `retryLabel` | `string` | `'Tentar de novo'` | Nome acessível e tooltip da ação de recuperação. |
|
|
271
303
|
| `onRowClick` | `(item: TItem) => void` | | Clique na linha (ex.: navegar para o detalhe) — só na tabela. |
|
|
272
|
-
| `
|
|
304
|
+
| `toolbarActions` | `ReactNode` | | Ações do consumidor no fim da barra, separadas do grupo de controles. |
|
|
305
|
+
| `rowActions` | `(item: TItem) => ReactNode` | | Ações por linha (coluna final, à direita), agrupadas automaticamente com intervalo compacto; cliques ali não disparam o `onRowClick`. |
|
|
@@ -37,9 +37,10 @@ confirm: {
|
|
|
37
37
|
|
|
38
38
|
## Ação somente com ícone
|
|
39
39
|
|
|
40
|
-
Com `icon`, o botão exibe somente o ícone
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
Com `icon`, o botão exibe somente o ícone no quadrado `icon-xs` da escala (1.5rem, a ação que mora
|
|
41
|
+
dentro de uma linha ou card) e usa `label`, ou `action.label`, no tooltip e no nome acessível. Uma
|
|
42
|
+
composição que peça mais presença, como a barra do `PageHeader`, declara `size="icon-sm"`. O clique
|
|
43
|
+
não aciona o item clicável ao redor. `itemLabel` identifica o registro na mensagem de confirmação.
|
|
43
44
|
|
|
44
45
|
```tsx
|
|
45
46
|
<ActionTrigger
|
|
@@ -83,4 +84,4 @@ um atalho, um arrastar, um item de menu.
|
|
|
83
84
|
| `onSuccess` | `(data: TData) => void` | | Pós-sucesso (cache já invalidado pelo action.invalidates). |
|
|
84
85
|
| `icon` | `React.ReactNode` | | Torna o botão icon-only: rótulo no tooltip e no `aria-label`, clique que não vaza para o item. |
|
|
85
86
|
| `itemLabel` | `string` | | Nome do alvo na pergunta (sai entre aspas, em destaque, antes da mensagem do contrato). |
|
|
86
|
-
| `className` | `string` | | Classes do botão (
|
|
87
|
+
| `className` | `string` | | Classes do botão. O tamanho vem de `size` (escala única); com `icon`, o default é `icon-xs`. |
|
|
@@ -21,7 +21,7 @@ render(
|
|
|
21
21
|
<div className="space-y-1.5">
|
|
22
22
|
<div className="flex items-center gap-2">
|
|
23
23
|
<h3 className="text-sm font-semibold">{ws.name}</h3>
|
|
24
|
-
<Badge
|
|
24
|
+
<Badge context={ws.status === 'active' ? 'success' : 'warning'}>{ws.status}</Badge>
|
|
25
25
|
</div>
|
|
26
26
|
<p className="text-sm text-muted-foreground">
|
|
27
27
|
Cliente {ws.client} · {ws.agents} agentes vinculados.
|
|
@@ -50,5 +50,6 @@ mais de uma região da tela ou quando o carregamento precisa ser orquestrado por
|
|
|
50
50
|
| `children` | `(data: TData, refetch) => ReactNode` | | Conteúdo apresentado quando os dados estão disponíveis. `refetch` permite recarregar por código. |
|
|
51
51
|
| `render` | `(data: TData, refetch) => ReactNode` | | Alias de compatibilidade de `children`; `children` tem precedência. |
|
|
52
52
|
| `loading` | `ReactNode \| boolean` | `3 skeletons` | Sobrescreve o carregamento: um nó próprio, `true` para o padrão ou `false` para não renderizar nada enquanto carrega. |
|
|
53
|
-
| `empty` | `ReactNode` | `
|
|
53
|
+
| `empty` | `ReactNode` | `emptyMessage` ou nada | Sobrescreve o estado vazio (200 sem dado). |
|
|
54
|
+
| `emptyMessage` | `string` | | Atalho do vazio: a frase na mesma superfície de `DataState` (`Empty` com moldura sólida). |
|
|
54
55
|
| `error` | `(err, retry) => ReactNode` | `"Não foi possível carregar" + Tentar de novo` | Sobrescreve o estado de erro padrão. Sem ele, a frase do servidor só aparece quando é legível pela pessoa (`conflict`, `validation`, `not_found`, `authorization`, `authentication`); código técnico nunca vira título, e o botão de tentar de novo some quando o problema é de permissão ou sessão. |
|
|
@@ -54,6 +54,11 @@ Quando a mensagem precisa de conteúdo rico, componha os slots da família. `Ale
|
|
|
54
54
|
`AlertDescription` pertencem ao header. As ações ficam no fim lógico da superfície e centralizadas
|
|
55
55
|
verticalmente na mesma linha do conteúdo — a mesma posição usada pelas ações do Toast.
|
|
56
56
|
|
|
57
|
+
Use `ghost` como tratamento padrão para ações dentro do alert: a mensagem já fornece a superfície e
|
|
58
|
+
o botão não precisa desenhar outra moldura. Mantenha texto quando a ação expressa um resultado
|
|
59
|
+
específico. Para tentar novamente, recarregar ou repetir a operação descrita pelo aviso, prefira um
|
|
60
|
+
botão só com ícone, nome acessível e tooltip.
|
|
61
|
+
|
|
57
62
|
```tsx preview col
|
|
58
63
|
<Alert context="danger">
|
|
59
64
|
<AlertMedia>
|
|
@@ -67,9 +72,16 @@ verticalmente na mesma linha do conteúdo — a mesma posição usada pelas aç
|
|
|
67
72
|
</AlertDescription>
|
|
68
73
|
</AlertHeader>
|
|
69
74
|
<AlertActions>
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
<TooltipProvider>
|
|
76
|
+
<Tooltip>
|
|
77
|
+
<TooltipTrigger asChild>
|
|
78
|
+
<Button variant="ghost" size="icon" aria-label="Tentar novamente">
|
|
79
|
+
<RefreshCw />
|
|
80
|
+
</Button>
|
|
81
|
+
</TooltipTrigger>
|
|
82
|
+
<TooltipContent>Tentar novamente</TooltipContent>
|
|
83
|
+
</Tooltip>
|
|
84
|
+
</TooltipProvider>
|
|
73
85
|
</AlertActions>
|
|
74
86
|
</Alert>
|
|
75
87
|
```
|
|
@@ -83,7 +95,7 @@ ação.
|
|
|
83
95
|
title="Sessão presa"
|
|
84
96
|
description="O ambiente não subiu no tempo esperado."
|
|
85
97
|
>
|
|
86
|
-
<Button
|
|
98
|
+
<Button variant="ghost">
|
|
87
99
|
Reiniciar
|
|
88
100
|
</Button>
|
|
89
101
|
</Alert>
|
|
@@ -28,10 +28,14 @@ para agente.
|
|
|
28
28
|
|
|
29
29
|
## Tamanhos
|
|
30
30
|
|
|
31
|
-
size
|
|
32
|
-
|
|
31
|
+
`size` usa a escala única dos controles: o avatar de uma linha mede o mesmo que o controle ao lado
|
|
32
|
+
(`sm` 2rem, `default` 2.25rem, `lg` 2.5rem); `xs` (1.5rem) é a lista densa. O fallback e o selo
|
|
33
|
+
acompanham o tamanho.
|
|
33
34
|
|
|
34
35
|
```tsx preview
|
|
36
|
+
<Avatar size="xs">
|
|
37
|
+
<AvatarFallback>AL</AvatarFallback>
|
|
38
|
+
</Avatar>
|
|
35
39
|
<Avatar size="sm">
|
|
36
40
|
<AvatarFallback>AL</AvatarFallback>
|
|
37
41
|
</Avatar>
|
|
@@ -86,7 +90,7 @@ excedente — os membros do workspace Empresa X.
|
|
|
86
90
|
|
|
87
91
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
88
92
|
|---|---|---|---|
|
|
89
|
-
| `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | Diâmetro do retrato; o fallback e o selo acompanham
|
|
93
|
+
| `size` | `'xs' \| 'sm' \| 'default' \| 'lg'` | `'default'` | Diâmetro do retrato na escala única dos controles (1.5 · 2 · 2.25 · 2.5rem); o fallback e o selo acompanham. |
|
|
90
94
|
|
|
91
95
|
## Propriedades de AvatarImage
|
|
92
96
|
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`context` declara a hierarquia ou o risco da ação; `variant` escolhe o tratamento visual. Use
|
|
4
4
|
`primary` para a ação principal, `neutral` para ações de apoio e `danger` quando a ação tiver uma
|
|
5
|
-
consequência perigosa.
|
|
6
|
-
compatibilidade.
|
|
5
|
+
consequência perigosa.
|
|
7
6
|
|
|
8
7
|
```tsx preview
|
|
9
8
|
<Button>Criar workspace</Button>
|
|
@@ -16,28 +15,45 @@ compatibilidade.
|
|
|
16
15
|
|
|
17
16
|
## Tamanhos
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
`size` usa a escala única dos controles: o mesmo nome tem a mesma medida em `Button`, `Select`,
|
|
19
|
+
`Tabs`, `Toggle`, `Switch`, `Avatar`, `Spinner` e nos botões embutidos. Os tamanhos de texto dão a
|
|
20
|
+
altura da linha; os `icon-*` são quadrados para botões só de ícone, que exigem `aria-label` porque não
|
|
21
|
+
há texto visível. O glifo dentro do controle acompanha o tamanho (0.875rem em `xs` e `sm`, 1rem em
|
|
22
|
+
`default`, 1.25rem em `lg`), a menos que o ícone traga um `size-*` próprio.
|
|
23
|
+
|
|
24
|
+
| Nome | Medida | Uso |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `xs` | 1.5rem | Ação dentro de um campo (`InputGroupButton`). |
|
|
27
|
+
| `sm` | 2rem | Toolbar e cabeçalho densos, ao lado de `Select` e `Tabs` `sm`. |
|
|
28
|
+
| `default` | 2.25rem | A linha padrão, a mesma de `Input`. |
|
|
29
|
+
| `lg` | 2.5rem | Chamada principal com mais área de toque. |
|
|
30
|
+
| `icon-xs` | 1.5rem | Ação só de ícone dentro de um campo ou de uma linha densa; é o quadrado de `ActionTrigger` com `icon`. |
|
|
31
|
+
| `icon-sm` | 1.75rem | Ação só de ícone em composições compactas que precisam de mais presença; também é o quadrado das setas do pager de `ActionList`. |
|
|
32
|
+
| `icon` | 2.25rem | Ação só de ícone na linha padrão. |
|
|
33
|
+
| `icon-lg` | 2.5rem | Ação só de ícone ao lado de um `lg`. |
|
|
20
34
|
|
|
21
35
|
```tsx preview
|
|
36
|
+
<Button size="xs">Mínimo</Button>
|
|
22
37
|
<Button size="sm">Pequeno</Button>
|
|
23
38
|
<Button>Padrão</Button>
|
|
24
39
|
<Button size="lg">Grande</Button>
|
|
25
|
-
<Button size="icon" aria-label="Novo"><Plus /></Button>
|
|
26
|
-
<Button size="icon-sm" variant="ghost" aria-label="Novo"><Plus /></Button>
|
|
27
40
|
<Button size="icon-xs" variant="ghost" aria-label="Novo"><Plus /></Button>
|
|
41
|
+
<Button size="icon-sm" variant="ghost" aria-label="Novo"><Plus /></Button>
|
|
42
|
+
<Button size="icon" aria-label="Novo"><Plus /></Button>
|
|
43
|
+
<Button size="icon-lg" aria-label="Novo"><Plus /></Button>
|
|
28
44
|
```
|
|
29
45
|
|
|
30
46
|
## Estados
|
|
31
47
|
|
|
32
|
-
busy = ação em andamento (DEPOIS do clique): o Spinner e o disabled vêm do botão.
|
|
33
|
-
Spinner TROCA o ícone (não soma). Não
|
|
34
|
-
|
|
48
|
+
busy = ação em andamento (DEPOIS do clique): o Spinner e o disabled vêm do botão. `icon` recebe o
|
|
49
|
+
elemento do ícone e o dimensiona pelo `size`; com icon, o Spinner TROCA o ícone (não soma). Não
|
|
50
|
+
confunda com carregar conteúdo (ANTES) — isso é `DataState` ou `Skeleton` em um nível de página.
|
|
35
51
|
|
|
36
52
|
```tsx preview
|
|
37
53
|
<Button disabled>Desabilitado</Button>
|
|
38
54
|
<Button busy>Salvando…</Button>
|
|
39
|
-
<Button icon={Plus}>Novo</Button>
|
|
40
|
-
<Button icon={Plus} busy>Novo</Button>
|
|
55
|
+
<Button icon={<Plus />}>Novo</Button>
|
|
56
|
+
<Button icon={<Plus />} busy>Novo</Button>
|
|
41
57
|
```
|
|
42
58
|
|
|
43
59
|
## Como outro elemento (asChild)
|
|
@@ -57,16 +73,16 @@ buttonVariants serve para o caso sem filho único.
|
|
|
57
73
|
|---|---|---|---|
|
|
58
74
|
| `context` | `'neutral' \| 'primary' \| 'danger'` | `'primary'` | A hierarquia ou o risco comunicado pela ação. |
|
|
59
75
|
| `variant` | `'solid' \| 'subtle' \| 'outline' \| 'ghost' \| 'link'` | `'solid'` | O tratamento visual aplicado ao contexto. |
|
|
60
|
-
| `size` | `'
|
|
76
|
+
| `size` | `'xs' \| 'sm' \| 'default' \| 'lg' \| 'icon-xs' \| 'icon-sm' \| 'icon' \| 'icon-lg'` | `'default'` | A medida na escala única dos controles (tabela acima). Os `icon-*` são quadrados para botões só de ícone, com `aria-label`. |
|
|
61
77
|
| `asChild` | `boolean` | `false` | Renderiza como o filho (Radix Slot) em vez de `<button>` — para âncoras e afins. |
|
|
62
78
|
| `busy` | `boolean` | `false` | Ação em andamento (depois do clique): mostra Spinner + desabilita. Não é "carregando" de conteúdo (que é Spinner/Skeleton em um nível de página). |
|
|
63
|
-
| `icon` | `React.
|
|
79
|
+
| `icon` | `React.ReactNode` | | Ícone à esquerda, como nó (ex.: `icon={<Plus />}`); o glifo segue o `size`. No busy é trocado pelo Spinner — não soma. |
|
|
64
80
|
|
|
65
81
|
## ButtonGroup
|
|
66
82
|
|
|
67
|
-
Use `ButtonGroup`
|
|
68
|
-
colapsam e somente as pontas externas
|
|
69
|
-
para preservar a unidade visual.
|
|
83
|
+
Use `ButtonGroup` para manter ações relacionadas juntas. No modo `connected`, que permanece como
|
|
84
|
+
padrão, as bordas internas colapsam e somente as pontas externas ficam arredondadas. Mantenha a
|
|
85
|
+
mesma variante nos filhos para preservar a unidade visual.
|
|
70
86
|
|
|
71
87
|
```tsx preview
|
|
72
88
|
<ButtonGroup>
|
|
@@ -76,6 +92,20 @@ para preservar a unidade visual.
|
|
|
76
92
|
</ButtonGroup>
|
|
77
93
|
```
|
|
78
94
|
|
|
95
|
+
### Ações espaçadas
|
|
96
|
+
|
|
97
|
+
Use `mode="spaced"` quando os controles pertencem ao mesmo conjunto, mas cada um precisa preservar
|
|
98
|
+
sua própria forma e superfície de hover. O grupo aplica o intervalo compacto de `0.25rem` sem
|
|
99
|
+
conectar bordas. Esse modo atende ações icon-only em barras e linhas de listagem.
|
|
100
|
+
|
|
101
|
+
```tsx preview
|
|
102
|
+
<ButtonGroup mode="spaced">
|
|
103
|
+
<Button variant="ghost" size="icon" aria-label="Recarregar"><RotateCw /></Button>
|
|
104
|
+
<Button variant="ghost" size="icon" aria-label="Exibição"><SlidersHorizontal /></Button>
|
|
105
|
+
<Button context="primary" size="icon" aria-label="Novo item"><Plus /></Button>
|
|
106
|
+
</ButtonGroup>
|
|
107
|
+
```
|
|
108
|
+
|
|
79
109
|
### Ação dividida
|
|
80
110
|
|
|
81
111
|
Combine a ação principal, um separador e um botão de ícone quando o mesmo comando oferecer
|
|
@@ -83,7 +113,7 @@ variações.
|
|
|
83
113
|
|
|
84
114
|
```tsx preview
|
|
85
115
|
<ButtonGroup>
|
|
86
|
-
<Button icon={Play}>Rodar agente developer</Button>
|
|
116
|
+
<Button icon={<Play />}>Rodar agente developer</Button>
|
|
87
117
|
<ButtonGroupSeparator />
|
|
88
118
|
<Button size="icon" aria-label="Mais opções">
|
|
89
119
|
<ChevronDown />
|
|
@@ -102,7 +132,7 @@ semântica de outro elemento, como `label`.
|
|
|
102
132
|
<GitBranch />
|
|
103
133
|
empresa-x-api
|
|
104
134
|
</ButtonGroupText>
|
|
105
|
-
<Button variant="outline" icon={RotateCw}>Sincronizar</Button>
|
|
135
|
+
<Button variant="outline" icon={<RotateCw />}>Sincronizar</Button>
|
|
106
136
|
</ButtonGroup>
|
|
107
137
|
```
|
|
108
138
|
|
|
@@ -123,6 +153,7 @@ semântica de outro elemento, como `label`.
|
|
|
123
153
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
124
154
|
|---|---|---|---|
|
|
125
155
|
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção do bloco e do colapso das bordas. |
|
|
156
|
+
| `mode` | `'connected' \| 'spaced'` | `'connected'` | Conecta as bordas dos controles ou preserva cada controle com intervalo compacto. |
|
|
126
157
|
| `shape` | `'default' \| 'pill'` | `'default'` | Geometria das extremidades externas do grupo. |
|
|
127
158
|
|
|
128
159
|
### Propriedades de ButtonGroupSeparator
|
|
@@ -50,6 +50,42 @@ frase. Não copie as regras da Base para cá.
|
|
|
50
50
|
| placeholder de campo ou busca | `placeholder` |
|
|
51
51
|
| `Select.emptyText` e grupos de opção | `empty-state` e `heading` |
|
|
52
52
|
| confirmação local do `ActionTrigger` | `title`, `dialog-body` e `button` |
|
|
53
|
+
| `dialog.alert/confirm/prompt/choose` — `title`, `description`/`body`, `action`/`cancel`, `actions[].label` | `title`, `dialog-body`, `button` (e `placeholder` no `prompt`) |
|
|
54
|
+
| filhos de `TooltipContent` | `label` |
|
|
55
|
+
| `LabelHelp.help` | `helper-text` |
|
|
56
|
+
| `emptyMessage`, `errorMessage`, `retryLabel` de `DataState`, `PageState`, `ActionList`, `ActionListDialog` e `ActionView` | `empty-state`, `error`, `button` |
|
|
57
|
+
|
|
58
|
+
O tooltip entra como `label` porque nomeia um controle icon-only: é um fragmento curto, sem
|
|
59
|
+
ponto final, e a Base não define um papel próprio para tooltip. Texto de ajuda que precisa de
|
|
60
|
+
frase completa pertence ao `help` de um campo (contrato ou `LabelHelp`), não ao tooltip.
|
|
61
|
+
|
|
62
|
+
## Alcance do inventário além das props
|
|
63
|
+
|
|
64
|
+
Quem vê o gate reprovar ou precisa de uma dispensa deve saber o que o extrator alcança sem
|
|
65
|
+
declaração adicional:
|
|
66
|
+
|
|
67
|
+
- **Diálogos imperativos.** `dialog.confirm({ title, description, action })` e as demais
|
|
68
|
+
respostas (`alert`, `prompt`, `choose`) entram no inventário como se fossem props de um
|
|
69
|
+
`ActionFormDialog`; o campo do diagnóstico é `dialog.confirm().title`,
|
|
70
|
+
`dialog.choose().actions[0].label` etc. Constantes locais resolvem normalmente. Uma função
|
|
71
|
+
que devolve template (`description: describe(nome)`) gera o diagnóstico `content` habitual e
|
|
72
|
+
se declara em `copy.dynamic` como `message-template`. Opções construídas em tempo de execução
|
|
73
|
+
ou mutadas depois de declaradas reprovam como estrutura, porque nenhum texto pode ser
|
|
74
|
+
atribuído a elas.
|
|
75
|
+
- **`LabelHelp` como filho.** `<FieldLabel>Nome<LabelHelp help="…" /></FieldLabel>` inventaria
|
|
76
|
+
`Nome` como label e a ajuda como helper-text; o ícone não torna o label opaco. Um wrapper
|
|
77
|
+
local com o mesmo nome continua opaco, porque o extrator não infere o que ele renderiza.
|
|
78
|
+
- **Rótulos em array literal local.** Quando um componente mapeado lê `item.label` (ou a chave
|
|
79
|
+
destruturada) dentro de `map`, `forEach`, `filter` ou `for…of` sobre um array literal do
|
|
80
|
+
mesmo arquivo, cada literal do array vira um texto na linha em que foi escrito, em vez de um
|
|
81
|
+
diagnóstico sobre a prop. `filter`, `slice`, `toSorted` e `toReversed` podem ficar entre o
|
|
82
|
+
array e a iteração. O limite é deliberado: o array precisa ser literal e declarado no mesmo
|
|
83
|
+
arquivo; o elemento só pode ser lido (`item.chave`), nunca passado adiante, espalhado ou
|
|
84
|
+
atribuído; o callback não pode receber o próprio array como terceiro parâmetro; e nenhum
|
|
85
|
+
elemento pode ter a chave dinâmica. Arrays importados, derivados por spread ou por
|
|
86
|
+
concatenação continuam gerando o diagnóstico da prop.
|
|
87
|
+
- **Elementos JSX genéricos.** `<ActionList<Input, Row> emptyMessage="…">` extrai igual a
|
|
88
|
+
`<ActionList emptyMessage="…">`.
|
|
53
89
|
|
|
54
90
|
Copy visual e nome acessível são superfícies cumulativas. Um `aria-label` estático nomeia
|
|
55
91
|
um controle icon-only, mas não torna aceitável nem invisível ao gate um texto visual opaco.
|
|
@@ -22,7 +22,8 @@ render(
|
|
|
22
22
|
|
|
23
23
|
Use os slots quando o header precisar de composição própria. A árvore aceita exatamente um
|
|
24
24
|
`ContentHeader` e um `ContentBody` como filhos diretos. O header exige um `ContentTitle` e aceita
|
|
25
|
-
uma descrição, um
|
|
25
|
+
uma descrição, um contador (`ContentMeta`) e uma região de ações. O contador é próprio de
|
|
26
|
+
`Content`; `PageHeader` reconhece somente título, descrição e ações.
|
|
26
27
|
|
|
27
28
|
```tsx preview col
|
|
28
29
|
render(
|
|
@@ -48,12 +49,12 @@ do pai correto, filhos estruturais indiretos e a mistura de shorthand com compos
|
|
|
48
49
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
49
50
|
|---|---|---|---|
|
|
50
51
|
| `title` | `ReactNode` | | Forma curta: título da região (vira o heading ligado à `section`). |
|
|
51
|
-
| `
|
|
52
|
+
| `count` | `number` | | Forma curta: total de itens ao lado do título da região. |
|
|
52
53
|
| `description` | `ReactNode` | | Forma curta: frase de apoio sob o título. |
|
|
53
54
|
| `actions` | `ReactNode` | | Forma curta: ações alinhadas à direita do header. |
|
|
54
55
|
| `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | herdado | Nível semântico do heading, independente do destaque visual. |
|
|
55
|
-
| `variant` | `'page' \| 'section'` | `'section'` | Hierarquia visual
|
|
56
|
+
| `variant` | `'page' \| 'section'` | `'section'` | Hierarquia visual: `page` reproduz o cabeçalho de `Page`; `section` é a região dentro de uma superfície. |
|
|
56
57
|
|
|
57
58
|
Na composição explícita, `ContentHeader` recebe `ContentTitle`, `ContentMeta`, `ContentDescription` e
|
|
58
59
|
`ContentActions`, e `ContentBody` recebe o conteúdo; nenhum desses slots aceita `title` ou `level`
|
|
59
|
-
próprios — a hierarquia é declarada em `Content`.
|
|
60
|
+
próprios — a hierarquia é declarada em `Content`. `ContentHeader` não existe fora de `Content`.
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
## Estados
|
|
2
2
|
|
|
3
|
-
Use `DataState` para apresentar carregamento, erro, vazio e conteúdo de uma mesma consulta.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
`
|
|
3
|
+
Use `DataState` para apresentar carregamento, erro, vazio e conteúdo de uma mesma consulta. As
|
|
4
|
+
superfícies são as mesmas de `PageState`, na escala de uma seção: o carregamento é um contêiner
|
|
5
|
+
`role="status"` com o `Spinner`; o vazio compõe `Empty` com moldura sólida e `emptyMessage` como
|
|
6
|
+
título; o erro é um `Alert` de contexto `danger` com uma mensagem segura, sem expor detalhes
|
|
7
|
+
técnicos, e uma ação `ghost` só com ícone quando há `onRetry`. `retryLabel` fornece o nome acessível
|
|
8
|
+
e o tooltip dessa ação. Para uma ação em andamento depois do clique, use `busy` em `Button`.
|
|
7
9
|
|
|
8
10
|
```tsx preview col
|
|
9
11
|
<div className="w-full space-y-3">
|
|
10
12
|
<DataState loading>
|
|
11
13
|
<div />
|
|
12
14
|
</DataState>
|
|
13
|
-
<DataState empty
|
|
15
|
+
<DataState empty emptyMessage="Nenhum papel.">
|
|
14
16
|
<div />
|
|
15
17
|
</DataState>
|
|
16
|
-
<DataState error={{ message: 'detalhe técnico fica no console' }}>
|
|
18
|
+
<DataState error={{ message: 'detalhe técnico fica no console' }} onRetry={() => undefined}>
|
|
17
19
|
<div />
|
|
18
20
|
</DataState>
|
|
19
21
|
</div>
|
|
@@ -22,13 +24,13 @@ segura, sem expor detalhes técnicos. Para uma ação em andamento depois do cli
|
|
|
22
24
|
## Dentro de uma tabela
|
|
23
25
|
|
|
24
26
|
Em uma tabela já emoldurada, passe `colSpan` para ocupar uma linha inteira dentro de `<tbody>`. A
|
|
25
|
-
tabela continua responsável pela borda
|
|
26
|
-
|
|
27
|
+
tabela continua responsável pela borda: o `Empty` dentro dela vem sem moldura. Para uma região
|
|
28
|
+
disponível para criação ou vínculo, use `Empty` diretamente, com a moldura tracejada.
|
|
27
29
|
|
|
28
30
|
```tsx preview col
|
|
29
31
|
<table className="w-full overflow-hidden rounded-lg border border-border text-sm">
|
|
30
32
|
<tbody>
|
|
31
|
-
<DataState empty
|
|
33
|
+
<DataState empty emptyMessage="Nenhum usuário." colSpan={3}>
|
|
32
34
|
<tr>
|
|
33
35
|
<td />
|
|
34
36
|
</tr>
|
|
@@ -42,8 +44,10 @@ região disponível para criação ou vínculo, use `Empty`.
|
|
|
42
44
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
43
45
|
|---|---|---|---|
|
|
44
46
|
| `loading` | `boolean` | | Carregando (antes do conteúdo) — mostra o Spinner centralizado. |
|
|
45
|
-
| `empty` | `boolean` | | Sem itens —
|
|
46
|
-
| `
|
|
47
|
-
| `error` | `{ message?: string } \| null` | | Erro da carga — aviso calmo. A mensagem técnica não vai para tela (use
|
|
48
|
-
| `
|
|
47
|
+
| `empty` | `boolean` | | Sem itens — compõe `Empty` com o `emptyMessage`. |
|
|
48
|
+
| `emptyMessage` | `string` | `'Nada por aqui.'` | Frase do vazio (ex.: "Nenhum papel."). |
|
|
49
|
+
| `error` | `{ message?: string } \| null` | | Erro da carga — aviso calmo. A mensagem técnica não vai para tela (use `errorMessage`). |
|
|
50
|
+
| `errorMessage` | `string` | `'Não foi possível carregar.'` | Título do aviso de erro, orientado à pessoa. |
|
|
51
|
+
| `onRetry` | `() => void \| Promise<void>` | | Recuperação: mostra a ação de tentar de novo somente com ícone no estado de erro. |
|
|
52
|
+
| `retryLabel` | `string` | `'Tentar de novo'` | Nome acessível e tooltip da ação de recuperação. |
|
|
49
53
|
| `colSpan` | `number` | | Em tabela: renderiza o estado como `<tr><td colSpan>` (cabe direto no tbody). |
|
|
@@ -305,7 +305,7 @@ avançada; ele recebe `children` e `container` conforme o portal do Radix.
|
|
|
305
305
|
## Propriedades de DialogHost
|
|
306
306
|
|
|
307
307
|
`DialogHost` não recebe propriedades. Monte uma instância no shell para atender toda a API
|
|
308
|
-
imperativa.
|
|
308
|
+
imperativa.
|
|
309
309
|
|
|
310
310
|
## Opções de dialog.alert
|
|
311
311
|
|
|
@@ -363,6 +363,3 @@ imperativa. `ConfirmHost` permanece como alias de migração.
|
|
|
363
363
|
| `context` | `ButtonContext` | Última ação: `primary`; demais: `neutral` | Define o significado semântico. |
|
|
364
364
|
| `variant` | `ButtonVariant` | Última ação: `solid`; demais: `ghost` | Define o tratamento visual. |
|
|
365
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`.
|
|
@@ -166,7 +166,7 @@ Com `InputGroupTextarea`, um addon em `block-end` forma uma região de ações a
|
|
|
166
166
|
|
|
167
167
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
168
168
|
|---|---|---|---|
|
|
169
|
-
| `size` | `'xs' \| 'sm' \| 'icon-xs' \| 'icon-sm'` | `'xs'` |
|
|
169
|
+
| `size` | `'xs' \| 'sm' \| 'icon-xs' \| 'icon-sm'` | `'xs'` | O subconjunto da escala única que cabe num campo (1.5 · 2 · 1.5 · 1.75rem); os `icon-*` são quadrados. |
|
|
170
170
|
| `context` | `'neutral' \| 'primary' \| 'danger'` | `'primary'` | Contexto semântico herdado de Button. |
|
|
171
171
|
| `variant` | `'solid' \| 'subtle' \| 'outline' \| 'ghost' \| 'link'` | `'ghost'` | Tratamento visual; use `solid` quando a ação precisar de ênfase. |
|
|
172
172
|
|
|
@@ -105,7 +105,7 @@ usa `ItemHeader` ou `ItemBody` conforme o papel do conteúdo.
|
|
|
105
105
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
106
106
|
|---|---|---|---|
|
|
107
107
|
| `variant` | `'default' \| 'outline' \| 'muted'` | `'default'` | O fundo da linha: transparente, com borda ou levemente tingido. |
|
|
108
|
-
| `size` | `'default' \| 'sm'` | `'default'` | O respiro interno; `sm` atende listas densas. |
|
|
108
|
+
| `size` | `'default' \| 'sm'` | `'default'` | O respiro interno, com os nomes da escala única; `sm` atende listas densas. |
|
|
109
109
|
| `asChild` | `boolean` | `false` | Funde o Item no filho para que a linha inteira assuma sua semântica. |
|
|
110
110
|
|
|
111
111
|
## Propriedades de ItemGroup
|