@softize/opus 18.5.0 → 19.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 +82 -0
- package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +293 -0
- package/package.json +1 -1
- package/src/ui/components/patterns/content-header.tsx +1 -9
- package/src/ui/components/patterns/heading-level.ts +16 -0
- package/src/ui/components/patterns/list.tsx +34 -15
- package/src/ui/components/patterns/pane.tsx +294 -0
- package/src/ui/components/patterns/presentation.tsx +29 -17
- package/src/ui/components/patterns/sidebar.tsx +13 -72
- package/src/ui/components/patterns/split.tsx +8 -35
- package/src/ui/components/patterns/surface-anatomy.ts +219 -0
- package/src/ui/components/patterns/surface-header.tsx +51 -6
- package/src/ui/components/patterns/surface-media.tsx +69 -0
- package/src/ui/components/primitives/card.tsx +91 -29
- package/src/ui/components/primitives/command.tsx +12 -4
- package/src/ui/components/primitives/detail.tsx +10 -13
- package/src/ui/components/primitives/dialog.tsx +138 -35
- package/src/ui/components/primitives/drawer.tsx +122 -23
- package/src/ui/components/primitives/metric-card.tsx +98 -39
- package/src/ui/docs/content/card.md +221 -30
- package/src/ui/docs/content/content.md +6 -0
- package/src/ui/docs/content/dialog.md +115 -7
- package/src/ui/docs/content/drawer.md +136 -9
- package/src/ui/docs/content/metric-card.md +57 -18
- package/src/ui/docs/content/page.md +8 -0
- package/src/ui/docs/content/pane.md +250 -0
- package/src/ui/docs/content/sidebar.md +22 -43
- package/src/ui/docs/content/skeleton.md +7 -3
- package/src/ui/docs/content/split.md +16 -21
- package/src/ui/docs/registry.tsx +8 -2
- package/src/ui/meta.ts +8 -2
- package/src/ui/react.tsx +14 -0
|
@@ -42,7 +42,25 @@ interface MetricCardBaseProps extends Omit<React.ComponentProps<typeof Card>, 'c
|
|
|
42
42
|
secondaryMetric?: MetricCardSecondaryMetric
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
type MetricCardActivationProps =
|
|
46
|
+
| {
|
|
47
|
+
/** Indica se a medida controla o recorte atualmente aplicado. */
|
|
48
|
+
active: boolean
|
|
49
|
+
/** Ativa a medida como recorte ou seleção. */
|
|
50
|
+
onActivate: () => void
|
|
51
|
+
/** Nome acessível da ativação, como “Filtrar por Agendados”. */
|
|
52
|
+
activationLabel: string
|
|
53
|
+
/** Use `onActivate`; o clique genérico conflita com os controles internos do card. */
|
|
54
|
+
onClick?: never
|
|
55
|
+
}
|
|
56
|
+
| {
|
|
57
|
+
active?: never
|
|
58
|
+
onActivate?: never
|
|
59
|
+
activationLabel?: never
|
|
60
|
+
}
|
|
61
|
+
|
|
45
62
|
export type MetricCardProps = MetricCardBaseProps &
|
|
63
|
+
MetricCardActivationProps &
|
|
46
64
|
(
|
|
47
65
|
| {
|
|
48
66
|
/** Nome curto da medida apresentada. */
|
|
@@ -75,6 +93,9 @@ export function MetricCard({
|
|
|
75
93
|
tone,
|
|
76
94
|
action,
|
|
77
95
|
secondaryMetric,
|
|
96
|
+
active,
|
|
97
|
+
onActivate,
|
|
98
|
+
activationLabel,
|
|
78
99
|
loading = false,
|
|
79
100
|
className,
|
|
80
101
|
...props
|
|
@@ -84,6 +105,7 @@ export function MetricCard({
|
|
|
84
105
|
}
|
|
85
106
|
const hasIcon = icon !== undefined && icon !== null
|
|
86
107
|
const resolvedContext = context ?? (tone === 'warning' ? 'warning' : 'neutral')
|
|
108
|
+
const interactive = onActivate !== undefined
|
|
87
109
|
|
|
88
110
|
const primaryCard = (
|
|
89
111
|
<Card
|
|
@@ -92,9 +114,9 @@ export function MetricCard({
|
|
|
92
114
|
className={cn(
|
|
93
115
|
'min-w-0 p-4',
|
|
94
116
|
secondaryMetric !== undefined && 'relative z-10',
|
|
95
|
-
secondaryMetric === undefined && className,
|
|
117
|
+
secondaryMetric === undefined && !interactive && className,
|
|
96
118
|
)}
|
|
97
|
-
{...(secondaryMetric === undefined ? props : {})}
|
|
119
|
+
{...(secondaryMetric === undefined && !interactive ? props : {})}
|
|
98
120
|
>
|
|
99
121
|
{loading ? (
|
|
100
122
|
<div className="space-y-3">
|
|
@@ -106,7 +128,10 @@ export function MetricCard({
|
|
|
106
128
|
<>
|
|
107
129
|
<div className="flex items-start justify-between gap-3">
|
|
108
130
|
<div className="flex min-w-0 items-center gap-1.5">
|
|
109
|
-
<div
|
|
131
|
+
<div
|
|
132
|
+
data-slot="metric-card-label"
|
|
133
|
+
className="min-w-0 truncate text-sm font-medium text-muted-foreground"
|
|
134
|
+
>
|
|
110
135
|
{label}
|
|
111
136
|
</div>
|
|
112
137
|
{labelAction !== undefined && (
|
|
@@ -129,7 +154,10 @@ export function MetricCard({
|
|
|
129
154
|
</span>
|
|
130
155
|
)}
|
|
131
156
|
</div>
|
|
132
|
-
<div
|
|
157
|
+
<div
|
|
158
|
+
data-slot="metric-card-value"
|
|
159
|
+
className="mt-3 text-2xl font-semibold tracking-tight tabular-nums"
|
|
160
|
+
>
|
|
133
161
|
{value}
|
|
134
162
|
</div>
|
|
135
163
|
{description !== undefined && (
|
|
@@ -147,46 +175,77 @@ export function MetricCard({
|
|
|
147
175
|
</Card>
|
|
148
176
|
)
|
|
149
177
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
{primaryCard}
|
|
160
|
-
<Card
|
|
161
|
-
data-slot="metric-card-secondary"
|
|
162
|
-
className="relative z-0 -mt-8 flex w-full min-w-0 items-center gap-1.5 bg-[color-mix(in_oklab,var(--card)_88%,black)] px-4 pt-9 pb-1"
|
|
178
|
+
const metric =
|
|
179
|
+
secondaryMetric === undefined ? (
|
|
180
|
+
primaryCard
|
|
181
|
+
) : (
|
|
182
|
+
<div
|
|
183
|
+
{...(!interactive ? props : {})}
|
|
184
|
+
data-slot="metric-card-stack"
|
|
185
|
+
aria-busy={loading || undefined}
|
|
186
|
+
className={cn('relative isolate min-w-0', !interactive && className)}
|
|
163
187
|
>
|
|
164
|
-
{
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
</div>
|
|
174
|
-
<div className="flex min-w-0 items-center gap-1.5">
|
|
188
|
+
{primaryCard}
|
|
189
|
+
<Card
|
|
190
|
+
data-slot="metric-card-secondary"
|
|
191
|
+
className="relative z-0 -mt-8 flex w-full min-w-0 items-center gap-1.5 bg-[color-mix(in_oklab,var(--card)_88%,black)] px-4 pt-9 pb-1"
|
|
192
|
+
>
|
|
193
|
+
{loading ? (
|
|
194
|
+
<Skeleton className="h-5 w-full" />
|
|
195
|
+
) : (
|
|
196
|
+
<>
|
|
175
197
|
<div
|
|
176
|
-
data-slot="metric-card-secondary-
|
|
177
|
-
className="
|
|
198
|
+
data-slot="metric-card-secondary-value"
|
|
199
|
+
className="shrink-0 text-xs font-medium text-muted-foreground tabular-nums"
|
|
178
200
|
>
|
|
179
|
-
{secondaryMetric.
|
|
201
|
+
{secondaryMetric.value}
|
|
180
202
|
</div>
|
|
181
|
-
|
|
182
|
-
<div
|
|
183
|
-
|
|
203
|
+
<div className="flex min-w-0 items-center gap-1.5">
|
|
204
|
+
<div
|
|
205
|
+
data-slot="metric-card-secondary-label"
|
|
206
|
+
className="min-w-0 truncate text-xs font-medium text-muted-foreground"
|
|
207
|
+
>
|
|
208
|
+
{secondaryMetric.label}
|
|
184
209
|
</div>
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
210
|
+
{secondaryMetric.labelAction !== undefined && (
|
|
211
|
+
<div data-slot="metric-card-secondary-label-action" className="shrink-0">
|
|
212
|
+
{secondaryMetric.labelAction}
|
|
213
|
+
</div>
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
216
|
+
</>
|
|
217
|
+
)}
|
|
218
|
+
</Card>
|
|
219
|
+
</div>
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
if (!interactive) return metric
|
|
223
|
+
|
|
224
|
+
return (
|
|
225
|
+
<div
|
|
226
|
+
{...props}
|
|
227
|
+
data-slot="metric-card-activation"
|
|
228
|
+
data-active={active}
|
|
229
|
+
className={cn(
|
|
230
|
+
// Ativa = a borda na cor primária, e só. Anel somaria uma segunda linha de
|
|
231
|
+
// outra cor à borda e, pintado para fora, sumiria no primeiro ancestral com
|
|
232
|
+
// `overflow-hidden`.
|
|
233
|
+
'relative isolate min-w-0 data-[active=true]:[&_[data-slot=metric-card]]:border-primary',
|
|
234
|
+
className,
|
|
235
|
+
)}
|
|
236
|
+
>
|
|
237
|
+
<button
|
|
238
|
+
type="button"
|
|
239
|
+
data-slot="metric-card-activation-control"
|
|
240
|
+
className="peer absolute inset-0 z-0 cursor-pointer rounded-xl outline-none"
|
|
241
|
+
aria-label={activationLabel}
|
|
242
|
+
aria-pressed={active}
|
|
243
|
+
disabled={loading}
|
|
244
|
+
onClick={onActivate}
|
|
245
|
+
/>
|
|
246
|
+
<div className="pointer-events-none relative z-10 h-full [&>[data-slot=metric-card]]:h-full [&_[data-slot=metric-card]]:transition-colors [&_[data-slot=metric-card-action]]:pointer-events-auto [&_[data-slot=metric-card-label-action]]:pointer-events-auto [&_[data-slot=metric-card-secondary-label-action]]:pointer-events-auto peer-hover:[&_[data-slot=metric-card]]:border-foreground/40 peer-focus-visible:[&_[data-slot=metric-card]]:ring-2 peer-focus-visible:[&_[data-slot=metric-card]]:ring-inset peer-focus-visible:[&_[data-slot=metric-card]]:ring-ring">
|
|
247
|
+
{metric}
|
|
248
|
+
</div>
|
|
190
249
|
</div>
|
|
191
250
|
)
|
|
192
251
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Caixa simples
|
|
2
2
|
|
|
3
|
-
O uso mais comum
|
|
3
|
+
O uso mais comum é a superfície em repouso: raio, borda e fundo, sem sombra. O `Card` não impõe
|
|
4
|
+
flex, gap nem padding, então o miolo continua seu — inclusive o `space-y` ou o grid que a tela já
|
|
5
|
+
usa. Nesse formato o recuo vem do `className`.
|
|
4
6
|
|
|
5
7
|
```tsx preview col
|
|
6
8
|
<Card className="p-4">
|
|
@@ -8,9 +10,15 @@ O uso mais comum: a superfície de conteúdo em repouso (`rounded-xl` + `border`
|
|
|
8
10
|
</Card>
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
Quando a hierarquia realmente pedir elevação, declare a intensidade, por exemplo
|
|
14
|
+
`className="shadow-sm"`.
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
## Cabeçalho, corpo e rodapé
|
|
17
|
+
|
|
18
|
+
Quando o card tem estrutura, cada região vira um slot e traz o próprio recuo. `CardHeader`
|
|
19
|
+
reconhece quatro slots — `CardMedia`, `CardTitle`, `CardDescription` e `CardAction` — e monta a
|
|
20
|
+
linha sozinho: mídia, título e descrição de um lado, ações no extremo oposto. A ordem em que os
|
|
21
|
+
slots são escritos não altera a posição.
|
|
14
22
|
|
|
15
23
|
```tsx preview col
|
|
16
24
|
<Card>
|
|
@@ -21,55 +29,238 @@ Para painel com estrutura: cada slot é dono do próprio padding (como o Dialog)
|
|
|
21
29
|
<CardBody>
|
|
22
30
|
<p className="text-sm text-muted-foreground">Última sessão concluída há 2 horas.</p>
|
|
23
31
|
</CardBody>
|
|
24
|
-
<CardFooter
|
|
25
|
-
<Button size="sm">Abrir sessão</Button>
|
|
32
|
+
<CardFooter>
|
|
26
33
|
<Button size="sm" variant="outline">Ver repositórios</Button>
|
|
34
|
+
<Button size="sm">Abrir sessão</Button>
|
|
27
35
|
</CardFooter>
|
|
28
36
|
</Card>
|
|
29
37
|
```
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
O rodapé alinha as ações no fim da linha. Um nó que não seja um desses quatro slots dentro do
|
|
40
|
+
`CardHeader` interrompe a renderização com um erro: a linha do cabeçalho pertence à família, e um
|
|
41
|
+
`div` solto ali devolveria à tela a decisão de posicionamento. Conteúdo livre vai para `CardBody`.
|
|
42
|
+
|
|
43
|
+
## Escolher entre contínuo e seccionado
|
|
32
44
|
|
|
33
|
-
`
|
|
45
|
+
`layout` declara a anatomia da superfície inteira e fica no root. Não é uma borda ligada ou
|
|
46
|
+
desligada: cada valor é um pacote de recuo, ritmo vertical, escala do título e divisores.
|
|
47
|
+
|
|
48
|
+
| Aspecto | `continuous` (padrão) | `sectioned` |
|
|
49
|
+
| --- | --- | --- |
|
|
50
|
+
| Divisores | nenhum | entre as regiões vizinhas |
|
|
51
|
+
| Ritmo vertical | o cabeçalho se aproxima do corpo; o espaço separa | regiões simétricas; o divisor separa |
|
|
52
|
+
| Recuo lateral | mais folgado, para leitura | o mesmo das demais superfícies de operação |
|
|
53
|
+
| Escala do título | um degrau acima | um degrau mais contida |
|
|
54
|
+
|
|
55
|
+
Use `continuous` no card de leitura, que apresenta um assunto e termina. Use `sectioned` quando o
|
|
56
|
+
card for uma superfície de operação — regiões com pesos diferentes, lista dentro do corpo, rodapé
|
|
57
|
+
com ações — ou quando ele precisar conviver com um [`Dialog`](/ui/dialog), um
|
|
58
|
+
[`Drawer`](/ui/drawer) ou um [`Pane`](/ui/pane) sem degrau visível entre eles.
|
|
34
59
|
|
|
35
60
|
```tsx preview col
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
61
|
+
render(
|
|
62
|
+
<div className="grid gap-4 md:grid-cols-2">
|
|
63
|
+
<Card>
|
|
64
|
+
<CardHeader>
|
|
65
|
+
<CardTitle>Contínuo</CardTitle>
|
|
66
|
+
<CardDescription>O espaço separa as regiões.</CardDescription>
|
|
67
|
+
</CardHeader>
|
|
68
|
+
<CardBody>
|
|
69
|
+
<p className="text-sm text-muted-foreground">Um assunto, lido de cima a baixo.</p>
|
|
70
|
+
</CardBody>
|
|
71
|
+
<CardFooter>
|
|
72
|
+
<Button size="sm" variant="outline">Abrir</Button>
|
|
73
|
+
</CardFooter>
|
|
74
|
+
</Card>
|
|
75
|
+
<Card layout="sectioned">
|
|
76
|
+
<CardHeader>
|
|
77
|
+
<CardTitle>Seccionado</CardTitle>
|
|
78
|
+
<CardDescription>O divisor separa as regiões.</CardDescription>
|
|
79
|
+
</CardHeader>
|
|
80
|
+
<CardBody>
|
|
81
|
+
<p className="text-sm text-muted-foreground">Regiões com pesos diferentes.</p>
|
|
82
|
+
</CardBody>
|
|
83
|
+
<CardFooter>
|
|
84
|
+
<Button size="sm" variant="outline">Abrir</Button>
|
|
85
|
+
</CardFooter>
|
|
86
|
+
</Card>
|
|
87
|
+
</div>,
|
|
88
|
+
);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Os divisores são derivados
|
|
92
|
+
|
|
93
|
+
Em `sectioned`, cada região desenha a própria linha quando existe uma região vizinha do outro lado.
|
|
94
|
+
Um cabeçalho sozinho não fecha nada, um rodapé que é a única região também não, e um cabeçalho
|
|
95
|
+
seguido direto de um rodapé produz uma linha só, em vez de duas encostadas.
|
|
96
|
+
|
|
97
|
+
A tela não escreve mais `border-b` no cabeçalho nem `border-t` no rodapé. A regra depende da
|
|
98
|
+
presença das regiões irmãs, que o componente conhece e a tela não.
|
|
99
|
+
|
|
100
|
+
## Mídia e ações no cabeçalho
|
|
101
|
+
|
|
102
|
+
`CardMedia` abre a linha com a moldura de ícone da casa — o mesmo [`IconTile`](/ui/icon-tile), na
|
|
103
|
+
escala `sm`, com `context` e `variant` para declarar o significado da cor. `CardAction` fica no
|
|
104
|
+
extremo oposto ao título, sem precisar de classe de alinhamento.
|
|
105
|
+
|
|
106
|
+
```tsx preview col
|
|
107
|
+
<Card layout="sectioned">
|
|
108
|
+
<CardHeader>
|
|
109
|
+
<CardMedia context="primary">
|
|
110
|
+
<CarFront />
|
|
111
|
+
</CardMedia>
|
|
112
|
+
<CardTitle>Revisão dos 20.000 km</CardTitle>
|
|
113
|
+
<CardDescription>Agendada para 12 de maio, às 9h.</CardDescription>
|
|
114
|
+
<CardAction>
|
|
115
|
+
<Button size="sm" variant="outline">Reagendar</Button>
|
|
116
|
+
</CardAction>
|
|
40
117
|
</CardHeader>
|
|
41
118
|
<CardBody>
|
|
42
|
-
<p className="text-sm text-muted-foreground">
|
|
119
|
+
<p className="text-sm text-muted-foreground">Consultor responsável: Marina Alves.</p>
|
|
43
120
|
</CardBody>
|
|
44
|
-
<CardFooter className="justify-between border-t">
|
|
45
|
-
<Badge context="success">Publicada</Badge>
|
|
46
|
-
<Button size="sm" variant="outline">Editar skill</Button>
|
|
47
|
-
</CardFooter>
|
|
48
121
|
</Card>
|
|
49
122
|
```
|
|
50
123
|
|
|
51
|
-
##
|
|
124
|
+
## Hierarquia do título
|
|
52
125
|
|
|
53
|
-
`
|
|
54
|
-
|
|
126
|
+
`CardTitle` é um heading. `level` escolhe a hierarquia do documento, de `1` a `6`, com `3` como
|
|
127
|
+
padrão — um card costuma ser uma seção dentro de uma página que já tem o `h1`. O tamanho visual não
|
|
128
|
+
vem daí: ele é decidido pela família e pelo `layout`, então corrigir o nível para acertar a
|
|
129
|
+
estrutura do documento não muda a aparência.
|
|
55
130
|
|
|
56
131
|
```tsx preview col
|
|
57
132
|
<Card>
|
|
58
|
-
<CardHeader
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
<CardDescription>3 agentes vinculados.</CardDescription>
|
|
62
|
-
</div>
|
|
63
|
-
<CardAction>
|
|
64
|
-
<Button size="sm" variant="outline">Editar</Button>
|
|
65
|
-
</CardAction>
|
|
133
|
+
<CardHeader>
|
|
134
|
+
<CardTitle level={2}>Qualidade da base</CardTitle>
|
|
135
|
+
<CardDescription>O nível muda a semântica; a escala continua a do layout.</CardDescription>
|
|
66
136
|
</CardHeader>
|
|
67
137
|
</Card>
|
|
68
138
|
```
|
|
69
139
|
|
|
140
|
+
Use `asChild` quando o heading precisar vir de outro componente, como um link.
|
|
141
|
+
|
|
142
|
+
## A matriz de slots
|
|
143
|
+
|
|
144
|
+
As regiões são independentes: o cabeçalho existe sem corpo, o corpo existe sem cabeçalho e o rodapé
|
|
145
|
+
entra apenas quando há ações persistentes. O recuo e os divisores se ajustam a cada combinação, nos
|
|
146
|
+
dois layouts.
|
|
147
|
+
|
|
148
|
+
```tsx preview col
|
|
149
|
+
render(
|
|
150
|
+
<div className="grid gap-4 md:grid-cols-2">
|
|
151
|
+
<Card>
|
|
152
|
+
<CardHeader>
|
|
153
|
+
<CardMedia>
|
|
154
|
+
<UserRound />
|
|
155
|
+
</CardMedia>
|
|
156
|
+
<CardTitle>Contínuo com mídia</CardTitle>
|
|
157
|
+
</CardHeader>
|
|
158
|
+
<CardBody>
|
|
159
|
+
<p className="text-sm text-muted-foreground">
|
|
160
|
+
Sem rodapé, o corpo reassume o respiro de baixo.
|
|
161
|
+
</p>
|
|
162
|
+
</CardBody>
|
|
163
|
+
</Card>
|
|
164
|
+
<Card>
|
|
165
|
+
<CardBody>
|
|
166
|
+
<p className="text-sm text-muted-foreground">
|
|
167
|
+
Só corpo: o card vira uma caixa com o recuo da família.
|
|
168
|
+
</p>
|
|
169
|
+
</CardBody>
|
|
170
|
+
</Card>
|
|
171
|
+
<Card layout="sectioned">
|
|
172
|
+
<CardHeader>
|
|
173
|
+
<CardTitle>Seccionado sem corpo</CardTitle>
|
|
174
|
+
<CardAction>
|
|
175
|
+
<Button size="sm" variant="ghost">Editar</Button>
|
|
176
|
+
</CardAction>
|
|
177
|
+
</CardHeader>
|
|
178
|
+
<CardFooter>
|
|
179
|
+
<Button size="sm">Salvar</Button>
|
|
180
|
+
</CardFooter>
|
|
181
|
+
</Card>
|
|
182
|
+
<Card layout="sectioned">
|
|
183
|
+
<CardHeader>
|
|
184
|
+
<CardMedia context="success">
|
|
185
|
+
<ShieldCheck />
|
|
186
|
+
</CardMedia>
|
|
187
|
+
<CardTitle>Seccionado completo</CardTitle>
|
|
188
|
+
<CardDescription>Cabeçalho, corpo e rodapé.</CardDescription>
|
|
189
|
+
</CardHeader>
|
|
190
|
+
<CardBody>
|
|
191
|
+
<p className="text-sm text-muted-foreground">Cada fronteira ganha uma linha.</p>
|
|
192
|
+
</CardBody>
|
|
193
|
+
<CardFooter>
|
|
194
|
+
<Button size="sm" variant="outline">Descartar</Button>
|
|
195
|
+
<Button size="sm">Publicar</Button>
|
|
196
|
+
</CardFooter>
|
|
197
|
+
</Card>
|
|
198
|
+
</div>,
|
|
199
|
+
);
|
|
200
|
+
```
|
|
201
|
+
|
|
70
202
|
## Propriedades de Card
|
|
71
203
|
|
|
72
204
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
73
205
|
|---|---|---|---|
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
206
|
+
| `layout` | `'continuous' \| 'sectioned'` | `'continuous'` | Anatomia da superfície inteira; os slots leem esse valor e não o recebem. |
|
|
207
|
+
| `asChild` | `boolean` | `false` | Renderiza o filho com a superfície do Card, por exemplo um `<button>` clicável inteiro. |
|
|
208
|
+
| `className` | `string` | | Compõe sobre a superfície. Sem slots, é aqui que entra o recuo da caixa. |
|
|
209
|
+
|
|
210
|
+
As demais propriedades são as propriedades nativas de `div`.
|
|
211
|
+
|
|
212
|
+
## Propriedades de CardHeader
|
|
213
|
+
|
|
214
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
215
|
+
|---|---|---|---|
|
|
216
|
+
| `children` | `ReactNode` | | Somente `CardMedia`, `CardTitle`, `CardDescription` e `CardAction`, no máximo um de cada. |
|
|
217
|
+
| `className` | `string` | | Ajusta a faixa superior sobre o recuo do layout. |
|
|
218
|
+
|
|
219
|
+
## Propriedades de CardMedia
|
|
220
|
+
|
|
221
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
222
|
+
|---|---|---|---|
|
|
223
|
+
| `size` | `'xs' \| 'sm' \| 'default' \| 'lg'` | `'sm'` | Tamanho da moldura e do glifo na escala compartilhada dos controles. |
|
|
224
|
+
| `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | Declara o significado da cor. |
|
|
225
|
+
| `variant` | `'subtle' \| 'outline'` | `'subtle'` | Usa preenchimento tonal ou acrescenta borda. |
|
|
226
|
+
| `className` | `string` | | Ajusta a moldura sem substituir a escala. |
|
|
227
|
+
| `asChild` | `boolean` | `false` | Cede o elemento para uma marca com visual próprio — um orbe, um logotipo — que ocupa o slot sem ganhar a moldura por cima. |
|
|
228
|
+
|
|
229
|
+
## Propriedades de CardTitle
|
|
230
|
+
|
|
231
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
232
|
+
|---|---|---|---|
|
|
233
|
+
| `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `3` | Hierarquia do documento. O tamanho vem do `layout`, nunca do nível. |
|
|
234
|
+
| `asChild` | `boolean` | `false` | Usa o filho como heading, preservando a escala do título. |
|
|
235
|
+
| `className` | `string` | | Ajusta o título. |
|
|
236
|
+
|
|
237
|
+
## Propriedades de CardDescription
|
|
238
|
+
|
|
239
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
240
|
+
|---|---|---|---|
|
|
241
|
+
| `children` | `ReactNode` | | Frase de apoio sob o título, dentro do cabeçalho. |
|
|
242
|
+
| `className` | `string` | | Ajusta a descrição. |
|
|
243
|
+
|
|
244
|
+
## Propriedades de CardAction
|
|
245
|
+
|
|
246
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
247
|
+
|---|---|---|---|
|
|
248
|
+
| `children` | `ReactNode` | | Controles no extremo oposto ao título, como um botão ou um menu. |
|
|
249
|
+
| `className` | `string` | | Ajusta o agrupamento das ações. |
|
|
250
|
+
|
|
251
|
+
## Propriedades de CardBody
|
|
252
|
+
|
|
253
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
254
|
+
|---|---|---|---|
|
|
255
|
+
| `children` | `ReactNode` | | Conteúdo principal do card. |
|
|
256
|
+
| `className` | `string` | | Ajusta a região sobre o recuo do layout. |
|
|
257
|
+
|
|
258
|
+
`CardContent` continua exportado como alias depreciado de `CardBody` e sai em uma próxima versão
|
|
259
|
+
maior. Código novo usa `CardBody`.
|
|
260
|
+
|
|
261
|
+
## Propriedades de CardFooter
|
|
262
|
+
|
|
263
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
264
|
+
|---|---|---|---|
|
|
265
|
+
| `children` | `ReactNode` | | Ações persistentes, alinhadas no fim da linha. |
|
|
266
|
+
| `className` | `string` | | Ajusta o rodapé, inclusive a distribuição das ações. |
|
|
@@ -4,6 +4,12 @@ Use `Content` para dar título e estrutura a uma região dentro de `PageBody`, `
|
|
|
4
4
|
`DialogBody` ou outra superfície. Ele renderiza uma `section` ligada ao próprio título e mantém o
|
|
5
5
|
espaçamento entre header e body. `ContentHeader` pertence sempre a essa estrutura; não o use solto.
|
|
6
6
|
|
|
7
|
+
O `Content` é contínuo por definição e não expõe `layout`: ele é uma região nomeada dentro de outra
|
|
8
|
+
superfície, e a escolha entre regiões seccionadas e contínuas pertence a quem a hospeda — o
|
|
9
|
+
[`Card`](/ui/card), o [`Dialog`](/ui/dialog), o [`Drawer`](/ui/drawer) ou o [`Pane`](/ui/pane).
|
|
10
|
+
Quando a própria região precisar de uma fronteira visível, use `framed`, descrito adiante. Pelo
|
|
11
|
+
mesmo motivo o `Content` não tem rodapé: um rodapé pertence à superfície que a hospeda.
|
|
12
|
+
|
|
7
13
|
No caso comum, prefira o shorthand:
|
|
8
14
|
|
|
9
15
|
```tsx preview col
|
|
@@ -54,6 +54,91 @@ decide a distribuição nem a aparência dos botões.
|
|
|
54
54
|
`DialogClose` encerra o modal sem exigir estado controlado. Use `open` e `onOpenChange` quando outra
|
|
55
55
|
parte da interface também precisar controlar a abertura.
|
|
56
56
|
|
|
57
|
+
## Regiões separadas ou contínuas
|
|
58
|
+
|
|
59
|
+
`layout` declara a anatomia da superfície inteira e fica em `DialogContent`; os slots leem esse
|
|
60
|
+
valor e não o recebem. O padrão é `sectioned`: as regiões têm o mesmo recuo, o rodapé ganha uma
|
|
61
|
+
faixa sutil e as fronteiras entre regiões vizinhas ganham um divisor. É o modal de tarefa, com
|
|
62
|
+
cabeçalho, conteúdo e ações claramente separados.
|
|
63
|
+
|
|
64
|
+
Use `layout="continuous"` para uma superfície curta, em que o espaço basta para separar as regiões
|
|
65
|
+
e a linha seria um ruído — um aviso de uma frase, uma escolha rápida.
|
|
66
|
+
|
|
67
|
+
```tsx preview
|
|
68
|
+
<Dialog>
|
|
69
|
+
<DialogTrigger asChild>
|
|
70
|
+
<Button variant="outline">Publicar contrato</Button>
|
|
71
|
+
</DialogTrigger>
|
|
72
|
+
<DialogContent layout="continuous">
|
|
73
|
+
<DialogHeader>
|
|
74
|
+
<DialogTitle>Publicar contrato</DialogTitle>
|
|
75
|
+
</DialogHeader>
|
|
76
|
+
<DialogBody>
|
|
77
|
+
<p className="text-sm text-muted-foreground">
|
|
78
|
+
A versão publicada passa a valer para todos os consumidores.
|
|
79
|
+
</p>
|
|
80
|
+
</DialogBody>
|
|
81
|
+
<DialogFooter>
|
|
82
|
+
<ButtonGroup mode="spaced">
|
|
83
|
+
<DialogClose asChild>
|
|
84
|
+
<Button variant="ghost">Cancelar</Button>
|
|
85
|
+
</DialogClose>
|
|
86
|
+
<Button>Publicar</Button>
|
|
87
|
+
</ButtonGroup>
|
|
88
|
+
</DialogFooter>
|
|
89
|
+
</DialogContent>
|
|
90
|
+
</Dialog>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Os divisores do modo seccionado são derivados da vizinhança: cada região desenha a linha quando há
|
|
94
|
+
uma região do outro lado. Um cabeçalho sem corpo não fecha nada, e um cabeçalho seguido direto de um
|
|
95
|
+
rodapé produz uma linha só. A tela não escreve `border-b` nem `border-t`.
|
|
96
|
+
|
|
97
|
+
A faixa sutil do rodapé é a mesma no `Dialog` e no [`Drawer`](/ui/drawer): as duas famílias
|
|
98
|
+
compartilham a anatomia, e um fluxo que troca de superfície não muda de aparência no meio.
|
|
99
|
+
|
|
100
|
+
`mode="alert"` mantém a composição centrada da resposta obrigatória e não usa `layout`.
|
|
101
|
+
|
|
102
|
+
## Ações e navegação no cabeçalho
|
|
103
|
+
|
|
104
|
+
O cabeçalho reconhece quatro slots — `DialogNavigation`, `DialogMedia`, `DialogTitle` e
|
|
105
|
+
`DialogActions` — e posiciona cada um: a navegação abre a linha, o título ocupa a folga, as ações
|
|
106
|
+
vão para o extremo oposto e o botão de fechar entra depois delas. Um nó que não seja um desses
|
|
107
|
+
slots interrompe a renderização com um erro; contexto e conteúdo pertencem ao `DialogBody`.
|
|
108
|
+
|
|
109
|
+
Use `DialogActions` para uma ação da tela que precisa ficar à vista sem competir com o rodapé, como
|
|
110
|
+
abrir um assistente ou alternar um modo de visualização. Use `DialogNavigation` para o retorno de um
|
|
111
|
+
fluxo modal encadeado, em que a superfície permanece aberta e troca de etapa.
|
|
112
|
+
|
|
113
|
+
```tsx preview
|
|
114
|
+
<Dialog>
|
|
115
|
+
<DialogTrigger asChild>
|
|
116
|
+
<Button variant="outline">Revisar evento</Button>
|
|
117
|
+
</DialogTrigger>
|
|
118
|
+
<DialogContent>
|
|
119
|
+
<DialogHeader>
|
|
120
|
+
<DialogNavigation>
|
|
121
|
+
<Button variant="ghost" size="icon-sm" aria-label="Voltar para a lista">
|
|
122
|
+
<ArrowLeft />
|
|
123
|
+
</Button>
|
|
124
|
+
</DialogNavigation>
|
|
125
|
+
<DialogTitle>Evento 4821</DialogTitle>
|
|
126
|
+
<DialogActions>
|
|
127
|
+
<Button variant="ghost" size="icon-sm" aria-label="Mais ações">
|
|
128
|
+
<Ellipsis />
|
|
129
|
+
</Button>
|
|
130
|
+
</DialogActions>
|
|
131
|
+
</DialogHeader>
|
|
132
|
+
<DialogBody>
|
|
133
|
+
<p className="text-sm text-muted-foreground">Detalhe do evento selecionado.</p>
|
|
134
|
+
</DialogBody>
|
|
135
|
+
</DialogContent>
|
|
136
|
+
</Dialog>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
O cabeçalho não recebe descrição: um modal já é a superfície de uma tarefa, e o contexto que ajuda a
|
|
140
|
+
decidir aparece no começo do corpo, como texto ou `Alert`.
|
|
141
|
+
|
|
57
142
|
## Pedir uma resposta pela API imperativa
|
|
58
143
|
|
|
59
144
|
Use `dialog.alert`, `dialog.confirm`, `dialog.prompt` e `dialog.choose` quando a resposta couber em
|
|
@@ -280,6 +365,7 @@ botão. O texto informa o efeito real, como `Excluir`, `Revogar acesso` ou `Ence
|
|
|
280
365
|
| `showCloseButton` | `boolean` | `true` | No modo padrão, inclui a ação de fechamento no header; o modo de alerta exige uma resposta identificável. |
|
|
281
366
|
| `closeDisabled` | `boolean` | `false` | Mantém a ação visível e impede o fechamento enquanto a tarefa está bloqueada. |
|
|
282
367
|
| `closeSize` | `"xs" \| "sm"` | `"sm"` | Define o tamanho da ação circular de fechar. |
|
|
368
|
+
| `layout` | `'continuous' \| 'sectioned'` | `'sectioned'` | Anatomia da superfície inteira; os slots leem esse valor e não o recebem. Ignorado em `mode="alert"`. |
|
|
283
369
|
| `className` | `string` | | Ajusta a superfície. |
|
|
284
370
|
| `children` | `ReactNode` | | Cabeçalho, corpo, rodapé ou conteúdo próprio. |
|
|
285
371
|
|
|
@@ -287,8 +373,22 @@ botão. O texto informa o efeito real, como `Excluir`, `Revogar acesso` ou `Ence
|
|
|
287
373
|
|
|
288
374
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
289
375
|
| ----------- | ----------- | ------ | ---------------------------------------------------------- |
|
|
290
|
-
| `className` | `string` | | Ajusta a faixa superior.
|
|
291
|
-
| `children` | `ReactNode` | |
|
|
376
|
+
| `className` | `string` | | Ajusta a faixa superior sobre o recuo do layout. |
|
|
377
|
+
| `children` | `ReactNode` | | Somente `DialogNavigation`, `DialogMedia`, `DialogTitle` e `DialogActions`, no máximo um de cada; o fechamento padrão entra depois deles. |
|
|
378
|
+
|
|
379
|
+
## Propriedades de DialogNavigation
|
|
380
|
+
|
|
381
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
382
|
+
| ----------- | ----------- | ------ | -------------------------------------------------------------- |
|
|
383
|
+
| `className` | `string` | | Ajusta a região introdutória da linha. |
|
|
384
|
+
| `children` | `ReactNode` | | Retorno de um fluxo modal encadeado, antes do título. |
|
|
385
|
+
|
|
386
|
+
## Propriedades de DialogActions
|
|
387
|
+
|
|
388
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
389
|
+
| ----------- | ----------- | ------ | ------------------------------------------------------------------ |
|
|
390
|
+
| `className` | `string` | | Ajusta o agrupamento das ações. |
|
|
391
|
+
| `children` | `ReactNode` | | Ações da tela no cabeçalho, antes do botão de fechar. |
|
|
292
392
|
|
|
293
393
|
## Propriedades de DialogBody
|
|
294
394
|
|
|
@@ -308,15 +408,23 @@ botão. O texto informa o efeito real, como `Excluir`, `Revogar acesso` ou `Ence
|
|
|
308
408
|
|
|
309
409
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
310
410
|
| ----------- | ----------- | ------ | ------------------------------------ |
|
|
311
|
-
| `
|
|
411
|
+
| `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `h2` da primitiva | Hierarquia do documento. O tamanho vem do `layout`, nunca do nível. |
|
|
412
|
+
| `className` | `string` | | Ajusta o título, que trunca quando não cabe na linha. |
|
|
312
413
|
| `children` | `ReactNode` | | Nome da tarefa ou do conteúdo modal. |
|
|
313
414
|
|
|
314
415
|
## Propriedades de DialogMedia
|
|
315
416
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
|
417
|
+
A moldura vem do `IconTile`, como nas demais famílias. No `mode="alert"` ela cresce e centraliza:
|
|
418
|
+
ali a mídia não acompanha uma linha de título, ela é o que faz a pessoa reconhecer a resposta.
|
|
419
|
+
|
|
420
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
421
|
+
| ----------- | ----------------------------------------------------------- | --------- | -------------------------------------------------- |
|
|
422
|
+
| `size` | `'xs' \| 'sm' \| 'default' \| 'lg'` | `sm` | Escala da moldura; no alerta vale a moldura maior. |
|
|
423
|
+
| `context` | `UiContext` | `neutral` | Cor semântica da moldura. |
|
|
424
|
+
| `variant` | `'subtle' \| 'outline'` | `subtle` | Tratamento visual da moldura. |
|
|
425
|
+
| `className` | `string` | | Ajustes pontuais da moldura. |
|
|
426
|
+
| `children` | `ReactNode` | | Ícone que reforça o contexto da resposta. |
|
|
427
|
+
| `asChild` | `boolean` | `false` | Cede o elemento para uma marca com visual próprio — um orbe, um logotipo — que ocupa o slot sem ganhar a moldura por cima. |
|
|
320
428
|
|
|
321
429
|
## Propriedades de DialogClose
|
|
322
430
|
|