@multicomma/arkcn 0.1.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 (237) hide show
  1. package/README.md +78 -0
  2. package/bin/arkcn.mjs +78 -0
  3. package/cli/commands.mjs +251 -0
  4. package/cli/project.mjs +168 -0
  5. package/cli/registry.mjs +50 -0
  6. package/cli/ui.mjs +46 -0
  7. package/mcp/server.mjs +149 -0
  8. package/package.json +201 -0
  9. package/registry/base.css +680 -0
  10. package/registry/index.json +1833 -0
  11. package/registry/items/accordion.json +18 -0
  12. package/registry/items/activity-feed.json +18 -0
  13. package/registry/items/alert-dialog.json +18 -0
  14. package/registry/items/alert.json +17 -0
  15. package/registry/items/angle-slider.json +17 -0
  16. package/registry/items/app-shell.json +28 -0
  17. package/registry/items/aspect-ratio.json +13 -0
  18. package/registry/items/attachment.json +19 -0
  19. package/registry/items/avatar.json +17 -0
  20. package/registry/items/badge.json +18 -0
  21. package/registry/items/breadcrumb.json +18 -0
  22. package/registry/items/bubble.json +18 -0
  23. package/registry/items/button-group.json +19 -0
  24. package/registry/items/button.json +18 -0
  25. package/registry/items/calendar.json +19 -0
  26. package/registry/items/canvas.json +24 -0
  27. package/registry/items/card.json +15 -0
  28. package/registry/items/carousel.json +19 -0
  29. package/registry/items/cascader.json +23 -0
  30. package/registry/items/chart.json +17 -0
  31. package/registry/items/chat.json +23 -0
  32. package/registry/items/checkbox.json +18 -0
  33. package/registry/items/clipboard.json +19 -0
  34. package/registry/items/collapsible.json +15 -0
  35. package/registry/items/color-picker.json +19 -0
  36. package/registry/items/combobox.json +20 -0
  37. package/registry/items/command.json +21 -0
  38. package/registry/items/comment-thread.json +25 -0
  39. package/registry/items/context-menu.json +18 -0
  40. package/registry/items/controllable.json +14 -0
  41. package/registry/items/data-grid.json +24 -0
  42. package/registry/items/data-table-adapter.json +14 -0
  43. package/registry/items/data-table.json +32 -0
  44. package/registry/items/date-input.json +17 -0
  45. package/registry/items/dialog.json +19 -0
  46. package/registry/items/direction.json +13 -0
  47. package/registry/items/drag-preview.json +16 -0
  48. package/registry/items/drawer.json +18 -0
  49. package/registry/items/dropdown-menu.json +18 -0
  50. package/registry/items/editable.json +17 -0
  51. package/registry/items/empty.json +17 -0
  52. package/registry/items/field.json +19 -0
  53. package/registry/items/file-upload.json +19 -0
  54. package/registry/items/floating-panel.json +19 -0
  55. package/registry/items/floating-toolbar.json +16 -0
  56. package/registry/items/form.json +14 -0
  57. package/registry/items/format.json +15 -0
  58. package/registry/items/gantt.json +22 -0
  59. package/registry/items/history.json +14 -0
  60. package/registry/items/hotkeys.json +18 -0
  61. package/registry/items/hover-card.json +17 -0
  62. package/registry/items/image-cropper.json +17 -0
  63. package/registry/items/input-group.json +20 -0
  64. package/registry/items/input-otp.json +18 -0
  65. package/registry/items/input.json +15 -0
  66. package/registry/items/item.json +19 -0
  67. package/registry/items/json-tree-view.json +17 -0
  68. package/registry/items/kanban.json +26 -0
  69. package/registry/items/kbd.json +15 -0
  70. package/registry/items/label.json +15 -0
  71. package/registry/items/live-region.json +16 -0
  72. package/registry/items/marker.json +18 -0
  73. package/registry/items/marquee.json +17 -0
  74. package/registry/items/menubar.json +19 -0
  75. package/registry/items/message.json +15 -0
  76. package/registry/items/navigation-menu.json +19 -0
  77. package/registry/items/node-graph.json +22 -0
  78. package/registry/items/number-input.json +18 -0
  79. package/registry/items/pagination.json +18 -0
  80. package/registry/items/password-input.json +18 -0
  81. package/registry/items/popconfirm.json +20 -0
  82. package/registry/items/popover.json +18 -0
  83. package/registry/items/progress.json +17 -0
  84. package/registry/items/qr-code.json +17 -0
  85. package/registry/items/query-builder.json +23 -0
  86. package/registry/items/radio-group.json +17 -0
  87. package/registry/items/rating-group.json +18 -0
  88. package/registry/items/resizable.json +17 -0
  89. package/registry/items/rich-text-editor.json +37 -0
  90. package/registry/items/row-layout.json +15 -0
  91. package/registry/items/scheduler.json +23 -0
  92. package/registry/items/scroll-area.json +17 -0
  93. package/registry/items/segment-group.json +17 -0
  94. package/registry/items/select.json +19 -0
  95. package/registry/items/separator.json +15 -0
  96. package/registry/items/sheet.json +19 -0
  97. package/registry/items/sidebar.json +27 -0
  98. package/registry/items/signature-pad.json +19 -0
  99. package/registry/items/skeleton.json +15 -0
  100. package/registry/items/slider.json +17 -0
  101. package/registry/items/sonner.json +17 -0
  102. package/registry/items/spinner.json +17 -0
  103. package/registry/items/steps.json +19 -0
  104. package/registry/items/swap.json +17 -0
  105. package/registry/items/switch.json +17 -0
  106. package/registry/items/table.json +16 -0
  107. package/registry/items/tabs.json +18 -0
  108. package/registry/items/tags-input.json +18 -0
  109. package/registry/items/textarea.json +15 -0
  110. package/registry/items/time.json +14 -0
  111. package/registry/items/timer.json +18 -0
  112. package/registry/items/toc.json +17 -0
  113. package/registry/items/toggle-group.json +19 -0
  114. package/registry/items/toggle.json +18 -0
  115. package/registry/items/tooltip.json +17 -0
  116. package/registry/items/tour.json +19 -0
  117. package/registry/items/transfer-list.json +24 -0
  118. package/registry/items/tree-select.json +24 -0
  119. package/registry/items/tree-view.json +18 -0
  120. package/registry/items/use-mobile.json +13 -0
  121. package/registry/items/utils.json +17 -0
  122. package/registry/items/virtual-list.json +16 -0
  123. package/src/components/ui/accordion.tsx +111 -0
  124. package/src/components/ui/activity-feed.tsx +228 -0
  125. package/src/components/ui/alert-dialog.tsx +204 -0
  126. package/src/components/ui/alert.tsx +55 -0
  127. package/src/components/ui/angle-slider.tsx +117 -0
  128. package/src/components/ui/app-shell.tsx +985 -0
  129. package/src/components/ui/aspect-ratio.tsx +35 -0
  130. package/src/components/ui/attachment.tsx +193 -0
  131. package/src/components/ui/avatar.tsx +94 -0
  132. package/src/components/ui/badge.tsx +45 -0
  133. package/src/components/ui/breadcrumb.tsx +93 -0
  134. package/src/components/ui/bubble.tsx +119 -0
  135. package/src/components/ui/button-group.tsx +79 -0
  136. package/src/components/ui/button.tsx +65 -0
  137. package/src/components/ui/calendar.tsx +172 -0
  138. package/src/components/ui/canvas.tsx +723 -0
  139. package/src/components/ui/card.tsx +69 -0
  140. package/src/components/ui/carousel.tsx +228 -0
  141. package/src/components/ui/cascader.tsx +754 -0
  142. package/src/components/ui/chart.tsx +303 -0
  143. package/src/components/ui/chat.tsx +544 -0
  144. package/src/components/ui/checkbox.tsx +94 -0
  145. package/src/components/ui/clipboard.tsx +94 -0
  146. package/src/components/ui/collapsible.tsx +32 -0
  147. package/src/components/ui/color-picker.tsx +441 -0
  148. package/src/components/ui/combobox.tsx +400 -0
  149. package/src/components/ui/command.tsx +292 -0
  150. package/src/components/ui/comment-thread.tsx +923 -0
  151. package/src/components/ui/context-menu.tsx +331 -0
  152. package/src/components/ui/data-grid.tsx +1227 -0
  153. package/src/components/ui/data-table.tsx +993 -0
  154. package/src/components/ui/date-input.tsx +103 -0
  155. package/src/components/ui/dialog.tsx +159 -0
  156. package/src/components/ui/direction.tsx +32 -0
  157. package/src/components/ui/drawer.tsx +200 -0
  158. package/src/components/ui/dropdown-menu.tsx +329 -0
  159. package/src/components/ui/editable.tsx +90 -0
  160. package/src/components/ui/empty.tsx +86 -0
  161. package/src/components/ui/field.tsx +218 -0
  162. package/src/components/ui/file-upload.tsx +204 -0
  163. package/src/components/ui/floating-panel.tsx +199 -0
  164. package/src/components/ui/floating-toolbar.tsx +79 -0
  165. package/src/components/ui/format.tsx +22 -0
  166. package/src/components/ui/gantt.tsx +950 -0
  167. package/src/components/ui/hotkeys.tsx +340 -0
  168. package/src/components/ui/hover-card.tsx +83 -0
  169. package/src/components/ui/image-cropper.tsx +100 -0
  170. package/src/components/ui/input-group.tsx +133 -0
  171. package/src/components/ui/input-otp.tsx +82 -0
  172. package/src/components/ui/input.tsx +18 -0
  173. package/src/components/ui/item.tsx +174 -0
  174. package/src/components/ui/json-tree-view.tsx +37 -0
  175. package/src/components/ui/kanban.tsx +884 -0
  176. package/src/components/ui/kbd.tsx +20 -0
  177. package/src/components/ui/label.tsx +30 -0
  178. package/src/components/ui/live-region.tsx +54 -0
  179. package/src/components/ui/marker.tsx +66 -0
  180. package/src/components/ui/marquee.tsx +61 -0
  181. package/src/components/ui/menubar.tsx +456 -0
  182. package/src/components/ui/message.tsx +74 -0
  183. package/src/components/ui/navigation-menu.tsx +202 -0
  184. package/src/components/ui/node-graph.tsx +1723 -0
  185. package/src/components/ui/number-input.tsx +148 -0
  186. package/src/components/ui/pagination.tsx +88 -0
  187. package/src/components/ui/password-input.tsx +104 -0
  188. package/src/components/ui/popconfirm.tsx +149 -0
  189. package/src/components/ui/popover.tsx +126 -0
  190. package/src/components/ui/progress.tsx +114 -0
  191. package/src/components/ui/qr-code.tsx +51 -0
  192. package/src/components/ui/query-builder.tsx +938 -0
  193. package/src/components/ui/radio-group.tsx +108 -0
  194. package/src/components/ui/rating-group.tsx +91 -0
  195. package/src/components/ui/resizable.tsx +41 -0
  196. package/src/components/ui/rich-text-editor.tsx +845 -0
  197. package/src/components/ui/scheduler.tsx +1164 -0
  198. package/src/components/ui/scroll-area.tsx +82 -0
  199. package/src/components/ui/segment-group.tsx +95 -0
  200. package/src/components/ui/select.tsx +354 -0
  201. package/src/components/ui/separator.tsx +32 -0
  202. package/src/components/ui/sheet.tsx +130 -0
  203. package/src/components/ui/sidebar.tsx +665 -0
  204. package/src/components/ui/signature-pad.tsx +112 -0
  205. package/src/components/ui/skeleton.tsx +7 -0
  206. package/src/components/ui/slider.tsx +183 -0
  207. package/src/components/ui/sonner.tsx +37 -0
  208. package/src/components/ui/spinner.tsx +16 -0
  209. package/src/components/ui/steps.tsx +152 -0
  210. package/src/components/ui/swap.tsx +33 -0
  211. package/src/components/ui/switch.tsx +83 -0
  212. package/src/components/ui/table.tsx +72 -0
  213. package/src/components/ui/tabs.tsx +100 -0
  214. package/src/components/ui/tags-input.tsx +153 -0
  215. package/src/components/ui/textarea.tsx +17 -0
  216. package/src/components/ui/timer.tsx +67 -0
  217. package/src/components/ui/toc.tsx +64 -0
  218. package/src/components/ui/toggle-group.tsx +89 -0
  219. package/src/components/ui/toggle.tsx +46 -0
  220. package/src/components/ui/tooltip.tsx +113 -0
  221. package/src/components/ui/tour.tsx +168 -0
  222. package/src/components/ui/transfer-list.tsx +700 -0
  223. package/src/components/ui/tree-select.tsx +638 -0
  224. package/src/components/ui/tree-view.tsx +217 -0
  225. package/src/components/ui/virtual-list.tsx +343 -0
  226. package/src/hooks/use-mobile.ts +19 -0
  227. package/src/lib/controllable.ts +31 -0
  228. package/src/lib/data-table-adapter.ts +67 -0
  229. package/src/lib/drag-preview.ts +39 -0
  230. package/src/lib/form.ts +39 -0
  231. package/src/lib/history.ts +55 -0
  232. package/src/lib/row-layout.ts +81 -0
  233. package/src/lib/table.ts +483 -0
  234. package/src/lib/time.ts +37 -0
  235. package/src/lib/utils.ts +6 -0
  236. package/src/styles/arkcn.css +764 -0
  237. package/src/styles/index.css +3 -0
@@ -0,0 +1,218 @@
1
+ "use client"
2
+
3
+ import { useMemo } from "react"
4
+ import { cva, type VariantProps } from "class-variance-authority"
5
+ import { cn } from "@/lib/utils"
6
+
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-4 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("mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base", className)}
33
+ {...props}
34
+ />
35
+ )
36
+ }
37
+
38
+ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
39
+ return (
40
+ <div
41
+ data-slot="field-group"
42
+ className={cn(
43
+ "group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
44
+ className
45
+ )}
46
+ {...props}
47
+ />
48
+ )
49
+ }
50
+
51
+ const fieldVariants = cva("group/field flex w-full gap-2 data-[invalid=true]:text-destructive", {
52
+ variants: {
53
+ orientation: {
54
+ vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
55
+ horizontal:
56
+ "flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
57
+ responsive:
58
+ "flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
59
+ },
60
+ },
61
+ defaultVariants: {
62
+ orientation: "vertical",
63
+ },
64
+ })
65
+
66
+ function Field({
67
+ className,
68
+ orientation = "vertical",
69
+ ...props
70
+ }: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
71
+ return (
72
+ <div
73
+ role="group"
74
+ data-slot="field"
75
+ data-orientation={orientation}
76
+ className={cn(fieldVariants({ orientation }), className)}
77
+ {...props}
78
+ />
79
+ )
80
+ }
81
+
82
+ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
83
+ return (
84
+ <div
85
+ data-slot="field-content"
86
+ className={cn("group/field-content flex flex-1 flex-col gap-0.5 leading-snug", className)}
87
+ {...props}
88
+ />
89
+ )
90
+ }
91
+
92
+ function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
93
+ return (
94
+ <Label
95
+ data-slot="field-label"
96
+ className={cn(
97
+ "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-muted/50 has-[>[data-slot=field]]:has-focus-visible:border-ring has-[>[data-slot=field]]:has-focus-visible:ring-3 has-[>[data-slot=field]]:has-focus-visible:ring-ring/50 *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10",
98
+ "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
99
+ className
100
+ )}
101
+ {...props}
102
+ />
103
+ )
104
+ }
105
+
106
+ function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
107
+ return (
108
+ <div
109
+ data-slot="field-label"
110
+ className={cn(
111
+ "flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50",
112
+ className
113
+ )}
114
+ {...props}
115
+ />
116
+ )
117
+ }
118
+
119
+ function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
120
+ return (
121
+ <p
122
+ data-slot="field-description"
123
+ className={cn(
124
+ "text-left text-sm/normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5",
125
+ "last:mt-0 nth-last-2:-mt-1",
126
+ "[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
127
+ className
128
+ )}
129
+ {...props}
130
+ />
131
+ )
132
+ }
133
+
134
+ function FieldSeparator({
135
+ children,
136
+ className,
137
+ ...props
138
+ }: React.ComponentProps<"div"> & {
139
+ children?: React.ReactNode
140
+ }) {
141
+ return (
142
+ <div
143
+ data-slot="field-separator"
144
+ data-content={!!children}
145
+ className={cn("relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2", className)}
146
+ {...props}
147
+ >
148
+ <Separator className="absolute inset-0 top-1/2" />
149
+ {children && (
150
+ <span
151
+ className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
152
+ data-slot="field-separator-content"
153
+ >
154
+ {children}
155
+ </span>
156
+ )}
157
+ </div>
158
+ )
159
+ }
160
+
161
+ function FieldError({
162
+ className,
163
+ children,
164
+ errors,
165
+ ...props
166
+ }: React.ComponentProps<"div"> & {
167
+ errors?: Array<{ message?: string } | undefined>
168
+ }) {
169
+ const content = useMemo(() => {
170
+ if (children) {
171
+ return children
172
+ }
173
+
174
+ if (!errors?.length) {
175
+ return null
176
+ }
177
+
178
+ const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()]
179
+
180
+ if (uniqueErrors?.length == 1) {
181
+ return uniqueErrors[0]?.message
182
+ }
183
+
184
+ return (
185
+ <ul className="ml-4 flex list-disc flex-col gap-1">
186
+ {uniqueErrors.map((error, index) => error?.message && <li key={index}>{error.message}</li>)}
187
+ </ul>
188
+ )
189
+ }, [children, errors])
190
+
191
+ if (!content) {
192
+ return null
193
+ }
194
+
195
+ return (
196
+ <div
197
+ role="alert"
198
+ data-slot="field-error"
199
+ className={cn("text-sm font-normal text-destructive", className)}
200
+ {...props}
201
+ >
202
+ {content}
203
+ </div>
204
+ )
205
+ }
206
+
207
+ export {
208
+ Field,
209
+ FieldLabel,
210
+ FieldDescription,
211
+ FieldError,
212
+ FieldGroup,
213
+ FieldLegend,
214
+ FieldSeparator,
215
+ FieldSet,
216
+ FieldContent,
217
+ FieldTitle,
218
+ }
@@ -0,0 +1,204 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "@/lib/utils"
5
+ import { FileUpload as FileUploadPrimitive } from "@ark-ui/react"
6
+ import { FileIcon, UploadIcon, XIcon } from "lucide-react"
7
+
8
+ import { Button } from "@/components/ui/button"
9
+
10
+ function FileUpload({ className, ...props }: React.ComponentProps<typeof FileUploadPrimitive.Root>) {
11
+ return (
12
+ <FileUploadPrimitive.Root
13
+ data-slot="file-upload"
14
+ className={cn("flex w-full flex-col gap-3", className)}
15
+ {...props}
16
+ />
17
+ )
18
+ }
19
+
20
+ function FileUploadContext({ ...props }: React.ComponentProps<typeof FileUploadPrimitive.Context>) {
21
+ return <FileUploadPrimitive.Context {...props} />
22
+ }
23
+
24
+ function FileUploadLabel({ className, ...props }: React.ComponentProps<typeof FileUploadPrimitive.Label>) {
25
+ return (
26
+ <FileUploadPrimitive.Label
27
+ data-slot="file-upload-label"
28
+ className={cn("text-sm font-medium select-none", className)}
29
+ {...props}
30
+ />
31
+ )
32
+ }
33
+
34
+ function FileUploadDropzone({
35
+ className,
36
+ children,
37
+ ...props
38
+ }: React.ComponentProps<typeof FileUploadPrimitive.Dropzone>) {
39
+ return (
40
+ <FileUploadPrimitive.Dropzone
41
+ data-slot="file-upload-dropzone"
42
+ className={cn(
43
+ "flex min-h-32 cursor-pointer flex-col items-center justify-center gap-2 rounded-xl border border-dashed border-input bg-transparent p-6 text-center text-sm text-muted-foreground transition-colors outline-none hover:bg-muted/50 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 data-dragging:border-ring data-dragging:bg-muted/50 data-invalid:border-destructive dark:bg-input/30 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-5",
44
+ className
45
+ )}
46
+ {...props}
47
+ >
48
+ {children ?? (
49
+ <>
50
+ <UploadIcon />
51
+ <span>Drag your files here or click to browse</span>
52
+ </>
53
+ )}
54
+ </FileUploadPrimitive.Dropzone>
55
+ )
56
+ }
57
+
58
+ function FileUploadTrigger({
59
+ className,
60
+ children,
61
+ ...props
62
+ }: React.ComponentProps<typeof FileUploadPrimitive.Trigger>) {
63
+ return (
64
+ <FileUploadPrimitive.Trigger data-slot="file-upload-trigger" className={cn(className)} asChild {...props}>
65
+ <Button variant="outline" size="sm">
66
+ {children}
67
+ </Button>
68
+ </FileUploadPrimitive.Trigger>
69
+ )
70
+ }
71
+
72
+ function FileUploadClearTrigger({
73
+ className,
74
+ children,
75
+ ...props
76
+ }: React.ComponentProps<typeof FileUploadPrimitive.ClearTrigger>) {
77
+ return (
78
+ <FileUploadPrimitive.ClearTrigger
79
+ data-slot="file-upload-clear-trigger"
80
+ className={cn(className)}
81
+ asChild
82
+ {...props}
83
+ >
84
+ <Button variant="ghost" size="sm">
85
+ {children}
86
+ </Button>
87
+ </FileUploadPrimitive.ClearTrigger>
88
+ )
89
+ }
90
+
91
+ function FileUploadItemGroup({ className, ...props }: React.ComponentProps<typeof FileUploadPrimitive.ItemGroup>) {
92
+ return (
93
+ <FileUploadPrimitive.ItemGroup
94
+ data-slot="file-upload-item-group"
95
+ className={cn("flex flex-col gap-2", className)}
96
+ {...props}
97
+ />
98
+ )
99
+ }
100
+
101
+ function FileUploadItem({ className, ...props }: React.ComponentProps<typeof FileUploadPrimitive.Item>) {
102
+ return (
103
+ <FileUploadPrimitive.Item
104
+ data-slot="file-upload-item"
105
+ className={cn("grid grid-cols-[auto_1fr_auto] items-center gap-x-3 rounded-lg border p-2 text-sm", className)}
106
+ {...props}
107
+ />
108
+ )
109
+ }
110
+
111
+ function FileUploadItemPreview({
112
+ className,
113
+ children,
114
+ ...props
115
+ }: React.ComponentProps<typeof FileUploadPrimitive.ItemPreview>) {
116
+ return (
117
+ <FileUploadPrimitive.ItemPreview
118
+ data-slot="file-upload-item-preview"
119
+ className={cn(
120
+ "row-span-2 flex size-10 items-center justify-center overflow-hidden rounded-md bg-muted text-muted-foreground [&_svg]:size-5",
121
+ className
122
+ )}
123
+ {...props}
124
+ >
125
+ {children ?? <FileIcon />}
126
+ </FileUploadPrimitive.ItemPreview>
127
+ )
128
+ }
129
+
130
+ function FileUploadItemPreviewImage({
131
+ className,
132
+ ...props
133
+ }: React.ComponentProps<typeof FileUploadPrimitive.ItemPreviewImage>) {
134
+ return (
135
+ <FileUploadPrimitive.ItemPreviewImage
136
+ data-slot="file-upload-item-preview-image"
137
+ className={cn("size-full object-cover", className)}
138
+ {...props}
139
+ />
140
+ )
141
+ }
142
+
143
+ function FileUploadItemName({ className, ...props }: React.ComponentProps<typeof FileUploadPrimitive.ItemName>) {
144
+ return (
145
+ <FileUploadPrimitive.ItemName
146
+ data-slot="file-upload-item-name"
147
+ className={cn("truncate font-medium", className)}
148
+ {...props}
149
+ />
150
+ )
151
+ }
152
+
153
+ function FileUploadItemSizeText({
154
+ className,
155
+ ...props
156
+ }: React.ComponentProps<typeof FileUploadPrimitive.ItemSizeText>) {
157
+ return (
158
+ <FileUploadPrimitive.ItemSizeText
159
+ data-slot="file-upload-item-size-text"
160
+ className={cn("col-start-2 text-xs text-muted-foreground", className)}
161
+ {...props}
162
+ />
163
+ )
164
+ }
165
+
166
+ function FileUploadItemDeleteTrigger({
167
+ className,
168
+ children,
169
+ ...props
170
+ }: React.ComponentProps<typeof FileUploadPrimitive.ItemDeleteTrigger>) {
171
+ return (
172
+ <FileUploadPrimitive.ItemDeleteTrigger
173
+ data-slot="file-upload-item-delete-trigger"
174
+ className={cn("row-span-2", className)}
175
+ asChild
176
+ {...props}
177
+ >
178
+ <Button variant="ghost" size="icon-sm">
179
+ {children ?? <XIcon />}
180
+ </Button>
181
+ </FileUploadPrimitive.ItemDeleteTrigger>
182
+ )
183
+ }
184
+
185
+ function FileUploadHiddenInput({ ...props }: React.ComponentProps<typeof FileUploadPrimitive.HiddenInput>) {
186
+ return <FileUploadPrimitive.HiddenInput {...props} />
187
+ }
188
+
189
+ export {
190
+ FileUpload,
191
+ FileUploadClearTrigger,
192
+ FileUploadContext,
193
+ FileUploadDropzone,
194
+ FileUploadHiddenInput,
195
+ FileUploadItem,
196
+ FileUploadItemDeleteTrigger,
197
+ FileUploadItemGroup,
198
+ FileUploadItemName,
199
+ FileUploadItemPreview,
200
+ FileUploadItemPreviewImage,
201
+ FileUploadItemSizeText,
202
+ FileUploadLabel,
203
+ FileUploadTrigger,
204
+ }
@@ -0,0 +1,199 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "@/lib/utils"
5
+ import { FloatingPanel as FloatingPanelPrimitive, Portal as PortalPrimitive } from "@ark-ui/react"
6
+ import { Maximize2Icon, Minimize2Icon, MinusIcon, XIcon } from "lucide-react"
7
+
8
+ import { Button } from "@/components/ui/button"
9
+
10
+ function FloatingPanel({
11
+ lazyMount = true,
12
+ unmountOnExit = true,
13
+ ...props
14
+ }: React.ComponentProps<typeof FloatingPanelPrimitive.Root>) {
15
+ return <FloatingPanelPrimitive.Root lazyMount={lazyMount} unmountOnExit={unmountOnExit} {...props} />
16
+ }
17
+
18
+ function FloatingPanelContext({ ...props }: React.ComponentProps<typeof FloatingPanelPrimitive.Context>) {
19
+ return <FloatingPanelPrimitive.Context {...props} />
20
+ }
21
+
22
+ function FloatingPanelTrigger({ ...props }: React.ComponentProps<typeof FloatingPanelPrimitive.Trigger>) {
23
+ return <FloatingPanelPrimitive.Trigger data-slot="floating-panel-trigger" {...props} />
24
+ }
25
+
26
+ function FloatingPanelPortal({ ...props }: React.ComponentProps<typeof PortalPrimitive>) {
27
+ return <PortalPrimitive {...props} />
28
+ }
29
+
30
+ function FloatingPanelPositioner({
31
+ className,
32
+ ...props
33
+ }: React.ComponentProps<typeof FloatingPanelPrimitive.Positioner>) {
34
+ return (
35
+ <FloatingPanelPrimitive.Positioner
36
+ data-slot="floating-panel-positioner"
37
+ className={cn("[--z-index:50]", className)}
38
+ {...props}
39
+ />
40
+ )
41
+ }
42
+
43
+ function FloatingPanelContent({ className, ...props }: React.ComponentProps<typeof FloatingPanelPrimitive.Content>) {
44
+ return (
45
+ <FloatingPanelPortal>
46
+ <FloatingPanelPositioner>
47
+ <FloatingPanelPrimitive.Content
48
+ data-slot="floating-panel-content"
49
+ className={cn(
50
+ "flex flex-col overflow-hidden rounded-xl bg-popover text-sm text-popover-foreground shadow-lg ring-1 ring-foreground/10 outline-hidden data-[stage=minimized]:h-auto data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
51
+ className
52
+ )}
53
+ {...props}
54
+ />
55
+ </FloatingPanelPositioner>
56
+ </FloatingPanelPortal>
57
+ )
58
+ }
59
+
60
+ function FloatingPanelDragTrigger({
61
+ className,
62
+ ...props
63
+ }: React.ComponentProps<typeof FloatingPanelPrimitive.DragTrigger>) {
64
+ return (
65
+ <FloatingPanelPrimitive.DragTrigger
66
+ data-slot="floating-panel-drag-trigger"
67
+ className={cn("flex-1 cursor-grab select-none data-dragging:cursor-grabbing", className)}
68
+ {...props}
69
+ />
70
+ )
71
+ }
72
+
73
+ function FloatingPanelHeader({ className, ...props }: React.ComponentProps<typeof FloatingPanelPrimitive.Header>) {
74
+ return (
75
+ <FloatingPanelPrimitive.Header
76
+ data-slot="floating-panel-header"
77
+ className={cn("flex h-9 items-center gap-1 border-b bg-muted/50 pr-1 pl-3", className)}
78
+ {...props}
79
+ />
80
+ )
81
+ }
82
+
83
+ function FloatingPanelTitle({ className, ...props }: React.ComponentProps<typeof FloatingPanelPrimitive.Title>) {
84
+ return (
85
+ <FloatingPanelPrimitive.Title
86
+ data-slot="floating-panel-title"
87
+ className={cn("font-heading text-sm font-medium", className)}
88
+ {...props}
89
+ />
90
+ )
91
+ }
92
+
93
+ function FloatingPanelControl({
94
+ className,
95
+ children,
96
+ ...props
97
+ }: React.ComponentProps<typeof FloatingPanelPrimitive.Control>) {
98
+ return (
99
+ <FloatingPanelPrimitive.Control
100
+ data-slot="floating-panel-control"
101
+ className={cn("flex items-center", className)}
102
+ {...props}
103
+ >
104
+ {children ?? (
105
+ <>
106
+ <FloatingPanelStageTrigger stage="minimized">
107
+ <MinusIcon />
108
+ </FloatingPanelStageTrigger>
109
+ <FloatingPanelStageTrigger stage="maximized">
110
+ <Maximize2Icon />
111
+ </FloatingPanelStageTrigger>
112
+ <FloatingPanelStageTrigger stage="default">
113
+ <Minimize2Icon />
114
+ </FloatingPanelStageTrigger>
115
+ <FloatingPanelCloseTrigger />
116
+ </>
117
+ )}
118
+ </FloatingPanelPrimitive.Control>
119
+ )
120
+ }
121
+
122
+ function FloatingPanelStageTrigger({
123
+ className,
124
+ children,
125
+ ...props
126
+ }: React.ComponentProps<typeof FloatingPanelPrimitive.StageTrigger>) {
127
+ return (
128
+ <FloatingPanelPrimitive.StageTrigger
129
+ data-slot="floating-panel-stage-trigger"
130
+ className={cn(className)}
131
+ asChild
132
+ {...props}
133
+ >
134
+ <Button variant="ghost" size="icon-xs">
135
+ {children}
136
+ </Button>
137
+ </FloatingPanelPrimitive.StageTrigger>
138
+ )
139
+ }
140
+
141
+ function FloatingPanelCloseTrigger({
142
+ className,
143
+ children,
144
+ ...props
145
+ }: React.ComponentProps<typeof FloatingPanelPrimitive.CloseTrigger>) {
146
+ return (
147
+ <FloatingPanelPrimitive.CloseTrigger
148
+ data-slot="floating-panel-close-trigger"
149
+ className={cn(className)}
150
+ asChild
151
+ {...props}
152
+ >
153
+ <Button variant="ghost" size="icon-xs">
154
+ {children ?? <XIcon />}
155
+ <span className="sr-only">Close</span>
156
+ </Button>
157
+ </FloatingPanelPrimitive.CloseTrigger>
158
+ )
159
+ }
160
+
161
+ function FloatingPanelBody({ className, ...props }: React.ComponentProps<typeof FloatingPanelPrimitive.Body>) {
162
+ return (
163
+ <FloatingPanelPrimitive.Body
164
+ data-slot="floating-panel-body"
165
+ className={cn("flex-1 overflow-auto p-3", className)}
166
+ {...props}
167
+ />
168
+ )
169
+ }
170
+
171
+ function FloatingPanelResizeTrigger({
172
+ className,
173
+ ...props
174
+ }: React.ComponentProps<typeof FloatingPanelPrimitive.ResizeTrigger>) {
175
+ return (
176
+ <FloatingPanelPrimitive.ResizeTrigger
177
+ data-slot="floating-panel-resize-trigger"
178
+ className={cn(className)}
179
+ {...props}
180
+ />
181
+ )
182
+ }
183
+
184
+ export {
185
+ FloatingPanel,
186
+ FloatingPanelBody,
187
+ FloatingPanelCloseTrigger,
188
+ FloatingPanelContent,
189
+ FloatingPanelContext,
190
+ FloatingPanelControl,
191
+ FloatingPanelDragTrigger,
192
+ FloatingPanelHeader,
193
+ FloatingPanelPortal,
194
+ FloatingPanelPositioner,
195
+ FloatingPanelResizeTrigger,
196
+ FloatingPanelStageTrigger,
197
+ FloatingPanelTitle,
198
+ FloatingPanelTrigger,
199
+ }
@@ -0,0 +1,79 @@
1
+ import * as React from "react"
2
+ import { cn } from "@/lib/utils"
3
+
4
+ /**
5
+ * FloatingToolbar — the snackbar-style action bar that floats over a page
6
+ * (bulk actions on tables, pending edits on grids). Arrow keys move between
7
+ * its buttons, Home/End jump, and Escape calls `onEscape` unless focus is in a
8
+ * nested menu. Renders nothing while `open` is false.
9
+ */
10
+ function FloatingToolbar({
11
+ open = true,
12
+ onEscape,
13
+ className,
14
+ children,
15
+ onKeyDown,
16
+ ...props
17
+ }: React.ComponentProps<"div"> & { open?: boolean; onEscape?: () => void }) {
18
+ const ref = React.useRef<HTMLDivElement>(null)
19
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
20
+ onKeyDown?.(event)
21
+ if (event.defaultPrevented) return
22
+ const list = Array.from(ref.current?.querySelectorAll<HTMLButtonElement>("button:not([disabled])") ?? [])
23
+ const currentIndex = list.findIndex((b) => b === document.activeElement)
24
+ switch (event.key) {
25
+ case "ArrowRight":
26
+ event.preventDefault()
27
+ list[(currentIndex + 1) % list.length]?.focus()
28
+ break
29
+ case "ArrowLeft":
30
+ event.preventDefault()
31
+ list[currentIndex <= 0 ? list.length - 1 : currentIndex - 1]?.focus()
32
+ break
33
+ case "Home":
34
+ event.preventDefault()
35
+ list[0]?.focus()
36
+ break
37
+ case "End":
38
+ event.preventDefault()
39
+ list[list.length - 1]?.focus()
40
+ break
41
+ case "Escape": {
42
+ const target = event.target as HTMLElement
43
+ const active = document.activeElement as HTMLElement | null
44
+ const inMenu = [target, active].some(
45
+ (el) =>
46
+ el?.closest('[data-slot="dropdown-menu-trigger"]') || el?.closest('[data-slot="dropdown-menu-content"]')
47
+ )
48
+ if (inMenu || !onEscape) return
49
+ event.preventDefault()
50
+ onEscape()
51
+ break
52
+ }
53
+ }
54
+ }
55
+ if (!open) return null
56
+ return (
57
+ <div
58
+ ref={ref}
59
+ role="toolbar"
60
+ data-slot="floating-toolbar"
61
+ tabIndex={-1}
62
+ onKeyDown={handleKeyDown}
63
+ className={cn(
64
+ "fixed bottom-6 left-1/2 z-50 -translate-x-1/2 rounded-xl transition-all delay-100 duration-300 ease-out hover:scale-105 focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:outline-none",
65
+ className
66
+ )}
67
+ {...props}
68
+ >
69
+ <div
70
+ data-slot="floating-toolbar-content"
71
+ className="flex items-center gap-2 rounded-xl border bg-background/95 p-2 shadow-xl backdrop-blur-lg supports-backdrop-filter:bg-background/60"
72
+ >
73
+ {children}
74
+ </div>
75
+ </div>
76
+ )
77
+ }
78
+
79
+ export { FloatingToolbar }