@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.
- package/.turbo/turbo-lint.log +4 -0
- package/components.json +21 -0
- package/eslint.config.js +4 -0
- package/index.ts +2 -0
- package/package.json +67 -0
- package/postcss.config.mjs +6 -0
- package/src/components/accordion.tsx +84 -0
- package/src/components/alert-dialog.tsx +198 -0
- package/src/components/alert.tsx +77 -0
- package/src/components/aspect-ratio.tsx +11 -0
- package/src/components/avatar.tsx +109 -0
- package/src/components/badge.tsx +50 -0
- package/src/components/breadcrumb.tsx +118 -0
- package/src/components/button-group.tsx +84 -0
- package/src/components/button.tsx +68 -0
- package/src/components/calendar.tsx +223 -0
- package/src/components/card.tsx +102 -0
- package/src/components/carousel.tsx +241 -0
- package/src/components/chart.tsx +373 -0
- package/src/components/checkbox.tsx +32 -0
- package/src/components/collapsible.tsx +33 -0
- package/src/components/combobox.tsx +299 -0
- package/src/components/command.tsx +194 -0
- package/src/components/context-menu.tsx +272 -0
- package/src/components/dialog.tsx +171 -0
- package/src/components/direction.tsx +20 -0
- package/src/components/drawer.tsx +130 -0
- package/src/components/dropdown-menu.tsx +278 -0
- package/src/components/empty.tsx +102 -0
- package/src/components/field.tsx +237 -0
- package/src/components/hover-card.tsx +43 -0
- package/src/components/input-group.tsx +157 -0
- package/src/components/input.tsx +18 -0
- package/src/components/item.tsx +197 -0
- package/src/components/kbd.tsx +26 -0
- package/src/components/label.tsx +21 -0
- package/src/components/menubar.tsx +283 -0
- package/src/components/native-select.tsx +64 -0
- package/src/components/navigation-menu.tsx +166 -0
- package/src/components/pagination.tsx +131 -0
- package/src/components/popover.tsx +88 -0
- package/src/components/progress.tsx +30 -0
- package/src/components/radio-group.tsx +46 -0
- package/src/components/resizable.tsx +49 -0
- package/src/components/scroll-area.tsx +52 -0
- package/src/components/select.tsx +209 -0
- package/src/components/separator.tsx +25 -0
- package/src/components/sheet.tsx +152 -0
- package/src/components/sidebar.tsx +703 -0
- package/src/components/skeleton.tsx +13 -0
- package/src/components/slider.tsx +58 -0
- package/src/components/sonner.tsx +45 -0
- package/src/components/spinner.tsx +15 -0
- package/src/components/switch.tsx +32 -0
- package/src/components/table.tsx +115 -0
- package/src/components/tabs.tsx +89 -0
- package/src/components/textarea.tsx +17 -0
- package/src/components/toggle-group.tsx +86 -0
- package/src/components/toggle.tsx +48 -0
- package/src/components/tooltip.tsx +56 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/lib/portal-container.ts +11 -0
- package/src/lib/utils.ts +8 -0
- package/src/theme.css +125 -0
- 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
|
+
}
|