@uploadista/react 0.0.3
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/.turbo/turbo-check.log +89 -0
- package/FLOW_UPLOAD.md +307 -0
- package/LICENSE +21 -0
- package/README.md +318 -0
- package/package.json +35 -0
- package/src/components/flow-upload-list.tsx +614 -0
- package/src/components/flow-upload-zone.tsx +441 -0
- package/src/components/upload-list.tsx +626 -0
- package/src/components/upload-zone.tsx +545 -0
- package/src/components/uploadista-provider.tsx +190 -0
- package/src/hooks/use-drag-drop.ts +404 -0
- package/src/hooks/use-flow-upload.ts +568 -0
- package/src/hooks/use-multi-flow-upload.ts +477 -0
- package/src/hooks/use-multi-upload.ts +691 -0
- package/src/hooks/use-upload-metrics.ts +585 -0
- package/src/hooks/use-upload.ts +411 -0
- package/src/hooks/use-uploadista-client.ts +145 -0
- package/src/index.ts +87 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upload Zone Components
|
|
3
|
+
*
|
|
4
|
+
* Enhanced error handling features:
|
|
5
|
+
* - MIME type validation with detailed error messages
|
|
6
|
+
* - File count validation (single vs multiple mode)
|
|
7
|
+
* - Custom validation error callbacks
|
|
8
|
+
* - Built-in error display in SimpleUploadZone
|
|
9
|
+
* - Configurable error styling
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type React from "react";
|
|
13
|
+
import { useCallback } from "react";
|
|
14
|
+
import type {
|
|
15
|
+
DragDropOptions,
|
|
16
|
+
UseDragDropReturn,
|
|
17
|
+
} from "../hooks/use-drag-drop";
|
|
18
|
+
import { useDragDrop } from "../hooks/use-drag-drop";
|
|
19
|
+
import type {
|
|
20
|
+
MultiUploadOptions,
|
|
21
|
+
UseMultiUploadReturn,
|
|
22
|
+
} from "../hooks/use-multi-upload";
|
|
23
|
+
import { useMultiUpload } from "../hooks/use-multi-upload";
|
|
24
|
+
import type { UseUploadOptions, UseUploadReturn } from "../hooks/use-upload";
|
|
25
|
+
import { useUpload } from "../hooks/use-upload";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Render props passed to the UploadZone children function.
|
|
29
|
+
* Provides access to drag-drop state, upload controls, and helper functions.
|
|
30
|
+
*
|
|
31
|
+
* @property dragDrop - Complete drag-and-drop state and event handlers
|
|
32
|
+
* @property upload - Single upload hook (null when multiple=true)
|
|
33
|
+
* @property multiUpload - Multi-upload hook (null when multiple=false)
|
|
34
|
+
* @property openFilePicker - Programmatically trigger file selection dialog
|
|
35
|
+
* @property isActive - True when dragging over zone or files selected
|
|
36
|
+
* @property isProcessing - True when uploads are in progress
|
|
37
|
+
*/
|
|
38
|
+
export interface UploadZoneRenderProps {
|
|
39
|
+
/**
|
|
40
|
+
* Drag and drop state and handlers
|
|
41
|
+
*/
|
|
42
|
+
dragDrop: UseDragDropReturn;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Single upload functionality (if not using multi-upload)
|
|
46
|
+
*/
|
|
47
|
+
upload: UseUploadReturn | null;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Multi-upload functionality (if using multi-upload)
|
|
51
|
+
*/
|
|
52
|
+
multiUpload: UseMultiUploadReturn | null;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Helper function to open file picker
|
|
56
|
+
*/
|
|
57
|
+
openFilePicker: () => void;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Whether the zone is currently active (dragging or uploading)
|
|
61
|
+
*/
|
|
62
|
+
isActive: boolean;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether files are being processed
|
|
66
|
+
*/
|
|
67
|
+
isProcessing: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Props for the UploadZone component.
|
|
72
|
+
* Combines drag-drop options with upload configuration.
|
|
73
|
+
*
|
|
74
|
+
* @property multiple - Enable multi-file selection and upload (default: true)
|
|
75
|
+
* @property multiUploadOptions - Configuration for multi-upload mode
|
|
76
|
+
* @property uploadOptions - Configuration for single-upload mode
|
|
77
|
+
* @property children - Render function receiving upload zone state
|
|
78
|
+
* @property onUploadStart - Called when files pass validation and upload begins
|
|
79
|
+
* @property onValidationError - Called when file validation fails
|
|
80
|
+
* @property accept - Accepted file types (e.g., ['image/*', '.pdf'])
|
|
81
|
+
* @property maxFiles - Maximum number of files allowed
|
|
82
|
+
* @property maxFileSize - Maximum file size in bytes
|
|
83
|
+
* @property validator - Custom validation function
|
|
84
|
+
*/
|
|
85
|
+
export interface UploadZoneProps
|
|
86
|
+
extends Omit<DragDropOptions, "onFilesReceived"> {
|
|
87
|
+
/**
|
|
88
|
+
* Whether to enable multi-file upload mode
|
|
89
|
+
*/
|
|
90
|
+
multiple?: boolean;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Multi-upload specific options (only used when multiple=true)
|
|
94
|
+
*/
|
|
95
|
+
multiUploadOptions?: MultiUploadOptions;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Single upload specific options (only used when multiple=false)
|
|
99
|
+
*/
|
|
100
|
+
uploadOptions?: UseUploadOptions;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Render prop that receives upload zone state and handlers
|
|
104
|
+
*/
|
|
105
|
+
children: (props: UploadZoneRenderProps) => React.ReactNode;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Called when files are processed and uploads begin
|
|
109
|
+
*/
|
|
110
|
+
onUploadStart?: (files: File[]) => void;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Called when validation errors occur
|
|
114
|
+
*/
|
|
115
|
+
onValidationError?: (errors: string[]) => void;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Headless upload zone component that combines drag and drop functionality
|
|
120
|
+
* with upload management. Uses render props pattern for maximum flexibility.
|
|
121
|
+
* Includes enhanced error handling for MIME type validation and file count validation.
|
|
122
|
+
*
|
|
123
|
+
* @param props - Upload zone configuration and render prop
|
|
124
|
+
* @returns Rendered upload zone using the provided render prop
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```tsx
|
|
128
|
+
* // Single file upload zone with error handling
|
|
129
|
+
* <UploadZone
|
|
130
|
+
* multiple={false}
|
|
131
|
+
* accept={['image/*']}
|
|
132
|
+
* maxFileSize={5 * 1024 * 1024}
|
|
133
|
+
* onValidationError={(errors) => {
|
|
134
|
+
* console.error('Validation errors:', errors);
|
|
135
|
+
* }}
|
|
136
|
+
* uploadOptions={{
|
|
137
|
+
* onSuccess: (result) => console.log('Upload complete:', result),
|
|
138
|
+
* onError: (error) => console.error('Upload failed:', error),
|
|
139
|
+
* }}
|
|
140
|
+
* >
|
|
141
|
+
* {({ dragDrop, upload, openFilePicker, isActive }) => (
|
|
142
|
+
* <div {...dragDrop.dragHandlers} onClick={openFilePicker}>
|
|
143
|
+
* {dragDrop.state.isDragging ? (
|
|
144
|
+
* <p>Drop file here...</p>
|
|
145
|
+
* ) : upload?.isUploading ? (
|
|
146
|
+
* <p>Uploading... {upload.state.progress}%</p>
|
|
147
|
+
* ) : (
|
|
148
|
+
* <p>Drag a file here or click to select</p>
|
|
149
|
+
* )}
|
|
150
|
+
*
|
|
151
|
+
* {dragDrop.state.errors.length > 0 && (
|
|
152
|
+
* <div style={{ color: 'red' }}>
|
|
153
|
+
* {dragDrop.state.errors.map((error, index) => (
|
|
154
|
+
* <p key={index}>{error}</p>
|
|
155
|
+
* ))}
|
|
156
|
+
* </div>
|
|
157
|
+
* )}
|
|
158
|
+
*
|
|
159
|
+
* <input {...dragDrop.inputProps} />
|
|
160
|
+
* </div>
|
|
161
|
+
* )}
|
|
162
|
+
* </UploadZone>
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
export function UploadZone({
|
|
166
|
+
children,
|
|
167
|
+
multiple = true,
|
|
168
|
+
multiUploadOptions = {},
|
|
169
|
+
uploadOptions = {},
|
|
170
|
+
onUploadStart,
|
|
171
|
+
onValidationError,
|
|
172
|
+
...dragDropOptions
|
|
173
|
+
}: UploadZoneProps) {
|
|
174
|
+
// Always initialize both hooks, but only use the appropriate one
|
|
175
|
+
const singleUpload = useUpload(uploadOptions);
|
|
176
|
+
const multiUpload = useMultiUpload(multiUploadOptions);
|
|
177
|
+
|
|
178
|
+
// Enhanced validation function for better error handling
|
|
179
|
+
const enhancedValidator = useCallback(
|
|
180
|
+
(files: File[]): string[] | null => {
|
|
181
|
+
const errors: string[] = [];
|
|
182
|
+
|
|
183
|
+
// Check file count based on multiple setting
|
|
184
|
+
if (!multiple && files.length > 1) {
|
|
185
|
+
errors.push(
|
|
186
|
+
`Single file mode is enabled. Please select only one file. You selected ${files.length} files.`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Enhanced MIME type validation with better error messages
|
|
191
|
+
if (dragDropOptions.accept && dragDropOptions.accept.length > 0) {
|
|
192
|
+
const invalidFiles = files.filter((file) => {
|
|
193
|
+
return !dragDropOptions.accept?.some((acceptType) => {
|
|
194
|
+
if (acceptType.startsWith(".")) {
|
|
195
|
+
// File extension check
|
|
196
|
+
return file.name.toLowerCase().endsWith(acceptType.toLowerCase());
|
|
197
|
+
} else {
|
|
198
|
+
// MIME type check (supports wildcards like image/*)
|
|
199
|
+
if (acceptType.endsWith("/*")) {
|
|
200
|
+
const baseType = acceptType.slice(0, -2);
|
|
201
|
+
return file.type.startsWith(baseType);
|
|
202
|
+
} else {
|
|
203
|
+
return file.type === acceptType;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
if (invalidFiles.length > 0) {
|
|
210
|
+
const fileNames = invalidFiles
|
|
211
|
+
.map((f) => `"${f.name}" (${f.type})`)
|
|
212
|
+
.join(", ");
|
|
213
|
+
const acceptedTypes = dragDropOptions.accept.join(", ");
|
|
214
|
+
errors.push(
|
|
215
|
+
`Invalid file type(s): ${fileNames}. Accepted types: ${acceptedTypes}.`,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return errors.length > 0 ? errors : null;
|
|
221
|
+
},
|
|
222
|
+
[multiple, dragDropOptions.accept],
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// Handle file processing
|
|
226
|
+
const handleFilesReceived = (files: File[]) => {
|
|
227
|
+
onUploadStart?.(files);
|
|
228
|
+
|
|
229
|
+
if (multiple && multiUpload) {
|
|
230
|
+
// Add files to multi-upload queue
|
|
231
|
+
multiUpload.addFiles(files);
|
|
232
|
+
|
|
233
|
+
// Auto-start uploads if configured to do so
|
|
234
|
+
// Note: This could be made configurable with an autoStart prop
|
|
235
|
+
setTimeout(() => multiUpload.startAll(), 0);
|
|
236
|
+
} else if (!multiple && singleUpload && files.length > 0 && files[0]) {
|
|
237
|
+
// Start single file upload
|
|
238
|
+
singleUpload.upload(files[0]);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// Handle validation errors
|
|
243
|
+
const handleValidationError = useCallback(
|
|
244
|
+
(errors: string[]) => {
|
|
245
|
+
console.error("Upload zone validation errors:", errors);
|
|
246
|
+
// Call the custom error handler if provided
|
|
247
|
+
onValidationError?.(errors);
|
|
248
|
+
},
|
|
249
|
+
[onValidationError],
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
// Initialize drag and drop with enhanced validation
|
|
253
|
+
const dragDrop = useDragDrop({
|
|
254
|
+
...dragDropOptions,
|
|
255
|
+
multiple,
|
|
256
|
+
validator: enhancedValidator,
|
|
257
|
+
onFilesReceived: handleFilesReceived,
|
|
258
|
+
onValidationError: handleValidationError,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Determine active state
|
|
262
|
+
const isActive = dragDrop.state.isDragging || dragDrop.state.isOver;
|
|
263
|
+
|
|
264
|
+
// Determine processing state
|
|
265
|
+
const isProcessing = multiple
|
|
266
|
+
? (multiUpload?.state.isUploading ?? false)
|
|
267
|
+
: (singleUpload?.isUploading ?? false);
|
|
268
|
+
|
|
269
|
+
// Create render props object
|
|
270
|
+
const renderProps: UploadZoneRenderProps = {
|
|
271
|
+
dragDrop,
|
|
272
|
+
upload: singleUpload,
|
|
273
|
+
multiUpload,
|
|
274
|
+
openFilePicker: dragDrop.openFilePicker,
|
|
275
|
+
isActive,
|
|
276
|
+
isProcessing,
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
return <>{children(renderProps)}</>;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Props for the SimpleUploadZone component with built-in styling.
|
|
284
|
+
*
|
|
285
|
+
* @property className - CSS class name for custom styling
|
|
286
|
+
* @property style - Inline styles for the upload zone container
|
|
287
|
+
* @property text - Custom text labels for different states
|
|
288
|
+
* @property text.idle - Text shown when zone is idle
|
|
289
|
+
* @property text.dragging - Text shown when dragging files over zone
|
|
290
|
+
* @property text.uploading - Text shown during upload
|
|
291
|
+
* @property errorStyle - Custom styles for validation error display
|
|
292
|
+
*/
|
|
293
|
+
export interface SimpleUploadZoneProps extends UploadZoneProps {
|
|
294
|
+
/**
|
|
295
|
+
* Additional CSS class name for styling
|
|
296
|
+
*/
|
|
297
|
+
className?: string;
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Inline styles for the upload zone
|
|
301
|
+
*/
|
|
302
|
+
style?: React.CSSProperties;
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Custom text to display in different states
|
|
306
|
+
*/
|
|
307
|
+
text?: {
|
|
308
|
+
idle?: string;
|
|
309
|
+
dragging?: string;
|
|
310
|
+
uploading?: string;
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Custom error message styling
|
|
315
|
+
*/
|
|
316
|
+
errorStyle?: React.CSSProperties;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Simple pre-styled upload zone component with built-in UI and error handling.
|
|
321
|
+
* Provides a ready-to-use drag-and-drop upload interface with minimal configuration.
|
|
322
|
+
*
|
|
323
|
+
* Features:
|
|
324
|
+
* - Built-in drag-and-drop visual feedback
|
|
325
|
+
* - Automatic progress display
|
|
326
|
+
* - File validation error display
|
|
327
|
+
* - Customizable text and styling
|
|
328
|
+
* - Keyboard accessible
|
|
329
|
+
*
|
|
330
|
+
* @param props - Upload zone configuration with styling options
|
|
331
|
+
* @returns Styled upload zone component
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```tsx
|
|
335
|
+
* // Multi-file upload with validation
|
|
336
|
+
* <SimpleUploadZone
|
|
337
|
+
* multiple={true}
|
|
338
|
+
* accept={['image/*', '.pdf']}
|
|
339
|
+
* maxFiles={5}
|
|
340
|
+
* maxFileSize={10 * 1024 * 1024} // 10MB
|
|
341
|
+
* onUploadStart={(files) => console.log('Starting uploads:', files.length)}
|
|
342
|
+
* onValidationError={(errors) => {
|
|
343
|
+
* errors.forEach(err => console.error(err));
|
|
344
|
+
* }}
|
|
345
|
+
* multiUploadOptions={{
|
|
346
|
+
* maxConcurrent: 3,
|
|
347
|
+
* onComplete: (results) => {
|
|
348
|
+
* console.log(`${results.successful.length}/${results.total} uploaded`);
|
|
349
|
+
* },
|
|
350
|
+
* }}
|
|
351
|
+
* style={{
|
|
352
|
+
* width: '400px',
|
|
353
|
+
* height: '200px',
|
|
354
|
+
* margin: '20px auto',
|
|
355
|
+
* }}
|
|
356
|
+
* text={{
|
|
357
|
+
* idle: 'Drop your files here or click to browse',
|
|
358
|
+
* dragging: 'Release to upload',
|
|
359
|
+
* uploading: 'Uploading files...',
|
|
360
|
+
* }}
|
|
361
|
+
* errorStyle={{
|
|
362
|
+
* backgroundColor: '#fff3cd',
|
|
363
|
+
* borderColor: '#ffeaa7',
|
|
364
|
+
* }}
|
|
365
|
+
* />
|
|
366
|
+
*
|
|
367
|
+
* // Single file upload
|
|
368
|
+
* <SimpleUploadZone
|
|
369
|
+
* multiple={false}
|
|
370
|
+
* accept={['image/*']}
|
|
371
|
+
* uploadOptions={{
|
|
372
|
+
* onSuccess: (result) => console.log('Uploaded:', result),
|
|
373
|
+
* onError: (error) => console.error('Failed:', error),
|
|
374
|
+
* }}
|
|
375
|
+
* text={{
|
|
376
|
+
* idle: 'Click or drag an image to upload',
|
|
377
|
+
* }}
|
|
378
|
+
* />
|
|
379
|
+
* ```
|
|
380
|
+
*/
|
|
381
|
+
export function SimpleUploadZone({
|
|
382
|
+
className = "",
|
|
383
|
+
style = {},
|
|
384
|
+
text = {},
|
|
385
|
+
errorStyle = {},
|
|
386
|
+
children,
|
|
387
|
+
...uploadZoneProps
|
|
388
|
+
}: SimpleUploadZoneProps) {
|
|
389
|
+
const defaultText = {
|
|
390
|
+
idle: uploadZoneProps.multiple
|
|
391
|
+
? "Drag files here or click to select"
|
|
392
|
+
: "Drag a file here or click to select",
|
|
393
|
+
dragging: uploadZoneProps.multiple
|
|
394
|
+
? "Drop files here..."
|
|
395
|
+
: "Drop file here...",
|
|
396
|
+
uploading: "Uploading...",
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const displayText = { ...defaultText, ...text };
|
|
400
|
+
|
|
401
|
+
// If children render prop is provided, use UploadZone directly
|
|
402
|
+
if (children) {
|
|
403
|
+
return <UploadZone {...uploadZoneProps}>{children}</UploadZone>;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Otherwise, provide default UI
|
|
407
|
+
return (
|
|
408
|
+
<UploadZone {...uploadZoneProps}>
|
|
409
|
+
{({
|
|
410
|
+
dragDrop,
|
|
411
|
+
upload,
|
|
412
|
+
multiUpload,
|
|
413
|
+
openFilePicker,
|
|
414
|
+
isActive,
|
|
415
|
+
isProcessing,
|
|
416
|
+
}) => (
|
|
417
|
+
<button
|
|
418
|
+
type="button"
|
|
419
|
+
onKeyDown={(e) => {
|
|
420
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
421
|
+
openFilePicker();
|
|
422
|
+
}
|
|
423
|
+
}}
|
|
424
|
+
onKeyUp={(e) => {
|
|
425
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
426
|
+
openFilePicker();
|
|
427
|
+
}
|
|
428
|
+
}}
|
|
429
|
+
{...dragDrop.dragHandlers}
|
|
430
|
+
onClick={openFilePicker}
|
|
431
|
+
className={`upload-zone ${isActive ? "upload-zone--active" : ""} ${isProcessing ? "upload-zone--processing" : ""} ${className}`}
|
|
432
|
+
style={{
|
|
433
|
+
border: isActive ? "2px dashed #007bff" : "2px dashed #ccc",
|
|
434
|
+
borderRadius: "8px",
|
|
435
|
+
padding: "2rem",
|
|
436
|
+
textAlign: "center",
|
|
437
|
+
cursor: "pointer",
|
|
438
|
+
backgroundColor: isActive ? "#f8f9fa" : "transparent",
|
|
439
|
+
transition: "all 0.2s ease",
|
|
440
|
+
minHeight: "120px",
|
|
441
|
+
display: "flex",
|
|
442
|
+
flexDirection: "column",
|
|
443
|
+
alignItems: "center",
|
|
444
|
+
justifyContent: "center",
|
|
445
|
+
...style,
|
|
446
|
+
}}
|
|
447
|
+
>
|
|
448
|
+
{dragDrop.state.isDragging ? (
|
|
449
|
+
<p style={{ margin: 0, fontSize: "16px", color: "#007bff" }}>
|
|
450
|
+
{displayText.dragging}
|
|
451
|
+
</p>
|
|
452
|
+
) : isProcessing ? (
|
|
453
|
+
<div style={{ textAlign: "center" }}>
|
|
454
|
+
<p style={{ margin: "0 0 10px 0", fontSize: "14px" }}>
|
|
455
|
+
{displayText.uploading}
|
|
456
|
+
</p>
|
|
457
|
+
{upload && (
|
|
458
|
+
<div>
|
|
459
|
+
<progress
|
|
460
|
+
value={upload.state.progress}
|
|
461
|
+
max={100}
|
|
462
|
+
style={{ width: "200px", height: "8px" }}
|
|
463
|
+
/>
|
|
464
|
+
<p
|
|
465
|
+
style={{
|
|
466
|
+
margin: "5px 0 0 0",
|
|
467
|
+
fontSize: "12px",
|
|
468
|
+
color: "#666",
|
|
469
|
+
}}
|
|
470
|
+
>
|
|
471
|
+
{upload.state.progress}%
|
|
472
|
+
</p>
|
|
473
|
+
</div>
|
|
474
|
+
)}
|
|
475
|
+
{multiUpload && (
|
|
476
|
+
<div>
|
|
477
|
+
<progress
|
|
478
|
+
value={multiUpload.state.progress}
|
|
479
|
+
max={100}
|
|
480
|
+
style={{ width: "200px", height: "8px" }}
|
|
481
|
+
/>
|
|
482
|
+
<p
|
|
483
|
+
style={{
|
|
484
|
+
margin: "5px 0 0 0",
|
|
485
|
+
fontSize: "12px",
|
|
486
|
+
color: "#666",
|
|
487
|
+
}}
|
|
488
|
+
>
|
|
489
|
+
{multiUpload.state.progress}% ({multiUpload.state.uploading}{" "}
|
|
490
|
+
uploading, {multiUpload.state.successful} completed)
|
|
491
|
+
</p>
|
|
492
|
+
</div>
|
|
493
|
+
)}
|
|
494
|
+
</div>
|
|
495
|
+
) : (
|
|
496
|
+
<p style={{ margin: 0, fontSize: "16px", color: "#666" }}>
|
|
497
|
+
{displayText.idle}
|
|
498
|
+
</p>
|
|
499
|
+
)}
|
|
500
|
+
|
|
501
|
+
{dragDrop.state.errors.length > 0 && (
|
|
502
|
+
<div
|
|
503
|
+
style={{
|
|
504
|
+
marginTop: "10px",
|
|
505
|
+
padding: "8px 12px",
|
|
506
|
+
backgroundColor: "#f8d7da",
|
|
507
|
+
border: "1px solid #f5c6cb",
|
|
508
|
+
borderRadius: "4px",
|
|
509
|
+
maxWidth: "100%",
|
|
510
|
+
...errorStyle,
|
|
511
|
+
}}
|
|
512
|
+
>
|
|
513
|
+
<p
|
|
514
|
+
style={{
|
|
515
|
+
margin: "0 0 5px 0",
|
|
516
|
+
fontSize: "12px",
|
|
517
|
+
fontWeight: "bold",
|
|
518
|
+
color: "#721c24",
|
|
519
|
+
}}
|
|
520
|
+
>
|
|
521
|
+
Validation Errors:
|
|
522
|
+
</p>
|
|
523
|
+
{dragDrop.state.errors.map((error, index) => (
|
|
524
|
+
<p
|
|
525
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: index is used as key
|
|
526
|
+
key={index}
|
|
527
|
+
style={{
|
|
528
|
+
color: "#721c24",
|
|
529
|
+
fontSize: "11px",
|
|
530
|
+
margin: "2px 0",
|
|
531
|
+
lineHeight: "1.3",
|
|
532
|
+
}}
|
|
533
|
+
>
|
|
534
|
+
• {error}
|
|
535
|
+
</p>
|
|
536
|
+
))}
|
|
537
|
+
</div>
|
|
538
|
+
)}
|
|
539
|
+
|
|
540
|
+
<input {...dragDrop.inputProps} />
|
|
541
|
+
</button>
|
|
542
|
+
)}
|
|
543
|
+
</UploadZone>
|
|
544
|
+
);
|
|
545
|
+
}
|