uploaderkit 1.0.0 → 2.0.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/LICENSE +21 -0
- package/NOTICE +16 -0
- package/README.es.md +1140 -0
- package/README.md +1135 -0
- package/dist/FileViewer-ChTf7-uY.d.cts +49 -0
- package/dist/FileViewer-nuD-GEdJ.d.ts +49 -0
- package/dist/adapters/gcs.cjs +83 -0
- package/dist/adapters/gcs.cjs.map +1 -0
- package/dist/adapters/gcs.d.cts +31 -0
- package/dist/adapters/gcs.d.ts +31 -0
- package/dist/adapters/gcs.js +81 -0
- package/dist/adapters/gcs.js.map +1 -0
- package/dist/adapters/memory.cjs +27 -0
- package/dist/adapters/memory.cjs.map +1 -0
- package/dist/adapters/memory.d.cts +18 -0
- package/dist/adapters/memory.d.ts +18 -0
- package/dist/adapters/memory.js +25 -0
- package/dist/adapters/memory.js.map +1 -0
- package/dist/adapters/s3.cjs +75 -0
- package/dist/adapters/s3.cjs.map +1 -0
- package/dist/adapters/s3.d.cts +27 -0
- package/dist/adapters/s3.d.ts +27 -0
- package/dist/adapters/s3.js +73 -0
- package/dist/adapters/s3.js.map +1 -0
- package/dist/chunk-3FI44IOW.js +150 -0
- package/dist/chunk-3FI44IOW.js.map +1 -0
- package/dist/chunk-H7BRW5IY.js +308 -0
- package/dist/chunk-H7BRW5IY.js.map +1 -0
- package/dist/chunk-PDKAF4GX.js +707 -0
- package/dist/chunk-PDKAF4GX.js.map +1 -0
- package/dist/chunk-PTEX7F4R.js +309 -0
- package/dist/chunk-PTEX7F4R.js.map +1 -0
- package/dist/chunk-T5YZG6JW.js +511 -0
- package/dist/chunk-T5YZG6JW.js.map +1 -0
- package/dist/index.cjs +220 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -170
- package/dist/index.d.ts +57 -170
- package/dist/index.js +1 -324
- package/dist/index.js.map +1 -1
- package/dist/presets.cjs +1665 -0
- package/dist/presets.cjs.map +1 -0
- package/dist/presets.d.cts +100 -0
- package/dist/presets.d.ts +100 -0
- package/dist/presets.js +405 -0
- package/dist/presets.js.map +1 -0
- package/dist/react.cjs +981 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +106 -0
- package/dist/react.d.ts +106 -0
- package/dist/react.js +5 -0
- package/dist/react.js.map +1 -0
- package/dist/server/express.cjs +179 -0
- package/dist/server/express.cjs.map +1 -0
- package/dist/server/express.d.cts +64 -0
- package/dist/server/express.d.ts +64 -0
- package/dist/server/express.js +111 -0
- package/dist/server/express.js.map +1 -0
- package/dist/server/next.cjs +165 -0
- package/dist/server/next.cjs.map +1 -0
- package/dist/server/next.d.cts +38 -0
- package/dist/server/next.d.ts +38 -0
- package/dist/server/next.js +107 -0
- package/dist/server/next.js.map +1 -0
- package/dist/server.cjs +550 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +19 -0
- package/dist/server.d.ts +19 -0
- package/dist/server.js +80 -0
- package/dist/server.js.map +1 -0
- package/dist/storage-CYkSHWZX.d.cts +133 -0
- package/dist/storage-Qc9epG0G.d.ts +133 -0
- package/dist/types-BSlJJwti.d.cts +341 -0
- package/dist/types-BSlJJwti.d.ts +341 -0
- package/dist/ui.cjs +2325 -0
- package/dist/ui.cjs.map +1 -0
- package/dist/ui.d.cts +331 -0
- package/dist/ui.d.ts +331 -0
- package/dist/ui.js +749 -0
- package/dist/ui.js.map +1 -0
- package/dist/useSlottedUploader-BzT5jV8c.d.cts +139 -0
- package/dist/useSlottedUploader-IkG5RhEO.d.ts +139 -0
- package/dist/useUploader-BiBdS-7y.d.cts +117 -0
- package/dist/useUploader-CQHpj_oI.d.ts +117 -0
- package/package.json +189 -3
- package/tailwind.css +80 -0
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
import { useUploaderLabels, warnDev } from './chunk-H7BRW5IY.js';
|
|
2
|
+
import { getMimeType } from './chunk-T5YZG6JW.js';
|
|
3
|
+
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
|
4
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
|
+
import { createPortal } from 'react-dom';
|
|
6
|
+
|
|
7
|
+
// src/ui/cn.ts
|
|
8
|
+
var cn = (...values) => values.filter(Boolean).join(" ");
|
|
9
|
+
var Kbd = ({ children }) => /* @__PURE__ */ jsx("kbd", { className: "border-ui-border bg-ui-surface text-ui-muted rounded-md border px-1.5 py-0.5 font-sans text-[10px] font-medium shadow-sm", children });
|
|
10
|
+
var useCoarsePointer = () => {
|
|
11
|
+
const [coarse, setCoarse] = useState(false);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const query = window.matchMedia("(pointer: coarse)");
|
|
14
|
+
setCoarse(query.matches);
|
|
15
|
+
const onChange = (event) => setCoarse(event.matches);
|
|
16
|
+
query.addEventListener("change", onChange);
|
|
17
|
+
return () => query.removeEventListener("change", onChange);
|
|
18
|
+
}, []);
|
|
19
|
+
return coarse;
|
|
20
|
+
};
|
|
21
|
+
var parseShortcut = (combo) => {
|
|
22
|
+
const parts = combo.toLowerCase().split("+");
|
|
23
|
+
return {
|
|
24
|
+
mod: parts.includes("mod"),
|
|
25
|
+
shift: parts.includes("shift"),
|
|
26
|
+
alt: parts.includes("alt"),
|
|
27
|
+
key: parts[parts.length - 1]
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
var IS_MAC = typeof navigator !== "undefined" && /mac/i.test(navigator.platform);
|
|
31
|
+
var formatShortcut = (combo) => {
|
|
32
|
+
const spec = parseShortcut(combo);
|
|
33
|
+
const key = spec.key.length === 1 ? spec.key.toUpperCase() : spec.key;
|
|
34
|
+
const parts = [];
|
|
35
|
+
if (spec.mod) parts.push(IS_MAC ? "\u2318" : "Ctrl");
|
|
36
|
+
if (spec.alt) parts.push(IS_MAC ? "\u2325" : "Alt");
|
|
37
|
+
if (spec.shift) parts.push(IS_MAC ? "\u21E7" : "Shift");
|
|
38
|
+
parts.push(key);
|
|
39
|
+
return parts.join(IS_MAC ? "" : "+");
|
|
40
|
+
};
|
|
41
|
+
var active = /* @__PURE__ */ new Map();
|
|
42
|
+
var useShortcut = (combo, handler, disabled = false) => {
|
|
43
|
+
const handlerRef = useRef(handler);
|
|
44
|
+
handlerRef.current = handler;
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (!combo || disabled) return;
|
|
47
|
+
const spec = parseShortcut(combo);
|
|
48
|
+
active.set(combo, (active.get(combo) ?? 0) + 1);
|
|
49
|
+
if ((active.get(combo) ?? 0) > 1) {
|
|
50
|
+
warnDev(
|
|
51
|
+
`shortcut-collision:${combo}`,
|
|
52
|
+
`two surfaces registered the shortcut "${combo}" at the same time \u2014 give each its own combo or scope one out.`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const onKey = (event) => {
|
|
56
|
+
if (event.repeat) return;
|
|
57
|
+
const target = event.target;
|
|
58
|
+
if (target instanceof Element && target.closest("input, textarea, select, [contenteditable]")) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (event.key.toLowerCase() !== spec.key) return;
|
|
62
|
+
if (spec.mod !== (event.metaKey || event.ctrlKey)) return;
|
|
63
|
+
if (spec.shift !== event.shiftKey) return;
|
|
64
|
+
if (spec.alt !== event.altKey) return;
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
handlerRef.current();
|
|
67
|
+
};
|
|
68
|
+
window.addEventListener("keydown", onKey);
|
|
69
|
+
return () => {
|
|
70
|
+
window.removeEventListener("keydown", onKey);
|
|
71
|
+
const count = (active.get(combo) ?? 1) - 1;
|
|
72
|
+
if (count <= 0) active.delete(combo);
|
|
73
|
+
else active.set(combo, count);
|
|
74
|
+
};
|
|
75
|
+
}, [combo, disabled]);
|
|
76
|
+
};
|
|
77
|
+
var Dropzone = ({
|
|
78
|
+
accept,
|
|
79
|
+
multiple = false,
|
|
80
|
+
disabled = false,
|
|
81
|
+
capture,
|
|
82
|
+
size = "md",
|
|
83
|
+
shortcut,
|
|
84
|
+
onFiles,
|
|
85
|
+
children,
|
|
86
|
+
className
|
|
87
|
+
}) => {
|
|
88
|
+
const inputRef = useRef(null);
|
|
89
|
+
const [dragging, setDragging] = useState(false);
|
|
90
|
+
const depth = useRef(0);
|
|
91
|
+
const coarse = useCoarsePointer();
|
|
92
|
+
useShortcut(shortcut, () => inputRef.current?.click(), disabled);
|
|
93
|
+
const emit = useCallback(
|
|
94
|
+
(list) => {
|
|
95
|
+
if (disabled || !list || list.length === 0) return;
|
|
96
|
+
onFiles(Array.from(list));
|
|
97
|
+
},
|
|
98
|
+
[disabled, onFiles]
|
|
99
|
+
);
|
|
100
|
+
const onDrop = useCallback(
|
|
101
|
+
(event) => {
|
|
102
|
+
event.preventDefault();
|
|
103
|
+
depth.current = 0;
|
|
104
|
+
setDragging(false);
|
|
105
|
+
emit(event.dataTransfer.files);
|
|
106
|
+
},
|
|
107
|
+
[emit]
|
|
108
|
+
);
|
|
109
|
+
const onPaste = useCallback(
|
|
110
|
+
(event) => {
|
|
111
|
+
if (event.clipboardData.files.length === 0) return;
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
emit(event.clipboardData.files);
|
|
114
|
+
},
|
|
115
|
+
[emit]
|
|
116
|
+
);
|
|
117
|
+
return /* @__PURE__ */ jsxs(
|
|
118
|
+
"div",
|
|
119
|
+
{
|
|
120
|
+
role: "button",
|
|
121
|
+
tabIndex: disabled ? -1 : 0,
|
|
122
|
+
"aria-disabled": disabled,
|
|
123
|
+
"data-dragging": dragging || void 0,
|
|
124
|
+
onClick: () => !disabled && inputRef.current?.click(),
|
|
125
|
+
onKeyDown: (event) => {
|
|
126
|
+
if (disabled) return;
|
|
127
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
128
|
+
event.preventDefault();
|
|
129
|
+
inputRef.current?.click();
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
onDragEnter: () => {
|
|
133
|
+
depth.current += 1;
|
|
134
|
+
if (!disabled) setDragging(true);
|
|
135
|
+
},
|
|
136
|
+
onDragOver: (event) => event.preventDefault(),
|
|
137
|
+
onDragLeave: () => {
|
|
138
|
+
depth.current = Math.max(0, depth.current - 1);
|
|
139
|
+
if (depth.current === 0) setDragging(false);
|
|
140
|
+
},
|
|
141
|
+
onDrop,
|
|
142
|
+
onPaste,
|
|
143
|
+
className: cn(
|
|
144
|
+
// `group/dz` + `data-dragging` let the children (the icon, the
|
|
145
|
+
// prompt) react to hover and drag without owning any state.
|
|
146
|
+
// `touch-manipulation` kills the 300ms tap delay; the active scale
|
|
147
|
+
// is the press feedback a touch screen needs in place of hover.
|
|
148
|
+
"group/dz rounded-ui-lg focus-visible:ring-ui-ring block w-full cursor-pointer touch-manipulation border-2 border-dashed text-center transition-all duration-200 focus-visible:ring-2 focus-visible:outline-none",
|
|
149
|
+
size === "sm" ? "p-3" : "p-5",
|
|
150
|
+
dragging ? "border-ui-primary bg-ui-primary-soft shadow-ui-primary/15 scale-[1.02] shadow-lg" : "border-ui-border-strong hover:border-ui-primary/60 hover:bg-ui-primary-soft/40 hover:shadow-ui-primary/10 bg-ui-surface-subtle/50 hover:shadow-md",
|
|
151
|
+
!disabled && "active:scale-[0.98]",
|
|
152
|
+
disabled && "cursor-not-allowed opacity-50",
|
|
153
|
+
className
|
|
154
|
+
),
|
|
155
|
+
children: [
|
|
156
|
+
/* @__PURE__ */ jsx(
|
|
157
|
+
"input",
|
|
158
|
+
{
|
|
159
|
+
ref: inputRef,
|
|
160
|
+
type: "file",
|
|
161
|
+
accept,
|
|
162
|
+
multiple,
|
|
163
|
+
disabled,
|
|
164
|
+
...capture ? { capture } : {},
|
|
165
|
+
className: "hidden",
|
|
166
|
+
onClick: (event) => event.stopPropagation(),
|
|
167
|
+
onChange: (event) => {
|
|
168
|
+
emit(event.target.files);
|
|
169
|
+
event.target.value = "";
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
),
|
|
173
|
+
children,
|
|
174
|
+
shortcut && !coarse && /* @__PURE__ */ jsx("span", { className: "mt-2 block", children: /* @__PURE__ */ jsx(Kbd, { children: formatShortcut(shortcut) }) })
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
};
|
|
179
|
+
var UploadCloudIcon = ({ className }) => /* @__PURE__ */ jsxs(
|
|
180
|
+
"svg",
|
|
181
|
+
{
|
|
182
|
+
viewBox: "0 0 24 24",
|
|
183
|
+
fill: "none",
|
|
184
|
+
stroke: "currentColor",
|
|
185
|
+
strokeWidth: "1.75",
|
|
186
|
+
strokeLinecap: "round",
|
|
187
|
+
strokeLinejoin: "round",
|
|
188
|
+
className,
|
|
189
|
+
"aria-hidden": true,
|
|
190
|
+
children: [
|
|
191
|
+
/* @__PURE__ */ jsx("path", { d: "M4 14.9A7 7 0 1 1 15.7 8h1.8a4.5 4.5 0 0 1 2.5 8.2" }),
|
|
192
|
+
/* @__PURE__ */ jsx("path", { d: "M12 12v9" }),
|
|
193
|
+
/* @__PURE__ */ jsx("path", { d: "m8 16 4-4 4 4" })
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
var DownloadIcon = ({ className }) => /* @__PURE__ */ jsxs(
|
|
198
|
+
"svg",
|
|
199
|
+
{
|
|
200
|
+
viewBox: "0 0 24 24",
|
|
201
|
+
fill: "none",
|
|
202
|
+
stroke: "currentColor",
|
|
203
|
+
strokeWidth: "1.75",
|
|
204
|
+
strokeLinecap: "round",
|
|
205
|
+
strokeLinejoin: "round",
|
|
206
|
+
className,
|
|
207
|
+
"aria-hidden": true,
|
|
208
|
+
children: [
|
|
209
|
+
/* @__PURE__ */ jsx("path", { d: "M12 3v12" }),
|
|
210
|
+
/* @__PURE__ */ jsx("path", { d: "m7 11 5 5 5-5" }),
|
|
211
|
+
/* @__PURE__ */ jsx("path", { d: "M4 20h16" })
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
);
|
|
215
|
+
var ExternalLinkIcon = ({ className }) => /* @__PURE__ */ jsxs(
|
|
216
|
+
"svg",
|
|
217
|
+
{
|
|
218
|
+
viewBox: "0 0 24 24",
|
|
219
|
+
fill: "none",
|
|
220
|
+
stroke: "currentColor",
|
|
221
|
+
strokeWidth: "1.75",
|
|
222
|
+
strokeLinecap: "round",
|
|
223
|
+
strokeLinejoin: "round",
|
|
224
|
+
className,
|
|
225
|
+
"aria-hidden": true,
|
|
226
|
+
children: [
|
|
227
|
+
/* @__PURE__ */ jsx("path", { d: "M14 4h6v6" }),
|
|
228
|
+
/* @__PURE__ */ jsx("path", { d: "M20 4 10 14" }),
|
|
229
|
+
/* @__PURE__ */ jsx("path", { d: "M18 14v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h5" })
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
var FileWarningIcon = ({ className }) => /* @__PURE__ */ jsxs(
|
|
234
|
+
"svg",
|
|
235
|
+
{
|
|
236
|
+
viewBox: "0 0 24 24",
|
|
237
|
+
fill: "none",
|
|
238
|
+
stroke: "currentColor",
|
|
239
|
+
strokeWidth: "1.5",
|
|
240
|
+
strokeLinecap: "round",
|
|
241
|
+
strokeLinejoin: "round",
|
|
242
|
+
className,
|
|
243
|
+
"aria-hidden": true,
|
|
244
|
+
children: [
|
|
245
|
+
/* @__PURE__ */ jsx("path", { d: "M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z" }),
|
|
246
|
+
/* @__PURE__ */ jsx("path", { d: "M14 3v5h5" }),
|
|
247
|
+
/* @__PURE__ */ jsx("path", { d: "M12 11v4" }),
|
|
248
|
+
/* @__PURE__ */ jsx("path", { d: "M12 18h.01" })
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
var sequence = 0;
|
|
253
|
+
var stack = [];
|
|
254
|
+
var useOverlayLayer = (active2) => {
|
|
255
|
+
const idRef = useRef(0);
|
|
256
|
+
if (idRef.current === 0) idRef.current = ++sequence;
|
|
257
|
+
useEffect(() => {
|
|
258
|
+
if (!active2) return;
|
|
259
|
+
const id = idRef.current;
|
|
260
|
+
stack.push(id);
|
|
261
|
+
return () => {
|
|
262
|
+
const at = stack.lastIndexOf(id);
|
|
263
|
+
if (at !== -1) stack.splice(at, 1);
|
|
264
|
+
};
|
|
265
|
+
}, [active2]);
|
|
266
|
+
return useMemo(
|
|
267
|
+
() => ({ isTopmost: () => stack[stack.length - 1] === idRef.current }),
|
|
268
|
+
[]
|
|
269
|
+
);
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// src/ui/scrollLock.ts
|
|
273
|
+
var KEY = /* @__PURE__ */ Symbol.for("uploaderkit.bodyScrollLock");
|
|
274
|
+
var state = () => {
|
|
275
|
+
const host = globalThis;
|
|
276
|
+
return host[KEY] ??= { count: 0, saved: "" };
|
|
277
|
+
};
|
|
278
|
+
var lockBodyScroll = () => {
|
|
279
|
+
const lock = state();
|
|
280
|
+
if (lock.count === 0) {
|
|
281
|
+
lock.saved = document.body.style.overflow;
|
|
282
|
+
document.body.style.overflow = "hidden";
|
|
283
|
+
}
|
|
284
|
+
lock.count += 1;
|
|
285
|
+
};
|
|
286
|
+
var unlockBodyScroll = () => {
|
|
287
|
+
const lock = state();
|
|
288
|
+
if (lock.count === 0) return;
|
|
289
|
+
lock.count -= 1;
|
|
290
|
+
if (lock.count === 0) document.body.style.overflow = lock.saved;
|
|
291
|
+
};
|
|
292
|
+
var FOCUSABLE = 'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])';
|
|
293
|
+
var useFocusTrap = (active2, ref) => {
|
|
294
|
+
useEffect(() => {
|
|
295
|
+
if (!active2) return;
|
|
296
|
+
const restoreTo = document.activeElement;
|
|
297
|
+
const panel = ref.current;
|
|
298
|
+
const initial = panel?.querySelector("[data-autofocus]") ?? panel;
|
|
299
|
+
initial?.focus({ preventScroll: true });
|
|
300
|
+
const onKeyDown = (event) => {
|
|
301
|
+
if (event.key !== "Tab" || !ref.current) return;
|
|
302
|
+
const focusables = Array.from(
|
|
303
|
+
ref.current.querySelectorAll(FOCUSABLE)
|
|
304
|
+
).filter((el) => el.offsetParent !== null);
|
|
305
|
+
if (focusables.length === 0) {
|
|
306
|
+
event.preventDefault();
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const first = focusables[0];
|
|
310
|
+
const last = focusables[focusables.length - 1];
|
|
311
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
312
|
+
event.preventDefault();
|
|
313
|
+
last.focus();
|
|
314
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
315
|
+
event.preventDefault();
|
|
316
|
+
first.focus();
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
document.addEventListener("keydown", onKeyDown);
|
|
320
|
+
return () => {
|
|
321
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
322
|
+
restoreTo?.focus({ preventScroll: true });
|
|
323
|
+
};
|
|
324
|
+
}, [active2, ref]);
|
|
325
|
+
};
|
|
326
|
+
var OVERLAY_ANIMATION_MS = 200;
|
|
327
|
+
var useOverlayTransition = (open) => {
|
|
328
|
+
const [entered, setEntered] = useState(false);
|
|
329
|
+
const [exiting, setExiting] = useState(false);
|
|
330
|
+
const wasOpen = useRef(false);
|
|
331
|
+
useEffect(() => {
|
|
332
|
+
if (open) {
|
|
333
|
+
wasOpen.current = true;
|
|
334
|
+
setExiting(false);
|
|
335
|
+
const raf = requestAnimationFrame(() => setEntered(true));
|
|
336
|
+
return () => cancelAnimationFrame(raf);
|
|
337
|
+
}
|
|
338
|
+
setEntered(false);
|
|
339
|
+
if (!wasOpen.current) return;
|
|
340
|
+
wasOpen.current = false;
|
|
341
|
+
setExiting(true);
|
|
342
|
+
const timer = setTimeout(() => setExiting(false), OVERLAY_ANIMATION_MS);
|
|
343
|
+
return () => clearTimeout(timer);
|
|
344
|
+
}, [open]);
|
|
345
|
+
return { mounted: open || exiting, entered };
|
|
346
|
+
};
|
|
347
|
+
var EMPTY_STATE_CLASS = "mx-4 flex max-w-sm flex-col items-center text-center";
|
|
348
|
+
var EMPTY_TITLE_CLASS = "text-sm font-medium text-white";
|
|
349
|
+
var EMPTY_HINT_CLASS = "mt-1.5 text-xs leading-relaxed text-white/60";
|
|
350
|
+
var EMPTY_PRIMARY_CLASS = "focus-visible:ring-ui-ring rounded-ui cursor-pointer bg-white/20 px-4 py-2 text-sm text-white transition-colors hover:bg-white/30 focus-visible:ring-2 focus-visible:outline-none";
|
|
351
|
+
var EMPTY_SECONDARY_CLASS = "focus-visible:ring-ui-ring rounded-ui cursor-pointer border border-white/20 px-4 py-2 text-sm text-white/80 transition-colors hover:bg-white/10 hover:text-white focus-visible:ring-2 focus-visible:outline-none";
|
|
352
|
+
var ACTION_CLASS = "focus-visible:ring-ui-ring rounded-ui inline-flex shrink-0 cursor-pointer items-center gap-1.5 bg-white/10 px-3 py-1.5 text-xs whitespace-nowrap text-white transition-colors hover:bg-white/20 focus-visible:ring-2 focus-visible:outline-none";
|
|
353
|
+
var kindOf = (file) => {
|
|
354
|
+
const mime = file.mimeType || getMimeType(file.fileName ?? file.url);
|
|
355
|
+
if (mime.startsWith("image/")) return "image";
|
|
356
|
+
if (mime === "application/pdf") return "pdf";
|
|
357
|
+
return "other";
|
|
358
|
+
};
|
|
359
|
+
var isIosWebkit = () => {
|
|
360
|
+
const ua = navigator.userAgent;
|
|
361
|
+
return /iPad|iPhone|iPod/.test(ua) || ua.includes("Macintosh") && navigator.maxTouchPoints > 1;
|
|
362
|
+
};
|
|
363
|
+
var browserEmbedsPdf = () => {
|
|
364
|
+
if (typeof navigator === "undefined") return false;
|
|
365
|
+
if (navigator.pdfViewerEnabled === false) return false;
|
|
366
|
+
return !isIosWebkit();
|
|
367
|
+
};
|
|
368
|
+
var Chevron = ({ direction }) => /* @__PURE__ */ jsx(
|
|
369
|
+
"svg",
|
|
370
|
+
{
|
|
371
|
+
viewBox: "0 0 24 24",
|
|
372
|
+
fill: "none",
|
|
373
|
+
stroke: "currentColor",
|
|
374
|
+
strokeWidth: "2.5",
|
|
375
|
+
strokeLinecap: "round",
|
|
376
|
+
strokeLinejoin: "round",
|
|
377
|
+
className: "h-5 w-5",
|
|
378
|
+
"aria-hidden": true,
|
|
379
|
+
children: direction === "left" ? /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) : /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
var FileViewer = ({
|
|
383
|
+
file,
|
|
384
|
+
onClose,
|
|
385
|
+
files,
|
|
386
|
+
resolveUrl,
|
|
387
|
+
renderError,
|
|
388
|
+
labels
|
|
389
|
+
}) => {
|
|
390
|
+
const copy = useUploaderLabels(labels);
|
|
391
|
+
const coarse = useCoarsePointer();
|
|
392
|
+
const [url, setUrl] = useState(null);
|
|
393
|
+
const [failed, setFailed] = useState(false);
|
|
394
|
+
const [attempt, setAttempt] = useState(0);
|
|
395
|
+
const retry = useCallback(() => setAttempt((previous) => previous + 1), []);
|
|
396
|
+
const panelRef = useRef(null);
|
|
397
|
+
const open = file !== null;
|
|
398
|
+
const { mounted, entered } = useOverlayTransition(open);
|
|
399
|
+
const layer = useOverlayLayer(open);
|
|
400
|
+
useFocusTrap(open, panelRef);
|
|
401
|
+
const list = files && files.length > 0 ? files : null;
|
|
402
|
+
const [cursor, setCursor] = useState(0);
|
|
403
|
+
useEffect(() => {
|
|
404
|
+
if (!file || !list) return;
|
|
405
|
+
const index = list.findIndex((entry) => entry.url === file.url);
|
|
406
|
+
setCursor(index >= 0 ? index : 0);
|
|
407
|
+
}, [file]);
|
|
408
|
+
const current = open ? list ? list[cursor] ?? file : file : null;
|
|
409
|
+
const lastViewRef = useRef(null);
|
|
410
|
+
if (current) lastViewRef.current = current;
|
|
411
|
+
const navigate = useCallback(
|
|
412
|
+
(delta) => {
|
|
413
|
+
if (!list) return;
|
|
414
|
+
setCursor(
|
|
415
|
+
(previous) => Math.min(list.length - 1, Math.max(0, previous + delta))
|
|
416
|
+
);
|
|
417
|
+
},
|
|
418
|
+
[list]
|
|
419
|
+
);
|
|
420
|
+
const objectUrlRef = useRef(null);
|
|
421
|
+
const releaseObjectUrl = useCallback(() => {
|
|
422
|
+
if (objectUrlRef.current) URL.revokeObjectURL(objectUrlRef.current);
|
|
423
|
+
objectUrlRef.current = null;
|
|
424
|
+
}, []);
|
|
425
|
+
const resolveRef = useRef(resolveUrl);
|
|
426
|
+
resolveRef.current = resolveUrl;
|
|
427
|
+
const currentUrl = current?.url;
|
|
428
|
+
useEffect(() => {
|
|
429
|
+
releaseObjectUrl();
|
|
430
|
+
if (!current) {
|
|
431
|
+
setUrl(null);
|
|
432
|
+
setFailed(false);
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
let alive = true;
|
|
436
|
+
setUrl(null);
|
|
437
|
+
setFailed(false);
|
|
438
|
+
void (async () => {
|
|
439
|
+
try {
|
|
440
|
+
const resolve = resolveRef.current;
|
|
441
|
+
const resolved = resolve ? await resolve(current) : current.url;
|
|
442
|
+
const minted = resolved.startsWith("blob:");
|
|
443
|
+
if (!alive) {
|
|
444
|
+
if (minted) URL.revokeObjectURL(resolved);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (minted) objectUrlRef.current = resolved;
|
|
448
|
+
setUrl(resolved);
|
|
449
|
+
} catch {
|
|
450
|
+
if (alive) {
|
|
451
|
+
setUrl(null);
|
|
452
|
+
setFailed(true);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
})();
|
|
456
|
+
return () => {
|
|
457
|
+
alive = false;
|
|
458
|
+
};
|
|
459
|
+
}, [currentUrl, attempt]);
|
|
460
|
+
useEffect(() => releaseObjectUrl, [releaseObjectUrl]);
|
|
461
|
+
const closeRef = useRef(onClose);
|
|
462
|
+
closeRef.current = onClose;
|
|
463
|
+
const downloadRef = useRef(null);
|
|
464
|
+
const openTabRef = useRef(null);
|
|
465
|
+
useEffect(() => {
|
|
466
|
+
if (!open) return;
|
|
467
|
+
const onKey = (event) => {
|
|
468
|
+
if (event.metaKey || event.ctrlKey || event.altKey) return;
|
|
469
|
+
if (event.key === "Escape") {
|
|
470
|
+
if (!layer.isTopmost()) return;
|
|
471
|
+
event.stopPropagation();
|
|
472
|
+
closeRef.current();
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (event.key === "ArrowLeft") navigate(-1);
|
|
476
|
+
if (event.key === "ArrowRight") navigate(1);
|
|
477
|
+
const key = event.key.toLowerCase();
|
|
478
|
+
if (key === "d") downloadRef.current?.click();
|
|
479
|
+
if (key === "o") openTabRef.current?.click();
|
|
480
|
+
};
|
|
481
|
+
window.addEventListener("keydown", onKey, true);
|
|
482
|
+
lockBodyScroll();
|
|
483
|
+
return () => {
|
|
484
|
+
window.removeEventListener("keydown", onKey, true);
|
|
485
|
+
unlockBodyScroll();
|
|
486
|
+
};
|
|
487
|
+
}, [open, navigate, layer]);
|
|
488
|
+
const [canEmbedPdf] = useState(browserEmbedsPdf);
|
|
489
|
+
const view = current ?? lastViewRef.current;
|
|
490
|
+
if (!mounted || !view) return null;
|
|
491
|
+
const kind = kindOf(view);
|
|
492
|
+
const showsPdf = kind === "pdf" && canEmbedPdf && !failed && !!url;
|
|
493
|
+
const hasGallery = list !== null && list.length > 1;
|
|
494
|
+
const arrow = "absolute top-1/2 z-10 flex h-11 w-11 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-white/10 text-white backdrop-blur-sm transition-all duration-200 hover:bg-white/25 focus-visible:ring-2 focus-visible:ring-ui-ring focus-visible:outline-none disabled:pointer-events-none disabled:opacity-25";
|
|
495
|
+
return createPortal(
|
|
496
|
+
/* @__PURE__ */ jsxs(
|
|
497
|
+
"div",
|
|
498
|
+
{
|
|
499
|
+
className: cn(
|
|
500
|
+
"fixed inset-0 z-1000 flex flex-col bg-black/85 transition-opacity duration-200 ease-out",
|
|
501
|
+
entered ? "opacity-100" : "opacity-0",
|
|
502
|
+
!open && "pointer-events-none"
|
|
503
|
+
),
|
|
504
|
+
onClick: onClose,
|
|
505
|
+
role: "dialog",
|
|
506
|
+
"aria-modal": "true",
|
|
507
|
+
"aria-label": view.fileName ?? copy.filePreview,
|
|
508
|
+
children: [
|
|
509
|
+
/* @__PURE__ */ jsxs(
|
|
510
|
+
"div",
|
|
511
|
+
{
|
|
512
|
+
className: "flex items-center justify-between gap-2 px-3 py-2.5 sm:gap-3 sm:px-4 sm:py-3",
|
|
513
|
+
onClick: (event) => event.stopPropagation(),
|
|
514
|
+
children: [
|
|
515
|
+
/* @__PURE__ */ jsx("p", { className: "min-w-0 flex-1 truncate text-xs font-medium text-white sm:text-sm", children: view.fileName ?? copy.filePreview }),
|
|
516
|
+
hasGallery && /* @__PURE__ */ jsxs("span", { className: "shrink-0 rounded-full bg-white/10 px-2.5 py-1 text-[11px] font-medium text-white tabular-nums", children: [
|
|
517
|
+
cursor + 1,
|
|
518
|
+
" / ",
|
|
519
|
+
list.length
|
|
520
|
+
] }),
|
|
521
|
+
url && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
522
|
+
/* @__PURE__ */ jsx(
|
|
523
|
+
"a",
|
|
524
|
+
{
|
|
525
|
+
ref: downloadRef,
|
|
526
|
+
href: url,
|
|
527
|
+
download: view.fileName,
|
|
528
|
+
"aria-label": copy.downloadShort,
|
|
529
|
+
title: copy.downloadShort,
|
|
530
|
+
className: ACTION_CLASS,
|
|
531
|
+
children: coarse ? /* @__PURE__ */ jsx(DownloadIcon, { className: "h-4 w-4" }) : copy.downloadShort
|
|
532
|
+
}
|
|
533
|
+
),
|
|
534
|
+
/* @__PURE__ */ jsx(
|
|
535
|
+
"a",
|
|
536
|
+
{
|
|
537
|
+
ref: openTabRef,
|
|
538
|
+
href: url,
|
|
539
|
+
target: "_blank",
|
|
540
|
+
rel: "noreferrer",
|
|
541
|
+
"aria-label": copy.openInTab,
|
|
542
|
+
title: copy.openInTab,
|
|
543
|
+
className: ACTION_CLASS,
|
|
544
|
+
children: coarse ? /* @__PURE__ */ jsx(ExternalLinkIcon, { className: "h-4 w-4" }) : copy.openInTab
|
|
545
|
+
}
|
|
546
|
+
)
|
|
547
|
+
] }),
|
|
548
|
+
/* @__PURE__ */ jsx(
|
|
549
|
+
"button",
|
|
550
|
+
{
|
|
551
|
+
type: "button",
|
|
552
|
+
onClick: onClose,
|
|
553
|
+
"aria-label": copy.close,
|
|
554
|
+
title: copy.close,
|
|
555
|
+
className: ACTION_CLASS,
|
|
556
|
+
children: coarse ? "\u2715" : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
557
|
+
"\u2715 ",
|
|
558
|
+
copy.close
|
|
559
|
+
] })
|
|
560
|
+
}
|
|
561
|
+
)
|
|
562
|
+
]
|
|
563
|
+
}
|
|
564
|
+
),
|
|
565
|
+
/* @__PURE__ */ jsxs(
|
|
566
|
+
"div",
|
|
567
|
+
{
|
|
568
|
+
ref: panelRef,
|
|
569
|
+
tabIndex: -1,
|
|
570
|
+
className: cn(
|
|
571
|
+
"relative flex min-h-0 flex-1 items-center justify-center p-3 outline-none sm:p-4",
|
|
572
|
+
showsPdf && "items-stretch"
|
|
573
|
+
),
|
|
574
|
+
onClick: (event) => event.stopPropagation(),
|
|
575
|
+
children: [
|
|
576
|
+
hasGallery && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
577
|
+
/* @__PURE__ */ jsx(
|
|
578
|
+
"button",
|
|
579
|
+
{
|
|
580
|
+
type: "button",
|
|
581
|
+
onClick: () => navigate(-1),
|
|
582
|
+
disabled: cursor === 0,
|
|
583
|
+
"aria-label": copy.previous,
|
|
584
|
+
className: cn(arrow, "left-2 sm:left-4"),
|
|
585
|
+
children: /* @__PURE__ */ jsx(Chevron, { direction: "left" })
|
|
586
|
+
}
|
|
587
|
+
),
|
|
588
|
+
/* @__PURE__ */ jsx(
|
|
589
|
+
"button",
|
|
590
|
+
{
|
|
591
|
+
type: "button",
|
|
592
|
+
onClick: () => navigate(1),
|
|
593
|
+
disabled: cursor === list.length - 1,
|
|
594
|
+
"aria-label": copy.next,
|
|
595
|
+
className: cn(arrow, "right-2 sm:right-4"),
|
|
596
|
+
children: /* @__PURE__ */ jsx(Chevron, { direction: "right" })
|
|
597
|
+
}
|
|
598
|
+
)
|
|
599
|
+
] }),
|
|
600
|
+
failed ? renderError?.({ file: view, retry }) ?? /* @__PURE__ */ jsxs("div", { className: EMPTY_STATE_CLASS, children: [
|
|
601
|
+
/* @__PURE__ */ jsx("span", { className: "mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-white/5 text-white/70 ring-1 ring-white/10", children: /* @__PURE__ */ jsx(FileWarningIcon, { className: "h-8 w-8" }) }),
|
|
602
|
+
/* @__PURE__ */ jsx("p", { className: EMPTY_TITLE_CLASS, children: copy.viewerError }),
|
|
603
|
+
/* @__PURE__ */ jsx("p", { className: EMPTY_HINT_CLASS, children: copy.viewerErrorHint }),
|
|
604
|
+
view.fileName && /* @__PURE__ */ jsx("p", { className: "mt-3 max-w-full truncate font-mono text-[11px] text-white/40", children: view.fileName }),
|
|
605
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-5 flex flex-wrap justify-center gap-2", children: [
|
|
606
|
+
/* @__PURE__ */ jsx(
|
|
607
|
+
"button",
|
|
608
|
+
{
|
|
609
|
+
type: "button",
|
|
610
|
+
onClick: retry,
|
|
611
|
+
className: EMPTY_PRIMARY_CLASS,
|
|
612
|
+
children: copy.viewerRetry
|
|
613
|
+
}
|
|
614
|
+
),
|
|
615
|
+
url && /* @__PURE__ */ jsx(
|
|
616
|
+
"a",
|
|
617
|
+
{
|
|
618
|
+
href: url,
|
|
619
|
+
download: view.fileName,
|
|
620
|
+
className: EMPTY_SECONDARY_CLASS,
|
|
621
|
+
children: copy.downloadShort
|
|
622
|
+
}
|
|
623
|
+
)
|
|
624
|
+
] })
|
|
625
|
+
] }) : !url ? /* @__PURE__ */ jsx("p", { className: "text-sm text-white/70", children: copy.viewerLoading }) : kind === "image" ? /* @__PURE__ */ jsx(
|
|
626
|
+
"img",
|
|
627
|
+
{
|
|
628
|
+
src: url,
|
|
629
|
+
alt: view.fileName ?? "",
|
|
630
|
+
onError: () => setFailed(true),
|
|
631
|
+
className: cn(
|
|
632
|
+
"rounded-ui-lg max-h-full max-w-full object-contain shadow-2xl",
|
|
633
|
+
"transition-transform duration-200 ease-out",
|
|
634
|
+
entered ? "scale-100" : "scale-[0.97]"
|
|
635
|
+
)
|
|
636
|
+
}
|
|
637
|
+
) : showsPdf ? /* @__PURE__ */ jsx(
|
|
638
|
+
"iframe",
|
|
639
|
+
{
|
|
640
|
+
src: url,
|
|
641
|
+
title: view.fileName ?? "PDF",
|
|
642
|
+
onError: () => setFailed(true),
|
|
643
|
+
className: "rounded-ui-lg h-full w-full max-w-4xl bg-white shadow-2xl"
|
|
644
|
+
}
|
|
645
|
+
) : /* @__PURE__ */ jsxs("div", { className: EMPTY_STATE_CLASS, children: [
|
|
646
|
+
/* @__PURE__ */ jsx("span", { className: "mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-white/5 text-white/70 ring-1 ring-white/10", children: /* @__PURE__ */ jsx(FileWarningIcon, { className: "h-8 w-8" }) }),
|
|
647
|
+
/* @__PURE__ */ jsx("p", { className: EMPTY_TITLE_CLASS, children: copy.noPreview }),
|
|
648
|
+
view.fileName && /* @__PURE__ */ jsx("p", { className: "mt-3 max-w-full truncate font-mono text-[11px] text-white/40", children: view.fileName }),
|
|
649
|
+
/* @__PURE__ */ jsx(
|
|
650
|
+
"a",
|
|
651
|
+
{
|
|
652
|
+
href: url,
|
|
653
|
+
download: view.fileName,
|
|
654
|
+
target: "_blank",
|
|
655
|
+
rel: "noreferrer",
|
|
656
|
+
className: cn(EMPTY_PRIMARY_CLASS, "mt-5 inline-block"),
|
|
657
|
+
children: copy.download(view.fileName ?? copy.filePreview)
|
|
658
|
+
}
|
|
659
|
+
)
|
|
660
|
+
] })
|
|
661
|
+
]
|
|
662
|
+
}
|
|
663
|
+
),
|
|
664
|
+
!coarse && /* @__PURE__ */ jsxs(
|
|
665
|
+
"div",
|
|
666
|
+
{
|
|
667
|
+
className: "flex items-center justify-center gap-4 pb-3 text-[11px] text-white/60",
|
|
668
|
+
onClick: (event) => event.stopPropagation(),
|
|
669
|
+
children: [
|
|
670
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
671
|
+
/* @__PURE__ */ jsx(Kbd, { children: "Esc" }),
|
|
672
|
+
" ",
|
|
673
|
+
copy.close
|
|
674
|
+
] }),
|
|
675
|
+
url && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
676
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
677
|
+
/* @__PURE__ */ jsx(Kbd, { children: "D" }),
|
|
678
|
+
" ",
|
|
679
|
+
copy.downloadShort
|
|
680
|
+
] }),
|
|
681
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
682
|
+
/* @__PURE__ */ jsx(Kbd, { children: "O" }),
|
|
683
|
+
" ",
|
|
684
|
+
copy.openInTab
|
|
685
|
+
] })
|
|
686
|
+
] }),
|
|
687
|
+
hasGallery && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
688
|
+
/* @__PURE__ */ jsx(Kbd, { children: "\u2190" }),
|
|
689
|
+
/* @__PURE__ */ jsx(Kbd, { children: "\u2192" }),
|
|
690
|
+
" ",
|
|
691
|
+
copy.previous,
|
|
692
|
+
" / ",
|
|
693
|
+
copy.next
|
|
694
|
+
] })
|
|
695
|
+
]
|
|
696
|
+
}
|
|
697
|
+
)
|
|
698
|
+
]
|
|
699
|
+
}
|
|
700
|
+
),
|
|
701
|
+
document.body
|
|
702
|
+
);
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
export { Dropzone, FileViewer, UploadCloudIcon, cn, lockBodyScroll, unlockBodyScroll, useCoarsePointer, useFocusTrap, useOverlayLayer, useOverlayTransition };
|
|
706
|
+
//# sourceMappingURL=chunk-PDKAF4GX.js.map
|
|
707
|
+
//# sourceMappingURL=chunk-PDKAF4GX.js.map
|