@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,614 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BrowserUploadInput,
|
|
3
|
+
FlowUploadConfig,
|
|
4
|
+
FlowUploadItem,
|
|
5
|
+
MultiFlowUploadOptions,
|
|
6
|
+
} from "@uploadista/client-browser";
|
|
7
|
+
import type { ReactNode } from "react";
|
|
8
|
+
import { useMultiFlowUpload } from "../hooks/use-multi-flow-upload";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Render props passed to the FlowUploadList children function.
|
|
12
|
+
* Provides access to upload items, aggregate statistics, and control methods.
|
|
13
|
+
*
|
|
14
|
+
* @property items - All flow upload items in the queue
|
|
15
|
+
* @property totalProgress - Average progress across all uploads (0-100)
|
|
16
|
+
* @property activeUploads - Count of currently uploading items
|
|
17
|
+
* @property completedUploads - Count of successfully completed uploads
|
|
18
|
+
* @property failedUploads - Count of failed uploads
|
|
19
|
+
* @property isUploading - True when any uploads are in progress
|
|
20
|
+
* @property addFiles - Add new files to the upload queue
|
|
21
|
+
* @property removeFile - Remove a specific file from the queue
|
|
22
|
+
* @property startUpload - Begin uploading all pending files
|
|
23
|
+
* @property abortUpload - Cancel a specific active upload
|
|
24
|
+
* @property abortAll - Cancel all active uploads
|
|
25
|
+
* @property clear - Remove all items from the queue
|
|
26
|
+
* @property retryUpload - Retry a specific failed upload
|
|
27
|
+
*/
|
|
28
|
+
export interface FlowUploadListRenderProps {
|
|
29
|
+
/**
|
|
30
|
+
* List of upload items
|
|
31
|
+
*/
|
|
32
|
+
items: FlowUploadItem<BrowserUploadInput>[];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Total progress across all uploads
|
|
36
|
+
*/
|
|
37
|
+
totalProgress: number;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Number of active uploads
|
|
41
|
+
*/
|
|
42
|
+
activeUploads: number;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Number of completed uploads
|
|
46
|
+
*/
|
|
47
|
+
completedUploads: number;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Number of failed uploads
|
|
51
|
+
*/
|
|
52
|
+
failedUploads: number;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Whether any uploads are in progress
|
|
56
|
+
*/
|
|
57
|
+
isUploading: boolean;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Add files to the upload queue
|
|
61
|
+
*/
|
|
62
|
+
addFiles: (files: File[] | FileList) => void;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Remove a file from the queue
|
|
66
|
+
*/
|
|
67
|
+
removeFile: (id: string) => void;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Start uploading all pending files
|
|
71
|
+
*/
|
|
72
|
+
startUpload: () => void;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Abort a specific upload
|
|
76
|
+
*/
|
|
77
|
+
abortUpload: (id: string) => void;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Abort all uploads
|
|
81
|
+
*/
|
|
82
|
+
abortAll: () => void;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Clear all items
|
|
86
|
+
*/
|
|
87
|
+
clear: () => void;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Retry a failed upload
|
|
91
|
+
*/
|
|
92
|
+
retryUpload: (id: string) => void;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Props for the FlowUploadList component.
|
|
97
|
+
*
|
|
98
|
+
* @property flowConfig - Flow execution configuration (flowId, storageId, etc.)
|
|
99
|
+
* @property options - Multi-flow upload options (callbacks, concurrency, etc.)
|
|
100
|
+
* @property children - Render function receiving flow upload list state
|
|
101
|
+
*/
|
|
102
|
+
export interface FlowUploadListProps {
|
|
103
|
+
/**
|
|
104
|
+
* Flow configuration
|
|
105
|
+
*/
|
|
106
|
+
flowConfig: FlowUploadConfig;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Multi-upload options
|
|
110
|
+
*/
|
|
111
|
+
options?: Omit<MultiFlowUploadOptions<BrowserUploadInput>, "flowConfig">;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Render function for the upload list
|
|
115
|
+
*/
|
|
116
|
+
children: (props: FlowUploadListRenderProps) => ReactNode;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Headless flow upload list component for managing batch file uploads through a flow.
|
|
121
|
+
* Uses render props pattern to provide complete control over the UI while handling
|
|
122
|
+
* concurrent uploads and flow processing.
|
|
123
|
+
*
|
|
124
|
+
* Each file is uploaded and processed independently through the specified flow,
|
|
125
|
+
* with automatic queue management and concurrency control.
|
|
126
|
+
*
|
|
127
|
+
* Must be used within an UploadistaProvider.
|
|
128
|
+
*
|
|
129
|
+
* @param props - Flow upload list configuration and render prop
|
|
130
|
+
* @returns Rendered flow upload list using the provided render prop
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```tsx
|
|
134
|
+
* // Batch image processing with custom UI
|
|
135
|
+
* <FlowUploadList
|
|
136
|
+
* flowConfig={{
|
|
137
|
+
* flowId: "image-batch-processing",
|
|
138
|
+
* storageId: "s3-images",
|
|
139
|
+
* outputNodeId: "optimized",
|
|
140
|
+
* }}
|
|
141
|
+
* options={{
|
|
142
|
+
* maxConcurrent: 3,
|
|
143
|
+
* onItemSuccess: (item) => {
|
|
144
|
+
* console.log(`${item.file.name} processed successfully`);
|
|
145
|
+
* },
|
|
146
|
+
* onComplete: (items) => {
|
|
147
|
+
* const successful = items.filter(i => i.status === 'success');
|
|
148
|
+
* console.log(`Batch complete: ${successful.length}/${items.length} successful`);
|
|
149
|
+
* },
|
|
150
|
+
* }}
|
|
151
|
+
* >
|
|
152
|
+
* {({
|
|
153
|
+
* items,
|
|
154
|
+
* totalProgress,
|
|
155
|
+
* activeUploads,
|
|
156
|
+
* completedUploads,
|
|
157
|
+
* failedUploads,
|
|
158
|
+
* addFiles,
|
|
159
|
+
* startUpload,
|
|
160
|
+
* abortUpload,
|
|
161
|
+
* retryUpload,
|
|
162
|
+
* clear,
|
|
163
|
+
* }) => (
|
|
164
|
+
* <div>
|
|
165
|
+
* <input
|
|
166
|
+
* type="file"
|
|
167
|
+
* multiple
|
|
168
|
+
* accept="image/*"
|
|
169
|
+
* onChange={(e) => {
|
|
170
|
+
* if (e.target.files) {
|
|
171
|
+
* addFiles(e.target.files);
|
|
172
|
+
* startUpload();
|
|
173
|
+
* }
|
|
174
|
+
* }}
|
|
175
|
+
* />
|
|
176
|
+
*
|
|
177
|
+
* <div style={{ marginTop: '1rem' }}>
|
|
178
|
+
* <h3>Upload Progress</h3>
|
|
179
|
+
* <div>Overall: {totalProgress}%</div>
|
|
180
|
+
* <div>
|
|
181
|
+
* Active: {activeUploads}, Completed: {completedUploads}, Failed: {failedUploads}
|
|
182
|
+
* </div>
|
|
183
|
+
* <button onClick={clear}>Clear All</button>
|
|
184
|
+
* </div>
|
|
185
|
+
*
|
|
186
|
+
* <ul style={{ listStyle: 'none', padding: 0 }}>
|
|
187
|
+
* {items.map((item) => (
|
|
188
|
+
* <li key={item.id} style={{
|
|
189
|
+
* padding: '1rem',
|
|
190
|
+
* border: '1px solid #ccc',
|
|
191
|
+
* marginBottom: '0.5rem'
|
|
192
|
+
* }}>
|
|
193
|
+
* <div>{item.file instanceof File ? item.file.name : 'File'}</div>
|
|
194
|
+
* <div>Status: {item.status}</div>
|
|
195
|
+
*
|
|
196
|
+
* {item.status === "uploading" && (
|
|
197
|
+
* <div>
|
|
198
|
+
* <progress value={item.progress} max={100} style={{ width: '100%' }} />
|
|
199
|
+
* <div>{item.progress}%</div>
|
|
200
|
+
* <button onClick={() => abortUpload(item.id)}>Cancel</button>
|
|
201
|
+
* </div>
|
|
202
|
+
* )}
|
|
203
|
+
*
|
|
204
|
+
* {item.status === "error" && (
|
|
205
|
+
* <div>
|
|
206
|
+
* <div style={{ color: 'red' }}>{item.error?.message}</div>
|
|
207
|
+
* <button onClick={() => retryUpload(item.id)}>Retry</button>
|
|
208
|
+
* </div>
|
|
209
|
+
* )}
|
|
210
|
+
*
|
|
211
|
+
* {item.status === "success" && (
|
|
212
|
+
* <div style={{ color: 'green' }}>✓ Complete</div>
|
|
213
|
+
* )}
|
|
214
|
+
* </li>
|
|
215
|
+
* ))}
|
|
216
|
+
* </ul>
|
|
217
|
+
* </div>
|
|
218
|
+
* )}
|
|
219
|
+
* </FlowUploadList>
|
|
220
|
+
* ```
|
|
221
|
+
*
|
|
222
|
+
* @see {@link SimpleFlowUploadList} for a pre-styled version
|
|
223
|
+
* @see {@link useMultiFlowUpload} for the underlying hook
|
|
224
|
+
*/
|
|
225
|
+
export function FlowUploadList({
|
|
226
|
+
flowConfig,
|
|
227
|
+
options,
|
|
228
|
+
children,
|
|
229
|
+
}: FlowUploadListProps) {
|
|
230
|
+
const multiUpload = useMultiFlowUpload({
|
|
231
|
+
...options,
|
|
232
|
+
flowConfig,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<>
|
|
237
|
+
{children({
|
|
238
|
+
items: multiUpload.state.items,
|
|
239
|
+
totalProgress: multiUpload.state.totalProgress,
|
|
240
|
+
activeUploads: multiUpload.state.activeUploads,
|
|
241
|
+
completedUploads: multiUpload.state.completedUploads,
|
|
242
|
+
failedUploads: multiUpload.state.failedUploads,
|
|
243
|
+
isUploading: multiUpload.isUploading,
|
|
244
|
+
addFiles: multiUpload.addFiles,
|
|
245
|
+
removeFile: multiUpload.removeFile,
|
|
246
|
+
startUpload: multiUpload.startUpload,
|
|
247
|
+
abortUpload: multiUpload.abortUpload,
|
|
248
|
+
abortAll: multiUpload.abortAll,
|
|
249
|
+
clear: multiUpload.clear,
|
|
250
|
+
retryUpload: multiUpload.retryUpload,
|
|
251
|
+
})}
|
|
252
|
+
</>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Props for the SimpleFlowUploadListItem component.
|
|
258
|
+
*
|
|
259
|
+
* @property item - The flow upload item to display
|
|
260
|
+
* @property onAbort - Called when the abort button is clicked
|
|
261
|
+
* @property onRetry - Called when the retry button is clicked
|
|
262
|
+
* @property onRemove - Called when the remove button is clicked
|
|
263
|
+
*/
|
|
264
|
+
export interface SimpleFlowUploadListItemProps {
|
|
265
|
+
/**
|
|
266
|
+
* Upload item
|
|
267
|
+
*/
|
|
268
|
+
item: FlowUploadItem<BrowserUploadInput>;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Abort the upload
|
|
272
|
+
*/
|
|
273
|
+
onAbort: () => void;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Retry the upload
|
|
277
|
+
*/
|
|
278
|
+
onRetry: () => void;
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Remove the item
|
|
282
|
+
*/
|
|
283
|
+
onRemove: () => void;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Pre-styled flow upload list item component with status indicators.
|
|
288
|
+
* Displays file name, upload progress, status, and contextual action buttons.
|
|
289
|
+
*
|
|
290
|
+
* Features:
|
|
291
|
+
* - Status-specific icons and colors
|
|
292
|
+
* - Progress bar with percentage and byte count
|
|
293
|
+
* - Error message display
|
|
294
|
+
* - Contextual action buttons (cancel, retry, remove)
|
|
295
|
+
*
|
|
296
|
+
* @param props - Upload item and callback functions
|
|
297
|
+
* @returns Styled flow upload list item component
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```tsx
|
|
301
|
+
* <SimpleFlowUploadListItem
|
|
302
|
+
* item={uploadItem}
|
|
303
|
+
* onAbort={() => console.log('Abort')}
|
|
304
|
+
* onRetry={() => console.log('Retry')}
|
|
305
|
+
* onRemove={() => console.log('Remove')}
|
|
306
|
+
* />
|
|
307
|
+
* ```
|
|
308
|
+
*/
|
|
309
|
+
export function SimpleFlowUploadListItem({
|
|
310
|
+
item,
|
|
311
|
+
onAbort,
|
|
312
|
+
onRetry,
|
|
313
|
+
onRemove,
|
|
314
|
+
}: SimpleFlowUploadListItemProps) {
|
|
315
|
+
const getStatusIcon = () => {
|
|
316
|
+
switch (item.status) {
|
|
317
|
+
case "success":
|
|
318
|
+
return "✓";
|
|
319
|
+
case "error":
|
|
320
|
+
return "✗";
|
|
321
|
+
case "uploading":
|
|
322
|
+
return "⟳";
|
|
323
|
+
case "aborted":
|
|
324
|
+
return "⊘";
|
|
325
|
+
default:
|
|
326
|
+
return "○";
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const getStatusColor = () => {
|
|
331
|
+
switch (item.status) {
|
|
332
|
+
case "success":
|
|
333
|
+
return "green";
|
|
334
|
+
case "error":
|
|
335
|
+
return "red";
|
|
336
|
+
case "uploading":
|
|
337
|
+
return "blue";
|
|
338
|
+
case "aborted":
|
|
339
|
+
return "gray";
|
|
340
|
+
default:
|
|
341
|
+
return "black";
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
return (
|
|
346
|
+
<div
|
|
347
|
+
style={{
|
|
348
|
+
display: "flex",
|
|
349
|
+
alignItems: "center",
|
|
350
|
+
gap: "12px",
|
|
351
|
+
padding: "8px",
|
|
352
|
+
borderBottom: "1px solid #eee",
|
|
353
|
+
}}
|
|
354
|
+
>
|
|
355
|
+
<span style={{ color: getStatusColor(), fontSize: "18px" }}>
|
|
356
|
+
{getStatusIcon()}
|
|
357
|
+
</span>
|
|
358
|
+
|
|
359
|
+
<div style={{ flex: 1, minWidth: 0 }}>
|
|
360
|
+
<div
|
|
361
|
+
style={{
|
|
362
|
+
fontSize: "14px",
|
|
363
|
+
fontWeight: 500,
|
|
364
|
+
overflow: "hidden",
|
|
365
|
+
textOverflow: "ellipsis",
|
|
366
|
+
whiteSpace: "nowrap",
|
|
367
|
+
}}
|
|
368
|
+
>
|
|
369
|
+
{item.file instanceof File ? item.file.name : "Upload"}
|
|
370
|
+
</div>
|
|
371
|
+
|
|
372
|
+
{item.status === "uploading" && (
|
|
373
|
+
<div style={{ marginTop: "4px" }}>
|
|
374
|
+
<progress
|
|
375
|
+
value={item.progress}
|
|
376
|
+
max={100}
|
|
377
|
+
style={{ width: "100%", height: "4px" }}
|
|
378
|
+
/>
|
|
379
|
+
<div style={{ fontSize: "12px", color: "#666", marginTop: "2px" }}>
|
|
380
|
+
{item.progress}% • {Math.round(item.bytesUploaded / 1024)} KB /{" "}
|
|
381
|
+
{Math.round(item.totalBytes / 1024)} KB
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
384
|
+
)}
|
|
385
|
+
|
|
386
|
+
{item.status === "error" && (
|
|
387
|
+
<div style={{ fontSize: "12px", color: "red", marginTop: "2px" }}>
|
|
388
|
+
{item.error?.message || "Upload failed"}
|
|
389
|
+
</div>
|
|
390
|
+
)}
|
|
391
|
+
|
|
392
|
+
{item.status === "success" && (
|
|
393
|
+
<div style={{ fontSize: "12px", color: "green", marginTop: "2px" }}>
|
|
394
|
+
Upload complete
|
|
395
|
+
</div>
|
|
396
|
+
)}
|
|
397
|
+
</div>
|
|
398
|
+
|
|
399
|
+
<div style={{ display: "flex", gap: "8px" }}>
|
|
400
|
+
{item.status === "uploading" && (
|
|
401
|
+
<button
|
|
402
|
+
type="button"
|
|
403
|
+
onClick={onAbort}
|
|
404
|
+
style={{
|
|
405
|
+
padding: "4px 8px",
|
|
406
|
+
fontSize: "12px",
|
|
407
|
+
borderRadius: "4px",
|
|
408
|
+
border: "1px solid #ccc",
|
|
409
|
+
backgroundColor: "#fff",
|
|
410
|
+
cursor: "pointer",
|
|
411
|
+
}}
|
|
412
|
+
>
|
|
413
|
+
Cancel
|
|
414
|
+
</button>
|
|
415
|
+
)}
|
|
416
|
+
|
|
417
|
+
{item.status === "error" && (
|
|
418
|
+
<button
|
|
419
|
+
type="button"
|
|
420
|
+
onClick={onRetry}
|
|
421
|
+
style={{
|
|
422
|
+
padding: "4px 8px",
|
|
423
|
+
fontSize: "12px",
|
|
424
|
+
borderRadius: "4px",
|
|
425
|
+
border: "1px solid #ccc",
|
|
426
|
+
backgroundColor: "#fff",
|
|
427
|
+
cursor: "pointer",
|
|
428
|
+
}}
|
|
429
|
+
>
|
|
430
|
+
Retry
|
|
431
|
+
</button>
|
|
432
|
+
)}
|
|
433
|
+
|
|
434
|
+
{(item.status === "pending" ||
|
|
435
|
+
item.status === "error" ||
|
|
436
|
+
item.status === "aborted") && (
|
|
437
|
+
<button
|
|
438
|
+
type="button"
|
|
439
|
+
onClick={onRemove}
|
|
440
|
+
style={{
|
|
441
|
+
padding: "4px 8px",
|
|
442
|
+
fontSize: "12px",
|
|
443
|
+
borderRadius: "4px",
|
|
444
|
+
border: "1px solid #ccc",
|
|
445
|
+
backgroundColor: "#fff",
|
|
446
|
+
cursor: "pointer",
|
|
447
|
+
}}
|
|
448
|
+
>
|
|
449
|
+
Remove
|
|
450
|
+
</button>
|
|
451
|
+
)}
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Props for the SimpleFlowUploadList component.
|
|
459
|
+
*
|
|
460
|
+
* @property flowConfig - Flow execution configuration
|
|
461
|
+
* @property options - Multi-flow upload options (callbacks, concurrency)
|
|
462
|
+
* @property className - CSS class name for the container
|
|
463
|
+
* @property showFileInput - Whether to display the file input (default: true)
|
|
464
|
+
* @property accept - Accepted file types for the file input
|
|
465
|
+
*/
|
|
466
|
+
export interface SimpleFlowUploadListProps {
|
|
467
|
+
/**
|
|
468
|
+
* Flow configuration
|
|
469
|
+
*/
|
|
470
|
+
flowConfig: FlowUploadConfig;
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Multi-upload options
|
|
474
|
+
*/
|
|
475
|
+
options?: Omit<MultiFlowUploadOptions<BrowserUploadInput>, "flowConfig">;
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* CSS class for the container
|
|
479
|
+
*/
|
|
480
|
+
className?: string;
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Show file input
|
|
484
|
+
*/
|
|
485
|
+
showFileInput?: boolean;
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* File input accept attribute
|
|
489
|
+
*/
|
|
490
|
+
accept?: string;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Simple pre-styled flow upload list component with built-in UI.
|
|
495
|
+
* Provides a ready-to-use interface for batch file uploads with flow processing.
|
|
496
|
+
*
|
|
497
|
+
* Features:
|
|
498
|
+
* - Built-in file input
|
|
499
|
+
* - Overall progress display
|
|
500
|
+
* - Individual item progress tracking
|
|
501
|
+
* - Status indicators and action buttons
|
|
502
|
+
* - Automatic upload start on file selection
|
|
503
|
+
*
|
|
504
|
+
* @param props - Flow upload list configuration with styling options
|
|
505
|
+
* @returns Styled flow upload list component
|
|
506
|
+
*
|
|
507
|
+
* @example
|
|
508
|
+
* ```tsx
|
|
509
|
+
* // Basic batch image upload
|
|
510
|
+
* <SimpleFlowUploadList
|
|
511
|
+
* flowConfig={{
|
|
512
|
+
* flowId: "image-batch-processing",
|
|
513
|
+
* storageId: "s3-images",
|
|
514
|
+
* }}
|
|
515
|
+
* options={{
|
|
516
|
+
* maxConcurrent: 3,
|
|
517
|
+
* onItemSuccess: (item) => {
|
|
518
|
+
* console.log(`${item.file.name} processed`);
|
|
519
|
+
* },
|
|
520
|
+
* onComplete: (items) => {
|
|
521
|
+
* console.log("Batch complete:", items.length);
|
|
522
|
+
* },
|
|
523
|
+
* }}
|
|
524
|
+
* accept="image/*"
|
|
525
|
+
* className="my-upload-list"
|
|
526
|
+
* />
|
|
527
|
+
*
|
|
528
|
+
* // Without file input (add files programmatically)
|
|
529
|
+
* <SimpleFlowUploadList
|
|
530
|
+
* flowConfig={{
|
|
531
|
+
* flowId: "document-processing",
|
|
532
|
+
* storageId: "docs",
|
|
533
|
+
* }}
|
|
534
|
+
* showFileInput={false}
|
|
535
|
+
* options={{
|
|
536
|
+
* maxConcurrent: 2,
|
|
537
|
+
* }}
|
|
538
|
+
* />
|
|
539
|
+
* ```
|
|
540
|
+
*
|
|
541
|
+
* @see {@link FlowUploadList} for the headless version with full control
|
|
542
|
+
*/
|
|
543
|
+
export function SimpleFlowUploadList({
|
|
544
|
+
flowConfig,
|
|
545
|
+
options,
|
|
546
|
+
className = "",
|
|
547
|
+
showFileInput = true,
|
|
548
|
+
accept,
|
|
549
|
+
}: SimpleFlowUploadListProps) {
|
|
550
|
+
return (
|
|
551
|
+
<FlowUploadList flowConfig={flowConfig} options={options}>
|
|
552
|
+
{({
|
|
553
|
+
items,
|
|
554
|
+
addFiles,
|
|
555
|
+
startUpload,
|
|
556
|
+
abortUpload,
|
|
557
|
+
retryUpload,
|
|
558
|
+
removeFile,
|
|
559
|
+
totalProgress,
|
|
560
|
+
}) => (
|
|
561
|
+
<div className={className}>
|
|
562
|
+
{showFileInput && (
|
|
563
|
+
<div style={{ marginBottom: "16px" }}>
|
|
564
|
+
<input
|
|
565
|
+
type="file"
|
|
566
|
+
multiple
|
|
567
|
+
accept={accept}
|
|
568
|
+
onChange={(e) => {
|
|
569
|
+
if (e.target.files) {
|
|
570
|
+
addFiles(e.target.files);
|
|
571
|
+
startUpload();
|
|
572
|
+
}
|
|
573
|
+
}}
|
|
574
|
+
style={{
|
|
575
|
+
padding: "8px",
|
|
576
|
+
border: "1px solid #ccc",
|
|
577
|
+
borderRadius: "4px",
|
|
578
|
+
}}
|
|
579
|
+
/>
|
|
580
|
+
</div>
|
|
581
|
+
)}
|
|
582
|
+
|
|
583
|
+
{items.length > 0 && (
|
|
584
|
+
<div>
|
|
585
|
+
<div
|
|
586
|
+
style={{ marginBottom: "8px", fontSize: "14px", color: "#666" }}
|
|
587
|
+
>
|
|
588
|
+
Total Progress: {totalProgress}%
|
|
589
|
+
</div>
|
|
590
|
+
|
|
591
|
+
<div
|
|
592
|
+
style={{
|
|
593
|
+
border: "1px solid #eee",
|
|
594
|
+
borderRadius: "8px",
|
|
595
|
+
overflow: "hidden",
|
|
596
|
+
}}
|
|
597
|
+
>
|
|
598
|
+
{items.map((item) => (
|
|
599
|
+
<SimpleFlowUploadListItem
|
|
600
|
+
key={item.id}
|
|
601
|
+
item={item}
|
|
602
|
+
onAbort={() => abortUpload(item.id)}
|
|
603
|
+
onRetry={() => retryUpload(item.id)}
|
|
604
|
+
onRemove={() => removeFile(item.id)}
|
|
605
|
+
/>
|
|
606
|
+
))}
|
|
607
|
+
</div>
|
|
608
|
+
</div>
|
|
609
|
+
)}
|
|
610
|
+
</div>
|
|
611
|
+
)}
|
|
612
|
+
</FlowUploadList>
|
|
613
|
+
);
|
|
614
|
+
}
|