@softize/opus 15.2.2 → 16.1.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 +60 -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 +539 -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 +188 -0
  35. package/src/ui/docs/registry.tsx +6 -0
  36. package/src/ui/meta.ts +8 -2
  37. package/src/ui/react.tsx +16 -3
@@ -1,71 +1,80 @@
1
- import { cva, type VariantProps } from "class-variance-authority"
2
- import { Slot } from "radix-ui"
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import { Slot } from "radix-ui";
3
3
 
4
- import { cn } from '../../lib/cn.ts'
5
- import { Separator } from './separator.tsx'
4
+ import { cn } from "../../lib/cn.ts";
5
+ import { Separator } from "./separator.tsx";
6
6
 
7
7
  const buttonGroupVariants = cva(
8
8
  "flex w-fit items-stretch has-[>[data-slot=button-group]]:gap-2 [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
9
9
  {
10
10
  variants: {
11
11
  orientation: {
12
- horizontal: '',
13
- vertical: 'flex-col',
12
+ horizontal: "",
13
+ vertical: "flex-col",
14
14
  },
15
15
  mode: {
16
- connected: '',
17
- spaced: 'gap-1',
16
+ connected: "",
17
+ spaced: "gap-1",
18
18
  },
19
19
  shape: {
20
- default: '',
21
- pill: 'rounded-full',
20
+ default: "",
21
+ pill: "rounded-full",
22
+ },
23
+ distribution: {
24
+ content: "",
25
+ equal:
26
+ "w-full flex-col-reverse gap-2 sm:flex-row [&>*]:min-w-0 [&>*]:w-full [&>*]:flex-1",
22
27
  },
23
28
  },
24
29
  compoundVariants: [
25
30
  {
26
- orientation: 'horizontal',
27
- mode: 'connected',
31
+ orientation: "horizontal",
32
+ mode: "connected",
28
33
  className:
29
- '[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none',
34
+ "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none",
30
35
  },
31
36
  {
32
- orientation: 'vertical',
33
- mode: 'connected',
37
+ orientation: "vertical",
38
+ mode: "connected",
34
39
  className:
35
- '[&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none',
40
+ "[&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none",
36
41
  },
37
42
  {
38
- orientation: 'horizontal',
39
- mode: 'connected',
40
- shape: 'pill',
41
- className: '[&>*:first-child]:rounded-l-full [&>*:last-child]:rounded-r-full [&>[data-slot=select]:first-child_[data-slot=select-control]]:rounded-l-full [&>[data-slot=select]:last-child_[data-slot=select-control]]:rounded-r-full [&>[data-slot=select-wrapper]:first-child_[data-slot=select]]:rounded-l-full [&>[data-slot=select-wrapper]:last-child_[data-slot=select]]:rounded-r-full',
43
+ orientation: "horizontal",
44
+ mode: "connected",
45
+ shape: "pill",
46
+ className:
47
+ "[&>*:first-child]:rounded-l-full [&>*:last-child]:rounded-r-full [&>[data-slot=select]:first-child_[data-slot=select-control]]:rounded-l-full [&>[data-slot=select]:last-child_[data-slot=select-control]]:rounded-r-full [&>[data-slot=select-wrapper]:first-child_[data-slot=select]]:rounded-l-full [&>[data-slot=select-wrapper]:last-child_[data-slot=select]]:rounded-r-full",
42
48
  },
43
49
  {
44
- orientation: 'vertical',
45
- mode: 'connected',
46
- shape: 'pill',
47
- className: '[&>*:first-child]:rounded-t-full [&>*:last-child]:rounded-b-full [&>[data-slot=select]:first-child_[data-slot=select-control]]:rounded-t-full [&>[data-slot=select]:last-child_[data-slot=select-control]]:rounded-b-full [&>[data-slot=select-wrapper]:first-child_[data-slot=select]]:rounded-t-full [&>[data-slot=select-wrapper]:last-child_[data-slot=select]]:rounded-b-full',
50
+ orientation: "vertical",
51
+ mode: "connected",
52
+ shape: "pill",
53
+ className:
54
+ "[&>*:first-child]:rounded-t-full [&>*:last-child]:rounded-b-full [&>[data-slot=select]:first-child_[data-slot=select-control]]:rounded-t-full [&>[data-slot=select]:last-child_[data-slot=select-control]]:rounded-b-full [&>[data-slot=select-wrapper]:first-child_[data-slot=select]]:rounded-t-full [&>[data-slot=select-wrapper]:last-child_[data-slot=select]]:rounded-b-full",
48
55
  },
49
56
  {
50
- mode: 'spaced',
51
- shape: 'pill',
57
+ mode: "spaced",
58
+ shape: "pill",
52
59
  className:
53
- '[&>*]:rounded-full [&>[data-slot=select]_[data-slot=select-control]]:rounded-full [&>[data-slot=select-wrapper]_[data-slot=select]]:rounded-full',
60
+ "[&>*]:rounded-full [&>[data-slot=select]_[data-slot=select-control]]:rounded-full [&>[data-slot=select-wrapper]_[data-slot=select]]:rounded-full",
54
61
  },
55
62
  ],
56
63
  defaultVariants: {
57
64
  orientation: "horizontal",
58
- mode: 'connected',
65
+ mode: "connected",
59
66
  shape: "default",
67
+ distribution: "content",
60
68
  },
61
- }
62
- )
69
+ },
70
+ );
63
71
 
64
72
  function ButtonGroup({
65
73
  className,
66
74
  orientation,
67
75
  mode,
68
76
  shape,
77
+ distribution,
69
78
  ...props
70
79
  }: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {
71
80
  return (
@@ -75,17 +84,18 @@ function ButtonGroup({
75
84
  data-orientation={orientation}
76
85
  data-mode={mode}
77
86
  data-shape={shape}
87
+ data-distribution={distribution}
78
88
  className={cn(
79
- buttonGroupVariants({ orientation, mode, shape }),
80
- mode !== 'spaced' &&
81
- (orientation === 'vertical'
82
- ? '[&>[data-slot=select]:not(:first-child)_[data-slot=select-control]]:rounded-t-none [&>[data-slot=select]:not(:first-child)_[data-slot=select-control]]:border-t-0 [&>[data-slot=select]:not(:last-child)_[data-slot=select-control]]:rounded-b-none [&>[data-slot=select-wrapper]:not(:first-child)_[data-slot=select]]:rounded-t-none [&>[data-slot=select-wrapper]:not(:first-child)_[data-slot=select]]:border-t-0 [&>[data-slot=select-wrapper]:not(:last-child)_[data-slot=select]]:rounded-b-none'
83
- : '[&>[data-slot=select]:not(:first-child)_[data-slot=select-control]]:rounded-l-none [&>[data-slot=select]:not(:first-child)_[data-slot=select-control]]:border-l-0 [&>[data-slot=select]:not(:last-child)_[data-slot=select-control]]:rounded-r-none [&>[data-slot=select-wrapper]:not(:first-child)_[data-slot=select]]:rounded-l-none [&>[data-slot=select-wrapper]:not(:first-child)_[data-slot=select]]:border-l-0 [&>[data-slot=select-wrapper]:not(:last-child)_[data-slot=select]]:rounded-r-none'),
89
+ buttonGroupVariants({ orientation, mode, shape, distribution }),
90
+ mode !== "spaced" &&
91
+ (orientation === "vertical"
92
+ ? "[&>[data-slot=select]:not(:first-child)_[data-slot=select-control]]:rounded-t-none [&>[data-slot=select]:not(:first-child)_[data-slot=select-control]]:border-t-0 [&>[data-slot=select]:not(:last-child)_[data-slot=select-control]]:rounded-b-none [&>[data-slot=select-wrapper]:not(:first-child)_[data-slot=select]]:rounded-t-none [&>[data-slot=select-wrapper]:not(:first-child)_[data-slot=select]]:border-t-0 [&>[data-slot=select-wrapper]:not(:last-child)_[data-slot=select]]:rounded-b-none"
93
+ : "[&>[data-slot=select]:not(:first-child)_[data-slot=select-control]]:rounded-l-none [&>[data-slot=select]:not(:first-child)_[data-slot=select-control]]:border-l-0 [&>[data-slot=select]:not(:last-child)_[data-slot=select-control]]:rounded-r-none [&>[data-slot=select-wrapper]:not(:first-child)_[data-slot=select]]:rounded-l-none [&>[data-slot=select-wrapper]:not(:first-child)_[data-slot=select]]:border-l-0 [&>[data-slot=select-wrapper]:not(:last-child)_[data-slot=select]]:rounded-r-none"),
84
94
  className,
85
95
  )}
86
96
  {...props}
87
97
  />
88
- )
98
+ );
89
99
  }
90
100
 
91
101
  function ButtonGroupText({
@@ -93,19 +103,19 @@ function ButtonGroupText({
93
103
  asChild = false,
94
104
  ...props
95
105
  }: React.ComponentProps<"div"> & {
96
- asChild?: boolean
106
+ asChild?: boolean;
97
107
  }) {
98
- const Comp = asChild ? Slot.Root : "div"
108
+ const Comp = asChild ? Slot.Root : "div";
99
109
 
100
110
  return (
101
111
  <Comp
102
112
  className={cn(
103
113
  "flex items-center gap-2 rounded-md border bg-muted px-4 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
104
- className
114
+ className,
105
115
  )}
106
116
  {...props}
107
117
  />
108
- )
118
+ );
109
119
  }
110
120
 
111
121
  function ButtonGroupSeparator({
@@ -119,11 +129,11 @@ function ButtonGroupSeparator({
119
129
  orientation={orientation}
120
130
  className={cn(
121
131
  "relative m-0! self-stretch bg-input data-[orientation=vertical]:h-auto",
122
- className
132
+ className,
123
133
  )}
124
134
  {...props}
125
135
  />
126
- )
136
+ );
127
137
  }
128
138
 
129
139
  export {
@@ -131,4 +141,4 @@ export {
131
141
  ButtonGroupSeparator,
132
142
  ButtonGroupText,
133
143
  buttonGroupVariants,
134
- }
144
+ };
@@ -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
- }