@softize/opus 15.2.2 → 16.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +51 -18
  2. package/bin/lib/check.mjs +98 -15
  3. package/bin/lib/copy.mjs +811 -314
  4. package/bin/lib/gen-manifest.mjs +24 -23
  5. package/bin/lib/gen-runner.mjs +188 -148
  6. package/docs/adr/0010-page-header-owns-page-chrome.md +3 -4
  7. package/docs/adr/0011-page-shell-coordinates-persistent-page-chrome.md +5 -3
  8. package/docs/adr/0012-modal-header-only-names-the-surface.md +45 -0
  9. package/docs/adr/0013-presentation-is-a-portable-action-oriented-artifact.md +92 -0
  10. package/docs/code-style.md +24 -19
  11. package/package.json +5 -1
  12. package/registry/skills/build-opus-ui/references/ui-patterns.md +43 -26
  13. package/src/core/presentation.ts +512 -0
  14. package/src/presentation/index.ts +1 -0
  15. package/src/ui/components/patterns/action-list-dialog.tsx +26 -9
  16. package/src/ui/components/patterns/confirm.tsx +34 -29
  17. package/src/ui/components/patterns/form-dialog.tsx +20 -8
  18. package/src/ui/components/patterns/list.tsx +26 -9
  19. package/src/ui/components/patterns/page.tsx +99 -139
  20. package/src/ui/components/patterns/presentation.tsx +316 -0
  21. package/src/ui/components/patterns/sidebar.tsx +3 -3
  22. package/src/ui/components/patterns/trigger.tsx +38 -17
  23. package/src/ui/components/primitives/button-group.tsx +53 -43
  24. package/src/ui/components/primitives/command.tsx +30 -72
  25. package/src/ui/components/primitives/dialog.tsx +23 -89
  26. package/src/ui/components/primitives/drawer.tsx +8 -34
  27. package/src/ui/docs/content/action-form-dialog.md +12 -10
  28. package/src/ui/docs/content/action-list-dialog.md +22 -17
  29. package/src/ui/docs/content/button.md +52 -35
  30. package/src/ui/docs/content/communication.md +26 -26
  31. package/src/ui/docs/content/dialog.md +173 -154
  32. package/src/ui/docs/content/drawer.md +12 -11
  33. package/src/ui/docs/content/page.md +72 -91
  34. package/src/ui/docs/content/presentation.md +158 -0
  35. package/src/ui/docs/registry.tsx +6 -0
  36. package/src/ui/meta.ts +8 -2
  37. package/src/ui/react.tsx +10 -3
@@ -1,26 +1,17 @@
1
- import * as React from "react"
2
- import { Command as CommandPrimitive } from "cmdk"
3
- import { SearchIcon } from "lucide-react"
1
+ import * as React from 'react'
2
+ import { Command as CommandPrimitive } from 'cmdk'
3
+ import { SearchIcon } from 'lucide-react'
4
4
 
5
5
  import { cn } from '../../lib/cn.ts'
6
- import {
7
- Dialog,
8
- DialogContent,
9
- DialogDescription,
10
- DialogHeader,
11
- DialogTitle,
12
- } from './dialog.tsx'
6
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from './dialog.tsx'
13
7
 
14
- function Command({
15
- className,
16
- ...props
17
- }: React.ComponentProps<typeof CommandPrimitive>) {
8
+ function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {
18
9
  return (
19
10
  <CommandPrimitive
20
11
  data-slot="command"
21
12
  className={cn(
22
- "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
23
- className
13
+ 'flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground',
14
+ className,
24
15
  )}
25
16
  {...props}
26
17
  />
@@ -28,8 +19,8 @@ function Command({
28
19
  }
29
20
 
30
21
  function CommandDialog({
31
- title = "Paleta de comandos",
32
- description = "Busque um comando para executar…",
22
+ title = 'Paleta de comandos',
23
+ description = 'Busque um comando para executar…',
33
24
  children,
34
25
  className,
35
26
  showCloseButton = true,
@@ -40,15 +31,17 @@ function CommandDialog({
40
31
  className?: string
41
32
  showCloseButton?: boolean
42
33
  }) {
34
+ const descriptionId = React.useId()
43
35
  return (
44
36
  <Dialog {...props}>
45
37
  <DialogHeader className="sr-only">
46
38
  <DialogTitle>{title}</DialogTitle>
47
- <DialogDescription>{description}</DialogDescription>
39
+ <p id={descriptionId}>{description}</p>
48
40
  </DialogHeader>
49
41
  <DialogContent
50
- className={cn("overflow-hidden p-0", className)}
42
+ className={cn('overflow-hidden p-0', className)}
51
43
  showCloseButton={showCloseButton}
44
+ aria-describedby={descriptionId}
52
45
  >
53
46
  <Command className="**:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
54
47
  {children}
@@ -58,21 +51,15 @@ function CommandDialog({
58
51
  )
59
52
  }
60
53
 
61
- function CommandInput({
62
- className,
63
- ...props
64
- }: React.ComponentProps<typeof CommandPrimitive.Input>) {
54
+ function CommandInput({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) {
65
55
  return (
66
- <div
67
- data-slot="command-input-wrapper"
68
- className="flex h-9 items-center gap-2 border-b px-3"
69
- >
56
+ <div data-slot="command-input-wrapper" className="flex h-9 items-center gap-2 border-b px-3">
70
57
  <SearchIcon className="size-4 shrink-0 opacity-50" />
71
58
  <CommandPrimitive.Input
72
59
  data-slot="command-input"
73
60
  className={cn(
74
- "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
75
- className
61
+ 'flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
62
+ className,
76
63
  )}
77
64
  {...props}
78
65
  />
@@ -80,90 +67,61 @@ function CommandInput({
80
67
  )
81
68
  }
82
69
 
83
- function CommandList({
84
- className,
85
- ...props
86
- }: React.ComponentProps<typeof CommandPrimitive.List>) {
70
+ function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
87
71
  return (
88
72
  <CommandPrimitive.List
89
73
  data-slot="command-list"
90
- className={cn(
91
- "max-h-[18.75rem] scroll-py-1 overflow-x-hidden overflow-y-auto",
92
- className
93
- )}
74
+ className={cn('max-h-[18.75rem] scroll-py-1 overflow-x-hidden overflow-y-auto', className)}
94
75
  {...props}
95
76
  />
96
77
  )
97
78
  }
98
79
 
99
- function CommandEmpty({
100
- ...props
101
- }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
102
- return (
103
- <CommandPrimitive.Empty
104
- data-slot="command-empty"
105
- className="py-6 text-center text-sm"
106
- {...props}
107
- />
108
- )
80
+ function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
81
+ return <CommandPrimitive.Empty data-slot="command-empty" className="py-6 text-center text-sm" {...props} />
109
82
  }
110
83
 
111
- function CommandGroup({
112
- className,
113
- ...props
114
- }: React.ComponentProps<typeof CommandPrimitive.Group>) {
84
+ function CommandGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {
115
85
  return (
116
86
  <CommandPrimitive.Group
117
87
  data-slot="command-group"
118
88
  className={cn(
119
- "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
120
- className
89
+ 'overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground',
90
+ className,
121
91
  )}
122
92
  {...props}
123
93
  />
124
94
  )
125
95
  }
126
96
 
127
- function CommandSeparator({
128
- className,
129
- ...props
130
- }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
97
+ function CommandSeparator({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
131
98
  return (
132
99
  <CommandPrimitive.Separator
133
100
  data-slot="command-separator"
134
- className={cn("-mx-1 h-px bg-border", className)}
101
+ className={cn('-mx-1 h-px bg-border', className)}
135
102
  {...props}
136
103
  />
137
104
  )
138
105
  }
139
106
 
140
- function CommandItem({
141
- className,
142
- ...props
143
- }: React.ComponentProps<typeof CommandPrimitive.Item>) {
107
+ function CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {
144
108
  return (
145
109
  <CommandPrimitive.Item
146
110
  data-slot="command-item"
147
111
  className={cn(
148
112
  "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
149
- className
113
+ className,
150
114
  )}
151
115
  {...props}
152
116
  />
153
117
  )
154
118
  }
155
119
 
156
- function CommandShortcut({
157
- className,
158
- ...props
159
- }: React.ComponentProps<"span">) {
120
+ function CommandShortcut({ className, ...props }: React.ComponentProps<'span'>) {
160
121
  return (
161
122
  <span
162
123
  data-slot="command-shortcut"
163
- className={cn(
164
- "ml-auto text-xs tracking-widest text-muted-foreground",
165
- className
166
- )}
124
+ className={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)}
167
125
  {...props}
168
126
  />
169
127
  )
@@ -12,26 +12,22 @@ interface DialogContextValue {
12
12
  onResult?: (result: string) => void
13
13
  }
14
14
 
15
- const DialogContext = React.createContext<DialogContextValue>({ mode: 'default' })
15
+ const DialogContext = React.createContext<DialogContextValue>({
16
+ mode: 'default',
17
+ })
16
18
 
17
19
  function useDialogContext(): DialogContextValue {
18
20
  return React.useContext(DialogContext)
19
21
  }
20
22
 
21
- export interface DialogProps
22
- extends React.ComponentProps<typeof DialogPrimitive.Root> {
23
+ export interface DialogProps extends React.ComponentProps<typeof DialogPrimitive.Root> {
23
24
  /** `alert` exige uma resposta explícita e deriva `role="alertdialog"`. */
24
25
  mode?: DialogMode
25
26
  /** Recebe o `result` do `DialogClose` acionado. */
26
27
  onResult?: (result: string) => void
27
28
  }
28
29
 
29
- export function Dialog({
30
- mode = 'default',
31
- onResult,
32
- modal,
33
- ...props
34
- }: DialogProps) {
30
+ export function Dialog({ mode = 'default', onResult, modal, ...props }: DialogProps) {
35
31
  const context = React.useMemo(() => ({ mode, onResult }), [mode, onResult])
36
32
 
37
33
  return (
@@ -45,9 +41,7 @@ export function Dialog({
45
41
  )
46
42
  }
47
43
 
48
- export function DialogTrigger({
49
- ...props
50
- }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
44
+ export function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
51
45
  const { mode } = useDialogContext()
52
46
  return mode === 'alert' ? (
53
47
  <AlertDialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
@@ -56,9 +50,7 @@ export function DialogTrigger({
56
50
  )
57
51
  }
58
52
 
59
- export function DialogPortal({
60
- ...props
61
- }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
53
+ export function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
62
54
  const { mode } = useDialogContext()
63
55
  return mode === 'alert' ? (
64
56
  <AlertDialogPrimitive.Portal data-slot="dialog-portal" {...props} />
@@ -67,20 +59,14 @@ export function DialogPortal({
67
59
  )
68
60
  }
69
61
 
70
- export interface DialogCloseProps
71
- extends React.ComponentProps<typeof DialogPrimitive.Close> {
62
+ export interface DialogCloseProps extends React.ComponentProps<typeof DialogPrimitive.Close> {
72
63
  /** No modo `alert`, registra este controle como destino inicial seguro do foco. */
73
64
  initialFocus?: boolean
74
65
  /** Resposta entregue a `Dialog.onResult` antes do fechamento. */
75
66
  result?: string
76
67
  }
77
68
 
78
- export function DialogClose({
79
- initialFocus = false,
80
- result,
81
- onClick,
82
- ...props
83
- }: DialogCloseProps) {
69
+ export function DialogClose({ initialFocus = false, result, onClick, ...props }: DialogCloseProps) {
84
70
  const root = useDialogContext()
85
71
  const handleClick: React.MouseEventHandler<HTMLButtonElement> = (event) => {
86
72
  onClick?.(event)
@@ -88,9 +74,7 @@ export function DialogClose({
88
74
  }
89
75
 
90
76
  if (root.mode === 'alert') {
91
- const ClosePrimitive = initialFocus
92
- ? AlertDialogPrimitive.Cancel
93
- : AlertDialogPrimitive.Action
77
+ const ClosePrimitive = initialFocus ? AlertDialogPrimitive.Cancel : AlertDialogPrimitive.Action
94
78
  return (
95
79
  <ClosePrimitive
96
80
  data-slot="dialog-close"
@@ -115,24 +99,13 @@ export function DialogClose({
115
99
  export const overlayClasses =
116
100
  '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'
117
101
 
118
- export function DialogOverlay({
119
- className,
120
- ...props
121
- }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
102
+ export function DialogOverlay({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
122
103
  const { mode } = useDialogContext()
123
104
  const overlay = cn(overlayClasses, className)
124
105
  return mode === 'alert' ? (
125
- <AlertDialogPrimitive.Overlay
126
- data-slot="dialog-overlay"
127
- className={overlay}
128
- {...props}
129
- />
106
+ <AlertDialogPrimitive.Overlay data-slot="dialog-overlay" className={overlay} {...props} />
130
107
  ) : (
131
- <DialogPrimitive.Overlay
132
- data-slot="dialog-overlay"
133
- className={overlay}
134
- {...props}
135
- />
108
+ <DialogPrimitive.Overlay data-slot="dialog-overlay" className={overlay} {...props} />
136
109
  )
137
110
  }
138
111
 
@@ -140,6 +113,7 @@ export function DialogContent({
140
113
  className,
141
114
  children,
142
115
  showCloseButton,
116
+ 'aria-describedby': ariaDescribedBy,
143
117
  ...props
144
118
  }: React.ComponentProps<typeof DialogPrimitive.Content> & {
145
119
  /** Mostra o “X” no modo padrão. O modo `alert` exige uma resposta identificável. */
@@ -158,6 +132,7 @@ export function DialogContent({
158
132
  data-slot="dialog-content"
159
133
  data-mode="alert"
160
134
  className={cn(contentClasses, 'grid max-w-xs gap-4 p-6', className)}
135
+ aria-describedby={ariaDescribedBy}
161
136
  {...props}
162
137
  >
163
138
  {children}
@@ -172,11 +147,8 @@ export function DialogContent({
172
147
  <DialogPrimitive.Content
173
148
  data-slot="dialog-content"
174
149
  data-mode="default"
175
- className={cn(
176
- contentClasses,
177
- 'flex max-h-[85vh] flex-col sm:max-w-lg',
178
- className,
179
- )}
150
+ className={cn(contentClasses, 'flex max-h-[85vh] flex-col sm:max-w-lg', className)}
151
+ aria-describedby={ariaDescribedBy}
180
152
  {...props}
181
153
  >
182
154
  {children}
@@ -197,10 +169,7 @@ export function DialogContent({
197
169
  )
198
170
  }
199
171
 
200
- export function DialogHeader({
201
- className,
202
- ...props
203
- }: React.ComponentProps<'div'>) {
172
+ export function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
204
173
  const { mode } = useDialogContext()
205
174
  return (
206
175
  <div
@@ -216,10 +185,7 @@ export function DialogHeader({
216
185
  )
217
186
  }
218
187
 
219
- export function DialogMedia({
220
- className,
221
- ...props
222
- }: React.ComponentProps<'div'>) {
188
+ export function DialogMedia({ className, ...props }: React.ComponentProps<'div'>) {
223
189
  return (
224
190
  <div
225
191
  data-slot="dialog-media"
@@ -232,27 +198,18 @@ export function DialogMedia({
232
198
  )
233
199
  }
234
200
 
235
- export function DialogBody({
236
- className,
237
- ...props
238
- }: React.ComponentProps<'div'>) {
201
+ export function DialogBody({ className, ...props }: React.ComponentProps<'div'>) {
239
202
  const { mode } = useDialogContext()
240
203
  return (
241
204
  <div
242
205
  data-slot="dialog-body"
243
- className={cn(
244
- mode === 'alert' ? 'min-w-0' : 'min-h-0 flex-1 overflow-y-auto p-5',
245
- className,
246
- )}
206
+ className={cn(mode === 'alert' ? 'min-w-0' : 'min-h-0 flex-1 overflow-y-auto p-5', className)}
247
207
  {...props}
248
208
  />
249
209
  )
250
210
  }
251
211
 
252
- export function DialogFooter({
253
- className,
254
- ...props
255
- }: React.ComponentProps<'div'>) {
212
+ export function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
256
213
  const { mode } = useDialogContext()
257
214
  return (
258
215
  <div
@@ -268,10 +225,7 @@ export function DialogFooter({
268
225
  )
269
226
  }
270
227
 
271
- export function DialogTitle({
272
- className,
273
- ...props
274
- }: React.ComponentProps<typeof DialogPrimitive.Title>) {
228
+ export function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
275
229
  const { mode } = useDialogContext()
276
230
  return mode === 'alert' ? (
277
231
  <AlertDialogPrimitive.Title
@@ -287,23 +241,3 @@ export function DialogTitle({
287
241
  />
288
242
  )
289
243
  }
290
-
291
- export function DialogDescription({
292
- className,
293
- ...props
294
- }: React.ComponentProps<typeof DialogPrimitive.Description>) {
295
- const { mode } = useDialogContext()
296
- return mode === 'alert' ? (
297
- <AlertDialogPrimitive.Description
298
- data-slot="dialog-description"
299
- className={cn('text-sm text-muted-foreground', className)}
300
- {...props}
301
- />
302
- ) : (
303
- <DialogPrimitive.Description
304
- data-slot="dialog-description"
305
- className={cn('text-sm text-muted-foreground', className)}
306
- {...props}
307
- />
308
- )
309
- }
@@ -6,27 +6,19 @@ import { cn } from "../../lib/cn.ts";
6
6
  import { focusRing } from "./control.ts";
7
7
  import { overlayClasses } from "./dialog.tsx";
8
8
 
9
- function Drawer({
10
- ...props
11
- }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
9
+ function Drawer({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
12
10
  return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
13
11
  }
14
12
 
15
- function DrawerTrigger({
16
- ...props
17
- }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
13
+ function DrawerTrigger({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
18
14
  return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
19
15
  }
20
16
 
21
- function DrawerClose({
22
- ...props
23
- }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
17
+ function DrawerClose({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
24
18
  return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
25
19
  }
26
20
 
27
- function DrawerPortal({
28
- ...props
29
- }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
21
+ function DrawerPortal({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
30
22
  return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
31
23
  }
32
24
 
@@ -37,10 +29,7 @@ function DrawerOverlay({
37
29
  return (
38
30
  <DrawerPrimitive.Overlay
39
31
  data-slot="drawer-overlay"
40
- className={cn(
41
- overlayClasses,
42
- className,
43
- )}
32
+ className={cn(overlayClasses, className)}
44
33
  {...props}
45
34
  />
46
35
  );
@@ -51,6 +40,7 @@ function DrawerContent({
51
40
  children,
52
41
  side = "right",
53
42
  showCloseButton = true,
43
+ "aria-describedby": ariaDescribedBy,
54
44
  ...props
55
45
  }: React.ComponentProps<typeof DrawerPrimitive.Content> & {
56
46
  side?: "top" | "right" | "bottom" | "left";
@@ -73,6 +63,7 @@ function DrawerContent({
73
63
  "inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
74
64
  className,
75
65
  )}
66
+ aria-describedby={ariaDescribedBy}
76
67
  {...props}
77
68
  >
78
69
  {children}
@@ -125,10 +116,7 @@ function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) {
125
116
  );
126
117
  }
127
118
 
128
- function DrawerTitle({
129
- className,
130
- ...props
131
- }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
119
+ function DrawerTitle({ className, ...props }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
132
120
  return (
133
121
  <DrawerPrimitive.Title
134
122
  data-slot="drawer-title"
@@ -138,19 +126,6 @@ function DrawerTitle({
138
126
  );
139
127
  }
140
128
 
141
- function DrawerDescription({
142
- className,
143
- ...props
144
- }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
145
- return (
146
- <DrawerPrimitive.Description
147
- data-slot="drawer-description"
148
- className={cn("text-sm text-muted-foreground", className)}
149
- {...props}
150
- />
151
- );
152
- }
153
-
154
129
  export {
155
130
  Drawer,
156
131
  DrawerTrigger,
@@ -160,5 +135,4 @@ export {
160
135
  DrawerBody,
161
136
  DrawerFooter,
162
137
  DrawerTitle,
163
- DrawerDescription,
164
138
  };
@@ -5,28 +5,30 @@ para outra página. O consumidor controla `open`; depois de uma execução bem-s
5
5
  fecha o modal. O cabeçalho e o rodapé permanecem visíveis enquanto os campos podem rolar.
6
6
 
7
7
  ```tsx preview
8
- const [open, setOpen] = useState(false)
8
+ const [open, setOpen] = useState(false);
9
9
 
10
10
  render(
11
11
  <DocBrowserActionProvider>
12
- <Button onClick={() => setOpen(true)}><Plus /> Novo workspace</Button>
12
+ <Button onClick={() => setOpen(true)}>
13
+ <Plus /> Novo workspace
14
+ </Button>
13
15
  <ActionFormDialog
14
16
  open={open}
15
17
  onOpenChange={setOpen}
16
18
  title="Novo workspace"
17
- description="O workspace agrupa os repositórios e agentes do cliente."
18
19
  action={docWorkspaceCreate}
19
20
  submitLabel="Criar workspace"
20
21
  />
21
22
  </DocBrowserActionProvider>,
22
- )
23
+ );
23
24
  ```
24
25
 
25
26
  ## Propriedades de ActionFormDialog
26
27
 
27
- | Propriedade | Tipo | Padrão | Descrição |
28
- |---|---|---|---|
29
- | `open / onOpenChange` | `boolean / (open: boolean) => void` | | Estado controlado do modal. `onOpenChange(false)` é chamado no sucesso e ao cancelar. |
30
- | `title / description` | `string` | | Conteúdo do cabeçalho; `description` é opcional. |
31
- | `submitLabel` | `string` | `'Salvar'` | Resultado da ação principal. Em criação, informe `Criar {recurso}`; em edição, `Salvar alterações`. |
32
- | `…ActionFormProps` | `action, defaultValues, onSuccess, fieldOptions…` | | Demais propriedades repassadas ao `ActionForm` interno. |
28
+ | Propriedade | Tipo | Padrão | Descrição |
29
+ | --------------------- | ------------------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------- |
30
+ | `open / onOpenChange` | `boolean / (open: boolean) => void` | | Estado controlado do modal. `onOpenChange(false)` é chamado no sucesso e ao cancelar. |
31
+ | `title` | `string` | | Nome da tarefa exibido no cabeçalho. |
32
+ | `intro` | `ReactNode` | | Contexto relevante no início do corpo, como texto ou `Alert`. |
33
+ | `submitLabel` | `string` | `'Salvar'` | Resultado da ação principal. Em criação, informe `Criar {recurso}`; em edição, `Salvar alterações`. |
34
+ | `…ActionFormProps` | `action, defaultValues, onSuccess, fieldOptions…` | | Demais propriedades repassadas ao `ActionForm` interno. |
@@ -6,7 +6,7 @@ outras actions. O consumidor compõe os itens por `children`; o modal mantém os
6
6
  ações.
7
7
 
8
8
  ```tsx preview col
9
- const [open, setOpen] = useState(false)
9
+ const [open, setOpen] = useState(false);
10
10
 
11
11
  render(
12
12
  <DocBrowserActionProvider>
@@ -17,7 +17,6 @@ render(
17
17
  open={open}
18
18
  onOpenChange={setOpen}
19
19
  title="Workspaces"
20
- description="Back-office"
21
20
  actions={
22
21
  <Button size="sm" variant="outline">
23
22
  <Plus /> Workspace
@@ -28,17 +27,22 @@ render(
28
27
  {(workspaces) => (
29
28
  <div className="space-y-2">
30
29
  {workspaces.map((w) => (
31
- <div key={w.id} className="flex items-center gap-2 rounded-md border border-border p-3">
30
+ <div
31
+ key={w.id}
32
+ className="flex items-center gap-2 rounded-md border border-border p-3"
33
+ >
32
34
  <span className="text-sm font-semibold">{w.name}</span>
33
35
  <DictionaryValue dict={docWorkspaceStatus} value={w.status} />
34
- <span className="ml-auto text-xs text-muted-foreground">{w.agents} agentes</span>
36
+ <span className="ml-auto text-xs text-muted-foreground">
37
+ {w.agents} agentes
38
+ </span>
35
39
  </div>
36
40
  ))}
37
41
  </div>
38
42
  )}
39
43
  </ActionListDialog>
40
44
  </DocBrowserActionProvider>,
41
- )
45
+ );
42
46
  ```
43
47
 
44
48
  ## Estado adicional do consumidor
@@ -59,15 +63,16 @@ corpo do modal.
59
63
 
60
64
  ## Propriedades de ActionListDialog
61
65
 
62
- | Propriedade | Tipo | Padrão | Descrição |
63
- |---|---|---|---|
64
- | `action / input` | `ListAction / TInput` | | O contrato e os filtros — mudou o input, re-busca; `invalidates` de forms/triggers refaz sozinho. |
65
- | `open / onOpenChange` | `boolean / (open) => void` | | Controle do modal — de quem orquestra. |
66
- | `title / description` | `string` | | O DialogHeader fixo (em geral, o que é a lista e de quem). |
67
- | `children` | `(items, refetch) => ReactNode` | | O layout dos itens (cards/linhas) os estados saíram daqui. |
68
- | `note` | `ReactNode \| (items) => ReactNode` | `"N no total"` | Nota à esquerda da toolbaro default é derivado dos itens. |
69
- | `actions` | `ReactNode` | | Ação à direita da toolbar — em geral o botão de criar. |
70
- | `empty` | `(items) => boolean` | `items.length === 0` | Sobrepõe o vazio derivado. |
71
- | `loading` | `boolean` | | Carga extra agregada à do fetch (query irmã). |
72
- | `emptyMessage / errorMessage / retryLabel` | `string` | | Textos dos estados; `retryLabel` nomeia a ação somente com ícone e seu tooltip. |
73
- | `className` | `string` | `sm:max-w-3xl` | Largura do DialogContent. |
66
+ | Propriedade | Tipo | Padrão | Descrição |
67
+ | ------------------------------------------ | ----------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------- |
68
+ | `action / input` | `ListAction / TInput` | | O contrato e os filtros — mudou o input, re-busca; `invalidates` de forms/triggers refaz sozinho. |
69
+ | `open / onOpenChange` | `boolean / (open) => void` | | Controle do modal — de quem orquestra. |
70
+ | `title` | `string` | | Nome da lista exibido no cabeçalho fixo. |
71
+ | `intro` | `ReactNode` | | Contexto relevante no início do corpo, como texto ou `Alert`. |
72
+ | `children` | `(items, refetch) => ReactNode` | | O layout dos itens (cards/linhas)os estados saíram daqui. |
73
+ | `note` | `ReactNode \| (items) => ReactNode` | `"N no total"` | Nota à esquerda da toolbar — o default é derivado dos itens. |
74
+ | `actions` | `ReactNode` | | Ação à direita da toolbar em geral o botão de criar. |
75
+ | `empty` | `(items) => boolean` | `items.length === 0` | Sobrepõe o vazio derivado. |
76
+ | `loading` | `boolean` | | Carga extra agregada à do fetch (query irmã). |
77
+ | `emptyMessage / errorMessage / retryLabel` | `string` | | Textos dos estados; `retryLabel` nomeia a ação somente com ícone e seu tooltip. |
78
+ | `className` | `string` | `sm:max-w-3xl` | Largura do DialogContent. |