@questpie/admin 0.0.1 → 1.0.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.
- package/README.md +439 -424
- package/dist/auth-layout-M8K8_q5R.mjs +181 -0
- package/dist/auth-layout-M8K8_q5R.mjs.map +1 -0
- package/dist/bulk-upload-dialog-D7w7W1Hl.mjs +273 -0
- package/dist/bulk-upload-dialog-D7w7W1Hl.mjs.map +1 -0
- package/dist/{components/ui/card.mjs → card-BKHjBQfw.mjs} +8 -8
- package/dist/card-BKHjBQfw.mjs.map +1 -0
- package/dist/client/styles/index.css +434 -0
- package/dist/client-DbpZKSgH.d.mts +13585 -0
- package/dist/client-DbpZKSgH.d.mts.map +1 -0
- package/dist/client-njX1rZmi.mjs +22612 -0
- package/dist/client-njX1rZmi.mjs.map +1 -0
- package/dist/client.d.mts +3 -0
- package/dist/client.mjs +13 -0
- package/dist/content-locales-provider-BXvuIgfg.mjs +1650 -0
- package/dist/content-locales-provider-BXvuIgfg.mjs.map +1 -0
- package/dist/dashboard-page-B4PGEdc2.mjs +2500 -0
- package/dist/dashboard-page-B4PGEdc2.mjs.map +1 -0
- package/dist/dashboard-page-mCY0pgZv.mjs +3 -0
- package/dist/dropzone-Do3awXKd.mjs +634 -0
- package/dist/dropzone-Do3awXKd.mjs.map +1 -0
- package/dist/{views/auth/forgot-password-form.mjs → forgot-password-page-Bcp-An4Y.mjs} +87 -14
- package/dist/forgot-password-page-Bcp-An4Y.mjs.map +1 -0
- package/dist/forgot-password-page-CEwsdLwn.mjs +3 -0
- package/dist/index-B9Xwk4hi.d.mts +2753 -0
- package/dist/index-B9Xwk4hi.d.mts.map +1 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +13 -0
- package/dist/login-page-BUnpCbCa.mjs +3 -0
- package/dist/login-page-CP4gA-dl.mjs +298 -0
- package/dist/login-page-CP4gA-dl.mjs.map +1 -0
- package/dist/preview-utils-BKQ9-TMa.mjs +65 -0
- package/dist/preview-utils-BKQ9-TMa.mjs.map +1 -0
- package/dist/{views/auth/reset-password-form.mjs → reset-password-page-BqfDmLxA.mjs} +111 -14
- package/dist/reset-password-page-BqfDmLxA.mjs.map +1 -0
- package/dist/reset-password-page-CufHz3h3.mjs +3 -0
- package/dist/runtime-6VZM878K.mjs +69 -0
- package/dist/runtime-6VZM878K.mjs.map +1 -0
- package/dist/saved-views.types-BMsz5mCy.d.mts +42 -0
- package/dist/saved-views.types-BMsz5mCy.d.mts.map +1 -0
- package/dist/server.d.mts +250 -0
- package/dist/server.d.mts.map +1 -0
- package/dist/server.mjs +832 -0
- package/dist/server.mjs.map +1 -0
- package/dist/setup-page-BNNzt_Z6.mjs +3 -0
- package/dist/setup-page-YAP_fzqh.mjs +264 -0
- package/dist/setup-page-YAP_fzqh.mjs.map +1 -0
- package/dist/shared.d.mts +57 -0
- package/dist/shared.d.mts.map +1 -0
- package/dist/shared.mjs +3 -0
- package/dist/{hooks/use-auth.mjs → use-auth-BoLmWtmU.mjs} +42 -30
- package/dist/use-auth-BoLmWtmU.mjs.map +1 -0
- package/package.json +48 -198
- package/.turbo/turbo-build.log +0 -108
- package/CHANGELOG.md +0 -10
- package/STATUS.md +0 -917
- package/VALIDATION.md +0 -602
- package/components.json +0 -24
- package/dist/__tests__/setup.mjs +0 -38
- package/dist/__tests__/test-utils.mjs +0 -45
- package/dist/__tests__/vitest.d.mjs +0 -3
- package/dist/components/admin-app.mjs +0 -69
- package/dist/components/fields/array-field.mjs +0 -190
- package/dist/components/fields/checkbox-field.mjs +0 -34
- package/dist/components/fields/custom-field.mjs +0 -32
- package/dist/components/fields/date-field.mjs +0 -41
- package/dist/components/fields/datetime-field.mjs +0 -42
- package/dist/components/fields/email-field.mjs +0 -37
- package/dist/components/fields/embedded-collection.mjs +0 -253
- package/dist/components/fields/field-types.mjs +0 -1
- package/dist/components/fields/field-utils.mjs +0 -10
- package/dist/components/fields/field-wrapper.mjs +0 -34
- package/dist/components/fields/index.mjs +0 -23
- package/dist/components/fields/json-field.mjs +0 -243
- package/dist/components/fields/locale-badge.mjs +0 -16
- package/dist/components/fields/number-field.mjs +0 -39
- package/dist/components/fields/password-field.mjs +0 -37
- package/dist/components/fields/relation-field.mjs +0 -104
- package/dist/components/fields/relation-picker.mjs +0 -229
- package/dist/components/fields/relation-select.mjs +0 -188
- package/dist/components/fields/rich-text-editor/index.mjs +0 -897
- package/dist/components/fields/select-field.mjs +0 -41
- package/dist/components/fields/switch-field.mjs +0 -34
- package/dist/components/fields/text-field.mjs +0 -38
- package/dist/components/fields/textarea-field.mjs +0 -38
- package/dist/components/index.mjs +0 -59
- package/dist/components/primitives/checkbox-input.mjs +0 -127
- package/dist/components/primitives/date-input.mjs +0 -303
- package/dist/components/primitives/index.mjs +0 -12
- package/dist/components/primitives/number-input.mjs +0 -104
- package/dist/components/primitives/select-input.mjs +0 -177
- package/dist/components/primitives/tag-input.mjs +0 -135
- package/dist/components/primitives/text-input.mjs +0 -39
- package/dist/components/primitives/textarea-input.mjs +0 -37
- package/dist/components/primitives/toggle-input.mjs +0 -31
- package/dist/components/primitives/types.mjs +0 -12
- package/dist/components/ui/accordion.mjs +0 -55
- package/dist/components/ui/avatar.mjs +0 -54
- package/dist/components/ui/badge.mjs +0 -34
- package/dist/components/ui/button.mjs +0 -48
- package/dist/components/ui/checkbox.mjs +0 -21
- package/dist/components/ui/combobox.mjs +0 -163
- package/dist/components/ui/dialog.mjs +0 -95
- package/dist/components/ui/dropdown-menu.mjs +0 -138
- package/dist/components/ui/field.mjs +0 -113
- package/dist/components/ui/input-group.mjs +0 -82
- package/dist/components/ui/input.mjs +0 -17
- package/dist/components/ui/label.mjs +0 -15
- package/dist/components/ui/popover.mjs +0 -56
- package/dist/components/ui/scroll-area.mjs +0 -38
- package/dist/components/ui/select.mjs +0 -100
- package/dist/components/ui/separator.mjs +0 -16
- package/dist/components/ui/sheet.mjs +0 -90
- package/dist/components/ui/sidebar.mjs +0 -387
- package/dist/components/ui/skeleton.mjs +0 -14
- package/dist/components/ui/spinner.mjs +0 -16
- package/dist/components/ui/switch.mjs +0 -22
- package/dist/components/ui/table.mjs +0 -68
- package/dist/components/ui/tabs.mjs +0 -48
- package/dist/components/ui/textarea.mjs +0 -15
- package/dist/components/ui/tooltip.mjs +0 -44
- package/dist/config/component-registry.mjs +0 -38
- package/dist/config/index.mjs +0 -129
- package/dist/hooks/admin-provider.mjs +0 -70
- package/dist/hooks/index.mjs +0 -7
- package/dist/hooks/store.mjs +0 -178
- package/dist/hooks/use-collection-db.mjs +0 -146
- package/dist/hooks/use-collection.mjs +0 -112
- package/dist/hooks/use-global.mjs +0 -46
- package/dist/hooks/use-mobile.mjs +0 -20
- package/dist/lib/utils.mjs +0 -10
- package/dist/styles/index.css +0 -336
- package/dist/styles/index.mjs +0 -1
- package/dist/utils/index.mjs +0 -9
- package/dist/views/auth/auth-layout.mjs +0 -52
- package/dist/views/auth/index.mjs +0 -6
- package/dist/views/auth/login-form.mjs +0 -156
- package/dist/views/collection/auto-form-fields.mjs +0 -525
- package/dist/views/collection/collection-form.mjs +0 -91
- package/dist/views/collection/collection-list.mjs +0 -76
- package/dist/views/collection/form-field.mjs +0 -42
- package/dist/views/collection/index.mjs +0 -6
- package/dist/views/common/index.mjs +0 -4
- package/dist/views/common/locale-switcher.mjs +0 -39
- package/dist/views/common/version-history.mjs +0 -272
- package/dist/views/index.mjs +0 -9
- package/dist/views/layout/admin-layout.mjs +0 -40
- package/dist/views/layout/admin-router.mjs +0 -95
- package/dist/views/layout/admin-sidebar.mjs +0 -63
- package/dist/views/layout/index.mjs +0 -5
- package/src/__tests__/setup.ts +0 -44
- package/src/__tests__/test-utils.tsx +0 -49
- package/src/__tests__/vitest.d.ts +0 -9
- package/src/components/admin-app.tsx +0 -221
- package/src/components/fields/array-field.tsx +0 -237
- package/src/components/fields/checkbox-field.tsx +0 -47
- package/src/components/fields/custom-field.tsx +0 -50
- package/src/components/fields/date-field.tsx +0 -65
- package/src/components/fields/datetime-field.tsx +0 -67
- package/src/components/fields/email-field.tsx +0 -51
- package/src/components/fields/embedded-collection.tsx +0 -315
- package/src/components/fields/field-types.ts +0 -162
- package/src/components/fields/field-utils.ts +0 -6
- package/src/components/fields/field-wrapper.tsx +0 -52
- package/src/components/fields/index.ts +0 -66
- package/src/components/fields/json-field.tsx +0 -440
- package/src/components/fields/locale-badge.tsx +0 -15
- package/src/components/fields/number-field.tsx +0 -57
- package/src/components/fields/password-field.tsx +0 -51
- package/src/components/fields/relation-field.tsx +0 -243
- package/src/components/fields/relation-picker.tsx +0 -402
- package/src/components/fields/relation-select.tsx +0 -327
- package/src/components/fields/rich-text-editor/index.tsx +0 -1337
- package/src/components/fields/select-field.tsx +0 -61
- package/src/components/fields/switch-field.tsx +0 -47
- package/src/components/fields/text-field.tsx +0 -55
- package/src/components/fields/textarea-field.tsx +0 -55
- package/src/components/index.ts +0 -40
- package/src/components/primitives/checkbox-input.tsx +0 -193
- package/src/components/primitives/date-input.tsx +0 -401
- package/src/components/primitives/index.ts +0 -24
- package/src/components/primitives/number-input.tsx +0 -132
- package/src/components/primitives/select-input.tsx +0 -296
- package/src/components/primitives/tag-input.tsx +0 -200
- package/src/components/primitives/text-input.tsx +0 -49
- package/src/components/primitives/textarea-input.tsx +0 -46
- package/src/components/primitives/toggle-input.tsx +0 -36
- package/src/components/primitives/types.ts +0 -235
- package/src/components/ui/accordion.tsx +0 -72
- package/src/components/ui/avatar.tsx +0 -106
- package/src/components/ui/badge.tsx +0 -48
- package/src/components/ui/button.tsx +0 -53
- package/src/components/ui/card.tsx +0 -94
- package/src/components/ui/checkbox.tsx +0 -27
- package/src/components/ui/combobox.tsx +0 -290
- package/src/components/ui/dialog.tsx +0 -151
- package/src/components/ui/dropdown-menu.tsx +0 -254
- package/src/components/ui/field.tsx +0 -227
- package/src/components/ui/input-group.tsx +0 -149
- package/src/components/ui/input.tsx +0 -20
- package/src/components/ui/label.tsx +0 -18
- package/src/components/ui/popover.tsx +0 -88
- package/src/components/ui/scroll-area.tsx +0 -53
- package/src/components/ui/select.tsx +0 -192
- package/src/components/ui/separator.tsx +0 -23
- package/src/components/ui/sheet.tsx +0 -127
- package/src/components/ui/sidebar.tsx +0 -723
- package/src/components/ui/skeleton.tsx +0 -13
- package/src/components/ui/spinner.tsx +0 -10
- package/src/components/ui/switch.tsx +0 -32
- package/src/components/ui/table.tsx +0 -99
- package/src/components/ui/tabs.tsx +0 -82
- package/src/components/ui/textarea.tsx +0 -18
- package/src/components/ui/tooltip.tsx +0 -70
- package/src/config/component-registry.ts +0 -190
- package/src/config/index.ts +0 -1099
- package/src/hooks/README.md +0 -269
- package/src/hooks/admin-provider.tsx +0 -110
- package/src/hooks/index.ts +0 -41
- package/src/hooks/store.ts +0 -248
- package/src/hooks/use-auth.ts +0 -168
- package/src/hooks/use-collection-db.ts +0 -209
- package/src/hooks/use-collection.ts +0 -156
- package/src/hooks/use-global.ts +0 -69
- package/src/hooks/use-mobile.ts +0 -21
- package/src/lib/utils.ts +0 -6
- package/src/styles/index.css +0 -340
- package/src/utils/index.ts +0 -6
- package/src/views/auth/auth-layout.tsx +0 -77
- package/src/views/auth/forgot-password-form.tsx +0 -192
- package/src/views/auth/index.ts +0 -21
- package/src/views/auth/login-form.tsx +0 -229
- package/src/views/auth/reset-password-form.tsx +0 -232
- package/src/views/collection/auto-form-fields.tsx +0 -982
- package/src/views/collection/collection-form.tsx +0 -186
- package/src/views/collection/collection-list.tsx +0 -223
- package/src/views/collection/form-field.tsx +0 -52
- package/src/views/collection/index.ts +0 -15
- package/src/views/common/index.ts +0 -8
- package/src/views/common/locale-switcher.tsx +0 -45
- package/src/views/common/version-history.tsx +0 -406
- package/src/views/index.ts +0 -25
- package/src/views/layout/admin-layout.tsx +0 -117
- package/src/views/layout/admin-router.tsx +0 -206
- package/src/views/layout/admin-sidebar.tsx +0 -185
- package/src/views/layout/index.ts +0 -12
- package/tsconfig.json +0 -13
- package/tsconfig.tsbuildinfo +0 -1
- package/tsdown.config.ts +0 -13
- package/vitest.config.ts +0 -29
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
import { f as useAdminStore, g as cn, h as Button, s as selectClient } from "./content-locales-provider-BXvuIgfg.mjs";
|
|
2
|
+
import { CloudArrowUp, SpinnerGap, XIcon } from "@phosphor-icons/react";
|
|
3
|
+
import * as React$1 from "react";
|
|
4
|
+
import { useCallback, useState } from "react";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
7
|
+
import { Dialog } from "@base-ui/react/dialog";
|
|
8
|
+
import { Drawer } from "vaul";
|
|
9
|
+
|
|
10
|
+
//#region src/client/hooks/use-media-query.ts
|
|
11
|
+
/**
|
|
12
|
+
* Hook to detect media query matches
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const isMobile = useMediaQuery("(max-width: 768px)");
|
|
17
|
+
* const isDesktop = useMediaQuery("(min-width: 769px)");
|
|
18
|
+
* const prefersDark = useMediaQuery("(prefers-color-scheme: dark)");
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
function useMediaQuery(query) {
|
|
22
|
+
const [matches, setMatches] = React$1.useState(() => {
|
|
23
|
+
if (typeof window === "undefined") return false;
|
|
24
|
+
return window.matchMedia(query).matches;
|
|
25
|
+
});
|
|
26
|
+
React$1.useEffect(() => {
|
|
27
|
+
if (typeof window === "undefined") return;
|
|
28
|
+
const mediaQuery = window.matchMedia(query);
|
|
29
|
+
setMatches(mediaQuery.matches);
|
|
30
|
+
const listener = (event) => {
|
|
31
|
+
setMatches(event.matches);
|
|
32
|
+
};
|
|
33
|
+
if (mediaQuery.addEventListener) mediaQuery.addEventListener("change", listener);
|
|
34
|
+
else mediaQuery.addListener(listener);
|
|
35
|
+
return () => {
|
|
36
|
+
if (mediaQuery.removeEventListener) mediaQuery.removeEventListener("change", listener);
|
|
37
|
+
else mediaQuery.removeListener(listener);
|
|
38
|
+
};
|
|
39
|
+
}, [query]);
|
|
40
|
+
return matches;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Predefined breakpoints matching Tailwind CSS defaults
|
|
44
|
+
*/
|
|
45
|
+
const breakpoints = {
|
|
46
|
+
sm: "(min-width: 640px)",
|
|
47
|
+
md: "(min-width: 768px)",
|
|
48
|
+
lg: "(min-width: 1024px)",
|
|
49
|
+
xl: "(min-width: 1280px)",
|
|
50
|
+
"2xl": "(min-width: 1536px)"
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Hook to check if viewport is mobile (below md breakpoint)
|
|
54
|
+
*/
|
|
55
|
+
function useIsMobile() {
|
|
56
|
+
return !useMediaQuery(breakpoints.md);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Hook to check if viewport is desktop (md breakpoint and above)
|
|
60
|
+
*/
|
|
61
|
+
function useIsDesktop() {
|
|
62
|
+
return useMediaQuery(breakpoints.md);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/client/components/ui/dialog.tsx
|
|
67
|
+
function Dialog$1({ ...props }) {
|
|
68
|
+
return /* @__PURE__ */ jsx(Dialog.Root, {
|
|
69
|
+
"data-slot": "dialog",
|
|
70
|
+
...props
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function DialogPortal({ ...props }) {
|
|
74
|
+
return /* @__PURE__ */ jsx(Dialog.Portal, {
|
|
75
|
+
"data-slot": "dialog-portal",
|
|
76
|
+
...props
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function DialogOverlay({ className, ...props }) {
|
|
80
|
+
return /* @__PURE__ */ jsx(Dialog.Backdrop, {
|
|
81
|
+
"data-slot": "dialog-overlay",
|
|
82
|
+
className: cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/80 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50", className),
|
|
83
|
+
...props
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function DialogContent({ className, children, showCloseButton = true, ...props }) {
|
|
87
|
+
return /* @__PURE__ */ jsxs(DialogPortal, { children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(Dialog.Popup, {
|
|
88
|
+
"data-slot": "dialog-content",
|
|
89
|
+
className: cn("bg-background data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-4 p-4 text-sm ring-1 duration-100 sm:max-w-sm fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none", className),
|
|
90
|
+
...props,
|
|
91
|
+
children: [children, showCloseButton && /* @__PURE__ */ jsxs(Dialog.Close, {
|
|
92
|
+
"data-slot": "dialog-close",
|
|
93
|
+
render: /* @__PURE__ */ jsx(Button, {
|
|
94
|
+
variant: "ghost",
|
|
95
|
+
className: "absolute top-2 right-2",
|
|
96
|
+
size: "icon-sm"
|
|
97
|
+
}),
|
|
98
|
+
children: [/* @__PURE__ */ jsx(XIcon, {}), /* @__PURE__ */ jsx("span", {
|
|
99
|
+
className: "sr-only",
|
|
100
|
+
children: "Close"
|
|
101
|
+
})]
|
|
102
|
+
})]
|
|
103
|
+
})] });
|
|
104
|
+
}
|
|
105
|
+
function DialogHeader({ className, ...props }) {
|
|
106
|
+
return /* @__PURE__ */ jsx("div", {
|
|
107
|
+
"data-slot": "dialog-header",
|
|
108
|
+
className: cn("gap-1 flex flex-col", className),
|
|
109
|
+
...props
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function DialogFooter({ className, showCloseButton = false, children, ...props }) {
|
|
113
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
114
|
+
"data-slot": "dialog-footer",
|
|
115
|
+
className: cn("gap-2 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className),
|
|
116
|
+
...props,
|
|
117
|
+
children: [children, showCloseButton && /* @__PURE__ */ jsx(Dialog.Close, {
|
|
118
|
+
render: /* @__PURE__ */ jsx(Button, { variant: "outline" }),
|
|
119
|
+
children: "Close"
|
|
120
|
+
})]
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function DialogTitle({ className, ...props }) {
|
|
124
|
+
return /* @__PURE__ */ jsx(Dialog.Title, {
|
|
125
|
+
"data-slot": "dialog-title",
|
|
126
|
+
className: cn("text-sm font-medium", className),
|
|
127
|
+
...props
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function DialogDescription({ className, ...props }) {
|
|
131
|
+
return /* @__PURE__ */ jsx(Dialog.Description, {
|
|
132
|
+
"data-slot": "dialog-description",
|
|
133
|
+
className: cn("text-muted-foreground *:[a]:hover:text-foreground text-xs/relaxed *:[a]:underline *:[a]:underline-offset-3", className),
|
|
134
|
+
...props
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/client/components/ui/drawer.tsx
|
|
140
|
+
function Drawer$1({ ...props }) {
|
|
141
|
+
return /* @__PURE__ */ jsx(Drawer.Root, {
|
|
142
|
+
"data-slot": "drawer",
|
|
143
|
+
...props
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function DrawerTrigger({ ...props }) {
|
|
147
|
+
return /* @__PURE__ */ jsx(Drawer.Trigger, {
|
|
148
|
+
"data-slot": "drawer-trigger",
|
|
149
|
+
...props
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function DrawerPortal({ ...props }) {
|
|
153
|
+
return /* @__PURE__ */ jsx(Drawer.Portal, {
|
|
154
|
+
"data-slot": "drawer-portal",
|
|
155
|
+
...props
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function DrawerOverlay({ className, ...props }) {
|
|
159
|
+
return /* @__PURE__ */ jsx(Drawer.Overlay, {
|
|
160
|
+
"data-slot": "drawer-overlay",
|
|
161
|
+
className: cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/80 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 z-50", className),
|
|
162
|
+
...props
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
function DrawerContent({ className, children, ...props }) {
|
|
166
|
+
return /* @__PURE__ */ jsxs(DrawerPortal, {
|
|
167
|
+
"data-slot": "drawer-portal",
|
|
168
|
+
children: [/* @__PURE__ */ jsx(DrawerOverlay, {}), /* @__PURE__ */ jsxs(Drawer.Content, {
|
|
169
|
+
"data-slot": "drawer-content",
|
|
170
|
+
className: cn("before:bg-background px-4 pb-4 relative flex h-auto flex-col bg-transparent text-xs/relaxed before:absolute before:inset-2 before:-z-10 before:rounded-xl data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm group/drawer-content fixed z-50", className),
|
|
171
|
+
...props,
|
|
172
|
+
children: [/* @__PURE__ */ jsx("div", { className: "bg-muted mx-auto mt-4 hidden h-1.5 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }), children]
|
|
173
|
+
})]
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function DrawerHeader({ className, ...props }) {
|
|
177
|
+
return /* @__PURE__ */ jsx("div", {
|
|
178
|
+
"data-slot": "drawer-header",
|
|
179
|
+
className: cn("gap-1 pt-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:text-left flex flex-col", className),
|
|
180
|
+
...props
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function DrawerFooter({ className, ...props }) {
|
|
184
|
+
return /* @__PURE__ */ jsx("div", {
|
|
185
|
+
"data-slot": "drawer-footer",
|
|
186
|
+
className: cn("gap-2 py-4 mt-auto flex flex-col", className),
|
|
187
|
+
...props
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function DrawerTitle({ className, ...props }) {
|
|
191
|
+
return /* @__PURE__ */ jsx(Drawer.Title, {
|
|
192
|
+
"data-slot": "drawer-title",
|
|
193
|
+
className: cn("text-foreground text-sm font-medium", className),
|
|
194
|
+
...props
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function DrawerDescription({ className, ...props }) {
|
|
198
|
+
return /* @__PURE__ */ jsx(Drawer.Description, {
|
|
199
|
+
"data-slot": "drawer-description",
|
|
200
|
+
className: cn("text-muted-foreground text-xs/relaxed", className),
|
|
201
|
+
...props
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region src/client/components/ui/responsive-dialog.tsx
|
|
207
|
+
const ResponsiveDialogContext = React$1.createContext(null);
|
|
208
|
+
function useResponsiveDialog() {
|
|
209
|
+
const context = React$1.useContext(ResponsiveDialogContext);
|
|
210
|
+
if (!context) throw new Error("ResponsiveDialog components must be used within ResponsiveDialog");
|
|
211
|
+
return context;
|
|
212
|
+
}
|
|
213
|
+
function ResponsiveDialog({ children, open, onOpenChange }) {
|
|
214
|
+
const isMobile = useIsMobile();
|
|
215
|
+
const contextValue = React$1.useMemo(() => ({ isMobile }), [isMobile]);
|
|
216
|
+
if (isMobile) return /* @__PURE__ */ jsx(ResponsiveDialogContext.Provider, {
|
|
217
|
+
value: contextValue,
|
|
218
|
+
children: /* @__PURE__ */ jsx(Drawer$1, {
|
|
219
|
+
open,
|
|
220
|
+
onOpenChange,
|
|
221
|
+
children
|
|
222
|
+
})
|
|
223
|
+
});
|
|
224
|
+
return /* @__PURE__ */ jsx(ResponsiveDialogContext.Provider, {
|
|
225
|
+
value: contextValue,
|
|
226
|
+
children: /* @__PURE__ */ jsx(Dialog.Root, {
|
|
227
|
+
open,
|
|
228
|
+
onOpenChange,
|
|
229
|
+
children
|
|
230
|
+
})
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
function ResponsiveDialogContent({ children, className }) {
|
|
234
|
+
const { isMobile } = useResponsiveDialog();
|
|
235
|
+
if (isMobile) return /* @__PURE__ */ jsx(DrawerContent, {
|
|
236
|
+
className: cn("max-h-[96vh]", className),
|
|
237
|
+
children
|
|
238
|
+
});
|
|
239
|
+
return /* @__PURE__ */ jsx(DialogContent, {
|
|
240
|
+
className,
|
|
241
|
+
children
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
function ResponsiveDialogHeader({ className, ...props }) {
|
|
245
|
+
const { isMobile } = useResponsiveDialog();
|
|
246
|
+
if (isMobile) return /* @__PURE__ */ jsx(DrawerHeader, {
|
|
247
|
+
className,
|
|
248
|
+
...props
|
|
249
|
+
});
|
|
250
|
+
return /* @__PURE__ */ jsx(DialogHeader, {
|
|
251
|
+
className,
|
|
252
|
+
...props
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
function ResponsiveDialogTitle({ className, children, ...props }) {
|
|
256
|
+
const { isMobile } = useResponsiveDialog();
|
|
257
|
+
if (isMobile) return /* @__PURE__ */ jsx(DrawerTitle, {
|
|
258
|
+
className,
|
|
259
|
+
...props,
|
|
260
|
+
children
|
|
261
|
+
});
|
|
262
|
+
return /* @__PURE__ */ jsx(Dialog.Title, {
|
|
263
|
+
"data-slot": "responsive-dialog-title",
|
|
264
|
+
className: cn("text-sm font-medium", className),
|
|
265
|
+
...props,
|
|
266
|
+
children
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
function ResponsiveDialogDescription({ className, children, ...props }) {
|
|
270
|
+
const { isMobile } = useResponsiveDialog();
|
|
271
|
+
if (isMobile) return /* @__PURE__ */ jsx(DrawerDescription, {
|
|
272
|
+
className,
|
|
273
|
+
...props,
|
|
274
|
+
children
|
|
275
|
+
});
|
|
276
|
+
return /* @__PURE__ */ jsx(Dialog.Description, {
|
|
277
|
+
"data-slot": "responsive-dialog-description",
|
|
278
|
+
className: cn("text-muted-foreground text-xs/relaxed", className),
|
|
279
|
+
...props,
|
|
280
|
+
children
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
function ResponsiveDialogFooter({ className, ...props }) {
|
|
284
|
+
const { isMobile } = useResponsiveDialog();
|
|
285
|
+
if (isMobile) return /* @__PURE__ */ jsx(DrawerFooter, {
|
|
286
|
+
className,
|
|
287
|
+
...props
|
|
288
|
+
});
|
|
289
|
+
return /* @__PURE__ */ jsx(DialogFooter, {
|
|
290
|
+
className,
|
|
291
|
+
...props
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
//#endregion
|
|
296
|
+
//#region src/client/hooks/use-upload.ts
|
|
297
|
+
/**
|
|
298
|
+
* useUpload Hook
|
|
299
|
+
*
|
|
300
|
+
* Handles file uploads to the CMS with progress tracking.
|
|
301
|
+
* Uses the QuestpieClient's upload method which uses XMLHttpRequest for progress.
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* ```tsx
|
|
305
|
+
* const { upload, uploadMany, isUploading, progress } = useUpload();
|
|
306
|
+
*
|
|
307
|
+
* // Single file upload
|
|
308
|
+
* const asset = await upload(file);
|
|
309
|
+
*
|
|
310
|
+
* // Multiple files upload
|
|
311
|
+
* const assets = await uploadMany(files, {
|
|
312
|
+
* onProgress: (p) => console.log(`${p}%`),
|
|
313
|
+
* });
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
/**
|
|
317
|
+
* Hook for uploading files to the CMS
|
|
318
|
+
*
|
|
319
|
+
* Uses the QuestpieClient's built-in upload method which provides
|
|
320
|
+
* progress tracking via XMLHttpRequest.
|
|
321
|
+
*/
|
|
322
|
+
function useUpload() {
|
|
323
|
+
const client = useAdminStore(selectClient);
|
|
324
|
+
const queryClient = useQueryClient();
|
|
325
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
326
|
+
const [progress, setProgress] = useState(0);
|
|
327
|
+
const [error, setError] = useState(null);
|
|
328
|
+
return {
|
|
329
|
+
upload: useCallback(async (file, options = {}) => {
|
|
330
|
+
const { collection = "assets", onProgress, signal } = options;
|
|
331
|
+
setIsUploading(true);
|
|
332
|
+
setProgress(0);
|
|
333
|
+
setError(null);
|
|
334
|
+
try {
|
|
335
|
+
const collectionApi = client.collections[collection];
|
|
336
|
+
if (!collectionApi?.upload) throw new Error(`Collection "${collection}" does not support uploads. Make sure .upload() is enabled on the collection.`);
|
|
337
|
+
const result = await collectionApi.upload(file, {
|
|
338
|
+
signal,
|
|
339
|
+
onProgress: (p) => {
|
|
340
|
+
setProgress(p);
|
|
341
|
+
onProgress?.(p);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
queryClient.invalidateQueries({ queryKey: [
|
|
345
|
+
"questpie",
|
|
346
|
+
"collections",
|
|
347
|
+
collection
|
|
348
|
+
] });
|
|
349
|
+
return result;
|
|
350
|
+
} catch (err) {
|
|
351
|
+
const uploadError = err instanceof Error ? err : /* @__PURE__ */ new Error("Upload failed");
|
|
352
|
+
setError(uploadError);
|
|
353
|
+
throw uploadError;
|
|
354
|
+
} finally {
|
|
355
|
+
setIsUploading(false);
|
|
356
|
+
}
|
|
357
|
+
}, [client, queryClient]),
|
|
358
|
+
uploadMany: useCallback(async (files, options = {}) => {
|
|
359
|
+
const { collection = "assets", onProgress, signal } = options;
|
|
360
|
+
if (files.length === 0) return [];
|
|
361
|
+
setIsUploading(true);
|
|
362
|
+
setProgress(0);
|
|
363
|
+
setError(null);
|
|
364
|
+
try {
|
|
365
|
+
const collectionApi = client.collections[collection];
|
|
366
|
+
if (!collectionApi?.uploadMany) throw new Error(`Collection "${collection}" does not support uploads. Make sure .upload() is enabled on the collection.`);
|
|
367
|
+
const results = await collectionApi.uploadMany(files, {
|
|
368
|
+
signal,
|
|
369
|
+
onProgress: (p, fileIndex) => {
|
|
370
|
+
setProgress(p);
|
|
371
|
+
onProgress?.(p, fileIndex);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
queryClient.invalidateQueries({ queryKey: [
|
|
375
|
+
"questpie",
|
|
376
|
+
"collections",
|
|
377
|
+
collection
|
|
378
|
+
] });
|
|
379
|
+
setProgress(100);
|
|
380
|
+
return results;
|
|
381
|
+
} catch (err) {
|
|
382
|
+
const uploadError = err instanceof Error ? err : /* @__PURE__ */ new Error("Upload failed");
|
|
383
|
+
setError(uploadError);
|
|
384
|
+
throw uploadError;
|
|
385
|
+
} finally {
|
|
386
|
+
setIsUploading(false);
|
|
387
|
+
}
|
|
388
|
+
}, [client, queryClient]),
|
|
389
|
+
isUploading,
|
|
390
|
+
progress,
|
|
391
|
+
error,
|
|
392
|
+
reset: useCallback(() => {
|
|
393
|
+
setIsUploading(false);
|
|
394
|
+
setProgress(0);
|
|
395
|
+
setError(null);
|
|
396
|
+
}, [])
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
//#endregion
|
|
401
|
+
//#region src/client/components/primitives/dropzone.tsx
|
|
402
|
+
/**
|
|
403
|
+
* Dropzone Primitive
|
|
404
|
+
*
|
|
405
|
+
* A reusable drag-and-drop area for file uploads.
|
|
406
|
+
* Supports file type filtering, size validation, and visual feedback.
|
|
407
|
+
*
|
|
408
|
+
* @example
|
|
409
|
+
* ```tsx
|
|
410
|
+
* <Dropzone
|
|
411
|
+
* onDrop={(files) => handleUpload(files)}
|
|
412
|
+
* accept={["image/*", "application/pdf"]}
|
|
413
|
+
* maxSize={5_000_000}
|
|
414
|
+
* />
|
|
415
|
+
* ```
|
|
416
|
+
*/
|
|
417
|
+
/**
|
|
418
|
+
* Check if a file matches accepted types
|
|
419
|
+
*/
|
|
420
|
+
function matchesAccept(file, accept) {
|
|
421
|
+
if (!accept || accept.length === 0) return true;
|
|
422
|
+
const mimeType = file.type.toLowerCase();
|
|
423
|
+
const fileName = file.name.toLowerCase();
|
|
424
|
+
return accept.some((pattern) => {
|
|
425
|
+
const normalizedPattern = pattern.toLowerCase();
|
|
426
|
+
if (normalizedPattern.endsWith("/*")) {
|
|
427
|
+
const baseType = normalizedPattern.slice(0, -2);
|
|
428
|
+
return mimeType.startsWith(`${baseType}/`);
|
|
429
|
+
}
|
|
430
|
+
if (normalizedPattern.startsWith(".")) return fileName.endsWith(normalizedPattern);
|
|
431
|
+
return mimeType === normalizedPattern;
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Format file size for display
|
|
436
|
+
*/
|
|
437
|
+
function formatFileSize(bytes) {
|
|
438
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
439
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
440
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
441
|
+
}
|
|
442
|
+
function Dropzone({ onDrop, accept, maxSize, multiple = false, disabled = false, loading = false, progress, label = "Drop files here or click to browse", hint, error, className, children, onValidationError }) {
|
|
443
|
+
const [isDragging, setIsDragging] = React$1.useState(false);
|
|
444
|
+
const inputRef = React$1.useRef(null);
|
|
445
|
+
const dragCounterRef = React$1.useRef(0);
|
|
446
|
+
/**
|
|
447
|
+
* Validate files and return valid ones + errors
|
|
448
|
+
*/
|
|
449
|
+
const validateFiles = React$1.useCallback((files) => {
|
|
450
|
+
const valid = [];
|
|
451
|
+
const errors = [];
|
|
452
|
+
for (const file of files) {
|
|
453
|
+
if (!matchesAccept(file, accept)) {
|
|
454
|
+
errors.push({
|
|
455
|
+
file,
|
|
456
|
+
type: "type",
|
|
457
|
+
message: `"${file.name}" is not an accepted file type`
|
|
458
|
+
});
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (maxSize && file.size > maxSize) {
|
|
462
|
+
errors.push({
|
|
463
|
+
file,
|
|
464
|
+
type: "size",
|
|
465
|
+
message: `"${file.name}" exceeds maximum size of ${formatFileSize(maxSize)}`
|
|
466
|
+
});
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
valid.push(file);
|
|
470
|
+
}
|
|
471
|
+
return {
|
|
472
|
+
valid,
|
|
473
|
+
errors
|
|
474
|
+
};
|
|
475
|
+
}, [accept, maxSize]);
|
|
476
|
+
/**
|
|
477
|
+
* Handle file selection
|
|
478
|
+
*/
|
|
479
|
+
const handleFiles = React$1.useCallback((files) => {
|
|
480
|
+
if (disabled || loading) return;
|
|
481
|
+
const fileArray = Array.from(files);
|
|
482
|
+
const { valid, errors } = validateFiles(multiple ? fileArray : fileArray.slice(0, 1));
|
|
483
|
+
if (errors.length > 0) onValidationError?.(errors);
|
|
484
|
+
if (valid.length > 0) onDrop(valid);
|
|
485
|
+
}, [
|
|
486
|
+
disabled,
|
|
487
|
+
loading,
|
|
488
|
+
multiple,
|
|
489
|
+
validateFiles,
|
|
490
|
+
onDrop,
|
|
491
|
+
onValidationError
|
|
492
|
+
]);
|
|
493
|
+
/**
|
|
494
|
+
* Handle drag events
|
|
495
|
+
*/
|
|
496
|
+
const handleDragEnter = React$1.useCallback((e) => {
|
|
497
|
+
e.preventDefault();
|
|
498
|
+
e.stopPropagation();
|
|
499
|
+
dragCounterRef.current += 1;
|
|
500
|
+
if (e.dataTransfer.items.length > 0 && !disabled && !loading) setIsDragging(true);
|
|
501
|
+
}, [disabled, loading]);
|
|
502
|
+
const handleDragLeave = React$1.useCallback((e) => {
|
|
503
|
+
e.preventDefault();
|
|
504
|
+
e.stopPropagation();
|
|
505
|
+
dragCounterRef.current -= 1;
|
|
506
|
+
if (dragCounterRef.current === 0) setIsDragging(false);
|
|
507
|
+
}, []);
|
|
508
|
+
const handleDragOver = React$1.useCallback((e) => {
|
|
509
|
+
e.preventDefault();
|
|
510
|
+
e.stopPropagation();
|
|
511
|
+
}, []);
|
|
512
|
+
const handleDrop = React$1.useCallback((e) => {
|
|
513
|
+
e.preventDefault();
|
|
514
|
+
e.stopPropagation();
|
|
515
|
+
dragCounterRef.current = 0;
|
|
516
|
+
setIsDragging(false);
|
|
517
|
+
if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files);
|
|
518
|
+
}, [handleFiles]);
|
|
519
|
+
/**
|
|
520
|
+
* Handle click to open file dialog
|
|
521
|
+
*/
|
|
522
|
+
const handleClick = React$1.useCallback(() => {
|
|
523
|
+
if (disabled || loading) return;
|
|
524
|
+
inputRef.current?.click();
|
|
525
|
+
}, [disabled, loading]);
|
|
526
|
+
/**
|
|
527
|
+
* Handle file input change
|
|
528
|
+
*/
|
|
529
|
+
const handleInputChange = React$1.useCallback((e) => {
|
|
530
|
+
if (e.target.files && e.target.files.length > 0) handleFiles(e.target.files);
|
|
531
|
+
e.target.value = "";
|
|
532
|
+
}, [handleFiles]);
|
|
533
|
+
/**
|
|
534
|
+
* Build accept string for input
|
|
535
|
+
*/
|
|
536
|
+
const acceptString = accept?.join(",") || void 0;
|
|
537
|
+
/**
|
|
538
|
+
* Build hint text
|
|
539
|
+
*/
|
|
540
|
+
const hintText = React$1.useMemo(() => {
|
|
541
|
+
if (hint) return hint;
|
|
542
|
+
const parts = [];
|
|
543
|
+
if (accept && accept.length > 0) {
|
|
544
|
+
const types = accept.map((t) => {
|
|
545
|
+
if (t.startsWith("image/")) return "Images";
|
|
546
|
+
if (t.startsWith("video/")) return "Videos";
|
|
547
|
+
if (t.startsWith("audio/")) return "Audio";
|
|
548
|
+
if (t === "application/pdf") return "PDF";
|
|
549
|
+
if (t.startsWith(".")) return t.toUpperCase();
|
|
550
|
+
return t;
|
|
551
|
+
}).filter((v, i, a) => a.indexOf(v) === i);
|
|
552
|
+
parts.push(types.join(", "));
|
|
553
|
+
}
|
|
554
|
+
if (maxSize) parts.push(`Max ${formatFileSize(maxSize)}`);
|
|
555
|
+
return parts.length > 0 ? parts.join(" • ") : void 0;
|
|
556
|
+
}, [
|
|
557
|
+
hint,
|
|
558
|
+
accept,
|
|
559
|
+
maxSize
|
|
560
|
+
]);
|
|
561
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
562
|
+
role: "button",
|
|
563
|
+
tabIndex: disabled || loading ? -1 : 0,
|
|
564
|
+
onClick: handleClick,
|
|
565
|
+
onKeyDown: (e) => {
|
|
566
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
567
|
+
e.preventDefault();
|
|
568
|
+
handleClick();
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
onDragEnter: handleDragEnter,
|
|
572
|
+
onDragLeave: handleDragLeave,
|
|
573
|
+
onDragOver: handleDragOver,
|
|
574
|
+
onDrop: handleDrop,
|
|
575
|
+
className: cn("relative flex min-h-[120px] cursor-pointer flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed p-6 text-center transition-colors", "border-border/60 bg-muted/30 hover:border-border hover:bg-muted/50", isDragging && "border-primary bg-primary/5", error && "border-destructive/50 bg-destructive/5", (disabled || loading) && "pointer-events-none opacity-60", className),
|
|
576
|
+
"aria-disabled": disabled || loading,
|
|
577
|
+
"data-dragging": isDragging || void 0,
|
|
578
|
+
children: [
|
|
579
|
+
/* @__PURE__ */ jsx("input", {
|
|
580
|
+
ref: inputRef,
|
|
581
|
+
type: "file",
|
|
582
|
+
accept: acceptString,
|
|
583
|
+
multiple,
|
|
584
|
+
onChange: handleInputChange,
|
|
585
|
+
className: "sr-only",
|
|
586
|
+
disabled: disabled || loading,
|
|
587
|
+
tabIndex: -1
|
|
588
|
+
}),
|
|
589
|
+
children || /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
590
|
+
className: "flex items-center justify-center",
|
|
591
|
+
children: loading ? /* @__PURE__ */ jsxs("div", {
|
|
592
|
+
className: "relative",
|
|
593
|
+
children: [/* @__PURE__ */ jsx(SpinnerGap, {
|
|
594
|
+
className: "text-muted-foreground size-10 animate-spin",
|
|
595
|
+
weight: "regular"
|
|
596
|
+
}), typeof progress === "number" && /* @__PURE__ */ jsxs("span", {
|
|
597
|
+
className: "text-muted-foreground absolute inset-0 flex items-center justify-center text-xs font-medium",
|
|
598
|
+
children: [progress, "%"]
|
|
599
|
+
})]
|
|
600
|
+
}) : /* @__PURE__ */ jsx(CloudArrowUp, {
|
|
601
|
+
className: cn("size-10 transition-colors", isDragging ? "text-primary" : "text-muted-foreground"),
|
|
602
|
+
weight: "regular"
|
|
603
|
+
})
|
|
604
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
605
|
+
className: "space-y-1",
|
|
606
|
+
children: [
|
|
607
|
+
/* @__PURE__ */ jsx("p", {
|
|
608
|
+
className: cn("text-sm font-medium", isDragging ? "text-primary" : "text-foreground"),
|
|
609
|
+
children: loading ? "Uploading..." : label
|
|
610
|
+
}),
|
|
611
|
+
hintText && !loading && /* @__PURE__ */ jsx("p", {
|
|
612
|
+
className: "text-muted-foreground text-xs",
|
|
613
|
+
children: hintText
|
|
614
|
+
}),
|
|
615
|
+
loading && typeof progress === "number" && /* @__PURE__ */ jsx("div", {
|
|
616
|
+
className: "bg-muted mx-auto mt-2 h-1.5 w-32 overflow-hidden rounded-full",
|
|
617
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
618
|
+
className: "bg-primary h-full rounded-full transition-all duration-300",
|
|
619
|
+
style: { width: `${progress}%` }
|
|
620
|
+
})
|
|
621
|
+
})
|
|
622
|
+
]
|
|
623
|
+
})] }),
|
|
624
|
+
error && /* @__PURE__ */ jsx("p", {
|
|
625
|
+
className: "text-destructive absolute bottom-2 left-0 right-0 text-center text-xs",
|
|
626
|
+
children: error
|
|
627
|
+
})
|
|
628
|
+
]
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
//#endregion
|
|
633
|
+
export { useMediaQuery as S, DialogFooter as _, ResponsiveDialogDescription as a, useIsDesktop as b, ResponsiveDialogTitle as c, DrawerHeader as d, DrawerTitle as f, DialogDescription as g, DialogContent as h, ResponsiveDialogContent as i, Drawer$1 as l, Dialog$1 as m, useUpload as n, ResponsiveDialogFooter as o, DrawerTrigger as p, ResponsiveDialog as r, ResponsiveDialogHeader as s, Dropzone as t, DrawerContent as u, DialogHeader as v, useIsMobile as x, DialogTitle as y };
|
|
634
|
+
//# sourceMappingURL=dropzone-Do3awXKd.mjs.map
|