@softize/opus 18.5.0 → 20.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 +275 -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/0023-surfaces-share-one-anatomy-and-two-layouts.md +298 -0
- package/docs/adr/0024-fill-height-propagates-by-context.md +118 -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 +16 -12
- package/src/ui/components/patterns/fill-region.ts +150 -0
- package/src/ui/components/patterns/heading-level.ts +16 -0
- package/src/ui/components/patterns/list.tsx +103 -73
- package/src/ui/components/patterns/page.tsx +20 -4
- package/src/ui/components/patterns/pane.tsx +294 -0
- package/src/ui/components/patterns/presentation.tsx +81 -54
- package/src/ui/components/patterns/sidebar.tsx +13 -72
- package/src/ui/components/patterns/split.tsx +51 -38
- package/src/ui/components/patterns/surface-anatomy.ts +246 -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/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 +91 -29
- 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 +14 -5
- package/src/ui/components/primitives/copyable.tsx +120 -10
- package/src/ui/components/primitives/detail.tsx +10 -13
- package/src/ui/components/primitives/dialog.tsx +179 -45
- package/src/ui/components/primitives/drawer.tsx +131 -24
- 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/metric-card.tsx +98 -39
- 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 +134 -52
- 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 +252 -35
- 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 +37 -7
- 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 +219 -28
- 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 +193 -22
- 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 +28 -5
- 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 +68 -21
- package/src/ui/docs/content/observability.md +10 -9
- package/src/ui/docs/content/page.md +125 -11
- package/src/ui/docs/content/pagination.md +13 -5
- package/src/ui/docs/content/pane.md +300 -0
- 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 +25 -45
- package/src/ui/docs/content/skeleton.md +13 -4
- 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 +34 -20
- 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 +14 -2
- package/src/ui/drivers/react.tsx +16 -8
- package/src/ui/meta.ts +38 -32
- package/src/ui/react.tsx +14 -0
- package/src/vite/design.ts +16 -4
|
@@ -4,14 +4,40 @@ import { Slider as SliderPrimitive } from "radix-ui"
|
|
|
4
4
|
import { cn } from '../../lib/cn.ts'
|
|
5
5
|
import { focusRing } from './control.ts'
|
|
6
6
|
|
|
7
|
+
export interface SliderProps extends React.ComponentProps<typeof SliderPrimitive.Root> {
|
|
8
|
+
/**
|
|
9
|
+
* Nome de cada thumb, na ordem de `value`. Use em intervalos, em que cada controle precisa de
|
|
10
|
+
* um nome próprio (`['Custo mínimo', 'Custo máximo']`). Prevalece sobre `aria-label` e
|
|
11
|
+
* `aria-labelledby` no thumb correspondente.
|
|
12
|
+
*/
|
|
13
|
+
thumbLabels?: readonly string[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Quem recebe foco e anuncia o valor é o thumb (`role="slider"`), não a raiz. Por isso
|
|
17
|
+
// `aria-label` e `aria-labelledby` vão para os thumbs, como o Select faz com o campo interno;
|
|
18
|
+
// na raiz, sem papel, não nomeariam nada. Em intervalo, `thumbLabels` dá um nome a cada controle.
|
|
19
|
+
// Sem nome declarado, o Radix usaria "Minimum"/"Maximum": o fallback fica em português. Em
|
|
20
|
+
// intervalo sem `thumbLabels`, o nome comum se compõe com o fallback ("Preço, mínimo"), senão os
|
|
21
|
+
// dois thumbs teriam o mesmo nome; com `aria-labelledby`, o fallback entra por um span oculto
|
|
22
|
+
// referenciado junto (`hidden` sai da tela, mas continua valendo como rótulo referenciado).
|
|
23
|
+
function thumbFallback(index: number, total: number): string | undefined {
|
|
24
|
+
if (total > 2) return `Valor ${index + 1} de ${total}`
|
|
25
|
+
if (total === 2) return index === 0 ? 'Mínimo' : 'Máximo'
|
|
26
|
+
return undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
7
29
|
function Slider({
|
|
8
30
|
className,
|
|
9
31
|
defaultValue,
|
|
10
32
|
value,
|
|
11
33
|
min = 0,
|
|
12
34
|
max = 100,
|
|
35
|
+
thumbLabels,
|
|
36
|
+
'aria-label': ariaLabel,
|
|
37
|
+
'aria-labelledby': ariaLabelledby,
|
|
13
38
|
...props
|
|
14
|
-
}:
|
|
39
|
+
}: SliderProps) {
|
|
40
|
+
const fallbackIdBase = React.useId()
|
|
15
41
|
const _values = React.useMemo(
|
|
16
42
|
() =>
|
|
17
43
|
Array.isArray(value)
|
|
@@ -48,16 +74,41 @@ function Slider({
|
|
|
48
74
|
)}
|
|
49
75
|
/>
|
|
50
76
|
</SliderPrimitive.Track>
|
|
51
|
-
{Array.from({ length: _values.length }, (_, index) =>
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
77
|
+
{Array.from({ length: _values.length }, (_, index) => {
|
|
78
|
+
const thumbLabel = thumbLabels?.[index]
|
|
79
|
+
const fallback = thumbFallback(index, _values.length)
|
|
80
|
+
const fallbackId = `${fallbackIdBase}-thumb-${index}`
|
|
81
|
+
const composeLabelledby = thumbLabel === undefined && ariaLabelledby !== undefined && fallback !== undefined
|
|
82
|
+
const label =
|
|
83
|
+
thumbLabel ??
|
|
84
|
+
(ariaLabel !== undefined && fallback !== undefined
|
|
85
|
+
? `${ariaLabel}, ${fallback.toLowerCase()}`
|
|
86
|
+
: ariaLabel ?? fallback)
|
|
87
|
+
return (
|
|
88
|
+
<React.Fragment key={index}>
|
|
89
|
+
{composeLabelledby && (
|
|
90
|
+
<span id={fallbackId} hidden>
|
|
91
|
+
{fallback}
|
|
92
|
+
</span>
|
|
93
|
+
)}
|
|
94
|
+
<SliderPrimitive.Thumb
|
|
95
|
+
data-slot="slider-thumb"
|
|
96
|
+
aria-label={composeLabelledby ? undefined : label}
|
|
97
|
+
aria-labelledby={
|
|
98
|
+
thumbLabel !== undefined
|
|
99
|
+
? undefined
|
|
100
|
+
: composeLabelledby
|
|
101
|
+
? `${ariaLabelledby} ${fallbackId}`
|
|
102
|
+
: ariaLabelledby
|
|
103
|
+
}
|
|
104
|
+
className={cn(
|
|
105
|
+
"block size-4 shrink-0 rounded-full border border-primary bg-background shadow-sm transition-[color,box-shadow] hover:ring-[0.1875rem] hover:ring-ring/50 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50",
|
|
106
|
+
focusRing,
|
|
107
|
+
)}
|
|
108
|
+
/>
|
|
109
|
+
</React.Fragment>
|
|
110
|
+
)
|
|
111
|
+
})}
|
|
61
112
|
</SliderPrimitive.Root>
|
|
62
113
|
)
|
|
63
114
|
}
|
|
@@ -2,6 +2,7 @@ import * as React from 'react'
|
|
|
2
2
|
import { Tooltip, TooltipContent, TooltipTrigger } from './tooltip.tsx'
|
|
3
3
|
import { useOverflowing } from '../../lib/overflow.ts'
|
|
4
4
|
import { cn } from '../../lib/cn.ts'
|
|
5
|
+
import { focusRing } from './control.ts'
|
|
5
6
|
|
|
6
7
|
export interface TruncateProps extends React.ComponentProps<'span'> {
|
|
7
8
|
/** Conteúdo do tooltip quando transborda. Default: os próprios children. */
|
|
@@ -17,13 +18,21 @@ export interface TruncateProps extends React.ComponentProps<'span'> {
|
|
|
17
18
|
* Requer `<TooltipProvider>` na raiz do app (o esqueleto do `opus create` já monta).
|
|
18
19
|
* Com `fade`, a mesma medição decide quando esmaecer o fim da linha em vez de cortar
|
|
19
20
|
* em reticências — sem transbordo, nada é aplicado.
|
|
21
|
+
* Com o texto cortado, o span entra na ordem de tabulação para que o teclado alcance a dica;
|
|
22
|
+
* sem corte, fica fora dela, porque não há nada escondido para revelar.
|
|
20
23
|
*/
|
|
21
24
|
export function Truncate({ tooltip, fade = false, children, className, ...props }: TruncateProps): React.ReactElement {
|
|
22
25
|
const { ref, overflowing } = useOverflowing<HTMLSpanElement>(children)
|
|
23
26
|
const cut = fade ? (overflowing ? 'truncate-fade' : 'overflow-hidden whitespace-nowrap') : 'truncate'
|
|
24
27
|
|
|
25
28
|
const span = (
|
|
26
|
-
<span
|
|
29
|
+
<span
|
|
30
|
+
ref={ref}
|
|
31
|
+
data-slot="truncate"
|
|
32
|
+
tabIndex={overflowing ? 0 : undefined}
|
|
33
|
+
className={cn('block', cut, overflowing && ['rounded-sm outline-none', focusRing], className)}
|
|
34
|
+
{...props}
|
|
35
|
+
>
|
|
27
36
|
{children}
|
|
28
37
|
</span>
|
|
29
38
|
)
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
## Um painel por vez
|
|
2
2
|
|
|
3
|
+
Use `Accordion` para dividir um conteúdo longo em painéis que a pessoa expande sob demanda, cada um
|
|
4
|
+
com seu título. Para um único bloco recolhível, use [`Collapsible`](/ui/collapsible).
|
|
5
|
+
|
|
3
6
|
Use `type="single"` quando somente um painel deve permanecer aberto. Com `collapsible`, a pessoa
|
|
4
|
-
também pode fechar o painel atual; sem essa propriedade, um item
|
|
7
|
+
também pode fechar o painel atual; sem essa propriedade, depois que um item é aberto, sempre haverá
|
|
8
|
+
um item expandido.
|
|
5
9
|
`defaultValue` define o item inicialmente aberto no modo não controlado.
|
|
6
10
|
|
|
7
11
|
```tsx preview
|
|
@@ -9,41 +13,43 @@ também pode fechar o painel atual; sem essa propriedade, um item permanece expa
|
|
|
9
13
|
<AccordionItem value="overview">
|
|
10
14
|
<AccordionTrigger>Visão geral</AccordionTrigger>
|
|
11
15
|
<AccordionContent>
|
|
12
|
-
<p className="text-muted-foreground">
|
|
16
|
+
<p className="text-muted-foreground">Corolla XEi 2022, placa ABC-1D23.</p>
|
|
13
17
|
</AccordionContent>
|
|
14
18
|
</AccordionItem>
|
|
15
|
-
<AccordionItem value="
|
|
16
|
-
<AccordionTrigger>
|
|
19
|
+
<AccordionItem value="services">
|
|
20
|
+
<AccordionTrigger>Serviços</AccordionTrigger>
|
|
17
21
|
<AccordionContent>
|
|
18
|
-
<p className="text-muted-foreground">
|
|
22
|
+
<p className="text-muted-foreground">Troca de óleo, filtro de ar e alinhamento.</p>
|
|
19
23
|
</AccordionContent>
|
|
20
24
|
</AccordionItem>
|
|
21
|
-
<AccordionItem value="
|
|
22
|
-
<AccordionTrigger>
|
|
25
|
+
<AccordionItem value="parts">
|
|
26
|
+
<AccordionTrigger>Peças</AccordionTrigger>
|
|
23
27
|
<AccordionContent>
|
|
24
|
-
<p className="text-muted-foreground">
|
|
28
|
+
<p className="text-muted-foreground">Filtro de óleo e pastilhas de freio.</p>
|
|
25
29
|
</AccordionContent>
|
|
26
30
|
</AccordionItem>
|
|
27
31
|
</Accordion>
|
|
28
32
|
```
|
|
29
33
|
|
|
34
|
+
O `AccordionTrigger` já traz o ícone que gira conforme o estado; não acrescente outro.
|
|
35
|
+
|
|
30
36
|
## Múltiplos abertos
|
|
31
37
|
|
|
32
38
|
Use `type="multiple"` quando os painéis puderem permanecer abertos ao mesmo tempo. Nesse modo,
|
|
33
39
|
`defaultValue` recebe um array com os itens inicialmente expandidos.
|
|
34
40
|
|
|
35
41
|
```tsx preview
|
|
36
|
-
<Accordion type="multiple" defaultValue={['
|
|
37
|
-
<AccordionItem value="
|
|
38
|
-
<AccordionTrigger>
|
|
42
|
+
<Accordion type="multiple" defaultValue={['notes', 'parts']} className="w-full">
|
|
43
|
+
<AccordionItem value="notes">
|
|
44
|
+
<AccordionTrigger>Observações</AccordionTrigger>
|
|
39
45
|
<AccordionContent>
|
|
40
|
-
<p className="text-muted-foreground">O
|
|
46
|
+
<p className="text-muted-foreground">O cliente relatou ruído ao frear.</p>
|
|
41
47
|
</AccordionContent>
|
|
42
48
|
</AccordionItem>
|
|
43
|
-
<AccordionItem value="
|
|
44
|
-
<AccordionTrigger>
|
|
49
|
+
<AccordionItem value="parts">
|
|
50
|
+
<AccordionTrigger>Peças</AccordionTrigger>
|
|
45
51
|
<AccordionContent>
|
|
46
|
-
<p className="text-muted-foreground">
|
|
52
|
+
<p className="text-muted-foreground">Pastilhas de freio substituídas.</p>
|
|
47
53
|
</AccordionContent>
|
|
48
54
|
</AccordionItem>
|
|
49
55
|
</Accordion>
|
|
@@ -55,38 +61,79 @@ Use `value` e `onValueChange` quando outro elemento ou estado do aplicativo tamb
|
|
|
55
61
|
controlar o painel aberto.
|
|
56
62
|
|
|
57
63
|
```tsx preview
|
|
58
|
-
const [open, setOpen] = React.useState('
|
|
64
|
+
const [open, setOpen] = React.useState('diagnosis')
|
|
59
65
|
|
|
60
66
|
render(
|
|
61
67
|
<Accordion type="single" collapsible value={open} onValueChange={setOpen} className="w-full">
|
|
62
|
-
<AccordionItem value="
|
|
63
|
-
<AccordionTrigger>
|
|
68
|
+
<AccordionItem value="diagnosis">
|
|
69
|
+
<AccordionTrigger>Diagnóstico</AccordionTrigger>
|
|
64
70
|
<AccordionContent>
|
|
65
|
-
<p className="text-muted-foreground">
|
|
71
|
+
<p className="text-muted-foreground">Identifica a causa do ruído relatado pelo cliente.</p>
|
|
66
72
|
</AccordionContent>
|
|
67
73
|
</AccordionItem>
|
|
68
|
-
<AccordionItem value="
|
|
69
|
-
<AccordionTrigger>
|
|
74
|
+
<AccordionItem value="execution">
|
|
75
|
+
<AccordionTrigger>Execução</AccordionTrigger>
|
|
70
76
|
<AccordionContent>
|
|
71
|
-
<p className="text-muted-foreground">
|
|
77
|
+
<p className="text-muted-foreground">Substitui as peças aprovadas no orçamento.</p>
|
|
72
78
|
</AccordionContent>
|
|
73
79
|
</AccordionItem>
|
|
74
80
|
</Accordion>,
|
|
75
81
|
)
|
|
76
82
|
```
|
|
77
83
|
|
|
84
|
+
## Item indisponível
|
|
85
|
+
|
|
86
|
+
Use `disabled` no `AccordionItem` quando um painel existe, mas ainda não pode ser aberto. O gatilho
|
|
87
|
+
fica esmaecido e não responde ao clique nem ao teclado.
|
|
88
|
+
|
|
89
|
+
```tsx preview
|
|
90
|
+
<Accordion type="single" collapsible className="w-full">
|
|
91
|
+
<AccordionItem value="quote">
|
|
92
|
+
<AccordionTrigger>Orçamento</AccordionTrigger>
|
|
93
|
+
<AccordionContent>
|
|
94
|
+
<p className="text-muted-foreground">Aprovado pelo cliente em 12 de maio.</p>
|
|
95
|
+
</AccordionContent>
|
|
96
|
+
</AccordionItem>
|
|
97
|
+
<AccordionItem value="invoice" disabled>
|
|
98
|
+
<AccordionTrigger>Nota fiscal</AccordionTrigger>
|
|
99
|
+
<AccordionContent>
|
|
100
|
+
<p className="text-muted-foreground">Emitida depois da entrega do veículo.</p>
|
|
101
|
+
</AccordionContent>
|
|
102
|
+
</AccordionItem>
|
|
103
|
+
</Accordion>
|
|
104
|
+
```
|
|
105
|
+
|
|
78
106
|
## Propriedades de Accordion
|
|
79
107
|
|
|
80
108
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
81
109
|
|---|---|---|---|
|
|
82
|
-
| `type` | `'single' \| 'multiple'` | | `single` abre um painel por vez; `multiple` permite manter vários abertos. |
|
|
110
|
+
| `type` | `'single' \| 'multiple'` | | Obrigatório. `single` abre um painel por vez; `multiple` permite manter vários abertos. |
|
|
83
111
|
| `collapsible` | `boolean` | `false` | No modo `single`, permite fechar o item aberto. |
|
|
84
|
-
| `defaultValue` | `string
|
|
85
|
-
| `value` | `string
|
|
86
|
-
| `onValueChange` | `(value) => void` | | Chamado quando a pessoa abre ou fecha um item. |
|
|
112
|
+
| `defaultValue` | `string` em `single`; `string[]` em `multiple` | | Item ou itens inicialmente abertos no modo não controlado. |
|
|
113
|
+
| `value` | `string` em `single`; `string[]` em `multiple` | | Item ou itens abertos no modo controlado. Use com `onValueChange`. |
|
|
114
|
+
| `onValueChange` | `(value: string) => void` em `single`; `(value: string[]) => void` em `multiple` | | Chamado quando a pessoa abre ou fecha um item. Em `single`, recebe `''` quando o item aberto é fechado. |
|
|
115
|
+
| `disabled` | `boolean` | `false` | Desabilita todos os itens. |
|
|
116
|
+
| `className` | `string` | | Ajusta o contêiner. |
|
|
87
117
|
|
|
88
118
|
## Propriedades de AccordionItem
|
|
89
119
|
|
|
90
120
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
91
121
|
|---|---|---|---|
|
|
92
|
-
| `value` | `string` | | Identificador usado por `defaultValue` e `value` no `Accordion`. |
|
|
122
|
+
| `value` | `string` | | Obrigatório. Identificador usado por `defaultValue` e `value` no `Accordion`. |
|
|
123
|
+
| `disabled` | `boolean` | `false` | Impede abrir ou fechar este item. |
|
|
124
|
+
| `className` | `string` | | Ajusta o item, que traz um divisor inferior exceto no último. |
|
|
125
|
+
|
|
126
|
+
## Propriedades de AccordionTrigger
|
|
127
|
+
|
|
128
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
129
|
+
|---|---|---|---|
|
|
130
|
+
| `children` | `ReactNode` | | Rótulo do painel. O ícone de estado é acrescentado depois dele. |
|
|
131
|
+
| `className` | `string` | | Ajusta o botão do gatilho. |
|
|
132
|
+
|
|
133
|
+
## Propriedades de AccordionContent
|
|
134
|
+
|
|
135
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
136
|
+
|---|---|---|---|
|
|
137
|
+
| `children` | `ReactNode` | | Conteúdo do painel. |
|
|
138
|
+
| `className` | `string` | | Ajusta o recuo interno do conteúdo, aplicado a um contêiner dentro da região animada. |
|
|
139
|
+
| `forceMount` | `boolean` | `false` | Mantém o conteúdo montado mesmo fechado. |
|
|
@@ -1,25 +1,34 @@
|
|
|
1
1
|
## Formulário em uma seção
|
|
2
2
|
|
|
3
|
-
Use `ActionFormCard`
|
|
4
|
-
aplica a estrutura e o espaçamento de `Card
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Use `ActionFormCard` quando o formulário ocupar uma seção delimitada da página, ao lado de outros
|
|
4
|
+
blocos. O componente aplica a estrutura e o espaçamento de [`Card`](/ui/card) ao
|
|
5
|
+
[`ActionForm`](/ui/action-form), sem a faixa de ações de um modal. O título é opcional e segue a
|
|
6
|
+
mesma regra de um formulário modal: nomeia a operação e o recurso, como `Criar workspace`, enquanto
|
|
7
|
+
a ação principal mantém `Salvar`.
|
|
7
8
|
|
|
8
9
|
```tsx preview col md
|
|
9
10
|
<DocBrowserActionProvider>
|
|
10
|
-
<ActionFormCard
|
|
11
|
-
title="Editar workspace"
|
|
12
|
-
action={docWorkspaceCreate}
|
|
13
|
-
defaultValues={{ name: "Empresa X", status: "active" }}
|
|
14
|
-
/>
|
|
11
|
+
<ActionFormCard title="Criar workspace" action={docWorkspaceCreate} />
|
|
15
12
|
</DocBrowserActionProvider>
|
|
16
13
|
```
|
|
17
14
|
|
|
15
|
+
As ações ficam alinhadas no fim do rodapé e preservam a largura do conteúdo. Quando `onCancel`
|
|
16
|
+
acrescenta `Cancelar`, os dois botões formam um `ButtonGroup` espaçado. Para interromper o fluxo
|
|
17
|
+
com um modal, use [`ActionFormDialog`](/ui/action-form-dialog).
|
|
18
|
+
|
|
19
|
+
O título do card é um heading `h3`, o nível padrão de `CardTitle`, adequado a uma seção dentro de
|
|
20
|
+
uma página que já tem o próprio título. Quando o card ocupar outro lugar na hierarquia — por
|
|
21
|
+
exemplo, a seção principal logo abaixo do `h1` da página —, declare `titleLevel={2}`. O nível muda
|
|
22
|
+
só a estrutura do documento, que leitores de tela usam para navegar; a aparência do título continua
|
|
23
|
+
a mesma.
|
|
24
|
+
|
|
18
25
|
## Propriedades de ActionFormCard
|
|
19
26
|
|
|
20
|
-
| Propriedade
|
|
21
|
-
|
|
|
22
|
-
| `title`
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `cardClassName`
|
|
27
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
28
|
+
| ------------------ | ---------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
29
|
+
| `title` | `string` | | Título do cabeçalho. Sem ele e sem `description`, o card começa diretamente pelo corpo. |
|
|
30
|
+
| `titleLevel` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `3` | Nível do heading do título. Muda a hierarquia do documento, não a escala visual. |
|
|
31
|
+
| `description` | `string` | | Texto de apoio abaixo do título. |
|
|
32
|
+
| `cardClassName` | `string` | | Classes aplicadas à superfície do card. |
|
|
33
|
+
| `className` | `string` | | Classes aplicadas ao `<form>` interno. |
|
|
34
|
+
| Demais propriedades | Propriedades de `ActionForm` | | `action`, `defaultValues`, `fieldOptions`, `submitLabel`, `cancelLabel`, `cancelVariant`, `onCancel`, `onSuccess`, `disabled`, `onLoadingChange` e `children` mantêm o comportamento de `ActionForm`. `body` e `footer` não são aceitas: o card fornece o corpo e o rodapé. |
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Use `ActionFormDialog` quando o formulário precisar interromper o fluxo atual sem levar a pessoa
|
|
4
4
|
para outra página. O consumidor controla `open`; depois de uma execução bem-sucedida, o componente
|
|
5
|
-
fecha o modal. O cabeçalho e o rodapé permanecem visíveis enquanto os campos
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
fecha o modal. O cabeçalho e o rodapé permanecem visíveis enquanto os campos rolam.
|
|
6
|
+
|
|
7
|
+
O título nomeia a operação e o recurso: `Criar workspace` em uma criação e `Editar workspace` em uma
|
|
8
|
+
edição. Nos dois casos, a ação principal mantém o rótulo padrão `Salvar`, porque o título já
|
|
9
|
+
distingue a operação. `submitLabel` fica para efeitos que `Salvar` não descreve, como `Renomear` ou
|
|
10
|
+
`Criar nova versão`.
|
|
11
11
|
|
|
12
12
|
```tsx preview
|
|
13
13
|
const [open, setOpen] = useState(false);
|
|
@@ -25,12 +25,22 @@ render(
|
|
|
25
25
|
);
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
## Ações do rodapé
|
|
29
|
+
|
|
30
|
+
O rodapé agrupa as decisões em um `ButtonGroup` cuja largura acompanha o conteúdo: `Cancelar`
|
|
31
|
+
aparece em `ghost` e a ação principal permanece `solid`, ambas no tamanho normal de uma decisão
|
|
32
|
+
modal. `Cancelar` está sempre presente; sem `onCancel` próprio, ele fecha o modal. Com
|
|
33
|
+
`footerDistribution="equal"`, as duas decisões dividem a faixa e `Cancelar` passa a `outline`,
|
|
34
|
+
salvo escolha explícita em `cancelVariant`.
|
|
35
|
+
|
|
36
|
+
Para consultar uma coleção no mesmo formato de modal, use [`ActionListDialog`](/ui/action-list-dialog).
|
|
37
|
+
|
|
28
38
|
## Propriedades de ActionFormDialog
|
|
29
39
|
|
|
30
40
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
31
41
|
| --------------------- | ------------------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
32
42
|
| `open / onOpenChange` | `boolean / (open: boolean) => void` | | Estado controlado do modal. `onOpenChange(false)` é chamado no sucesso e, quando não há `onCancel` próprio, ao cancelar. |
|
|
33
|
-
| `title` | `string` | |
|
|
43
|
+
| `title` | `string` | | Operação e recurso exibidos no cabeçalho, como `Criar workspace` ou `Editar workspace`. |
|
|
34
44
|
| `intro` | `ReactNode` | | Contexto relevante no início do corpo, como texto ou `Alert`. |
|
|
35
45
|
| `submitLabel` | `string` | `'Salvar'` | Resultado da ação principal. Preserve o padrão em criação e edição comuns; sobrescreva apenas quando a operação tiver outro efeito, como `Renomear` ou `Criar nova versão`. |
|
|
36
46
|
| `footerDistribution` | `'content' \| 'equal'` | `'content'` | Mantém a largura das ações pelo conteúdo ou divide a faixa igualmente. |
|
|
@@ -1,8 +1,28 @@
|
|
|
1
|
+
## Pré-requisitos
|
|
2
|
+
|
|
3
|
+
`ActionForm` executa a action pelo cliente registrado no `OpusProvider` e usa o React Query para
|
|
4
|
+
invalidar as consultas afetadas. Monte os dois providers uma vez na raiz do aplicativo; todos os
|
|
5
|
+
exemplos desta página pressupõem essa montagem.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
/* main.tsx do app. */
|
|
9
|
+
<QueryClientProvider client={queryClient}>
|
|
10
|
+
<OpusProvider client={apiClient}>
|
|
11
|
+
<App />
|
|
12
|
+
</OpusProvider>
|
|
13
|
+
</QueryClientProvider>
|
|
14
|
+
```
|
|
15
|
+
|
|
1
16
|
## Formulário derivado do contrato
|
|
2
17
|
|
|
3
18
|
Use `ActionForm` para gerar campos, validação e mensagens a partir de uma `FormAction`. Sem
|
|
4
|
-
`children`, o componente
|
|
5
|
-
|
|
19
|
+
`children`, o componente monta um campo para cada chave declarada em `fields` no contrato, na ordem
|
|
20
|
+
dessa declaração, e escolhe o controle pelo tipo do campo no schema de input. Um campo presente no
|
|
21
|
+
schema, mas sem entrada em `fields`, não aparece no modo automático: declare-o em `fields` ou
|
|
22
|
+
posicione-o com `ActionFormField`, como mostra a próxima seção.
|
|
23
|
+
|
|
24
|
+
A ação principal permanece desabilitada até que algum valor mude. No exemplo, preencha o nome para
|
|
25
|
+
habilitá-la; o valor “Softize” simula um erro devolvido pelo servidor.
|
|
6
26
|
|
|
7
27
|
```tsx preview col md
|
|
8
28
|
<DocBrowserActionProvider>
|
|
@@ -13,36 +33,43 @@ no contrato. No exemplo, altere um valor para habilitar a ação principal.
|
|
|
13
33
|
## Composição dos campos
|
|
14
34
|
|
|
15
35
|
Passe `children` quando a disposição automática não atender ao formulário. Cada
|
|
16
|
-
`ActionFormField` mantém rótulo, ajuda, controle, obrigatoriedade e erro derivados do contrato;
|
|
17
|
-
|
|
18
|
-
invalidação continuam sob
|
|
36
|
+
`ActionFormField` mantém rótulo, ajuda, controle, obrigatoriedade e erro derivados do contrato; o
|
|
37
|
+
consumidor define apenas a grade e as condições. O espaçamento vertical entre os filhos vem do
|
|
38
|
+
`FieldGroup` que o formulário já aplica. Execução, validação, toast e invalidação continuam sob
|
|
39
|
+
responsabilidade de `ActionForm`.
|
|
40
|
+
|
|
41
|
+
Nesse modo, qualquer campo do schema de input pode ser posicionado, mesmo sem entrada em `fields`;
|
|
42
|
+
nesse caso, o rótulo exibido é o próprio nome técnico do campo e não há ajuda. Declare o campo em
|
|
43
|
+
`fields` para nomeá-lo.
|
|
19
44
|
|
|
20
45
|
```tsx preview col md
|
|
21
46
|
<DocBrowserActionProvider>
|
|
22
47
|
<ActionForm action={docWorkspaceCreate}>
|
|
23
|
-
<div className="
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
<ActionFormField name="status" />
|
|
27
|
-
</div>
|
|
28
|
-
<ActionFormField name="contact" />
|
|
29
|
-
<ActionFormField name="notes" />
|
|
48
|
+
<div className="grid grid-cols-2 gap-4">
|
|
49
|
+
<ActionFormField name="name" />
|
|
50
|
+
<ActionFormField name="status" />
|
|
30
51
|
</div>
|
|
52
|
+
<ActionFormField name="contact" />
|
|
53
|
+
<ActionFormField name="notes" />
|
|
31
54
|
</ActionForm>
|
|
32
55
|
</DocBrowserActionProvider>
|
|
33
56
|
```
|
|
34
57
|
|
|
35
58
|
## Valores iniciais e rótulos
|
|
36
59
|
|
|
37
|
-
`defaultValues` preenche o formulário
|
|
38
|
-
|
|
39
|
-
|
|
60
|
+
`defaultValues` preenche o formulário antes da primeira alteração. Em uma edição, carregue ali os
|
|
61
|
+
valores atuais do registro; em uma criação, use-o para sugerir um ponto de partida, como o status
|
|
62
|
+
inicial do exemplo. A ação principal continua desabilitada até que a pessoa altere algum campo.
|
|
63
|
+
|
|
64
|
+
Criação e edição comuns mantêm o rótulo padrão `Salvar`; use `submitLabel` somente quando a
|
|
65
|
+
operação pedir outro verbo, como `Renomear`. `onCancel` acrescenta `Cancelar` e devolve ao
|
|
66
|
+
consumidor a decisão de fechar um painel ou navegar para outra página.
|
|
40
67
|
|
|
41
68
|
```tsx preview col md
|
|
42
69
|
<DocBrowserActionProvider>
|
|
43
70
|
<ActionForm
|
|
44
71
|
action={docWorkspaceCreate}
|
|
45
|
-
defaultValues={{
|
|
72
|
+
defaultValues={{ status: "active" }}
|
|
46
73
|
onCancel={() => undefined}
|
|
47
74
|
/>
|
|
48
75
|
</DocBrowserActionProvider>
|
|
@@ -50,58 +77,17 @@ invalidação continuam sob responsabilidade de `ActionForm`.
|
|
|
50
77
|
|
|
51
78
|
## Formulário modal
|
|
52
79
|
|
|
53
|
-
[`ActionFormDialog`](/ui/action-form-dialog) acrescenta a moldura, o corpo rolável e o
|
|
54
|
-
ações ficam alinhadas no fim da faixa e preservam a largura do conteúdo;
|
|
55
|
-
Use `footerDistribution="equal"` somente quando as duas decisões precisarem
|
|
56
|
-
Nesse caso, o cancelamento muda para `outline`, salvo escolha explícita em
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
## Pré-requisitos
|
|
60
|
-
|
|
61
|
-
Monte os providers de consulta e execução uma vez na raiz do aplicativo:
|
|
80
|
+
[`ActionFormDialog`](/ui/action-form-dialog) acrescenta a moldura, o corpo rolável e o rodapé ao
|
|
81
|
+
formulário. Por padrão, as ações ficam alinhadas no fim da faixa e preservam a largura do conteúdo;
|
|
82
|
+
`Cancelar` usa `ghost`. Use `footerDistribution="equal"` somente quando as duas decisões precisarem
|
|
83
|
+
do mesmo peso visual. Nesse caso, o cancelamento muda para `outline`, salvo escolha explícita em
|
|
84
|
+
`cancelVariant`. Para apresentar o formulário como uma seção delimitada da página, use
|
|
85
|
+
[`ActionFormCard`](/ui/action-form-card). As propriedades de cada wrapper estão na página dele.
|
|
62
86
|
|
|
63
|
-
|
|
64
|
-
/* main.tsx do app. */
|
|
65
|
-
<QueryClientProvider client={queryClient}>
|
|
66
|
-
<OpusProvider client={apiClient}>
|
|
67
|
-
<App />
|
|
68
|
-
</OpusProvider>
|
|
69
|
-
</QueryClientProvider>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## useFormAction
|
|
73
|
-
|
|
74
|
-
`ActionForm` é uma composição sobre `useFormAction(action, { defaultValues, onSuccess, onError })`,
|
|
75
|
-
que devolve o `form` do react-hook-form já com o resolver Zod em pt-BR, `submit`, `isLoading`,
|
|
76
|
-
`error`, `isSuccess` e `reset`. Use o hook direto quando o formulário precisar de um layout ou de um
|
|
77
|
-
ciclo de vida que o pattern não cobre; validação e execução continuam iguais.
|
|
78
|
-
|
|
79
|
-
## Propriedades de ActionForm
|
|
80
|
-
|
|
81
|
-
| Propriedade | Tipo | Padrão | Descrição |
|
|
82
|
-
| ------------------------------------------------------ | ----------------------------------------------------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
83
|
-
| `action` | `FormContract<TInput, TData>` | | O contrato da FormAction — dele saem campos (input Zod + fields), mensagens e invalidação de cache. |
|
|
84
|
-
| `defaultValues` | `Partial<TInput>` | | Valores iniciais — o modo edição de um update/patch. |
|
|
85
|
-
| `onSuccess` | `(data: TData) => void` | | Pós-sucesso (o toast e a invalidação de cache já aconteceram). |
|
|
86
|
-
| `submitLabel / cancelLabel / cancelVariant / onCancel` | `string / string / 'ghost' \| 'outline' / () => void` | `'Salvar' / 'Cancelar' / 'ghost'` | Rodapé do form — o Cancelar só aparece com onCancel. Fora de um modal, mantenha `ghost`; o `ActionFormDialog` usa `outline` somente com `footerDistribution="equal"`. |
|
|
87
|
-
| `disabled` | `boolean` | `false` | Bloqueia campos e ações sem desmontar o formulário. |
|
|
88
|
-
| `onLoadingChange` | `(loading: boolean) => void` | | Informa início e fim da execução a quem coordena ações como um grupo. |
|
|
89
|
-
| `fieldOptions` | `Record<string, SelectOption[]>` | | Opções de runtime por campo (ex.: ids de skills) — sobrepõe as inferidas do z.enum. |
|
|
90
|
-
| `className / body / footer` | `string / (node) => node / (node) => node` | | className = classes do `<form>`. body/footer = slots: recebem os campos / os botões e escolhem o invólucro — é como o ActionFormDialog injeta DialogBody/DialogFooter (scroll + faixa). |
|
|
91
|
-
| `children` | `ReactNode` | | Modo composição: diagrame com `<ActionFormField name />`. Sem children, o automático monta todos os campos na ordem do contrato. |
|
|
87
|
+
## Ajuda no rótulo
|
|
92
88
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
| Propriedade | Tipo | Descrição |
|
|
96
|
-
| ----------- | ---------------- | ------------------------------------------------------------------------------ |
|
|
97
|
-
| `name` | `string` | O campo do contrato (chave em `fields`/schema). Fora do schema, não renderiza. |
|
|
98
|
-
| `options` | `SelectOption[]` | Opções por id de runtime — sobrepõe o fieldOptions do form e o z.enum. |
|
|
99
|
-
| `className` | `string` | Classes do invólucro (ex.: `col-span-2` em uma grid). |
|
|
100
|
-
|
|
101
|
-
## Ajuda na label
|
|
102
|
-
|
|
103
|
-
`FieldSpec.help` aparece como um ícone junto à label, com o texto num tooltip. Escreva ali o
|
|
104
|
-
critério, o efeito ou a limitação que a label não diz; ajuda que só repete a label deve ser omitida.
|
|
89
|
+
`FieldSpec.help` aparece como um ícone junto ao rótulo, com o texto em um tooltip. Escreva ali o
|
|
90
|
+
critério, o efeito ou a limitação que o rótulo não diz; ajuda que só repete o rótulo deve ser omitida.
|
|
105
91
|
Não existe texto auxiliar sob o campo: quem precisa de uma frase permanente abaixo do controle usa
|
|
106
92
|
`FieldDescription` na composição própria.
|
|
107
93
|
|
|
@@ -110,20 +96,14 @@ lugar:
|
|
|
110
96
|
|
|
111
97
|
```tsx preview col md
|
|
112
98
|
<Field>
|
|
113
|
-
<FieldLabel htmlFor="
|
|
114
|
-
<span>
|
|
115
|
-
<LabelHelp help="
|
|
99
|
+
<FieldLabel htmlFor="billing-contact" className="items-center gap-1.5">
|
|
100
|
+
<span>Contato</span>
|
|
101
|
+
<LabelHelp help="Recebe os avisos de faturamento e de integração do workspace." />
|
|
116
102
|
</FieldLabel>
|
|
117
|
-
<Input id="
|
|
103
|
+
<Input id="billing-contact" type="email" placeholder="email@cliente.com" />
|
|
118
104
|
</Field>
|
|
119
105
|
```
|
|
120
106
|
|
|
121
|
-
### Propriedades de LabelHelp
|
|
122
|
-
|
|
123
|
-
| Propriedade | Tipo | Descrição |
|
|
124
|
-
| ----------- | -------- | ------------------------------------------------------------ |
|
|
125
|
-
| `help` | `string` | O texto do tooltip. Vazio ou ausente, o ícone não renderiza. |
|
|
126
|
-
|
|
127
107
|
## Origem das opções de seleção
|
|
128
108
|
|
|
129
109
|
As opções seguem esta precedência: `options` no campo, `fieldOptions` no formulário, `options` no
|
|
@@ -137,7 +117,7 @@ referência; itens estáticos permanecem como declarados no contrato.
|
|
|
137
117
|
O literal canônico do grupo de escolhas é sempre kebab-case: `widget: 'toggle-group'`. Em enum ele
|
|
138
118
|
renderiza seleção única; em array de enum, seleção múltipla. As opções seguem a mesma precedência e
|
|
139
119
|
o mesmo `SelectOption` rico dos selects. Use esse widget quando cada opção funcionar como uma
|
|
140
|
-
escolha rica em card, com conteúdo de apoio; para uma lista textual comum, prefira `RadioGroup
|
|
120
|
+
escolha rica em card, com conteúdo de apoio; para uma lista textual comum, prefira [`RadioGroup`](/ui/radio-group).
|
|
141
121
|
Outros identificadores continuam válidos como metadado
|
|
142
122
|
para renderers próprios; o `ActionForm` aplica sua inferência normal quando não reconhece o widget.
|
|
143
123
|
|
|
@@ -170,3 +150,38 @@ function PermissionGrid() {
|
|
|
170
150
|
|
|
171
151
|
O contexto também expõe `shape` (Zod por campo), `fields` (FieldSpec) e `fieldOptions`.
|
|
172
152
|
Fora de um `<ActionForm>`, o hook lança um erro que informa o provider ausente.
|
|
153
|
+
|
|
154
|
+
## useFormAction
|
|
155
|
+
|
|
156
|
+
`ActionForm` é uma composição sobre `useFormAction(action, { defaultValues, onSuccess, onError })`,
|
|
157
|
+
que devolve o `form` do react-hook-form já com o resolver Zod em pt-BR, `submit`, `isLoading`,
|
|
158
|
+
`error`, `isSuccess` e `reset`. Use o hook direto quando o formulário precisar de um layout ou de um
|
|
159
|
+
ciclo de vida que o pattern não cobre; validação e execução continuam iguais.
|
|
160
|
+
|
|
161
|
+
## Propriedades de ActionForm
|
|
162
|
+
|
|
163
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
164
|
+
| ------------------------------------------------------ | ----------------------------------------------------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
165
|
+
| `action` | `FormContract<TInput, TData>` | | O contrato da FormAction — dele saem campos (input Zod + fields), mensagens e invalidação de cache. |
|
|
166
|
+
| `defaultValues` | `Partial<TInput>` | | Valores iniciais: os dados atuais do registro em uma edição ou uma sugestão em uma criação. Não contam como alteração para habilitar a ação principal. |
|
|
167
|
+
| `onSuccess` | `(data: TData) => void` | | Pós-sucesso (o toast e a invalidação de cache já aconteceram). |
|
|
168
|
+
| `submitLabel / cancelLabel / cancelVariant / onCancel` | `string / string / 'ghost' \| 'outline' / () => void` | `'Salvar' / 'Cancelar' / 'ghost'` | Rodapé do form — o Cancelar só aparece com onCancel. Fora de um modal, mantenha `ghost`; o `ActionFormDialog` usa `outline` somente com `footerDistribution="equal"`. |
|
|
169
|
+
| `disabled` | `boolean` | `false` | Bloqueia campos e ações sem desmontar o formulário. |
|
|
170
|
+
| `onLoadingChange` | `(loading: boolean) => void` | | Informa início e fim da execução a quem coordena ações como um grupo. |
|
|
171
|
+
| `fieldOptions` | `Record<string, SelectOption[]>` | | Opções de runtime por campo (ex.: ids de skills) — sobrepõe as inferidas do z.enum. |
|
|
172
|
+
| `className / body / footer` | `string / (node) => node / (node) => node` | | className = classes do `<form>`. body/footer = slots: recebem os campos / os botões e escolhem o invólucro — é como o ActionFormDialog injeta DialogBody/DialogFooter (scroll + faixa). |
|
|
173
|
+
| `children` | `ReactNode` | | Modo composição: diagrame com `<ActionFormField name />`. Sem children, o modo automático monta um campo por chave de `fields`, na ordem declarada; campo sem entrada em `fields` não aparece. |
|
|
174
|
+
|
|
175
|
+
## Propriedades de ActionFormField
|
|
176
|
+
|
|
177
|
+
| Propriedade | Tipo | Descrição |
|
|
178
|
+
| ----------- | ---------------- | ------------------------------------------------------------------------------ |
|
|
179
|
+
| `name` | `string` | O campo do schema de input. Sem entrada em `fields`, usa o nome técnico como rótulo; fora do schema, não renderiza. |
|
|
180
|
+
| `options` | `SelectOption[]` | Opções por id de runtime — sobrepõe o fieldOptions do form e o z.enum. |
|
|
181
|
+
| `className` | `string` | Classes do invólucro (ex.: `col-span-2` em uma grid). |
|
|
182
|
+
|
|
183
|
+
## Propriedades de LabelHelp
|
|
184
|
+
|
|
185
|
+
| Propriedade | Tipo | Descrição |
|
|
186
|
+
| ----------- | -------- | ------------------------------------------------------------ |
|
|
187
|
+
| `help` | `string` | O texto do tooltip. Vazio ou ausente, o ícone não renderiza. |
|