react-upload-pro 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,534 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var chunkVVMPVTIX_cjs = require('./chunk-VVMPVTIX.cjs');
5
+ var react = require('react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ function useUploader(options = {}) {
9
+ const opts = react.useMemo(
10
+ () => ({ mode: "auto", ...options }),
11
+ [options]
12
+ );
13
+ return chunkVVMPVTIX_cjs.useDropzone(opts);
14
+ }
15
+ function UploadArea({
16
+ state = "idle",
17
+ disabled,
18
+ children,
19
+ icon,
20
+ description,
21
+ className,
22
+ ...rest
23
+ }) {
24
+ const { t } = chunkVVMPVTIX_cjs.useI18n();
25
+ return /* @__PURE__ */ jsxRuntime.jsx(
26
+ "div",
27
+ {
28
+ className: chunkVVMPVTIX_cjs.cn(
29
+ "flex min-h-[160px] cursor-pointer flex-col items-center justify-center gap-2 rounded-rup border-2 border-dashed border-rup-border bg-rup-bg p-6 text-center transition-all duration-200",
30
+ "hover:border-rup-accent/60 hover:bg-rup-accent/5",
31
+ state === "drag-accept" && "border-rup-accent bg-rup-accent/10 scale-[1.01]",
32
+ state === "drag-reject" && "border-rup-error bg-rup-error/10",
33
+ disabled && "cursor-not-allowed opacity-50",
34
+ className
35
+ ),
36
+ ...rest,
37
+ children: children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
38
+ /* @__PURE__ */ jsxRuntime.jsx(
39
+ "div",
40
+ {
41
+ className: chunkVVMPVTIX_cjs.cn(
42
+ "flex h-12 w-12 items-center justify-center rounded-full bg-rup-accent/10 text-rup-accent transition-transform",
43
+ state === "drag-accept" && "scale-110"
44
+ ),
45
+ children: icon ?? /* @__PURE__ */ jsxRuntime.jsx(chunkVVMPVTIX_cjs.UploadIcon, { width: 24, height: 24 })
46
+ }
47
+ ),
48
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-rup-fg", children: state === "drag-accept" ? t.dropHere : t.dragOrClick }),
49
+ description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-rup-muted", children: description })
50
+ ] })
51
+ }
52
+ );
53
+ }
54
+ function Dropzone({
55
+ children,
56
+ hideGallery,
57
+ galleryLayout = "list",
58
+ progressVariant,
59
+ progressSize,
60
+ previewable,
61
+ editable,
62
+ onRename,
63
+ onMetadataChange,
64
+ scrollAfter,
65
+ maxHeight,
66
+ width,
67
+ height,
68
+ style,
69
+ className,
70
+ hint,
71
+ ...options
72
+ }) {
73
+ const api = chunkVVMPVTIX_cjs.useDropzone(options);
74
+ if (children) {
75
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children(api) });
76
+ }
77
+ const handleRename = onRename ?? ((f, newName) => api.rename(f.id, newName));
78
+ const handleMetadata = onMetadataChange ?? ((f, m) => api.setMetadata(f.id, m));
79
+ const containerStyle = width !== void 0 || style !== void 0 ? { width, ...style } : void 0;
80
+ const surfaceStyle = height !== void 0 ? { height } : void 0;
81
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunkVVMPVTIX_cjs.cn("flex flex-col gap-4", className), style: containerStyle, children: [
82
+ /* @__PURE__ */ jsxRuntime.jsx(
83
+ UploadArea,
84
+ {
85
+ state: api.state,
86
+ disabled: options.disabled,
87
+ description: hint,
88
+ style: surfaceStyle,
89
+ ...api.getRootProps()
90
+ }
91
+ ),
92
+ /* @__PURE__ */ jsxRuntime.jsx("input", { ...api.getInputProps() }),
93
+ !hideGallery && api.files.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
94
+ chunkVVMPVTIX_cjs.UploadGallery,
95
+ {
96
+ files: api.files,
97
+ layout: galleryLayout,
98
+ progressVariant,
99
+ progressSize,
100
+ previewable,
101
+ editable,
102
+ scrollAfter,
103
+ maxHeight,
104
+ onRename: handleRename,
105
+ onMetadataChange: handleMetadata,
106
+ onRemove: (f) => api.remove(f.id),
107
+ onRetry: (f) => api.retry(f.id),
108
+ onPause: (f) => api.pause(f.id),
109
+ onResume: (f) => api.resume(f.id),
110
+ onCancel: (f) => api.cancel(f.id)
111
+ }
112
+ )
113
+ ] });
114
+ }
115
+ var variantClasses = {
116
+ primary: "bg-rup-accent text-rup-accent-fg hover:opacity-90",
117
+ secondary: "bg-rup-border text-rup-fg hover:bg-rup-border/80",
118
+ ghost: "bg-transparent text-rup-fg hover:bg-rup-border/30",
119
+ outline: "border border-rup-border bg-transparent text-rup-fg hover:bg-rup-border/30"
120
+ };
121
+ function UploadButton({
122
+ variant = "primary",
123
+ icon,
124
+ iconOnly,
125
+ children,
126
+ className,
127
+ disabled,
128
+ // Filter DropzoneOptions out from button props.
129
+ multiple,
130
+ directory,
131
+ clipboard,
132
+ accept,
133
+ minSize,
134
+ maxSize,
135
+ maxFiles,
136
+ rejectDuplicates,
137
+ validators,
138
+ onDrop,
139
+ onDropAccepted,
140
+ onDropRejected,
141
+ onUploadStart,
142
+ onUploadProgress,
143
+ onUploadSuccess,
144
+ onUploadError,
145
+ onRetry,
146
+ onPause,
147
+ onResume,
148
+ onRemove,
149
+ onAllComplete,
150
+ endpoint,
151
+ method,
152
+ headers,
153
+ fieldName,
154
+ formData,
155
+ withCredentials,
156
+ concurrency,
157
+ strategy,
158
+ mode,
159
+ retries,
160
+ retryBackoffMs,
161
+ chunkSize,
162
+ cloud,
163
+ getUploadToken,
164
+ virusScan,
165
+ ...buttonProps
166
+ }) {
167
+ const { t } = chunkVVMPVTIX_cjs.useI18n();
168
+ const { getInputProps, open } = chunkVVMPVTIX_cjs.useDropzone({
169
+ multiple,
170
+ directory,
171
+ clipboard: clipboard ?? false,
172
+ accept,
173
+ minSize,
174
+ maxSize,
175
+ maxFiles,
176
+ rejectDuplicates,
177
+ validators,
178
+ onDrop,
179
+ onDropAccepted,
180
+ onDropRejected,
181
+ onUploadStart,
182
+ onUploadProgress,
183
+ onUploadSuccess,
184
+ onUploadError,
185
+ onRetry,
186
+ onPause,
187
+ onResume,
188
+ onRemove,
189
+ onAllComplete,
190
+ endpoint,
191
+ method,
192
+ headers,
193
+ fieldName,
194
+ formData,
195
+ withCredentials,
196
+ concurrency,
197
+ strategy,
198
+ mode,
199
+ retries,
200
+ retryBackoffMs,
201
+ chunkSize,
202
+ cloud,
203
+ getUploadToken,
204
+ virusScan,
205
+ disabled: !!disabled
206
+ });
207
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
208
+ /* @__PURE__ */ jsxRuntime.jsxs(
209
+ "button",
210
+ {
211
+ type: "button",
212
+ onClick: open,
213
+ disabled,
214
+ className: chunkVVMPVTIX_cjs.cn(
215
+ "inline-flex items-center gap-2 rounded-rup px-4 py-2 text-sm font-medium transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rup-accent disabled:cursor-not-allowed disabled:opacity-50",
216
+ variantClasses[variant],
217
+ className
218
+ ),
219
+ ...buttonProps,
220
+ children: [
221
+ icon !== false && (icon ?? /* @__PURE__ */ jsxRuntime.jsx(chunkVVMPVTIX_cjs.UploadIcon, { width: 14, height: 14 })),
222
+ !iconOnly && (children ?? t.browse)
223
+ ]
224
+ }
225
+ ),
226
+ /* @__PURE__ */ jsxRuntime.jsx("input", { ...getInputProps() })
227
+ ] });
228
+ }
229
+ function ValidationErrorsModal({
230
+ open,
231
+ errors,
232
+ onClose,
233
+ title
234
+ }) {
235
+ const { t } = chunkVVMPVTIX_cjs.useI18n();
236
+ if (!open || errors.length === 0) return null;
237
+ return /* @__PURE__ */ jsxRuntime.jsx(
238
+ chunkVVMPVTIX_cjs.UploadModal,
239
+ {
240
+ open,
241
+ onClose,
242
+ title: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2 text-rup-error", children: [
243
+ /* @__PURE__ */ jsxRuntime.jsx(chunkVVMPVTIX_cjs.AlertIcon, { width: 18, height: 18 }),
244
+ title ?? t.error
245
+ ] }),
246
+ size: "md",
247
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
248
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-rup-muted", children: [
249
+ errors.length,
250
+ " ",
251
+ errors.length === 1 ? "file was" : "files were",
252
+ " rejected:"
253
+ ] }),
254
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex max-h-[50vh] flex-col gap-2 overflow-y-auto rup-scrollbar", children: errors.map((err, i) => /* @__PURE__ */ jsxRuntime.jsxs(
255
+ "li",
256
+ {
257
+ className: "flex items-start gap-2 rounded-rup border border-rup-error/30 bg-rup-error/5 px-3 py-2 text-sm",
258
+ children: [
259
+ /* @__PURE__ */ jsxRuntime.jsx(
260
+ chunkVVMPVTIX_cjs.AlertIcon,
261
+ {
262
+ width: 14,
263
+ height: 14,
264
+ className: "mt-0.5 shrink-0 text-rup-error"
265
+ }
266
+ ),
267
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
268
+ err.file && /* @__PURE__ */ jsxRuntime.jsx(
269
+ "p",
270
+ {
271
+ className: "truncate font-medium text-rup-fg",
272
+ title: err.file.name,
273
+ children: err.file.name
274
+ }
275
+ ),
276
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-rup-error", children: err.message })
277
+ ] }),
278
+ /* @__PURE__ */ jsxRuntime.jsx(
279
+ "span",
280
+ {
281
+ className: chunkVVMPVTIX_cjs.cn(
282
+ "shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide",
283
+ badgeClassesByCode[err.code] ?? "bg-rup-error/15 text-rup-error"
284
+ ),
285
+ children: err.code
286
+ }
287
+ )
288
+ ]
289
+ },
290
+ i
291
+ )) }),
292
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end pt-1", children: /* @__PURE__ */ jsxRuntime.jsx(
293
+ "button",
294
+ {
295
+ type: "button",
296
+ onClick: onClose,
297
+ className: "rounded-md bg-rup-accent px-4 py-2 text-sm font-medium text-rup-accent-fg hover:opacity-90",
298
+ children: "OK"
299
+ }
300
+ ) })
301
+ ] })
302
+ }
303
+ );
304
+ }
305
+ var badgeClassesByCode = {
306
+ "file-too-large": "bg-amber-500/15 text-amber-600 dark:text-amber-400",
307
+ "file-too-small": "bg-amber-500/15 text-amber-600 dark:text-amber-400",
308
+ "file-invalid-type": "bg-rup-error/15 text-rup-error",
309
+ "too-many-files": "bg-blue-500/15 text-blue-600 dark:text-blue-400",
310
+ "duplicate-file": "bg-violet-500/15 text-violet-600 dark:text-violet-400"
311
+ };
312
+ var ThemeContext = react.createContext(null);
313
+ function ThemeProvider({
314
+ defaultTheme = "auto",
315
+ theme: controlled,
316
+ onThemeChange,
317
+ children
318
+ }) {
319
+ const [internal, setInternal] = react.useState(controlled ?? defaultTheme);
320
+ const theme = controlled ?? internal;
321
+ const [resolved, setResolved] = react.useState("light");
322
+ react.useEffect(() => {
323
+ if (typeof window === "undefined") return;
324
+ if (theme === "auto") {
325
+ const mql = window.matchMedia("(prefers-color-scheme: dark)");
326
+ const apply = () => setResolved(mql.matches ? "dark" : "light");
327
+ apply();
328
+ mql.addEventListener("change", apply);
329
+ return () => mql.removeEventListener("change", apply);
330
+ }
331
+ setResolved(theme);
332
+ return;
333
+ }, [theme]);
334
+ const setTheme = (next) => {
335
+ if (controlled === void 0) setInternal(next);
336
+ onThemeChange?.(next);
337
+ };
338
+ const value = react.useMemo(
339
+ () => ({ theme, resolved, setTheme }),
340
+ [theme, resolved]
341
+ );
342
+ return /* @__PURE__ */ jsxRuntime.jsx(ThemeContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx("div", { "data-rup-root": true, "data-theme": resolved, className: "contents", children }) });
343
+ }
344
+ function useTheme() {
345
+ const ctx = react.useContext(ThemeContext);
346
+ if (ctx) return ctx;
347
+ return { theme: "light", resolved: "light", setTheme: () => void 0 };
348
+ }
349
+
350
+ Object.defineProperty(exports, "AlertIcon", {
351
+ enumerable: true,
352
+ get: function () { return chunkVVMPVTIX_cjs.AlertIcon; }
353
+ });
354
+ Object.defineProperty(exports, "CheckIcon", {
355
+ enumerable: true,
356
+ get: function () { return chunkVVMPVTIX_cjs.CheckIcon; }
357
+ });
358
+ Object.defineProperty(exports, "CloseIcon", {
359
+ enumerable: true,
360
+ get: function () { return chunkVVMPVTIX_cjs.CloseIcon; }
361
+ });
362
+ Object.defineProperty(exports, "DownloadIcon", {
363
+ enumerable: true,
364
+ get: function () { return chunkVVMPVTIX_cjs.DownloadIcon; }
365
+ });
366
+ Object.defineProperty(exports, "EditIcon", {
367
+ enumerable: true,
368
+ get: function () { return chunkVVMPVTIX_cjs.EditIcon; }
369
+ });
370
+ Object.defineProperty(exports, "EyeIcon", {
371
+ enumerable: true,
372
+ get: function () { return chunkVVMPVTIX_cjs.EyeIcon; }
373
+ });
374
+ Object.defineProperty(exports, "FileEditModal", {
375
+ enumerable: true,
376
+ get: function () { return chunkVVMPVTIX_cjs.FileEditModal; }
377
+ });
378
+ Object.defineProperty(exports, "FileIcon", {
379
+ enumerable: true,
380
+ get: function () { return chunkVVMPVTIX_cjs.FileIcon; }
381
+ });
382
+ Object.defineProperty(exports, "FilePreviewModal", {
383
+ enumerable: true,
384
+ get: function () { return chunkVVMPVTIX_cjs.FilePreviewModal; }
385
+ });
386
+ Object.defineProperty(exports, "FolderIcon", {
387
+ enumerable: true,
388
+ get: function () { return chunkVVMPVTIX_cjs.FolderIcon; }
389
+ });
390
+ Object.defineProperty(exports, "I18nProvider", {
391
+ enumerable: true,
392
+ get: function () { return chunkVVMPVTIX_cjs.I18nProvider; }
393
+ });
394
+ Object.defineProperty(exports, "ImageIcon", {
395
+ enumerable: true,
396
+ get: function () { return chunkVVMPVTIX_cjs.ImageIcon; }
397
+ });
398
+ Object.defineProperty(exports, "MaximizeIcon", {
399
+ enumerable: true,
400
+ get: function () { return chunkVVMPVTIX_cjs.MaximizeIcon; }
401
+ });
402
+ Object.defineProperty(exports, "PauseIcon", {
403
+ enumerable: true,
404
+ get: function () { return chunkVVMPVTIX_cjs.PauseIcon; }
405
+ });
406
+ Object.defineProperty(exports, "PlayIcon", {
407
+ enumerable: true,
408
+ get: function () { return chunkVVMPVTIX_cjs.PlayIcon; }
409
+ });
410
+ Object.defineProperty(exports, "RetryIcon", {
411
+ enumerable: true,
412
+ get: function () { return chunkVVMPVTIX_cjs.RetryIcon; }
413
+ });
414
+ Object.defineProperty(exports, "TagIcon", {
415
+ enumerable: true,
416
+ get: function () { return chunkVVMPVTIX_cjs.TagIcon; }
417
+ });
418
+ Object.defineProperty(exports, "TrashIcon", {
419
+ enumerable: true,
420
+ get: function () { return chunkVVMPVTIX_cjs.TrashIcon; }
421
+ });
422
+ Object.defineProperty(exports, "UploadGallery", {
423
+ enumerable: true,
424
+ get: function () { return chunkVVMPVTIX_cjs.UploadGallery; }
425
+ });
426
+ Object.defineProperty(exports, "UploadIcon", {
427
+ enumerable: true,
428
+ get: function () { return chunkVVMPVTIX_cjs.UploadIcon; }
429
+ });
430
+ Object.defineProperty(exports, "UploadModal", {
431
+ enumerable: true,
432
+ get: function () { return chunkVVMPVTIX_cjs.UploadModal; }
433
+ });
434
+ Object.defineProperty(exports, "UploadPreview", {
435
+ enumerable: true,
436
+ get: function () { return chunkVVMPVTIX_cjs.UploadPreview; }
437
+ });
438
+ Object.defineProperty(exports, "UploadProgress", {
439
+ enumerable: true,
440
+ get: function () { return chunkVVMPVTIX_cjs.UploadProgress; }
441
+ });
442
+ Object.defineProperty(exports, "UploadQueue", {
443
+ enumerable: true,
444
+ get: function () { return chunkVVMPVTIX_cjs.UploadQueue; }
445
+ });
446
+ Object.defineProperty(exports, "cn", {
447
+ enumerable: true,
448
+ get: function () { return chunkVVMPVTIX_cjs.cn; }
449
+ });
450
+ Object.defineProperty(exports, "detectSignature", {
451
+ enumerable: true,
452
+ get: function () { return chunkVVMPVTIX_cjs.detectSignature; }
453
+ });
454
+ Object.defineProperty(exports, "formatBytes", {
455
+ enumerable: true,
456
+ get: function () { return chunkVVMPVTIX_cjs.formatBytes; }
457
+ });
458
+ Object.defineProperty(exports, "formatEta", {
459
+ enumerable: true,
460
+ get: function () { return chunkVVMPVTIX_cjs.formatEta; }
461
+ });
462
+ Object.defineProperty(exports, "formatPercent", {
463
+ enumerable: true,
464
+ get: function () { return chunkVVMPVTIX_cjs.formatPercent; }
465
+ });
466
+ Object.defineProperty(exports, "formatSpeed", {
467
+ enumerable: true,
468
+ get: function () { return chunkVVMPVTIX_cjs.formatSpeed; }
469
+ });
470
+ Object.defineProperty(exports, "generatePreview", {
471
+ enumerable: true,
472
+ get: function () { return chunkVVMPVTIX_cjs.generatePreview; }
473
+ });
474
+ Object.defineProperty(exports, "getFileCategory", {
475
+ enumerable: true,
476
+ get: function () { return chunkVVMPVTIX_cjs.getFileCategory; }
477
+ });
478
+ Object.defineProperty(exports, "matchesAccept", {
479
+ enumerable: true,
480
+ get: function () { return chunkVVMPVTIX_cjs.matchesAccept; }
481
+ });
482
+ Object.defineProperty(exports, "revokePreview", {
483
+ enumerable: true,
484
+ get: function () { return chunkVVMPVTIX_cjs.revokePreview; }
485
+ });
486
+ Object.defineProperty(exports, "rtlLocales", {
487
+ enumerable: true,
488
+ get: function () { return chunkVVMPVTIX_cjs.rtlLocales; }
489
+ });
490
+ Object.defineProperty(exports, "translations", {
491
+ enumerable: true,
492
+ get: function () { return chunkVVMPVTIX_cjs.translations; }
493
+ });
494
+ Object.defineProperty(exports, "useDropzone", {
495
+ enumerable: true,
496
+ get: function () { return chunkVVMPVTIX_cjs.useDropzone; }
497
+ });
498
+ Object.defineProperty(exports, "useFilePreview", {
499
+ enumerable: true,
500
+ get: function () { return chunkVVMPVTIX_cjs.useFilePreview; }
501
+ });
502
+ Object.defineProperty(exports, "useI18n", {
503
+ enumerable: true,
504
+ get: function () { return chunkVVMPVTIX_cjs.useI18n; }
505
+ });
506
+ Object.defineProperty(exports, "useUploadProgress", {
507
+ enumerable: true,
508
+ get: function () { return chunkVVMPVTIX_cjs.useUploadProgress; }
509
+ });
510
+ Object.defineProperty(exports, "useUploadQueue", {
511
+ enumerable: true,
512
+ get: function () { return chunkVVMPVTIX_cjs.useUploadQueue; }
513
+ });
514
+ Object.defineProperty(exports, "validateBatch", {
515
+ enumerable: true,
516
+ get: function () { return chunkVVMPVTIX_cjs.validateBatch; }
517
+ });
518
+ Object.defineProperty(exports, "validateFile", {
519
+ enumerable: true,
520
+ get: function () { return chunkVVMPVTIX_cjs.validateFile; }
521
+ });
522
+ Object.defineProperty(exports, "wrapFile", {
523
+ enumerable: true,
524
+ get: function () { return chunkVVMPVTIX_cjs.wrapFile; }
525
+ });
526
+ exports.Dropzone = Dropzone;
527
+ exports.ThemeProvider = ThemeProvider;
528
+ exports.UploadArea = UploadArea;
529
+ exports.UploadButton = UploadButton;
530
+ exports.ValidationErrorsModal = ValidationErrorsModal;
531
+ exports.useTheme = useTheme;
532
+ exports.useUploader = useUploader;
533
+ //# sourceMappingURL=index.cjs.map
534
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useUploader.ts","../src/components/UploadArea.tsx","../src/components/Dropzone.tsx","../src/components/UploadButton.tsx","../src/components/ValidationErrorsModal.tsx","../src/theme/ThemeProvider.tsx"],"names":["useMemo","useDropzone","useI18n","jsx","cn","jsxs","Fragment","UploadIcon","UploadGallery","UploadModal","AlertIcon","createContext","useState","useEffect","useContext"],"mappings":";;;;;;AAWO,SAAS,WAAA,CAAY,OAAA,GAA8B,EAAC,EAAsB;AAC/E,EAAA,MAAM,IAAA,GAAOA,aAAA;AAAA,IACX,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,GAAG,OAAA,EAAQ,CAAA;AAAA,IAClC,CAAC,OAAO;AAAA,GACV;AACA,EAAA,OAAOC,8BAAY,IAAI,CAAA;AACzB;ACIO,SAAS,UAAA,CAAW;AAAA,EACzB,KAAA,GAAQ,MAAA;AAAA,EACR,QAAA;AAAA,EACA,QAAA;AAAA,EACA,IAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAoB;AAClB,EAAA,MAAM,EAAE,CAAA,EAAE,GAAIC,yBAAA,EAAQ;AACtB,EAAA,uBACEC,cAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAWC,oBAAA;AAAA,QACT,yLAAA;AAAA,QACA,kDAAA;AAAA,QACA,UAAU,aAAA,IAAiB,iDAAA;AAAA,QAC3B,UAAU,aAAA,IAAiB,kCAAA;AAAA,QAC3B,QAAA,IAAY,+BAAA;AAAA,QACZ;AAAA,OACF;AAAA,MACC,GAAG,IAAA;AAAA,MAEH,sCACCC,eAAA,CAAAC,mBAAA,EAAA,EACE,QAAA,EAAA;AAAA,wBAAAH,cAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAWC,oBAAA;AAAA,cACT,+GAAA;AAAA,cACA,UAAU,aAAA,IAAiB;AAAA,aAC7B;AAAA,YAEC,kCAAQD,cAAA,CAACI,4BAAA,EAAA,EAAW,KAAA,EAAO,EAAA,EAAI,QAAQ,EAAA,EAAI;AAAA;AAAA,SAC9C;AAAA,wBACAJ,cAAA,CAAC,OAAE,SAAA,EAAU,iCAAA,EACV,oBAAU,aAAA,GAAgB,CAAA,CAAE,QAAA,GAAW,CAAA,CAAE,WAAA,EAC5C,CAAA;AAAA,QACC,WAAA,oBAAeA,cAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,0BAA0B,QAAA,EAAA,WAAA,EAAY;AAAA,OAAA,EACrE;AAAA;AAAA,GAEJ;AAEJ;ACTO,SAAS,QAAA,CAAS;AAAA,EACvB,QAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA,GAAgB,MAAA;AAAA,EAChB,eAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,gBAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,KAAA;AAAA,EACA,SAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAkB;AAChB,EAAA,MAAM,GAAA,GAAMF,8BAAY,OAAO,CAAA;AAE/B,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,uBAAOE,cAAAA,CAAAG,mBAAAA,EAAA,EAAG,QAAA,EAAA,QAAA,CAAS,GAAG,CAAA,EAAE,CAAA;AAAA,EAC1B;AAIA,EAAA,MAAM,YAAA,GAAe,aAAa,CAAC,CAAA,EAAG,YAAY,GAAA,CAAI,MAAA,CAAO,CAAA,CAAE,EAAA,EAAI,OAAO,CAAA,CAAA;AAC1E,EAAA,MAAM,cAAA,GAAiB,qBAAqB,CAAC,CAAA,EAAG,MAAM,GAAA,CAAI,WAAA,CAAY,CAAA,CAAE,EAAA,EAAI,CAAC,CAAA,CAAA;AAK7E,EAAA,MAAM,cAAA,GACJ,UAAU,MAAA,IAAa,KAAA,KAAU,SAC7B,EAAE,KAAA,EAAO,GAAG,KAAA,EAAM,GAClB,MAAA;AACN,EAAA,MAAM,YAAA,GAAe,MAAA,KAAW,MAAA,GAAY,EAAE,QAAO,GAAI,MAAA;AAEzD,EAAA,uBACED,gBAAC,KAAA,EAAA,EAAI,SAAA,EAAWD,qBAAG,qBAAA,EAAuB,SAAS,CAAA,EAAG,KAAA,EAAO,cAAA,EAC3D,QAAA,EAAA;AAAA,oBAAAD,cAAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,OAAO,GAAA,CAAI,KAAA;AAAA,QACX,UAAU,OAAA,CAAQ,QAAA;AAAA,QAClB,WAAA,EAAa,IAAA;AAAA,QACb,KAAA,EAAO,YAAA;AAAA,QACN,GAAG,IAAI,YAAA;AAAa;AAAA,KACvB;AAAA,oBACAA,cAAAA,CAAC,OAAA,EAAA,EAAO,GAAG,GAAA,CAAI,eAAc,EAAG,CAAA;AAAA,IAC/B,CAAC,WAAA,IAAe,GAAA,CAAI,KAAA,CAAM,MAAA,GAAS,qBAClCA,cAAAA;AAAA,MAACK,+BAAA;AAAA,MAAA;AAAA,QACC,OAAO,GAAA,CAAI,KAAA;AAAA,QACX,MAAA,EAAQ,aAAA;AAAA,QACR,eAAA;AAAA,QACA,YAAA;AAAA,QACA,WAAA;AAAA,QACA,QAAA;AAAA,QACA,WAAA;AAAA,QACA,SAAA;AAAA,QACA,QAAA,EAAU,YAAA;AAAA,QACV,gBAAA,EAAkB,cAAA;AAAA,QAClB,UAAU,CAAC,CAAA,KAAM,GAAA,CAAI,MAAA,CAAO,EAAE,EAAE,CAAA;AAAA,QAChC,SAAS,CAAC,CAAA,KAAM,GAAA,CAAI,KAAA,CAAM,EAAE,EAAE,CAAA;AAAA,QAC9B,SAAS,CAAC,CAAA,KAAM,GAAA,CAAI,KAAA,CAAM,EAAE,EAAE,CAAA;AAAA,QAC9B,UAAU,CAAC,CAAA,KAAM,GAAA,CAAI,MAAA,CAAO,EAAE,EAAE,CAAA;AAAA,QAChC,UAAU,CAAC,CAAA,KAAM,GAAA,CAAI,MAAA,CAAO,EAAE,EAAE;AAAA;AAAA;AAClC,GAAA,EAEJ,CAAA;AAEJ;ACrGA,IAAM,cAAA,GAA4E;AAAA,EAChF,OAAA,EAAS,mDAAA;AAAA,EACT,SAAA,EAAW,kDAAA;AAAA,EACX,KAAA,EAAO,mDAAA;AAAA,EACP,OAAA,EAAS;AACX,CAAA;AAMO,SAAS,YAAA,CAAa;AAAA,EAC3B,OAAA,GAAU,SAAA;AAAA,EACV,IAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA;AAAA,EAEA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,gBAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA;AAAA,EACA,aAAA;AAAA,EACA,gBAAA;AAAA,EACA,eAAA;AAAA,EACA,aAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,aAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,eAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA,IAAA;AAAA,EACA,OAAA;AAAA,EACA,cAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,cAAA;AAAA,EACA,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAsB;AACpB,EAAA,MAAM,EAAE,CAAA,EAAE,GAAIN,yBAAA,EAAQ;AACtB,EAAA,MAAM,EAAE,aAAA,EAAe,IAAA,EAAK,GAAID,6BAAA,CAAY;AAAA,IAC1C,QAAA;AAAA,IACA,SAAA;AAAA,IACA,WAAW,SAAA,IAAa,KAAA;AAAA,IACxB,MAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA,gBAAA;AAAA,IACA,UAAA;AAAA,IACA,MAAA;AAAA,IACA,cAAA;AAAA,IACA,cAAA;AAAA,IACA,aAAA;AAAA,IACA,gBAAA;AAAA,IACA,eAAA;AAAA,IACA,aAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,aAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAA;AAAA,IACA,SAAA;AAAA,IACA,QAAA;AAAA,IACA,eAAA;AAAA,IACA,WAAA;AAAA,IACA,QAAA;AAAA,IACA,IAAA;AAAA,IACA,OAAA;AAAA,IACA,cAAA;AAAA,IACA,SAAA;AAAA,IACA,KAAA;AAAA,IACA,cAAA;AAAA,IACA,SAAA;AAAA,IACA,QAAA,EAAU,CAAC,CAAC;AAAA,GACb,CAAA;AAED,EAAA,uBACEI,eAAAA,CAAAC,mBAAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAAD,eAAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,QAAA;AAAA,QACL,OAAA,EAAS,IAAA;AAAA,QACT,QAAA;AAAA,QACA,SAAA,EAAWD,oBAAA;AAAA,UACT,mNAAA;AAAA,UACA,eAAe,OAAO,CAAA;AAAA,UACtB;AAAA,SACF;AAAA,QACC,GAAG,WAAA;AAAA,QAEH,QAAA,EAAA;AAAA,UAAA,IAAA,KAAS,KAAA,KAAU,wBAAQD,cAAAA,CAACI,gCAAW,KAAA,EAAO,EAAA,EAAI,QAAQ,EAAA,EAAI,CAAA,CAAA;AAAA,UAC9D,CAAC,QAAA,KAAa,QAAA,IAAY,CAAA,CAAE,MAAA;AAAA;AAAA;AAAA,KAC/B;AAAA,oBACAJ,cAAAA,CAAC,OAAA,EAAA,EAAO,GAAG,eAAc,EAAG;AAAA,GAAA,EAC9B,CAAA;AAEJ;ACnHO,SAAS,qBAAA,CAAsB;AAAA,EACpC,IAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,EAA+B;AAC7B,EAAA,MAAM,EAAE,CAAA,EAAE,GAAID,yBAAA,EAAQ;AACtB,EAAA,IAAI,CAAC,IAAA,IAAQ,MAAA,CAAO,MAAA,KAAW,GAAG,OAAO,IAAA;AAEzC,EAAA,uBACEC,cAAAA;AAAA,IAACM,6BAAA;AAAA,IAAA;AAAA,MACC,IAAA;AAAA,MACA,OAAA;AAAA,MACA,KAAA,kBACEJ,eAAAA,CAAC,MAAA,EAAA,EAAK,WAAU,+CAAA,EACd,QAAA,EAAA;AAAA,wBAAAF,cAAAA,CAACO,2BAAA,EAAA,EAAU,KAAA,EAAO,EAAA,EAAI,QAAQ,EAAA,EAAI,CAAA;AAAA,QACjC,SAAS,CAAA,CAAE;AAAA,OAAA,EACd,CAAA;AAAA,MAEF,IAAA,EAAK,IAAA;AAAA,MAEL,QAAA,kBAAAL,eAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qBAAA,EACb,QAAA,EAAA;AAAA,wBAAAA,eAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,wBAAA,EACV,QAAA,EAAA;AAAA,UAAA,MAAA,CAAO,MAAA;AAAA,UAAO,GAAA;AAAA,UAAE,MAAA,CAAO,MAAA,KAAW,CAAA,GAAI,UAAA,GAAa,YAAA;AAAA,UAAa;AAAA,SAAA,EACnE,CAAA;AAAA,wBACAF,cAAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,gEAAA,EACX,iBAAO,GAAA,CAAI,CAAC,GAAA,EAAK,CAAA,qBAChBE,eAAAA;AAAA,UAAC,IAAA;AAAA,UAAA;AAAA,YAEC,SAAA,EAAU,gGAAA;AAAA,YAEV,QAAA,EAAA;AAAA,8BAAAF,cAAAA;AAAA,gBAACO,2BAAA;AAAA,gBAAA;AAAA,kBACC,KAAA,EAAO,EAAA;AAAA,kBACP,MAAA,EAAQ,EAAA;AAAA,kBACR,SAAA,EAAU;AAAA;AAAA,eACZ;AAAA,8BACAL,eAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gBAAA,EACZ,QAAA,EAAA;AAAA,gBAAA,GAAA,CAAI,wBACHF,cAAAA;AAAA,kBAAC,GAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAU,kCAAA;AAAA,oBACV,KAAA,EAAO,IAAI,IAAA,CAAK,IAAA;AAAA,oBAEf,cAAI,IAAA,CAAK;AAAA;AAAA,iBACZ;AAAA,gCAEFA,cAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,wBAAA,EAA0B,cAAI,OAAA,EAAQ;AAAA,eAAA,EACrD,CAAA;AAAA,8BACAA,cAAAA;AAAA,gBAAC,MAAA;AAAA,gBAAA;AAAA,kBACC,SAAA,EAAWC,oBAAA;AAAA,oBACT,mFAAA;AAAA,oBACA,kBAAA,CAAmB,GAAA,CAAI,IAAI,CAAA,IAAK;AAAA,mBAClC;AAAA,kBAEC,QAAA,EAAA,GAAA,CAAI;AAAA;AAAA;AACP;AAAA,WAAA;AAAA,UA1BK;AAAA,SA4BR,CAAA,EACH,CAAA;AAAA,wBACAD,cAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,sCACb,QAAA,kBAAAA,cAAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,QAAA;AAAA,YACL,OAAA,EAAS,OAAA;AAAA,YACT,SAAA,EAAU,4FAAA;AAAA,YACX,QAAA,EAAA;AAAA;AAAA,SAED,EACF;AAAA,OAAA,EACF;AAAA;AAAA,GACF;AAEJ;AAEA,IAAM,kBAAA,GAAmE;AAAA,EACvE,gBAAA,EAAkB,oDAAA;AAAA,EAClB,gBAAA,EAAkB,oDAAA;AAAA,EAClB,mBAAA,EAAqB,gCAAA;AAAA,EACrB,gBAAA,EAAkB,iDAAA;AAAA,EAClB,gBAAA,EAAkB;AACpB,CAAA;ACrFA,IAAM,YAAA,GAAeQ,oBAAwC,IAAI,CAAA;AAiB1D,SAAS,aAAA,CAAc;AAAA,EAC5B,YAAA,GAAe,MAAA;AAAA,EACf,KAAA,EAAO,UAAA;AAAA,EACP,aAAA;AAAA,EACA;AACF,CAAA,EAAuB;AACrB,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIC,cAAA,CAAgB,cAAc,YAAY,CAAA;AAC1E,EAAA,MAAM,QAAQ,UAAA,IAAc,QAAA;AAC5B,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIA,eAA2B,OAAO,CAAA;AAElE,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,IAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,UAAA,CAAW,8BAA8B,CAAA;AAC5D,MAAA,MAAM,QAAQ,MAAM,WAAA,CAAY,GAAA,CAAI,OAAA,GAAU,SAAS,OAAO,CAAA;AAC9D,MAAA,KAAA,EAAM;AACN,MAAA,GAAA,CAAI,gBAAA,CAAiB,UAAU,KAAK,CAAA;AACpC,MAAA,OAAO,MAAM,GAAA,CAAI,mBAAA,CAAoB,QAAA,EAAU,KAAK,CAAA;AAAA,IACtD;AACA,IAAA,WAAA,CAAY,KAAK,CAAA;AACjB,IAAA;AAAA,EACF,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,MAAM,QAAA,GAAW,CAAC,IAAA,KAAgB;AAChC,IAAA,IAAI,UAAA,KAAe,MAAA,EAAW,WAAA,CAAY,IAAI,CAAA;AAC9C,IAAA,aAAA,GAAgB,IAAI,CAAA;AAAA,EACtB,CAAA;AAEA,EAAA,MAAM,KAAA,GAAQb,aAAAA;AAAA,IACZ,OAAO,EAAE,KAAA,EAAO,QAAA,EAAU,QAAA,EAAS,CAAA;AAAA,IACnC,CAAC,OAAO,QAAQ;AAAA,GAClB;AAEA,EAAA,uBACEG,cAAAA,CAAC,YAAA,CAAa,QAAA,EAAb,EAAsB,OACrB,QAAA,kBAAAA,cAAAA,CAAC,KAAA,EAAA,EAAI,eAAA,EAAa,MAAC,YAAA,EAAY,QAAA,EAAU,SAAA,EAAU,UAAA,EAChD,UACH,CAAA,EACF,CAAA;AAEJ;AAEO,SAAS,QAAA,GAA8B;AAC5C,EAAA,MAAM,GAAA,GAAMW,iBAAW,YAAY,CAAA;AACnC,EAAA,IAAI,KAAK,OAAO,GAAA;AAEhB,EAAA,OAAO,EAAE,KAAA,EAAO,OAAA,EAAS,UAAU,OAAA,EAAS,QAAA,EAAU,MAAM,MAAA,EAAU;AACxE","file":"index.cjs","sourcesContent":["import { useMemo } from 'react';\nimport type { DropzoneOptions } from '../types';\nimport { useDropzone, type UseDropzoneReturn } from './useDropzone';\n\nexport type UseUploaderOptions = DropzoneOptions;\nexport type UseUploaderReturn = UseDropzoneReturn;\n\n/**\n * Alias of useDropzone with an `auto` mode default. Useful when the user\n * wants files to upload immediately on selection (no separate \"Upload\" button).\n */\nexport function useUploader(options: UseUploaderOptions = {}): UseUploaderReturn {\n const opts = useMemo<DropzoneOptions>(\n () => ({ mode: 'auto', ...options }),\n [options],\n );\n return useDropzone(opts);\n}\n","import { type HTMLAttributes, type ReactNode } from 'react';\nimport { useI18n } from '../i18n/I18nProvider';\nimport type { DropzoneState } from '../types';\nimport { cn } from '../utils/cn';\nimport { UploadIcon } from './icons';\n\nexport interface UploadAreaProps extends HTMLAttributes<HTMLDivElement> {\n state?: DropzoneState;\n disabled?: boolean;\n /** Replaces the default label content. */\n children?: ReactNode;\n /** Icon override. */\n icon?: ReactNode;\n /** Secondary descriptive line. */\n description?: ReactNode;\n}\n\n/**\n * The visual \"drop here\" surface, separated from the dropzone logic so users\n * can drop it into any container that spreads getRootProps().\n */\nexport function UploadArea({\n state = 'idle',\n disabled,\n children,\n icon,\n description,\n className,\n ...rest\n}: UploadAreaProps) {\n const { t } = useI18n();\n return (\n <div\n className={cn(\n 'flex min-h-[160px] cursor-pointer flex-col items-center justify-center gap-2 rounded-rup border-2 border-dashed border-rup-border bg-rup-bg p-6 text-center transition-all duration-200',\n 'hover:border-rup-accent/60 hover:bg-rup-accent/5',\n state === 'drag-accept' && 'border-rup-accent bg-rup-accent/10 scale-[1.01]',\n state === 'drag-reject' && 'border-rup-error bg-rup-error/10',\n disabled && 'cursor-not-allowed opacity-50',\n className,\n )}\n {...rest}\n >\n {children ?? (\n <>\n <div\n className={cn(\n 'flex h-12 w-12 items-center justify-center rounded-full bg-rup-accent/10 text-rup-accent transition-transform',\n state === 'drag-accept' && 'scale-110',\n )}\n >\n {icon ?? <UploadIcon width={24} height={24} />}\n </div>\n <p className=\"text-sm font-medium text-rup-fg\">\n {state === 'drag-accept' ? t.dropHere : t.dragOrClick}\n </p>\n {description && <p className=\"text-xs text-rup-muted\">{description}</p>}\n </>\n )}\n </div>\n );\n}\n","import { type ReactNode } from 'react';\nimport { useDropzone, type UseDropzoneReturn } from '../hooks/useDropzone';\nimport type { DropzoneOptions, FileMetadata, UploadFile } from '../types';\nimport { cn } from '../utils/cn';\nimport { UploadArea } from './UploadArea';\nimport { UploadGallery, type GalleryLayout } from './UploadGallery';\nimport type { ProgressVariant } from './UploadProgress';\n\nexport interface DropzoneProps extends DropzoneOptions {\n /** Render-prop API for full control. */\n children?: (api: UseDropzoneReturn) => ReactNode;\n /** Hide the built-in file list. Default false. */\n hideGallery?: boolean;\n /** Layout of the built-in gallery. Default 'list'. */\n galleryLayout?: GalleryLayout;\n /** Pick a progress visualization. */\n progressVariant?: ProgressVariant;\n /** Progress bar height (px) or circle diameter (px). */\n progressSize?: number;\n /** Enable the built-in fullscreen preview modal. */\n previewable?: boolean;\n /** Enable the built-in edit modal (rename + tags + metadata). */\n editable?: boolean;\n /** Called when the edit modal renames a file. Defaults to queue.rename. */\n onRename?: (file: UploadFile, newName: string) => void;\n /** Called when the edit modal saves metadata. Defaults to queue.setMetadata. */\n onMetadataChange?: (file: UploadFile, metadata: FileMetadata) => void;\n /** Once the file list exceeds this count, the list becomes scrollable. 0 = off. */\n scrollAfter?: number;\n /** Max CSS height for the scroll region. Default '280px'. */\n maxHeight?: string;\n /** Outer container width (any CSS value). */\n width?: string;\n /** Outer container height (any CSS value). */\n height?: string;\n /** Extra inline style merged onto the outer container. */\n style?: React.CSSProperties;\n className?: string;\n /** Hint text under the icon. */\n hint?: ReactNode;\n}\n\n/**\n * The all-in-one drop-in component. For simple cases this is all you need.\n * For more control, pass `children` as a render-prop.\n *\n * <Dropzone endpoint=\"/upload\" maxSize={5e6} previewable editable>\n * {({ getRootProps, getInputProps }) => (\n * <div {...getRootProps()}>...</div>\n * )}\n * </Dropzone>\n */\nexport function Dropzone({\n children,\n hideGallery,\n galleryLayout = 'list',\n progressVariant,\n progressSize,\n previewable,\n editable,\n onRename,\n onMetadataChange,\n scrollAfter,\n maxHeight,\n width,\n height,\n style,\n className,\n hint,\n ...options\n}: DropzoneProps) {\n const api = useDropzone(options);\n\n if (children) {\n return <>{children(api)}</>;\n }\n\n // Default rename/metadata handlers map to the queue's built-in methods so\n // the file list updates in place after editing.\n const handleRename = onRename ?? ((f, newName) => api.rename(f.id, newName));\n const handleMetadata = onMetadataChange ?? ((f, m) => api.setMetadata(f.id, m));\n\n // width applies to the OUTER wrapper (drives the whole component's width);\n // height applies to the DROP SURFACE so the file gallery below stays at its\n // natural height (otherwise a small height value squashes the list).\n const containerStyle =\n width !== undefined || style !== undefined\n ? { width, ...style }\n : undefined;\n const surfaceStyle = height !== undefined ? { height } : undefined;\n\n return (\n <div className={cn('flex flex-col gap-4', className)} style={containerStyle}>\n <UploadArea\n state={api.state}\n disabled={options.disabled}\n description={hint}\n style={surfaceStyle}\n {...api.getRootProps()}\n />\n <input {...api.getInputProps()} />\n {!hideGallery && api.files.length > 0 && (\n <UploadGallery\n files={api.files}\n layout={galleryLayout}\n progressVariant={progressVariant}\n progressSize={progressSize}\n previewable={previewable}\n editable={editable}\n scrollAfter={scrollAfter}\n maxHeight={maxHeight}\n onRename={handleRename}\n onMetadataChange={handleMetadata}\n onRemove={(f) => api.remove(f.id)}\n onRetry={(f) => api.retry(f.id)}\n onPause={(f) => api.pause(f.id)}\n onResume={(f) => api.resume(f.id)}\n onCancel={(f) => api.cancel(f.id)}\n />\n )}\n </div>\n );\n}\n","import { type ButtonHTMLAttributes, type ReactNode } from 'react';\nimport { useI18n } from '../i18n/I18nProvider';\nimport type { DropzoneOptions } from '../types';\nimport { useDropzone } from '../hooks/useDropzone';\nimport { cn } from '../utils/cn';\nimport { UploadIcon } from './icons';\n\ntype ButtonAttrs = Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n 'onDrop' | 'onPause' | 'onResume' | 'onAbort'\n>;\n\nexport interface UploadButtonProps extends ButtonAttrs, DropzoneOptions {\n /** Visual variant. */\n variant?: 'primary' | 'secondary' | 'ghost' | 'outline';\n /** Show the upload icon. */\n icon?: ReactNode;\n /** Hide the icon entirely. */\n iconOnly?: boolean;\n}\n\nconst variantClasses: Record<NonNullable<UploadButtonProps['variant']>, string> = {\n primary: 'bg-rup-accent text-rup-accent-fg hover:opacity-90',\n secondary: 'bg-rup-border text-rup-fg hover:bg-rup-border/80',\n ghost: 'bg-transparent text-rup-fg hover:bg-rup-border/30',\n outline: 'border border-rup-border bg-transparent text-rup-fg hover:bg-rup-border/30',\n};\n\n/**\n * Standalone button that opens the file picker. Internally uses useDropzone\n * with `noBubble` so nesting inside another dropzone is safe.\n */\nexport function UploadButton({\n variant = 'primary',\n icon,\n iconOnly,\n children,\n className,\n disabled,\n // Filter DropzoneOptions out from button props.\n multiple,\n directory,\n clipboard,\n accept,\n minSize,\n maxSize,\n maxFiles,\n rejectDuplicates,\n validators,\n onDrop,\n onDropAccepted,\n onDropRejected,\n onUploadStart,\n onUploadProgress,\n onUploadSuccess,\n onUploadError,\n onRetry,\n onPause,\n onResume,\n onRemove,\n onAllComplete,\n endpoint,\n method,\n headers,\n fieldName,\n formData,\n withCredentials,\n concurrency,\n strategy,\n mode,\n retries,\n retryBackoffMs,\n chunkSize,\n cloud,\n getUploadToken,\n virusScan,\n ...buttonProps\n}: UploadButtonProps) {\n const { t } = useI18n();\n const { getInputProps, open } = useDropzone({\n multiple,\n directory,\n clipboard: clipboard ?? false,\n accept,\n minSize,\n maxSize,\n maxFiles,\n rejectDuplicates,\n validators,\n onDrop,\n onDropAccepted,\n onDropRejected,\n onUploadStart,\n onUploadProgress,\n onUploadSuccess,\n onUploadError,\n onRetry,\n onPause,\n onResume,\n onRemove,\n onAllComplete,\n endpoint,\n method,\n headers,\n fieldName,\n formData,\n withCredentials,\n concurrency,\n strategy,\n mode,\n retries,\n retryBackoffMs,\n chunkSize,\n cloud,\n getUploadToken,\n virusScan,\n disabled: !!disabled,\n });\n\n return (\n <>\n <button\n type=\"button\"\n onClick={open}\n disabled={disabled}\n className={cn(\n 'inline-flex items-center gap-2 rounded-rup px-4 py-2 text-sm font-medium transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rup-accent disabled:cursor-not-allowed disabled:opacity-50',\n variantClasses[variant],\n className,\n )}\n {...buttonProps}\n >\n {icon !== false && (icon ?? <UploadIcon width={14} height={14} />)}\n {!iconOnly && (children ?? t.browse)}\n </button>\n <input {...getInputProps()} />\n </>\n );\n}\n","import { useI18n } from '../i18n/I18nProvider';\nimport type { ValidationError, ValidationErrorCode } from '../types';\nimport { cn } from '../utils/cn';\nimport { AlertIcon } from './icons';\nimport { UploadModal } from './UploadModal';\n\nexport interface ValidationErrorsModalProps {\n open: boolean;\n errors: ValidationError[];\n onClose: () => void;\n /** Modal title override. Default uses the localized \"Failed\" string. */\n title?: React.ReactNode;\n}\n\n/**\n * Shows validation rejections (wrong type, too large, too many, …) in a modal.\n * Use together with the `onDropRejected` callback or by reading\n * `api.rejected` from `useDropzone`.\n *\n * const [errors, setErrors] = useState<ValidationError[]>([]);\n * <Dropzone accept=\"image/*\" onDropRejected={setErrors} />\n * <ValidationErrorsModal open={errors.length > 0} errors={errors} onClose={() => setErrors([])} />\n */\nexport function ValidationErrorsModal({\n open,\n errors,\n onClose,\n title,\n}: ValidationErrorsModalProps) {\n const { t } = useI18n();\n if (!open || errors.length === 0) return null;\n\n return (\n <UploadModal\n open={open}\n onClose={onClose}\n title={\n <span className=\"inline-flex items-center gap-2 text-rup-error\">\n <AlertIcon width={18} height={18} />\n {title ?? t.error}\n </span>\n }\n size=\"md\"\n >\n <div className=\"flex flex-col gap-3\">\n <p className=\"text-sm text-rup-muted\">\n {errors.length} {errors.length === 1 ? 'file was' : 'files were'} rejected:\n </p>\n <ul className=\"flex max-h-[50vh] flex-col gap-2 overflow-y-auto rup-scrollbar\">\n {errors.map((err, i) => (\n <li\n key={i}\n className=\"flex items-start gap-2 rounded-rup border border-rup-error/30 bg-rup-error/5 px-3 py-2 text-sm\"\n >\n <AlertIcon\n width={14}\n height={14}\n className=\"mt-0.5 shrink-0 text-rup-error\"\n />\n <div className=\"min-w-0 flex-1\">\n {err.file && (\n <p\n className=\"truncate font-medium text-rup-fg\"\n title={err.file.name}\n >\n {err.file.name}\n </p>\n )}\n <p className=\"text-xs text-rup-error\">{err.message}</p>\n </div>\n <span\n className={cn(\n 'shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide',\n badgeClassesByCode[err.code] ?? 'bg-rup-error/15 text-rup-error',\n )}\n >\n {err.code}\n </span>\n </li>\n ))}\n </ul>\n <div className=\"flex items-center justify-end pt-1\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-md bg-rup-accent px-4 py-2 text-sm font-medium text-rup-accent-fg hover:opacity-90\"\n >\n OK\n </button>\n </div>\n </div>\n </UploadModal>\n );\n}\n\nconst badgeClassesByCode: Partial<Record<ValidationErrorCode, string>> = {\n 'file-too-large': 'bg-amber-500/15 text-amber-600 dark:text-amber-400',\n 'file-too-small': 'bg-amber-500/15 text-amber-600 dark:text-amber-400',\n 'file-invalid-type': 'bg-rup-error/15 text-rup-error',\n 'too-many-files': 'bg-blue-500/15 text-blue-600 dark:text-blue-400',\n 'duplicate-file': 'bg-violet-500/15 text-violet-600 dark:text-violet-400',\n};\n","import {\n type ReactNode,\n createContext,\n useContext,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport type { Theme } from '../types';\n\ninterface ThemeContextValue {\n theme: Theme;\n resolved: 'light' | 'dark';\n setTheme: (theme: Theme) => void;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nexport interface ThemeProviderProps {\n /** Initial theme. Default 'auto'. */\n defaultTheme?: Theme;\n /** Override of resolved theme; useful for controlled mode. */\n theme?: Theme;\n /** Called when the user toggles the theme. */\n onThemeChange?: (theme: Theme) => void;\n children: ReactNode;\n}\n\n/**\n * Wraps children in a theme context and applies a data-theme attribute to the\n * scope so CSS variables can switch values. SSR-safe: `resolved` defaults to\n * 'light' on the server and reconciles after hydration.\n */\nexport function ThemeProvider({\n defaultTheme = 'auto',\n theme: controlled,\n onThemeChange,\n children,\n}: ThemeProviderProps) {\n const [internal, setInternal] = useState<Theme>(controlled ?? defaultTheme);\n const theme = controlled ?? internal;\n const [resolved, setResolved] = useState<'light' | 'dark'>('light');\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n if (theme === 'auto') {\n const mql = window.matchMedia('(prefers-color-scheme: dark)');\n const apply = () => setResolved(mql.matches ? 'dark' : 'light');\n apply();\n mql.addEventListener('change', apply);\n return () => mql.removeEventListener('change', apply);\n }\n setResolved(theme);\n return;\n }, [theme]);\n\n const setTheme = (next: Theme) => {\n if (controlled === undefined) setInternal(next);\n onThemeChange?.(next);\n };\n\n const value = useMemo<ThemeContextValue>(\n () => ({ theme, resolved, setTheme }),\n [theme, resolved],\n );\n\n return (\n <ThemeContext.Provider value={value}>\n <div data-rup-root data-theme={resolved} className=\"contents\">\n {children}\n </div>\n </ThemeContext.Provider>\n );\n}\n\nexport function useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (ctx) return ctx;\n // Fallback: behave as if a ThemeProvider with default 'light' was present.\n return { theme: 'light', resolved: 'light', setTheme: () => undefined };\n}\n"]}