@softize/opus 12.11.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 +29 -0
- package/bin/lib/check.mjs +2 -7
- package/bin/lib/copy.mjs +1 -5
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +93 -10
- 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/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/src/ui/components/patterns/confirm.tsx +140 -40
- package/src/ui/components/patterns/list.tsx +35 -40
- package/src/ui/components/patterns/page-state.tsx +2 -2
- package/src/ui/components/patterns/sidebar.tsx +26 -26
- package/src/ui/components/patterns/trigger.tsx +25 -22
- package/src/ui/components/primitives/alert.tsx +3 -3
- package/src/ui/components/primitives/dialog.tsx +196 -39
- package/src/ui/components/primitives/drawer.tsx +8 -5
- package/src/ui/components/primitives/empty.tsx +3 -3
- package/src/ui/components/primitives/item.tsx +3 -3
- package/src/ui/components/primitives/sonner.tsx +187 -8
- package/src/ui/docs/DocBrowser.tsx +102 -23
- 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 +14 -12
- 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 +3 -3
- package/src/ui/docs/content/breadcrumb.md +13 -8
- package/src/ui/docs/content/button.md +81 -6
- package/src/ui/docs/content/calendar.md +5 -5
- package/src/ui/docs/content/card.md +1 -1
- 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 +2 -2
- package/src/ui/docs/content/copyable.md +4 -3
- package/src/ui/docs/content/customization.md +5 -5
- 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 +3 -3
- package/src/ui/docs/content/dialog.md +339 -31
- package/src/ui/docs/content/dictionary-value.md +8 -8
- package/src/ui/docs/content/dock.md +3 -3
- package/src/ui/docs/content/drawer.md +27 -14
- 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 +27 -13
- 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 +34 -16
- package/src/ui/docs/content/observability.md +2 -2
- package/src/ui/docs/content/page.md +51 -6
- 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/separator.md +5 -5
- package/src/ui/docs/content/sidebar.md +323 -54
- package/src/ui/docs/content/skeleton.md +3 -2
- 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 +13 -3
- 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 +3 -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/registry.tsx +20 -37
- package/src/ui/meta.ts +64 -94
- package/src/ui/react.tsx +15 -16
- package/src/ui/theme.css +50 -0
- package/src/ui/components/primitives/alert-dialog.tsx +0 -192
- 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 -79
- package/src/ui/docs/content/page-state.md +0 -45
- package/src/ui/docs/content/toggle-group.md +0 -81
package/src/ui/react.tsx
CHANGED
|
@@ -125,11 +125,17 @@ export {
|
|
|
125
125
|
DialogOverlay,
|
|
126
126
|
DialogContent,
|
|
127
127
|
DialogHeader,
|
|
128
|
+
DialogMedia,
|
|
128
129
|
DialogBody,
|
|
129
130
|
DialogFooter,
|
|
130
131
|
DialogTitle,
|
|
131
132
|
DialogDescription,
|
|
132
133
|
} from "./components/primitives/dialog.tsx";
|
|
134
|
+
export type {
|
|
135
|
+
DialogProps,
|
|
136
|
+
DialogCloseProps,
|
|
137
|
+
DialogMode,
|
|
138
|
+
} from "./components/primitives/dialog.tsx";
|
|
133
139
|
|
|
134
140
|
export {
|
|
135
141
|
Menu,
|
|
@@ -203,6 +209,10 @@ export type {
|
|
|
203
209
|
} from "./components/primitives/select.tsx";
|
|
204
210
|
|
|
205
211
|
export { Toaster, toast } from "./components/primitives/sonner.tsx";
|
|
212
|
+
export type {
|
|
213
|
+
ToastAction,
|
|
214
|
+
ToastOptions,
|
|
215
|
+
} from "./components/primitives/sonner.tsx";
|
|
206
216
|
export { Markdown } from "./components/primitives/markdown.tsx";
|
|
207
217
|
|
|
208
218
|
// shadcn (lote inicial — byte-fiéis via registry JSON, locked no registry.lock.json)
|
|
@@ -212,20 +222,6 @@ export {
|
|
|
212
222
|
AccordionItem,
|
|
213
223
|
AccordionTrigger,
|
|
214
224
|
} from "./components/primitives/accordion.tsx";
|
|
215
|
-
export {
|
|
216
|
-
AlertDialog,
|
|
217
|
-
AlertDialogAction,
|
|
218
|
-
AlertDialogCancel,
|
|
219
|
-
AlertDialogContent,
|
|
220
|
-
AlertDialogDescription,
|
|
221
|
-
AlertDialogFooter,
|
|
222
|
-
AlertDialogHeader,
|
|
223
|
-
AlertDialogMedia,
|
|
224
|
-
AlertDialogOverlay,
|
|
225
|
-
AlertDialogPortal,
|
|
226
|
-
AlertDialogTitle,
|
|
227
|
-
AlertDialogTrigger,
|
|
228
|
-
} from "./components/primitives/alert-dialog.tsx";
|
|
229
225
|
export { AspectRatio } from "./components/primitives/aspect-ratio.tsx";
|
|
230
226
|
export {
|
|
231
227
|
Avatar,
|
|
@@ -279,7 +275,7 @@ export {
|
|
|
279
275
|
} from "./components/primitives/drawer.tsx";
|
|
280
276
|
export {
|
|
281
277
|
Empty,
|
|
282
|
-
|
|
278
|
+
EmptyActions,
|
|
283
279
|
EmptyDescription,
|
|
284
280
|
EmptyHeader,
|
|
285
281
|
EmptyMedia,
|
|
@@ -407,7 +403,9 @@ export {
|
|
|
407
403
|
} from "./components/patterns/confirm.tsx";
|
|
408
404
|
export type {
|
|
409
405
|
AlertOptions,
|
|
406
|
+
ChooseOptions,
|
|
410
407
|
ConfirmOptions,
|
|
408
|
+
DialogChoice,
|
|
411
409
|
PromptOptions,
|
|
412
410
|
} from "./components/patterns/confirm.tsx";
|
|
413
411
|
export type { ActionTriggerProps } from "./components/patterns/trigger.tsx";
|
|
@@ -488,13 +486,14 @@ export {
|
|
|
488
486
|
Sidebar,
|
|
489
487
|
SidebarItem,
|
|
490
488
|
SidebarGroupLabel,
|
|
489
|
+
SidebarTreeGroup,
|
|
491
490
|
SidebarNav,
|
|
492
491
|
} from "./components/patterns/sidebar.tsx";
|
|
493
492
|
export type {
|
|
494
493
|
SidebarProps,
|
|
495
494
|
SidebarItemProps,
|
|
495
|
+
SidebarTreeGroupProps,
|
|
496
496
|
SidebarNavGroup,
|
|
497
|
-
SidebarNavSubgroup,
|
|
498
497
|
SidebarNavItem,
|
|
499
498
|
} from "./components/patterns/sidebar.tsx";
|
|
500
499
|
|
package/src/ui/theme.css
CHANGED
|
@@ -324,3 +324,53 @@
|
|
|
324
324
|
.prose :where(pre):not(:where([class~='not-prose'], [class~='not-prose'] *)) {
|
|
325
325
|
border: 1px solid var(--border);
|
|
326
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,192 +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
|
-
context = "primary",
|
|
144
|
-
variant = "solid",
|
|
145
|
-
size = "default",
|
|
146
|
-
...props
|
|
147
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
|
|
148
|
-
Pick<React.ComponentProps<typeof Button>, "context" | "variant" | "size">) {
|
|
149
|
-
return (
|
|
150
|
-
<Button context={context} variant={variant} size={size} asChild>
|
|
151
|
-
<AlertDialogPrimitive.Action
|
|
152
|
-
data-slot="alert-dialog-action"
|
|
153
|
-
className={cn(className)}
|
|
154
|
-
{...props}
|
|
155
|
-
/>
|
|
156
|
-
</Button>
|
|
157
|
-
)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function AlertDialogCancel({
|
|
161
|
-
className,
|
|
162
|
-
context = "neutral",
|
|
163
|
-
variant = "ghost",
|
|
164
|
-
size = "default",
|
|
165
|
-
...props
|
|
166
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
|
|
167
|
-
Pick<React.ComponentProps<typeof Button>, "context" | "variant" | "size">) {
|
|
168
|
-
return (
|
|
169
|
-
<Button context={context} variant={variant} size={size} asChild>
|
|
170
|
-
<AlertDialogPrimitive.Cancel
|
|
171
|
-
data-slot="alert-dialog-cancel"
|
|
172
|
-
className={cn(className)}
|
|
173
|
-
{...props}
|
|
174
|
-
/>
|
|
175
|
-
</Button>
|
|
176
|
-
)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export {
|
|
180
|
-
AlertDialog,
|
|
181
|
-
AlertDialogAction,
|
|
182
|
-
AlertDialogCancel,
|
|
183
|
-
AlertDialogContent,
|
|
184
|
-
AlertDialogDescription,
|
|
185
|
-
AlertDialogFooter,
|
|
186
|
-
AlertDialogHeader,
|
|
187
|
-
AlertDialogMedia,
|
|
188
|
-
AlertDialogOverlay,
|
|
189
|
-
AlertDialogPortal,
|
|
190
|
-
AlertDialogTitle,
|
|
191
|
-
AlertDialogTrigger,
|
|
192
|
-
}
|
|
@@ -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
|
-
`context="danger"` no `AlertDialogAction` (ele herda contexto, variante e tamanho do Button)
|
|
42
|
-
comunica que a confirmação é perigosa. Uma confirmação destrutiva **binária** é só
|
|
43
|
-
`dialog.confirm({ context: 'danger' })` — o exemplo abaixo é só para mostrar a composição e
|
|
44
|
-
onde o `context` entra:
|
|
45
|
-
|
|
46
|
-
```tsx preview
|
|
47
|
-
<AlertDialog>
|
|
48
|
-
<AlertDialogTrigger asChild>
|
|
49
|
-
<Button context="danger">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 context="danger">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
|
-
| `context` (Action/Cancel) | `'neutral' \| 'primary' \| 'danger'` | `primary` / `neutral` | Significado semântico herdado do Button. |
|
|
73
|
-
| `variant` (Action/Cancel) | `'solid' \| 'subtle' \| 'outline' \| 'ghost' \| 'link'` | `solid` / `ghost` | Tratamento visual herdado 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,79 +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" context="primary" variant="solid" 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
|
-
| `context` (InputGroupButton) | `'neutral' \| 'primary' \| 'danger'` | `'primary'` | Significado semântico herdado do Button. |
|
|
78
|
-
| `variant` (InputGroupButton) | `'solid' \| 'subtle' \| 'outline' \| 'ghost' \| 'link'` | `'ghost'` | Tratamento visual herdado do Button. O padrão ghost some na moldura; use `solid` para dar ênfase à ação. |
|
|
79
|
-
| `placeholder / defaultValue / value` (InputGroupInput, InputGroupTextarea) | `string` | | Props nativas do <input>/<textarea> repassadas ao controle. Controle como faria com Input/Textarea (value + onChange). |
|