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
package/dist/presets.js
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { cn, Dropzone, FileViewer } from './chunk-PDKAF4GX.js';
|
|
2
|
+
import { useUploaderLabels, useUploader } from './chunk-H7BRW5IY.js';
|
|
3
|
+
import './chunk-T5YZG6JW.js';
|
|
4
|
+
import { useRef, useState, useEffect } from 'react';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
import { createPortal } from 'react-dom';
|
|
7
|
+
|
|
8
|
+
var AvatarUploader = ({
|
|
9
|
+
scopes,
|
|
10
|
+
scope,
|
|
11
|
+
entityId,
|
|
12
|
+
strategy,
|
|
13
|
+
rename,
|
|
14
|
+
src,
|
|
15
|
+
fallback,
|
|
16
|
+
size = 96,
|
|
17
|
+
onUploaded,
|
|
18
|
+
onRemove,
|
|
19
|
+
onError,
|
|
20
|
+
alt = "",
|
|
21
|
+
disabled = false,
|
|
22
|
+
labels,
|
|
23
|
+
className
|
|
24
|
+
}) => {
|
|
25
|
+
const copy = useUploaderLabels(labels);
|
|
26
|
+
const input = useRef(null);
|
|
27
|
+
const [dropping, setDropping] = useState(false);
|
|
28
|
+
const depth = useRef(0);
|
|
29
|
+
const uploader = useUploader({
|
|
30
|
+
scopes,
|
|
31
|
+
scope,
|
|
32
|
+
entityId,
|
|
33
|
+
strategy,
|
|
34
|
+
uploadOn: "select",
|
|
35
|
+
maxFiles: 1,
|
|
36
|
+
...rename ? { rename } : {},
|
|
37
|
+
onUploaded: (stored) => {
|
|
38
|
+
if (stored[0]) onUploaded(stored[0]);
|
|
39
|
+
},
|
|
40
|
+
onError,
|
|
41
|
+
labels
|
|
42
|
+
});
|
|
43
|
+
const current = uploader.files[0];
|
|
44
|
+
const uploading = current?.status === "uploading";
|
|
45
|
+
const failed = current?.status === "error" ? current.error : void 0;
|
|
46
|
+
const shown = current?.preview ?? src ?? null;
|
|
47
|
+
const locked = disabled || uploading;
|
|
48
|
+
const onDrop = (event) => {
|
|
49
|
+
event.preventDefault();
|
|
50
|
+
depth.current = 0;
|
|
51
|
+
setDropping(false);
|
|
52
|
+
if (locked) return;
|
|
53
|
+
const file = event.dataTransfer.files[0];
|
|
54
|
+
if (file) void uploader.addFiles([file]);
|
|
55
|
+
};
|
|
56
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("inline-flex flex-col items-center gap-2", className), children: [
|
|
57
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", style: { width: size, height: size }, children: [
|
|
58
|
+
/* @__PURE__ */ jsxs(
|
|
59
|
+
"button",
|
|
60
|
+
{
|
|
61
|
+
type: "button",
|
|
62
|
+
onClick: () => !locked && input.current?.click(),
|
|
63
|
+
onDragEnter: () => {
|
|
64
|
+
depth.current += 1;
|
|
65
|
+
if (!locked) setDropping(true);
|
|
66
|
+
},
|
|
67
|
+
onDragOver: (event) => event.preventDefault(),
|
|
68
|
+
onDragLeave: () => {
|
|
69
|
+
depth.current = Math.max(0, depth.current - 1);
|
|
70
|
+
if (depth.current === 0) setDropping(false);
|
|
71
|
+
},
|
|
72
|
+
onDrop,
|
|
73
|
+
disabled: locked,
|
|
74
|
+
"aria-busy": uploading,
|
|
75
|
+
"aria-label": copy.avatarChange,
|
|
76
|
+
className: cn(
|
|
77
|
+
"group ring-ui-border focus-visible:ring-ui-ring relative flex h-full w-full overflow-hidden rounded-full ring-2 ring-offset-2 transition-all duration-200 outline-none",
|
|
78
|
+
locked ? "cursor-not-allowed" : "hover:ring-ui-primary cursor-pointer",
|
|
79
|
+
dropping && "ring-ui-primary cursor-copy ring-4"
|
|
80
|
+
),
|
|
81
|
+
children: [
|
|
82
|
+
shown ? /* @__PURE__ */ jsx("img", { src: shown, alt, className: "h-full w-full object-cover" }) : /* @__PURE__ */ jsx("span", { className: "bg-ui-surface-muted text-ui-muted flex h-full w-full items-center justify-center text-xl font-semibold", children: fallback }),
|
|
83
|
+
dropping ? /* @__PURE__ */ jsx("span", { className: "bg-ui-primary/70 text-ui-primary-fg absolute inset-0 flex items-center justify-center text-xs font-semibold", children: copy.avatarDropHere }) : uploading ? /* @__PURE__ */ jsxs("span", { className: "absolute inset-0 flex items-center justify-center bg-black/50 text-xs font-medium text-white", children: [
|
|
84
|
+
current.progress,
|
|
85
|
+
"%"
|
|
86
|
+
] }) : locked ? null : /* @__PURE__ */ jsx("span", { className: "absolute inset-0 flex items-center justify-center bg-black/50 text-xs font-medium text-white opacity-0 transition-opacity duration-200 group-hover:opacity-100", children: copy.avatarChange })
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
uploading ? /* @__PURE__ */ jsx(
|
|
91
|
+
"svg",
|
|
92
|
+
{
|
|
93
|
+
viewBox: "0 0 100 100",
|
|
94
|
+
className: "pointer-events-none absolute -inset-1.5 -rotate-90",
|
|
95
|
+
"aria-hidden": true,
|
|
96
|
+
children: /* @__PURE__ */ jsx(
|
|
97
|
+
"circle",
|
|
98
|
+
{
|
|
99
|
+
cx: "50",
|
|
100
|
+
cy: "50",
|
|
101
|
+
r: "48",
|
|
102
|
+
pathLength: 100,
|
|
103
|
+
fill: "none",
|
|
104
|
+
strokeWidth: 4,
|
|
105
|
+
strokeLinecap: "round",
|
|
106
|
+
strokeDasharray: 100,
|
|
107
|
+
strokeDashoffset: 100 - (current?.progress ?? 0),
|
|
108
|
+
className: "stroke-ui-primary transition-[stroke-dashoffset] duration-200"
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
) : null
|
|
113
|
+
] }),
|
|
114
|
+
failed ? /* @__PURE__ */ jsx("p", { className: "text-ui-danger max-w-[16rem] text-center text-xs", children: failed }) : onRemove && src && !uploading && !disabled ? /* @__PURE__ */ jsx(
|
|
115
|
+
"button",
|
|
116
|
+
{
|
|
117
|
+
type: "button",
|
|
118
|
+
onClick: onRemove,
|
|
119
|
+
className: "text-ui-danger cursor-pointer text-xs hover:underline",
|
|
120
|
+
children: copy.avatarRemove
|
|
121
|
+
}
|
|
122
|
+
) : null,
|
|
123
|
+
/* @__PURE__ */ jsx(
|
|
124
|
+
"input",
|
|
125
|
+
{
|
|
126
|
+
ref: input,
|
|
127
|
+
type: "file",
|
|
128
|
+
accept: uploader.accept,
|
|
129
|
+
className: "hidden",
|
|
130
|
+
disabled: locked,
|
|
131
|
+
onChange: (event) => {
|
|
132
|
+
const file = event.target.files?.[0];
|
|
133
|
+
if (file) void uploader.addFiles([file]);
|
|
134
|
+
event.target.value = "";
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
] });
|
|
139
|
+
};
|
|
140
|
+
var DocumentGlyph = () => /* @__PURE__ */ jsxs(
|
|
141
|
+
"svg",
|
|
142
|
+
{
|
|
143
|
+
width: "40",
|
|
144
|
+
height: "40",
|
|
145
|
+
viewBox: "0 0 24 24",
|
|
146
|
+
fill: "none",
|
|
147
|
+
stroke: "currentColor",
|
|
148
|
+
strokeWidth: "1.6",
|
|
149
|
+
strokeLinecap: "round",
|
|
150
|
+
strokeLinejoin: "round",
|
|
151
|
+
"aria-hidden": true,
|
|
152
|
+
children: [
|
|
153
|
+
/* @__PURE__ */ jsx("path", { d: "M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z" }),
|
|
154
|
+
/* @__PURE__ */ jsx("path", { d: "M14 3v5h5" }),
|
|
155
|
+
/* @__PURE__ */ jsx("path", { d: "M12 18v-6" }),
|
|
156
|
+
/* @__PURE__ */ jsx("path", { d: "m9.5 14.5 2.5-2.5 2.5 2.5" })
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
var DropAnywhereOverlay = ({
|
|
161
|
+
open,
|
|
162
|
+
icon,
|
|
163
|
+
title,
|
|
164
|
+
hint,
|
|
165
|
+
labels,
|
|
166
|
+
className
|
|
167
|
+
}) => {
|
|
168
|
+
const copy = useUploaderLabels(labels);
|
|
169
|
+
if (!open || typeof document === "undefined") return null;
|
|
170
|
+
return createPortal(
|
|
171
|
+
/* @__PURE__ */ jsx(
|
|
172
|
+
"div",
|
|
173
|
+
{
|
|
174
|
+
"aria-hidden": true,
|
|
175
|
+
className: "animate-ui-fade-in bg-ui-overlay pointer-events-none fixed inset-0 z-1010 flex items-center justify-center p-6",
|
|
176
|
+
children: /* @__PURE__ */ jsxs(
|
|
177
|
+
"div",
|
|
178
|
+
{
|
|
179
|
+
className: cn(
|
|
180
|
+
"border-ui-primary bg-ui-surface text-ui-fg rounded-ui-xl flex flex-col items-center gap-2 border-2 border-dashed px-10 py-8 shadow-2xl",
|
|
181
|
+
className
|
|
182
|
+
),
|
|
183
|
+
children: [
|
|
184
|
+
/* @__PURE__ */ jsx("span", { className: "text-ui-primary", children: icon ?? /* @__PURE__ */ jsx(DocumentGlyph, {}) }),
|
|
185
|
+
/* @__PURE__ */ jsx("p", { className: "text-base font-semibold", children: title ?? copy.dropAnywhereTitle }),
|
|
186
|
+
/* @__PURE__ */ jsx("p", { className: "text-ui-muted text-sm", children: hint ?? copy.dropAnywhereHint })
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
),
|
|
192
|
+
document.body
|
|
193
|
+
);
|
|
194
|
+
};
|
|
195
|
+
var GalleryUploader = ({
|
|
196
|
+
scopes,
|
|
197
|
+
scope,
|
|
198
|
+
entityId,
|
|
199
|
+
strategy,
|
|
200
|
+
maxFiles,
|
|
201
|
+
rename,
|
|
202
|
+
stored = [],
|
|
203
|
+
onUploaded,
|
|
204
|
+
onRemoveStored,
|
|
205
|
+
onError,
|
|
206
|
+
resolveViewUrl,
|
|
207
|
+
columnsClassName = "grid-cols-2 sm:grid-cols-3 md:grid-cols-4",
|
|
208
|
+
disabled = false,
|
|
209
|
+
labels,
|
|
210
|
+
className
|
|
211
|
+
}) => {
|
|
212
|
+
const copy = useUploaderLabels(labels);
|
|
213
|
+
const [viewing, setViewing] = useState(null);
|
|
214
|
+
const uploader = useUploader({
|
|
215
|
+
scopes,
|
|
216
|
+
scope,
|
|
217
|
+
entityId,
|
|
218
|
+
strategy,
|
|
219
|
+
multiple: true,
|
|
220
|
+
...maxFiles !== void 0 ? { maxFiles } : {},
|
|
221
|
+
...rename ? { rename } : {},
|
|
222
|
+
uploadOn: "select",
|
|
223
|
+
onUploaded,
|
|
224
|
+
onError,
|
|
225
|
+
labels
|
|
226
|
+
});
|
|
227
|
+
const storedKeys = new Set(stored.map((file) => file.key));
|
|
228
|
+
const inFlight = uploader.files.filter(
|
|
229
|
+
(file) => !(file.stored && storedKeys.has(file.stored.key))
|
|
230
|
+
);
|
|
231
|
+
const viewable = [
|
|
232
|
+
...stored,
|
|
233
|
+
...inFlight.flatMap((file) => file.stored ? [file.stored] : [])
|
|
234
|
+
];
|
|
235
|
+
const tile = "group animate-ui-fade-in border-ui-border bg-ui-surface-muted rounded-ui-lg relative aspect-square overflow-hidden border shadow-sm transition-shadow duration-200 hover:shadow-md";
|
|
236
|
+
const action = "bg-ui-surface/90 text-ui-fg hover:bg-ui-surface rounded-ui cursor-pointer px-2 py-1 text-[11px] font-medium shadow-sm transition-colors";
|
|
237
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
238
|
+
/* @__PURE__ */ jsxs("div", { className: cn("grid gap-3", columnsClassName), children: [
|
|
239
|
+
stored.map((file) => /* @__PURE__ */ jsxs("div", { className: tile, children: [
|
|
240
|
+
/* @__PURE__ */ jsx(
|
|
241
|
+
"img",
|
|
242
|
+
{
|
|
243
|
+
src: file.url,
|
|
244
|
+
alt: file.fileName,
|
|
245
|
+
className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
246
|
+
}
|
|
247
|
+
),
|
|
248
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute inset-x-0 bottom-0 flex justify-between gap-1 bg-gradient-to-t from-black/60 to-transparent p-2 opacity-0 transition-opacity duration-200 group-hover:opacity-100", children: [
|
|
249
|
+
/* @__PURE__ */ jsx(
|
|
250
|
+
"button",
|
|
251
|
+
{
|
|
252
|
+
type: "button",
|
|
253
|
+
onClick: () => setViewing(file),
|
|
254
|
+
className: action,
|
|
255
|
+
children: copy.view
|
|
256
|
+
}
|
|
257
|
+
),
|
|
258
|
+
onRemoveStored && !disabled ? /* @__PURE__ */ jsx(
|
|
259
|
+
"button",
|
|
260
|
+
{
|
|
261
|
+
type: "button",
|
|
262
|
+
onClick: () => onRemoveStored(file),
|
|
263
|
+
className: cn(action, "text-ui-danger"),
|
|
264
|
+
children: copy.remove
|
|
265
|
+
}
|
|
266
|
+
) : null
|
|
267
|
+
] })
|
|
268
|
+
] }, file.key)),
|
|
269
|
+
inFlight.map((file) => /* @__PURE__ */ jsxs("div", { className: tile, children: [
|
|
270
|
+
file.preview ? /* @__PURE__ */ jsx(
|
|
271
|
+
"img",
|
|
272
|
+
{
|
|
273
|
+
src: file.preview,
|
|
274
|
+
alt: file.file.name,
|
|
275
|
+
className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
276
|
+
}
|
|
277
|
+
) : null,
|
|
278
|
+
file.status === "uploading" ? /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-2 bg-black/45", children: [
|
|
279
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm font-semibold text-white", children: [
|
|
280
|
+
file.progress,
|
|
281
|
+
"%"
|
|
282
|
+
] }),
|
|
283
|
+
/* @__PURE__ */ jsx("div", { className: "h-1 w-2/3 overflow-hidden rounded-full bg-white/30", children: /* @__PURE__ */ jsx(
|
|
284
|
+
"div",
|
|
285
|
+
{
|
|
286
|
+
className: "h-full bg-white transition-all duration-150",
|
|
287
|
+
style: { width: `${file.progress}%` }
|
|
288
|
+
}
|
|
289
|
+
) })
|
|
290
|
+
] }) : null,
|
|
291
|
+
file.status === "error" ? /* @__PURE__ */ jsx("div", { className: "bg-ui-danger/70 absolute inset-0 flex items-center justify-center p-2", children: /* @__PURE__ */ jsx("p", { className: "text-center text-[11px] leading-tight text-white", children: file.error }) }) : null,
|
|
292
|
+
file.status !== "uploading" ? /* @__PURE__ */ jsxs("div", { className: "absolute inset-x-0 bottom-0 flex justify-between gap-1 bg-gradient-to-t from-black/60 to-transparent p-2 opacity-0 transition-opacity duration-200 group-hover:opacity-100", children: [
|
|
293
|
+
file.stored ? /* @__PURE__ */ jsx(
|
|
294
|
+
"button",
|
|
295
|
+
{
|
|
296
|
+
type: "button",
|
|
297
|
+
onClick: () => file.stored && setViewing(file.stored),
|
|
298
|
+
className: action,
|
|
299
|
+
children: copy.view
|
|
300
|
+
}
|
|
301
|
+
) : /* @__PURE__ */ jsx("span", {}),
|
|
302
|
+
/* @__PURE__ */ jsx(
|
|
303
|
+
"button",
|
|
304
|
+
{
|
|
305
|
+
type: "button",
|
|
306
|
+
onClick: () => uploader.removeFile(file.id),
|
|
307
|
+
className: cn(action, "text-ui-danger"),
|
|
308
|
+
children: copy.remove
|
|
309
|
+
}
|
|
310
|
+
)
|
|
311
|
+
] }) : null
|
|
312
|
+
] }, file.id)),
|
|
313
|
+
/* @__PURE__ */ jsxs(
|
|
314
|
+
Dropzone,
|
|
315
|
+
{
|
|
316
|
+
accept: uploader.accept,
|
|
317
|
+
multiple: true,
|
|
318
|
+
size: "sm",
|
|
319
|
+
disabled,
|
|
320
|
+
onFiles: (files) => void uploader.addFiles(files),
|
|
321
|
+
className: "flex aspect-square flex-col items-center justify-center",
|
|
322
|
+
children: [
|
|
323
|
+
/* @__PURE__ */ jsx("span", { className: "text-ui-muted text-3xl leading-none font-light", children: "+" }),
|
|
324
|
+
/* @__PURE__ */ jsx("span", { className: "text-ui-muted mt-1 text-xs", children: copy.galleryAdd })
|
|
325
|
+
]
|
|
326
|
+
}
|
|
327
|
+
)
|
|
328
|
+
] }),
|
|
329
|
+
/* @__PURE__ */ jsx(
|
|
330
|
+
FileViewer,
|
|
331
|
+
{
|
|
332
|
+
file: viewing,
|
|
333
|
+
files: viewable,
|
|
334
|
+
onClose: () => setViewing(null),
|
|
335
|
+
...resolveViewUrl ? { resolveUrl: resolveViewUrl } : {}
|
|
336
|
+
}
|
|
337
|
+
)
|
|
338
|
+
] });
|
|
339
|
+
};
|
|
340
|
+
var matchesAccept = (file, accept) => {
|
|
341
|
+
const tokens = accept.split(",").map((token) => token.trim().toLowerCase()).filter(Boolean);
|
|
342
|
+
if (tokens.length === 0) return true;
|
|
343
|
+
const name = file.name.toLowerCase();
|
|
344
|
+
const type = file.type.toLowerCase();
|
|
345
|
+
return tokens.some((token) => {
|
|
346
|
+
if (token.startsWith(".")) return name.endsWith(token);
|
|
347
|
+
if (token.endsWith("/*")) return type.startsWith(token.slice(0, -1));
|
|
348
|
+
return type === token;
|
|
349
|
+
});
|
|
350
|
+
};
|
|
351
|
+
var useDropAnywhere = ({
|
|
352
|
+
enabled = true,
|
|
353
|
+
onFiles,
|
|
354
|
+
accept,
|
|
355
|
+
onRejected
|
|
356
|
+
}) => {
|
|
357
|
+
const [dragging, setDragging] = useState(false);
|
|
358
|
+
const onFilesRef = useRef(onFiles);
|
|
359
|
+
onFilesRef.current = onFiles;
|
|
360
|
+
const onRejectedRef = useRef(onRejected);
|
|
361
|
+
onRejectedRef.current = onRejected;
|
|
362
|
+
useEffect(() => {
|
|
363
|
+
if (!enabled) {
|
|
364
|
+
setDragging(false);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
let depth = 0;
|
|
368
|
+
const onEnter = (event) => {
|
|
369
|
+
if (!event.dataTransfer?.types.includes("Files")) return;
|
|
370
|
+
depth += 1;
|
|
371
|
+
setDragging(true);
|
|
372
|
+
};
|
|
373
|
+
const onLeave = () => {
|
|
374
|
+
depth = Math.max(0, depth - 1);
|
|
375
|
+
if (depth === 0) setDragging(false);
|
|
376
|
+
};
|
|
377
|
+
const onOver = (event) => event.preventDefault();
|
|
378
|
+
const onDrop = (event) => {
|
|
379
|
+
event.preventDefault();
|
|
380
|
+
depth = 0;
|
|
381
|
+
setDragging(false);
|
|
382
|
+
const files = Array.from(event.dataTransfer?.files ?? []);
|
|
383
|
+
if (files.length === 0) return;
|
|
384
|
+
const accepted = accept ? files.filter((file) => matchesAccept(file, accept)) : files;
|
|
385
|
+
const rejected = files.filter((file) => !accepted.includes(file));
|
|
386
|
+
if (accepted.length > 0) onFilesRef.current(accepted);
|
|
387
|
+
if (rejected.length > 0) onRejectedRef.current?.(rejected);
|
|
388
|
+
};
|
|
389
|
+
window.addEventListener("dragenter", onEnter);
|
|
390
|
+
window.addEventListener("dragleave", onLeave);
|
|
391
|
+
window.addEventListener("dragover", onOver);
|
|
392
|
+
window.addEventListener("drop", onDrop);
|
|
393
|
+
return () => {
|
|
394
|
+
window.removeEventListener("dragenter", onEnter);
|
|
395
|
+
window.removeEventListener("dragleave", onLeave);
|
|
396
|
+
window.removeEventListener("dragover", onOver);
|
|
397
|
+
window.removeEventListener("drop", onDrop);
|
|
398
|
+
};
|
|
399
|
+
}, [enabled, accept]);
|
|
400
|
+
return { dragging };
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
export { AvatarUploader, DropAnywhereOverlay, GalleryUploader, useDropAnywhere };
|
|
404
|
+
//# sourceMappingURL=presets.js.map
|
|
405
|
+
//# sourceMappingURL=presets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/presets/AvatarUploader.tsx","../src/presets/DropAnywhereOverlay.tsx","../src/presets/GalleryUploader.tsx","../src/presets/useDropAnywhere.ts"],"names":["jsxs","jsx","useState","useRef"],"mappings":"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","file":"presets.js","sourcesContent":["import { type DragEvent, type ReactNode, useRef, useState } from 'react'\n\nimport type { UploaderLabels } from '../labels'\nimport { useUploaderLabels } from '../react/UploaderProvider'\nimport { useUploader, type UseUploaderOptions } from '../react/useUploader'\nimport type { ScopeConfig, StoredFile } from '../types'\nimport { cn } from '../ui/cn'\n\nexport type AvatarUploaderProps<\n\tT extends Record<string, ScopeConfig> = Record<string, ScopeConfig>,\n> = Pick<\n\tUseUploaderOptions<T>,\n\t'scopes' | 'scope' | 'entityId' | 'strategy' | 'rename'\n> & {\n\t/** The stored image, if any. */\n\tsrc?: string | null\n\t/** What shows without an image — initials, an icon. */\n\tfallback?: ReactNode\n\t/** Diameter in px. @defaultValue 96 */\n\tsize?: number\n\tonUploaded: (stored: StoredFile) => void\n\t/** Offered as a link under the circle when there is an image to remove. */\n\tonRemove?: () => void\n\tonError?: (message: string) => void\n\talt?: string\n\tdisabled?: boolean\n\tlabels?: Partial<UploaderLabels>\n\tclassName?: string\n}\n\n/**\n * A picture that is its own control: click it, pick a file, watch the ring\n * close around it; or drop an image straight onto it. Uploads on select — a\n * progress ring with nothing travelling behind it would be theatre — so the\n * caller decides when the returned `StoredFile` becomes the entity's image.\n */\nexport const AvatarUploader = <T extends Record<string, ScopeConfig>>({\n\tscopes,\n\tscope,\n\tentityId,\n\tstrategy,\n\trename,\n\tsrc,\n\tfallback,\n\tsize = 96,\n\tonUploaded,\n\tonRemove,\n\tonError,\n\talt = '',\n\tdisabled = false,\n\tlabels,\n\tclassName,\n}: AvatarUploaderProps<T>) => {\n\tconst copy = useUploaderLabels(labels)\n\tconst input = useRef<HTMLInputElement>(null)\n\tconst [dropping, setDropping] = useState(false)\n\t// Depth counter, never a boolean — see AGENTS.md §6.\n\tconst depth = useRef(0)\n\n\tconst uploader = useUploader<T>({\n\t\tscopes,\n\t\tscope,\n\t\tentityId,\n\t\tstrategy,\n\t\tuploadOn: 'select',\n\t\tmaxFiles: 1,\n\t\t...(rename ? { rename } : {}),\n\t\tonUploaded: stored => {\n\t\t\tif (stored[0]) onUploaded(stored[0])\n\t\t},\n\t\tonError,\n\t\tlabels,\n\t})\n\n\tconst current = uploader.files[0]\n\tconst uploading = current?.status === 'uploading'\n\tconst failed = current?.status === 'error' ? current.error : undefined\n\tconst shown = current?.preview ?? src ?? null\n\tconst locked = disabled || uploading\n\n\tconst onDrop = (event: DragEvent) => {\n\t\tevent.preventDefault()\n\t\tdepth.current = 0\n\t\tsetDropping(false)\n\t\tif (locked) return\n\t\tconst file = event.dataTransfer.files[0]\n\t\tif (file) void uploader.addFiles([file])\n\t}\n\n\treturn (\n\t\t<div className={cn('inline-flex flex-col items-center gap-2', className)}>\n\t\t\t<div className='relative' style={{ width: size, height: size }}>\n\t\t\t\t<button\n\t\t\t\t\ttype='button'\n\t\t\t\t\tonClick={() => !locked && input.current?.click()}\n\t\t\t\t\tonDragEnter={() => {\n\t\t\t\t\t\tdepth.current += 1\n\t\t\t\t\t\tif (!locked) setDropping(true)\n\t\t\t\t\t}}\n\t\t\t\t\tonDragOver={event => event.preventDefault()}\n\t\t\t\t\tonDragLeave={() => {\n\t\t\t\t\t\tdepth.current = Math.max(0, depth.current - 1)\n\t\t\t\t\t\tif (depth.current === 0) setDropping(false)\n\t\t\t\t\t}}\n\t\t\t\t\tonDrop={onDrop}\n\t\t\t\t\tdisabled={locked}\n\t\t\t\t\taria-busy={uploading}\n\t\t\t\t\taria-label={copy.avatarChange}\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t'group ring-ui-border focus-visible:ring-ui-ring relative flex h-full w-full overflow-hidden rounded-full ring-2 ring-offset-2 transition-all duration-200 outline-none',\n\t\t\t\t\t\tlocked\n\t\t\t\t\t\t\t? 'cursor-not-allowed'\n\t\t\t\t\t\t\t: 'hover:ring-ui-primary cursor-pointer',\n\t\t\t\t\t\tdropping && 'ring-ui-primary cursor-copy ring-4'\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t{shown ? (\n\t\t\t\t\t\t<img src={shown} alt={alt} className='h-full w-full object-cover' />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<span className='bg-ui-surface-muted text-ui-muted flex h-full w-full items-center justify-center text-xl font-semibold'>\n\t\t\t\t\t\t\t{fallback}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t)}\n\t\t\t\t\t{dropping ? (\n\t\t\t\t\t\t<span className='bg-ui-primary/70 text-ui-primary-fg absolute inset-0 flex items-center justify-center text-xs font-semibold'>\n\t\t\t\t\t\t\t{copy.avatarDropHere}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t) : uploading ? (\n\t\t\t\t\t\t<span className='absolute inset-0 flex items-center justify-center bg-black/50 text-xs font-medium text-white'>\n\t\t\t\t\t\t\t{current.progress}%\n\t\t\t\t\t\t</span>\n\t\t\t\t\t) : locked ? null : (\n\t\t\t\t\t\t<span className='absolute inset-0 flex items-center justify-center bg-black/50 text-xs font-medium text-white opacity-0 transition-opacity duration-200 group-hover:opacity-100'>\n\t\t\t\t\t\t\t{copy.avatarChange}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t)}\n\t\t\t\t</button>\n\n\t\t\t\t{/* Outside the circle so it never crops it; `pathLength` makes the\n\t\t\t\t dash offset the percentage itself. */}\n\t\t\t\t{uploading ? (\n\t\t\t\t\t<svg\n\t\t\t\t\t\tviewBox='0 0 100 100'\n\t\t\t\t\t\tclassName='pointer-events-none absolute -inset-1.5 -rotate-90'\n\t\t\t\t\t\taria-hidden\n\t\t\t\t\t>\n\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\tcx='50'\n\t\t\t\t\t\t\tcy='50'\n\t\t\t\t\t\t\tr='48'\n\t\t\t\t\t\t\tpathLength={100}\n\t\t\t\t\t\t\tfill='none'\n\t\t\t\t\t\t\tstrokeWidth={4}\n\t\t\t\t\t\t\tstrokeLinecap='round'\n\t\t\t\t\t\t\tstrokeDasharray={100}\n\t\t\t\t\t\t\tstrokeDashoffset={100 - (current?.progress ?? 0)}\n\t\t\t\t\t\t\tclassName='stroke-ui-primary transition-[stroke-dashoffset] duration-200'\n\t\t\t\t\t\t/>\n\t\t\t\t\t</svg>\n\t\t\t\t) : null}\n\t\t\t</div>\n\n\t\t\t{failed ? (\n\t\t\t\t<p className='text-ui-danger max-w-[16rem] text-center text-xs'>\n\t\t\t\t\t{failed}\n\t\t\t\t</p>\n\t\t\t) : onRemove && src && !uploading && !disabled ? (\n\t\t\t\t<button\n\t\t\t\t\ttype='button'\n\t\t\t\t\tonClick={onRemove}\n\t\t\t\t\tclassName='text-ui-danger cursor-pointer text-xs hover:underline'\n\t\t\t\t>\n\t\t\t\t\t{copy.avatarRemove}\n\t\t\t\t</button>\n\t\t\t) : null}\n\n\t\t\t<input\n\t\t\t\tref={input}\n\t\t\t\ttype='file'\n\t\t\t\taccept={uploader.accept}\n\t\t\t\tclassName='hidden'\n\t\t\t\tdisabled={locked}\n\t\t\t\tonChange={event => {\n\t\t\t\t\tconst file = event.target.files?.[0]\n\t\t\t\t\tif (file) void uploader.addFiles([file])\n\t\t\t\t\t// The same file twice in a row must still fire `change`.\n\t\t\t\t\tevent.target.value = ''\n\t\t\t\t}}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n","import type { ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport type { UploaderLabels } from '../labels'\nimport { useUploaderLabels } from '../react/UploaderProvider'\nimport { cn } from '../ui/cn'\n\nexport type DropAnywhereOverlayProps = {\n\t/** Usually `useDropAnywhere().dragging`. */\n\topen: boolean\n\t/** Replaces the default document glyph. */\n\ticon?: ReactNode\n\ttitle?: ReactNode\n\thint?: ReactNode\n\tlabels?: Partial<UploaderLabels>\n\tclassName?: string\n}\n\nconst DocumentGlyph = () => (\n\t<svg\n\t\twidth='40'\n\t\theight='40'\n\t\tviewBox='0 0 24 24'\n\t\tfill='none'\n\t\tstroke='currentColor'\n\t\tstrokeWidth='1.6'\n\t\tstrokeLinecap='round'\n\t\tstrokeLinejoin='round'\n\t\taria-hidden\n\t>\n\t\t<path d='M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z' />\n\t\t<path d='M14 3v5h5' />\n\t\t<path d='M12 18v-6' />\n\t\t<path d='m9.5 14.5 2.5-2.5 2.5 2.5' />\n\t</svg>\n)\n\n/**\n * The full-window invitation that pairs with {@link useDropAnywhere}. Purely\n * visual and `pointer-events-none`: the drop lands on the window, where the\n * hook delivers the files exactly once. Portals to `document.body` above the\n * kit's overlays.\n */\nexport const DropAnywhereOverlay = ({\n\topen,\n\ticon,\n\ttitle,\n\thint,\n\tlabels,\n\tclassName,\n}: DropAnywhereOverlayProps) => {\n\tconst copy = useUploaderLabels(labels)\n\tif (!open || typeof document === 'undefined') return null\n\treturn createPortal(\n\t\t<div\n\t\t\taria-hidden\n\t\t\tclassName='animate-ui-fade-in bg-ui-overlay pointer-events-none fixed inset-0 z-1010 flex items-center justify-center p-6'\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'border-ui-primary bg-ui-surface text-ui-fg rounded-ui-xl flex flex-col items-center gap-2 border-2 border-dashed px-10 py-8 shadow-2xl',\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<span className='text-ui-primary'>{icon ?? <DocumentGlyph />}</span>\n\t\t\t\t<p className='text-base font-semibold'>\n\t\t\t\t\t{title ?? copy.dropAnywhereTitle}\n\t\t\t\t</p>\n\t\t\t\t<p className='text-ui-muted text-sm'>{hint ?? copy.dropAnywhereHint}</p>\n\t\t\t</div>\n\t\t</div>,\n\t\tdocument.body\n\t)\n}\n","import { useState } from 'react'\n\nimport type { UploaderLabels } from '../labels'\nimport { useUploaderLabels } from '../react/UploaderProvider'\nimport { useUploader, type UseUploaderOptions } from '../react/useUploader'\nimport type { ScopeConfig, StoredFile } from '../types'\nimport { cn } from '../ui/cn'\nimport { Dropzone } from '../ui/Dropzone'\nimport { FileViewer, type ViewableFile } from '../ui/FileViewer'\n\nexport type GalleryUploaderProps<\n\tT extends Record<string, ScopeConfig> = Record<string, ScopeConfig>,\n> = Pick<\n\tUseUploaderOptions<T>,\n\t'scopes' | 'scope' | 'entityId' | 'strategy' | 'maxFiles' | 'rename'\n> & {\n\t/** Images already persisted, rendered as tiles ahead of the in-flight ones. */\n\tstored?: StoredFile[]\n\tonUploaded?: (stored: StoredFile[]) => void\n\t/** Offered on a stored tile; the caller forgets the reference (and sweeps). */\n\tonRemoveStored?: (file: StoredFile) => void\n\tonError?: (message: string) => void\n\t/** Re-sign or proxy a stored url right before the viewer opens it. */\n\tresolveViewUrl?: (file: ViewableFile) => Promise<string>\n\t/** Grid columns. @defaultValue 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4' */\n\tcolumnsClassName?: string\n\tdisabled?: boolean\n\tlabels?: Partial<UploaderLabels>\n\tclassName?: string\n}\n\n/**\n * A thumbnail grid over the headless hook: every tile is the file's own\n * preview, the progress washes over it while it travels, hover reveals view\n * and remove, and a click opens the full-screen viewer over the whole set.\n * The add tile IS the dropzone.\n */\nexport const GalleryUploader = <T extends Record<string, ScopeConfig>>({\n\tscopes,\n\tscope,\n\tentityId,\n\tstrategy,\n\tmaxFiles,\n\trename,\n\tstored = [],\n\tonUploaded,\n\tonRemoveStored,\n\tonError,\n\tresolveViewUrl,\n\tcolumnsClassName = 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4',\n\tdisabled = false,\n\tlabels,\n\tclassName,\n}: GalleryUploaderProps<T>) => {\n\tconst copy = useUploaderLabels(labels)\n\tconst [viewing, setViewing] = useState<ViewableFile | null>(null)\n\tconst uploader = useUploader<T>({\n\t\tscopes,\n\t\tscope,\n\t\tentityId,\n\t\tstrategy,\n\t\tmultiple: true,\n\t\t...(maxFiles !== undefined ? { maxFiles } : {}),\n\t\t...(rename ? { rename } : {}),\n\t\tuploadOn: 'select',\n\t\tonUploaded,\n\t\tonError,\n\t\tlabels,\n\t})\n\n\t// Persisted tiles come first; a file the hook already confirmed and the\n\t// caller echoed back through `stored` renders once.\n\tconst storedKeys = new Set(stored.map(file => file.key))\n\tconst inFlight = uploader.files.filter(\n\t\tfile => !(file.stored && storedKeys.has(file.stored.key))\n\t)\n\tconst viewable: ViewableFile[] = [\n\t\t...stored,\n\t\t...inFlight.flatMap(file => (file.stored ? [file.stored] : [])),\n\t]\n\n\tconst tile =\n\t\t'group animate-ui-fade-in border-ui-border bg-ui-surface-muted rounded-ui-lg relative aspect-square overflow-hidden border shadow-sm transition-shadow duration-200 hover:shadow-md'\n\tconst action =\n\t\t'bg-ui-surface/90 text-ui-fg hover:bg-ui-surface rounded-ui cursor-pointer px-2 py-1 text-[11px] font-medium shadow-sm transition-colors'\n\n\treturn (\n\t\t<div className={className}>\n\t\t\t<div className={cn('grid gap-3', columnsClassName)}>\n\t\t\t\t{stored.map(file => (\n\t\t\t\t\t<div key={file.key} className={tile}>\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc={file.url}\n\t\t\t\t\t\t\talt={file.fileName}\n\t\t\t\t\t\t\tclassName='h-full w-full object-cover transition-transform duration-300 group-hover:scale-105'\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<div className='absolute inset-x-0 bottom-0 flex justify-between gap-1 bg-gradient-to-t from-black/60 to-transparent p-2 opacity-0 transition-opacity duration-200 group-hover:opacity-100'>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype='button'\n\t\t\t\t\t\t\t\tonClick={() => setViewing(file)}\n\t\t\t\t\t\t\t\tclassName={action}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{copy.view}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{onRemoveStored && !disabled ? (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype='button'\n\t\t\t\t\t\t\t\t\tonClick={() => onRemoveStored(file)}\n\t\t\t\t\t\t\t\t\tclassName={cn(action, 'text-ui-danger')}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{copy.remove}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t))}\n\n\t\t\t\t{inFlight.map(file => (\n\t\t\t\t\t<div key={file.id} className={tile}>\n\t\t\t\t\t\t{file.preview ? (\n\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\tsrc={file.preview}\n\t\t\t\t\t\t\t\talt={file.file.name}\n\t\t\t\t\t\t\t\tclassName='h-full w-full object-cover transition-transform duration-300 group-hover:scale-105'\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{file.status === 'uploading' ? (\n\t\t\t\t\t\t\t<div className='absolute inset-0 flex flex-col items-center justify-center gap-2 bg-black/45'>\n\t\t\t\t\t\t\t\t<span className='text-sm font-semibold text-white'>\n\t\t\t\t\t\t\t\t\t{file.progress}%\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<div className='h-1 w-2/3 overflow-hidden rounded-full bg-white/30'>\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tclassName='h-full bg-white transition-all duration-150'\n\t\t\t\t\t\t\t\t\t\tstyle={{ width: `${file.progress}%` }}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{file.status === 'error' ? (\n\t\t\t\t\t\t\t<div className='bg-ui-danger/70 absolute inset-0 flex items-center justify-center p-2'>\n\t\t\t\t\t\t\t\t<p className='text-center text-[11px] leading-tight text-white'>\n\t\t\t\t\t\t\t\t\t{file.error}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{file.status !== 'uploading' ? (\n\t\t\t\t\t\t\t<div className='absolute inset-x-0 bottom-0 flex justify-between gap-1 bg-gradient-to-t from-black/60 to-transparent p-2 opacity-0 transition-opacity duration-200 group-hover:opacity-100'>\n\t\t\t\t\t\t\t\t{file.stored ? (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype='button'\n\t\t\t\t\t\t\t\t\t\tonClick={() => file.stored && setViewing(file.stored)}\n\t\t\t\t\t\t\t\t\t\tclassName={action}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{copy.view}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t<span />\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype='button'\n\t\t\t\t\t\t\t\t\tonClick={() => uploader.removeFile(file.id)}\n\t\t\t\t\t\t\t\t\tclassName={cn(action, 'text-ui-danger')}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{copy.remove}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</div>\n\t\t\t\t))}\n\n\t\t\t\t<Dropzone\n\t\t\t\t\taccept={uploader.accept}\n\t\t\t\t\tmultiple\n\t\t\t\t\tsize='sm'\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\tonFiles={files => void uploader.addFiles(files)}\n\t\t\t\t\tclassName='flex aspect-square flex-col items-center justify-center'\n\t\t\t\t>\n\t\t\t\t\t<span className='text-ui-muted text-3xl leading-none font-light'>\n\t\t\t\t\t\t+\n\t\t\t\t\t</span>\n\t\t\t\t\t<span className='text-ui-muted mt-1 text-xs'>{copy.galleryAdd}</span>\n\t\t\t\t</Dropzone>\n\t\t\t</div>\n\n\t\t\t<FileViewer\n\t\t\t\tfile={viewing}\n\t\t\t\tfiles={viewable}\n\t\t\t\tonClose={() => setViewing(null)}\n\t\t\t\t{...(resolveViewUrl ? { resolveUrl: resolveViewUrl } : {})}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n","import { useEffect, useRef, useState } from 'react'\n\nexport type UseDropAnywhereOptions = {\n\t/** Listen at all. Off while the surface that wants the files is closed. */\n\tenabled?: boolean\n\t/** Receives the accepted files of one drop, anywhere in the window. */\n\tonFiles: (files: File[]) => void\n\t/**\n\t * Same syntax as `<input accept>`: `.pdf,.xlsx,image/*`. Files outside it go\n\t * to `onRejected` instead of `onFiles`; omit to accept everything.\n\t */\n\taccept?: string\n\tonRejected?: (files: File[]) => void\n}\n\nexport type DropAnywhere = {\n\t/** A file drag is over the window — show the overlay. */\n\tdragging: boolean\n}\n\nconst matchesAccept = (file: File, accept: string): boolean => {\n\tconst tokens = accept\n\t\t.split(',')\n\t\t.map(token => token.trim().toLowerCase())\n\t\t.filter(Boolean)\n\tif (tokens.length === 0) return true\n\tconst name = file.name.toLowerCase()\n\tconst type = file.type.toLowerCase()\n\treturn tokens.some(token => {\n\t\tif (token.startsWith('.')) return name.endsWith(token)\n\t\tif (token.endsWith('/*')) return type.startsWith(token.slice(0, -1))\n\t\treturn type === token\n\t})\n}\n\n/**\n * The whole window as a drop target. A depth counter on `dragenter` /\n * `dragleave` at window level says when a file drag is present (a boolean\n * flickers on every child boundary — see AGENTS.md §6), and the window's\n * `drop` is the ONE place files are delivered: an overlay drawn while dragging\n * must not handle `drop` itself, or every file arrives twice.\n *\n * Pair it with {@link DropAnywhereOverlay}, or draw your own — the overlay is\n * purely visual.\n */\nexport const useDropAnywhere = ({\n\tenabled = true,\n\tonFiles,\n\taccept,\n\tonRejected,\n}: UseDropAnywhereOptions): DropAnywhere => {\n\tconst [dragging, setDragging] = useState(false)\n\n\t// In refs, not the deps: a consumer passing an inline arrow would re-run\n\t// the effect on every render, and `depth` lives INSIDE it — resetting the\n\t// counter mid-drag reintroduces exactly the flicker it exists to prevent\n\t// (AGENTS.md §3, same rule as FileViewer's resolveViewUrl).\n\tconst onFilesRef = useRef(onFiles)\n\tonFilesRef.current = onFiles\n\tconst onRejectedRef = useRef(onRejected)\n\tonRejectedRef.current = onRejected\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\tsetDragging(false)\n\t\t\treturn\n\t\t}\n\t\tlet depth = 0\n\t\tconst onEnter = (event: DragEvent) => {\n\t\t\tif (!event.dataTransfer?.types.includes('Files')) return\n\t\t\tdepth += 1\n\t\t\tsetDragging(true)\n\t\t}\n\t\tconst onLeave = () => {\n\t\t\tdepth = Math.max(0, depth - 1)\n\t\t\tif (depth === 0) setDragging(false)\n\t\t}\n\t\tconst onOver = (event: DragEvent) => event.preventDefault()\n\t\tconst onDrop = (event: DragEvent) => {\n\t\t\tevent.preventDefault()\n\t\t\tdepth = 0\n\t\t\tsetDragging(false)\n\t\t\tconst files = Array.from(event.dataTransfer?.files ?? [])\n\t\t\tif (files.length === 0) return\n\t\t\tconst accepted = accept\n\t\t\t\t? files.filter(file => matchesAccept(file, accept))\n\t\t\t\t: files\n\t\t\tconst rejected = files.filter(file => !accepted.includes(file))\n\t\t\tif (accepted.length > 0) onFilesRef.current(accepted)\n\t\t\tif (rejected.length > 0) onRejectedRef.current?.(rejected)\n\t\t}\n\t\twindow.addEventListener('dragenter', onEnter)\n\t\twindow.addEventListener('dragleave', onLeave)\n\t\twindow.addEventListener('dragover', onOver)\n\t\twindow.addEventListener('drop', onDrop)\n\t\treturn () => {\n\t\t\twindow.removeEventListener('dragenter', onEnter)\n\t\t\twindow.removeEventListener('dragleave', onLeave)\n\t\t\twindow.removeEventListener('dragover', onOver)\n\t\t\twindow.removeEventListener('drop', onDrop)\n\t\t}\n\t\t// Only what genuinely re-binds the listeners.\n\t}, [enabled, accept])\n\n\treturn { dragging }\n}\n"]}
|