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.js ADDED
@@ -0,0 +1,351 @@
1
+ "use client";
2
+ import { useDropzone, useI18n, UploadIcon, cn, UploadGallery, UploadModal, AlertIcon } from './chunk-JI2V5ITY.js';
3
+ export { AlertIcon, CheckIcon, CloseIcon, DownloadIcon, EditIcon, EyeIcon, FileEditModal, FileIcon, FilePreviewModal, FolderIcon, I18nProvider, ImageIcon, MaximizeIcon, PauseIcon, PlayIcon, RetryIcon, TagIcon, TrashIcon, UploadGallery, UploadIcon, UploadModal, UploadPreview, UploadProgress, UploadQueue, cn, detectSignature, formatBytes, formatEta, formatPercent, formatSpeed, generatePreview, getFileCategory, matchesAccept, revokePreview, rtlLocales, translations, useDropzone, useFilePreview, useI18n, useUploadProgress, useUploadQueue, validateBatch, validateFile, wrapFile } from './chunk-JI2V5ITY.js';
4
+ import { createContext, useMemo, useState, useEffect, useContext } from 'react';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
+
7
+ function useUploader(options = {}) {
8
+ const opts = useMemo(
9
+ () => ({ mode: "auto", ...options }),
10
+ [options]
11
+ );
12
+ return useDropzone(opts);
13
+ }
14
+ function UploadArea({
15
+ state = "idle",
16
+ disabled,
17
+ children,
18
+ icon,
19
+ description,
20
+ className,
21
+ ...rest
22
+ }) {
23
+ const { t } = useI18n();
24
+ return /* @__PURE__ */ jsx(
25
+ "div",
26
+ {
27
+ className: cn(
28
+ "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",
29
+ "hover:border-rup-accent/60 hover:bg-rup-accent/5",
30
+ state === "drag-accept" && "border-rup-accent bg-rup-accent/10 scale-[1.01]",
31
+ state === "drag-reject" && "border-rup-error bg-rup-error/10",
32
+ disabled && "cursor-not-allowed opacity-50",
33
+ className
34
+ ),
35
+ ...rest,
36
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
37
+ /* @__PURE__ */ jsx(
38
+ "div",
39
+ {
40
+ className: cn(
41
+ "flex h-12 w-12 items-center justify-center rounded-full bg-rup-accent/10 text-rup-accent transition-transform",
42
+ state === "drag-accept" && "scale-110"
43
+ ),
44
+ children: icon ?? /* @__PURE__ */ jsx(UploadIcon, { width: 24, height: 24 })
45
+ }
46
+ ),
47
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-rup-fg", children: state === "drag-accept" ? t.dropHere : t.dragOrClick }),
48
+ description && /* @__PURE__ */ jsx("p", { className: "text-xs text-rup-muted", children: description })
49
+ ] })
50
+ }
51
+ );
52
+ }
53
+ function Dropzone({
54
+ children,
55
+ hideGallery,
56
+ galleryLayout = "list",
57
+ progressVariant,
58
+ progressSize,
59
+ previewable,
60
+ editable,
61
+ onRename,
62
+ onMetadataChange,
63
+ scrollAfter,
64
+ maxHeight,
65
+ width,
66
+ height,
67
+ style,
68
+ className,
69
+ hint,
70
+ ...options
71
+ }) {
72
+ const api = useDropzone(options);
73
+ if (children) {
74
+ return /* @__PURE__ */ jsx(Fragment, { children: children(api) });
75
+ }
76
+ const handleRename = onRename ?? ((f, newName) => api.rename(f.id, newName));
77
+ const handleMetadata = onMetadataChange ?? ((f, m) => api.setMetadata(f.id, m));
78
+ const containerStyle = width !== void 0 || style !== void 0 ? { width, ...style } : void 0;
79
+ const surfaceStyle = height !== void 0 ? { height } : void 0;
80
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-4", className), style: containerStyle, children: [
81
+ /* @__PURE__ */ jsx(
82
+ UploadArea,
83
+ {
84
+ state: api.state,
85
+ disabled: options.disabled,
86
+ description: hint,
87
+ style: surfaceStyle,
88
+ ...api.getRootProps()
89
+ }
90
+ ),
91
+ /* @__PURE__ */ jsx("input", { ...api.getInputProps() }),
92
+ !hideGallery && api.files.length > 0 && /* @__PURE__ */ jsx(
93
+ UploadGallery,
94
+ {
95
+ files: api.files,
96
+ layout: galleryLayout,
97
+ progressVariant,
98
+ progressSize,
99
+ previewable,
100
+ editable,
101
+ scrollAfter,
102
+ maxHeight,
103
+ onRename: handleRename,
104
+ onMetadataChange: handleMetadata,
105
+ onRemove: (f) => api.remove(f.id),
106
+ onRetry: (f) => api.retry(f.id),
107
+ onPause: (f) => api.pause(f.id),
108
+ onResume: (f) => api.resume(f.id),
109
+ onCancel: (f) => api.cancel(f.id)
110
+ }
111
+ )
112
+ ] });
113
+ }
114
+ var variantClasses = {
115
+ primary: "bg-rup-accent text-rup-accent-fg hover:opacity-90",
116
+ secondary: "bg-rup-border text-rup-fg hover:bg-rup-border/80",
117
+ ghost: "bg-transparent text-rup-fg hover:bg-rup-border/30",
118
+ outline: "border border-rup-border bg-transparent text-rup-fg hover:bg-rup-border/30"
119
+ };
120
+ function UploadButton({
121
+ variant = "primary",
122
+ icon,
123
+ iconOnly,
124
+ children,
125
+ className,
126
+ disabled,
127
+ // Filter DropzoneOptions out from button props.
128
+ multiple,
129
+ directory,
130
+ clipboard,
131
+ accept,
132
+ minSize,
133
+ maxSize,
134
+ maxFiles,
135
+ rejectDuplicates,
136
+ validators,
137
+ onDrop,
138
+ onDropAccepted,
139
+ onDropRejected,
140
+ onUploadStart,
141
+ onUploadProgress,
142
+ onUploadSuccess,
143
+ onUploadError,
144
+ onRetry,
145
+ onPause,
146
+ onResume,
147
+ onRemove,
148
+ onAllComplete,
149
+ endpoint,
150
+ method,
151
+ headers,
152
+ fieldName,
153
+ formData,
154
+ withCredentials,
155
+ concurrency,
156
+ strategy,
157
+ mode,
158
+ retries,
159
+ retryBackoffMs,
160
+ chunkSize,
161
+ cloud,
162
+ getUploadToken,
163
+ virusScan,
164
+ ...buttonProps
165
+ }) {
166
+ const { t } = useI18n();
167
+ const { getInputProps, open } = useDropzone({
168
+ multiple,
169
+ directory,
170
+ clipboard: clipboard ?? false,
171
+ accept,
172
+ minSize,
173
+ maxSize,
174
+ maxFiles,
175
+ rejectDuplicates,
176
+ validators,
177
+ onDrop,
178
+ onDropAccepted,
179
+ onDropRejected,
180
+ onUploadStart,
181
+ onUploadProgress,
182
+ onUploadSuccess,
183
+ onUploadError,
184
+ onRetry,
185
+ onPause,
186
+ onResume,
187
+ onRemove,
188
+ onAllComplete,
189
+ endpoint,
190
+ method,
191
+ headers,
192
+ fieldName,
193
+ formData,
194
+ withCredentials,
195
+ concurrency,
196
+ strategy,
197
+ mode,
198
+ retries,
199
+ retryBackoffMs,
200
+ chunkSize,
201
+ cloud,
202
+ getUploadToken,
203
+ virusScan,
204
+ disabled: !!disabled
205
+ });
206
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
207
+ /* @__PURE__ */ jsxs(
208
+ "button",
209
+ {
210
+ type: "button",
211
+ onClick: open,
212
+ disabled,
213
+ className: cn(
214
+ "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",
215
+ variantClasses[variant],
216
+ className
217
+ ),
218
+ ...buttonProps,
219
+ children: [
220
+ icon !== false && (icon ?? /* @__PURE__ */ jsx(UploadIcon, { width: 14, height: 14 })),
221
+ !iconOnly && (children ?? t.browse)
222
+ ]
223
+ }
224
+ ),
225
+ /* @__PURE__ */ jsx("input", { ...getInputProps() })
226
+ ] });
227
+ }
228
+ function ValidationErrorsModal({
229
+ open,
230
+ errors,
231
+ onClose,
232
+ title
233
+ }) {
234
+ const { t } = useI18n();
235
+ if (!open || errors.length === 0) return null;
236
+ return /* @__PURE__ */ jsx(
237
+ UploadModal,
238
+ {
239
+ open,
240
+ onClose,
241
+ title: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 text-rup-error", children: [
242
+ /* @__PURE__ */ jsx(AlertIcon, { width: 18, height: 18 }),
243
+ title ?? t.error
244
+ ] }),
245
+ size: "md",
246
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
247
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-rup-muted", children: [
248
+ errors.length,
249
+ " ",
250
+ errors.length === 1 ? "file was" : "files were",
251
+ " rejected:"
252
+ ] }),
253
+ /* @__PURE__ */ jsx("ul", { className: "flex max-h-[50vh] flex-col gap-2 overflow-y-auto rup-scrollbar", children: errors.map((err, i) => /* @__PURE__ */ jsxs(
254
+ "li",
255
+ {
256
+ className: "flex items-start gap-2 rounded-rup border border-rup-error/30 bg-rup-error/5 px-3 py-2 text-sm",
257
+ children: [
258
+ /* @__PURE__ */ jsx(
259
+ AlertIcon,
260
+ {
261
+ width: 14,
262
+ height: 14,
263
+ className: "mt-0.5 shrink-0 text-rup-error"
264
+ }
265
+ ),
266
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
267
+ err.file && /* @__PURE__ */ jsx(
268
+ "p",
269
+ {
270
+ className: "truncate font-medium text-rup-fg",
271
+ title: err.file.name,
272
+ children: err.file.name
273
+ }
274
+ ),
275
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-rup-error", children: err.message })
276
+ ] }),
277
+ /* @__PURE__ */ jsx(
278
+ "span",
279
+ {
280
+ className: cn(
281
+ "shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide",
282
+ badgeClassesByCode[err.code] ?? "bg-rup-error/15 text-rup-error"
283
+ ),
284
+ children: err.code
285
+ }
286
+ )
287
+ ]
288
+ },
289
+ i
290
+ )) }),
291
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end pt-1", children: /* @__PURE__ */ jsx(
292
+ "button",
293
+ {
294
+ type: "button",
295
+ onClick: onClose,
296
+ className: "rounded-md bg-rup-accent px-4 py-2 text-sm font-medium text-rup-accent-fg hover:opacity-90",
297
+ children: "OK"
298
+ }
299
+ ) })
300
+ ] })
301
+ }
302
+ );
303
+ }
304
+ var badgeClassesByCode = {
305
+ "file-too-large": "bg-amber-500/15 text-amber-600 dark:text-amber-400",
306
+ "file-too-small": "bg-amber-500/15 text-amber-600 dark:text-amber-400",
307
+ "file-invalid-type": "bg-rup-error/15 text-rup-error",
308
+ "too-many-files": "bg-blue-500/15 text-blue-600 dark:text-blue-400",
309
+ "duplicate-file": "bg-violet-500/15 text-violet-600 dark:text-violet-400"
310
+ };
311
+ var ThemeContext = createContext(null);
312
+ function ThemeProvider({
313
+ defaultTheme = "auto",
314
+ theme: controlled,
315
+ onThemeChange,
316
+ children
317
+ }) {
318
+ const [internal, setInternal] = useState(controlled ?? defaultTheme);
319
+ const theme = controlled ?? internal;
320
+ const [resolved, setResolved] = useState("light");
321
+ useEffect(() => {
322
+ if (typeof window === "undefined") return;
323
+ if (theme === "auto") {
324
+ const mql = window.matchMedia("(prefers-color-scheme: dark)");
325
+ const apply = () => setResolved(mql.matches ? "dark" : "light");
326
+ apply();
327
+ mql.addEventListener("change", apply);
328
+ return () => mql.removeEventListener("change", apply);
329
+ }
330
+ setResolved(theme);
331
+ return;
332
+ }, [theme]);
333
+ const setTheme = (next) => {
334
+ if (controlled === void 0) setInternal(next);
335
+ onThemeChange?.(next);
336
+ };
337
+ const value = useMemo(
338
+ () => ({ theme, resolved, setTheme }),
339
+ [theme, resolved]
340
+ );
341
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { "data-rup-root": true, "data-theme": resolved, className: "contents", children }) });
342
+ }
343
+ function useTheme() {
344
+ const ctx = useContext(ThemeContext);
345
+ if (ctx) return ctx;
346
+ return { theme: "light", resolved: "light", setTheme: () => void 0 };
347
+ }
348
+
349
+ export { Dropzone, ThemeProvider, UploadArea, UploadButton, ValidationErrorsModal, useTheme, useUploader };
350
+ //# sourceMappingURL=index.js.map
351
+ //# sourceMappingURL=index.js.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":["jsx","Fragment","jsxs","useMemo"],"mappings":"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","file":"index.js","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"]}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Base CSS variables for react-upload-pro. Imported by consumers via
3
+ * import 'react-upload-pro/styles.css';
4
+ * Variables are defined in RGB-triplet form so Tailwind alpha modifiers work.
5
+ */
6
+
7
+ [data-rup-root],
8
+ [data-rup-root][data-theme='light'] {
9
+ --rup-bg: 255 255 255;
10
+ --rup-fg: 17 24 39;
11
+ --rup-muted: 107 114 128;
12
+ --rup-border: 229 231 235;
13
+ --rup-accent: 79 70 229;
14
+ --rup-accent-fg: 255 255 255;
15
+ --rup-success: 34 197 94;
16
+ --rup-error: 239 68 68;
17
+ --rup-warning: 234 179 8;
18
+ --rup-radius: 0.75rem;
19
+ --rup-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05), 0 4px 12px -2px rgb(0 0 0 / 0.06);
20
+ }
21
+
22
+ [data-rup-root][data-theme='dark'] {
23
+ --rup-bg: 17 24 39;
24
+ --rup-fg: 243 244 246;
25
+ --rup-muted: 156 163 175;
26
+ --rup-border: 55 65 81;
27
+ --rup-accent: 129 140 248;
28
+ --rup-accent-fg: 17 24 39;
29
+ --rup-success: 74 222 128;
30
+ --rup-error: 248 113 113;
31
+ --rup-warning: 250 204 21;
32
+ --rup-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.4), 0 4px 12px -2px rgb(0 0 0 / 0.5);
33
+ }
34
+
35
+ [dir='rtl'] [data-rup-root] {
36
+ direction: rtl;
37
+ }
38
+
39
+ @keyframes rup-pulse {
40
+ 0%, 100% { opacity: 1; }
41
+ 50% { opacity: 0.5; }
42
+ }
43
+
44
+ @keyframes rup-shimmer {
45
+ 0% { background-position: -200% 0; }
46
+ 100% { background-position: 200% 0; }
47
+ }
48
+
49
+ /* Themed scrollbar used by bounded gallery lists and any consumer who opts in
50
+ via the `rup-scrollbar` utility class. Falls back to the browser default
51
+ when neither pseudo-element nor `scrollbar-color` is supported. */
52
+ .rup-scrollbar {
53
+ scrollbar-width: thin;
54
+ scrollbar-color: rgb(var(--rup-accent) / 0.7) transparent;
55
+ }
56
+
57
+ .rup-scrollbar::-webkit-scrollbar {
58
+ width: 8px;
59
+ height: 8px;
60
+ }
61
+
62
+ .rup-scrollbar::-webkit-scrollbar-track {
63
+ background-color: rgb(var(--rup-border) / 0.4);
64
+ border-radius: 999px;
65
+ }
66
+
67
+ .rup-scrollbar::-webkit-scrollbar-thumb {
68
+ background-color: rgb(var(--rup-accent) / 0.7);
69
+ border-radius: 999px;
70
+ border: 2px solid transparent;
71
+ background-clip: content-box;
72
+ transition: background-color 0.15s ease;
73
+ }
74
+
75
+ .rup-scrollbar::-webkit-scrollbar-thumb:hover {
76
+ background-color: rgb(var(--rup-accent) / 1);
77
+ }
78
+
79
+ .rup-scrollbar::-webkit-scrollbar-corner {
80
+ background: transparent;
81
+ }