@srcroot/ui 0.0.54 → 0.0.56

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 (107) hide show
  1. package/README.md +151 -151
  2. package/dist/index.d.ts +0 -0
  3. package/dist/index.js +55 -1
  4. package/package.json +7 -2
  5. package/src/registry/analytics/google-analytics.tsx +36 -39
  6. package/src/registry/analytics/google-tag-manager.tsx +62 -65
  7. package/src/registry/analytics/meta-pixel.tsx +44 -47
  8. package/src/registry/analytics/microsoft-clarity.tsx +31 -34
  9. package/src/registry/analytics/tiktok-pixel.tsx +34 -37
  10. package/src/registry/lib/utils.ts +0 -0
  11. package/src/registry/themes/v3/blue.css +157 -157
  12. package/src/registry/themes/v3/glass.css +153 -153
  13. package/src/registry/themes/v3/gray.css +157 -157
  14. package/src/registry/themes/v3/green.css +157 -157
  15. package/src/registry/themes/v3/neutral.css +157 -157
  16. package/src/registry/themes/v3/orange.css +157 -157
  17. package/src/registry/themes/v3/rose.css +157 -157
  18. package/src/registry/themes/v3/slate.css +157 -157
  19. package/src/registry/themes/v3/stone.css +157 -157
  20. package/src/registry/themes/v3/violet.css +186 -186
  21. package/src/registry/themes/v3/zinc.css +157 -157
  22. package/src/registry/themes/v4/blue.css +184 -184
  23. package/src/registry/themes/v4/glass.css +180 -180
  24. package/src/registry/themes/v4/gray.css +184 -184
  25. package/src/registry/themes/v4/green.css +184 -184
  26. package/src/registry/themes/v4/neutral.css +184 -184
  27. package/src/registry/themes/v4/orange.css +184 -184
  28. package/src/registry/themes/v4/rose.css +184 -184
  29. package/src/registry/themes/v4/slate.css +184 -184
  30. package/src/registry/themes/v4/stone.css +184 -184
  31. package/src/registry/themes/v4/violet.css +184 -184
  32. package/src/registry/themes/v4/zinc.css +184 -184
  33. package/src/registry/ui/accordion.tsx +164 -165
  34. package/src/registry/ui/alert-dialog.tsx +213 -214
  35. package/src/registry/ui/alert.tsx +73 -76
  36. package/src/registry/ui/aspect-ratio.tsx +44 -47
  37. package/src/registry/ui/avatar.tsx +96 -97
  38. package/src/registry/ui/badge.tsx +52 -55
  39. package/src/registry/ui/breadcrumb.tsx +147 -150
  40. package/src/registry/ui/button-group.tsx +64 -67
  41. package/src/registry/ui/button.tsx +71 -72
  42. package/src/registry/ui/calendar.tsx +514 -515
  43. package/src/registry/ui/card.tsx +88 -91
  44. package/src/registry/ui/carousel.tsx +214 -214
  45. package/src/registry/ui/chart.tsx +373 -373
  46. package/src/registry/ui/chatbot.tsx +86 -13
  47. package/src/registry/ui/checkbox.tsx +93 -94
  48. package/src/registry/ui/collapsible.tsx +107 -108
  49. package/src/registry/ui/combobox.tsx +171 -171
  50. package/src/registry/ui/command.tsx +300 -300
  51. package/src/registry/ui/container.tsx +44 -47
  52. package/src/registry/ui/context-menu.tsx +221 -221
  53. package/src/registry/ui/date-picker.tsx +228 -228
  54. package/src/registry/ui/dialog.tsx +269 -270
  55. package/src/registry/ui/drawer.tsx +10 -4
  56. package/src/registry/ui/dropdown-menu.tsx +529 -530
  57. package/src/registry/ui/empty-state.tsx +0 -2
  58. package/src/registry/ui/file-upload.tsx +0 -0
  59. package/src/registry/ui/floating-dock.tsx +0 -0
  60. package/src/registry/ui/form-field.tsx +91 -94
  61. package/src/registry/ui/google-analytics.tsx +38 -0
  62. package/src/registry/ui/google-tag-manager.tsx +64 -0
  63. package/src/registry/ui/hover-card.tsx +223 -223
  64. package/src/registry/ui/image.tsx +144 -147
  65. package/src/registry/ui/input-group.tsx +82 -85
  66. package/src/registry/ui/input.tsx +125 -125
  67. package/src/registry/ui/kbd.tsx +60 -63
  68. package/src/registry/ui/label.tsx +36 -37
  69. package/src/registry/ui/loading-spinner.tsx +108 -111
  70. package/src/registry/ui/map.tsx +0 -0
  71. package/src/registry/ui/marquee.tsx +2 -0
  72. package/src/registry/ui/menubar.tsx +246 -246
  73. package/src/registry/ui/meta-pixel.tsx +46 -0
  74. package/src/registry/ui/microsoft-clarity.tsx +33 -0
  75. package/src/registry/ui/native-select.tsx +49 -52
  76. package/src/registry/ui/otp-input.tsx +152 -155
  77. package/src/registry/ui/pagination.tsx +149 -152
  78. package/src/registry/ui/patterns.tsx +28 -0
  79. package/src/registry/ui/popover.tsx +226 -227
  80. package/src/registry/ui/progress.tsx +51 -52
  81. package/src/registry/ui/radio.tsx +99 -102
  82. package/src/registry/ui/resizable.tsx +314 -314
  83. package/src/registry/ui/scroll-animation.tsx +45 -0
  84. package/src/registry/ui/scroll-area.tsx +121 -122
  85. package/src/registry/ui/scroll-to-top.tsx +0 -0
  86. package/src/registry/ui/search.tsx +147 -150
  87. package/src/registry/ui/select.tsx +292 -293
  88. package/src/registry/ui/separator.tsx +46 -47
  89. package/src/registry/ui/sheet.tsx +6 -3
  90. package/src/registry/ui/sidebar.tsx +628 -628
  91. package/src/registry/ui/skeleton.tsx +26 -29
  92. package/src/registry/ui/slider.tsx +196 -197
  93. package/src/registry/ui/slot.tsx +69 -72
  94. package/src/registry/ui/star-rating.tsx +131 -134
  95. package/src/registry/ui/switch.tsx +72 -73
  96. package/src/registry/ui/table-of-contents.tsx +96 -96
  97. package/src/registry/ui/table.tsx +138 -139
  98. package/src/registry/ui/tabs.tsx +124 -125
  99. package/src/registry/ui/text.tsx +61 -64
  100. package/src/registry/ui/textarea.tsx +41 -42
  101. package/src/registry/ui/theme-switcher.tsx +66 -66
  102. package/src/registry/ui/tiktok-pixel.tsx +36 -0
  103. package/src/registry/ui/toast.tsx +97 -98
  104. package/src/registry/ui/toggle-group.tsx +129 -129
  105. package/src/registry/ui/toggle.tsx +72 -72
  106. package/src/registry/ui/tooltip.tsx +143 -144
  107. package/src/registry/ui/whatsapp.tsx +0 -0
@@ -1,270 +1,269 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import { createPortal } from "react-dom"
5
- import { cn } from "@/lib/utils"
6
- import { Slot } from "@/components/ui/slot"
7
-
8
- interface DialogContextValue {
9
- open: boolean
10
- onOpenChange: (open: boolean) => void
11
- }
12
-
13
- const DialogContext = React.createContext<DialogContextValue | null>(null)
14
-
15
- function useDialogContext() {
16
- const context = React.useContext(DialogContext)
17
- if (!context) {
18
- throw new Error("Dialog components must be used within a Dialog")
19
- }
20
- return context
21
- }
22
-
23
- interface DialogProps {
24
- children: React.ReactNode
25
- open?: boolean
26
- onOpenChange?: (open: boolean) => void
27
- defaultOpen?: boolean
28
- }
29
-
30
- /**
31
- * Dialog component with focus trap and keyboard handling
32
- *
33
- * @example
34
- * const [open, setOpen] = useState(false)
35
- *
36
- * <Dialog open={open} onOpenChange={setOpen}>
37
- * <DialogTrigger asChild>
38
- * <Button>Open Dialog</Button>
39
- * </DialogTrigger>
40
- * <DialogContent>
41
- * <DialogHeader>
42
- * <DialogTitle>Title</DialogTitle>
43
- * <DialogDescription>Description</DialogDescription>
44
- * </DialogHeader>
45
- * <div>Content</div>
46
- * <DialogFooter>
47
- * <Button>Save</Button>
48
- * </DialogFooter>
49
- * </DialogContent>
50
- * </Dialog>
51
- */
52
- function Dialog({ children, open: controlledOpen, onOpenChange, defaultOpen = false }: DialogProps) {
53
- const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)
54
-
55
- const open = controlledOpen !== undefined ? controlledOpen : uncontrolledOpen
56
- const setOpen = onOpenChange || setUncontrolledOpen
57
-
58
- return (
59
- <DialogContext.Provider value={{ open, onOpenChange: setOpen }}>
60
- {children}
61
- </DialogContext.Provider>
62
- )
63
- }
64
-
65
- interface DialogTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
66
- asChild?: boolean
67
- }
68
-
69
- const DialogTrigger = React.forwardRef<HTMLButtonElement, DialogTriggerProps>(
70
- ({ onClick, asChild, children, ...props }, ref) => {
71
- const { onOpenChange } = useDialogContext()
72
-
73
- const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
74
- onClick?.(e)
75
- onOpenChange(true)
76
- }
77
-
78
- const Comp = asChild ? Slot : "button"
79
-
80
- return (
81
- <Comp ref={ref} onClick={handleClick} {...props}>
82
- {children}
83
- </Comp>
84
- )
85
- }
86
- )
87
- DialogTrigger.displayName = "DialogTrigger"
88
-
89
- const DialogPortal = ({ children }: { children: React.ReactNode }) => {
90
- const { open } = useDialogContext()
91
- const [mounted, setMounted] = React.useState(false)
92
-
93
- React.useEffect(() => {
94
- setMounted(true)
95
- }, [])
96
-
97
- if (!open) return null
98
- if (!mounted) return null
99
-
100
- return createPortal(children, document.body)
101
- }
102
-
103
- const DialogOverlay = React.forwardRef<
104
- HTMLDivElement,
105
- React.HTMLAttributes<HTMLDivElement>
106
- >(({ className, ...props }, ref) => (
107
- <div
108
- ref={ref}
109
- className={cn(
110
- "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
111
- className
112
- )}
113
- {...props}
114
- />
115
- ))
116
- DialogOverlay.displayName = "DialogOverlay"
117
-
118
- const DialogContent = React.forwardRef<
119
- HTMLDivElement,
120
- React.HTMLAttributes<HTMLDivElement>
121
- >(({ className, children, ...props }, ref) => {
122
- const { open, onOpenChange } = useDialogContext()
123
- const contentRef = React.useRef<HTMLDivElement>(null)
124
-
125
- // Handle escape key
126
- React.useEffect(() => {
127
- const handleEscape = (e: KeyboardEvent) => {
128
- if (e.key === "Escape") {
129
- onOpenChange(false)
130
- }
131
- }
132
-
133
- if (open) {
134
- document.addEventListener("keydown", handleEscape)
135
- document.body.style.overflow = "hidden"
136
- }
137
-
138
- return () => {
139
- document.removeEventListener("keydown", handleEscape)
140
- document.body.style.overflow = ""
141
- }
142
- }, [open, onOpenChange])
143
-
144
- // Focus trap
145
- React.useEffect(() => {
146
- if (open && contentRef.current) {
147
- const focusableElements = contentRef.current.querySelectorAll(
148
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
149
- )
150
- const firstElement = focusableElements[0] as HTMLElement
151
- const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement
152
-
153
- const handleTab = (e: KeyboardEvent) => {
154
- if (e.key === "Tab") {
155
- if (e.shiftKey && document.activeElement === firstElement) {
156
- e.preventDefault()
157
- lastElement?.focus()
158
- } else if (!e.shiftKey && document.activeElement === lastElement) {
159
- e.preventDefault()
160
- firstElement?.focus()
161
- }
162
- }
163
- }
164
-
165
- document.addEventListener("keydown", handleTab)
166
- firstElement?.focus()
167
-
168
- return () => document.removeEventListener("keydown", handleTab)
169
- }
170
- }, [open])
171
-
172
- if (!open) return null
173
-
174
- return (
175
- <DialogPortal>
176
- <DialogOverlay onClick={() => onOpenChange(false)} />
177
- <div
178
- ref={(node) => {
179
- (contentRef as any).current = node
180
- if (typeof ref === "function") ref(node)
181
- else if (ref) ref.current = node
182
- }}
183
- role="dialog"
184
- aria-modal="true"
185
- className={cn(
186
- "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 sm:rounded-lg",
187
- className
188
- )}
189
- {...props}
190
- >
191
- {children}
192
- <button
193
- className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none"
194
- onClick={() => onOpenChange(false)}
195
- >
196
- <svg
197
- className="h-4 w-4"
198
- fill="none"
199
- viewBox="0 0 24 24"
200
- stroke="currentColor"
201
- strokeWidth={2}
202
- >
203
- <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
204
- </svg>
205
- <span className="sr-only">Close</span>
206
- </button>
207
- </div>
208
- </DialogPortal>
209
- )
210
- })
211
- DialogContent.displayName = "DialogContent"
212
-
213
- const DialogHeader = ({
214
- className,
215
- ...props
216
- }: React.HTMLAttributes<HTMLDivElement>) => (
217
- <div
218
- className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)}
219
- {...props}
220
- />
221
- )
222
- DialogHeader.displayName = "DialogHeader"
223
-
224
- const DialogFooter = ({
225
- className,
226
- ...props
227
- }: React.HTMLAttributes<HTMLDivElement>) => (
228
- <div
229
- className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
230
- {...props}
231
- />
232
- )
233
- DialogFooter.displayName = "DialogFooter"
234
-
235
- const DialogTitle = React.forwardRef<
236
- HTMLHeadingElement,
237
- React.HTMLAttributes<HTMLHeadingElement>
238
- >(({ className, ...props }, ref) => (
239
- <h2
240
- ref={ref}
241
- className={cn("text-lg font-semibold leading-none tracking-tight", className)}
242
- {...props}
243
- />
244
- ))
245
- DialogTitle.displayName = "DialogTitle"
246
-
247
- const DialogDescription = React.forwardRef<
248
- HTMLParagraphElement,
249
- React.HTMLAttributes<HTMLParagraphElement>
250
- >(({ className, ...props }, ref) => (
251
- <p
252
- ref={ref}
253
- className={cn("text-sm text-muted-foreground", className)}
254
- {...props}
255
- />
256
- ))
257
- DialogDescription.displayName = "DialogDescription"
258
-
259
- export {
260
- Dialog,
261
- DialogPortal,
262
- DialogOverlay,
263
- DialogTrigger,
264
- DialogContent,
265
- DialogHeader,
266
- DialogFooter,
267
- DialogTitle,
268
- DialogDescription,
269
- }
270
-
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { createPortal } from "react-dom"
5
+ import { cn } from "@/lib/utils"
6
+ import { Slot } from "@/components/ui/slot"
7
+
8
+ interface DialogContextValue {
9
+ open: boolean
10
+ onOpenChange: (open: boolean) => void
11
+ }
12
+
13
+ const DialogContext = React.createContext<DialogContextValue | null>(null)
14
+
15
+ function useDialogContext() {
16
+ const context = React.useContext(DialogContext)
17
+ if (!context) {
18
+ throw new Error("Dialog components must be used within a Dialog")
19
+ }
20
+ return context
21
+ }
22
+
23
+ interface DialogProps {
24
+ children: React.ReactNode
25
+ open?: boolean
26
+ onOpenChange?: (open: boolean) => void
27
+ defaultOpen?: boolean
28
+ }
29
+
30
+ /**
31
+ * Dialog component with focus trap and keyboard handling
32
+ *
33
+ * @example
34
+ * const [open, setOpen] = useState(false)
35
+ *
36
+ * <Dialog open={open} onOpenChange={setOpen}>
37
+ * <DialogTrigger asChild>
38
+ * <Button>Open Dialog</Button>
39
+ * </DialogTrigger>
40
+ * <DialogContent>
41
+ * <DialogHeader>
42
+ * <DialogTitle>Title</DialogTitle>
43
+ * <DialogDescription>Description</DialogDescription>
44
+ * </DialogHeader>
45
+ * <div>Content</div>
46
+ * <DialogFooter>
47
+ * <Button>Save</Button>
48
+ * </DialogFooter>
49
+ * </DialogContent>
50
+ * </Dialog>
51
+ */
52
+ function Dialog({ children, open: controlledOpen, onOpenChange, defaultOpen = false }: DialogProps) {
53
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)
54
+
55
+ const open = controlledOpen !== undefined ? controlledOpen : uncontrolledOpen
56
+ const setOpen = onOpenChange || setUncontrolledOpen
57
+
58
+ return (
59
+ <DialogContext.Provider value={{ open, onOpenChange: setOpen }}>
60
+ {children}
61
+ </DialogContext.Provider>
62
+ )
63
+ }
64
+
65
+ interface DialogTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
66
+ asChild?: boolean
67
+ }
68
+
69
+ const DialogTrigger = React.forwardRef<HTMLButtonElement, DialogTriggerProps>(
70
+ ({ onClick, asChild, children, ...props }, ref) => {
71
+ const { onOpenChange } = useDialogContext()
72
+
73
+ const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
74
+ onClick?.(e)
75
+ onOpenChange(true)
76
+ }
77
+
78
+ const Comp = asChild ? Slot : "button"
79
+
80
+ return (
81
+ <Comp ref={ref} onClick={handleClick} {...props}>
82
+ {children}
83
+ </Comp>
84
+ )
85
+ }
86
+ )
87
+ DialogTrigger.displayName = "DialogTrigger"
88
+
89
+ const DialogPortal = ({ children }: { children: React.ReactNode }) => {
90
+ const { open } = useDialogContext()
91
+ const [mounted, setMounted] = React.useState(false)
92
+
93
+ React.useEffect(() => {
94
+ setMounted(true)
95
+ }, [])
96
+
97
+ if (!open) return null
98
+ if (!mounted) return null
99
+
100
+ return createPortal(children, document.body)
101
+ }
102
+
103
+ const DialogOverlay = React.forwardRef<
104
+ HTMLDivElement,
105
+ React.HTMLAttributes<HTMLDivElement>
106
+ >(({ className, ...props }, ref) => (
107
+ <div
108
+ ref={ref}
109
+ className={cn(
110
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
111
+ className
112
+ )}
113
+ {...props}
114
+ />
115
+ ))
116
+ DialogOverlay.displayName = "DialogOverlay"
117
+
118
+ const DialogContent = React.forwardRef<
119
+ HTMLDivElement,
120
+ React.HTMLAttributes<HTMLDivElement>
121
+ >(({ className, children, ...props }, ref) => {
122
+ const { open, onOpenChange } = useDialogContext()
123
+ const contentRef = React.useRef<HTMLDivElement>(null)
124
+
125
+ // Handle escape key
126
+ React.useEffect(() => {
127
+ const handleEscape = (e: KeyboardEvent) => {
128
+ if (e.key === "Escape") {
129
+ onOpenChange(false)
130
+ }
131
+ }
132
+
133
+ if (open) {
134
+ document.addEventListener("keydown", handleEscape)
135
+ document.body.style.overflow = "hidden"
136
+ }
137
+
138
+ return () => {
139
+ document.removeEventListener("keydown", handleEscape)
140
+ document.body.style.overflow = ""
141
+ }
142
+ }, [open, onOpenChange])
143
+
144
+ // Focus trap
145
+ React.useEffect(() => {
146
+ if (open && contentRef.current) {
147
+ const focusableElements = contentRef.current.querySelectorAll(
148
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
149
+ )
150
+ const firstElement = focusableElements[0] as HTMLElement
151
+ const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement
152
+
153
+ const handleTab = (e: KeyboardEvent) => {
154
+ if (e.key === "Tab") {
155
+ if (e.shiftKey && document.activeElement === firstElement) {
156
+ e.preventDefault()
157
+ lastElement?.focus()
158
+ } else if (!e.shiftKey && document.activeElement === lastElement) {
159
+ e.preventDefault()
160
+ firstElement?.focus()
161
+ }
162
+ }
163
+ }
164
+
165
+ document.addEventListener("keydown", handleTab)
166
+ firstElement?.focus()
167
+
168
+ return () => document.removeEventListener("keydown", handleTab)
169
+ }
170
+ }, [open])
171
+
172
+ if (!open) return null
173
+
174
+ return (
175
+ <DialogPortal>
176
+ <DialogOverlay onClick={() => onOpenChange(false)} />
177
+ <div
178
+ ref={(node) => {
179
+ (contentRef as any).current = node
180
+ if (typeof ref === "function") ref(node)
181
+ else if (ref) ref.current = node
182
+ }}
183
+ role="dialog"
184
+ aria-modal="true"
185
+ className={cn(
186
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 sm:rounded-lg",
187
+ className
188
+ )}
189
+ {...props}
190
+ >
191
+ {children}
192
+ <button
193
+ className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none"
194
+ onClick={() => onOpenChange(false)}
195
+ >
196
+ <svg
197
+ className="h-4 w-4"
198
+ fill="none"
199
+ viewBox="0 0 24 24"
200
+ stroke="currentColor"
201
+ strokeWidth={2}
202
+ >
203
+ <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
204
+ </svg>
205
+ <span className="sr-only">Close</span>
206
+ </button>
207
+ </div>
208
+ </DialogPortal>
209
+ )
210
+ })
211
+ DialogContent.displayName = "DialogContent"
212
+
213
+ const DialogHeader = ({
214
+ className,
215
+ ...props
216
+ }: React.HTMLAttributes<HTMLDivElement>) => (
217
+ <div
218
+ className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)}
219
+ {...props}
220
+ />
221
+ )
222
+ DialogHeader.displayName = "DialogHeader"
223
+
224
+ const DialogFooter = ({
225
+ className,
226
+ ...props
227
+ }: React.HTMLAttributes<HTMLDivElement>) => (
228
+ <div
229
+ className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
230
+ {...props}
231
+ />
232
+ )
233
+ DialogFooter.displayName = "DialogFooter"
234
+
235
+ const DialogTitle = React.forwardRef<
236
+ HTMLHeadingElement,
237
+ React.HTMLAttributes<HTMLHeadingElement>
238
+ >(({ className, ...props }, ref) => (
239
+ <h2
240
+ ref={ref}
241
+ className={cn("text-lg font-semibold leading-none tracking-tight", className)}
242
+ {...props}
243
+ />
244
+ ))
245
+ DialogTitle.displayName = "DialogTitle"
246
+
247
+ const DialogDescription = React.forwardRef<
248
+ HTMLParagraphElement,
249
+ React.HTMLAttributes<HTMLParagraphElement>
250
+ >(({ className, ...props }, ref) => (
251
+ <p
252
+ ref={ref}
253
+ className={cn("text-sm text-muted-foreground", className)}
254
+ {...props}
255
+ />
256
+ ))
257
+ DialogDescription.displayName = "DialogDescription"
258
+
259
+ export {
260
+ Dialog,
261
+ DialogPortal,
262
+ DialogOverlay,
263
+ DialogTrigger,
264
+ DialogContent,
265
+ DialogHeader,
266
+ DialogFooter,
267
+ DialogTitle,
268
+ DialogDescription,
269
+ }
@@ -86,8 +86,12 @@ const DrawerTrigger = React.forwardRef<HTMLButtonElement, DrawerTriggerProps>(
86
86
  DrawerTrigger.displayName = "DrawerTrigger"
87
87
 
88
88
  // Drawer Close
89
- const DrawerClose = React.forwardRef<HTMLButtonElement, React.ButtonHTMLAttributes<HTMLButtonElement>>(
90
- ({ children, onClick, ...props }, ref) => {
89
+ interface DrawerCloseProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
90
+ asChild?: boolean
91
+ }
92
+
93
+ const DrawerClose = React.forwardRef<HTMLButtonElement, DrawerCloseProps>(
94
+ ({ children, onClick, asChild, ...props }, ref) => {
91
95
  const { onOpenChange } = useDrawer()
92
96
 
93
97
  const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
@@ -95,10 +99,12 @@ const DrawerClose = React.forwardRef<HTMLButtonElement, React.ButtonHTMLAttribut
95
99
  onOpenChange(false)
96
100
  }
97
101
 
102
+ const Comp = asChild ? Slot : "button"
103
+
98
104
  return (
99
- <button ref={ref} onClick={handleClick} {...props}>
105
+ <Comp ref={ref} onClick={handleClick} {...props}>
100
106
  {children}
101
- </button>
107
+ </Comp>
102
108
  )
103
109
  }
104
110
  )