@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.
Files changed (58) hide show
  1. package/package.json +59 -0
  2. package/src/components/ui/accordion.tsx +74 -0
  3. package/src/components/ui/alert-dialog.tsx +187 -0
  4. package/src/components/ui/alert.tsx +76 -0
  5. package/src/components/ui/aspect-ratio.tsx +22 -0
  6. package/src/components/ui/avatar.tsx +109 -0
  7. package/src/components/ui/badge.tsx +52 -0
  8. package/src/components/ui/breadcrumb.tsx +125 -0
  9. package/src/components/ui/button-group.tsx +87 -0
  10. package/src/components/ui/button.tsx +60 -0
  11. package/src/components/ui/calendar.tsx +221 -0
  12. package/src/components/ui/card.tsx +103 -0
  13. package/src/components/ui/carousel.tsx +242 -0
  14. package/src/components/ui/chart.tsx +356 -0
  15. package/src/components/ui/checkbox.tsx +29 -0
  16. package/src/components/ui/collapsible.tsx +21 -0
  17. package/src/components/ui/combobox.tsx +297 -0
  18. package/src/components/ui/command.tsx +196 -0
  19. package/src/components/ui/context-menu.tsx +271 -0
  20. package/src/components/ui/dialog.tsx +157 -0
  21. package/src/components/ui/direction.tsx +6 -0
  22. package/src/components/ui/drawer.tsx +131 -0
  23. package/src/components/ui/dropdown-menu.tsx +271 -0
  24. package/src/components/ui/empty.tsx +101 -0
  25. package/src/components/ui/field.tsx +238 -0
  26. package/src/components/ui/hover-card.tsx +51 -0
  27. package/src/components/ui/input-group.tsx +158 -0
  28. package/src/components/ui/input-otp.tsx +87 -0
  29. package/src/components/ui/input.tsx +20 -0
  30. package/src/components/ui/item.tsx +201 -0
  31. package/src/components/ui/kbd.tsx +26 -0
  32. package/src/components/ui/label.tsx +20 -0
  33. package/src/components/ui/menubar.tsx +283 -0
  34. package/src/components/ui/native-select.tsx +52 -0
  35. package/src/components/ui/navigation-menu.tsx +168 -0
  36. package/src/components/ui/pagination.tsx +130 -0
  37. package/src/components/ui/popover.tsx +90 -0
  38. package/src/components/ui/progress.tsx +83 -0
  39. package/src/components/ui/radio-group.tsx +38 -0
  40. package/src/components/ui/resizable.tsx +50 -0
  41. package/src/components/ui/scroll-area.tsx +55 -0
  42. package/src/components/ui/select.tsx +201 -0
  43. package/src/components/ui/separator.tsx +25 -0
  44. package/src/components/ui/sheet.tsx +135 -0
  45. package/src/components/ui/sidebar.tsx +723 -0
  46. package/src/components/ui/skeleton.tsx +13 -0
  47. package/src/components/ui/slider.tsx +59 -0
  48. package/src/components/ui/sonner.tsx +49 -0
  49. package/src/components/ui/spinner.tsx +10 -0
  50. package/src/components/ui/switch.tsx +32 -0
  51. package/src/components/ui/table.tsx +116 -0
  52. package/src/components/ui/tabs.tsx +82 -0
  53. package/src/components/ui/textarea.tsx +18 -0
  54. package/src/components/ui/toggle-group.tsx +89 -0
  55. package/src/components/ui/toggle.tsx +44 -0
  56. package/src/components/ui/tooltip.tsx +66 -0
  57. package/src/hooks/use-mobile.ts +19 -0
  58. 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 }