@bupple/vss-ui 1.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 (65) hide show
  1. package/.turbo/turbo-lint.log +4 -0
  2. package/components.json +21 -0
  3. package/eslint.config.js +4 -0
  4. package/index.ts +2 -0
  5. package/package.json +67 -0
  6. package/postcss.config.mjs +6 -0
  7. package/src/components/accordion.tsx +84 -0
  8. package/src/components/alert-dialog.tsx +198 -0
  9. package/src/components/alert.tsx +77 -0
  10. package/src/components/aspect-ratio.tsx +11 -0
  11. package/src/components/avatar.tsx +109 -0
  12. package/src/components/badge.tsx +50 -0
  13. package/src/components/breadcrumb.tsx +118 -0
  14. package/src/components/button-group.tsx +84 -0
  15. package/src/components/button.tsx +68 -0
  16. package/src/components/calendar.tsx +223 -0
  17. package/src/components/card.tsx +102 -0
  18. package/src/components/carousel.tsx +241 -0
  19. package/src/components/chart.tsx +373 -0
  20. package/src/components/checkbox.tsx +32 -0
  21. package/src/components/collapsible.tsx +33 -0
  22. package/src/components/combobox.tsx +299 -0
  23. package/src/components/command.tsx +194 -0
  24. package/src/components/context-menu.tsx +272 -0
  25. package/src/components/dialog.tsx +171 -0
  26. package/src/components/direction.tsx +20 -0
  27. package/src/components/drawer.tsx +130 -0
  28. package/src/components/dropdown-menu.tsx +278 -0
  29. package/src/components/empty.tsx +102 -0
  30. package/src/components/field.tsx +237 -0
  31. package/src/components/hover-card.tsx +43 -0
  32. package/src/components/input-group.tsx +157 -0
  33. package/src/components/input.tsx +18 -0
  34. package/src/components/item.tsx +197 -0
  35. package/src/components/kbd.tsx +26 -0
  36. package/src/components/label.tsx +21 -0
  37. package/src/components/menubar.tsx +283 -0
  38. package/src/components/native-select.tsx +64 -0
  39. package/src/components/navigation-menu.tsx +166 -0
  40. package/src/components/pagination.tsx +131 -0
  41. package/src/components/popover.tsx +88 -0
  42. package/src/components/progress.tsx +30 -0
  43. package/src/components/radio-group.tsx +46 -0
  44. package/src/components/resizable.tsx +49 -0
  45. package/src/components/scroll-area.tsx +52 -0
  46. package/src/components/select.tsx +209 -0
  47. package/src/components/separator.tsx +25 -0
  48. package/src/components/sheet.tsx +152 -0
  49. package/src/components/sidebar.tsx +703 -0
  50. package/src/components/skeleton.tsx +13 -0
  51. package/src/components/slider.tsx +58 -0
  52. package/src/components/sonner.tsx +45 -0
  53. package/src/components/spinner.tsx +15 -0
  54. package/src/components/switch.tsx +32 -0
  55. package/src/components/table.tsx +115 -0
  56. package/src/components/tabs.tsx +89 -0
  57. package/src/components/textarea.tsx +17 -0
  58. package/src/components/toggle-group.tsx +86 -0
  59. package/src/components/toggle.tsx +48 -0
  60. package/src/components/tooltip.tsx +56 -0
  61. package/src/hooks/use-mobile.ts +19 -0
  62. package/src/lib/portal-container.ts +11 -0
  63. package/src/lib/utils.ts +8 -0
  64. package/src/theme.css +125 -0
  65. package/tsconfig.json +15 -0
@@ -0,0 +1,272 @@
1
+ import { usePortalContainer } from '@bupple/vss-ui/lib/portal-container'
2
+ import { cn } from '@bupple/vss-ui/lib/utils'
3
+ import { CheckIcon, ChevronRightIcon } from 'lucide-react'
4
+ import { ContextMenu as ContextMenuPrimitive } from 'radix-ui'
5
+ import * as React from 'react'
6
+
7
+ function ContextMenu({
8
+ ...props
9
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
10
+ return <ContextMenuPrimitive.Root data-slot='context-menu' {...props} />
11
+ }
12
+
13
+ function ContextMenuTrigger({
14
+ className,
15
+ ...props
16
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {
17
+ return (
18
+ <ContextMenuPrimitive.Trigger
19
+ data-slot='context-menu-trigger'
20
+ className={cn('select-none', className)}
21
+ {...props}
22
+ />
23
+ )
24
+ }
25
+
26
+ function ContextMenuGroup({
27
+ ...props
28
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {
29
+ return (
30
+ <ContextMenuPrimitive.Group data-slot='context-menu-group' {...props} />
31
+ )
32
+ }
33
+
34
+ function ContextMenuPortal({
35
+ ...props
36
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
37
+ return (
38
+ <ContextMenuPrimitive.Portal data-slot='context-menu-portal' {...props} />
39
+ )
40
+ }
41
+
42
+ function ContextMenuSub({
43
+ ...props
44
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
45
+ return <ContextMenuPrimitive.Sub data-slot='context-menu-sub' {...props} />
46
+ }
47
+
48
+ function ContextMenuRadioGroup({
49
+ ...props
50
+ }: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {
51
+ return (
52
+ <ContextMenuPrimitive.RadioGroup
53
+ data-slot='context-menu-radio-group'
54
+ {...props}
55
+ />
56
+ )
57
+ }
58
+
59
+ function ContextMenuContent({
60
+ className,
61
+ ...props
62
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Content> & {
63
+ side?: 'top' | 'right' | 'bottom' | 'left'
64
+ }) {
65
+ const container = usePortalContainer()
66
+ return (
67
+ <ContextMenuPrimitive.Portal container={container ?? undefined}>
68
+ <ContextMenuPrimitive.Content
69
+ data-slot='context-menu-content'
70
+ className={cn(
71
+ 'z-50 max-h-(--radix-context-menu-content-available-height) min-w-36 origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
72
+ className,
73
+ )}
74
+ {...props}
75
+ />
76
+ </ContextMenuPrimitive.Portal>
77
+ )
78
+ }
79
+
80
+ function ContextMenuItem({
81
+ className,
82
+ inset,
83
+ variant = 'default',
84
+ ...props
85
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
86
+ inset?: boolean
87
+ variant?: 'default' | 'destructive'
88
+ }) {
89
+ return (
90
+ <ContextMenuPrimitive.Item
91
+ data-slot='context-menu-item'
92
+ data-inset={inset}
93
+ data-variant={variant}
94
+ className={cn(
95
+ "group/context-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive",
96
+ className,
97
+ )}
98
+ {...props}
99
+ />
100
+ )
101
+ }
102
+
103
+ function ContextMenuSubTrigger({
104
+ className,
105
+ inset,
106
+ children,
107
+ subTriggerIcon,
108
+ ...props
109
+ }: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
110
+ inset?: boolean
111
+ subTriggerIcon?: React.ReactNode
112
+ }) {
113
+ return (
114
+ <ContextMenuPrimitive.SubTrigger
115
+ data-slot='context-menu-sub-trigger'
116
+ data-inset={inset}
117
+ className={cn(
118
+ "flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
119
+ className,
120
+ )}
121
+ {...props}
122
+ >
123
+ {children}
124
+ {subTriggerIcon ?? <ChevronRightIcon className='ml-auto' />}
125
+ </ContextMenuPrimitive.SubTrigger>
126
+ )
127
+ }
128
+
129
+ function ContextMenuSubContent({
130
+ className,
131
+ ...props
132
+ }: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
133
+ return (
134
+ <ContextMenuPrimitive.SubContent
135
+ data-slot='context-menu-sub-content'
136
+ className={cn(
137
+ 'z-50 min-w-32 origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-lg border bg-popover p-1 text-popover-foreground shadow-lg duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
138
+ className,
139
+ )}
140
+ {...props}
141
+ />
142
+ )
143
+ }
144
+
145
+ function ContextMenuCheckboxItem({
146
+ className,
147
+ children,
148
+ checked,
149
+ inset,
150
+ checkIcon,
151
+ ...props
152
+ }: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem> & {
153
+ inset?: boolean
154
+ checkIcon?: React.ReactNode
155
+ }) {
156
+ return (
157
+ <ContextMenuPrimitive.CheckboxItem
158
+ data-slot='context-menu-checkbox-item'
159
+ data-inset={inset}
160
+ className={cn(
161
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
162
+ className,
163
+ )}
164
+ checked={checked}
165
+ {...props}
166
+ >
167
+ <span className='pointer-events-none absolute right-2'>
168
+ <ContextMenuPrimitive.ItemIndicator>
169
+ {checkIcon ?? <CheckIcon />}
170
+ </ContextMenuPrimitive.ItemIndicator>
171
+ </span>
172
+ {children}
173
+ </ContextMenuPrimitive.CheckboxItem>
174
+ )
175
+ }
176
+
177
+ function ContextMenuRadioItem({
178
+ className,
179
+ children,
180
+ inset,
181
+ radioIcon,
182
+ ...props
183
+ }: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem> & {
184
+ inset?: boolean
185
+ radioIcon?: React.ReactNode
186
+ }) {
187
+ return (
188
+ <ContextMenuPrimitive.RadioItem
189
+ data-slot='context-menu-radio-item'
190
+ data-inset={inset}
191
+ className={cn(
192
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
193
+ className,
194
+ )}
195
+ {...props}
196
+ >
197
+ <span className='pointer-events-none absolute right-2'>
198
+ <ContextMenuPrimitive.ItemIndicator>
199
+ {radioIcon ?? <CheckIcon />}
200
+ </ContextMenuPrimitive.ItemIndicator>
201
+ </span>
202
+ {children}
203
+ </ContextMenuPrimitive.RadioItem>
204
+ )
205
+ }
206
+
207
+ function ContextMenuLabel({
208
+ className,
209
+ inset,
210
+ ...props
211
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
212
+ inset?: boolean
213
+ }) {
214
+ return (
215
+ <ContextMenuPrimitive.Label
216
+ data-slot='context-menu-label'
217
+ data-inset={inset}
218
+ className={cn(
219
+ 'px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7',
220
+ className,
221
+ )}
222
+ {...props}
223
+ />
224
+ )
225
+ }
226
+
227
+ function ContextMenuSeparator({
228
+ className,
229
+ ...props
230
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {
231
+ return (
232
+ <ContextMenuPrimitive.Separator
233
+ data-slot='context-menu-separator'
234
+ className={cn('-mx-1 my-1 h-px bg-border', className)}
235
+ {...props}
236
+ />
237
+ )
238
+ }
239
+
240
+ function ContextMenuShortcut({
241
+ className,
242
+ ...props
243
+ }: React.ComponentProps<'span'>) {
244
+ return (
245
+ <span
246
+ data-slot='context-menu-shortcut'
247
+ className={cn(
248
+ 'ml-auto text-xs tracking-widest text-muted-foreground group-focus/context-menu-item:text-accent-foreground',
249
+ className,
250
+ )}
251
+ {...props}
252
+ />
253
+ )
254
+ }
255
+
256
+ export {
257
+ ContextMenu,
258
+ ContextMenuTrigger,
259
+ ContextMenuContent,
260
+ ContextMenuItem,
261
+ ContextMenuCheckboxItem,
262
+ ContextMenuRadioItem,
263
+ ContextMenuLabel,
264
+ ContextMenuSeparator,
265
+ ContextMenuShortcut,
266
+ ContextMenuGroup,
267
+ ContextMenuPortal,
268
+ ContextMenuSub,
269
+ ContextMenuSubContent,
270
+ ContextMenuSubTrigger,
271
+ ContextMenuRadioGroup,
272
+ }
@@ -0,0 +1,171 @@
1
+ import { Button } from '@bupple/vss-ui/components/button'
2
+ import { usePortalContainer } from '@bupple/vss-ui/lib/portal-container'
3
+ import { cn } from '@bupple/vss-ui/lib/utils'
4
+ import { XIcon } from 'lucide-react'
5
+ import { Dialog as DialogPrimitive } from 'radix-ui'
6
+ import * as React from 'react'
7
+
8
+ function Dialog({
9
+ ...props
10
+ }: React.ComponentProps<typeof DialogPrimitive.Root>) {
11
+ return <DialogPrimitive.Root data-slot='dialog' {...props} />
12
+ }
13
+
14
+ function DialogTrigger({
15
+ ...props
16
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
17
+ return <DialogPrimitive.Trigger data-slot='dialog-trigger' {...props} />
18
+ }
19
+
20
+ function DialogPortal({
21
+ ...props
22
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
23
+ const container = usePortalContainer()
24
+ return (
25
+ <DialogPrimitive.Portal
26
+ data-slot='dialog-portal'
27
+ container={container ?? undefined}
28
+ {...props}
29
+ />
30
+ )
31
+ }
32
+
33
+ function DialogClose({
34
+ ...props
35
+ }: React.ComponentProps<typeof DialogPrimitive.Close>) {
36
+ return <DialogPrimitive.Close data-slot='dialog-close' {...props} />
37
+ }
38
+
39
+ function DialogOverlay({
40
+ className,
41
+ ...props
42
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
43
+ return (
44
+ <DialogPrimitive.Overlay
45
+ data-slot='dialog-overlay'
46
+ className={cn(
47
+ 'fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0',
48
+ className,
49
+ )}
50
+ {...props}
51
+ />
52
+ )
53
+ }
54
+
55
+ function DialogContent({
56
+ className,
57
+ children,
58
+ showCloseButton = true,
59
+ closeIcon,
60
+ ...props
61
+ }: React.ComponentProps<typeof DialogPrimitive.Content> & {
62
+ showCloseButton?: boolean
63
+ closeIcon?: React.ReactNode
64
+ }) {
65
+ return (
66
+ <DialogPortal>
67
+ <DialogOverlay />
68
+ <DialogPrimitive.Content
69
+ data-slot='dialog-content'
70
+ className={cn(
71
+ 'fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
72
+ className,
73
+ )}
74
+ {...props}
75
+ >
76
+ {children}
77
+ {showCloseButton && (
78
+ <DialogPrimitive.Close data-slot='dialog-close' asChild>
79
+ <Button
80
+ variant='ghost'
81
+ className='absolute top-2 right-2'
82
+ size='icon-sm'
83
+ >
84
+ {closeIcon ?? <XIcon />}
85
+ <span className='sr-only'>Close</span>
86
+ </Button>
87
+ </DialogPrimitive.Close>
88
+ )}
89
+ </DialogPrimitive.Content>
90
+ </DialogPortal>
91
+ )
92
+ }
93
+
94
+ function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
95
+ return (
96
+ <div
97
+ data-slot='dialog-header'
98
+ className={cn('flex flex-col gap-2', className)}
99
+ {...props}
100
+ />
101
+ )
102
+ }
103
+
104
+ function DialogFooter({
105
+ className,
106
+ showCloseButton = false,
107
+ children,
108
+ ...props
109
+ }: React.ComponentProps<'div'> & {
110
+ showCloseButton?: boolean
111
+ }) {
112
+ return (
113
+ <div
114
+ data-slot='dialog-footer'
115
+ className={cn(
116
+ '-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end',
117
+ className,
118
+ )}
119
+ {...props}
120
+ >
121
+ {children}
122
+ {showCloseButton && (
123
+ <DialogPrimitive.Close asChild>
124
+ <Button variant='outline'>Close</Button>
125
+ </DialogPrimitive.Close>
126
+ )}
127
+ </div>
128
+ )
129
+ }
130
+
131
+ function DialogTitle({
132
+ className,
133
+ ...props
134
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
135
+ return (
136
+ <DialogPrimitive.Title
137
+ data-slot='dialog-title'
138
+ className={cn('text-base leading-none font-medium', className)}
139
+ {...props}
140
+ />
141
+ )
142
+ }
143
+
144
+ function DialogDescription({
145
+ className,
146
+ ...props
147
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
148
+ return (
149
+ <DialogPrimitive.Description
150
+ data-slot='dialog-description'
151
+ className={cn(
152
+ 'text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
153
+ className,
154
+ )}
155
+ {...props}
156
+ />
157
+ )
158
+ }
159
+
160
+ export {
161
+ Dialog,
162
+ DialogClose,
163
+ DialogContent,
164
+ DialogDescription,
165
+ DialogFooter,
166
+ DialogHeader,
167
+ DialogOverlay,
168
+ DialogPortal,
169
+ DialogTitle,
170
+ DialogTrigger,
171
+ }
@@ -0,0 +1,20 @@
1
+ import { Direction } from 'radix-ui'
2
+ import * as React from 'react'
3
+
4
+ function DirectionProvider({
5
+ dir,
6
+ direction,
7
+ children,
8
+ }: React.ComponentProps<typeof Direction.DirectionProvider> & {
9
+ direction?: React.ComponentProps<typeof Direction.DirectionProvider>['dir']
10
+ }) {
11
+ return (
12
+ <Direction.DirectionProvider dir={direction ?? dir}>
13
+ {children}
14
+ </Direction.DirectionProvider>
15
+ )
16
+ }
17
+
18
+ const useDirection = Direction.useDirection
19
+
20
+ export { DirectionProvider, useDirection }
@@ -0,0 +1,130 @@
1
+ 'use client'
2
+
3
+ import { cn } from '@bupple/vss-ui/lib/utils'
4
+ import * as React from 'react'
5
+ import { Drawer as DrawerPrimitive } from 'vaul'
6
+
7
+ function Drawer({
8
+ ...props
9
+ }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
10
+ return <DrawerPrimitive.Root data-slot='drawer' {...props} />
11
+ }
12
+
13
+ function DrawerTrigger({
14
+ ...props
15
+ }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
16
+ return <DrawerPrimitive.Trigger data-slot='drawer-trigger' {...props} />
17
+ }
18
+
19
+ function DrawerPortal({
20
+ ...props
21
+ }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
22
+ return <DrawerPrimitive.Portal data-slot='drawer-portal' {...props} />
23
+ }
24
+
25
+ function DrawerClose({
26
+ ...props
27
+ }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
28
+ return <DrawerPrimitive.Close data-slot='drawer-close' {...props} />
29
+ }
30
+
31
+ function DrawerOverlay({
32
+ className,
33
+ ...props
34
+ }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
35
+ return (
36
+ <DrawerPrimitive.Overlay
37
+ data-slot='drawer-overlay'
38
+ className={cn(
39
+ 'fixed inset-0 z-50 bg-black/10 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0',
40
+ className,
41
+ )}
42
+ {...props}
43
+ />
44
+ )
45
+ }
46
+
47
+ function DrawerContent({
48
+ className,
49
+ children,
50
+ ...props
51
+ }: React.ComponentProps<typeof DrawerPrimitive.Content>) {
52
+ return (
53
+ <DrawerPortal data-slot='drawer-portal'>
54
+ <DrawerOverlay />
55
+ <DrawerPrimitive.Content
56
+ data-slot='drawer-content'
57
+ className={cn(
58
+ 'group/drawer-content fixed z-50 flex h-auto flex-col bg-popover text-sm text-popover-foreground data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm',
59
+ className,
60
+ )}
61
+ {...props}
62
+ >
63
+ <div className='mx-auto mt-4 hidden h-1 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block' />
64
+ {children}
65
+ </DrawerPrimitive.Content>
66
+ </DrawerPortal>
67
+ )
68
+ }
69
+
70
+ function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
71
+ return (
72
+ <div
73
+ data-slot='drawer-header'
74
+ className={cn(
75
+ 'flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-0.5 md:text-left',
76
+ className,
77
+ )}
78
+ {...props}
79
+ />
80
+ )
81
+ }
82
+
83
+ function DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) {
84
+ return (
85
+ <div
86
+ data-slot='drawer-footer'
87
+ className={cn('mt-auto flex flex-col gap-2 p-4', className)}
88
+ {...props}
89
+ />
90
+ )
91
+ }
92
+
93
+ function DrawerTitle({
94
+ className,
95
+ ...props
96
+ }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
97
+ return (
98
+ <DrawerPrimitive.Title
99
+ data-slot='drawer-title'
100
+ className={cn('text-base font-medium text-foreground', className)}
101
+ {...props}
102
+ />
103
+ )
104
+ }
105
+
106
+ function DrawerDescription({
107
+ className,
108
+ ...props
109
+ }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
110
+ return (
111
+ <DrawerPrimitive.Description
112
+ data-slot='drawer-description'
113
+ className={cn('text-sm text-muted-foreground', className)}
114
+ {...props}
115
+ />
116
+ )
117
+ }
118
+
119
+ export {
120
+ Drawer,
121
+ DrawerPortal,
122
+ DrawerOverlay,
123
+ DrawerTrigger,
124
+ DrawerClose,
125
+ DrawerContent,
126
+ DrawerHeader,
127
+ DrawerFooter,
128
+ DrawerTitle,
129
+ DrawerDescription,
130
+ }