@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.
- package/README.md +78 -0
- package/bin/arkcn.mjs +78 -0
- package/cli/commands.mjs +251 -0
- package/cli/project.mjs +168 -0
- package/cli/registry.mjs +50 -0
- package/cli/ui.mjs +46 -0
- package/mcp/server.mjs +149 -0
- package/package.json +201 -0
- package/registry/base.css +680 -0
- package/registry/index.json +1833 -0
- package/registry/items/accordion.json +18 -0
- package/registry/items/activity-feed.json +18 -0
- package/registry/items/alert-dialog.json +18 -0
- package/registry/items/alert.json +17 -0
- package/registry/items/angle-slider.json +17 -0
- package/registry/items/app-shell.json +28 -0
- package/registry/items/aspect-ratio.json +13 -0
- package/registry/items/attachment.json +19 -0
- package/registry/items/avatar.json +17 -0
- package/registry/items/badge.json +18 -0
- package/registry/items/breadcrumb.json +18 -0
- package/registry/items/bubble.json +18 -0
- package/registry/items/button-group.json +19 -0
- package/registry/items/button.json +18 -0
- package/registry/items/calendar.json +19 -0
- package/registry/items/canvas.json +24 -0
- package/registry/items/card.json +15 -0
- package/registry/items/carousel.json +19 -0
- package/registry/items/cascader.json +23 -0
- package/registry/items/chart.json +17 -0
- package/registry/items/chat.json +23 -0
- package/registry/items/checkbox.json +18 -0
- package/registry/items/clipboard.json +19 -0
- package/registry/items/collapsible.json +15 -0
- package/registry/items/color-picker.json +19 -0
- package/registry/items/combobox.json +20 -0
- package/registry/items/command.json +21 -0
- package/registry/items/comment-thread.json +25 -0
- package/registry/items/context-menu.json +18 -0
- package/registry/items/controllable.json +14 -0
- package/registry/items/data-grid.json +24 -0
- package/registry/items/data-table-adapter.json +14 -0
- package/registry/items/data-table.json +32 -0
- package/registry/items/date-input.json +17 -0
- package/registry/items/dialog.json +19 -0
- package/registry/items/direction.json +13 -0
- package/registry/items/drag-preview.json +16 -0
- package/registry/items/drawer.json +18 -0
- package/registry/items/dropdown-menu.json +18 -0
- package/registry/items/editable.json +17 -0
- package/registry/items/empty.json +17 -0
- package/registry/items/field.json +19 -0
- package/registry/items/file-upload.json +19 -0
- package/registry/items/floating-panel.json +19 -0
- package/registry/items/floating-toolbar.json +16 -0
- package/registry/items/form.json +14 -0
- package/registry/items/format.json +15 -0
- package/registry/items/gantt.json +22 -0
- package/registry/items/history.json +14 -0
- package/registry/items/hotkeys.json +18 -0
- package/registry/items/hover-card.json +17 -0
- package/registry/items/image-cropper.json +17 -0
- package/registry/items/input-group.json +20 -0
- package/registry/items/input-otp.json +18 -0
- package/registry/items/input.json +15 -0
- package/registry/items/item.json +19 -0
- package/registry/items/json-tree-view.json +17 -0
- package/registry/items/kanban.json +26 -0
- package/registry/items/kbd.json +15 -0
- package/registry/items/label.json +15 -0
- package/registry/items/live-region.json +16 -0
- package/registry/items/marker.json +18 -0
- package/registry/items/marquee.json +17 -0
- package/registry/items/menubar.json +19 -0
- package/registry/items/message.json +15 -0
- package/registry/items/navigation-menu.json +19 -0
- package/registry/items/node-graph.json +22 -0
- package/registry/items/number-input.json +18 -0
- package/registry/items/pagination.json +18 -0
- package/registry/items/password-input.json +18 -0
- package/registry/items/popconfirm.json +20 -0
- package/registry/items/popover.json +18 -0
- package/registry/items/progress.json +17 -0
- package/registry/items/qr-code.json +17 -0
- package/registry/items/query-builder.json +23 -0
- package/registry/items/radio-group.json +17 -0
- package/registry/items/rating-group.json +18 -0
- package/registry/items/resizable.json +17 -0
- package/registry/items/rich-text-editor.json +37 -0
- package/registry/items/row-layout.json +15 -0
- package/registry/items/scheduler.json +23 -0
- package/registry/items/scroll-area.json +17 -0
- package/registry/items/segment-group.json +17 -0
- package/registry/items/select.json +19 -0
- package/registry/items/separator.json +15 -0
- package/registry/items/sheet.json +19 -0
- package/registry/items/sidebar.json +27 -0
- package/registry/items/signature-pad.json +19 -0
- package/registry/items/skeleton.json +15 -0
- package/registry/items/slider.json +17 -0
- package/registry/items/sonner.json +17 -0
- package/registry/items/spinner.json +17 -0
- package/registry/items/steps.json +19 -0
- package/registry/items/swap.json +17 -0
- package/registry/items/switch.json +17 -0
- package/registry/items/table.json +16 -0
- package/registry/items/tabs.json +18 -0
- package/registry/items/tags-input.json +18 -0
- package/registry/items/textarea.json +15 -0
- package/registry/items/time.json +14 -0
- package/registry/items/timer.json +18 -0
- package/registry/items/toc.json +17 -0
- package/registry/items/toggle-group.json +19 -0
- package/registry/items/toggle.json +18 -0
- package/registry/items/tooltip.json +17 -0
- package/registry/items/tour.json +19 -0
- package/registry/items/transfer-list.json +24 -0
- package/registry/items/tree-select.json +24 -0
- package/registry/items/tree-view.json +18 -0
- package/registry/items/use-mobile.json +13 -0
- package/registry/items/utils.json +17 -0
- package/registry/items/virtual-list.json +16 -0
- package/src/components/ui/accordion.tsx +111 -0
- package/src/components/ui/activity-feed.tsx +228 -0
- package/src/components/ui/alert-dialog.tsx +204 -0
- package/src/components/ui/alert.tsx +55 -0
- package/src/components/ui/angle-slider.tsx +117 -0
- package/src/components/ui/app-shell.tsx +985 -0
- package/src/components/ui/aspect-ratio.tsx +35 -0
- package/src/components/ui/attachment.tsx +193 -0
- package/src/components/ui/avatar.tsx +94 -0
- package/src/components/ui/badge.tsx +45 -0
- package/src/components/ui/breadcrumb.tsx +93 -0
- package/src/components/ui/bubble.tsx +119 -0
- package/src/components/ui/button-group.tsx +79 -0
- package/src/components/ui/button.tsx +65 -0
- package/src/components/ui/calendar.tsx +172 -0
- package/src/components/ui/canvas.tsx +723 -0
- package/src/components/ui/card.tsx +69 -0
- package/src/components/ui/carousel.tsx +228 -0
- package/src/components/ui/cascader.tsx +754 -0
- package/src/components/ui/chart.tsx +303 -0
- package/src/components/ui/chat.tsx +544 -0
- package/src/components/ui/checkbox.tsx +94 -0
- package/src/components/ui/clipboard.tsx +94 -0
- package/src/components/ui/collapsible.tsx +32 -0
- package/src/components/ui/color-picker.tsx +441 -0
- package/src/components/ui/combobox.tsx +400 -0
- package/src/components/ui/command.tsx +292 -0
- package/src/components/ui/comment-thread.tsx +923 -0
- package/src/components/ui/context-menu.tsx +331 -0
- package/src/components/ui/data-grid.tsx +1227 -0
- package/src/components/ui/data-table.tsx +993 -0
- package/src/components/ui/date-input.tsx +103 -0
- package/src/components/ui/dialog.tsx +159 -0
- package/src/components/ui/direction.tsx +32 -0
- package/src/components/ui/drawer.tsx +200 -0
- package/src/components/ui/dropdown-menu.tsx +329 -0
- package/src/components/ui/editable.tsx +90 -0
- package/src/components/ui/empty.tsx +86 -0
- package/src/components/ui/field.tsx +218 -0
- package/src/components/ui/file-upload.tsx +204 -0
- package/src/components/ui/floating-panel.tsx +199 -0
- package/src/components/ui/floating-toolbar.tsx +79 -0
- package/src/components/ui/format.tsx +22 -0
- package/src/components/ui/gantt.tsx +950 -0
- package/src/components/ui/hotkeys.tsx +340 -0
- package/src/components/ui/hover-card.tsx +83 -0
- package/src/components/ui/image-cropper.tsx +100 -0
- package/src/components/ui/input-group.tsx +133 -0
- package/src/components/ui/input-otp.tsx +82 -0
- package/src/components/ui/input.tsx +18 -0
- package/src/components/ui/item.tsx +174 -0
- package/src/components/ui/json-tree-view.tsx +37 -0
- package/src/components/ui/kanban.tsx +884 -0
- package/src/components/ui/kbd.tsx +20 -0
- package/src/components/ui/label.tsx +30 -0
- package/src/components/ui/live-region.tsx +54 -0
- package/src/components/ui/marker.tsx +66 -0
- package/src/components/ui/marquee.tsx +61 -0
- package/src/components/ui/menubar.tsx +456 -0
- package/src/components/ui/message.tsx +74 -0
- package/src/components/ui/navigation-menu.tsx +202 -0
- package/src/components/ui/node-graph.tsx +1723 -0
- package/src/components/ui/number-input.tsx +148 -0
- package/src/components/ui/pagination.tsx +88 -0
- package/src/components/ui/password-input.tsx +104 -0
- package/src/components/ui/popconfirm.tsx +149 -0
- package/src/components/ui/popover.tsx +126 -0
- package/src/components/ui/progress.tsx +114 -0
- package/src/components/ui/qr-code.tsx +51 -0
- package/src/components/ui/query-builder.tsx +938 -0
- package/src/components/ui/radio-group.tsx +108 -0
- package/src/components/ui/rating-group.tsx +91 -0
- package/src/components/ui/resizable.tsx +41 -0
- package/src/components/ui/rich-text-editor.tsx +845 -0
- package/src/components/ui/scheduler.tsx +1164 -0
- package/src/components/ui/scroll-area.tsx +82 -0
- package/src/components/ui/segment-group.tsx +95 -0
- package/src/components/ui/select.tsx +354 -0
- package/src/components/ui/separator.tsx +32 -0
- package/src/components/ui/sheet.tsx +130 -0
- package/src/components/ui/sidebar.tsx +665 -0
- package/src/components/ui/signature-pad.tsx +112 -0
- package/src/components/ui/skeleton.tsx +7 -0
- package/src/components/ui/slider.tsx +183 -0
- package/src/components/ui/sonner.tsx +37 -0
- package/src/components/ui/spinner.tsx +16 -0
- package/src/components/ui/steps.tsx +152 -0
- package/src/components/ui/swap.tsx +33 -0
- package/src/components/ui/switch.tsx +83 -0
- package/src/components/ui/table.tsx +72 -0
- package/src/components/ui/tabs.tsx +100 -0
- package/src/components/ui/tags-input.tsx +153 -0
- package/src/components/ui/textarea.tsx +17 -0
- package/src/components/ui/timer.tsx +67 -0
- package/src/components/ui/toc.tsx +64 -0
- package/src/components/ui/toggle-group.tsx +89 -0
- package/src/components/ui/toggle.tsx +46 -0
- package/src/components/ui/tooltip.tsx +113 -0
- package/src/components/ui/tour.tsx +168 -0
- package/src/components/ui/transfer-list.tsx +700 -0
- package/src/components/ui/tree-select.tsx +638 -0
- package/src/components/ui/tree-view.tsx +217 -0
- package/src/components/ui/virtual-list.tsx +343 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/lib/controllable.ts +31 -0
- package/src/lib/data-table-adapter.ts +67 -0
- package/src/lib/drag-preview.ts +39 -0
- package/src/lib/form.ts +39 -0
- package/src/lib/history.ts +55 -0
- package/src/lib/row-layout.ts +81 -0
- package/src/lib/table.ts +483 -0
- package/src/lib/time.ts +37 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles/arkcn.css +764 -0
- 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 }
|