@uploadista/react 0.0.17-beta.4 → 0.0.17-beta.6
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/hooks/index.d.mts +3 -3
- package/dist/hooks/index.mjs +1 -1
- package/dist/index.d.mts +6 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/upload-zone-BPIAbcsx.mjs +6 -0
- package/dist/upload-zone-BPIAbcsx.mjs.map +1 -0
- package/dist/{uploadista-provider-DIMEpwsu.d.mts → uploadista-provider-bZlGf-uJ.d.mts} +62 -15
- package/dist/uploadista-provider-bZlGf-uJ.d.mts.map +1 -0
- package/dist/use-upload-BpnD7FA-.mjs +2 -0
- package/dist/use-upload-BpnD7FA-.mjs.map +1 -0
- package/dist/use-upload-metrics-BoGtFTrl.mjs +2 -0
- package/dist/use-upload-metrics-BoGtFTrl.mjs.map +1 -0
- package/dist/{use-upload-metrics-R1xNz6Aa.d.mts → use-upload-metrics-DhzS4lhG.d.mts} +207 -5
- package/dist/use-upload-metrics-DhzS4lhG.d.mts.map +1 -0
- package/dist/{use-uploadista-client-ty1ffKD7.d.mts → use-uploadista-client-CfpEI3Us.d.mts} +228 -3
- package/dist/use-uploadista-client-CfpEI3Us.d.mts.map +1 -0
- package/package.json +5 -5
- package/src/components/flow-input.tsx +298 -0
- package/src/components/index.tsx +3 -0
- package/src/contexts/flow-manager-context.tsx +1 -0
- package/src/hooks/index.ts +20 -6
- package/src/hooks/use-flow-execution.ts +505 -0
- package/src/hooks/use-flow-upload.ts +23 -0
- package/src/hooks/use-flow.ts +439 -0
- package/src/index.ts +15 -0
- package/dist/upload-zone-DN7Gem65.mjs +0 -2
- package/dist/upload-zone-DN7Gem65.mjs.map +0 -1
- package/dist/uploadista-provider-DIMEpwsu.d.mts.map +0 -1
- package/dist/use-upload-C7QZSt1M.mjs +0 -2
- package/dist/use-upload-C7QZSt1M.mjs.map +0 -1
- package/dist/use-upload-metrics-DEaujDeM.mjs +0 -2
- package/dist/use-upload-metrics-DEaujDeM.mjs.map +0 -1
- package/dist/use-upload-metrics-R1xNz6Aa.d.mts.map +0 -1
- package/dist/use-uploadista-client-ty1ffKD7.d.mts.map +0 -1
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import type { FlowUploadOptions } from "@uploadista/client-browser";
|
|
2
|
+
import type {
|
|
3
|
+
FlowManager,
|
|
4
|
+
FlowUploadState,
|
|
5
|
+
FlowUploadStatus,
|
|
6
|
+
InputExecutionState,
|
|
7
|
+
} from "@uploadista/client-core";
|
|
8
|
+
import type { Flow, TypedOutput } from "@uploadista/core/flow";
|
|
9
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
10
|
+
import { useUploadistaContext } from "../components/uploadista-provider";
|
|
11
|
+
import { useFlowManagerContext } from "../contexts/flow-manager-context";
|
|
12
|
+
|
|
13
|
+
// Re-export types from core for convenience
|
|
14
|
+
export type { FlowUploadState, FlowUploadStatus, InputExecutionState };
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Input metadata discovered from the flow
|
|
18
|
+
*/
|
|
19
|
+
export interface FlowInputMetadata {
|
|
20
|
+
/** Input node ID */
|
|
21
|
+
nodeId: string;
|
|
22
|
+
/** Human-readable node name */
|
|
23
|
+
nodeName: string;
|
|
24
|
+
/** Node description explaining what input is needed */
|
|
25
|
+
nodeDescription: string;
|
|
26
|
+
/** Input node type */
|
|
27
|
+
nodeType: string;
|
|
28
|
+
/** Whether this input is required */
|
|
29
|
+
required: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Return value from the useFlow hook with upload control methods and state.
|
|
34
|
+
*
|
|
35
|
+
* @property state - Complete flow upload state with progress and outputs
|
|
36
|
+
* @property inputMetadata - Metadata about discovered input nodes (null until discovered)
|
|
37
|
+
* @property inputStates - Per-input execution state for multi-input flows
|
|
38
|
+
* @property inputs - Current input values set via setInput()
|
|
39
|
+
* @property setInput - Set an input value for a specific node (for progressive provision)
|
|
40
|
+
* @property execute - Execute the flow with current inputs (auto-detects types)
|
|
41
|
+
* @property upload - Convenience method for single-file upload (same as execute with one file input)
|
|
42
|
+
* @property abort - Cancel the current upload and flow execution
|
|
43
|
+
* @property pause - Pause the current upload
|
|
44
|
+
* @property reset - Reset state to idle (clears all data)
|
|
45
|
+
* @property isUploading - True when upload or processing is active
|
|
46
|
+
* @property isUploadingFile - True only during file upload phase
|
|
47
|
+
* @property isProcessing - True only during flow processing phase
|
|
48
|
+
* @property isDiscoveringInputs - True while discovering flow inputs
|
|
49
|
+
*/
|
|
50
|
+
export interface UseFlowReturn {
|
|
51
|
+
/**
|
|
52
|
+
* Current upload state
|
|
53
|
+
*/
|
|
54
|
+
state: FlowUploadState;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Discovered input nodes metadata (null until discovery completes)
|
|
58
|
+
*/
|
|
59
|
+
inputMetadata: FlowInputMetadata[] | null;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Per-input execution state for multi-input flows
|
|
63
|
+
*/
|
|
64
|
+
inputStates: ReadonlyMap<string, InputExecutionState>;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Current inputs set via setInput()
|
|
68
|
+
*/
|
|
69
|
+
inputs: Record<string, unknown>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Set an input value for a specific node.
|
|
73
|
+
* For progressive input provision before calling execute().
|
|
74
|
+
*
|
|
75
|
+
* @param nodeId - The input node ID
|
|
76
|
+
* @param value - The input value (File, URL string, or structured data)
|
|
77
|
+
*/
|
|
78
|
+
setInput: (nodeId: string, value: unknown) => void;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Execute the flow with current inputs.
|
|
82
|
+
* Automatically detects input types and routes appropriately.
|
|
83
|
+
* For single input, uses standard upload path.
|
|
84
|
+
* For multiple inputs, requires multiInputUploadFn.
|
|
85
|
+
*/
|
|
86
|
+
execute: () => Promise<void>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Upload a single file through the flow (convenience method).
|
|
90
|
+
* Equivalent to setInput(firstNodeId, file) + execute().
|
|
91
|
+
*
|
|
92
|
+
* @param file - File or Blob to upload
|
|
93
|
+
*/
|
|
94
|
+
upload: (file: File | Blob) => Promise<void>;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Abort the current upload
|
|
98
|
+
*/
|
|
99
|
+
abort: () => void;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Pause the current upload
|
|
103
|
+
*/
|
|
104
|
+
pause: () => void;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Reset the upload state and clear all inputs
|
|
108
|
+
*/
|
|
109
|
+
reset: () => void;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Whether an upload or flow execution is in progress (uploading OR processing)
|
|
113
|
+
*/
|
|
114
|
+
isUploading: boolean;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Whether the file is currently being uploaded (chunks being sent)
|
|
118
|
+
*/
|
|
119
|
+
isUploadingFile: boolean;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Whether the flow is currently processing (after upload completes)
|
|
123
|
+
*/
|
|
124
|
+
isProcessing: boolean;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Whether the hook is discovering flow inputs
|
|
128
|
+
*/
|
|
129
|
+
isDiscoveringInputs: boolean;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const initialState: FlowUploadState = {
|
|
133
|
+
status: "idle",
|
|
134
|
+
progress: 0,
|
|
135
|
+
bytesUploaded: 0,
|
|
136
|
+
totalBytes: null,
|
|
137
|
+
error: null,
|
|
138
|
+
jobId: null,
|
|
139
|
+
flowStarted: false,
|
|
140
|
+
currentNodeName: null,
|
|
141
|
+
currentNodeType: null,
|
|
142
|
+
flowOutputs: null,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* React hook for executing flows with single or multiple inputs.
|
|
147
|
+
* Automatically discovers input nodes and detects input types (File, URL, structured data).
|
|
148
|
+
* Supports progressive input provision via setInput() and execute().
|
|
149
|
+
*
|
|
150
|
+
* This is the unified flow hook that replaces useFlowUpload for advanced use cases.
|
|
151
|
+
* It provides:
|
|
152
|
+
* - Auto-discovery of flow input nodes
|
|
153
|
+
* - Automatic input type detection (file → upload, string → URL, object → data)
|
|
154
|
+
* - Progressive input provision via setInput()
|
|
155
|
+
* - Multi-input support with parallel coordination
|
|
156
|
+
* - Per-input state tracking
|
|
157
|
+
*
|
|
158
|
+
* Must be used within FlowManagerProvider (which must be within UploadistaProvider).
|
|
159
|
+
* Flow events are automatically routed by the provider to the appropriate manager.
|
|
160
|
+
*
|
|
161
|
+
* @param options - Flow upload configuration including flow ID and event handlers
|
|
162
|
+
* @returns Flow upload state and control methods
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```tsx
|
|
166
|
+
* // Single file upload (simple case)
|
|
167
|
+
* function SingleFileUploader() {
|
|
168
|
+
* const flow = useFlow({
|
|
169
|
+
* flowConfig: {
|
|
170
|
+
* flowId: "image-optimization",
|
|
171
|
+
* storageId: "s3-images",
|
|
172
|
+
* },
|
|
173
|
+
* onSuccess: (outputs) => {
|
|
174
|
+
* console.log("Flow outputs:", outputs);
|
|
175
|
+
* },
|
|
176
|
+
* });
|
|
177
|
+
*
|
|
178
|
+
* return (
|
|
179
|
+
* <div>
|
|
180
|
+
* <input
|
|
181
|
+
* type="file"
|
|
182
|
+
* onChange={(e) => {
|
|
183
|
+
* const file = e.target.files?.[0];
|
|
184
|
+
* if (file) flow.upload(file);
|
|
185
|
+
* }}
|
|
186
|
+
* />
|
|
187
|
+
* {flow.isUploading && <div>Progress: {flow.state.progress}%</div>}
|
|
188
|
+
* </div>
|
|
189
|
+
* );
|
|
190
|
+
* }
|
|
191
|
+
*
|
|
192
|
+
* // Multi-input with progressive provision
|
|
193
|
+
* function MultiInputFlow() {
|
|
194
|
+
* const flow = useFlow({
|
|
195
|
+
* flowConfig: {
|
|
196
|
+
* flowId: "multi-source-processing",
|
|
197
|
+
* storageId: "default",
|
|
198
|
+
* },
|
|
199
|
+
* });
|
|
200
|
+
*
|
|
201
|
+
* return (
|
|
202
|
+
* <div>
|
|
203
|
+
* {flow.inputMetadata?.map((input) => (
|
|
204
|
+
* <div key={input.nodeId}>
|
|
205
|
+
* <label>{input.nodeId}</label>
|
|
206
|
+
* {input.nodeType === "streaming-input-v1" ? (
|
|
207
|
+
* <input
|
|
208
|
+
* type="file"
|
|
209
|
+
* onChange={(e) => {
|
|
210
|
+
* const file = e.target.files?.[0];
|
|
211
|
+
* if (file) flow.setInput(input.nodeId, file);
|
|
212
|
+
* }}
|
|
213
|
+
* />
|
|
214
|
+
* ) : (
|
|
215
|
+
* <input
|
|
216
|
+
* type="url"
|
|
217
|
+
* onChange={(e) => flow.setInput(input.nodeId, e.target.value)}
|
|
218
|
+
* />
|
|
219
|
+
* )}
|
|
220
|
+
* </div>
|
|
221
|
+
* ))}
|
|
222
|
+
* <button onClick={flow.execute} disabled={flow.isUploading}>
|
|
223
|
+
* Execute Flow
|
|
224
|
+
* </button>
|
|
225
|
+
*
|
|
226
|
+
* {flow.isUploading && (
|
|
227
|
+
* <div>
|
|
228
|
+
* {Array.from(flow.inputStates.values()).map((inputState) => (
|
|
229
|
+
* <div key={inputState.nodeId}>
|
|
230
|
+
* {inputState.nodeId}: {inputState.status} ({inputState.progress}%)
|
|
231
|
+
* </div>
|
|
232
|
+
* ))}
|
|
233
|
+
* </div>
|
|
234
|
+
* )}
|
|
235
|
+
* </div>
|
|
236
|
+
* );
|
|
237
|
+
* }
|
|
238
|
+
* ```
|
|
239
|
+
*
|
|
240
|
+
* @see {@link useFlowUpload} for a simpler file-only upload hook
|
|
241
|
+
*/
|
|
242
|
+
export function useFlow(options: FlowUploadOptions): UseFlowReturn {
|
|
243
|
+
const { client } = useUploadistaContext();
|
|
244
|
+
const { getManager, releaseManager } = useFlowManagerContext();
|
|
245
|
+
const [state, setState] = useState<FlowUploadState>(initialState);
|
|
246
|
+
const [inputMetadata, setInputMetadata] = useState<
|
|
247
|
+
FlowInputMetadata[] | null
|
|
248
|
+
>(null);
|
|
249
|
+
const [isDiscoveringInputs, setIsDiscoveringInputs] = useState(false);
|
|
250
|
+
const [inputs, setInputs] = useState<Record<string, unknown>>({});
|
|
251
|
+
const [inputStates, setInputStates] = useState<
|
|
252
|
+
ReadonlyMap<string, InputExecutionState>
|
|
253
|
+
>(new Map());
|
|
254
|
+
const managerRef = useRef<FlowManager<unknown> | null>(null);
|
|
255
|
+
|
|
256
|
+
// Store callbacks in refs so they can be updated without recreating the manager
|
|
257
|
+
const callbacksRef = useRef(options);
|
|
258
|
+
|
|
259
|
+
// Update refs on every render to capture latest callbacks
|
|
260
|
+
useEffect(() => {
|
|
261
|
+
callbacksRef.current = options;
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Auto-discover flow inputs on mount
|
|
265
|
+
useEffect(() => {
|
|
266
|
+
const discoverInputs = async () => {
|
|
267
|
+
setIsDiscoveringInputs(true);
|
|
268
|
+
try {
|
|
269
|
+
const { flow } = await client.getFlow(options.flowConfig.flowId);
|
|
270
|
+
|
|
271
|
+
// Find all input nodes
|
|
272
|
+
const inputNodes = flow.nodes.filter((node) => node.type === "input");
|
|
273
|
+
|
|
274
|
+
const metadata: FlowInputMetadata[] = inputNodes.map((node) => ({
|
|
275
|
+
nodeId: node.id,
|
|
276
|
+
nodeName: node.name,
|
|
277
|
+
nodeDescription: node.description,
|
|
278
|
+
nodeType: node.nodeType,
|
|
279
|
+
// TODO: Add required field to node schema to determine if input is required
|
|
280
|
+
required: true,
|
|
281
|
+
}));
|
|
282
|
+
|
|
283
|
+
setInputMetadata(metadata);
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error("Failed to discover flow inputs:", error);
|
|
286
|
+
} finally {
|
|
287
|
+
setIsDiscoveringInputs(false);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
discoverInputs();
|
|
292
|
+
}, [client, options.flowConfig.flowId]);
|
|
293
|
+
|
|
294
|
+
// Get or create manager from context when component mounts
|
|
295
|
+
useEffect(() => {
|
|
296
|
+
const flowId = options.flowConfig.flowId;
|
|
297
|
+
|
|
298
|
+
// Create stable callback wrappers that call the latest callbacks via refs
|
|
299
|
+
const stableCallbacks = {
|
|
300
|
+
onStateChange: (newState: FlowUploadState) => {
|
|
301
|
+
setState(newState);
|
|
302
|
+
},
|
|
303
|
+
onProgress: (
|
|
304
|
+
uploadId: string,
|
|
305
|
+
bytesUploaded: number,
|
|
306
|
+
totalBytes: number | null,
|
|
307
|
+
) => {
|
|
308
|
+
callbacksRef.current.onProgress?.(uploadId, bytesUploaded, totalBytes);
|
|
309
|
+
},
|
|
310
|
+
onChunkComplete: (
|
|
311
|
+
chunkSize: number,
|
|
312
|
+
bytesAccepted: number,
|
|
313
|
+
bytesTotal: number | null,
|
|
314
|
+
) => {
|
|
315
|
+
callbacksRef.current.onChunkComplete?.(
|
|
316
|
+
chunkSize,
|
|
317
|
+
bytesAccepted,
|
|
318
|
+
bytesTotal,
|
|
319
|
+
);
|
|
320
|
+
},
|
|
321
|
+
onFlowComplete: (outputs: TypedOutput[]) => {
|
|
322
|
+
callbacksRef.current.onFlowComplete?.(outputs);
|
|
323
|
+
},
|
|
324
|
+
onSuccess: (outputs: TypedOutput[]) => {
|
|
325
|
+
callbacksRef.current.onSuccess?.(outputs);
|
|
326
|
+
},
|
|
327
|
+
onError: (error: Error) => {
|
|
328
|
+
callbacksRef.current.onError?.(error);
|
|
329
|
+
},
|
|
330
|
+
onAbort: () => {
|
|
331
|
+
callbacksRef.current.onAbort?.();
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// Get manager from context (creates if doesn't exist, increments ref count)
|
|
336
|
+
managerRef.current = getManager(flowId, stableCallbacks, options);
|
|
337
|
+
|
|
338
|
+
// Set up interval to poll input states for multi-input flows
|
|
339
|
+
const pollInterval = setInterval(() => {
|
|
340
|
+
if (managerRef.current) {
|
|
341
|
+
const states = managerRef.current.getInputStates();
|
|
342
|
+
if (states.size > 0) {
|
|
343
|
+
setInputStates(new Map(states));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}, 100); // Poll every 100ms
|
|
347
|
+
|
|
348
|
+
// Release manager when component unmounts or flowId changes
|
|
349
|
+
return () => {
|
|
350
|
+
clearInterval(pollInterval);
|
|
351
|
+
releaseManager(flowId);
|
|
352
|
+
managerRef.current = null;
|
|
353
|
+
};
|
|
354
|
+
}, [
|
|
355
|
+
options.flowConfig.flowId,
|
|
356
|
+
options.flowConfig.storageId,
|
|
357
|
+
options.flowConfig.outputNodeId,
|
|
358
|
+
getManager,
|
|
359
|
+
releaseManager,
|
|
360
|
+
]);
|
|
361
|
+
|
|
362
|
+
// Set an input value
|
|
363
|
+
const setInput = useCallback((nodeId: string, value: unknown) => {
|
|
364
|
+
setInputs((prev) => ({ ...prev, [nodeId]: value }));
|
|
365
|
+
}, []);
|
|
366
|
+
|
|
367
|
+
// Execute flow with current inputs
|
|
368
|
+
const execute = useCallback(async () => {
|
|
369
|
+
if (!managerRef.current) {
|
|
370
|
+
throw new Error("FlowManager not initialized");
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (Object.keys(inputs).length === 0) {
|
|
374
|
+
throw new Error(
|
|
375
|
+
"No inputs provided. Use setInput() to provide inputs before calling execute()",
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
await managerRef.current.executeFlow(inputs);
|
|
380
|
+
}, [inputs]);
|
|
381
|
+
|
|
382
|
+
// Convenience method for single file upload
|
|
383
|
+
const upload = useCallback(
|
|
384
|
+
async (file: File | Blob) => {
|
|
385
|
+
if (!managerRef.current) {
|
|
386
|
+
throw new Error("FlowManager not initialized");
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// If we have input metadata, use the first input node
|
|
390
|
+
// Otherwise, let the manager discover it
|
|
391
|
+
if (inputMetadata && inputMetadata.length > 0) {
|
|
392
|
+
const firstInputNode = inputMetadata[0];
|
|
393
|
+
setInputs({ [firstInputNode.nodeId]: file });
|
|
394
|
+
await managerRef.current.executeFlow({ [firstInputNode.nodeId]: file });
|
|
395
|
+
} else {
|
|
396
|
+
// Fall back to direct upload (manager will handle discovery)
|
|
397
|
+
await managerRef.current.upload(file);
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
[inputMetadata],
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
const abort = useCallback(() => {
|
|
404
|
+
managerRef.current?.abort();
|
|
405
|
+
}, []);
|
|
406
|
+
|
|
407
|
+
const pause = useCallback(() => {
|
|
408
|
+
managerRef.current?.pause();
|
|
409
|
+
}, []);
|
|
410
|
+
|
|
411
|
+
const reset = useCallback(() => {
|
|
412
|
+
managerRef.current?.reset();
|
|
413
|
+
setInputs({});
|
|
414
|
+
setInputStates(new Map());
|
|
415
|
+
}, []);
|
|
416
|
+
|
|
417
|
+
// Derive computed values from state (reactive to state changes)
|
|
418
|
+
const isUploading =
|
|
419
|
+
state.status === "uploading" || state.status === "processing";
|
|
420
|
+
const isUploadingFile = state.status === "uploading";
|
|
421
|
+
const isProcessing = state.status === "processing";
|
|
422
|
+
|
|
423
|
+
return {
|
|
424
|
+
state,
|
|
425
|
+
inputMetadata,
|
|
426
|
+
inputStates,
|
|
427
|
+
inputs,
|
|
428
|
+
setInput,
|
|
429
|
+
execute,
|
|
430
|
+
upload,
|
|
431
|
+
abort,
|
|
432
|
+
pause,
|
|
433
|
+
reset,
|
|
434
|
+
isUploading,
|
|
435
|
+
isUploadingFile,
|
|
436
|
+
isProcessing,
|
|
437
|
+
isDiscoveringInputs,
|
|
438
|
+
};
|
|
439
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,8 @@ export type {
|
|
|
15
15
|
export { useUploadEvents } from "./hooks/use-upload-events";
|
|
16
16
|
|
|
17
17
|
// Flow Upload Hooks
|
|
18
|
+
export type { FlowInputProps } from "./components/flow-input";
|
|
19
|
+
export { FlowInput } from "./components/flow-input";
|
|
18
20
|
|
|
19
21
|
export type {
|
|
20
22
|
FlowUploadListProps,
|
|
@@ -47,6 +49,19 @@ export type { UseMultiFlowUploadReturn } from "./hooks/use-multi-flow-upload";
|
|
|
47
49
|
export { useMultiFlowUpload } from "./hooks/use-multi-flow-upload";
|
|
48
50
|
|
|
49
51
|
// Flow Hooks
|
|
52
|
+
export type {
|
|
53
|
+
FlowInputMetadata,
|
|
54
|
+
InputExecutionState,
|
|
55
|
+
UseFlowReturn,
|
|
56
|
+
} from "./hooks/use-flow";
|
|
57
|
+
export { useFlow } from "./hooks/use-flow";
|
|
58
|
+
|
|
59
|
+
export type {
|
|
60
|
+
InputBuilder,
|
|
61
|
+
UseFlowExecutionOptions,
|
|
62
|
+
UseFlowExecutionReturn,
|
|
63
|
+
} from "./hooks/use-flow-execution";
|
|
64
|
+
export { useFlowExecution } from "./hooks/use-flow-execution";
|
|
50
65
|
|
|
51
66
|
// Upload Hooks
|
|
52
67
|
export type {
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{a as e,i as t,n,r,t as i}from"./use-upload-C7QZSt1M.mjs";import{useCallback as a}from"react";import{Fragment as o,jsx as s,jsxs as c}from"react/jsx-runtime";function l({flowConfig:t,options:n,children:r}){let i=e({...n,flowConfig:t});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 u({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 d({flowConfig:e,options:t,className:n=``,showFileInput:r=!0,accept:i}){return s(l,{flowConfig:e,options:t,children:({items:e,addFiles:t,startUpload:a,abortUpload:o,retryUpload:l,removeFile:d,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(u,{item:e,onAbort:()=>o(e.id),onRetry:()=>l(e.id),onRemove:()=>d(e.id)},e.id))})]})]})})}function f({flowConfig:e,options:n,accept:i,multiple:a=!1,children:c}){let l=r({...n,flowConfig:e}),u=t({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 p({flowConfig:e,options:t,accept:n,className:r=``,dragText:i=`Drop files here`,idleText:a=`Drag & drop files or click to browse`}){return s(f,{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 m({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 h({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 g({children:e,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=t({...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:e({dragDrop:v,upload:p,multiUpload:m,openFilePicker:v.openFilePicker,isActive:y,isProcessing:b})})}function _({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(g,{...a,children:i}):s(g,{...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{f as a,d as c,m as i,u as l,g as n,p as o,h as r,l as s,_ as t};
|
|
2
|
-
//# sourceMappingURL=upload-zone-DN7Gem65.mjs.map
|