@opencosmos/ui 1.3.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 (260) hide show
  1. package/.claude/CLAUDE.md +239 -0
  2. package/README.md +161 -0
  3. package/dist/cli.mjs +151 -0
  4. package/dist/dates.d.mts +20 -0
  5. package/dist/dates.d.ts +20 -0
  6. package/dist/dates.js +240 -0
  7. package/dist/dates.js.map +1 -0
  8. package/dist/dates.mjs +203 -0
  9. package/dist/dates.mjs.map +1 -0
  10. package/dist/dnd.d.mts +126 -0
  11. package/dist/dnd.d.ts +126 -0
  12. package/dist/dnd.js +274 -0
  13. package/dist/dnd.js.map +1 -0
  14. package/dist/dnd.mjs +250 -0
  15. package/dist/dnd.mjs.map +1 -0
  16. package/dist/fontThemes-Dh8mtXES.d.mts +868 -0
  17. package/dist/fontThemes-Dh8mtXES.d.ts +868 -0
  18. package/dist/forms.d.mts +38 -0
  19. package/dist/forms.d.ts +38 -0
  20. package/dist/forms.js +198 -0
  21. package/dist/forms.js.map +1 -0
  22. package/dist/forms.mjs +159 -0
  23. package/dist/forms.mjs.map +1 -0
  24. package/dist/hooks-1b8WaQf1.d.mts +225 -0
  25. package/dist/hooks-CKW8vE9H.d.ts +225 -0
  26. package/dist/hooks.d.mts +3 -0
  27. package/dist/hooks.d.ts +3 -0
  28. package/dist/hooks.js +971 -0
  29. package/dist/hooks.js.map +1 -0
  30. package/dist/hooks.mjs +943 -0
  31. package/dist/hooks.mjs.map +1 -0
  32. package/dist/index-DscTIrZ2.d.mts +29 -0
  33. package/dist/index-DscTIrZ2.d.ts +29 -0
  34. package/dist/index.d.mts +3382 -0
  35. package/dist/index.d.ts +3382 -0
  36. package/dist/index.js +15146 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/index.mjs +14802 -0
  39. package/dist/index.mjs.map +1 -0
  40. package/dist/providers-CXPDMsl7.d.mts +30 -0
  41. package/dist/providers-Dn_Msjvz.d.ts +30 -0
  42. package/dist/providers.d.mts +3 -0
  43. package/dist/providers.d.ts +3 -0
  44. package/dist/providers.js +1885 -0
  45. package/dist/providers.js.map +1 -0
  46. package/dist/providers.mjs +1859 -0
  47. package/dist/providers.mjs.map +1 -0
  48. package/dist/tables.d.mts +10 -0
  49. package/dist/tables.d.ts +10 -0
  50. package/dist/tables.js +248 -0
  51. package/dist/tables.js.map +1 -0
  52. package/dist/tables.mjs +218 -0
  53. package/dist/tables.mjs.map +1 -0
  54. package/dist/tokens.d.mts +1065 -0
  55. package/dist/tokens.d.ts +1065 -0
  56. package/dist/tokens.js +2637 -0
  57. package/dist/tokens.js.map +1 -0
  58. package/dist/tokens.mjs +2555 -0
  59. package/dist/tokens.mjs.map +1 -0
  60. package/dist/utils-CIIM7dAC.d.ts +986 -0
  61. package/dist/utils-Cs04sxth.d.mts +986 -0
  62. package/dist/utils.d.mts +4 -0
  63. package/dist/utils.d.ts +4 -0
  64. package/dist/utils.js +874 -0
  65. package/dist/utils.js.map +1 -0
  66. package/dist/utils.mjs +806 -0
  67. package/dist/utils.mjs.map +1 -0
  68. package/dist/validation-Bj1ye-v_.d.mts +114 -0
  69. package/dist/validation-Bj1ye-v_.d.ts +114 -0
  70. package/dist/webgl.d.mts +104 -0
  71. package/dist/webgl.d.ts +104 -0
  72. package/dist/webgl.js +226 -0
  73. package/dist/webgl.js.map +1 -0
  74. package/dist/webgl.mjs +195 -0
  75. package/dist/webgl.mjs.map +1 -0
  76. package/package.json +267 -0
  77. package/src/cli.ts +206 -0
  78. package/src/component-registry.ts +183 -0
  79. package/src/components/actions/Button.test.tsx +61 -0
  80. package/src/components/actions/Button.tsx +70 -0
  81. package/src/components/actions/Link.tsx +78 -0
  82. package/src/components/actions/Magnetic.tsx +68 -0
  83. package/src/components/actions/Toggle.test.tsx +40 -0
  84. package/src/components/actions/Toggle.tsx +47 -0
  85. package/src/components/actions/ToggleGroup.tsx +70 -0
  86. package/src/components/actions/index.ts +5 -0
  87. package/src/components/backgrounds/FaultyTerminal.tsx +426 -0
  88. package/src/components/backgrounds/OrbBackground.tsx +424 -0
  89. package/src/components/backgrounds/WarpBackground.tsx +358 -0
  90. package/src/components/backgrounds/index.ts +3 -0
  91. package/src/components/blocks/Hero.tsx +142 -0
  92. package/src/components/blocks/social/OpenGraphCard.tsx +243 -0
  93. package/src/components/cursor/SplashCursor.tsx +1315 -0
  94. package/src/components/cursor/TargetCursor.tsx +187 -0
  95. package/src/components/cursor/index.ts +2 -0
  96. package/src/components/data-display/AspectImage.tsx +73 -0
  97. package/src/components/data-display/Avatar.test.tsx +35 -0
  98. package/src/components/data-display/Avatar.tsx +55 -0
  99. package/src/components/data-display/Badge.test.tsx +43 -0
  100. package/src/components/data-display/Badge.tsx +84 -0
  101. package/src/components/data-display/Brand.tsx +123 -0
  102. package/src/components/data-display/Calendar.tsx +70 -0
  103. package/src/components/data-display/Card.test.tsx +92 -0
  104. package/src/components/data-display/Card.tsx +115 -0
  105. package/src/components/data-display/Code.tsx +210 -0
  106. package/src/components/data-display/CollapsibleCodeBlock.tsx +238 -0
  107. package/src/components/data-display/DataTable.tsx +119 -0
  108. package/src/components/data-display/DescriptionList.tsx +41 -0
  109. package/src/components/data-display/GitHubIcon.tsx +44 -0
  110. package/src/components/data-display/Heading.test.tsx +36 -0
  111. package/src/components/data-display/Heading.tsx +83 -0
  112. package/src/components/data-display/StatCard.tsx +195 -0
  113. package/src/components/data-display/Table.tsx +133 -0
  114. package/src/components/data-display/Text.test.tsx +48 -0
  115. package/src/components/data-display/Text.tsx +144 -0
  116. package/src/components/data-display/Timeline.tsx +194 -0
  117. package/src/components/data-display/TreeView.tsx +226 -0
  118. package/src/components/data-display/Typewriter.tsx +119 -0
  119. package/src/components/data-display/VariableWeightText.tsx +130 -0
  120. package/src/components/data-display/index.ts +19 -0
  121. package/src/components/feedback/Alert.test.tsx +44 -0
  122. package/src/components/feedback/Alert.tsx +65 -0
  123. package/src/components/feedback/EmptyState.tsx +113 -0
  124. package/src/components/feedback/Progress.test.tsx +60 -0
  125. package/src/components/feedback/Progress.tsx +30 -0
  126. package/src/components/feedback/ProgressBar.tsx +158 -0
  127. package/src/components/feedback/Skeleton.test.tsx +39 -0
  128. package/src/components/feedback/Skeleton.tsx +45 -0
  129. package/src/components/feedback/Sonner.tsx +28 -0
  130. package/src/components/feedback/Spinner.test.tsx +33 -0
  131. package/src/components/feedback/Spinner.tsx +99 -0
  132. package/src/components/feedback/Stepper.tsx +307 -0
  133. package/src/components/feedback/Toast/Toast.tsx +243 -0
  134. package/src/components/feedback/Toast/index.ts +2 -0
  135. package/src/components/feedback/index.ts +9 -0
  136. package/src/components/forms/Checkbox.test.tsx +40 -0
  137. package/src/components/forms/Checkbox.tsx +31 -0
  138. package/src/components/forms/ColorPicker.tsx +118 -0
  139. package/src/components/forms/Combobox.tsx +96 -0
  140. package/src/components/forms/DragDrop.tsx +440 -0
  141. package/src/components/forms/FileUpload.tsx +252 -0
  142. package/src/components/forms/FilterButton.tsx +65 -0
  143. package/src/components/forms/Form.tsx +197 -0
  144. package/src/components/forms/Input.test.tsx +46 -0
  145. package/src/components/forms/Input.tsx +43 -0
  146. package/src/components/forms/InputOTP.tsx +81 -0
  147. package/src/components/forms/Label.test.tsx +20 -0
  148. package/src/components/forms/Label.tsx +25 -0
  149. package/src/components/forms/RadioGroup.tsx +51 -0
  150. package/src/components/forms/SearchBar.tsx +215 -0
  151. package/src/components/forms/Select.test.tsx +118 -0
  152. package/src/components/forms/Select.tsx +274 -0
  153. package/src/components/forms/Slider.tsx +29 -0
  154. package/src/components/forms/Switch.test.tsx +76 -0
  155. package/src/components/forms/Switch.tsx +30 -0
  156. package/src/components/forms/TextField.tsx +152 -0
  157. package/src/components/forms/Textarea.test.tsx +41 -0
  158. package/src/components/forms/Textarea.tsx +29 -0
  159. package/src/components/forms/ThemeSwitcher.tsx +290 -0
  160. package/src/components/forms/ThemeToggle.tsx +151 -0
  161. package/src/components/forms/index.ts +19 -0
  162. package/src/components/layout/Accordion.test.tsx +66 -0
  163. package/src/components/layout/Accordion.tsx +64 -0
  164. package/src/components/layout/AspectRatio.tsx +7 -0
  165. package/src/components/layout/Carousel.tsx +277 -0
  166. package/src/components/layout/Collapsible.test.tsx +40 -0
  167. package/src/components/layout/Collapsible.tsx +31 -0
  168. package/src/components/layout/Container.test.tsx +45 -0
  169. package/src/components/layout/Container.tsx +99 -0
  170. package/src/components/layout/CustomizerPanel.tsx +400 -0
  171. package/src/components/layout/DatePicker.tsx +57 -0
  172. package/src/components/layout/Footer/Footer.tsx +175 -0
  173. package/src/components/layout/Footer/index.ts +2 -0
  174. package/src/components/layout/GlassSurface.tsx +82 -0
  175. package/src/components/layout/Grid.test.tsx +31 -0
  176. package/src/components/layout/Grid.tsx +130 -0
  177. package/src/components/layout/Header/Header.tsx +450 -0
  178. package/src/components/layout/Header/index.ts +2 -0
  179. package/src/components/layout/PageLayout.tsx +180 -0
  180. package/src/components/layout/PageTemplate.tsx +158 -0
  181. package/src/components/layout/Resizable.tsx +48 -0
  182. package/src/components/layout/ScrollArea.tsx +53 -0
  183. package/src/components/layout/Separator.test.tsx +28 -0
  184. package/src/components/layout/Separator.tsx +29 -0
  185. package/src/components/layout/Sidebar.tsx +171 -0
  186. package/src/components/layout/Stack.test.tsx +41 -0
  187. package/src/components/layout/Stack.tsx +89 -0
  188. package/src/components/layout/glass-surface.css +60 -0
  189. package/src/components/layout/index.ts +18 -0
  190. package/src/components/motion/AnimatedBeam.tsx +159 -0
  191. package/src/components/navigation/Breadcrumb.test.tsx +57 -0
  192. package/src/components/navigation/Breadcrumb.tsx +119 -0
  193. package/src/components/navigation/Breadcrumbs.tsx +221 -0
  194. package/src/components/navigation/Command.tsx +159 -0
  195. package/src/components/navigation/Menubar.tsx +115 -0
  196. package/src/components/navigation/NavLink.tsx +55 -0
  197. package/src/components/navigation/NavigationMenu.tsx +125 -0
  198. package/src/components/navigation/Pagination.tsx +121 -0
  199. package/src/components/navigation/SecondaryNav.tsx +100 -0
  200. package/src/components/navigation/Tabs.test.tsx +47 -0
  201. package/src/components/navigation/Tabs.tsx +60 -0
  202. package/src/components/navigation/TertiaryNav.tsx +90 -0
  203. package/src/components/navigation/index.ts +10 -0
  204. package/src/components/overlays/AlertDialog.test.tsx +69 -0
  205. package/src/components/overlays/AlertDialog.tsx +166 -0
  206. package/src/components/overlays/ContextMenu.tsx +243 -0
  207. package/src/components/overlays/Dialog.test.tsx +79 -0
  208. package/src/components/overlays/Dialog.tsx +158 -0
  209. package/src/components/overlays/Drawer.tsx +128 -0
  210. package/src/components/overlays/Dropdown.tsx +253 -0
  211. package/src/components/overlays/DropdownMenu.tsx +242 -0
  212. package/src/components/overlays/HoverCard.tsx +32 -0
  213. package/src/components/overlays/Modal.tsx +250 -0
  214. package/src/components/overlays/NotificationCenter.tsx +364 -0
  215. package/src/components/overlays/Popover.test.tsx +40 -0
  216. package/src/components/overlays/Popover.tsx +46 -0
  217. package/src/components/overlays/Sheet.tsx +163 -0
  218. package/src/components/overlays/Tooltip.test.tsx +33 -0
  219. package/src/components/overlays/Tooltip.tsx +32 -0
  220. package/src/components/overlays/index.ts +12 -0
  221. package/src/dates.ts +2 -0
  222. package/src/dnd.ts +1 -0
  223. package/src/forms.ts +1 -0
  224. package/src/globals.css +187 -0
  225. package/src/hooks/index.ts +6 -0
  226. package/src/hooks/useForm.ts +247 -0
  227. package/src/hooks/useMotionPreference.test.ts +102 -0
  228. package/src/hooks/useMotionPreference.ts +78 -0
  229. package/src/hooks/useTheme.ts +58 -0
  230. package/src/hooks.ts +9 -0
  231. package/src/index.ts +168 -0
  232. package/src/lib/animations.ts +356 -0
  233. package/src/lib/breadcrumbs.ts +94 -0
  234. package/src/lib/colors.ts +493 -0
  235. package/src/lib/store/customizer.ts +482 -0
  236. package/src/lib/store/index.ts +3 -0
  237. package/src/lib/store/theme.ts +55 -0
  238. package/src/lib/syntax-parser/index.ts +50 -0
  239. package/src/lib/syntax-parser/patterns.ts +64 -0
  240. package/src/lib/syntax-parser/tokenizer.ts +117 -0
  241. package/src/lib/syntax-parser/types.ts +27 -0
  242. package/src/lib/utils.ts +6 -0
  243. package/src/lib/validation.ts +204 -0
  244. package/src/lib/webgl/Color.ts +11 -0
  245. package/src/lib/webgl/Mesh.ts +41 -0
  246. package/src/lib/webgl/Program.ts +118 -0
  247. package/src/lib/webgl/Renderer.ts +51 -0
  248. package/src/lib/webgl/Triangle.ts +27 -0
  249. package/src/lib/webgl/Vec3.ts +18 -0
  250. package/src/lib/webgl/index.ts +13 -0
  251. package/src/nativewind-env.d.ts +1 -0
  252. package/src/providers/ThemeProvider.tsx +461 -0
  253. package/src/providers/index.ts +1 -0
  254. package/src/providers.ts +7 -0
  255. package/src/tables.ts +1 -0
  256. package/src/test/setup.ts +39 -0
  257. package/src/theme.css +158 -0
  258. package/src/tokens.ts +7 -0
  259. package/src/utils.ts +12 -0
  260. package/src/webgl.ts +1 -0
@@ -0,0 +1,242 @@
1
+ "use client";
2
+ import * as React from "react"
3
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
4
+ import { Check, ChevronRight, Circle } from "lucide-react"
5
+
6
+ import { cn } from "../../lib/utils"
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 = (
21
+ {
22
+ ref,
23
+ className,
24
+ inset,
25
+ children,
26
+ ...props
27
+ }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
28
+ inset?: boolean
29
+ } & {
30
+ ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>>;
31
+ }
32
+ ) => (<DropdownMenuPrimitive.SubTrigger
33
+ ref={ref}
34
+ className={cn(
35
+ "flex cursor-default select-none items-center rounded-xs px-2 py-1.5 text-sm outline-none focus:bg-primary focus:text-primary-foreground data-[state=open]:bg-primary data-[state=open]:text-primary-foreground",
36
+ inset && "pl-8",
37
+ className
38
+ )}
39
+ {...props}
40
+ >
41
+ {children}
42
+ <ChevronRight className="ml-auto h-4 w-4" />
43
+ </DropdownMenuPrimitive.SubTrigger>)
44
+
45
+ const DropdownMenuSubContent = (
46
+ {
47
+ ref,
48
+ className,
49
+ style,
50
+ ...props
51
+ }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> & {
52
+ ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.SubContent>>;
53
+ }
54
+ ) => (<DropdownMenuPrimitive.SubContent
55
+ ref={ref}
56
+ className={cn(
57
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out 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",
58
+ className
59
+ )}
60
+ style={{
61
+ backgroundColor: 'var(--color-popover, #ffffff)',
62
+ color: 'var(--color-popover-foreground, #0a0a0a)',
63
+ border: '1px solid var(--color-border, #d4d4d4)',
64
+ borderRadius: 'var(--radius, 0.5rem)',
65
+ boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
66
+ zIndex: 50,
67
+ overflow: 'hidden',
68
+ ...style,
69
+ }}
70
+ {...props}
71
+ />)
72
+
73
+ const DropdownMenuContent = (
74
+ {
75
+ ref,
76
+ className,
77
+ sideOffset = 4,
78
+ style,
79
+ ...props
80
+ }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> & {
81
+ ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.Content>>;
82
+ }
83
+ ) => (<DropdownMenuPrimitive.Portal>
84
+ <DropdownMenuPrimitive.Content
85
+ ref={ref}
86
+ sideOffset={sideOffset}
87
+ className={cn(
88
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out 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",
89
+ className
90
+ )}
91
+ style={{
92
+ backgroundColor: 'var(--color-popover, #ffffff)',
93
+ color: 'var(--color-popover-foreground, #0a0a0a)',
94
+ border: '1px solid var(--color-border, #d4d4d4)',
95
+ borderRadius: 'var(--radius, 0.5rem)',
96
+ boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
97
+ zIndex: 50,
98
+ overflow: 'hidden',
99
+ ...style,
100
+ }}
101
+ {...props}
102
+ />
103
+ </DropdownMenuPrimitive.Portal>)
104
+
105
+ const DropdownMenuItem = (
106
+ {
107
+ ref,
108
+ className,
109
+ inset,
110
+ ...props
111
+ }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
112
+ inset?: boolean
113
+ } & {
114
+ ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.Item>>;
115
+ }
116
+ ) => (<DropdownMenuPrimitive.Item
117
+ ref={ref}
118
+ className={cn(
119
+ "relative flex cursor-default select-none items-center rounded-xs px-2 py-1.5 text-sm outline-none transition-colors focus:bg-primary focus:text-primary-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
120
+ inset && "pl-8",
121
+ className
122
+ )}
123
+ {...props}
124
+ />)
125
+
126
+ const DropdownMenuCheckboxItem = (
127
+ {
128
+ ref,
129
+ className,
130
+ children,
131
+ checked,
132
+ ...props
133
+ }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> & {
134
+ ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>>;
135
+ }
136
+ ) => (<DropdownMenuPrimitive.CheckboxItem
137
+ ref={ref}
138
+ className={cn(
139
+ "relative flex cursor-default select-none items-center rounded-xs py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-primary focus:text-primary-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
140
+ className
141
+ )}
142
+ checked={checked}
143
+ {...props}
144
+ >
145
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
146
+ <DropdownMenuPrimitive.ItemIndicator>
147
+ <Check className="h-4 w-4" />
148
+ </DropdownMenuPrimitive.ItemIndicator>
149
+ </span>
150
+ {children}
151
+ </DropdownMenuPrimitive.CheckboxItem>)
152
+
153
+ const DropdownMenuRadioItem = (
154
+ {
155
+ ref,
156
+ className,
157
+ children,
158
+ ...props
159
+ }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> & {
160
+ ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>>;
161
+ }
162
+ ) => (<DropdownMenuPrimitive.RadioItem
163
+ ref={ref}
164
+ className={cn(
165
+ "relative flex cursor-default select-none items-center rounded-xs py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-primary focus:text-primary-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
166
+ className
167
+ )}
168
+ {...props}
169
+ >
170
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
171
+ <DropdownMenuPrimitive.ItemIndicator>
172
+ <Circle className="h-2 w-2 fill-current" />
173
+ </DropdownMenuPrimitive.ItemIndicator>
174
+ </span>
175
+ {children}
176
+ </DropdownMenuPrimitive.RadioItem>)
177
+
178
+ const DropdownMenuLabel = (
179
+ {
180
+ ref,
181
+ className,
182
+ inset,
183
+ ...props
184
+ }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
185
+ inset?: boolean
186
+ } & {
187
+ ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.Label>>;
188
+ }
189
+ ) => (<DropdownMenuPrimitive.Label
190
+ ref={ref}
191
+ className={cn(
192
+ "px-2 py-1.5 text-sm font-semibold",
193
+ inset && "pl-8",
194
+ className
195
+ )}
196
+ {...props}
197
+ />)
198
+
199
+ const DropdownMenuSeparator = (
200
+ {
201
+ ref,
202
+ className,
203
+ ...props
204
+ }: React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator> & {
205
+ ref?: React.Ref<React.ElementRef<typeof DropdownMenuPrimitive.Separator>>;
206
+ }
207
+ ) => (<DropdownMenuPrimitive.Separator
208
+ ref={ref}
209
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
210
+ {...props}
211
+ />)
212
+
213
+ const DropdownMenuShortcut = ({
214
+ className,
215
+ ...props
216
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
217
+ return (
218
+ <span
219
+ className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
220
+ {...props}
221
+ />
222
+ )
223
+ }
224
+ DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
225
+
226
+ export {
227
+ DropdownMenu,
228
+ DropdownMenuTrigger,
229
+ DropdownMenuContent,
230
+ DropdownMenuItem,
231
+ DropdownMenuCheckboxItem,
232
+ DropdownMenuRadioItem,
233
+ DropdownMenuLabel,
234
+ DropdownMenuSeparator,
235
+ DropdownMenuShortcut,
236
+ DropdownMenuGroup,
237
+ DropdownMenuPortal,
238
+ DropdownMenuSub,
239
+ DropdownMenuSubContent,
240
+ DropdownMenuSubTrigger,
241
+ DropdownMenuRadioGroup,
242
+ }
@@ -0,0 +1,32 @@
1
+ "use client";
2
+ import * as React from "react"
3
+ import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
4
+
5
+ import { cn } from "../../lib/utils"
6
+
7
+ const HoverCard = HoverCardPrimitive.Root
8
+
9
+ const HoverCardTrigger = HoverCardPrimitive.Trigger
10
+
11
+ const HoverCardContent = (
12
+ {
13
+ ref,
14
+ className,
15
+ align = "center",
16
+ sideOffset = 4,
17
+ ...props
18
+ }: React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content> & {
19
+ ref?: React.Ref<React.ElementRef<typeof HoverCardPrimitive.Content>>;
20
+ }
21
+ ) => (<HoverCardPrimitive.Content
22
+ ref={ref}
23
+ align={align}
24
+ sideOffset={sideOffset}
25
+ className={cn(
26
+ "z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out 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",
27
+ className
28
+ )}
29
+ {...props}
30
+ />)
31
+
32
+ export { HoverCard, HoverCardTrigger, HoverCardContent }
@@ -0,0 +1,250 @@
1
+ 'use client';
2
+
3
+ import React, { useEffect } from 'react';
4
+ import { useMotionPreference } from '../../hooks';
5
+
6
+ export interface ModalProps {
7
+ /**
8
+ * Whether the modal is open
9
+ */
10
+ isOpen: boolean;
11
+
12
+ /**
13
+ * Callback fired when the modal should close
14
+ */
15
+ onClose: () => void;
16
+
17
+ /**
18
+ * Modal title (optional)
19
+ */
20
+ title?: string;
21
+
22
+ /**
23
+ * Modal content
24
+ */
25
+ children: React.ReactNode;
26
+
27
+ /**
28
+ * Footer content (typically buttons)
29
+ */
30
+ footer?: React.ReactNode;
31
+
32
+ /**
33
+ * Size of the modal
34
+ * @default 'md'
35
+ */
36
+ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
37
+
38
+ /**
39
+ * Whether clicking the overlay closes the modal
40
+ * @default true
41
+ */
42
+ closeOnOverlayClick?: boolean;
43
+
44
+ /**
45
+ * Whether pressing Escape closes the modal
46
+ * @default true
47
+ */
48
+ closeOnEscape?: boolean;
49
+
50
+ /**
51
+ * Additional CSS classes for the modal content
52
+ */
53
+ className?: string;
54
+ }
55
+
56
+ /**
57
+ * Modal Component
58
+ *
59
+ * A dialog overlay for focused user interactions.
60
+ *
61
+ * Features:
62
+ * - Keyboard accessible (Escape to close, focus trap)
63
+ * - Click outside to close (optional)
64
+ * - Multiple size variants
65
+ * - Smooth animations
66
+ * - Blocks body scroll when open
67
+ * - Theme-aware styling
68
+ *
69
+ * Example:
70
+ * ```tsx
71
+ * const [isOpen, setIsOpen] = useState(false);
72
+ *
73
+ * <Modal
74
+ * isOpen={isOpen}
75
+ * onClose={() => setIsOpen(false)}
76
+ * title="Confirm Action"
77
+ * footer={
78
+ * <>
79
+ * <Button variant="ghost" onClick={() => setIsOpen(false)}>Cancel</Button>
80
+ * <Button onClick={handleConfirm}>Confirm</Button>
81
+ * </>
82
+ * }
83
+ * >
84
+ * <p>Are you sure you want to continue?</p>
85
+ * </Modal>
86
+ * ```
87
+ */
88
+ export const Modal: React.FC<ModalProps> = ({
89
+ isOpen,
90
+ onClose,
91
+ title,
92
+ children,
93
+ footer,
94
+ size = 'md',
95
+ closeOnOverlayClick = true,
96
+ closeOnEscape = true,
97
+ className = '',
98
+ }) => {
99
+ const { shouldAnimate, scale } = useMotionPreference();
100
+
101
+ const animationDuration = shouldAnimate && scale > 0 ? `${0.2 * (5 / scale)}s` : '0s';
102
+
103
+ // Lock body scroll when modal is open
104
+ useEffect(() => {
105
+ if (isOpen) {
106
+ document.body.style.overflow = 'hidden';
107
+ } else {
108
+ document.body.style.overflow = '';
109
+ }
110
+ return () => {
111
+ document.body.style.overflow = '';
112
+ };
113
+ }, [isOpen]);
114
+
115
+ // Handle escape key
116
+ useEffect(() => {
117
+ if (!isOpen || !closeOnEscape) return;
118
+
119
+ const handleEscape = (e: KeyboardEvent) => {
120
+ if (e.key === 'Escape') {
121
+ onClose();
122
+ }
123
+ };
124
+
125
+ document.addEventListener('keydown', handleEscape);
126
+ return () => document.removeEventListener('keydown', handleEscape);
127
+ }, [isOpen, closeOnEscape, onClose]);
128
+
129
+ if (!isOpen) return null;
130
+
131
+ const sizeClasses = {
132
+ sm: 'max-w-md',
133
+ md: 'max-w-lg',
134
+ lg: 'max-w-2xl',
135
+ xl: 'max-w-4xl',
136
+ full: 'max-w-[95vw] max-h-[95vh]',
137
+ };
138
+
139
+ const handleOverlayClick = (e: React.MouseEvent<HTMLDivElement>) => {
140
+ if (closeOnOverlayClick && e.target === e.currentTarget) {
141
+ onClose();
142
+ }
143
+ };
144
+
145
+ return (
146
+ <div
147
+ className={`
148
+ fixed inset-0 z-[9999] flex items-center justify-center p-4
149
+ bg-black/50 backdrop-blur-xs
150
+ ${shouldAnimate ? 'animate-fade-in' : ''}
151
+ `}
152
+ style={{ animationDuration }}
153
+ onClick={handleOverlayClick}
154
+ role="dialog"
155
+ aria-modal="true"
156
+ aria-labelledby={title ? 'modal-title' : undefined}
157
+ >
158
+ <div
159
+ className={`
160
+ ${sizeClasses[size]}
161
+ w-full
162
+ bg-[var(--color-surface)]
163
+ rounded-2xl
164
+ shadow-2xl
165
+ border border-[var(--color-border)]
166
+ flex flex-col
167
+ max-h-[90vh]
168
+ ${shouldAnimate ? 'animate-scale-in' : ''}
169
+ ${className}
170
+ `}
171
+ style={{ animationDuration }}
172
+ >
173
+ {/* Header */}
174
+ {title && (
175
+ <div className="flex items-center justify-between px-6 py-4 border-b border-[var(--color-border)]">
176
+ <h2
177
+ id="modal-title"
178
+ className="text-xl font-semibold text-[var(--color-text-primary)]"
179
+ >
180
+ {title}
181
+ </h2>
182
+ <button
183
+ onClick={onClose}
184
+ className="
185
+ p-2 rounded-lg
186
+ text-[var(--color-text-primary)]
187
+ hover:bg-[var(--color-hover)]
188
+ transition-colors
189
+ focus-visible:outline-none
190
+ focus-visible:ring-2
191
+ focus-visible:ring-[var(--color-focus)]
192
+ "
193
+ aria-label="Close modal"
194
+ >
195
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
196
+ <line x1="18" y1="6" x2="6" y2="18" />
197
+ <line x1="6" y1="6" x2="18" y2="18" />
198
+ </svg>
199
+ </button>
200
+ </div>
201
+ )}
202
+
203
+ {/* Content */}
204
+ <div className="flex-1 overflow-y-auto px-6 py-4">
205
+ {children}
206
+ </div>
207
+
208
+ {/* Footer */}
209
+ {footer && (
210
+ <div className="flex items-center justify-end gap-3 px-6 py-4 border-t border-[var(--color-border)]">
211
+ {footer}
212
+ </div>
213
+ )}
214
+ </div>
215
+ </div>
216
+ );
217
+ };
218
+
219
+ Modal.displayName = 'Modal';
220
+
221
+ // Add animation keyframes
222
+ if (typeof document !== 'undefined') {
223
+ const style = document.createElement('style');
224
+ style.textContent = `
225
+ @keyframes fade-in {
226
+ from { opacity: 0; }
227
+ to { opacity: 1; }
228
+ }
229
+ @keyframes scale-in {
230
+ from {
231
+ opacity: 0;
232
+ transform: scale(0.95);
233
+ }
234
+ to {
235
+ opacity: 1;
236
+ transform: scale(1);
237
+ }
238
+ }
239
+ .animate-fade-in {
240
+ animation: fade-in 0.2s ease-out;
241
+ }
242
+ .animate-scale-in {
243
+ animation: scale-in 0.2s ease-out;
244
+ }
245
+ `;
246
+ if (!document.querySelector('style[data-modal-animations]')) {
247
+ style.setAttribute('data-modal-animations', 'true');
248
+ document.head.appendChild(style);
249
+ }
250
+ }