@vendure-io/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/package.json +59 -0
- package/src/components/ui/accordion.tsx +74 -0
- package/src/components/ui/alert-dialog.tsx +187 -0
- package/src/components/ui/alert.tsx +76 -0
- package/src/components/ui/aspect-ratio.tsx +22 -0
- package/src/components/ui/avatar.tsx +109 -0
- package/src/components/ui/badge.tsx +52 -0
- package/src/components/ui/breadcrumb.tsx +125 -0
- package/src/components/ui/button-group.tsx +87 -0
- package/src/components/ui/button.tsx +60 -0
- package/src/components/ui/calendar.tsx +221 -0
- package/src/components/ui/card.tsx +103 -0
- package/src/components/ui/carousel.tsx +242 -0
- package/src/components/ui/chart.tsx +356 -0
- package/src/components/ui/checkbox.tsx +29 -0
- package/src/components/ui/collapsible.tsx +21 -0
- package/src/components/ui/combobox.tsx +297 -0
- package/src/components/ui/command.tsx +196 -0
- package/src/components/ui/context-menu.tsx +271 -0
- package/src/components/ui/dialog.tsx +157 -0
- package/src/components/ui/direction.tsx +6 -0
- package/src/components/ui/drawer.tsx +131 -0
- package/src/components/ui/dropdown-menu.tsx +271 -0
- package/src/components/ui/empty.tsx +101 -0
- package/src/components/ui/field.tsx +238 -0
- package/src/components/ui/hover-card.tsx +51 -0
- package/src/components/ui/input-group.tsx +158 -0
- package/src/components/ui/input-otp.tsx +87 -0
- package/src/components/ui/input.tsx +20 -0
- package/src/components/ui/item.tsx +201 -0
- package/src/components/ui/kbd.tsx +26 -0
- package/src/components/ui/label.tsx +20 -0
- package/src/components/ui/menubar.tsx +283 -0
- package/src/components/ui/native-select.tsx +52 -0
- package/src/components/ui/navigation-menu.tsx +168 -0
- package/src/components/ui/pagination.tsx +130 -0
- package/src/components/ui/popover.tsx +90 -0
- package/src/components/ui/progress.tsx +83 -0
- package/src/components/ui/radio-group.tsx +38 -0
- package/src/components/ui/resizable.tsx +50 -0
- package/src/components/ui/scroll-area.tsx +55 -0
- package/src/components/ui/select.tsx +201 -0
- package/src/components/ui/separator.tsx +25 -0
- package/src/components/ui/sheet.tsx +135 -0
- package/src/components/ui/sidebar.tsx +723 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +59 -0
- package/src/components/ui/sonner.tsx +49 -0
- package/src/components/ui/spinner.tsx +10 -0
- package/src/components/ui/switch.tsx +32 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/tabs.tsx +82 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle-group.tsx +89 -0
- package/src/components/ui/toggle.tsx +44 -0
- package/src/components/ui/tooltip.tsx +66 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/lib/utils.ts +25 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Menu as MenuPrimitive } from "@base-ui/react/menu"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
import { ChevronRightIcon, CheckIcon } from "lucide-react"
|
|
8
|
+
|
|
9
|
+
function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
|
|
10
|
+
return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
|
|
14
|
+
return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
|
|
18
|
+
return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function DropdownMenuContent({
|
|
22
|
+
align = "start",
|
|
23
|
+
alignOffset = 0,
|
|
24
|
+
side = "bottom",
|
|
25
|
+
sideOffset = 4,
|
|
26
|
+
className,
|
|
27
|
+
...props
|
|
28
|
+
}: MenuPrimitive.Popup.Props &
|
|
29
|
+
Pick<
|
|
30
|
+
MenuPrimitive.Positioner.Props,
|
|
31
|
+
"align" | "alignOffset" | "side" | "sideOffset"
|
|
32
|
+
>) {
|
|
33
|
+
return (
|
|
34
|
+
<MenuPrimitive.Portal>
|
|
35
|
+
<MenuPrimitive.Positioner
|
|
36
|
+
className="isolate z-50 outline-none"
|
|
37
|
+
align={align}
|
|
38
|
+
alignOffset={alignOffset}
|
|
39
|
+
side={side}
|
|
40
|
+
sideOffset={sideOffset}
|
|
41
|
+
>
|
|
42
|
+
<MenuPrimitive.Popup
|
|
43
|
+
data-slot="dropdown-menu-content"
|
|
44
|
+
className={cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 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 ring-foreground/10 bg-popover text-popover-foreground data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md p-1 shadow-md ring-1 duration-100 outline-none data-closed:overflow-hidden", className )}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
</MenuPrimitive.Positioner>
|
|
48
|
+
</MenuPrimitive.Portal>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
|
|
53
|
+
return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function DropdownMenuLabel({
|
|
57
|
+
className,
|
|
58
|
+
inset,
|
|
59
|
+
...props
|
|
60
|
+
}: MenuPrimitive.GroupLabel.Props & {
|
|
61
|
+
inset?: boolean
|
|
62
|
+
}) {
|
|
63
|
+
return (
|
|
64
|
+
<MenuPrimitive.GroupLabel
|
|
65
|
+
data-slot="dropdown-menu-label"
|
|
66
|
+
data-inset={inset}
|
|
67
|
+
className={cn(
|
|
68
|
+
"text-muted-foreground px-2 py-1.5 text-xs font-medium data-inset:pl-8",
|
|
69
|
+
className
|
|
70
|
+
)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function DropdownMenuItem({
|
|
77
|
+
className,
|
|
78
|
+
inset,
|
|
79
|
+
variant = "default",
|
|
80
|
+
...props
|
|
81
|
+
}: MenuPrimitive.Item.Props & {
|
|
82
|
+
inset?: boolean
|
|
83
|
+
variant?: "default" | "destructive"
|
|
84
|
+
}) {
|
|
85
|
+
return (
|
|
86
|
+
<MenuPrimitive.Item
|
|
87
|
+
data-slot="dropdown-menu-item"
|
|
88
|
+
data-inset={inset}
|
|
89
|
+
data-variant={variant}
|
|
90
|
+
className={cn(
|
|
91
|
+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground group/dropdown-menu-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
92
|
+
className
|
|
93
|
+
)}
|
|
94
|
+
{...props}
|
|
95
|
+
/>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
|
|
100
|
+
return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function DropdownMenuSubTrigger({
|
|
104
|
+
className,
|
|
105
|
+
inset,
|
|
106
|
+
children,
|
|
107
|
+
...props
|
|
108
|
+
}: MenuPrimitive.SubmenuTrigger.Props & {
|
|
109
|
+
inset?: boolean
|
|
110
|
+
}) {
|
|
111
|
+
return (
|
|
112
|
+
<MenuPrimitive.SubmenuTrigger
|
|
113
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
114
|
+
data-inset={inset}
|
|
115
|
+
className={cn(
|
|
116
|
+
"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-popup-open:bg-accent data-popup-open:text-accent-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-inset:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
117
|
+
className
|
|
118
|
+
)}
|
|
119
|
+
{...props}
|
|
120
|
+
>
|
|
121
|
+
{children}
|
|
122
|
+
<ChevronRightIcon className="ml-auto" />
|
|
123
|
+
</MenuPrimitive.SubmenuTrigger>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function DropdownMenuSubContent({
|
|
128
|
+
align = "start",
|
|
129
|
+
alignOffset = -3,
|
|
130
|
+
side = "right",
|
|
131
|
+
sideOffset = 0,
|
|
132
|
+
className,
|
|
133
|
+
...props
|
|
134
|
+
}: React.ComponentProps<typeof DropdownMenuContent>) {
|
|
135
|
+
return (
|
|
136
|
+
<DropdownMenuContent
|
|
137
|
+
data-slot="dropdown-menu-sub-content"
|
|
138
|
+
className={cn(
|
|
139
|
+
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 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 ring-foreground/10 bg-popover text-popover-foreground w-auto min-w-[96px] rounded-md p-1 shadow-lg ring-1 duration-100",
|
|
140
|
+
className
|
|
141
|
+
)}
|
|
142
|
+
align={align}
|
|
143
|
+
alignOffset={alignOffset}
|
|
144
|
+
side={side}
|
|
145
|
+
sideOffset={sideOffset}
|
|
146
|
+
{...props}
|
|
147
|
+
/>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function DropdownMenuCheckboxItem({
|
|
152
|
+
className,
|
|
153
|
+
children,
|
|
154
|
+
checked,
|
|
155
|
+
inset,
|
|
156
|
+
...props
|
|
157
|
+
}: MenuPrimitive.CheckboxItem.Props & {
|
|
158
|
+
inset?: boolean
|
|
159
|
+
}) {
|
|
160
|
+
return (
|
|
161
|
+
<MenuPrimitive.CheckboxItem
|
|
162
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
163
|
+
data-inset={inset}
|
|
164
|
+
className={cn(
|
|
165
|
+
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
166
|
+
className
|
|
167
|
+
)}
|
|
168
|
+
checked={checked}
|
|
169
|
+
{...props}
|
|
170
|
+
>
|
|
171
|
+
<span
|
|
172
|
+
className="pointer-events-none absolute right-2 flex items-center justify-center"
|
|
173
|
+
data-slot="dropdown-menu-checkbox-item-indicator"
|
|
174
|
+
>
|
|
175
|
+
<MenuPrimitive.CheckboxItemIndicator>
|
|
176
|
+
<CheckIcon
|
|
177
|
+
/>
|
|
178
|
+
</MenuPrimitive.CheckboxItemIndicator>
|
|
179
|
+
</span>
|
|
180
|
+
{children}
|
|
181
|
+
</MenuPrimitive.CheckboxItem>
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
|
|
186
|
+
return (
|
|
187
|
+
<MenuPrimitive.RadioGroup
|
|
188
|
+
data-slot="dropdown-menu-radio-group"
|
|
189
|
+
{...props}
|
|
190
|
+
/>
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function DropdownMenuRadioItem({
|
|
195
|
+
className,
|
|
196
|
+
children,
|
|
197
|
+
inset,
|
|
198
|
+
...props
|
|
199
|
+
}: MenuPrimitive.RadioItem.Props & {
|
|
200
|
+
inset?: boolean
|
|
201
|
+
}) {
|
|
202
|
+
return (
|
|
203
|
+
<MenuPrimitive.RadioItem
|
|
204
|
+
data-slot="dropdown-menu-radio-item"
|
|
205
|
+
data-inset={inset}
|
|
206
|
+
className={cn(
|
|
207
|
+
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
208
|
+
className
|
|
209
|
+
)}
|
|
210
|
+
{...props}
|
|
211
|
+
>
|
|
212
|
+
<span
|
|
213
|
+
className="pointer-events-none absolute right-2 flex items-center justify-center"
|
|
214
|
+
data-slot="dropdown-menu-radio-item-indicator"
|
|
215
|
+
>
|
|
216
|
+
<MenuPrimitive.RadioItemIndicator>
|
|
217
|
+
<CheckIcon
|
|
218
|
+
/>
|
|
219
|
+
</MenuPrimitive.RadioItemIndicator>
|
|
220
|
+
</span>
|
|
221
|
+
{children}
|
|
222
|
+
</MenuPrimitive.RadioItem>
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function DropdownMenuSeparator({
|
|
227
|
+
className,
|
|
228
|
+
...props
|
|
229
|
+
}: MenuPrimitive.Separator.Props) {
|
|
230
|
+
return (
|
|
231
|
+
<MenuPrimitive.Separator
|
|
232
|
+
data-slot="dropdown-menu-separator"
|
|
233
|
+
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
|
234
|
+
{...props}
|
|
235
|
+
/>
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function DropdownMenuShortcut({
|
|
240
|
+
className,
|
|
241
|
+
...props
|
|
242
|
+
}: React.ComponentProps<"span">) {
|
|
243
|
+
return (
|
|
244
|
+
<span
|
|
245
|
+
data-slot="dropdown-menu-shortcut"
|
|
246
|
+
className={cn(
|
|
247
|
+
"text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest",
|
|
248
|
+
className
|
|
249
|
+
)}
|
|
250
|
+
{...props}
|
|
251
|
+
/>
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export {
|
|
256
|
+
DropdownMenu,
|
|
257
|
+
DropdownMenuPortal,
|
|
258
|
+
DropdownMenuTrigger,
|
|
259
|
+
DropdownMenuContent,
|
|
260
|
+
DropdownMenuGroup,
|
|
261
|
+
DropdownMenuLabel,
|
|
262
|
+
DropdownMenuItem,
|
|
263
|
+
DropdownMenuCheckboxItem,
|
|
264
|
+
DropdownMenuRadioGroup,
|
|
265
|
+
DropdownMenuRadioItem,
|
|
266
|
+
DropdownMenuSeparator,
|
|
267
|
+
DropdownMenuShortcut,
|
|
268
|
+
DropdownMenuSub,
|
|
269
|
+
DropdownMenuSubTrigger,
|
|
270
|
+
DropdownMenuSubContent,
|
|
271
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
function Empty({ className, ...props }: React.ComponentProps<"div">) {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
data-slot="empty"
|
|
9
|
+
className={cn(
|
|
10
|
+
"flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-lg border-dashed p-12 text-center text-balance",
|
|
11
|
+
className
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
data-slot="empty-header"
|
|
22
|
+
className={cn("flex max-w-sm flex-col items-center gap-2", className)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const emptyMediaVariants = cva(
|
|
29
|
+
"mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
30
|
+
{
|
|
31
|
+
variants: {
|
|
32
|
+
variant: {
|
|
33
|
+
default: "bg-transparent",
|
|
34
|
+
icon: "bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
defaultVariants: {
|
|
38
|
+
variant: "default",
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
function EmptyMedia({
|
|
44
|
+
className,
|
|
45
|
+
variant = "default",
|
|
46
|
+
...props
|
|
47
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof emptyMediaVariants>) {
|
|
48
|
+
return (
|
|
49
|
+
<div
|
|
50
|
+
data-slot="empty-icon"
|
|
51
|
+
data-variant={variant}
|
|
52
|
+
className={cn(emptyMediaVariants({ variant, className }))}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
59
|
+
return (
|
|
60
|
+
<div
|
|
61
|
+
data-slot="empty-title"
|
|
62
|
+
className={cn("text-lg font-medium tracking-tight", className)}
|
|
63
|
+
{...props}
|
|
64
|
+
/>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
|
|
69
|
+
return (
|
|
70
|
+
<div
|
|
71
|
+
data-slot="empty-description"
|
|
72
|
+
className={cn(
|
|
73
|
+
"text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4",
|
|
74
|
+
className
|
|
75
|
+
)}
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
data-slot="empty-content"
|
|
85
|
+
className={cn(
|
|
86
|
+
"flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance",
|
|
87
|
+
className
|
|
88
|
+
)}
|
|
89
|
+
{...props}
|
|
90
|
+
/>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export {
|
|
95
|
+
Empty,
|
|
96
|
+
EmptyHeader,
|
|
97
|
+
EmptyTitle,
|
|
98
|
+
EmptyDescription,
|
|
99
|
+
EmptyContent,
|
|
100
|
+
EmptyMedia,
|
|
101
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useMemo } from "react"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
import { Label } from "@/components/ui/label"
|
|
8
|
+
import { Separator } from "@/components/ui/separator"
|
|
9
|
+
|
|
10
|
+
function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
|
|
11
|
+
return (
|
|
12
|
+
<fieldset
|
|
13
|
+
data-slot="field-set"
|
|
14
|
+
className={cn(
|
|
15
|
+
"flex flex-col gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
|
|
16
|
+
className
|
|
17
|
+
)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function FieldLegend({
|
|
24
|
+
className,
|
|
25
|
+
variant = "legend",
|
|
26
|
+
...props
|
|
27
|
+
}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
|
|
28
|
+
return (
|
|
29
|
+
<legend
|
|
30
|
+
data-slot="field-legend"
|
|
31
|
+
data-variant={variant}
|
|
32
|
+
className={cn(
|
|
33
|
+
"mb-3 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
|
|
34
|
+
className
|
|
35
|
+
)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
data-slot="field-group"
|
|
45
|
+
className={cn(
|
|
46
|
+
"group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
|
|
47
|
+
className
|
|
48
|
+
)}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const fieldVariants = cva(
|
|
55
|
+
"data-[invalid=true]:text-destructive gap-3 group/field flex w-full",
|
|
56
|
+
{
|
|
57
|
+
variants: {
|
|
58
|
+
orientation: {
|
|
59
|
+
vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
|
|
60
|
+
horizontal:
|
|
61
|
+
"flex-row items-center *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
|
62
|
+
responsive:
|
|
63
|
+
"flex-col *:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:*:data-[slot=field-label]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
defaultVariants: {
|
|
67
|
+
orientation: "vertical",
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
function Field({
|
|
73
|
+
className,
|
|
74
|
+
orientation = "vertical",
|
|
75
|
+
...props
|
|
76
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
|
|
77
|
+
return (
|
|
78
|
+
<div
|
|
79
|
+
role="group"
|
|
80
|
+
data-slot="field"
|
|
81
|
+
data-orientation={orientation}
|
|
82
|
+
className={cn(fieldVariants({ orientation }), className)}
|
|
83
|
+
{...props}
|
|
84
|
+
/>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
89
|
+
return (
|
|
90
|
+
<div
|
|
91
|
+
data-slot="field-content"
|
|
92
|
+
className={cn(
|
|
93
|
+
"group/field-content flex flex-1 flex-col gap-1 leading-snug",
|
|
94
|
+
className
|
|
95
|
+
)}
|
|
96
|
+
{...props}
|
|
97
|
+
/>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function FieldLabel({
|
|
102
|
+
className,
|
|
103
|
+
...props
|
|
104
|
+
}: React.ComponentProps<typeof Label>) {
|
|
105
|
+
return (
|
|
106
|
+
<Label
|
|
107
|
+
data-slot="field-label"
|
|
108
|
+
className={cn(
|
|
109
|
+
"has-data-checked:bg-primary/5 has-data-checked:border-primary/30 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10 group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border *:data-[slot=field]:p-3",
|
|
110
|
+
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
|
|
111
|
+
className
|
|
112
|
+
)}
|
|
113
|
+
{...props}
|
|
114
|
+
/>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
119
|
+
return (
|
|
120
|
+
<div
|
|
121
|
+
data-slot="field-label"
|
|
122
|
+
className={cn(
|
|
123
|
+
"flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50",
|
|
124
|
+
className
|
|
125
|
+
)}
|
|
126
|
+
{...props}
|
|
127
|
+
/>
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
|
|
132
|
+
return (
|
|
133
|
+
<p
|
|
134
|
+
data-slot="field-description"
|
|
135
|
+
className={cn(
|
|
136
|
+
"text-muted-foreground text-left text-sm leading-normal font-normal group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5",
|
|
137
|
+
"last:mt-0 nth-last-2:-mt-1",
|
|
138
|
+
"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
|
|
139
|
+
className
|
|
140
|
+
)}
|
|
141
|
+
{...props}
|
|
142
|
+
/>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function FieldSeparator({
|
|
147
|
+
children,
|
|
148
|
+
className,
|
|
149
|
+
...props
|
|
150
|
+
}: React.ComponentProps<"div"> & {
|
|
151
|
+
children?: React.ReactNode
|
|
152
|
+
}) {
|
|
153
|
+
return (
|
|
154
|
+
<div
|
|
155
|
+
data-slot="field-separator"
|
|
156
|
+
data-content={!!children}
|
|
157
|
+
className={cn(
|
|
158
|
+
"relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
|
|
159
|
+
className
|
|
160
|
+
)}
|
|
161
|
+
{...props}
|
|
162
|
+
>
|
|
163
|
+
<Separator className="absolute inset-0 top-1/2" />
|
|
164
|
+
{children && (
|
|
165
|
+
<span
|
|
166
|
+
className="text-muted-foreground bg-background relative mx-auto block w-fit px-2"
|
|
167
|
+
data-slot="field-separator-content"
|
|
168
|
+
>
|
|
169
|
+
{children}
|
|
170
|
+
</span>
|
|
171
|
+
)}
|
|
172
|
+
</div>
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function FieldError({
|
|
177
|
+
className,
|
|
178
|
+
children,
|
|
179
|
+
errors,
|
|
180
|
+
...props
|
|
181
|
+
}: React.ComponentProps<"div"> & {
|
|
182
|
+
errors?: Array<{ message?: string } | undefined>
|
|
183
|
+
}) {
|
|
184
|
+
const content = useMemo(() => {
|
|
185
|
+
if (children) {
|
|
186
|
+
return children
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!errors?.length) {
|
|
190
|
+
return null
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const uniqueErrors = [
|
|
194
|
+
...new Map(errors.map((error) => [error?.message, error])).values(),
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
if (uniqueErrors?.length == 1) {
|
|
198
|
+
return uniqueErrors[0]?.message
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return (
|
|
202
|
+
<ul className="ml-4 flex list-disc flex-col gap-1">
|
|
203
|
+
{uniqueErrors.map(
|
|
204
|
+
(error, index) =>
|
|
205
|
+
error?.message && <li key={index}>{error.message}</li>
|
|
206
|
+
)}
|
|
207
|
+
</ul>
|
|
208
|
+
)
|
|
209
|
+
}, [children, errors])
|
|
210
|
+
|
|
211
|
+
if (!content) {
|
|
212
|
+
return null
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<div
|
|
217
|
+
role="alert"
|
|
218
|
+
data-slot="field-error"
|
|
219
|
+
className={cn("text-destructive text-sm font-normal", className)}
|
|
220
|
+
{...props}
|
|
221
|
+
>
|
|
222
|
+
{content}
|
|
223
|
+
</div>
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export {
|
|
228
|
+
Field,
|
|
229
|
+
FieldLabel,
|
|
230
|
+
FieldDescription,
|
|
231
|
+
FieldError,
|
|
232
|
+
FieldGroup,
|
|
233
|
+
FieldLegend,
|
|
234
|
+
FieldSeparator,
|
|
235
|
+
FieldSet,
|
|
236
|
+
FieldContent,
|
|
237
|
+
FieldTitle,
|
|
238
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { PreviewCard as PreviewCardPrimitive } from "@base-ui/react/preview-card"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
function HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {
|
|
8
|
+
return <PreviewCardPrimitive.Root data-slot="hover-card" {...props} />
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function HoverCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) {
|
|
12
|
+
return (
|
|
13
|
+
<PreviewCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function HoverCardContent({
|
|
18
|
+
className,
|
|
19
|
+
side = "bottom",
|
|
20
|
+
sideOffset = 4,
|
|
21
|
+
align = "center",
|
|
22
|
+
alignOffset = 4,
|
|
23
|
+
...props
|
|
24
|
+
}: PreviewCardPrimitive.Popup.Props &
|
|
25
|
+
Pick<
|
|
26
|
+
PreviewCardPrimitive.Positioner.Props,
|
|
27
|
+
"align" | "alignOffset" | "side" | "sideOffset"
|
|
28
|
+
>) {
|
|
29
|
+
return (
|
|
30
|
+
<PreviewCardPrimitive.Portal data-slot="hover-card-portal">
|
|
31
|
+
<PreviewCardPrimitive.Positioner
|
|
32
|
+
align={align}
|
|
33
|
+
alignOffset={alignOffset}
|
|
34
|
+
side={side}
|
|
35
|
+
sideOffset={sideOffset}
|
|
36
|
+
className="isolate z-50"
|
|
37
|
+
>
|
|
38
|
+
<PreviewCardPrimitive.Popup
|
|
39
|
+
data-slot="hover-card-content"
|
|
40
|
+
className={cn(
|
|
41
|
+
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 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 ring-foreground/10 bg-popover text-popover-foreground data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 z-50 w-64 origin-(--transform-origin) rounded-lg p-4 text-sm shadow-md ring-1 outline-hidden duration-100",
|
|
42
|
+
className
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
</PreviewCardPrimitive.Positioner>
|
|
47
|
+
</PreviewCardPrimitive.Portal>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { HoverCard, HoverCardTrigger, HoverCardContent }
|