@uploadista/react 0.0.20-beta.1 → 0.0.20-beta.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/dist/components/index.d.mts +3 -3
- package/dist/components/index.mjs +1 -1
- package/dist/flow-upload-list-SbpCaxRq.mjs +2 -0
- package/dist/flow-upload-list-SbpCaxRq.mjs.map +1 -0
- package/dist/hooks/index.d.mts +3 -3
- package/dist/hooks/index.mjs +1 -1
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +1 -1
- package/dist/{uploadista-provider-CM48PPSp.d.mts → uploadista-provider-C1l0iBc9.d.mts} +503 -309
- package/dist/uploadista-provider-C1l0iBc9.d.mts.map +1 -0
- package/dist/use-upload-BvvGROMR.mjs +2 -0
- package/dist/use-upload-BvvGROMR.mjs.map +1 -0
- package/dist/{use-uploadista-client-m9nF-irM.d.mts → use-uploadista-client-DHbLSpIb.d.mts} +120 -286
- package/dist/use-uploadista-client-DHbLSpIb.d.mts.map +1 -0
- package/dist/use-uploadista-events-BwUD-2Ck.mjs +2 -0
- package/dist/use-uploadista-events-BwUD-2Ck.mjs.map +1 -0
- package/dist/{use-upload-metrics-DhzS4lhG.d.mts → use-uploadista-events-CtDXJYrR.d.mts} +169 -371
- package/dist/use-uploadista-events-CtDXJYrR.d.mts.map +1 -0
- package/package.json +6 -6
- package/src/components/flow-primitives.tsx +839 -0
- package/src/components/index.tsx +31 -13
- package/src/hooks/index.ts +25 -37
- package/src/index.ts +90 -81
- package/dist/upload-zone-BjWHuP7p.mjs +0 -6
- package/dist/upload-zone-BjWHuP7p.mjs.map +0 -1
- package/dist/uploadista-provider-CM48PPSp.d.mts.map +0 -1
- package/dist/use-upload-BDHVhQsI.mjs +0 -2
- package/dist/use-upload-BDHVhQsI.mjs.map +0 -1
- package/dist/use-upload-metrics-Df90wIos.mjs +0 -2
- package/dist/use-upload-metrics-Df90wIos.mjs.map +0 -1
- package/dist/use-upload-metrics-DhzS4lhG.d.mts.map +0 -1
- package/dist/use-uploadista-client-m9nF-irM.d.mts.map +0 -1
- package/src/components/flow-input.tsx +0 -299
- package/src/components/flow-upload-zone.tsx +0 -441
- package/src/hooks/use-flow-execution.ts +0 -502
- package/src/hooks/use-flow-upload.ts +0 -299
package/src/components/index.tsx
CHANGED
|
@@ -1,7 +1,35 @@
|
|
|
1
|
-
// Flow
|
|
2
|
-
export
|
|
3
|
-
|
|
1
|
+
// Flow Primitives (NEW compound component)
|
|
2
|
+
export {
|
|
3
|
+
Flow,
|
|
4
|
+
useFlowContext,
|
|
5
|
+
useFlowInputContext,
|
|
6
|
+
} from "./flow-primitives";
|
|
7
|
+
export type {
|
|
8
|
+
FlowCancelProps,
|
|
9
|
+
FlowContextValue,
|
|
10
|
+
FlowDropZoneProps,
|
|
11
|
+
FlowDropZoneRenderProps,
|
|
12
|
+
FlowErrorProps,
|
|
13
|
+
FlowErrorRenderProps,
|
|
14
|
+
FlowInputContextValue,
|
|
15
|
+
FlowInputDropZoneProps,
|
|
16
|
+
FlowInputDropZoneRenderProps,
|
|
17
|
+
FlowInputPreviewProps,
|
|
18
|
+
FlowInputPreviewRenderProps,
|
|
19
|
+
FlowInputProps,
|
|
20
|
+
FlowInputsProps,
|
|
21
|
+
FlowInputsRenderProps,
|
|
22
|
+
FlowInputUrlFieldProps,
|
|
23
|
+
FlowProgressProps,
|
|
24
|
+
FlowProgressRenderProps,
|
|
25
|
+
FlowProps,
|
|
26
|
+
FlowResetProps,
|
|
27
|
+
FlowStatusProps,
|
|
28
|
+
FlowStatusRenderProps,
|
|
29
|
+
FlowSubmitProps,
|
|
30
|
+
} from "./flow-primitives";
|
|
4
31
|
|
|
32
|
+
// Flow Upload List (for batch uploads with useMultiFlowUpload)
|
|
5
33
|
export type {
|
|
6
34
|
FlowUploadListProps,
|
|
7
35
|
FlowUploadListRenderProps,
|
|
@@ -14,16 +42,6 @@ export {
|
|
|
14
42
|
SimpleFlowUploadListItem,
|
|
15
43
|
} from "./flow-upload-list";
|
|
16
44
|
|
|
17
|
-
export type {
|
|
18
|
-
FlowUploadZoneProps,
|
|
19
|
-
FlowUploadZoneRenderProps,
|
|
20
|
-
SimpleFlowUploadZoneProps,
|
|
21
|
-
} from "./flow-upload-zone";
|
|
22
|
-
export {
|
|
23
|
-
FlowUploadZone,
|
|
24
|
-
SimpleFlowUploadZone,
|
|
25
|
-
} from "./flow-upload-zone";
|
|
26
|
-
|
|
27
45
|
// Upload Components
|
|
28
46
|
export type {
|
|
29
47
|
SimpleUploadListItemProps,
|
package/src/hooks/index.ts
CHANGED
|
@@ -1,51 +1,30 @@
|
|
|
1
1
|
// Event Hooks
|
|
2
2
|
export { isFlowEvent, isUploadEvent } from "./event-utils";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export { useFlowEvents } from "./use-flow-events";
|
|
3
|
+
|
|
4
|
+
// Drag & Drop Hook
|
|
6
5
|
export type {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
UploadValidationWarningEventData,
|
|
13
|
-
UseUploadEventsOptions,
|
|
14
|
-
} from "./use-upload-events";
|
|
15
|
-
export { useUploadEvents } from "./use-upload-events";
|
|
6
|
+
DragDropOptions,
|
|
7
|
+
DragDropState,
|
|
8
|
+
UseDragDropReturn,
|
|
9
|
+
} from "./use-drag-drop";
|
|
10
|
+
export { useDragDrop } from "./use-drag-drop";
|
|
16
11
|
|
|
17
|
-
// Flow
|
|
12
|
+
// Flow Hooks
|
|
13
|
+
// useFlow is the primary hook for flow operations (replaces useFlowUpload)
|
|
18
14
|
export type {
|
|
19
15
|
FlowInputMetadata,
|
|
16
|
+
FlowUploadState,
|
|
17
|
+
FlowUploadStatus,
|
|
20
18
|
InputExecutionState,
|
|
21
19
|
UseFlowReturn,
|
|
22
20
|
} from "./use-flow";
|
|
23
21
|
export { useFlow } from "./use-flow";
|
|
24
|
-
|
|
25
|
-
export
|
|
26
|
-
InputBuilder,
|
|
27
|
-
UseFlowExecutionOptions,
|
|
28
|
-
UseFlowExecutionReturn,
|
|
29
|
-
} from "./use-flow-execution";
|
|
30
|
-
export { useFlowExecution } from "./use-flow-execution";
|
|
31
|
-
|
|
32
|
-
export type {
|
|
33
|
-
FlowUploadState,
|
|
34
|
-
FlowUploadStatus,
|
|
35
|
-
UseFlowUploadReturn,
|
|
36
|
-
} from "./use-flow-upload";
|
|
37
|
-
export { useFlowUpload } from "./use-flow-upload";
|
|
38
|
-
|
|
39
|
-
// Upload Hooks
|
|
40
|
-
export type {
|
|
41
|
-
DragDropOptions,
|
|
42
|
-
DragDropState,
|
|
43
|
-
UseDragDropReturn,
|
|
44
|
-
} from "./use-drag-drop";
|
|
45
|
-
export { useDragDrop } from "./use-drag-drop";
|
|
22
|
+
export type { UseFlowEventsOptions } from "./use-flow-events";
|
|
23
|
+
export { useFlowEvents } from "./use-flow-events";
|
|
46
24
|
export type { UseMultiFlowUploadReturn } from "./use-multi-flow-upload";
|
|
47
25
|
export { useMultiFlowUpload } from "./use-multi-flow-upload";
|
|
48
26
|
|
|
27
|
+
// Upload Hooks
|
|
49
28
|
export type {
|
|
50
29
|
MultiUploadOptions,
|
|
51
30
|
MultiUploadState,
|
|
@@ -53,7 +32,6 @@ export type {
|
|
|
53
32
|
UseMultiUploadReturn,
|
|
54
33
|
} from "./use-multi-upload";
|
|
55
34
|
export { useMultiUpload } from "./use-multi-upload";
|
|
56
|
-
|
|
57
35
|
export type {
|
|
58
36
|
UploadState,
|
|
59
37
|
UploadStatus,
|
|
@@ -61,7 +39,16 @@ export type {
|
|
|
61
39
|
UseUploadReturn,
|
|
62
40
|
} from "./use-upload";
|
|
63
41
|
export { useUpload } from "./use-upload";
|
|
64
|
-
|
|
42
|
+
export type {
|
|
43
|
+
UploadFailedEventData,
|
|
44
|
+
UploadFileEventData,
|
|
45
|
+
UploadProgressEventData,
|
|
46
|
+
UploadValidationFailedEventData,
|
|
47
|
+
UploadValidationSuccessEventData,
|
|
48
|
+
UploadValidationWarningEventData,
|
|
49
|
+
UseUploadEventsOptions,
|
|
50
|
+
} from "./use-upload-events";
|
|
51
|
+
export { useUploadEvents } from "./use-upload-events";
|
|
65
52
|
export type {
|
|
66
53
|
FileUploadMetrics,
|
|
67
54
|
UploadMetrics,
|
|
@@ -76,3 +63,4 @@ export type {
|
|
|
76
63
|
UseUploadistaClientReturn,
|
|
77
64
|
} from "./use-uploadista-client";
|
|
78
65
|
export { useUploadistaClient } from "./use-uploadista-client";
|
|
66
|
+
export { useUploadistaEvents } from "./use-uploadista-events";
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,65 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
export {
|
|
1
|
+
// ============ FLOW PRIMITIVES (NEW) ============
|
|
2
|
+
// Compound component for flow-based uploads
|
|
3
|
+
export {
|
|
4
|
+
Flow,
|
|
5
|
+
useFlowContext,
|
|
6
|
+
useFlowInputContext,
|
|
7
|
+
} from "./components/flow-primitives";
|
|
8
|
+
export type {
|
|
9
|
+
FlowProps,
|
|
10
|
+
FlowContextValue,
|
|
11
|
+
FlowInputContextValue,
|
|
12
|
+
FlowDropZoneProps,
|
|
13
|
+
FlowDropZoneRenderProps,
|
|
14
|
+
FlowInputsProps,
|
|
15
|
+
FlowInputsRenderProps,
|
|
16
|
+
FlowInputProps,
|
|
17
|
+
FlowInputDropZoneProps,
|
|
18
|
+
FlowInputDropZoneRenderProps,
|
|
19
|
+
FlowInputUrlFieldProps,
|
|
20
|
+
FlowInputPreviewProps,
|
|
21
|
+
FlowInputPreviewRenderProps,
|
|
22
|
+
FlowProgressProps,
|
|
23
|
+
FlowProgressRenderProps,
|
|
24
|
+
FlowStatusProps,
|
|
25
|
+
FlowStatusRenderProps,
|
|
26
|
+
FlowErrorProps,
|
|
27
|
+
FlowErrorRenderProps,
|
|
28
|
+
FlowSubmitProps,
|
|
29
|
+
FlowCancelProps,
|
|
30
|
+
FlowResetProps,
|
|
31
|
+
} from "./components/flow-primitives";
|
|
32
|
+
|
|
33
|
+
// ============ FLOW HOOKS ============
|
|
34
|
+
// useFlow is the primary hook for flow operations
|
|
35
|
+
export type {
|
|
36
|
+
FlowInputMetadata,
|
|
37
|
+
InputExecutionState,
|
|
38
|
+
UseFlowReturn,
|
|
39
|
+
FlowUploadState,
|
|
40
|
+
FlowUploadStatus,
|
|
41
|
+
} from "./hooks/use-flow";
|
|
42
|
+
export { useFlow } from "./hooks/use-flow";
|
|
4
43
|
export type { UseFlowEventsOptions } from "./hooks/use-flow-events";
|
|
5
44
|
export { useFlowEvents } from "./hooks/use-flow-events";
|
|
45
|
+
export type { UseMultiFlowUploadReturn } from "./hooks/use-multi-flow-upload";
|
|
46
|
+
export { useMultiFlowUpload } from "./hooks/use-multi-flow-upload";
|
|
47
|
+
|
|
48
|
+
// ============ UPLOAD HOOKS ============
|
|
49
|
+
export type {
|
|
50
|
+
MultiUploadOptions,
|
|
51
|
+
MultiUploadState,
|
|
52
|
+
UploadItem,
|
|
53
|
+
UseMultiUploadReturn,
|
|
54
|
+
} from "./hooks/use-multi-upload";
|
|
55
|
+
export { useMultiUpload } from "./hooks/use-multi-upload";
|
|
56
|
+
export type {
|
|
57
|
+
UploadState,
|
|
58
|
+
UploadStatus,
|
|
59
|
+
UseUploadOptions,
|
|
60
|
+
UseUploadReturn,
|
|
61
|
+
} from "./hooks/use-upload";
|
|
62
|
+
export { useUpload } from "./hooks/use-upload";
|
|
6
63
|
export type {
|
|
7
64
|
UploadFailedEventData,
|
|
8
65
|
UploadFileEventData,
|
|
@@ -13,57 +70,23 @@ export type {
|
|
|
13
70
|
UseUploadEventsOptions,
|
|
14
71
|
} from "./hooks/use-upload-events";
|
|
15
72
|
export { useUploadEvents } from "./hooks/use-upload-events";
|
|
16
|
-
|
|
17
|
-
// Flow Upload Hooks
|
|
18
|
-
export type { FlowInputProps } from "./components/flow-input";
|
|
19
|
-
export { FlowInput } from "./components/flow-input";
|
|
20
|
-
|
|
21
73
|
export type {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
} from "./
|
|
27
|
-
export {
|
|
28
|
-
FlowUploadList,
|
|
29
|
-
SimpleFlowUploadList,
|
|
30
|
-
SimpleFlowUploadListItem,
|
|
31
|
-
} from "./components/flow-upload-list";
|
|
32
|
-
// Flow Upload Components
|
|
33
|
-
export type {
|
|
34
|
-
FlowUploadZoneProps,
|
|
35
|
-
FlowUploadZoneRenderProps,
|
|
36
|
-
SimpleFlowUploadZoneProps,
|
|
37
|
-
} from "./components/flow-upload-zone";
|
|
38
|
-
export {
|
|
39
|
-
FlowUploadZone,
|
|
40
|
-
SimpleFlowUploadZone,
|
|
41
|
-
} from "./components/flow-upload-zone";
|
|
42
|
-
export type {
|
|
43
|
-
FlowUploadState,
|
|
44
|
-
FlowUploadStatus,
|
|
45
|
-
UseFlowUploadReturn,
|
|
46
|
-
} from "./hooks/use-flow-upload";
|
|
47
|
-
export { useFlowUpload } from "./hooks/use-flow-upload";
|
|
48
|
-
export type { UseMultiFlowUploadReturn } from "./hooks/use-multi-flow-upload";
|
|
49
|
-
export { useMultiFlowUpload } from "./hooks/use-multi-flow-upload";
|
|
50
|
-
|
|
51
|
-
// Flow Hooks
|
|
52
|
-
export type {
|
|
53
|
-
FlowInputMetadata,
|
|
54
|
-
InputExecutionState,
|
|
55
|
-
UseFlowReturn,
|
|
56
|
-
} from "./hooks/use-flow";
|
|
57
|
-
export { useFlow } from "./hooks/use-flow";
|
|
74
|
+
FileUploadMetrics,
|
|
75
|
+
UploadMetrics,
|
|
76
|
+
UseUploadMetricsOptions,
|
|
77
|
+
UseUploadMetricsReturn,
|
|
78
|
+
} from "./hooks/use-upload-metrics";
|
|
79
|
+
export { useUploadMetrics } from "./hooks/use-upload-metrics";
|
|
58
80
|
|
|
81
|
+
// ============ DRAG & DROP ============
|
|
59
82
|
export type {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
} from "./hooks/use-
|
|
64
|
-
export {
|
|
83
|
+
DragDropOptions,
|
|
84
|
+
DragDropState,
|
|
85
|
+
UseDragDropReturn,
|
|
86
|
+
} from "./hooks/use-drag-drop";
|
|
87
|
+
export { useDragDrop } from "./hooks/use-drag-drop";
|
|
65
88
|
|
|
66
|
-
//
|
|
89
|
+
// ============ UPLOAD COMPONENTS ============
|
|
67
90
|
export type {
|
|
68
91
|
SimpleUploadListItemProps,
|
|
69
92
|
UploadListProps,
|
|
@@ -77,48 +100,34 @@ export type {
|
|
|
77
100
|
} from "./components/upload-zone";
|
|
78
101
|
export { SimpleUploadZone, UploadZone } from "./components/upload-zone";
|
|
79
102
|
|
|
80
|
-
//
|
|
103
|
+
// ============ FLOW UPLOAD LIST (for batch uploads) ============
|
|
104
|
+
export type {
|
|
105
|
+
FlowUploadListProps,
|
|
106
|
+
FlowUploadListRenderProps,
|
|
107
|
+
SimpleFlowUploadListItemProps,
|
|
108
|
+
SimpleFlowUploadListProps,
|
|
109
|
+
} from "./components/flow-upload-list";
|
|
110
|
+
export {
|
|
111
|
+
FlowUploadList,
|
|
112
|
+
SimpleFlowUploadList,
|
|
113
|
+
SimpleFlowUploadListItem,
|
|
114
|
+
} from "./components/flow-upload-list";
|
|
115
|
+
|
|
116
|
+
// ============ PROVIDERS & CONTEXTS ============
|
|
81
117
|
export {
|
|
82
118
|
UploadistaProvider,
|
|
83
119
|
useUploadistaContext,
|
|
84
120
|
} from "./components/uploadista-provider";
|
|
85
|
-
|
|
86
|
-
// Contexts
|
|
87
121
|
export {
|
|
88
122
|
FlowManagerProvider,
|
|
89
123
|
useFlowManagerContext,
|
|
90
124
|
} from "./contexts/flow-manager-context";
|
|
91
|
-
export type {
|
|
92
|
-
DragDropOptions,
|
|
93
|
-
DragDropState,
|
|
94
|
-
UseDragDropReturn,
|
|
95
|
-
} from "./hooks/use-drag-drop";
|
|
96
|
-
export { useDragDrop } from "./hooks/use-drag-drop";
|
|
97
125
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
MultiUploadState,
|
|
101
|
-
UploadItem,
|
|
102
|
-
UseMultiUploadReturn,
|
|
103
|
-
} from "./hooks/use-multi-upload";
|
|
104
|
-
export { useMultiUpload } from "./hooks/use-multi-upload";
|
|
105
|
-
export type {
|
|
106
|
-
UploadState,
|
|
107
|
-
UploadStatus,
|
|
108
|
-
UseUploadOptions,
|
|
109
|
-
UseUploadReturn,
|
|
110
|
-
} from "./hooks/use-upload";
|
|
111
|
-
export { useUpload } from "./hooks/use-upload";
|
|
112
|
-
export type {
|
|
113
|
-
FileUploadMetrics,
|
|
114
|
-
UploadMetrics,
|
|
115
|
-
UseUploadMetricsOptions,
|
|
116
|
-
UseUploadMetricsReturn,
|
|
117
|
-
} from "./hooks/use-upload-metrics";
|
|
118
|
-
export { useUploadMetrics } from "./hooks/use-upload-metrics";
|
|
119
|
-
// Types - Hooks
|
|
126
|
+
// ============ EVENT UTILITIES ============
|
|
127
|
+
export { isFlowEvent, isUploadEvent } from "./hooks/event-utils";
|
|
120
128
|
export type {
|
|
121
129
|
UseUploadistaClientOptions,
|
|
122
130
|
UseUploadistaClientReturn,
|
|
123
131
|
} from "./hooks/use-uploadista-client";
|
|
124
132
|
export { useUploadistaClient } from "./hooks/use-uploadista-client";
|
|
133
|
+
export { useUploadistaEvents } from "./hooks/use-uploadista-events";
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import{a as e,i as t,n,r,t as i}from"./use-upload-BDHVhQsI.mjs";import{useCallback as a}from"react";import{Fragment as o,jsx as s,jsxs as c}from"react/jsx-runtime";function l({input:t,accept:n=`*`,allowUrl:r=!0,value:i,onChange:a,disabled:o=!1,className:l=``}){let u=i instanceof File,d=typeof i==`string`&&i.length>0,f=t.inputTypeId===`streaming-input-v1`,p=r&&(t.inputTypeId===`url-input-v1`||t.inputTypeId===`streaming-input-v1`),m=e({onFilesReceived:e=>{e[0]&&a(e[0])},accept:n?n.split(`,`).map(e=>e.trim()):void 0,multiple:!1}),h=e=>{a(e.target.value)},g=()=>{a(``)};return c(`div`,{className:`space-y-3 ${l}`,children:[c(`div`,{children:[c(`div`,{className:`flex items-center gap-2 mb-1`,children:[s(`h4`,{className:`font-semibold text-gray-900`,children:t.nodeName}),t.required&&s(`span`,{className:`text-red-500 text-sm`,children:`*`}),s(`span`,{className:`text-xs px-2 py-1 rounded-full bg-blue-100 text-blue-700 font-medium`,children:t.inputTypeId})]}),t.nodeDescription&&s(`p`,{className:`text-sm text-gray-600`,children:t.nodeDescription})]}),f&&c(`div`,{role:`button`,tabIndex:o?-1:0,...m.dragHandlers,onClick:()=>!o&&m.openFilePicker(),onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&!o&&(e.preventDefault(),m.openFilePicker())},className:`
|
|
2
|
-
relative border-2 border-dashed rounded-xl p-6 text-center cursor-pointer transition-all
|
|
3
|
-
${m.state.isDragging?`border-indigo-500 bg-indigo-50`:`border-gray-300 bg-gray-50 hover:border-indigo-400 hover:bg-indigo-50/50`}
|
|
4
|
-
${o?`opacity-50 cursor-not-allowed`:``}
|
|
5
|
-
`,children:[m.state.isDragging?c(`div`,{className:`flex flex-col items-center gap-2`,children:[c(`svg`,{className:`w-8 h-8 text-indigo-600`,fill:`none`,viewBox:`0 0 24 24`,stroke:`currentColor`,children:[s(`title`,{children:`Drop file`}),s(`path`,{strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12`})]}),s(`p`,{className:`text-sm font-medium text-indigo-600`,children:`Drop file here`})]}):u?c(`div`,{className:`flex items-center justify-between`,children:[c(`div`,{className:`flex items-center gap-3`,children:[s(`svg`,{className:`w-5 h-5 text-green-500`,fill:`currentColor`,viewBox:`0 0 20 20`,"aria-hidden":`true`,children:s(`path`,{fillRule:`evenodd`,d:`M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z`,clipRule:`evenodd`})}),c(`div`,{className:`text-left`,children:[s(`p`,{className:`text-sm font-medium text-gray-900`,children:i.name}),c(`p`,{className:`text-xs text-gray-500`,children:[(i.size/1024).toFixed(1),` KB`]})]})]}),s(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),g()},className:`px-3 py-1 text-sm text-red-600 hover:text-red-700 hover:bg-red-50 rounded-lg transition-colors`,children:`Remove`})]}):c(`div`,{className:`flex flex-col items-center gap-2`,children:[c(`svg`,{className:`w-8 h-8 text-gray-400`,fill:`none`,viewBox:`0 0 24 24`,stroke:`currentColor`,children:[s(`title`,{children:`Upload file`}),s(`path`,{strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12`})]}),c(`div`,{children:[s(`p`,{className:`text-sm font-medium text-gray-700`,children:`Drag and drop a file here, or click to select`}),c(`p`,{className:`text-xs text-gray-500 mt-1`,children:[`Accepted: `,n]})]})]}),s(`input`,{...m.inputProps})]}),m.state.errors.length>0&&s(`div`,{className:`bg-red-50 border border-red-200 rounded-lg p-3`,children:m.state.errors.map((e,t)=>s(`p`,{className:`text-sm text-red-700`,children:e},t))}),p&&f&&c(`div`,{className:`flex items-center gap-3`,children:[s(`div`,{className:`flex-1 border-t border-gray-300`}),s(`span`,{className:`text-xs font-medium text-gray-500 uppercase`,children:`Or`}),s(`div`,{className:`flex-1 border-t border-gray-300`})]}),p&&c(`div`,{className:`space-y-2`,children:[!f&&s(`label`,{htmlFor:`url-${t.nodeId}`,className:`block text-sm font-medium text-gray-700`,children:`URL`}),c(`div`,{className:`relative`,children:[s(`input`,{id:`url-${t.nodeId}`,type:`url`,value:d?i:``,onChange:h,disabled:o,placeholder:`https://example.com/file.jpg`,className:`block w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed text-sm`}),d&&s(`button`,{type:`button`,onClick:g,disabled:o,className:`absolute right-2 top-1/2 -translate-y-1/2 p-1 text-gray-400 hover:text-gray-600 rounded-lg hover:bg-gray-100 transition-colors`,"aria-label":`Clear URL`,children:c(`svg`,{className:`w-4 h-4`,fill:`none`,viewBox:`0 0 24 24`,stroke:`currentColor`,children:[s(`title`,{children:`Clear`}),s(`path`,{strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M6 18L18 6M6 6l12 12`})]})})]}),d&&c(`div`,{className:`flex items-center gap-2 text-xs text-gray-600`,children:[s(`svg`,{className:`w-4 h-4 text-blue-500`,fill:`currentColor`,viewBox:`0 0 20 20`,"aria-hidden":`true`,children:s(`path`,{fillRule:`evenodd`,d:`M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5zm-5 5a2 2 0 012.828 0 1 1 0 101.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5a2 2 0 11-2.828-2.828l3-3z`,clipRule:`evenodd`})}),s(`span`,{className:`truncate`,children:i})]})]})]})}function u({flowConfig:e,options:n,children:r}){let i=t({...n,flowConfig:e});return s(o,{children:r({items:i.state.items,totalProgress:i.state.totalProgress,activeUploads:i.state.activeUploads,completedUploads:i.state.completedUploads,failedUploads:i.state.failedUploads,isUploading:i.isUploading,addFiles:i.addFiles,removeFile:i.removeFile,startUpload:i.startUpload,abortUpload:i.abortUpload,abortAll:i.abortAll,clear:i.clear,retryUpload:i.retryUpload})})}function d({item:e,onAbort:t,onRetry:n,onRemove:r}){return c(`div`,{style:{display:`flex`,alignItems:`center`,gap:`12px`,padding:`8px`,borderBottom:`1px solid #eee`},children:[s(`span`,{style:{color:(()=>{switch(e.status){case`success`:return`green`;case`error`:return`red`;case`uploading`:return`blue`;case`aborted`:return`gray`;default:return`black`}})(),fontSize:`18px`},children:(()=>{switch(e.status){case`success`:return`✓`;case`error`:return`✗`;case`uploading`:return`⟳`;case`aborted`:return`⊘`;default:return`○`}})()}),c(`div`,{style:{flex:1,minWidth:0},children:[s(`div`,{style:{fontSize:`14px`,fontWeight:500,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:e.file instanceof File?e.file.name:`Upload`}),e.status===`uploading`&&c(`div`,{style:{marginTop:`4px`},children:[s(`progress`,{value:e.progress,max:100,style:{width:`100%`,height:`4px`}}),c(`div`,{style:{fontSize:`12px`,color:`#666`,marginTop:`2px`},children:[e.progress,`% • `,Math.round(e.bytesUploaded/1024),` KB /`,` `,Math.round(e.totalBytes/1024),` KB`]})]}),e.status===`error`&&s(`div`,{style:{fontSize:`12px`,color:`red`,marginTop:`2px`},children:e.error?.message||`Upload failed`}),e.status===`success`&&s(`div`,{style:{fontSize:`12px`,color:`green`,marginTop:`2px`},children:`Upload complete`})]}),c(`div`,{style:{display:`flex`,gap:`8px`},children:[e.status===`uploading`&&s(`button`,{type:`button`,onClick:t,style:{padding:`4px 8px`,fontSize:`12px`,borderRadius:`4px`,border:`1px solid #ccc`,backgroundColor:`#fff`,cursor:`pointer`},children:`Cancel`}),e.status===`error`&&s(`button`,{type:`button`,onClick:n,style:{padding:`4px 8px`,fontSize:`12px`,borderRadius:`4px`,border:`1px solid #ccc`,backgroundColor:`#fff`,cursor:`pointer`},children:`Retry`}),(e.status===`pending`||e.status===`error`||e.status===`aborted`)&&s(`button`,{type:`button`,onClick:r,style:{padding:`4px 8px`,fontSize:`12px`,borderRadius:`4px`,border:`1px solid #ccc`,backgroundColor:`#fff`,cursor:`pointer`},children:`Remove`})]})]})}function f({flowConfig:e,options:t,className:n=``,showFileInput:r=!0,accept:i}){return s(u,{flowConfig:e,options:t,children:({items:e,addFiles:t,startUpload:a,abortUpload:o,retryUpload:l,removeFile:u,totalProgress:f})=>c(`div`,{className:n,children:[r&&s(`div`,{style:{marginBottom:`16px`},children:s(`input`,{type:`file`,multiple:!0,accept:i,onChange:e=>{e.target.files&&(t(e.target.files),a())},style:{padding:`8px`,border:`1px solid #ccc`,borderRadius:`4px`}})}),e.length>0&&c(`div`,{children:[c(`div`,{style:{marginBottom:`8px`,fontSize:`14px`,color:`#666`},children:[`Total Progress: `,f,`%`]}),s(`div`,{style:{border:`1px solid #eee`,borderRadius:`8px`,overflow:`hidden`},children:e.map(e=>s(d,{item:e,onAbort:()=>o(e.id),onRetry:()=>l(e.id),onRemove:()=>u(e.id)},e.id))})]})]})})}function p({flowConfig:t,options:n,accept:i,multiple:a=!1,children:c}){let l=r({...n,flowConfig:t}),u=e({onFilesReceived:e=>{let t=e[0];t&&l.upload(t)},accept:i?[i]:void 0,multiple:a}),d=e=>{let t=e.target.files?.[0];t&&l.upload(t)};return s(o,{children:c({flowUpload:l,dragDrop:u,isActive:u.state.isDragging||u.state.isOver,openFilePicker:u.openFilePicker,getRootProps:()=>u.dragHandlers,getInputProps:()=>({...u.inputProps,onChange:d})})})}function m({flowConfig:e,options:t,accept:n,className:r=``,dragText:i=`Drop files here`,idleText:a=`Drag & drop files or click to browse`}){return s(p,{flowConfig:e,options:t,accept:n,children:({dragDrop:e,flowUpload:t,getRootProps:n,getInputProps:o,openFilePicker:l})=>c(`div`,{...n(),className:r,style:{border:`2px dashed #ccc`,borderRadius:`8px`,padding:`32px`,textAlign:`center`,cursor:`pointer`,backgroundColor:e.state.isDragging?`#f0f0f0`:`transparent`,transition:`background-color 0.2s`},children:[s(`input`,{...o()}),e.state.isDragging&&s(`p`,{style:{margin:0},children:i}),!e.state.isDragging&&!t.isUploading&&t.state.status===`idle`&&c(`div`,{children:[s(`p`,{style:{margin:`0 0 16px 0`},children:a}),s(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),l()},style:{padding:`8px 16px`,borderRadius:`4px`,border:`1px solid #ccc`,backgroundColor:`#fff`,cursor:`pointer`},children:`Choose Files`})]}),t.isUploading&&c(`div`,{children:[s(`progress`,{value:t.state.progress,max:100,style:{width:`100%`,height:`8px`}}),c(`p`,{style:{margin:`8px 0 0 0`},children:[t.state.progress,`%`]}),s(`button`,{type:`button`,onClick:e=>{e.stopPropagation()},style:{marginTop:`8px`,padding:`4px 12px`,borderRadius:`4px`,border:`1px solid #ccc`,backgroundColor:`#fff`,cursor:`pointer`},children:`Cancel`})]}),t.state.status===`success`&&s(`div`,{children:s(`p`,{style:{margin:0,color:`green`},children:`✓ Upload complete!`})}),t.state.status===`error`&&s(`div`,{children:c(`p`,{style:{margin:0,color:`red`},children:[`✗ Error: `,t.state.error?.message]})})]})})}function h({multiUpload:e,filter:t,sortBy:n,children:r}){let i=e.items;t&&(i=i.filter(t)),n&&(i=[...i].sort(n));let a={idle:i.filter(e=>e.state.status===`idle`),uploading:i.filter(e=>e.state.status===`uploading`),success:i.filter(e=>e.state.status===`success`),error:i.filter(e=>e.state.status===`error`),aborted:i.filter(e=>e.state.status===`aborted`)};return s(o,{children:r({items:i,itemsByStatus:a,multiUpload:e,actions:{removeItem:t=>{e.removeItem(t)},retryItem:t=>{e.retryFailed()},abortItem:t=>{e.removeItem(t.id)},startItem:t=>{e.startAll()}}})})}function g({item:e,actions:t,className:n=``,style:r={},showDetails:i=!0}){let a=e=>{switch(e){case`idle`:return`#6c757d`;case`uploading`:return`#007bff`;case`success`:return`#28a745`;case`error`:return`#dc3545`;case`aborted`:return`#6c757d`;default:return`#6c757d`}},l=e=>{switch(e){case`idle`:return`⏳`;case`uploading`:return`📤`;case`success`:return`✅`;case`error`:return`❌`;case`aborted`:return`⏹️`;default:return`❓`}},u=e=>{if(e===0)return`0 Bytes`;let t=1024,n=[`Bytes`,`KB`,`MB`,`GB`],r=Math.floor(Math.log(e)/Math.log(t));return`${parseFloat((e/t**r).toFixed(2))} ${n[r]}`};return c(`div`,{className:`upload-list-item upload-list-item--${e.state.status} ${n}`,style:{padding:`12px`,border:`1px solid #e0e0e0`,borderRadius:`6px`,marginBottom:`8px`,backgroundColor:`#fff`,transition:`all 0.2s ease`,...r},children:[c(`div`,{style:{display:`flex`,justifyContent:`space-between`,alignItems:`center`,marginBottom:`8px`},children:[c(`div`,{style:{display:`flex`,alignItems:`center`,gap:`8px`,flex:1},children:[s(`span`,{style:{fontSize:`16px`},children:l(e.state.status)}),s(`span`,{style:{fontWeight:`500`,flex:1},children:e.file instanceof File?e.file.name:`File`})]}),s(`span`,{style:{fontSize:`12px`,color:a(e.state.status),fontWeight:`500`,textTransform:`uppercase`},children:e.state.status})]}),e.state.status===`uploading`&&c(`div`,{style:{marginBottom:`8px`},children:[c(`div`,{style:{display:`flex`,justifyContent:`space-between`,alignItems:`center`,marginBottom:`4px`},children:[c(`span`,{style:{fontSize:`12px`,color:`#666`},children:[e.state.progress,`%`]}),i&&e.state.totalBytes&&c(`span`,{style:{fontSize:`12px`,color:`#666`},children:[u(e.state.bytesUploaded),` /`,` `,u(e.state.totalBytes)]})]}),s(`div`,{style:{width:`100%`,height:`6px`,backgroundColor:`#e0e0e0`,borderRadius:`3px`,overflow:`hidden`},children:s(`div`,{style:{width:`${e.state.progress}%`,height:`100%`,backgroundColor:`#007bff`,transition:`width 0.2s ease`}})})]}),i&&c(`div`,{style:{fontSize:`12px`,color:`#666`,marginBottom:`8px`},children:[e.state.totalBytes&&s(`span`,{children:u(e.state.totalBytes)}),e.state.status===`uploading`&&e.state.progress>0&&c(`span`,{children:[` • Progress: `,e.state.progress,`%`]}),e.state.status===`error`&&e.state.error&&s(`div`,{style:{color:`#dc3545`,marginTop:`4px`},children:e.state.error.message})]}),c(`div`,{style:{display:`flex`,gap:`8px`,flexWrap:`wrap`},children:[e.state.status===`idle`&&c(o,{children:[s(`button`,{type:`button`,onClick:()=>t.startItem(e),style:{padding:`4px 8px`,fontSize:`12px`,border:`1px solid #007bff`,backgroundColor:`#007bff`,color:`white`,borderRadius:`4px`,cursor:`pointer`},children:`Start`}),s(`button`,{type:`button`,onClick:()=>t.removeItem(e.id),style:{padding:`4px 8px`,fontSize:`12px`,border:`1px solid #6c757d`,backgroundColor:`transparent`,color:`#6c757d`,borderRadius:`4px`,cursor:`pointer`},children:`Remove`})]}),e.state.status===`uploading`&&s(`button`,{type:`button`,onClick:()=>t.abortItem(e),style:{padding:`4px 8px`,fontSize:`12px`,border:`1px solid #dc3545`,backgroundColor:`transparent`,color:`#dc3545`,borderRadius:`4px`,cursor:`pointer`},children:`Cancel`}),e.state.status===`error`&&c(o,{children:[s(`button`,{type:`button`,onClick:()=>t.retryItem(e),style:{padding:`4px 8px`,fontSize:`12px`,border:`1px solid #28a745`,backgroundColor:`#28a745`,color:`white`,borderRadius:`4px`,cursor:`pointer`},children:`Retry`}),s(`button`,{type:`button`,onClick:()=>t.removeItem(e.id),style:{padding:`4px 8px`,fontSize:`12px`,border:`1px solid #6c757d`,backgroundColor:`transparent`,color:`#6c757d`,borderRadius:`4px`,cursor:`pointer`},children:`Remove`})]}),e.state.status===`success`&&s(`button`,{type:`button`,onClick:()=>t.removeItem(e.id),style:{padding:`4px 8px`,fontSize:`12px`,border:`1px solid #6c757d`,backgroundColor:`transparent`,color:`#6c757d`,borderRadius:`4px`,cursor:`pointer`},children:`Remove`}),e.state.status===`aborted`&&c(o,{children:[s(`button`,{type:`button`,onClick:()=>t.retryItem(e),style:{padding:`4px 8px`,fontSize:`12px`,border:`1px solid #007bff`,backgroundColor:`#007bff`,color:`white`,borderRadius:`4px`,cursor:`pointer`},children:`Retry`}),s(`button`,{type:`button`,onClick:()=>t.removeItem(e.id),style:{padding:`4px 8px`,fontSize:`12px`,border:`1px solid #6c757d`,backgroundColor:`transparent`,color:`#6c757d`,borderRadius:`4px`,cursor:`pointer`},children:`Remove`})]})]})]})}function _({children:t,multiple:r=!0,multiUploadOptions:c={},uploadOptions:l={},onUploadStart:u,onValidationError:d,...f}){let p=i(l),m=n(c),h=a(e=>{let t=[];if(!r&&e.length>1&&t.push(`Single file mode is enabled. Please select only one file. You selected ${e.length} files.`),f.accept&&f.accept.length>0){let n=e.filter(e=>!f.accept?.some(t=>{if(t.startsWith(`.`))return e.name.toLowerCase().endsWith(t.toLowerCase());if(t.endsWith(`/*`)){let n=t.slice(0,-2);return e.type.startsWith(n)}else return e.type===t}));if(n.length>0){let e=n.map(e=>`"${e.name}" (${e.type})`).join(`, `),r=f.accept.join(`, `);t.push(`Invalid file type(s): ${e}. Accepted types: ${r}.`)}}return t.length>0?t:null},[r,f.accept]),g=e=>{u?.(e),r&&m?(m.addFiles(e),setTimeout(()=>m.startAll(),0)):!r&&p&&e.length>0&&e[0]&&p.upload(e[0])},_=a(e=>{console.error(`Upload zone validation errors:`,e),d?.(e)},[d]),v=e({...f,multiple:r,validator:h,onFilesReceived:g,onValidationError:_}),y=v.state.isDragging||v.state.isOver,b=r?m?.state.isUploading??!1:p?.isUploading??!1;return s(o,{children:t({dragDrop:v,upload:p,multiUpload:m,openFilePicker:v.openFilePicker,isActive:y,isProcessing:b})})}function v({className:e=``,style:t={},text:n={},errorStyle:r={},children:i,...a}){let o={idle:a.multiple?`Drag files here or click to select`:`Drag a file here or click to select`,dragging:a.multiple?`Drop files here...`:`Drop file here...`,uploading:`Uploading...`,...n};return i?s(_,{...a,children:i}):s(_,{...a,children:({dragDrop:n,upload:i,multiUpload:a,openFilePicker:l,isActive:u,isProcessing:d})=>c(`button`,{type:`button`,onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&l()},onKeyUp:e=>{(e.key===`Enter`||e.key===` `)&&l()},...n.dragHandlers,onClick:l,className:`upload-zone ${u?`upload-zone--active`:``} ${d?`upload-zone--processing`:``} ${e}`,style:{border:u?`2px dashed #007bff`:`2px dashed #ccc`,borderRadius:`8px`,padding:`2rem`,textAlign:`center`,cursor:`pointer`,backgroundColor:u?`#f8f9fa`:`transparent`,transition:`all 0.2s ease`,minHeight:`120px`,display:`flex`,flexDirection:`column`,alignItems:`center`,justifyContent:`center`,...t},children:[n.state.isDragging?s(`p`,{style:{margin:0,fontSize:`16px`,color:`#007bff`},children:o.dragging}):d?c(`div`,{style:{textAlign:`center`},children:[s(`p`,{style:{margin:`0 0 10px 0`,fontSize:`14px`},children:o.uploading}),i&&c(`div`,{children:[s(`progress`,{value:i.state.progress,max:100,style:{width:`200px`,height:`8px`}}),c(`p`,{style:{margin:`5px 0 0 0`,fontSize:`12px`,color:`#666`},children:[i.state.progress,`%`]})]}),a&&c(`div`,{children:[s(`progress`,{value:a.state.progress,max:100,style:{width:`200px`,height:`8px`}}),c(`p`,{style:{margin:`5px 0 0 0`,fontSize:`12px`,color:`#666`},children:[a.state.progress,`% (`,a.state.uploading,` `,`uploading, `,a.state.successful,` completed)`]})]})]}):s(`p`,{style:{margin:0,fontSize:`16px`,color:`#666`},children:o.idle}),n.state.errors.length>0&&c(`div`,{style:{marginTop:`10px`,padding:`8px 12px`,backgroundColor:`#f8d7da`,border:`1px solid #f5c6cb`,borderRadius:`4px`,maxWidth:`100%`,...r},children:[s(`p`,{style:{margin:`0 0 5px 0`,fontSize:`12px`,fontWeight:`bold`,color:`#721c24`},children:`Validation Errors:`}),n.state.errors.map((e,t)=>c(`p`,{style:{color:`#721c24`,fontSize:`11px`,margin:`2px 0`,lineHeight:`1.3`},children:[`• `,e]},t))]}),s(`input`,{...n.inputProps})]})})}export{p as a,f as c,h as i,d as l,_ as n,m as o,g as r,u as s,v as t,l as u};
|
|
6
|
-
//# sourceMappingURL=upload-zone-BjWHuP7p.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"upload-zone-BjWHuP7p.mjs","names":["errors: string[]"],"sources":["../src/components/flow-input.tsx","../src/components/flow-upload-list.tsx","../src/components/flow-upload-zone.tsx","../src/components/upload-list.tsx","../src/components/upload-zone.tsx"],"sourcesContent":["/** biome-ignore-all lint/a11y/useSemanticElements: button is inside a div*/\n\"use client\";\n\nimport { useDragDrop } from \"../hooks/use-drag-drop\";\nimport type { FlowInputMetadata } from \"../hooks/use-flow\";\n\nexport interface FlowInputProps {\n /** Input metadata from flow discovery */\n input: FlowInputMetadata;\n /** Accepted file types (e.g., \"image/*\", \"video/*\") */\n accept?: string;\n /** Whether the input should support URL input */\n allowUrl?: boolean;\n /** Current value (File or URL string) */\n value?: File | string | null;\n /** Callback when value changes */\n onChange: (value: File | string) => void;\n /** Whether the input is disabled */\n disabled?: boolean;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Input component for flow execution with file drag-and-drop and URL support.\n *\n * Features:\n * - File drag-and-drop with visual feedback\n * - URL input for remote files\n * - Displays node name and description\n * - Shows selected file/URL with size\n * - Type validation and error display\n *\n * @example\n * ```tsx\n * <FlowInput\n * input={inputMetadata}\n * accept=\"image/*\"\n * allowUrl={true}\n * value={selectedValue}\n * onChange={(value) => flow.setInput(inputMetadata.nodeId, value)}\n * />\n * ```\n */\nexport function FlowInput({\n input,\n accept = \"*\",\n allowUrl = true,\n value,\n onChange,\n disabled = false,\n className = \"\",\n}: FlowInputProps) {\n const isFileValue = value instanceof File;\n const isUrlValue = typeof value === \"string\" && value.length > 0;\n\n // Determine input mode based on input type\n const supportsFileUpload = input.inputTypeId === \"streaming-input-v1\";\n const supportsUrl =\n allowUrl &&\n (input.inputTypeId === \"url-input-v1\" ||\n input.inputTypeId === \"streaming-input-v1\");\n\n const dragDrop = useDragDrop({\n onFilesReceived: (files) => {\n if (files[0]) {\n onChange(files[0]);\n }\n },\n accept: accept ? accept.split(\",\").map((type) => type.trim()) : undefined,\n multiple: false,\n });\n\n const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (file) {\n onChange(file);\n }\n };\n\n const handleUrlChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n onChange(e.target.value);\n };\n\n const handleClear = () => {\n onChange(\"\");\n };\n\n return (\n <div className={`space-y-3 ${className}`}>\n {/* Header */}\n <div>\n <div className=\"flex items-center gap-2 mb-1\">\n <h4 className=\"font-semibold text-gray-900\">{input.nodeName}</h4>\n {input.required && <span className=\"text-red-500 text-sm\">*</span>}\n <span className=\"text-xs px-2 py-1 rounded-full bg-blue-100 text-blue-700 font-medium\">\n {input.inputTypeId}\n </span>\n </div>\n {input.nodeDescription && (\n <p className=\"text-sm text-gray-600\">{input.nodeDescription}</p>\n )}\n </div>\n\n {/* File Upload Area */}\n {supportsFileUpload && (\n <div\n role=\"button\"\n tabIndex={disabled ? -1 : 0}\n {...dragDrop.dragHandlers}\n onClick={() => !disabled && dragDrop.openFilePicker()}\n onKeyDown={(e) => {\n if ((e.key === \"Enter\" || e.key === \" \") && !disabled) {\n e.preventDefault();\n dragDrop.openFilePicker();\n }\n }}\n className={`\n relative border-2 border-dashed rounded-xl p-6 text-center cursor-pointer transition-all\n ${\n dragDrop.state.isDragging\n ? \"border-indigo-500 bg-indigo-50\"\n : \"border-gray-300 bg-gray-50 hover:border-indigo-400 hover:bg-indigo-50/50\"\n }\n ${disabled ? \"opacity-50 cursor-not-allowed\" : \"\"}\n `}\n >\n {dragDrop.state.isDragging ? (\n <div className=\"flex flex-col items-center gap-2\">\n <svg\n className=\"w-8 h-8 text-indigo-600\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <title>Drop file</title>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12\"\n />\n </svg>\n <p className=\"text-sm font-medium text-indigo-600\">\n Drop file here\n </p>\n </div>\n ) : isFileValue ? (\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <svg\n className=\"w-5 h-5 text-green-500\"\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n aria-hidden=\"true\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z\"\n clipRule=\"evenodd\"\n />\n </svg>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-gray-900\">\n {value.name}\n </p>\n <p className=\"text-xs text-gray-500\">\n {(value.size / 1024).toFixed(1)} KB\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleClear();\n }}\n className=\"px-3 py-1 text-sm text-red-600 hover:text-red-700 hover:bg-red-50 rounded-lg transition-colors\"\n >\n Remove\n </button>\n </div>\n ) : (\n <div className=\"flex flex-col items-center gap-2\">\n <svg\n className=\"w-8 h-8 text-gray-400\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <title>Upload file</title>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12\"\n />\n </svg>\n <div>\n <p className=\"text-sm font-medium text-gray-700\">\n Drag and drop a file here, or click to select\n </p>\n <p className=\"text-xs text-gray-500 mt-1\">Accepted: {accept}</p>\n </div>\n </div>\n )}\n <input {...dragDrop.inputProps} />\n </div>\n )}\n\n {/* Drag & Drop Errors */}\n {dragDrop.state.errors.length > 0 && (\n <div className=\"bg-red-50 border border-red-200 rounded-lg p-3\">\n {dragDrop.state.errors.map((error, index) => (\n <p key={index} className=\"text-sm text-red-700\">\n {error}\n </p>\n ))}\n </div>\n )}\n\n {/* URL Input */}\n {supportsUrl && supportsFileUpload && (\n <div className=\"flex items-center gap-3\">\n <div className=\"flex-1 border-t border-gray-300\" />\n <span className=\"text-xs font-medium text-gray-500 uppercase\">\n Or\n </span>\n <div className=\"flex-1 border-t border-gray-300\" />\n </div>\n )}\n\n {supportsUrl && (\n <div className=\"space-y-2\">\n {!supportsFileUpload && (\n <label\n htmlFor={`url-${input.nodeId}`}\n className=\"block text-sm font-medium text-gray-700\"\n >\n URL\n </label>\n )}\n <div className=\"relative\">\n <input\n id={`url-${input.nodeId}`}\n type=\"url\"\n value={isUrlValue ? value : \"\"}\n onChange={handleUrlChange}\n disabled={disabled}\n placeholder=\"https://example.com/file.jpg\"\n className=\"block w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed text-sm\"\n />\n {isUrlValue && (\n <button\n type=\"button\"\n onClick={handleClear}\n disabled={disabled}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 p-1 text-gray-400 hover:text-gray-600 rounded-lg hover:bg-gray-100 transition-colors\"\n aria-label=\"Clear URL\"\n >\n <svg\n className=\"w-4 h-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <title>Clear</title>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n )}\n </div>\n {isUrlValue && (\n <div className=\"flex items-center gap-2 text-xs text-gray-600\">\n <svg\n className=\"w-4 h-4 text-blue-500\"\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n aria-hidden=\"true\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5zm-5 5a2 2 0 012.828 0 1 1 0 101.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5a2 2 0 11-2.828-2.828l3-3z\"\n clipRule=\"evenodd\"\n />\n </svg>\n <span className=\"truncate\">{value}</span>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n","import type {\n BrowserUploadInput,\n FlowUploadConfig,\n FlowUploadItem,\n MultiFlowUploadOptions,\n} from \"@uploadista/client-browser\";\nimport type { ReactNode } from \"react\";\nimport { useMultiFlowUpload } from \"../hooks/use-multi-flow-upload\";\n\n/**\n * Render props passed to the FlowUploadList children function.\n * Provides access to upload items, aggregate statistics, and control methods.\n *\n * @property items - All flow upload items in the queue\n * @property totalProgress - Average progress across all uploads (0-100)\n * @property activeUploads - Count of currently uploading items\n * @property completedUploads - Count of successfully completed uploads\n * @property failedUploads - Count of failed uploads\n * @property isUploading - True when any uploads are in progress\n * @property addFiles - Add new files to the upload queue\n * @property removeFile - Remove a specific file from the queue\n * @property startUpload - Begin uploading all pending files\n * @property abortUpload - Cancel a specific active upload\n * @property abortAll - Cancel all active uploads\n * @property clear - Remove all items from the queue\n * @property retryUpload - Retry a specific failed upload\n */\nexport interface FlowUploadListRenderProps {\n /**\n * List of upload items\n */\n items: FlowUploadItem<BrowserUploadInput>[];\n\n /**\n * Total progress across all uploads\n */\n totalProgress: number;\n\n /**\n * Number of active uploads\n */\n activeUploads: number;\n\n /**\n * Number of completed uploads\n */\n completedUploads: number;\n\n /**\n * Number of failed uploads\n */\n failedUploads: number;\n\n /**\n * Whether any uploads are in progress\n */\n isUploading: boolean;\n\n /**\n * Add files to the upload queue\n */\n addFiles: (files: File[] | FileList) => void;\n\n /**\n * Remove a file from the queue\n */\n removeFile: (id: string) => void;\n\n /**\n * Start uploading all pending files\n */\n startUpload: () => void;\n\n /**\n * Abort a specific upload\n */\n abortUpload: (id: string) => void;\n\n /**\n * Abort all uploads\n */\n abortAll: () => void;\n\n /**\n * Clear all items\n */\n clear: () => void;\n\n /**\n * Retry a failed upload\n */\n retryUpload: (id: string) => void;\n}\n\n/**\n * Props for the FlowUploadList component.\n *\n * @property flowConfig - Flow execution configuration (flowId, storageId, etc.)\n * @property options - Multi-flow upload options (callbacks, concurrency, etc.)\n * @property children - Render function receiving flow upload list state\n */\nexport interface FlowUploadListProps {\n /**\n * Flow configuration\n */\n flowConfig: FlowUploadConfig;\n\n /**\n * Multi-upload options\n */\n options?: Omit<MultiFlowUploadOptions<BrowserUploadInput>, \"flowConfig\">;\n\n /**\n * Render function for the upload list\n */\n children: (props: FlowUploadListRenderProps) => ReactNode;\n}\n\n/**\n * Headless flow upload list component for managing batch file uploads through a flow.\n * Uses render props pattern to provide complete control over the UI while handling\n * concurrent uploads and flow processing.\n *\n * Each file is uploaded and processed independently through the specified flow,\n * with automatic queue management and concurrency control.\n *\n * Must be used within an UploadistaProvider.\n *\n * @param props - Flow upload list configuration and render prop\n * @returns Rendered flow upload list using the provided render prop\n *\n * @example\n * ```tsx\n * // Batch image processing with custom UI\n * <FlowUploadList\n * flowConfig={{\n * flowId: \"image-batch-processing\",\n * storageId: \"s3-images\",\n * outputNodeId: \"optimized\",\n * }}\n * options={{\n * maxConcurrent: 3,\n * onItemSuccess: (item) => {\n * console.log(`${item.file.name} processed successfully`);\n * },\n * onComplete: (items) => {\n * const successful = items.filter(i => i.status === 'success');\n * console.log(`Batch complete: ${successful.length}/${items.length} successful`);\n * },\n * }}\n * >\n * {({\n * items,\n * totalProgress,\n * activeUploads,\n * completedUploads,\n * failedUploads,\n * addFiles,\n * startUpload,\n * abortUpload,\n * retryUpload,\n * clear,\n * }) => (\n * <div>\n * <input\n * type=\"file\"\n * multiple\n * accept=\"image/*\"\n * onChange={(e) => {\n * if (e.target.files) {\n * addFiles(e.target.files);\n * startUpload();\n * }\n * }}\n * />\n *\n * <div style={{ marginTop: '1rem' }}>\n * <h3>Upload Progress</h3>\n * <div>Overall: {totalProgress}%</div>\n * <div>\n * Active: {activeUploads}, Completed: {completedUploads}, Failed: {failedUploads}\n * </div>\n * <button onClick={clear}>Clear All</button>\n * </div>\n *\n * <ul style={{ listStyle: 'none', padding: 0 }}>\n * {items.map((item) => (\n * <li key={item.id} style={{\n * padding: '1rem',\n * border: '1px solid #ccc',\n * marginBottom: '0.5rem'\n * }}>\n * <div>{item.file instanceof File ? item.file.name : 'File'}</div>\n * <div>Status: {item.status}</div>\n *\n * {item.status === \"uploading\" && (\n * <div>\n * <progress value={item.progress} max={100} style={{ width: '100%' }} />\n * <div>{item.progress}%</div>\n * <button onClick={() => abortUpload(item.id)}>Cancel</button>\n * </div>\n * )}\n *\n * {item.status === \"error\" && (\n * <div>\n * <div style={{ color: 'red' }}>{item.error?.message}</div>\n * <button onClick={() => retryUpload(item.id)}>Retry</button>\n * </div>\n * )}\n *\n * {item.status === \"success\" && (\n * <div style={{ color: 'green' }}>✓ Complete</div>\n * )}\n * </li>\n * ))}\n * </ul>\n * </div>\n * )}\n * </FlowUploadList>\n * ```\n *\n * @see {@link SimpleFlowUploadList} for a pre-styled version\n * @see {@link useMultiFlowUpload} for the underlying hook\n */\nexport function FlowUploadList({\n flowConfig,\n options,\n children,\n}: FlowUploadListProps) {\n const multiUpload = useMultiFlowUpload({\n ...options,\n flowConfig,\n });\n\n return (\n <>\n {children({\n items: multiUpload.state.items,\n totalProgress: multiUpload.state.totalProgress,\n activeUploads: multiUpload.state.activeUploads,\n completedUploads: multiUpload.state.completedUploads,\n failedUploads: multiUpload.state.failedUploads,\n isUploading: multiUpload.isUploading,\n addFiles: multiUpload.addFiles,\n removeFile: multiUpload.removeFile,\n startUpload: multiUpload.startUpload,\n abortUpload: multiUpload.abortUpload,\n abortAll: multiUpload.abortAll,\n clear: multiUpload.clear,\n retryUpload: multiUpload.retryUpload,\n })}\n </>\n );\n}\n\n/**\n * Props for the SimpleFlowUploadListItem component.\n *\n * @property item - The flow upload item to display\n * @property onAbort - Called when the abort button is clicked\n * @property onRetry - Called when the retry button is clicked\n * @property onRemove - Called when the remove button is clicked\n */\nexport interface SimpleFlowUploadListItemProps {\n /**\n * Upload item\n */\n item: FlowUploadItem<BrowserUploadInput>;\n\n /**\n * Abort the upload\n */\n onAbort: () => void;\n\n /**\n * Retry the upload\n */\n onRetry: () => void;\n\n /**\n * Remove the item\n */\n onRemove: () => void;\n}\n\n/**\n * Pre-styled flow upload list item component with status indicators.\n * Displays file name, upload progress, status, and contextual action buttons.\n *\n * Features:\n * - Status-specific icons and colors\n * - Progress bar with percentage and byte count\n * - Error message display\n * - Contextual action buttons (cancel, retry, remove)\n *\n * @param props - Upload item and callback functions\n * @returns Styled flow upload list item component\n *\n * @example\n * ```tsx\n * <SimpleFlowUploadListItem\n * item={uploadItem}\n * onAbort={() => console.log('Abort')}\n * onRetry={() => console.log('Retry')}\n * onRemove={() => console.log('Remove')}\n * />\n * ```\n */\nexport function SimpleFlowUploadListItem({\n item,\n onAbort,\n onRetry,\n onRemove,\n}: SimpleFlowUploadListItemProps) {\n const getStatusIcon = () => {\n switch (item.status) {\n case \"success\":\n return \"✓\";\n case \"error\":\n return \"✗\";\n case \"uploading\":\n return \"⟳\";\n case \"aborted\":\n return \"⊘\";\n default:\n return \"○\";\n }\n };\n\n const getStatusColor = () => {\n switch (item.status) {\n case \"success\":\n return \"green\";\n case \"error\":\n return \"red\";\n case \"uploading\":\n return \"blue\";\n case \"aborted\":\n return \"gray\";\n default:\n return \"black\";\n }\n };\n\n return (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"12px\",\n padding: \"8px\",\n borderBottom: \"1px solid #eee\",\n }}\n >\n <span style={{ color: getStatusColor(), fontSize: \"18px\" }}>\n {getStatusIcon()}\n </span>\n\n <div style={{ flex: 1, minWidth: 0 }}>\n <div\n style={{\n fontSize: \"14px\",\n fontWeight: 500,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {item.file instanceof File ? item.file.name : \"Upload\"}\n </div>\n\n {item.status === \"uploading\" && (\n <div style={{ marginTop: \"4px\" }}>\n <progress\n value={item.progress}\n max={100}\n style={{ width: \"100%\", height: \"4px\" }}\n />\n <div style={{ fontSize: \"12px\", color: \"#666\", marginTop: \"2px\" }}>\n {item.progress}% • {Math.round(item.bytesUploaded / 1024)} KB /{\" \"}\n {Math.round(item.totalBytes / 1024)} KB\n </div>\n </div>\n )}\n\n {item.status === \"error\" && (\n <div style={{ fontSize: \"12px\", color: \"red\", marginTop: \"2px\" }}>\n {item.error?.message || \"Upload failed\"}\n </div>\n )}\n\n {item.status === \"success\" && (\n <div style={{ fontSize: \"12px\", color: \"green\", marginTop: \"2px\" }}>\n Upload complete\n </div>\n )}\n </div>\n\n <div style={{ display: \"flex\", gap: \"8px\" }}>\n {item.status === \"uploading\" && (\n <button\n type=\"button\"\n onClick={onAbort}\n style={{\n padding: \"4px 8px\",\n fontSize: \"12px\",\n borderRadius: \"4px\",\n border: \"1px solid #ccc\",\n backgroundColor: \"#fff\",\n cursor: \"pointer\",\n }}\n >\n Cancel\n </button>\n )}\n\n {item.status === \"error\" && (\n <button\n type=\"button\"\n onClick={onRetry}\n style={{\n padding: \"4px 8px\",\n fontSize: \"12px\",\n borderRadius: \"4px\",\n border: \"1px solid #ccc\",\n backgroundColor: \"#fff\",\n cursor: \"pointer\",\n }}\n >\n Retry\n </button>\n )}\n\n {(item.status === \"pending\" ||\n item.status === \"error\" ||\n item.status === \"aborted\") && (\n <button\n type=\"button\"\n onClick={onRemove}\n style={{\n padding: \"4px 8px\",\n fontSize: \"12px\",\n borderRadius: \"4px\",\n border: \"1px solid #ccc\",\n backgroundColor: \"#fff\",\n cursor: \"pointer\",\n }}\n >\n Remove\n </button>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * Props for the SimpleFlowUploadList component.\n *\n * @property flowConfig - Flow execution configuration\n * @property options - Multi-flow upload options (callbacks, concurrency)\n * @property className - CSS class name for the container\n * @property showFileInput - Whether to display the file input (default: true)\n * @property accept - Accepted file types for the file input\n */\nexport interface SimpleFlowUploadListProps {\n /**\n * Flow configuration\n */\n flowConfig: FlowUploadConfig;\n\n /**\n * Multi-upload options\n */\n options?: Omit<MultiFlowUploadOptions<BrowserUploadInput>, \"flowConfig\">;\n\n /**\n * CSS class for the container\n */\n className?: string;\n\n /**\n * Show file input\n */\n showFileInput?: boolean;\n\n /**\n * File input accept attribute\n */\n accept?: string;\n}\n\n/**\n * Simple pre-styled flow upload list component with built-in UI.\n * Provides a ready-to-use interface for batch file uploads with flow processing.\n *\n * Features:\n * - Built-in file input\n * - Overall progress display\n * - Individual item progress tracking\n * - Status indicators and action buttons\n * - Automatic upload start on file selection\n *\n * @param props - Flow upload list configuration with styling options\n * @returns Styled flow upload list component\n *\n * @example\n * ```tsx\n * // Basic batch image upload\n * <SimpleFlowUploadList\n * flowConfig={{\n * flowId: \"image-batch-processing\",\n * storageId: \"s3-images\",\n * }}\n * options={{\n * maxConcurrent: 3,\n * onItemSuccess: (item) => {\n * console.log(`${item.file.name} processed`);\n * },\n * onComplete: (items) => {\n * console.log(\"Batch complete:\", items.length);\n * },\n * }}\n * accept=\"image/*\"\n * className=\"my-upload-list\"\n * />\n *\n * // Without file input (add files programmatically)\n * <SimpleFlowUploadList\n * flowConfig={{\n * flowId: \"document-processing\",\n * storageId: \"docs\",\n * }}\n * showFileInput={false}\n * options={{\n * maxConcurrent: 2,\n * }}\n * />\n * ```\n *\n * @see {@link FlowUploadList} for the headless version with full control\n */\nexport function SimpleFlowUploadList({\n flowConfig,\n options,\n className = \"\",\n showFileInput = true,\n accept,\n}: SimpleFlowUploadListProps) {\n return (\n <FlowUploadList flowConfig={flowConfig} options={options}>\n {({\n items,\n addFiles,\n startUpload,\n abortUpload,\n retryUpload,\n removeFile,\n totalProgress,\n }) => (\n <div className={className}>\n {showFileInput && (\n <div style={{ marginBottom: \"16px\" }}>\n <input\n type=\"file\"\n multiple\n accept={accept}\n onChange={(e) => {\n if (e.target.files) {\n addFiles(e.target.files);\n startUpload();\n }\n }}\n style={{\n padding: \"8px\",\n border: \"1px solid #ccc\",\n borderRadius: \"4px\",\n }}\n />\n </div>\n )}\n\n {items.length > 0 && (\n <div>\n <div\n style={{ marginBottom: \"8px\", fontSize: \"14px\", color: \"#666\" }}\n >\n Total Progress: {totalProgress}%\n </div>\n\n <div\n style={{\n border: \"1px solid #eee\",\n borderRadius: \"8px\",\n overflow: \"hidden\",\n }}\n >\n {items.map((item) => (\n <SimpleFlowUploadListItem\n key={item.id}\n item={item}\n onAbort={() => abortUpload(item.id)}\n onRetry={() => retryUpload(item.id)}\n onRemove={() => removeFile(item.id)}\n />\n ))}\n </div>\n </div>\n )}\n </div>\n )}\n </FlowUploadList>\n );\n}\n","import type {\n FlowUploadConfig,\n FlowUploadOptions,\n} from \"@uploadista/client-browser\";\nimport type { ReactNode } from \"react\";\nimport { type UseDragDropReturn, useDragDrop } from \"../hooks/use-drag-drop\";\nimport {\n type UseFlowUploadReturn,\n useFlowUpload,\n} from \"../hooks/use-flow-upload\";\n\n/**\n * Render props passed to the FlowUploadZone children function.\n * Provides access to flow upload state, drag-drop handlers, and helper functions.\n *\n * @property dragDrop - Complete drag-and-drop state and handlers\n * @property flowUpload - Flow upload hook with upload state and controls\n * @property isActive - True when dragging over zone\n * @property openFilePicker - Programmatically open file selection dialog\n * @property getRootProps - Returns props to spread on the drop zone container\n * @property getInputProps - Returns props to spread on the hidden file input\n */\nexport interface FlowUploadZoneRenderProps {\n /**\n * Drag and drop state and handlers\n */\n dragDrop: UseDragDropReturn;\n\n /**\n * Flow upload functionality\n */\n flowUpload: UseFlowUploadReturn;\n\n /**\n * Whether the zone is currently active (dragging or uploading)\n */\n isActive: boolean;\n\n /**\n * Open file picker\n */\n openFilePicker: () => void;\n\n /**\n * Props to spread on the drop zone element\n */\n getRootProps: () => {\n onDragEnter: (e: React.DragEvent) => void;\n onDragOver: (e: React.DragEvent) => void;\n onDragLeave: (e: React.DragEvent) => void;\n onDrop: (e: React.DragEvent) => void;\n };\n\n /**\n * Props to spread on the file input element\n */\n getInputProps: () => React.InputHTMLAttributes<HTMLInputElement>;\n}\n\n/**\n * Props for the FlowUploadZone component.\n *\n * @property flowConfig - Flow execution configuration (flowId, storageId, etc.)\n * @property options - Flow upload options (callbacks, metadata, etc.)\n * @property accept - Accepted file types (e.g., \"image/*\", \".pdf\")\n * @property multiple - Allow multiple file selection (default: false)\n * @property children - Render function receiving flow upload zone state\n */\nexport interface FlowUploadZoneProps {\n /**\n * Flow configuration\n */\n flowConfig: FlowUploadConfig;\n\n /**\n * Upload options\n */\n options?: Omit<FlowUploadOptions, \"flowConfig\">;\n\n /**\n * Accepted file types (e.g., \"image/*\", \".pdf\", etc.)\n */\n accept?: string;\n\n /**\n * Whether to allow multiple files (uses multi-upload internally)\n */\n multiple?: boolean;\n\n /**\n * Render function for the drop zone\n */\n children: (props: FlowUploadZoneRenderProps) => ReactNode;\n}\n\n/**\n * Headless flow upload zone component with drag-and-drop support.\n * Combines drag-drop functionality with flow processing, using render props\n * for complete UI control.\n *\n * Files uploaded through this zone are automatically processed through the\n * specified flow, which can perform operations like image optimization,\n * storage saving, webhooks, etc.\n *\n * Must be used within an UploadistaProvider.\n *\n * @param props - Flow upload zone configuration and render prop\n * @returns Rendered flow upload zone using the provided render prop\n *\n * @example\n * ```tsx\n * // Image upload with flow processing\n * <FlowUploadZone\n * flowConfig={{\n * flowId: \"image-processing-flow\",\n * storageId: \"s3-images\",\n * outputNodeId: \"optimized-image\",\n * }}\n * options={{\n * onSuccess: (result) => console.log('Processed:', result),\n * onFlowComplete: (outputs) => {\n * console.log('All outputs:', outputs);\n * },\n * }}\n * accept=\"image/*\"\n * >\n * {({ dragDrop, flowUpload, getRootProps, getInputProps, openFilePicker }) => (\n * <div {...getRootProps()} style={{\n * border: dragDrop.state.isDragging ? '2px solid blue' : '2px dashed gray',\n * padding: '2rem',\n * textAlign: 'center'\n * }}>\n * <input {...getInputProps()} />\n *\n * {dragDrop.state.isDragging && (\n * <p>Drop image here...</p>\n * )}\n *\n * {!dragDrop.state.isDragging && !flowUpload.isUploading && (\n * <div>\n * <p>Drag an image or click to browse</p>\n * <button onClick={openFilePicker}>Choose File</button>\n * </div>\n * )}\n *\n * {flowUpload.isUploadingFile && (\n * <div>\n * <p>Uploading...</p>\n * <progress value={flowUpload.state.progress} max={100} />\n * <span>{flowUpload.state.progress}%</span>\n * </div>\n * )}\n *\n * {flowUpload.isProcessing && (\n * <div>\n * <p>Processing image...</p>\n * {flowUpload.state.currentNodeName && (\n * <span>Step: {flowUpload.state.currentNodeName}</span>\n * )}\n * </div>\n * )}\n *\n * {flowUpload.state.status === \"success\" && (\n * <div>\n * <p>✓ Upload complete!</p>\n * {flowUpload.state.result && (\n * <img src={flowUpload.state.result.url} alt=\"Uploaded\" />\n * )}\n * </div>\n * )}\n *\n * {flowUpload.state.status === \"error\" && (\n * <div>\n * <p>Error: {flowUpload.state.error?.message}</p>\n * <button onClick={flowUpload.reset}>Try Again</button>\n * </div>\n * )}\n *\n * {flowUpload.isUploading && (\n * <button onClick={flowUpload.abort}>Cancel</button>\n * )}\n * </div>\n * )}\n * </FlowUploadZone>\n * ```\n *\n * @see {@link SimpleFlowUploadZone} for a pre-styled version\n * @see {@link useFlowUpload} for the underlying hook\n */\nexport function FlowUploadZone({\n flowConfig,\n options,\n accept,\n multiple = false,\n children,\n}: FlowUploadZoneProps) {\n // Hook automatically subscribes to events through context\n const flowUpload = useFlowUpload({\n ...options,\n flowConfig,\n });\n\n const dragDrop = useDragDrop({\n onFilesReceived: (files: File[]) => {\n const file = files[0];\n if (file) {\n flowUpload.upload(file);\n }\n },\n accept: accept ? [accept] : undefined,\n multiple,\n });\n\n const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const files = e.target.files;\n const file = files?.[0];\n if (file) {\n flowUpload.upload(file);\n }\n };\n\n // Determine active state\n const isActive = dragDrop.state.isDragging || dragDrop.state.isOver;\n\n return (\n <>\n {children({\n flowUpload,\n dragDrop,\n isActive,\n openFilePicker: dragDrop.openFilePicker,\n getRootProps: () => dragDrop.dragHandlers,\n getInputProps: () => ({\n ...dragDrop.inputProps,\n onChange: handleFileChange,\n }),\n })}\n </>\n );\n}\n\n/**\n * Props for the SimpleFlowUploadZone component.\n *\n * @property flowConfig - Flow execution configuration\n * @property options - Flow upload options (callbacks, metadata)\n * @property accept - Accepted file types\n * @property className - CSS class name for custom styling\n * @property dragText - Text displayed when dragging files over zone\n * @property idleText - Text displayed when zone is idle\n */\nexport interface SimpleFlowUploadZoneProps {\n /**\n * Flow configuration\n */\n flowConfig: FlowUploadConfig;\n\n /**\n * Upload options\n */\n options?: Omit<FlowUploadOptions, \"flowConfig\">;\n\n /**\n * Accepted file types\n */\n accept?: string;\n\n /**\n * CSS class for the container\n */\n className?: string;\n\n /**\n * Custom drag overlay text\n */\n dragText?: string;\n\n /**\n * Custom idle text\n */\n idleText?: string;\n}\n\n/**\n * Simple pre-styled flow upload zone component with built-in UI.\n * Provides a ready-to-use drag-and-drop interface for flow uploads.\n *\n * Features:\n * - Built-in drag-and-drop visual feedback\n * - Automatic progress display for upload and processing phases\n * - Success and error state display\n * - Cancel button during upload\n * - Customizable text labels\n *\n * @param props - Flow upload zone configuration with styling options\n * @returns Styled flow upload zone component\n *\n * @example\n * ```tsx\n * // Basic image upload with flow processing\n * <SimpleFlowUploadZone\n * flowConfig={{\n * flowId: \"image-optimization-flow\",\n * storageId: \"s3-images\",\n * }}\n * accept=\"image/*\"\n * options={{\n * onSuccess: (result) => console.log(\"Image processed:\", result),\n * onError: (error) => console.error(\"Processing failed:\", error),\n * }}\n * idleText=\"Drop an image to optimize and upload\"\n * dragText=\"Release to start processing\"\n * className=\"my-upload-zone\"\n * />\n *\n * // Document upload with custom flow\n * <SimpleFlowUploadZone\n * flowConfig={{\n * flowId: \"document-processing-flow\",\n * storageId: \"docs\",\n * outputNodeId: \"processed-doc\",\n * }}\n * accept=\".pdf,.doc,.docx\"\n * options={{\n * onFlowComplete: (outputs) => {\n * console.log('Processing outputs:', outputs);\n * },\n * }}\n * />\n * ```\n *\n * @see {@link FlowUploadZone} for the headless version with full control\n */\nexport function SimpleFlowUploadZone({\n flowConfig,\n options,\n accept,\n className = \"\",\n dragText = \"Drop files here\",\n idleText = \"Drag & drop files or click to browse\",\n}: SimpleFlowUploadZoneProps) {\n return (\n <FlowUploadZone flowConfig={flowConfig} options={options} accept={accept}>\n {({\n dragDrop,\n flowUpload,\n getRootProps,\n getInputProps,\n openFilePicker,\n }) => (\n <div\n {...getRootProps()}\n className={className}\n style={{\n border: \"2px dashed #ccc\",\n borderRadius: \"8px\",\n padding: \"32px\",\n textAlign: \"center\",\n cursor: \"pointer\",\n backgroundColor: dragDrop.state.isDragging\n ? \"#f0f0f0\"\n : \"transparent\",\n transition: \"background-color 0.2s\",\n }}\n >\n <input {...getInputProps()} />\n\n {dragDrop.state.isDragging && <p style={{ margin: 0 }}>{dragText}</p>}\n\n {!dragDrop.state.isDragging &&\n !flowUpload.isUploading &&\n flowUpload.state.status === \"idle\" && (\n <div>\n <p style={{ margin: \"0 0 16px 0\" }}>{idleText}</p>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n openFilePicker();\n }}\n style={{\n padding: \"8px 16px\",\n borderRadius: \"4px\",\n border: \"1px solid #ccc\",\n backgroundColor: \"#fff\",\n cursor: \"pointer\",\n }}\n >\n Choose Files\n </button>\n </div>\n )}\n\n {flowUpload.isUploading && (\n <div>\n <progress\n value={flowUpload.state.progress}\n max={100}\n style={{ width: \"100%\", height: \"8px\" }}\n />\n <p style={{ margin: \"8px 0 0 0\" }}>\n {flowUpload.state.progress}%\n </p>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n // abort() will be passed from parent\n }}\n style={{\n marginTop: \"8px\",\n padding: \"4px 12px\",\n borderRadius: \"4px\",\n border: \"1px solid #ccc\",\n backgroundColor: \"#fff\",\n cursor: \"pointer\",\n }}\n >\n Cancel\n </button>\n </div>\n )}\n\n {flowUpload.state.status === \"success\" && (\n <div>\n <p style={{ margin: 0, color: \"green\" }}>✓ Upload complete!</p>\n </div>\n )}\n\n {flowUpload.state.status === \"error\" && (\n <div>\n <p style={{ margin: 0, color: \"red\" }}>\n ✗ Error: {flowUpload.state.error?.message}\n </p>\n </div>\n )}\n </div>\n )}\n </FlowUploadZone>\n );\n}\n","import type React from \"react\";\nimport type {\n UploadItem,\n UseMultiUploadReturn,\n} from \"../hooks/use-multi-upload\";\nimport type { UploadStatus } from \"../hooks/use-upload\";\n\n/**\n * Render props passed to the UploadList children function.\n * Provides organized access to upload items, status groupings, and actions.\n *\n * @property items - All upload items (filtered and sorted if configured)\n * @property itemsByStatus - Upload items grouped by their current status\n * @property multiUpload - Complete multi-upload hook instance\n * @property actions - Helper functions for common item operations\n * @property actions.removeItem - Remove an item from the list\n * @property actions.retryItem - Retry a failed upload\n * @property actions.abortItem - Cancel an active upload\n * @property actions.startItem - Begin uploading an idle item\n */\nexport interface UploadListRenderProps {\n /**\n * All upload items\n */\n items: UploadItem[];\n\n /**\n * Items filtered by status\n */\n itemsByStatus: {\n idle: UploadItem[];\n uploading: UploadItem[];\n success: UploadItem[];\n error: UploadItem[];\n aborted: UploadItem[];\n };\n\n /**\n * Multi-upload state and controls\n */\n multiUpload: UseMultiUploadReturn;\n\n /**\n * Helper functions for item management\n */\n actions: {\n removeItem: (id: string) => void;\n retryItem: (item: UploadItem) => void;\n abortItem: (item: UploadItem) => void;\n startItem: (item: UploadItem) => void;\n };\n}\n\n/**\n * Props for the UploadList component.\n *\n * @property multiUpload - Multi-upload hook instance to display\n * @property filter - Optional function to filter which items to show\n * @property sortBy - Optional comparator function to sort items\n * @property children - Render function receiving list state and actions\n */\nexport interface UploadListProps {\n /**\n * Multi-upload instance from useMultiUpload hook\n */\n multiUpload: UseMultiUploadReturn;\n\n /**\n * Optional filter for which items to display\n */\n filter?: (item: UploadItem) => boolean;\n\n /**\n * Optional sorting function for items\n */\n sortBy?: (a: UploadItem, b: UploadItem) => number;\n\n /**\n * Render prop that receives upload list state and actions\n */\n children: (props: UploadListRenderProps) => React.ReactNode;\n}\n\n/**\n * Headless upload list component that provides flexible rendering for upload items.\n * Uses render props pattern to give full control over how upload items are displayed.\n *\n * @param props - Upload list configuration and render prop\n * @returns Rendered upload list using the provided render prop\n *\n * @example\n * ```tsx\n * // Basic upload list with progress bars\n * <UploadList multiUpload={multiUpload}>\n * {({ items, actions }) => (\n * <div>\n * <h3>Upload Queue ({items.length} files)</h3>\n * {items.map((item) => (\n * <div key={item.id} style={{\n * padding: '1rem',\n * border: '1px solid #ccc',\n * marginBottom: '0.5rem',\n * borderRadius: '4px'\n * }}>\n * <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n * <span>{item.file.name}</span>\n * <span>{item.state.status}</span>\n * </div>\n *\n * {item.state.status === 'uploading' && (\n * <div>\n * <progress value={item.state.progress} max={100} />\n * <span>{item.state.progress}%</span>\n * <button onClick={() => actions.abortItem(item)}>Cancel</button>\n * </div>\n * )}\n *\n * {item.state.status === 'error' && (\n * <div>\n * <p style={{ color: 'red' }}>Error: {item.state.error?.message}</p>\n * <button onClick={() => actions.retryItem(item)}>Retry</button>\n * <button onClick={() => actions.removeItem(item.id)}>Remove</button>\n * </div>\n * )}\n *\n * {item.state.status === 'success' && (\n * <div>\n * <p style={{ color: 'green' }}>✓ Uploaded successfully</p>\n * <button onClick={() => actions.removeItem(item.id)}>Remove</button>\n * </div>\n * )}\n *\n * {item.state.status === 'idle' && (\n * <div>\n * <button onClick={() => actions.startItem(item)}>Start Upload</button>\n * <button onClick={() => actions.removeItem(item.id)}>Remove</button>\n * </div>\n * )}\n * </div>\n * ))}\n * </div>\n * )}\n * </UploadList>\n *\n * // Upload list with status filtering and sorting\n * <UploadList\n * multiUpload={multiUpload}\n * filter={(item) => item.state.status !== 'success'} // Hide successful uploads\n * sortBy={(a, b) => {\n * // Sort by status priority, then by filename\n * const statusPriority = { error: 0, uploading: 1, idle: 2, success: 3, aborted: 4 };\n * const aPriority = statusPriority[a.state.status];\n * const bPriority = statusPriority[b.state.status];\n *\n * if (aPriority !== bPriority) {\n * return aPriority - bPriority;\n * }\n *\n * return a.file.name.localeCompare(b.file.name);\n * }}\n * >\n * {({ items, itemsByStatus, multiUpload, actions }) => (\n * <div>\n * {itemsByStatus.error.length > 0 && (\n * <div>\n * <h4 style={{ color: 'red' }}>Failed Uploads ({itemsByStatus.error.length})</h4>\n * {itemsByStatus.error.map((item) => (\n * <UploadListItem key={item.id} item={item} actions={actions} />\n * ))}\n * </div>\n * )}\n *\n * {itemsByStatus.uploading.length > 0 && (\n * <div>\n * <h4>Uploading ({itemsByStatus.uploading.length})</h4>\n * {itemsByStatus.uploading.map((item) => (\n * <UploadListItem key={item.id} item={item} actions={actions} />\n * ))}\n * </div>\n * )}\n *\n * {itemsByStatus.idle.length > 0 && (\n * <div>\n * <h4>Pending ({itemsByStatus.idle.length})</h4>\n * {itemsByStatus.idle.map((item) => (\n * <UploadListItem key={item.id} item={item} actions={actions} />\n * ))}\n * </div>\n * )}\n * </div>\n * )}\n * </UploadList>\n * ```\n */\nexport function UploadList({\n multiUpload,\n filter,\n sortBy,\n children,\n}: UploadListProps) {\n // Apply filtering\n let items = multiUpload.items;\n if (filter) {\n items = items.filter(filter);\n }\n\n // Apply sorting\n if (sortBy) {\n items = [...items].sort(sortBy);\n }\n\n // Group items by status\n const itemsByStatus = {\n idle: items.filter((item) => item.state.status === \"idle\"),\n uploading: items.filter((item) => item.state.status === \"uploading\"),\n success: items.filter((item) => item.state.status === \"success\"),\n error: items.filter((item) => item.state.status === \"error\"),\n aborted: items.filter((item) => item.state.status === \"aborted\"),\n };\n\n // Create action helpers\n const actions = {\n removeItem: (id: string) => {\n multiUpload.removeItem(id);\n },\n retryItem: (_item: UploadItem) => {\n // Retry failed uploads using multiUpload method\n multiUpload.retryFailed();\n },\n abortItem: (item: UploadItem) => {\n // Remove the item to effectively abort it\n multiUpload.removeItem(item.id);\n },\n startItem: (_item: UploadItem) => {\n // Start all pending uploads\n multiUpload.startAll();\n },\n };\n\n // Create render props object\n const renderProps: UploadListRenderProps = {\n items,\n itemsByStatus,\n multiUpload,\n actions,\n };\n\n return <>{children(renderProps)}</>;\n}\n\n/**\n * Props for the SimpleUploadListItem component.\n *\n * @property item - The upload item to display\n * @property actions - Action functions from UploadList render props\n * @property className - Additional CSS class name\n * @property style - Inline styles for the item container\n * @property showDetails - Whether to display file size and upload details\n */\nexport interface SimpleUploadListItemProps {\n /**\n * The upload item to render\n */\n item: UploadItem;\n\n /**\n * Actions from UploadList render props\n */\n actions: UploadListRenderProps[\"actions\"];\n\n /**\n * Additional CSS class name\n */\n className?: string;\n\n /**\n * Inline styles\n */\n style?: React.CSSProperties;\n\n /**\n * Whether to show detailed information (file size, speed, etc.)\n */\n showDetails?: boolean;\n}\n\n/**\n * Pre-styled upload list item component with status indicators and action buttons.\n * Displays file info, progress, errors, and contextual actions based on upload status.\n *\n * Features:\n * - Status-specific color coding and icons\n * - Progress bar for active uploads\n * - Error message display\n * - File size formatting\n * - Contextual action buttons (start, cancel, retry, remove)\n *\n * @param props - Upload item and configuration\n * @returns Styled upload list item component\n *\n * @example\n * ```tsx\n * // Use with UploadList\n * <UploadList multiUpload={multiUpload}>\n * {({ items, actions }) => (\n * <div>\n * {items.map((item) => (\n * <SimpleUploadListItem\n * key={item.id}\n * item={item}\n * actions={actions}\n * showDetails={true}\n * />\n * ))}\n * </div>\n * )}\n * </UploadList>\n *\n * // Custom styling\n * <SimpleUploadListItem\n * item={uploadItem}\n * actions={actions}\n * className=\"my-upload-item\"\n * style={{ borderRadius: '12px', margin: '1rem' }}\n * showDetails={true}\n * />\n * ```\n */\nexport function SimpleUploadListItem({\n item,\n actions,\n className = \"\",\n style = {},\n showDetails = true,\n}: SimpleUploadListItemProps) {\n const getStatusColor = (status: UploadStatus) => {\n switch (status) {\n case \"idle\":\n return \"#6c757d\";\n case \"uploading\":\n return \"#007bff\";\n case \"success\":\n return \"#28a745\";\n case \"error\":\n return \"#dc3545\";\n case \"aborted\":\n return \"#6c757d\";\n default:\n return \"#6c757d\";\n }\n };\n\n const getStatusIcon = (status: UploadStatus) => {\n switch (status) {\n case \"idle\":\n return \"⏳\";\n case \"uploading\":\n return \"📤\";\n case \"success\":\n return \"✅\";\n case \"error\":\n return \"❌\";\n case \"aborted\":\n return \"⏹️\";\n default:\n return \"❓\";\n }\n };\n\n const formatFileSize = (bytes: number) => {\n if (bytes === 0) return \"0 Bytes\";\n const k = 1024;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;\n };\n\n return (\n <div\n className={`upload-list-item upload-list-item--${item.state.status} ${className}`}\n style={{\n padding: \"12px\",\n border: \"1px solid #e0e0e0\",\n borderRadius: \"6px\",\n marginBottom: \"8px\",\n backgroundColor: \"#fff\",\n transition: \"all 0.2s ease\",\n ...style,\n }}\n >\n {/* Header with filename and status */}\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n marginBottom: \"8px\",\n }}\n >\n <div\n style={{ display: \"flex\", alignItems: \"center\", gap: \"8px\", flex: 1 }}\n >\n <span style={{ fontSize: \"16px\" }}>\n {getStatusIcon(item.state.status)}\n </span>\n <span style={{ fontWeight: \"500\", flex: 1 }}>\n {item.file instanceof File ? item.file.name : \"File\"}\n </span>\n </div>\n <span\n style={{\n fontSize: \"12px\",\n color: getStatusColor(item.state.status),\n fontWeight: \"500\",\n textTransform: \"uppercase\",\n }}\n >\n {item.state.status}\n </span>\n </div>\n\n {/* Progress bar for uploading items */}\n {item.state.status === \"uploading\" && (\n <div style={{ marginBottom: \"8px\" }}>\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n marginBottom: \"4px\",\n }}\n >\n <span style={{ fontSize: \"12px\", color: \"#666\" }}>\n {item.state.progress}%\n </span>\n {showDetails && item.state.totalBytes && (\n <span style={{ fontSize: \"12px\", color: \"#666\" }}>\n {formatFileSize(item.state.bytesUploaded)} /{\" \"}\n {formatFileSize(item.state.totalBytes)}\n </span>\n )}\n </div>\n <div\n style={{\n width: \"100%\",\n height: \"6px\",\n backgroundColor: \"#e0e0e0\",\n borderRadius: \"3px\",\n overflow: \"hidden\",\n }}\n >\n <div\n style={{\n width: `${item.state.progress}%`,\n height: \"100%\",\n backgroundColor: \"#007bff\",\n transition: \"width 0.2s ease\",\n }}\n />\n </div>\n </div>\n )}\n\n {/* Details section */}\n {showDetails && (\n <div style={{ fontSize: \"12px\", color: \"#666\", marginBottom: \"8px\" }}>\n {item.state.totalBytes && (\n <span>{formatFileSize(item.state.totalBytes)}</span>\n )}\n {item.state.status === \"uploading\" && item.state.progress > 0 && (\n <span> • Progress: {item.state.progress}%</span>\n )}\n {item.state.status === \"error\" && item.state.error && (\n <div style={{ color: \"#dc3545\", marginTop: \"4px\" }}>\n {item.state.error.message}\n </div>\n )}\n </div>\n )}\n\n {/* Action buttons */}\n <div style={{ display: \"flex\", gap: \"8px\", flexWrap: \"wrap\" }}>\n {item.state.status === \"idle\" && (\n <>\n <button\n type=\"button\"\n onClick={() => actions.startItem(item)}\n style={{\n padding: \"4px 8px\",\n fontSize: \"12px\",\n border: \"1px solid #007bff\",\n backgroundColor: \"#007bff\",\n color: \"white\",\n borderRadius: \"4px\",\n cursor: \"pointer\",\n }}\n >\n Start\n </button>\n <button\n type=\"button\"\n onClick={() => actions.removeItem(item.id)}\n style={{\n padding: \"4px 8px\",\n fontSize: \"12px\",\n border: \"1px solid #6c757d\",\n backgroundColor: \"transparent\",\n color: \"#6c757d\",\n borderRadius: \"4px\",\n cursor: \"pointer\",\n }}\n >\n Remove\n </button>\n </>\n )}\n\n {item.state.status === \"uploading\" && (\n <button\n type=\"button\"\n onClick={() => actions.abortItem(item)}\n style={{\n padding: \"4px 8px\",\n fontSize: \"12px\",\n border: \"1px solid #dc3545\",\n backgroundColor: \"transparent\",\n color: \"#dc3545\",\n borderRadius: \"4px\",\n cursor: \"pointer\",\n }}\n >\n Cancel\n </button>\n )}\n\n {item.state.status === \"error\" && (\n <>\n <button\n type=\"button\"\n onClick={() => actions.retryItem(item)}\n style={{\n padding: \"4px 8px\",\n fontSize: \"12px\",\n border: \"1px solid #28a745\",\n backgroundColor: \"#28a745\",\n color: \"white\",\n borderRadius: \"4px\",\n cursor: \"pointer\",\n }}\n >\n Retry\n </button>\n <button\n type=\"button\"\n onClick={() => actions.removeItem(item.id)}\n style={{\n padding: \"4px 8px\",\n fontSize: \"12px\",\n border: \"1px solid #6c757d\",\n backgroundColor: \"transparent\",\n color: \"#6c757d\",\n borderRadius: \"4px\",\n cursor: \"pointer\",\n }}\n >\n Remove\n </button>\n </>\n )}\n\n {item.state.status === \"success\" && (\n <button\n type=\"button\"\n onClick={() => actions.removeItem(item.id)}\n style={{\n padding: \"4px 8px\",\n fontSize: \"12px\",\n border: \"1px solid #6c757d\",\n backgroundColor: \"transparent\",\n color: \"#6c757d\",\n borderRadius: \"4px\",\n cursor: \"pointer\",\n }}\n >\n Remove\n </button>\n )}\n\n {item.state.status === \"aborted\" && (\n <>\n <button\n type=\"button\"\n onClick={() => actions.retryItem(item)}\n style={{\n padding: \"4px 8px\",\n fontSize: \"12px\",\n border: \"1px solid #007bff\",\n backgroundColor: \"#007bff\",\n color: \"white\",\n borderRadius: \"4px\",\n cursor: \"pointer\",\n }}\n >\n Retry\n </button>\n <button\n type=\"button\"\n onClick={() => actions.removeItem(item.id)}\n style={{\n padding: \"4px 8px\",\n fontSize: \"12px\",\n border: \"1px solid #6c757d\",\n backgroundColor: \"transparent\",\n color: \"#6c757d\",\n borderRadius: \"4px\",\n cursor: \"pointer\",\n }}\n >\n Remove\n </button>\n </>\n )}\n </div>\n </div>\n );\n}\n","/**\n * Upload Zone Components\n *\n * Enhanced error handling features:\n * - MIME type validation with detailed error messages\n * - File count validation (single vs multiple mode)\n * - Custom validation error callbacks\n * - Built-in error display in SimpleUploadZone\n * - Configurable error styling\n */\n\nimport type React from \"react\";\nimport { useCallback } from \"react\";\nimport type {\n DragDropOptions,\n UseDragDropReturn,\n} from \"../hooks/use-drag-drop\";\nimport { useDragDrop } from \"../hooks/use-drag-drop\";\nimport type {\n MultiUploadOptions,\n UseMultiUploadReturn,\n} from \"../hooks/use-multi-upload\";\nimport { useMultiUpload } from \"../hooks/use-multi-upload\";\nimport type { UseUploadOptions, UseUploadReturn } from \"../hooks/use-upload\";\nimport { useUpload } from \"../hooks/use-upload\";\n\n/**\n * Render props passed to the UploadZone children function.\n * Provides access to drag-drop state, upload controls, and helper functions.\n *\n * @property dragDrop - Complete drag-and-drop state and event handlers\n * @property upload - Single upload hook (null when multiple=true)\n * @property multiUpload - Multi-upload hook (null when multiple=false)\n * @property openFilePicker - Programmatically trigger file selection dialog\n * @property isActive - True when dragging over zone or files selected\n * @property isProcessing - True when uploads are in progress\n */\nexport interface UploadZoneRenderProps {\n /**\n * Drag and drop state and handlers\n */\n dragDrop: UseDragDropReturn;\n\n /**\n * Single upload functionality (if not using multi-upload)\n */\n upload: UseUploadReturn | null;\n\n /**\n * Multi-upload functionality (if using multi-upload)\n */\n multiUpload: UseMultiUploadReturn | null;\n\n /**\n * Helper function to open file picker\n */\n openFilePicker: () => void;\n\n /**\n * Whether the zone is currently active (dragging or uploading)\n */\n isActive: boolean;\n\n /**\n * Whether files are being processed\n */\n isProcessing: boolean;\n}\n\n/**\n * Props for the UploadZone component.\n * Combines drag-drop options with upload configuration.\n *\n * @property multiple - Enable multi-file selection and upload (default: true)\n * @property multiUploadOptions - Configuration for multi-upload mode\n * @property uploadOptions - Configuration for single-upload mode\n * @property children - Render function receiving upload zone state\n * @property onUploadStart - Called when files pass validation and upload begins\n * @property onValidationError - Called when file validation fails\n * @property accept - Accepted file types (e.g., ['image/*', '.pdf'])\n * @property maxFiles - Maximum number of files allowed\n * @property maxFileSize - Maximum file size in bytes\n * @property validator - Custom validation function\n */\nexport interface UploadZoneProps\n extends Omit<DragDropOptions, \"onFilesReceived\"> {\n /**\n * Whether to enable multi-file upload mode\n */\n multiple?: boolean;\n\n /**\n * Multi-upload specific options (only used when multiple=true)\n */\n multiUploadOptions?: MultiUploadOptions;\n\n /**\n * Single upload specific options (only used when multiple=false)\n */\n uploadOptions?: UseUploadOptions;\n\n /**\n * Render prop that receives upload zone state and handlers\n */\n children: (props: UploadZoneRenderProps) => React.ReactNode;\n\n /**\n * Called when files are processed and uploads begin\n */\n onUploadStart?: (files: File[]) => void;\n\n /**\n * Called when validation errors occur\n */\n onValidationError?: (errors: string[]) => void;\n}\n\n/**\n * Headless upload zone component that combines drag and drop functionality\n * with upload management. Uses render props pattern for maximum flexibility.\n * Includes enhanced error handling for MIME type validation and file count validation.\n *\n * @param props - Upload zone configuration and render prop\n * @returns Rendered upload zone using the provided render prop\n *\n * @example\n * ```tsx\n * // Single file upload zone with error handling\n * <UploadZone\n * multiple={false}\n * accept={['image/*']}\n * maxFileSize={5 * 1024 * 1024}\n * onValidationError={(errors) => {\n * console.error('Validation errors:', errors);\n * }}\n * uploadOptions={{\n * onSuccess: (result) => console.log('Upload complete:', result),\n * onError: (error) => console.error('Upload failed:', error),\n * }}\n * >\n * {({ dragDrop, upload, openFilePicker, isActive }) => (\n * <div {...dragDrop.dragHandlers} onClick={openFilePicker}>\n * {dragDrop.state.isDragging ? (\n * <p>Drop file here...</p>\n * ) : upload?.isUploading ? (\n * <p>Uploading... {upload.state.progress}%</p>\n * ) : (\n * <p>Drag a file here or click to select</p>\n * )}\n *\n * {dragDrop.state.errors.length > 0 && (\n * <div style={{ color: 'red' }}>\n * {dragDrop.state.errors.map((error, index) => (\n * <p key={index}>{error}</p>\n * ))}\n * </div>\n * )}\n *\n * <input {...dragDrop.inputProps} />\n * </div>\n * )}\n * </UploadZone>\n * ```\n */\nexport function UploadZone({\n children,\n multiple = true,\n multiUploadOptions = {},\n uploadOptions = {},\n onUploadStart,\n onValidationError,\n ...dragDropOptions\n}: UploadZoneProps) {\n // Always initialize both hooks, but only use the appropriate one\n const singleUpload = useUpload(uploadOptions);\n const multiUpload = useMultiUpload(multiUploadOptions);\n\n // Enhanced validation function for better error handling\n const enhancedValidator = useCallback(\n (files: File[]): string[] | null => {\n const errors: string[] = [];\n\n // Check file count based on multiple setting\n if (!multiple && files.length > 1) {\n errors.push(\n `Single file mode is enabled. Please select only one file. You selected ${files.length} files.`,\n );\n }\n\n // Enhanced MIME type validation with better error messages\n if (dragDropOptions.accept && dragDropOptions.accept.length > 0) {\n const invalidFiles = files.filter((file) => {\n return !dragDropOptions.accept?.some((acceptType) => {\n if (acceptType.startsWith(\".\")) {\n // File extension check\n return file.name.toLowerCase().endsWith(acceptType.toLowerCase());\n } else {\n // MIME type check (supports wildcards like image/*)\n if (acceptType.endsWith(\"/*\")) {\n const baseType = acceptType.slice(0, -2);\n return file.type.startsWith(baseType);\n } else {\n return file.type === acceptType;\n }\n }\n });\n });\n\n if (invalidFiles.length > 0) {\n const fileNames = invalidFiles\n .map((f) => `\"${f.name}\" (${f.type})`)\n .join(\", \");\n const acceptedTypes = dragDropOptions.accept.join(\", \");\n errors.push(\n `Invalid file type(s): ${fileNames}. Accepted types: ${acceptedTypes}.`,\n );\n }\n }\n\n return errors.length > 0 ? errors : null;\n },\n [multiple, dragDropOptions.accept],\n );\n\n // Handle file processing\n const handleFilesReceived = (files: File[]) => {\n onUploadStart?.(files);\n\n if (multiple && multiUpload) {\n // Add files to multi-upload queue\n multiUpload.addFiles(files);\n\n // Auto-start uploads if configured to do so\n // Note: This could be made configurable with an autoStart prop\n setTimeout(() => multiUpload.startAll(), 0);\n } else if (!multiple && singleUpload && files.length > 0 && files[0]) {\n // Start single file upload\n singleUpload.upload(files[0]);\n }\n };\n\n // Handle validation errors\n const handleValidationError = useCallback(\n (errors: string[]) => {\n console.error(\"Upload zone validation errors:\", errors);\n // Call the custom error handler if provided\n onValidationError?.(errors);\n },\n [onValidationError],\n );\n\n // Initialize drag and drop with enhanced validation\n const dragDrop = useDragDrop({\n ...dragDropOptions,\n multiple,\n validator: enhancedValidator,\n onFilesReceived: handleFilesReceived,\n onValidationError: handleValidationError,\n });\n\n // Determine active state\n const isActive = dragDrop.state.isDragging || dragDrop.state.isOver;\n\n // Determine processing state\n const isProcessing = multiple\n ? (multiUpload?.state.isUploading ?? false)\n : (singleUpload?.isUploading ?? false);\n\n // Create render props object\n const renderProps: UploadZoneRenderProps = {\n dragDrop,\n upload: singleUpload,\n multiUpload,\n openFilePicker: dragDrop.openFilePicker,\n isActive,\n isProcessing,\n };\n\n return <>{children(renderProps)}</>;\n}\n\n/**\n * Props for the SimpleUploadZone component with built-in styling.\n *\n * @property className - CSS class name for custom styling\n * @property style - Inline styles for the upload zone container\n * @property text - Custom text labels for different states\n * @property text.idle - Text shown when zone is idle\n * @property text.dragging - Text shown when dragging files over zone\n * @property text.uploading - Text shown during upload\n * @property errorStyle - Custom styles for validation error display\n */\nexport interface SimpleUploadZoneProps extends UploadZoneProps {\n /**\n * Additional CSS class name for styling\n */\n className?: string;\n\n /**\n * Inline styles for the upload zone\n */\n style?: React.CSSProperties;\n\n /**\n * Custom text to display in different states\n */\n text?: {\n idle?: string;\n dragging?: string;\n uploading?: string;\n };\n\n /**\n * Custom error message styling\n */\n errorStyle?: React.CSSProperties;\n}\n\n/**\n * Simple pre-styled upload zone component with built-in UI and error handling.\n * Provides a ready-to-use drag-and-drop upload interface with minimal configuration.\n *\n * Features:\n * - Built-in drag-and-drop visual feedback\n * - Automatic progress display\n * - File validation error display\n * - Customizable text and styling\n * - Keyboard accessible\n *\n * @param props - Upload zone configuration with styling options\n * @returns Styled upload zone component\n *\n * @example\n * ```tsx\n * // Multi-file upload with validation\n * <SimpleUploadZone\n * multiple={true}\n * accept={['image/*', '.pdf']}\n * maxFiles={5}\n * maxFileSize={10 * 1024 * 1024} // 10MB\n * onUploadStart={(files) => console.log('Starting uploads:', files.length)}\n * onValidationError={(errors) => {\n * errors.forEach(err => console.error(err));\n * }}\n * multiUploadOptions={{\n * maxConcurrent: 3,\n * onComplete: (results) => {\n * console.log(`${results.successful.length}/${results.total} uploaded`);\n * },\n * }}\n * style={{\n * width: '400px',\n * height: '200px',\n * margin: '20px auto',\n * }}\n * text={{\n * idle: 'Drop your files here or click to browse',\n * dragging: 'Release to upload',\n * uploading: 'Uploading files...',\n * }}\n * errorStyle={{\n * backgroundColor: '#fff3cd',\n * borderColor: '#ffeaa7',\n * }}\n * />\n *\n * // Single file upload\n * <SimpleUploadZone\n * multiple={false}\n * accept={['image/*']}\n * uploadOptions={{\n * onSuccess: (result) => console.log('Uploaded:', result),\n * onError: (error) => console.error('Failed:', error),\n * }}\n * text={{\n * idle: 'Click or drag an image to upload',\n * }}\n * />\n * ```\n */\nexport function SimpleUploadZone({\n className = \"\",\n style = {},\n text = {},\n errorStyle = {},\n children,\n ...uploadZoneProps\n}: SimpleUploadZoneProps) {\n const defaultText = {\n idle: uploadZoneProps.multiple\n ? \"Drag files here or click to select\"\n : \"Drag a file here or click to select\",\n dragging: uploadZoneProps.multiple\n ? \"Drop files here...\"\n : \"Drop file here...\",\n uploading: \"Uploading...\",\n };\n\n const displayText = { ...defaultText, ...text };\n\n // If children render prop is provided, use UploadZone directly\n if (children) {\n return <UploadZone {...uploadZoneProps}>{children}</UploadZone>;\n }\n\n // Otherwise, provide default UI\n return (\n <UploadZone {...uploadZoneProps}>\n {({\n dragDrop,\n upload,\n multiUpload,\n openFilePicker,\n isActive,\n isProcessing,\n }) => (\n <button\n type=\"button\"\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n openFilePicker();\n }\n }}\n onKeyUp={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n openFilePicker();\n }\n }}\n {...dragDrop.dragHandlers}\n onClick={openFilePicker}\n className={`upload-zone ${isActive ? \"upload-zone--active\" : \"\"} ${isProcessing ? \"upload-zone--processing\" : \"\"} ${className}`}\n style={{\n border: isActive ? \"2px dashed #007bff\" : \"2px dashed #ccc\",\n borderRadius: \"8px\",\n padding: \"2rem\",\n textAlign: \"center\",\n cursor: \"pointer\",\n backgroundColor: isActive ? \"#f8f9fa\" : \"transparent\",\n transition: \"all 0.2s ease\",\n minHeight: \"120px\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n ...style,\n }}\n >\n {dragDrop.state.isDragging ? (\n <p style={{ margin: 0, fontSize: \"16px\", color: \"#007bff\" }}>\n {displayText.dragging}\n </p>\n ) : isProcessing ? (\n <div style={{ textAlign: \"center\" }}>\n <p style={{ margin: \"0 0 10px 0\", fontSize: \"14px\" }}>\n {displayText.uploading}\n </p>\n {upload && (\n <div>\n <progress\n value={upload.state.progress}\n max={100}\n style={{ width: \"200px\", height: \"8px\" }}\n />\n <p\n style={{\n margin: \"5px 0 0 0\",\n fontSize: \"12px\",\n color: \"#666\",\n }}\n >\n {upload.state.progress}%\n </p>\n </div>\n )}\n {multiUpload && (\n <div>\n <progress\n value={multiUpload.state.progress}\n max={100}\n style={{ width: \"200px\", height: \"8px\" }}\n />\n <p\n style={{\n margin: \"5px 0 0 0\",\n fontSize: \"12px\",\n color: \"#666\",\n }}\n >\n {multiUpload.state.progress}% ({multiUpload.state.uploading}{\" \"}\n uploading, {multiUpload.state.successful} completed)\n </p>\n </div>\n )}\n </div>\n ) : (\n <p style={{ margin: 0, fontSize: \"16px\", color: \"#666\" }}>\n {displayText.idle}\n </p>\n )}\n\n {dragDrop.state.errors.length > 0 && (\n <div\n style={{\n marginTop: \"10px\",\n padding: \"8px 12px\",\n backgroundColor: \"#f8d7da\",\n border: \"1px solid #f5c6cb\",\n borderRadius: \"4px\",\n maxWidth: \"100%\",\n ...errorStyle,\n }}\n >\n <p\n style={{\n margin: \"0 0 5px 0\",\n fontSize: \"12px\",\n fontWeight: \"bold\",\n color: \"#721c24\",\n }}\n >\n Validation Errors:\n </p>\n {dragDrop.state.errors.map((error, index) => (\n <p\n // biome-ignore lint/suspicious/noArrayIndexKey: index is used as key\n key={index}\n style={{\n color: \"#721c24\",\n fontSize: \"11px\",\n margin: \"2px 0\",\n lineHeight: \"1.3\",\n }}\n >\n • {error}\n </p>\n ))}\n </div>\n )}\n\n <input {...dragDrop.inputProps} />\n </button>\n )}\n </UploadZone>\n );\n}\n"],"mappings":"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"}
|