@gentleduck/registry-ui 0.2.1

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 (175) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/index.css +3 -0
  3. package/package.json +59 -0
  4. package/src/_old/_table/index.ts +5 -0
  5. package/src/_old/_table/table-advanced.constants.tsx +24 -0
  6. package/src/_old/_table/table-advanced.tsx +311 -0
  7. package/src/_old/_table/table-advanced.types.ts +272 -0
  8. package/src/_old/_table/table.constants.ts +2 -0
  9. package/src/_old/_table/table.hook.tsx +115 -0
  10. package/src/_old/_table/table.lib.ts +85 -0
  11. package/src/_old/_table/table.tsx +916 -0
  12. package/src/_old/_table/table.types.ts +118 -0
  13. package/src/_old/_table/todo.md +11 -0
  14. package/src/_old/_upload/index.ts +9 -0
  15. package/src/_old/_upload/todo.md +38 -0
  16. package/src/_old/_upload/upload-advanced-chunks.tsx +1624 -0
  17. package/src/_old/_upload/upload-advanced.tsx +507 -0
  18. package/src/_old/_upload/upload-sonner.tsx +58 -0
  19. package/src/_old/_upload/upload.assets.tsx +239 -0
  20. package/src/_old/_upload/upload.constants.tsx +75 -0
  21. package/src/_old/_upload/upload.dto.ts +19 -0
  22. package/src/_old/_upload/upload.lib.tsx +630 -0
  23. package/src/_old/_upload/upload.tsx +491 -0
  24. package/src/_old/_upload/upload.types.ts +436 -0
  25. package/src/accordion/accordion.tsx +247 -0
  26. package/src/accordion/index.ts +1 -0
  27. package/src/alert/alert.constants.ts +17 -0
  28. package/src/alert/alert.tsx +52 -0
  29. package/src/alert/index.ts +2 -0
  30. package/src/alert-dialog/alert-dialog.tsx +107 -0
  31. package/src/alert-dialog/index.ts +1 -0
  32. package/src/aspect-ratio/aspect-ratio.tsx +33 -0
  33. package/src/aspect-ratio/index.ts +1 -0
  34. package/src/audio/audio-record.tsx +776 -0
  35. package/src/audio/audio-visualizer.tsx +377 -0
  36. package/src/audio/audio.libs.ts +5 -0
  37. package/src/audio/audio.types.ts +50 -0
  38. package/src/audio/index.ts +2 -0
  39. package/src/avatar/avatar.tsx +78 -0
  40. package/src/avatar/index.ts +1 -0
  41. package/src/badge/badge.constants.ts +38 -0
  42. package/src/badge/badge.tsx +19 -0
  43. package/src/badge/index.ts +2 -0
  44. package/src/breadcrumb/breadcrumb.tsx +119 -0
  45. package/src/breadcrumb/index.ts +1 -0
  46. package/src/button/button.constants.ts +44 -0
  47. package/src/button/button.tsx +79 -0
  48. package/src/button/button.types.ts +38 -0
  49. package/src/button/index.ts +3 -0
  50. package/src/button-group/button-group.constants.ts +26 -0
  51. package/src/button-group/button-group.tsx +65 -0
  52. package/src/button-group/index.ts +2 -0
  53. package/src/calendar/calendar.tsx +191 -0
  54. package/src/calendar/index.ts +1 -0
  55. package/src/card/card.tsx +81 -0
  56. package/src/card/index.ts +1 -0
  57. package/src/carousel/carousel.tsx +211 -0
  58. package/src/carousel/carousel.types.ts +23 -0
  59. package/src/carousel/index.ts +2 -0
  60. package/src/chart/chart.libs.ts +27 -0
  61. package/src/chart/chart.tsx +260 -0
  62. package/src/chart/chart.types.ts +38 -0
  63. package/src/chart/index.ts +3 -0
  64. package/src/checkbox/checkbox.tsx +144 -0
  65. package/src/checkbox/checkbox.types.ts +24 -0
  66. package/src/checkbox/index.ts +2 -0
  67. package/src/collapsible/collapsible.tsx +151 -0
  68. package/src/collapsible/index.ts +1 -0
  69. package/src/combobox/combobox.tsx +132 -0
  70. package/src/combobox/index.ts +1 -0
  71. package/src/command/command.tsx +192 -0
  72. package/src/command/command.types.ts +11 -0
  73. package/src/command/index.ts +2 -0
  74. package/src/context-menu/context-menu.tsx +178 -0
  75. package/src/context-menu/index.ts +1 -0
  76. package/src/dialog/dialog-responsive.tsx +137 -0
  77. package/src/dialog/dialog.tsx +97 -0
  78. package/src/dialog/index.ts +2 -0
  79. package/src/direction/direction.tsx +13 -0
  80. package/src/direction/index.ts +1 -0
  81. package/src/drawer/drawer.tsx +185 -0
  82. package/src/drawer/index.ts +1 -0
  83. package/src/dropdown-menu/dropdown-menu.tsx +181 -0
  84. package/src/dropdown-menu/index.ts +1 -0
  85. package/src/empty/empty.constants.ts +15 -0
  86. package/src/empty/empty.tsx +73 -0
  87. package/src/empty/index.ts +2 -0
  88. package/src/field/field.constants.ts +22 -0
  89. package/src/field/field.tsx +203 -0
  90. package/src/field/index.ts +2 -0
  91. package/src/hover-card/hover-card.tsx +79 -0
  92. package/src/hover-card/index.ts +1 -0
  93. package/src/input/index.ts +1 -0
  94. package/src/input/input.tsx +45 -0
  95. package/src/input-group/index.ts +1 -0
  96. package/src/input-group/input-group.tsx +170 -0
  97. package/src/input-otp/index.ts +1 -0
  98. package/src/input-otp/input-otp.tsx +66 -0
  99. package/src/item/index.ts +2 -0
  100. package/src/item/item.constants.ts +22 -0
  101. package/src/item/item.tsx +185 -0
  102. package/src/json-editor/index.ts +4 -0
  103. package/src/json-editor/json-editor.hooks.ts +21 -0
  104. package/src/json-editor/json-editor.libs.ts +34 -0
  105. package/src/json-editor/json-editor.tsx +425 -0
  106. package/src/json-editor/json-editor.types.ts +80 -0
  107. package/src/json-editor/json-editor.view.tsx +110 -0
  108. package/src/json-editor/json-text-area.tsx +7 -0
  109. package/src/kbd/index.ts +1 -0
  110. package/src/kbd/kbd.tsx +39 -0
  111. package/src/label/index.ts +1 -0
  112. package/src/label/label.tsx +28 -0
  113. package/src/menubar/index.ts +1 -0
  114. package/src/menubar/menubar.tsx +213 -0
  115. package/src/navigation-menu/index.ts +1 -0
  116. package/src/navigation-menu/navigation-menu.tsx +152 -0
  117. package/src/pagination/index.ts +2 -0
  118. package/src/pagination/pagination.tsx +191 -0
  119. package/src/pagination/pagination.types.ts +17 -0
  120. package/src/popover/index.ts +1 -0
  121. package/src/popover/popover.tsx +35 -0
  122. package/src/preview-panel/index.ts +3 -0
  123. package/src/preview-panel/preview-panel-dialog.tsx +99 -0
  124. package/src/preview-panel/preview-panel.tsx +389 -0
  125. package/src/preview-panel/preview-panel.types.ts +49 -0
  126. package/src/progress/index.ts +1 -0
  127. package/src/progress/progress.tsx +32 -0
  128. package/src/radio-group/index.ts +1 -0
  129. package/src/radio-group/radio-group.tsx +92 -0
  130. package/src/resizable/index.ts +1 -0
  131. package/src/resizable/resizable.tsx +52 -0
  132. package/src/scroll-area/index.ts +1 -0
  133. package/src/scroll-area/scroll-area.tsx +30 -0
  134. package/src/select/index.ts +1 -0
  135. package/src/select/select.tsx +138 -0
  136. package/src/separator/index.ts +1 -0
  137. package/src/separator/separator.tsx +28 -0
  138. package/src/sheet/index.ts +2 -0
  139. package/src/sheet/sheet.constants.tsx +20 -0
  140. package/src/sheet/sheet.tsx +92 -0
  141. package/src/sidebar/index.ts +4 -0
  142. package/src/sidebar/sidebar.constants.ts +30 -0
  143. package/src/sidebar/sidebar.hooks.ts +13 -0
  144. package/src/sidebar/sidebar.tsx +676 -0
  145. package/src/sidebar/sidebar.types.ts +28 -0
  146. package/src/skeleton/index.ts +1 -0
  147. package/src/skeleton/skeleton.tsx +22 -0
  148. package/src/slider/index.ts +1 -0
  149. package/src/slider/slider.tsx +57 -0
  150. package/src/sonner/index.ts +4 -0
  151. package/src/sonner/sonner.chunks.tsx +80 -0
  152. package/src/sonner/sonner.libs.ts +13 -0
  153. package/src/sonner/sonner.tsx +31 -0
  154. package/src/sonner/sonner.types.ts +9 -0
  155. package/src/switch/index.ts +1 -0
  156. package/src/switch/switch.tsx +63 -0
  157. package/src/table/index.ts +1 -0
  158. package/src/table/table.tsx +95 -0
  159. package/src/tabs/index.ts +1 -0
  160. package/src/tabs/tabs.tsx +151 -0
  161. package/src/textarea/index.ts +1 -0
  162. package/src/textarea/textarea.tsx +24 -0
  163. package/src/toggle/index.ts +2 -0
  164. package/src/toggle/toggle.constants.ts +22 -0
  165. package/src/toggle/toggle.tsx +24 -0
  166. package/src/toggle-group/index.ts +1 -0
  167. package/src/toggle-group/toggle-group.tsx +69 -0
  168. package/src/tooltip/index.ts +1 -0
  169. package/src/tooltip/tooltip.tsx +32 -0
  170. package/src/upload/index.ts +1 -0
  171. package/src/upload/upload.constants.tsx +19 -0
  172. package/src/upload/upload.libs.ts +97 -0
  173. package/src/upload/upload.tsx +340 -0
  174. package/src/upload/upload.types.ts +44 -0
  175. package/tsconfig.json +25 -0
@@ -0,0 +1,181 @@
1
+ 'use client'
2
+
3
+ import { cn } from '@gentleduck/libs/cn'
4
+ import * as DropdownMenuPrimitive from '@gentleduck/primitives/dropdown-menu'
5
+ import { Check, ChevronRight, Circle } from 'lucide-react'
6
+ import * as React from 'react'
7
+
8
+ const DropdownMenu = DropdownMenuPrimitive.Root
9
+
10
+ const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
11
+
12
+ const DropdownMenuGroup = DropdownMenuPrimitive.Group
13
+
14
+ const DropdownMenuPortal = DropdownMenuPrimitive.Portal
15
+
16
+ const DropdownMenuSub = DropdownMenuPrimitive.Sub
17
+
18
+ const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
19
+
20
+ const DropdownMenuSubTrigger = React.forwardRef<
21
+ React.ComponentRef<typeof DropdownMenuPrimitive.SubTrigger>,
22
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
23
+ inset?: boolean
24
+ }
25
+ >(({ className, inset, children, ...props }, ref) => (
26
+ <DropdownMenuPrimitive.SubTrigger
27
+ ref={ref}
28
+ className={cn(
29
+ 'flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
30
+ inset && 'ps-8',
31
+ className,
32
+ )}
33
+ {...props}>
34
+ {children}
35
+ <ChevronRight aria-hidden="true" className="ms-auto rtl:rotate-180" />
36
+ </DropdownMenuPrimitive.SubTrigger>
37
+ ))
38
+ DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName
39
+
40
+ const DropdownMenuSubContent = React.forwardRef<
41
+ React.ComponentRef<typeof DropdownMenuPrimitive.SubContent>,
42
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
43
+ >(({ className, ...props }, ref) => (
44
+ <DropdownMenuPrimitive.SubContent
45
+ ref={ref}
46
+ className={cn(
47
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=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 z-50 min-w-32 origin-(--gentleduck-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=closed]:animate-out data-[state=open]:animate-in',
48
+ 'transition-all transition-discrete duration-[200ms,150ms] ease-(--duck-motion-ease)',
49
+ className,
50
+ )}
51
+ {...props}
52
+ />
53
+ ))
54
+ DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName
55
+
56
+ const DropdownMenuContent = React.forwardRef<
57
+ React.ComponentRef<typeof DropdownMenuPrimitive.Content>,
58
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
59
+ >(({ className, sideOffset = 4, ...props }, ref) => (
60
+ <DropdownMenuPrimitive.Portal>
61
+ <DropdownMenuPrimitive.Content
62
+ ref={ref}
63
+ sideOffset={sideOffset}
64
+ className={cn(
65
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=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 z-50 max-h-(--gentleduck-dropdown-menu-content-available-height) min-w-32 origin-(--gentleduck-dropdown-menu-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=open]:animate-in',
66
+ 'transition-all transition-discrete duration-[200ms,150ms] ease-(--duck-motion-ease)',
67
+ className,
68
+ )}
69
+ {...props}
70
+ />
71
+ </DropdownMenuPrimitive.Portal>
72
+ ))
73
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
74
+
75
+ const DropdownMenuItem = React.forwardRef<
76
+ React.ComponentRef<typeof DropdownMenuPrimitive.Item>,
77
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
78
+ inset?: boolean
79
+ variant?: 'default' | 'destructive'
80
+ }
81
+ >(({ className, inset, variant = 'default', ...props }, ref) => (
82
+ <DropdownMenuPrimitive.Item
83
+ ref={ref}
84
+ className={cn(
85
+ 'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
86
+ inset && 'ps-8',
87
+ variant === 'destructive' && 'text-destructive focus:text-destructive',
88
+ className,
89
+ )}
90
+ {...props}
91
+ />
92
+ ))
93
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
94
+
95
+ const DropdownMenuCheckboxItem = React.forwardRef<
96
+ React.ComponentRef<typeof DropdownMenuPrimitive.CheckboxItem>,
97
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
98
+ >(({ className, children, checked, ...props }, ref) => (
99
+ <DropdownMenuPrimitive.CheckboxItem
100
+ ref={ref}
101
+ className={cn(
102
+ 'relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
103
+ className,
104
+ )}
105
+ checked={checked}
106
+ {...props}>
107
+ <span className="absolute start-2 flex h-3.5 w-3.5 items-center justify-center">
108
+ <DropdownMenuPrimitive.ItemIndicator>
109
+ <Check aria-hidden="true" className="h-4 w-4" />
110
+ </DropdownMenuPrimitive.ItemIndicator>
111
+ </span>
112
+ {children}
113
+ </DropdownMenuPrimitive.CheckboxItem>
114
+ ))
115
+ DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName
116
+
117
+ const DropdownMenuRadioItem = React.forwardRef<
118
+ React.ComponentRef<typeof DropdownMenuPrimitive.RadioItem>,
119
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
120
+ >(({ className, children, ...props }, ref) => (
121
+ <DropdownMenuPrimitive.RadioItem
122
+ ref={ref}
123
+ className={cn(
124
+ 'relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50',
125
+ className,
126
+ )}
127
+ {...props}>
128
+ <span className="absolute start-2 flex h-3.5 w-3.5 items-center justify-center">
129
+ <DropdownMenuPrimitive.ItemIndicator>
130
+ <Circle aria-hidden="true" className="h-2 w-2 fill-current" />
131
+ </DropdownMenuPrimitive.ItemIndicator>
132
+ </span>
133
+ {children}
134
+ </DropdownMenuPrimitive.RadioItem>
135
+ ))
136
+ DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
137
+
138
+ const DropdownMenuLabel = React.forwardRef<
139
+ React.ComponentRef<typeof DropdownMenuPrimitive.Label>,
140
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
141
+ inset?: boolean
142
+ }
143
+ >(({ className, inset, ...props }, ref) => (
144
+ <DropdownMenuPrimitive.Label
145
+ ref={ref}
146
+ className={cn('px-2 py-1.5 font-semibold text-sm', inset && 'ps-8', className)}
147
+ {...props}
148
+ />
149
+ ))
150
+ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
151
+
152
+ const DropdownMenuSeparator = React.forwardRef<
153
+ React.ComponentRef<typeof DropdownMenuPrimitive.Separator>,
154
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
155
+ >(({ className, ...props }, ref) => (
156
+ <DropdownMenuPrimitive.Separator ref={ref} className={cn('-mx-1 my-1 h-px bg-muted', className)} {...props} />
157
+ ))
158
+ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
159
+
160
+ const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
161
+ return <span className={cn('ms-auto text-xs tracking-widest opacity-60', className)} {...props} />
162
+ }
163
+ DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'
164
+
165
+ export {
166
+ DropdownMenu,
167
+ DropdownMenuTrigger,
168
+ DropdownMenuContent,
169
+ DropdownMenuItem,
170
+ DropdownMenuCheckboxItem,
171
+ DropdownMenuRadioItem,
172
+ DropdownMenuLabel,
173
+ DropdownMenuSeparator,
174
+ DropdownMenuShortcut,
175
+ DropdownMenuGroup,
176
+ DropdownMenuPortal,
177
+ DropdownMenuSub,
178
+ DropdownMenuSubContent,
179
+ DropdownMenuSubTrigger,
180
+ DropdownMenuRadioGroup,
181
+ }
@@ -0,0 +1 @@
1
+ export * from './dropdown-menu'
@@ -0,0 +1,15 @@
1
+ import { cva } from '@gentleduck/variants'
2
+ export const emptyMediaVariants = cva(
3
+ 'mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0',
4
+ {
5
+ defaultVariants: {
6
+ variant: 'default',
7
+ },
8
+ variants: {
9
+ variant: {
10
+ default: 'bg-transparent',
11
+ icon: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6",
12
+ },
13
+ },
14
+ },
15
+ )
@@ -0,0 +1,73 @@
1
+ import { cn } from '@gentleduck/libs/cn'
2
+ import { type Direction, useDirection } from '@gentleduck/primitives/direction'
3
+ import type { VariantProps } from '@gentleduck/variants'
4
+ import { emptyMediaVariants } from './empty.constants'
5
+
6
+ function Empty({ className, dir, ...props }: React.ComponentProps<'div'>) {
7
+ const direction = useDirection(dir as Direction)
8
+ return (
9
+ <div
10
+ className={cn(
11
+ 'flex min-w-0 flex-1 flex-col items-center justify-center gap-6 text-balance rounded-lg border-dashed p-6 text-center md:p-12',
12
+ className,
13
+ )}
14
+ dir={direction}
15
+ data-slot="empty"
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+
21
+ function EmptyHeader({ className, ...props }: React.ComponentProps<'div'>) {
22
+ return (
23
+ <div
24
+ className={cn('flex max-w-sm flex-col items-center gap-2 text-center', className)}
25
+ data-slot="empty-header"
26
+ {...props}
27
+ />
28
+ )
29
+ }
30
+
31
+ function EmptyMedia({
32
+ className,
33
+ variant = 'default',
34
+ ...props
35
+ }: React.ComponentProps<'div'> & VariantProps<typeof emptyMediaVariants>) {
36
+ return (
37
+ <div
38
+ className={cn(emptyMediaVariants({ className, variant }))}
39
+ data-slot="empty-icon"
40
+ data-variant={variant}
41
+ {...props}
42
+ />
43
+ )
44
+ }
45
+
46
+ function EmptyTitle({ className, ...props }: React.ComponentProps<'div'>) {
47
+ return <div className={cn('font-medium text-lg tracking-tight', className)} data-slot="empty-title" {...props} />
48
+ }
49
+
50
+ function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) {
51
+ return (
52
+ <div
53
+ className={cn(
54
+ 'text-muted-foreground text-sm/relaxed [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4',
55
+ className,
56
+ )}
57
+ data-slot="empty-description"
58
+ {...props}
59
+ />
60
+ )
61
+ }
62
+
63
+ function EmptyContent({ className, ...props }: React.ComponentProps<'div'>) {
64
+ return (
65
+ <div
66
+ className={cn('flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm', className)}
67
+ data-slot="empty-content"
68
+ {...props}
69
+ />
70
+ )
71
+ }
72
+
73
+ export { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia }
@@ -0,0 +1,2 @@
1
+ export * from './empty'
2
+ export * from './empty.constants'
@@ -0,0 +1,22 @@
1
+ import { cva } from '@gentleduck/variants'
2
+
3
+ export const fieldVariants = cva('group/field flex w-full gap-3 data-[invalid=true]:text-destructive', {
4
+ defaultVariants: {
5
+ orientation: 'vertical',
6
+ },
7
+ variants: {
8
+ orientation: {
9
+ horizontal: [
10
+ 'flex-row items-center',
11
+ '[&>[data-slot=field-label]]:flex-auto',
12
+ 'has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
13
+ ],
14
+ responsive: [
15
+ '@md/field-group:flex-row flex-col @md/field-group:items-center @md/field-group:[&>*]:w-auto [&>*]:w-full [&>.sr-only]:w-auto',
16
+ '@md/field-group:[&>[data-slot=field-label]]:flex-auto',
17
+ '@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
18
+ ],
19
+ vertical: ['flex-col [&>*]:w-full [&>.sr-only]:w-auto'],
20
+ },
21
+ },
22
+ })
@@ -0,0 +1,203 @@
1
+ 'use client'
2
+
3
+ import { cn } from '@gentleduck/libs/cn'
4
+ import { type Direction, useDirection } from '@gentleduck/primitives/direction'
5
+ import type { VariantProps } from '@gentleduck/variants'
6
+ import { useMemo } from 'react'
7
+ import { Label } from '../label'
8
+ import { Separator } from '../separator'
9
+ import { fieldVariants } from './field.constants'
10
+
11
+ function FieldSet({ className, dir, ...props }: React.ComponentProps<'fieldset'>) {
12
+ const direction = useDirection(dir as Direction)
13
+ return (
14
+ <fieldset
15
+ className={cn(
16
+ 'flex flex-col gap-6',
17
+ 'has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3',
18
+ className,
19
+ )}
20
+ dir={direction}
21
+ data-slot="field-set"
22
+ {...props}
23
+ />
24
+ )
25
+ }
26
+
27
+ function FieldLegend({
28
+ className,
29
+ variant = 'legend',
30
+ ...props
31
+ }: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {
32
+ return (
33
+ <legend
34
+ className={cn('mb-3 font-medium', 'data-[variant=legend]:text-base', 'data-[variant=label]:text-sm', className)}
35
+ data-slot="field-legend"
36
+ data-variant={variant}
37
+ {...props}
38
+ />
39
+ )
40
+ }
41
+
42
+ function FieldGroup({ className, ...props }: React.ComponentProps<'div'>) {
43
+ return (
44
+ <div
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
+ data-slot="field-group"
50
+ {...props}
51
+ />
52
+ )
53
+ }
54
+
55
+ function Field({
56
+ className,
57
+ orientation = 'vertical',
58
+ ...props
59
+ }: React.ComponentProps<'div'> & VariantProps<typeof fieldVariants>) {
60
+ return (
61
+ <div
62
+ className={cn(fieldVariants({ orientation }), className)}
63
+ data-orientation={orientation}
64
+ data-slot="field"
65
+ role="group"
66
+ {...props}
67
+ />
68
+ )
69
+ }
70
+
71
+ function FieldContent({ className, ...props }: React.ComponentProps<'div'>) {
72
+ return (
73
+ <div
74
+ className={cn('group/field-content flex flex-1 flex-col gap-1.5 leading-snug', className)}
75
+ data-slot="field-content"
76
+ {...props}
77
+ />
78
+ )
79
+ }
80
+
81
+ function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
82
+ return (
83
+ <Label
84
+ className={cn(
85
+ 'group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50',
86
+ 'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4',
87
+ 'has-data-[state=checked]:border-primary has-data-[state=checked]:bg-primary/5 dark:has-data-[state=checked]:bg-primary/10',
88
+ className,
89
+ )}
90
+ data-slot="field-label"
91
+ {...props}
92
+ />
93
+ )
94
+ }
95
+
96
+ function FieldTitle({ className, ...props }: React.ComponentProps<'div'>) {
97
+ return (
98
+ <div
99
+ className={cn(
100
+ 'flex w-fit items-center gap-2 font-medium text-sm leading-snug group-data-[disabled=true]/field:opacity-50',
101
+ className,
102
+ )}
103
+ data-slot="field-label"
104
+ {...props}
105
+ />
106
+ )
107
+ }
108
+
109
+ function FieldDescription({ className, ...props }: React.ComponentProps<'p'>) {
110
+ return (
111
+ <p
112
+ className={cn(
113
+ 'font-normal text-muted-foreground text-sm leading-normal group-has-[[data-orientation=horizontal]]/field:text-balance',
114
+ 'nth-last-2:-mt-1 last:mt-0 [[data-variant=legend]+&]:-mt-1.5',
115
+ '[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4',
116
+ className,
117
+ )}
118
+ data-slot="field-description"
119
+ {...props}
120
+ />
121
+ )
122
+ }
123
+
124
+ function FieldSeparator({
125
+ children,
126
+ className,
127
+ ...props
128
+ }: React.ComponentProps<'div'> & {
129
+ children?: React.ReactNode
130
+ }) {
131
+ return (
132
+ <div
133
+ className={cn('relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2', className)}
134
+ data-content={!!children}
135
+ data-slot="field-separator"
136
+ {...props}>
137
+ <Separator className="absolute inset-0 top-1/2" />
138
+ {children && (
139
+ <span
140
+ className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
141
+ data-slot="field-separator-content">
142
+ {children}
143
+ </span>
144
+ )}
145
+ </div>
146
+ )
147
+ }
148
+
149
+ function FieldError({
150
+ className,
151
+ children,
152
+ errors,
153
+ ...props
154
+ }: React.ComponentProps<'div'> & {
155
+ errors?: Array<{ message?: string } | undefined>
156
+ }) {
157
+ const content = useMemo(() => {
158
+ if (children) {
159
+ return children
160
+ }
161
+
162
+ if (!errors) {
163
+ return null
164
+ }
165
+
166
+ if (errors?.length === 1 && errors[0]?.message) {
167
+ return errors[0].message
168
+ }
169
+
170
+ return (
171
+ <ul className="ms-4 flex list-disc flex-col gap-1">
172
+ {errors.map((error, index) => error?.message && <li key={index}>{error.message}</li>)}
173
+ </ul>
174
+ )
175
+ }, [children, errors])
176
+
177
+ if (!content) {
178
+ return null
179
+ }
180
+
181
+ return (
182
+ <div
183
+ className={cn('font-normal text-destructive text-sm', className)}
184
+ data-slot="field-error"
185
+ role="alert"
186
+ {...props}>
187
+ {content}
188
+ </div>
189
+ )
190
+ }
191
+
192
+ export {
193
+ Field,
194
+ FieldLabel,
195
+ FieldDescription,
196
+ FieldError,
197
+ FieldGroup,
198
+ FieldLegend,
199
+ FieldSeparator,
200
+ FieldSet,
201
+ FieldContent,
202
+ FieldTitle,
203
+ }
@@ -0,0 +1,2 @@
1
+ export * from './field'
2
+ export * from './field.constants'
@@ -0,0 +1,79 @@
1
+ 'use client'
2
+
3
+ import { cn } from '@gentleduck/libs/cn'
4
+ import * as HoverCardPrimitive from '@gentleduck/primitives/hover-card'
5
+ import type { VariantProps } from '@gentleduck/variants'
6
+ import * as React from 'react'
7
+ import { buttonVariants } from '../button'
8
+
9
+ const HoverCardPlacementContext =
10
+ React.createContext<React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>['side']>('top')
11
+
12
+ const HoverCard = React.forwardRef<
13
+ React.ComponentRef<typeof HoverCardPrimitive.Root>,
14
+ React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Root> & {
15
+ delayDuration?: number
16
+ skipDelayDuration?: number
17
+ placement?: React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>['side']
18
+ }
19
+ >(({ closeDelay, openDelay, placement = 'top', delayDuration, skipDelayDuration, ...props }, _ref) => {
20
+ void skipDelayDuration
21
+
22
+ return (
23
+ <HoverCardPlacementContext.Provider value={placement}>
24
+ <HoverCardPrimitive.Root
25
+ closeDelay={closeDelay}
26
+ data-slot="hover-card"
27
+ openDelay={openDelay ?? delayDuration}
28
+ {...props}
29
+ />
30
+ </HoverCardPlacementContext.Provider>
31
+ )
32
+ })
33
+ HoverCard.displayName = 'HoverCard'
34
+
35
+ const HoverCardTrigger = React.forwardRef<
36
+ React.ComponentRef<typeof HoverCardPrimitive.Trigger>,
37
+ React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Trigger> & VariantProps<typeof buttonVariants>
38
+ >(({ children, className, variant = 'outline', size = 'default', border = 'default', ...props }, ref) => {
39
+ return (
40
+ <HoverCardPrimitive.Trigger
41
+ ref={ref}
42
+ className={cn(buttonVariants({ variant, size, border }), className)}
43
+ data-slot="hover-card-trigger"
44
+ {...props}>
45
+ {children}
46
+ </HoverCardPrimitive.Trigger>
47
+ )
48
+ })
49
+ HoverCardTrigger.displayName = HoverCardPrimitive.Trigger.displayName
50
+
51
+ const HoverCardContent = React.forwardRef<
52
+ React.ComponentRef<typeof HoverCardPrimitive.Content>,
53
+ React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
54
+ >(({ className, children, side, align = 'center', sideOffset = 4, ...props }, ref) => {
55
+ const defaultSide = React.useContext(HoverCardPlacementContext)
56
+
57
+ return (
58
+ <HoverCardPrimitive.Portal>
59
+ <HoverCardPrimitive.Content
60
+ ref={ref}
61
+ align={align}
62
+ className={cn(
63
+ 'z-50 w-64 overflow-hidden rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden',
64
+ 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=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 origin-(--gentleduck-hover-card-content-transform-origin) data-[state=closed]:animate-out data-[state=open]:animate-in',
65
+ 'transition-all transition-discrete duration-[200ms,150ms] ease-(--duck-motion-ease)',
66
+ className,
67
+ )}
68
+ data-slot="hover-card-content"
69
+ side={side ?? defaultSide}
70
+ sideOffset={sideOffset}
71
+ {...props}>
72
+ {children}
73
+ </HoverCardPrimitive.Content>
74
+ </HoverCardPrimitive.Portal>
75
+ )
76
+ })
77
+ HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
78
+
79
+ export { HoverCard, HoverCardTrigger, HoverCardContent }
@@ -0,0 +1 @@
1
+ export * from './hover-card'
@@ -0,0 +1 @@
1
+ export * from './input'
@@ -0,0 +1,45 @@
1
+ import { cn } from '@gentleduck/libs/cn'
2
+ import { type Direction, useDirection } from '@gentleduck/primitives/direction'
3
+ import * as React from 'react'
4
+
5
+ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
6
+ ({ className, type, dir, ...props }, ref) => {
7
+ const direction = useDirection(dir as Direction)
8
+ return (
9
+ <input
10
+ dir={direction}
11
+ type={type}
12
+ className={cn(
13
+ // base
14
+ 'h-8 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base outline-none transition-colors',
15
+ // file
16
+ 'file:inline-flex file:h-6 file:border-0 file:bg-transparent file:font-medium file:text-foreground file:text-sm',
17
+ // placeholder
18
+ 'placeholder:text-muted-foreground',
19
+ // focus
20
+ 'focus-visible:ring-1 focus-visible:ring-ring',
21
+ // disabled
22
+ 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50',
23
+ // responsive
24
+ 'md:text-sm',
25
+ // dark
26
+ // 'dark:bg-input/30 dark:disabled:bg-input/80',
27
+ // aria-invalid
28
+ 'aria-invalid:border-destructive aria-invalid:text-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40',
29
+ // aria-disabled
30
+ 'aria-disabled:pointer-events-none aria-disabled:opacity-50',
31
+ // aria-warning
32
+ 'aria-warning:border-warning aria-warning:text-warning aria-warning:ring-warning/20 dark:aria-warning:border-warning/50 dark:aria-warning:ring-warning/40',
33
+ className,
34
+ )}
35
+ ref={ref}
36
+ data-slot="input"
37
+ {...props}
38
+ />
39
+ )
40
+ },
41
+ )
42
+
43
+ Input.displayName = 'Input'
44
+
45
+ export { Input }
@@ -0,0 +1 @@
1
+ export * from './input-group'