@softize/opus 12.10.0 → 13.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 +56 -0
- package/bin/lib/check.mjs +1098 -310
- package/bin/lib/copy.mjs +12 -5
- package/docs/adr/0003-dictionary-presentation-is-declared.md +3 -0
- package/docs/adr/0004-page-content-state-is-composed.md +65 -0
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +180 -0
- package/docs/adr/0006-semantic-context-precedes-visual-variant.md +182 -0
- package/docs/adr/0007-toast-actions-form-an-ordered-collection.md +63 -0
- package/docs/adr/0008-hierarchical-navigation-is-composed-at-the-consumer-boundary.md +71 -0
- package/docs/radius-scale.md +1 -1
- package/package.json +1 -1
- package/registry/instructions/opus.md +5 -0
- package/registry/skills/build-opus-ui/SKILL.md +27 -16
- package/registry/skills/build-opus-ui/references/evaluations.md +16 -5
- package/registry/skills/build-opus-ui/references/ui-patterns.md +38 -15
- package/registry/skills/maintain-opus-docs/SKILL.md +83 -0
- package/registry/skills/maintain-opus-docs/agents/openai.yaml +4 -0
- package/registry/skills/maintain-opus-docs/references/editorial-standard.md +85 -0
- package/registry/skills/maintain-opus-docs/references/evaluations.md +34 -0
- package/registry/skills/maintain-opus-docs/scripts/audit-docs.mjs +81 -0
- package/registry/skills/model-opus-dictionary/SKILL.md +4 -2
- package/registry/skills/model-opus-dictionary/references/evaluations.md +4 -3
- package/registry/templates/app/src/App.tsx +1 -1
- package/src/core/dictionary.ts +52 -14
- package/src/core/index.ts +10 -0
- package/src/core/ui-context.ts +29 -0
- package/src/schema/drivers/zod.ts +17 -8
- package/src/ui/components/patterns/action-form-card.tsx +18 -12
- package/src/ui/components/patterns/confirm.tsx +163 -40
- package/src/ui/components/patterns/content-header.tsx +335 -61
- package/src/ui/components/patterns/data-state.tsx +23 -10
- package/src/ui/components/patterns/list.tsx +1097 -783
- package/src/ui/components/patterns/page-state.tsx +115 -0
- package/src/ui/components/patterns/page.tsx +231 -41
- package/src/ui/components/patterns/sidebar.tsx +357 -83
- package/src/ui/components/patterns/trigger.tsx +37 -30
- package/src/ui/components/patterns/view.tsx +7 -11
- package/src/ui/components/primitives/alert.tsx +298 -110
- package/src/ui/components/primitives/ask.tsx +2 -1
- package/src/ui/components/primitives/badge.tsx +91 -30
- package/src/ui/components/primitives/button.tsx +99 -60
- package/src/ui/components/primitives/calendar.tsx +39 -39
- package/src/ui/components/primitives/card.tsx +96 -23
- package/src/ui/components/primitives/detail.tsx +2 -2
- package/src/ui/components/primitives/dialog.tsx +196 -39
- package/src/ui/components/primitives/dictionary-value.tsx +9 -14
- package/src/ui/components/primitives/dot.tsx +74 -21
- package/src/ui/components/primitives/drawer.tsx +40 -24
- package/src/ui/components/primitives/empty.tsx +3 -3
- package/src/ui/components/primitives/item.tsx +135 -79
- package/src/ui/components/primitives/menu.tsx +11 -3
- package/src/ui/components/primitives/metric-card.tsx +133 -0
- package/src/ui/components/primitives/sonner.tsx +187 -8
- package/src/ui/components/primitives/table.tsx +2 -2
- package/src/ui/docs/DocBrowser.tsx +104 -25
- package/src/ui/docs/changelog.tsx +1 -1
- package/src/ui/docs/content/accordion.md +22 -16
- package/src/ui/docs/content/action-form-card.md +8 -8
- package/src/ui/docs/content/action-form-dialog.md +9 -9
- package/src/ui/docs/content/action-form.md +28 -34
- package/src/ui/docs/content/action-list-dialog.md +11 -6
- package/src/ui/docs/content/action-list.md +64 -39
- package/src/ui/docs/content/action-trigger.md +21 -14
- package/src/ui/docs/content/action-view.md +8 -8
- package/src/ui/docs/content/actions.md +9 -9
- package/src/ui/docs/content/ai.md +3 -3
- package/src/ui/docs/content/alert.md +54 -28
- package/src/ui/docs/content/aspect-ratio.md +4 -4
- package/src/ui/docs/content/audit.md +2 -2
- package/src/ui/docs/content/auth.md +3 -3
- package/src/ui/docs/content/avatar.md +34 -14
- package/src/ui/docs/content/badge.md +21 -22
- package/src/ui/docs/content/breadcrumb.md +13 -8
- package/src/ui/docs/content/button.md +93 -15
- package/src/ui/docs/content/calendar.md +5 -5
- package/src/ui/docs/content/card.md +6 -6
- package/src/ui/docs/content/carousel.md +16 -11
- package/src/ui/docs/content/chat.md +3 -3
- package/src/ui/docs/content/checkbox.md +7 -7
- package/src/ui/docs/content/cli.md +5 -5
- package/src/ui/docs/content/collapsible.md +8 -8
- package/src/ui/docs/content/command.md +16 -8
- package/src/ui/docs/content/composer.md +2 -2
- package/src/ui/docs/content/content.md +44 -0
- package/src/ui/docs/content/copyable.md +4 -3
- package/src/ui/docs/content/customization.md +7 -7
- package/src/ui/docs/content/cycle.md +3 -3
- package/src/ui/docs/content/data-state.md +11 -12
- package/src/ui/docs/content/data.md +26 -33
- package/src/ui/docs/content/detail.md +8 -5
- package/src/ui/docs/content/dialog.md +339 -31
- package/src/ui/docs/content/dictionary-value.md +19 -18
- package/src/ui/docs/content/dock.md +3 -3
- package/src/ui/docs/content/dot.md +7 -7
- package/src/ui/docs/content/drawer.md +32 -16
- package/src/ui/docs/content/empty-value.md +2 -2
- package/src/ui/docs/content/empty.md +19 -12
- package/src/ui/docs/content/events.md +4 -4
- package/src/ui/docs/content/field.md +34 -12
- package/src/ui/docs/content/getting-started.md +1 -1
- package/src/ui/docs/content/icon-picker.md +8 -4
- package/src/ui/docs/content/input-otp.md +20 -12
- package/src/ui/docs/content/input.md +121 -9
- package/src/ui/docs/content/item.md +64 -24
- package/src/ui/docs/content/kbd.md +19 -11
- package/src/ui/docs/content/label.md +5 -3
- package/src/ui/docs/content/log.md +4 -4
- package/src/ui/docs/content/markdown.md +7 -6
- package/src/ui/docs/content/mcp.md +13 -15
- package/src/ui/docs/content/menu.md +36 -17
- package/src/ui/docs/content/metric-card.md +41 -0
- package/src/ui/docs/content/observability.md +2 -2
- package/src/ui/docs/content/page.md +93 -10
- package/src/ui/docs/content/pagination.md +22 -17
- package/src/ui/docs/content/popover.md +16 -8
- package/src/ui/docs/content/progress.md +7 -5
- package/src/ui/docs/content/queue.md +5 -5
- package/src/ui/docs/content/radio-group.md +20 -12
- package/src/ui/docs/content/router.md +11 -6
- package/src/ui/docs/content/scheduler.md +4 -5
- package/src/ui/docs/content/scroll-area.md +12 -7
- package/src/ui/docs/content/select.md +42 -29
- package/src/ui/docs/content/semantic-context.md +63 -0
- package/src/ui/docs/content/separator.md +5 -5
- package/src/ui/docs/content/sidebar.md +325 -56
- package/src/ui/docs/content/skeleton.md +5 -4
- package/src/ui/docs/content/slider.md +8 -7
- package/src/ui/docs/content/spinner.md +8 -8
- package/src/ui/docs/content/split.md +8 -5
- package/src/ui/docs/content/storage.md +6 -8
- package/src/ui/docs/content/switch.md +8 -7
- package/src/ui/docs/content/table.md +16 -6
- package/src/ui/docs/content/tabs.md +28 -14
- package/src/ui/docs/content/testing.md +9 -11
- package/src/ui/docs/content/textarea.md +5 -4
- package/src/ui/docs/content/toast.md +47 -13
- package/src/ui/docs/content/toggle.md +75 -7
- package/src/ui/docs/content/tokens.md +31 -3
- package/src/ui/docs/content/tooltip.md +19 -11
- package/src/ui/docs/content/truncate.md +7 -8
- package/src/ui/docs/content/ui.md +10 -9
- package/src/ui/docs/content/upgrading.md +7 -8
- package/src/ui/docs/doc-client.tsx +2 -2
- package/src/ui/docs/registry.tsx +580 -229
- package/src/ui/lib/semantic-context.ts +30 -0
- package/src/ui/meta.ts +278 -286
- package/src/ui/react.tsx +377 -111
- package/src/ui/theme.css +116 -0
- package/src/ui/components/primitives/alert-dialog.tsx +0 -190
- package/src/ui/docs/content/alert-dialog.md +0 -73
- package/src/ui/docs/content/button-group.md +0 -71
- package/src/ui/docs/content/confirm.md +0 -120
- package/src/ui/docs/content/input-group.md +0 -78
- package/src/ui/docs/content/toggle-group.md +0 -81
package/src/ui/theme.css
CHANGED
|
@@ -47,6 +47,38 @@
|
|
|
47
47
|
--accent-foreground: hsl(0 0% 9%);
|
|
48
48
|
--destructive: hsl(0 84.2% 60.2%);
|
|
49
49
|
--destructive-foreground: hsl(0 0% 98%);
|
|
50
|
+
/* Contextos semânticos (ADR 0006): família independente do tratamento
|
|
51
|
+
solid/subtle/outline escolhido por cada primitive. */
|
|
52
|
+
--context-neutral: var(--secondary);
|
|
53
|
+
--context-neutral-foreground: var(--secondary-foreground);
|
|
54
|
+
--context-neutral-subtle: var(--muted);
|
|
55
|
+
--context-neutral-emphasis: var(--muted-foreground);
|
|
56
|
+
--context-neutral-border: var(--border);
|
|
57
|
+
--context-primary: var(--primary);
|
|
58
|
+
--context-primary-foreground: var(--primary-foreground);
|
|
59
|
+
--context-primary-subtle: color-mix(in oklab, var(--primary) 8%, transparent);
|
|
60
|
+
--context-primary-emphasis: var(--foreground);
|
|
61
|
+
--context-primary-border: color-mix(in oklab, var(--primary) 25%, transparent);
|
|
62
|
+
--context-info: hsl(217 91% 60%);
|
|
63
|
+
--context-info-foreground: hsl(0 0% 100%);
|
|
64
|
+
--context-info-subtle: hsl(217 91% 60% / 0.1);
|
|
65
|
+
--context-info-emphasis: hsl(221 83% 40%);
|
|
66
|
+
--context-info-border: hsl(217 91% 60% / 0.3);
|
|
67
|
+
--context-success: hsl(142 71% 45%);
|
|
68
|
+
--context-success-foreground: hsl(0 0% 100%);
|
|
69
|
+
--context-success-subtle: hsl(142 71% 45% / 0.1);
|
|
70
|
+
--context-success-emphasis: hsl(142 72% 29%);
|
|
71
|
+
--context-success-border: hsl(142 71% 45% / 0.3);
|
|
72
|
+
--context-warning: hsl(38 92% 50%);
|
|
73
|
+
--context-warning-foreground: hsl(26 83% 14%);
|
|
74
|
+
--context-warning-subtle: hsl(38 92% 50% / 0.12);
|
|
75
|
+
--context-warning-emphasis: hsl(26 90% 32%);
|
|
76
|
+
--context-warning-border: hsl(38 92% 50% / 0.35);
|
|
77
|
+
--context-danger: var(--destructive);
|
|
78
|
+
--context-danger-foreground: var(--destructive-foreground);
|
|
79
|
+
--context-danger-subtle: color-mix(in oklab, var(--destructive) 10%, transparent);
|
|
80
|
+
--context-danger-emphasis: hsl(0 72% 45%);
|
|
81
|
+
--context-danger-border: color-mix(in oklab, var(--destructive) 35%, transparent);
|
|
50
82
|
--border: hsl(0 0% 92.5%);
|
|
51
83
|
--input: hsl(0 0% 92.5%);
|
|
52
84
|
/* Ring de foco LEVE (pedido da casa): aro sutil, não um halo. Com ring-[0.1875rem] + /50,
|
|
@@ -81,6 +113,10 @@
|
|
|
81
113
|
cima, como o shadcn novo. */
|
|
82
114
|
--destructive: hsl(0 72% 58%);
|
|
83
115
|
--destructive-foreground: hsl(0 0% 98%);
|
|
116
|
+
--context-info-emphasis: hsl(213 94% 68%);
|
|
117
|
+
--context-success-emphasis: hsl(142 69% 58%);
|
|
118
|
+
--context-warning-emphasis: hsl(43 96% 56%);
|
|
119
|
+
--context-danger-emphasis: hsl(0 91% 71%);
|
|
84
120
|
--border: hsl(0 0% 16.5%);
|
|
85
121
|
--input: hsl(0 0% 16.5%);
|
|
86
122
|
/* No escuro o 83.1% virava um halo quase branco; ~36% marca o foco sem brilhar. */
|
|
@@ -130,6 +166,36 @@
|
|
|
130
166
|
--color-accent-foreground: var(--accent-foreground);
|
|
131
167
|
--color-destructive: var(--destructive);
|
|
132
168
|
--color-destructive-foreground: var(--destructive-foreground);
|
|
169
|
+
--color-context-neutral: var(--context-neutral);
|
|
170
|
+
--color-context-neutral-foreground: var(--context-neutral-foreground);
|
|
171
|
+
--color-context-neutral-subtle: var(--context-neutral-subtle);
|
|
172
|
+
--color-context-neutral-emphasis: var(--context-neutral-emphasis);
|
|
173
|
+
--color-context-neutral-border: var(--context-neutral-border);
|
|
174
|
+
--color-context-primary: var(--context-primary);
|
|
175
|
+
--color-context-primary-foreground: var(--context-primary-foreground);
|
|
176
|
+
--color-context-primary-subtle: var(--context-primary-subtle);
|
|
177
|
+
--color-context-primary-emphasis: var(--context-primary-emphasis);
|
|
178
|
+
--color-context-primary-border: var(--context-primary-border);
|
|
179
|
+
--color-context-info: var(--context-info);
|
|
180
|
+
--color-context-info-foreground: var(--context-info-foreground);
|
|
181
|
+
--color-context-info-subtle: var(--context-info-subtle);
|
|
182
|
+
--color-context-info-emphasis: var(--context-info-emphasis);
|
|
183
|
+
--color-context-info-border: var(--context-info-border);
|
|
184
|
+
--color-context-success: var(--context-success);
|
|
185
|
+
--color-context-success-foreground: var(--context-success-foreground);
|
|
186
|
+
--color-context-success-subtle: var(--context-success-subtle);
|
|
187
|
+
--color-context-success-emphasis: var(--context-success-emphasis);
|
|
188
|
+
--color-context-success-border: var(--context-success-border);
|
|
189
|
+
--color-context-warning: var(--context-warning);
|
|
190
|
+
--color-context-warning-foreground: var(--context-warning-foreground);
|
|
191
|
+
--color-context-warning-subtle: var(--context-warning-subtle);
|
|
192
|
+
--color-context-warning-emphasis: var(--context-warning-emphasis);
|
|
193
|
+
--color-context-warning-border: var(--context-warning-border);
|
|
194
|
+
--color-context-danger: var(--context-danger);
|
|
195
|
+
--color-context-danger-foreground: var(--context-danger-foreground);
|
|
196
|
+
--color-context-danger-subtle: var(--context-danger-subtle);
|
|
197
|
+
--color-context-danger-emphasis: var(--context-danger-emphasis);
|
|
198
|
+
--color-context-danger-border: var(--context-danger-border);
|
|
133
199
|
--color-border: var(--border);
|
|
134
200
|
--color-input: var(--input);
|
|
135
201
|
--color-ring: var(--ring);
|
|
@@ -258,3 +324,53 @@
|
|
|
258
324
|
.prose :where(pre):not(:where([class~='not-prose'], [class~='not-prose'] *)) {
|
|
259
325
|
border: 1px solid var(--border);
|
|
260
326
|
}
|
|
327
|
+
|
|
328
|
+
/* O Sonner reserva por padrão apenas 1rem para a mídia. A moldura semântica do
|
|
329
|
+
* Opus ocupa 2.25rem; a coluna precisa ter a mesma medida para não avançar sob
|
|
330
|
+
* título e descrição. Estas classes são a fronteira estável com o CSS interno
|
|
331
|
+
* do Sonner, independentemente da ordem em que a biblioteca injeta seus estilos. */
|
|
332
|
+
.opus-toast {
|
|
333
|
+
gap: 0.75rem;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.opus-toast .opus-toast-icon {
|
|
337
|
+
width: 2.25rem;
|
|
338
|
+
height: 2.25rem;
|
|
339
|
+
margin: 0;
|
|
340
|
+
align-self: flex-start;
|
|
341
|
+
align-items: center;
|
|
342
|
+
justify-content: center;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.toaster .opus-toast[data-sonner-toast] .opus-toast-close[data-close-button]:focus-visible {
|
|
346
|
+
box-shadow: 0 0 0 0.125rem color-mix(in srgb, var(--ring) 50%, transparent);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.toaster .opus-toast[data-sonner-toast] .opus-toast-close[data-close-button][data-disabled='true'] {
|
|
350
|
+
pointer-events: none;
|
|
351
|
+
opacity: 0.5;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/* Fechar continua sendo um controle circular de ícone, mas deixa de herdar a
|
|
355
|
+
* paleta cinza, a medida e o foco particulares do Sonner. */
|
|
356
|
+
.toaster .opus-toast[data-sonner-toast] .opus-toast-close[data-close-button] {
|
|
357
|
+
box-sizing: border-box;
|
|
358
|
+
width: 1.5rem;
|
|
359
|
+
height: 1.5rem;
|
|
360
|
+
border: 0;
|
|
361
|
+
border-radius: 9999rem;
|
|
362
|
+
background: var(--popover);
|
|
363
|
+
color: var(--muted-foreground);
|
|
364
|
+
outline: none;
|
|
365
|
+
transition: color 150ms, background-color 150ms, box-shadow 150ms;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.toaster .opus-toast[data-sonner-toast] .opus-toast-close[data-close-button]:hover {
|
|
369
|
+
background: var(--accent);
|
|
370
|
+
color: var(--accent-foreground);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.toaster .opus-toast[data-sonner-toast] .opus-toast-close[data-close-button] svg {
|
|
374
|
+
width: 1rem;
|
|
375
|
+
height: 1rem;
|
|
376
|
+
}
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { AlertDialog as AlertDialogPrimitive } from "radix-ui"
|
|
3
|
-
|
|
4
|
-
import { cn } from '../../lib/cn.ts'
|
|
5
|
-
import { Button } from './button.tsx'
|
|
6
|
-
|
|
7
|
-
function AlertDialog({
|
|
8
|
-
...props
|
|
9
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
|
|
10
|
-
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function AlertDialogTrigger({
|
|
14
|
-
...props
|
|
15
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
|
|
16
|
-
return (
|
|
17
|
-
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function AlertDialogPortal({
|
|
22
|
-
...props
|
|
23
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
|
|
24
|
-
return (
|
|
25
|
-
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function AlertDialogOverlay({
|
|
30
|
-
className,
|
|
31
|
-
...props
|
|
32
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
|
|
33
|
-
return (
|
|
34
|
-
<AlertDialogPrimitive.Overlay
|
|
35
|
-
data-slot="alert-dialog-overlay"
|
|
36
|
-
className={cn(
|
|
37
|
-
"fixed inset-0 z-50 bg-black/30 backdrop-blur-xs data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
|
|
38
|
-
className
|
|
39
|
-
)}
|
|
40
|
-
{...props}
|
|
41
|
-
/>
|
|
42
|
-
)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function AlertDialogContent({
|
|
46
|
-
className,
|
|
47
|
-
...props
|
|
48
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
|
|
49
|
-
return (
|
|
50
|
-
<AlertDialogPortal>
|
|
51
|
-
<AlertDialogOverlay />
|
|
52
|
-
<AlertDialogPrimitive.Content
|
|
53
|
-
data-slot="alert-dialog-content"
|
|
54
|
-
className={cn(
|
|
55
|
-
"group/alert-dialog-content fixed top-[50%] left-[50%] z-50 grid w-full max-w-xs translate-x-[-50%] translate-y-[-50%] gap-4 rounded-xl border border-border bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
56
|
-
className
|
|
57
|
-
)}
|
|
58
|
-
{...props}
|
|
59
|
-
/>
|
|
60
|
-
</AlertDialogPortal>
|
|
61
|
-
)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function AlertDialogHeader({
|
|
65
|
-
className,
|
|
66
|
-
...props
|
|
67
|
-
}: React.ComponentProps<"div">) {
|
|
68
|
-
return (
|
|
69
|
-
<div
|
|
70
|
-
data-slot="alert-dialog-header"
|
|
71
|
-
className={cn(
|
|
72
|
-
"grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr]",
|
|
73
|
-
className
|
|
74
|
-
)}
|
|
75
|
-
{...props}
|
|
76
|
-
/>
|
|
77
|
-
)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function AlertDialogFooter({
|
|
81
|
-
className,
|
|
82
|
-
...props
|
|
83
|
-
}: React.ComponentProps<"div">) {
|
|
84
|
-
return (
|
|
85
|
-
<div
|
|
86
|
-
data-slot="alert-dialog-footer"
|
|
87
|
-
className={cn(
|
|
88
|
-
"grid grid-cols-2 gap-2",
|
|
89
|
-
className
|
|
90
|
-
)}
|
|
91
|
-
{...props}
|
|
92
|
-
/>
|
|
93
|
-
)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function AlertDialogTitle({
|
|
97
|
-
className,
|
|
98
|
-
...props
|
|
99
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
|
|
100
|
-
return (
|
|
101
|
-
<AlertDialogPrimitive.Title
|
|
102
|
-
data-slot="alert-dialog-title"
|
|
103
|
-
className={cn(
|
|
104
|
-
"text-lg font-semibold",
|
|
105
|
-
className
|
|
106
|
-
)}
|
|
107
|
-
{...props}
|
|
108
|
-
/>
|
|
109
|
-
)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function AlertDialogDescription({
|
|
113
|
-
className,
|
|
114
|
-
...props
|
|
115
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
|
|
116
|
-
return (
|
|
117
|
-
<AlertDialogPrimitive.Description
|
|
118
|
-
data-slot="alert-dialog-description"
|
|
119
|
-
className={cn("text-sm text-muted-foreground", className)}
|
|
120
|
-
{...props}
|
|
121
|
-
/>
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function AlertDialogMedia({
|
|
126
|
-
className,
|
|
127
|
-
...props
|
|
128
|
-
}: React.ComponentProps<"div">) {
|
|
129
|
-
return (
|
|
130
|
-
<div
|
|
131
|
-
data-slot="alert-dialog-media"
|
|
132
|
-
className={cn(
|
|
133
|
-
"mb-2 inline-flex size-16 items-center justify-center rounded-md bg-muted *:[svg:not([class*='size-'])]:size-8",
|
|
134
|
-
className
|
|
135
|
-
)}
|
|
136
|
-
{...props}
|
|
137
|
-
/>
|
|
138
|
-
)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function AlertDialogAction({
|
|
142
|
-
className,
|
|
143
|
-
variant = "default",
|
|
144
|
-
size = "default",
|
|
145
|
-
...props
|
|
146
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
|
|
147
|
-
Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
|
|
148
|
-
return (
|
|
149
|
-
<Button variant={variant} size={size} asChild>
|
|
150
|
-
<AlertDialogPrimitive.Action
|
|
151
|
-
data-slot="alert-dialog-action"
|
|
152
|
-
className={cn(className)}
|
|
153
|
-
{...props}
|
|
154
|
-
/>
|
|
155
|
-
</Button>
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function AlertDialogCancel({
|
|
160
|
-
className,
|
|
161
|
-
variant = "ghost",
|
|
162
|
-
size = "default",
|
|
163
|
-
...props
|
|
164
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
|
|
165
|
-
Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
|
|
166
|
-
return (
|
|
167
|
-
<Button variant={variant} size={size} asChild>
|
|
168
|
-
<AlertDialogPrimitive.Cancel
|
|
169
|
-
data-slot="alert-dialog-cancel"
|
|
170
|
-
className={cn(className)}
|
|
171
|
-
{...props}
|
|
172
|
-
/>
|
|
173
|
-
</Button>
|
|
174
|
-
)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export {
|
|
178
|
-
AlertDialog,
|
|
179
|
-
AlertDialogAction,
|
|
180
|
-
AlertDialogCancel,
|
|
181
|
-
AlertDialogContent,
|
|
182
|
-
AlertDialogDescription,
|
|
183
|
-
AlertDialogFooter,
|
|
184
|
-
AlertDialogHeader,
|
|
185
|
-
AlertDialogMedia,
|
|
186
|
-
AlertDialogOverlay,
|
|
187
|
-
AlertDialogPortal,
|
|
188
|
-
AlertDialogTitle,
|
|
189
|
-
AlertDialogTrigger,
|
|
190
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
## Quase sempre é o `dialog`
|
|
2
|
-
|
|
3
|
-
Pergunta de sim/não, um aviso a reconhecer, uma string — **não monte isto à mão**: o trio
|
|
4
|
-
`dialog.confirm/alert/prompt` faz em uma linha, e o `body` cobre até corpo com conteúdo
|
|
5
|
-
próprio (lista, detalhe). A demo viva mora na [página do dialog](/ui/confirm). Este
|
|
6
|
-
componente é o **primitivo por baixo deles**.
|
|
7
|
-
|
|
8
|
-
Então **componha o AlertDialog à mão só quando o trio não alcança**: mais de duas ações, ou
|
|
9
|
-
um layout totalmente custom. É o que as seções abaixo mostram.
|
|
10
|
-
|
|
11
|
-
## Mais de duas ações
|
|
12
|
-
|
|
13
|
-
O caso que o `confirm` (binário) não expressa: três saídas. `AlertDialogTrigger` (asChild
|
|
14
|
-
com Button) abre; cada `AlertDialogAction`/`AlertDialogCancel` é uma saída. Diferente do
|
|
15
|
-
Dialog, **não fecha clicando fora** — exige uma escolha.
|
|
16
|
-
|
|
17
|
-
```tsx preview
|
|
18
|
-
<AlertDialog>
|
|
19
|
-
<AlertDialogTrigger asChild>
|
|
20
|
-
<Button variant="outline">Fechar editor</Button>
|
|
21
|
-
</AlertDialogTrigger>
|
|
22
|
-
<AlertDialogContent>
|
|
23
|
-
<AlertDialogHeader>
|
|
24
|
-
<AlertDialogTitle>Alterações não salvas</AlertDialogTitle>
|
|
25
|
-
<AlertDialogDescription>
|
|
26
|
-
Você editou o contrato e ainda não salvou. O que fazer antes de fechar?
|
|
27
|
-
</AlertDialogDescription>
|
|
28
|
-
</AlertDialogHeader>
|
|
29
|
-
<AlertDialogFooter>
|
|
30
|
-
<AlertDialogCancel>Continuar editando</AlertDialogCancel>
|
|
31
|
-
<AlertDialogAction variant="outline">Descartar</AlertDialogAction>
|
|
32
|
-
<AlertDialogAction>Salvar e fechar</AlertDialogAction>
|
|
33
|
-
</AlertDialogFooter>
|
|
34
|
-
</AlertDialogContent>
|
|
35
|
-
</AlertDialog>
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## A estrutura e o botão destrutivo
|
|
39
|
-
|
|
40
|
-
`AlertDialogHeader` (media/título/descrição) + `AlertDialogFooter` (as ações).
|
|
41
|
-
`variant="destructive"` no `AlertDialogAction` (ele herda variant/size do Button) pinta o
|
|
42
|
-
confirmar de vermelho. Uma confirmação destrutiva **binária** é só
|
|
43
|
-
`dialog.confirm({ variant: 'destructive' })` — o exemplo abaixo é só pra mostrar a
|
|
44
|
-
composição e onde o `variant` entra:
|
|
45
|
-
|
|
46
|
-
```tsx preview
|
|
47
|
-
<AlertDialog>
|
|
48
|
-
<AlertDialogTrigger asChild>
|
|
49
|
-
<Button variant="destructive">Excluir repositório</Button>
|
|
50
|
-
</AlertDialogTrigger>
|
|
51
|
-
<AlertDialogContent>
|
|
52
|
-
<AlertDialogHeader>
|
|
53
|
-
<AlertDialogTitle>Excluir empresa-x-api?</AlertDialogTitle>
|
|
54
|
-
<AlertDialogDescription>
|
|
55
|
-
O repositório sai do workspace e os agentes vinculados perdem o acesso ao código. Não dá pra desfazer.
|
|
56
|
-
</AlertDialogDescription>
|
|
57
|
-
</AlertDialogHeader>
|
|
58
|
-
<AlertDialogFooter>
|
|
59
|
-
<AlertDialogCancel>Cancelar</AlertDialogCancel>
|
|
60
|
-
<AlertDialogAction variant="destructive">Excluir</AlertDialogAction>
|
|
61
|
-
</AlertDialogFooter>
|
|
62
|
-
</AlertDialogContent>
|
|
63
|
-
</AlertDialog>
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Props
|
|
67
|
-
|
|
68
|
-
| Prop | Tipo | Default | Descrição |
|
|
69
|
-
|---|---|---|---|
|
|
70
|
-
| `open` (AlertDialog) | `boolean` | | Estado de aberto no modo controlado — pareie com onOpenChange. No padrão, o Trigger cuida disso. |
|
|
71
|
-
| `onOpenChange` (AlertDialog) | `(open: boolean) => void` | | Chamado quando o diálogo abre ou fecha (Trigger, Cancel, Action ou Esc). |
|
|
72
|
-
| `variant` (AlertDialogAction) | `'default' \| 'destructive' \| 'outline' \| 'secondary' \| 'ghost' \| 'link'` | `'default'` | Intenção do botão de confirmar (herdada do Button) — destructive pra ação perigosa. |
|
|
73
|
-
| `variant` (AlertDialogCancel) | `'default' \| 'destructive' \| 'outline' \| 'secondary' \| 'ghost' \| 'link'` | `'ghost'` | Intenção do botão de cancelar (herdada do Button). |
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
## Básico
|
|
2
|
-
|
|
3
|
-
Envolva os botões no ButtonGroup: as bordas internas colapsam e só as pontas ficam arredondadas.
|
|
4
|
-
Use a mesma variant nos filhos pra manter a barra uniforme.
|
|
5
|
-
|
|
6
|
-
```tsx preview
|
|
7
|
-
<ButtonGroup>
|
|
8
|
-
<Button variant="outline">Visão geral</Button>
|
|
9
|
-
<Button variant="outline">Sessões</Button>
|
|
10
|
-
<Button variant="outline">Skills</Button>
|
|
11
|
-
</ButtonGroup>
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Ação dividida
|
|
15
|
-
|
|
16
|
-
O padrão da casa pra ação primária + variações: o botão principal, um separador e um gatilho de
|
|
17
|
-
menu (size=icon).
|
|
18
|
-
|
|
19
|
-
```tsx preview
|
|
20
|
-
<ButtonGroup>
|
|
21
|
-
<Button icon={Play}>Rodar agente developer</Button>
|
|
22
|
-
<ButtonGroupSeparator />
|
|
23
|
-
<Button size="icon" aria-label="Mais opções">
|
|
24
|
-
<ChevronDown />
|
|
25
|
-
</Button>
|
|
26
|
-
</ButtonGroup>
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Com rótulo
|
|
30
|
-
|
|
31
|
-
ButtonGroupText cola um rótulo (fundo muted) ao grupo — bom pra prefixar o contexto. Aceita ícone
|
|
32
|
-
via [&_svg] e asChild pra virar `<label>`.
|
|
33
|
-
|
|
34
|
-
```tsx preview
|
|
35
|
-
<ButtonGroup>
|
|
36
|
-
<ButtonGroupText>
|
|
37
|
-
<GitBranch />
|
|
38
|
-
empresa-x-api
|
|
39
|
-
</ButtonGroupText>
|
|
40
|
-
<Button variant="outline" icon={RotateCw}>
|
|
41
|
-
Sincronizar
|
|
42
|
-
</Button>
|
|
43
|
-
</ButtonGroup>
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Vertical
|
|
47
|
-
|
|
48
|
-
orientation=vertical empilha os filhos em coluna — as bordas que colapsam passam a ser as de
|
|
49
|
-
cima/baixo. Combina com size=icon pra uma mini-toolbar.
|
|
50
|
-
|
|
51
|
-
```tsx preview col-start
|
|
52
|
-
<ButtonGroup orientation="vertical">
|
|
53
|
-
<Button variant="outline" size="icon" aria-label="Rodar sessão">
|
|
54
|
-
<Play />
|
|
55
|
-
</Button>
|
|
56
|
-
<Button variant="outline" size="icon" aria-label="Pausar sessão">
|
|
57
|
-
<Pause />
|
|
58
|
-
</Button>
|
|
59
|
-
<Button variant="outline" size="icon" aria-label="Reiniciar sessão">
|
|
60
|
-
<RotateCw />
|
|
61
|
-
</Button>
|
|
62
|
-
</ButtonGroup>
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Props
|
|
66
|
-
|
|
67
|
-
| Prop | Tipo | Default | Descrição |
|
|
68
|
-
|---|---|---|---|
|
|
69
|
-
| `orientation` (ButtonGroup) | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção do bloco — vertical empilha os filhos em coluna. |
|
|
70
|
-
| `orientation` (ButtonGroupSeparator) | `'horizontal' \| 'vertical'` | `'vertical'` | Direção do traço divisor — herde do grupo (vertical no horizontal). |
|
|
71
|
-
| `asChild` (ButtonGroupText) | `boolean` | `false` | Renderiza como o filho (Radix Slot) — ex.: virar `<label>` sem forkar o estilo. |
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
## O trio imperativo: `dialog`
|
|
2
|
-
|
|
3
|
-
`dialog.alert` · `dialog.confirm` · `dialog.prompt` — o trio do browser (`alert`/`confirm`/`prompt`), mas **assíncrono, com tema e a11y-correto**. Cada um pergunta e **espera a resposta**:
|
|
4
|
-
|
|
5
|
-
```tsx
|
|
6
|
-
await dialog.alert({ title: 'Sessão expirada', description: 'Você será desconectado.' }) // Promise<void>
|
|
7
|
-
const ok = await dialog.confirm({ title: 'Excluir a sessão?', variant: 'destructive' }) // Promise<boolean>
|
|
8
|
-
const nome = await dialog.prompt({ title: 'Nome do snapshot', placeholder: 'ex.: v1-final' }) // Promise<string | null>
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
É a mesma forma do `toast` (superfície imperativa, host no shell), mas que **responde** — e é isso que tira as ~15 linhas de JSX que cada tela montava. Por baixo é o `AlertDialog`: `role="alertdialog"`, foco preso, **não fecha no clique fora** — a semântica de uma interrupção que EXIGE resposta. Fechar por ESC/Cancelar resolve o "não" de cada tipo (`void`, `false`, `null`).
|
|
12
|
-
|
|
13
|
-
```tsx preview
|
|
14
|
-
function Demo() {
|
|
15
|
-
const [last, setLast] = useState('—')
|
|
16
|
-
return (
|
|
17
|
-
<div className="flex flex-wrap items-center gap-3">
|
|
18
|
-
<DialogHost />
|
|
19
|
-
<Button variant="outline" size="sm" onClick={async () => {
|
|
20
|
-
await dialog.alert({ title: 'Sessão expirada', description: 'Você será desconectado.' })
|
|
21
|
-
setLast('alert → OK')
|
|
22
|
-
}}>alert</Button>
|
|
23
|
-
<Button variant="outline" size="sm" onClick={async () => {
|
|
24
|
-
const ok = await dialog.confirm({ title: 'Excluir a sessão?', description: 'Some o worktree e o preview.', action: 'Excluir', variant: 'destructive' })
|
|
25
|
-
setLast('confirm → ' + (ok ? 'excluída' : 'mantida'))
|
|
26
|
-
}}>confirm</Button>
|
|
27
|
-
<Button variant="outline" size="sm" onClick={async () => {
|
|
28
|
-
const name = await dialog.prompt({ title: 'Nome do snapshot', placeholder: 'ex.: v1-final' })
|
|
29
|
-
setLast('prompt → ' + (name === null ? 'cancelado' : name))
|
|
30
|
-
}}>prompt</Button>
|
|
31
|
-
<span className="text-sm text-muted-foreground">Última: {last}</span>
|
|
32
|
-
</div>
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
render(<Demo />)
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Por que namespace, e não `confirm()` solto
|
|
39
|
-
|
|
40
|
-
`alert`/`confirm`/`prompt` são **globais do browser**. Como função solta, esquecer o import não daria erro — o TypeScript resolveria pro global e o código abriria a caixa cinza do sistema (sem tema, que webview suprime). `window.dialog` não existe, então o namespace **imuniza** a família contra esse footgun. `confirm()` segue exportado como alias (retrocompat), mas o novo código usa `dialog.confirm`.
|
|
41
|
-
|
|
42
|
-
## `body`: corpo com conteúdo próprio
|
|
43
|
-
|
|
44
|
-
Os três aceitam `body` — conteúdo que a `description` de texto não expressa (uma lista do que será afetado, um detalhe destacado). É o que antes obrigava a compor o `AlertDialog` à mão:
|
|
45
|
-
|
|
46
|
-
```tsx preview
|
|
47
|
-
function Demo() {
|
|
48
|
-
const [status, setStatus] = useState('—')
|
|
49
|
-
return (
|
|
50
|
-
<div className="flex items-center gap-3">
|
|
51
|
-
<DialogHost />
|
|
52
|
-
<Button variant="outline" size="sm" onClick={async () => {
|
|
53
|
-
const ok = await dialog.confirm({
|
|
54
|
-
title: 'Encerrar 3 sessões?',
|
|
55
|
-
description: 'Os agentes abaixo serão interrompidos:',
|
|
56
|
-
body: (
|
|
57
|
-
<ul className="list-disc space-y-1 pl-5 text-sm text-muted-foreground">
|
|
58
|
-
<li><span className="font-medium text-foreground">developer</span> — empresa-x-api</li>
|
|
59
|
-
<li><span className="font-medium text-foreground">reviewer</span> — empresa-x-web</li>
|
|
60
|
-
</ul>
|
|
61
|
-
),
|
|
62
|
-
action: 'Encerrar todas',
|
|
63
|
-
})
|
|
64
|
-
setStatus(ok ? 'encerradas' : 'mantidas')
|
|
65
|
-
}}>Encerrar sessões</Button>
|
|
66
|
-
<span className="text-sm text-muted-foreground">Resposta: {status}</span>
|
|
67
|
-
</div>
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
render(<Demo />)
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## O host
|
|
74
|
-
|
|
75
|
-
Monte **um** `<DialogHost />` no shell do app, ao lado do `<Toaster />`:
|
|
76
|
-
|
|
77
|
-
```tsx
|
|
78
|
-
<>
|
|
79
|
-
{rotas}
|
|
80
|
-
<Toaster />
|
|
81
|
-
<DialogHost />
|
|
82
|
-
</>
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Sem ele, os três **lançam** — em vez de devolver uma promise que nunca resolve. Promise pendurada viraria clique sem efeito, o pior desfecho pra uma interrupção que exige resposta: a pessoa acha que respondeu, ou clica de novo. (`<ConfirmHost />` segue valendo como alias de `<DialogHost />`.)
|
|
86
|
-
|
|
87
|
-
Várias perguntas seguidas entram numa **fila** e aparecem uma por vez — cada `await` recebe a resposta do SEU diálogo (duas caixas empilhadas seriam ambíguas sobre qual clique respondeu o quê).
|
|
88
|
-
|
|
89
|
-
## Quando NÃO usar
|
|
90
|
-
|
|
91
|
-
- **Excluir uma entidade por contrato** → `ActionTrigger` com `icon={<Trash2 />}`: lê o `action.confirm`, dispara a action e fala o erro de negócio.
|
|
92
|
-
- **Um form de verdade** (validação, vários campos, submete uma action) → `ActionFormDialog`. O `dialog.prompt` é só pra UMA string livre.
|
|
93
|
-
- **Mais de duas ações** (ex.: Salvar / Descartar / Cancelar) ou **layout totalmente custom** → componha o `AlertDialog` à mão.
|
|
94
|
-
- **Só avisar, sem cobrar decisão** → `toast` (passageiro) ou `Alert` (fica na página). O `dialog.alert` é pro aviso que **bloqueia** e exige um "OK".
|
|
95
|
-
|
|
96
|
-
## Opções
|
|
97
|
-
|
|
98
|
-
Comuns aos três (`dialog.alert`/`confirm`/`prompt`):
|
|
99
|
-
|
|
100
|
-
| Opção | Tipo | Default | Descrição |
|
|
101
|
-
|---|---|---|---|
|
|
102
|
-
| `title` | `React.ReactNode` | | O enunciado. Obrigatório. |
|
|
103
|
-
| `description` | `React.ReactNode` | | O que a pessoa precisa saber pra decidir — a CONSEQUÊNCIA, não o óbvio. |
|
|
104
|
-
| `body` | `React.ReactNode` | | Conteúdo próprio entre a descrição e os botões (lista, detalhe). |
|
|
105
|
-
| `media` | `React.ReactNode` | | Ícone grande acima do título (o quadro do `AlertDialogMedia`). |
|
|
106
|
-
| `action` | `string` | `'OK'` / `'Confirmar'` | Rótulo do botão que confirma (default `'OK'` no alert, `'Confirmar'` nos outros). |
|
|
107
|
-
|
|
108
|
-
Só `confirm` e `prompt`:
|
|
109
|
-
|
|
110
|
-
| Opção | Tipo | Default | Descrição |
|
|
111
|
-
|---|---|---|---|
|
|
112
|
-
| `cancel` | `string` | `'Cancelar'` | Rótulo do botão que recusa. |
|
|
113
|
-
| `variant` | `'default' \| 'destructive'` | `'default'` | `destructive` pinta o confirmar — o que apaga tem que parecer o que apaga. |
|
|
114
|
-
|
|
115
|
-
Só `prompt`:
|
|
116
|
-
|
|
117
|
-
| Opção | Tipo | Default | Descrição |
|
|
118
|
-
|---|---|---|---|
|
|
119
|
-
| `placeholder` | `string` | | Texto-fantasma do input. |
|
|
120
|
-
| `defaultValue` | `string` | | Valor inicial (o input abre focado e selecionado). |
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
> Só um **ícone leading**? Não precisa do InputGroup — é a prop `icon` do `Input` (a mesma do Select e do Alert). O InputGroup é pros casos que a prop não cobre: botão no fim, prefixo de texto, adorno em bloco, múltiplos.
|
|
2
|
-
|
|
3
|
-
## Com ícone (addon)
|
|
4
|
-
|
|
5
|
-
InputGroupAddon na borda (align inline-start por padrão) cola um ícone ao InputGroupInput dentro da mesma moldura. Clicar no addon foca o campo. Pra um leading simples, prefira `<Input icon={…} />`; aqui é quando o ícone convive com outros adornos.
|
|
6
|
-
|
|
7
|
-
```tsx preview col
|
|
8
|
-
<InputGroup>
|
|
9
|
-
<InputGroupAddon>
|
|
10
|
-
<Search />
|
|
11
|
-
</InputGroupAddon>
|
|
12
|
-
<InputGroupInput placeholder="Buscar sessões do workspace" />
|
|
13
|
-
</InputGroup>
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Ícone + ação juntos (o caso do InputGroup)
|
|
17
|
-
|
|
18
|
-
> Só uma ação no fim (um limpar, um mostrar-senha)? Isso é `<Input trailing={…} />` — não precisa do InputGroup. O InputGroup entra quando os adornos se ACUMULAM: aqui, um ícone leading E um botão no fim, na mesma moldura (a busca do `ActionList` é assim). `align=inline-end` joga o addon pro fim; `InputGroupButton` já vem ghost, tamanho `icon-xs`.
|
|
19
|
-
|
|
20
|
-
```tsx preview col
|
|
21
|
-
<InputGroup>
|
|
22
|
-
<InputGroupAddon>
|
|
23
|
-
<Search />
|
|
24
|
-
</InputGroupAddon>
|
|
25
|
-
<InputGroupInput placeholder="Buscar agente" defaultValue="reviewer" />
|
|
26
|
-
<InputGroupAddon align="inline-end">
|
|
27
|
-
<InputGroupButton size="icon-xs" aria-label="Limpar filtro">
|
|
28
|
-
<X />
|
|
29
|
-
</InputGroupButton>
|
|
30
|
-
</InputGroupAddon>
|
|
31
|
-
</InputGroup>
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Prefixo e sufixo
|
|
35
|
-
|
|
36
|
-
InputGroupText é o rótulo inerte na borda — prefixo ou sufixo. Aqui emoldura o slug do repositório entre o host fixo e o sufixo .git.
|
|
37
|
-
|
|
38
|
-
```tsx preview col
|
|
39
|
-
<InputGroup>
|
|
40
|
-
<InputGroupAddon>
|
|
41
|
-
<GitBranch />
|
|
42
|
-
<InputGroupText>github.com/softize/</InputGroupText>
|
|
43
|
-
</InputGroupAddon>
|
|
44
|
-
<InputGroupInput placeholder="empresa-x-api" />
|
|
45
|
-
<InputGroupAddon align="inline-end">
|
|
46
|
-
<InputGroupText>.git</InputGroupText>
|
|
47
|
-
</InputGroupAddon>
|
|
48
|
-
</InputGroup>
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Composer com rodapé
|
|
52
|
-
|
|
53
|
-
Com InputGroupTextarea a moldura cresce; align=block-end empilha um rodapé de ações embaixo do campo — o padrão do composer de brief do agente.
|
|
54
|
-
|
|
55
|
-
```tsx preview col
|
|
56
|
-
<InputGroup>
|
|
57
|
-
<InputGroupTextarea placeholder="Descreva a task para o agente developer" rows={3} />
|
|
58
|
-
<InputGroupAddon align="block-end">
|
|
59
|
-
<InputGroupText>
|
|
60
|
-
<Sparkles />
|
|
61
|
-
Título gerado pela IA
|
|
62
|
-
</InputGroupText>
|
|
63
|
-
<InputGroupButton size="sm" variant="default" className="ml-auto" aria-label="Enviar brief">
|
|
64
|
-
Enviar
|
|
65
|
-
<ArrowUp />
|
|
66
|
-
</InputGroupButton>
|
|
67
|
-
</InputGroupAddon>
|
|
68
|
-
</InputGroup>
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Props
|
|
72
|
-
|
|
73
|
-
| Prop | Tipo | Default | Descrição |
|
|
74
|
-
|---|---|---|---|
|
|
75
|
-
| `align` (InputGroupAddon) | `'inline-start' \| 'inline-end' \| 'block-start' \| 'block-end'` | `'inline-start'` | Onde o addon ancora: inline nas laterais; block empilha (vira coluna) acima ou abaixo — block exige a moldura alta de um InputGroupTextarea. |
|
|
76
|
-
| `size` (InputGroupButton) | `'xs' \| 'sm' \| 'icon-xs' \| 'icon-sm'` | `'xs'` | Tamanho do botão embutido. As variantes icon-* são quadradas, pra um botão só de ícone. |
|
|
77
|
-
| `variant` (InputGroupButton) | `'default' \| 'secondary' \| 'outline' \| 'ghost' \| 'destructive' \| 'link'` | `'ghost'` | Intenção do botão (herda do Button). O padrão ghost some na moldura; use default pra a ação primária. |
|
|
78
|
-
| `placeholder / defaultValue / value` (InputGroupInput, InputGroupTextarea) | `string` | | Props nativas do <input>/<textarea> repassadas ao controle. Controle como faria com Input/Textarea (value + onChange). |
|