@uploadista/react 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-check.log +89 -0
- package/FLOW_UPLOAD.md +307 -0
- package/LICENSE +21 -0
- package/README.md +318 -0
- package/package.json +35 -0
- package/src/components/flow-upload-list.tsx +614 -0
- package/src/components/flow-upload-zone.tsx +441 -0
- package/src/components/upload-list.tsx +626 -0
- package/src/components/upload-zone.tsx +545 -0
- package/src/components/uploadista-provider.tsx +190 -0
- package/src/hooks/use-drag-drop.ts +404 -0
- package/src/hooks/use-flow-upload.ts +568 -0
- package/src/hooks/use-multi-flow-upload.ts +477 -0
- package/src/hooks/use-multi-upload.ts +691 -0
- package/src/hooks/use-upload-metrics.ts +585 -0
- package/src/hooks/use-upload.ts +411 -0
- package/src/hooks/use-uploadista-client.ts +145 -0
- package/src/index.ts +87 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
|
|
2
|
+
> @uploadista/react@ check /Users/denislaboureyras/Documents/uploadista/dev/uploadista/packages/uploadista/clients/react
|
|
3
|
+
> biome check --write ./src
|
|
4
|
+
|
|
5
|
+
src/hooks/use-upload-metrics.ts:5:21 lint/suspicious/noExplicitAny ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
6
|
+
|
|
7
|
+
! Unexpected any. Specify a different type.
|
|
8
|
+
|
|
9
|
+
4 │ // Types
|
|
10
|
+
> 5 │ type ChunkMetrics = any;
|
|
11
|
+
│ ^^^
|
|
12
|
+
6 │ type PerformanceInsights = any;
|
|
13
|
+
7 │ type UploadSessionMetrics = any;
|
|
14
|
+
|
|
15
|
+
i any disables many type checking rules. Its use should be avoided.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
src/hooks/use-upload-metrics.ts:6:28 lint/suspicious/noExplicitAny ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
19
|
+
|
|
20
|
+
! Unexpected any. Specify a different type.
|
|
21
|
+
|
|
22
|
+
4 │ // Types
|
|
23
|
+
5 │ type ChunkMetrics = any;
|
|
24
|
+
> 6 │ type PerformanceInsights = any;
|
|
25
|
+
│ ^^^
|
|
26
|
+
7 │ type UploadSessionMetrics = any;
|
|
27
|
+
8 │ type Timeout = ReturnType<typeof setTimeout>;
|
|
28
|
+
|
|
29
|
+
i any disables many type checking rules. Its use should be avoided.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
src/hooks/use-upload-metrics.ts:7:29 lint/suspicious/noExplicitAny ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
33
|
+
|
|
34
|
+
! Unexpected any. Specify a different type.
|
|
35
|
+
|
|
36
|
+
5 │ type ChunkMetrics = any;
|
|
37
|
+
6 │ type PerformanceInsights = any;
|
|
38
|
+
> 7 │ type UploadSessionMetrics = any;
|
|
39
|
+
│ ^^^
|
|
40
|
+
8 │ type Timeout = ReturnType<typeof setTimeout>;
|
|
41
|
+
9 │
|
|
42
|
+
|
|
43
|
+
i any disables many type checking rules. Its use should be avoided.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
src/hooks/use-upload.ts:9:28 lint/suspicious/noExplicitAny ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
47
|
+
|
|
48
|
+
! Unexpected any. Specify a different type.
|
|
49
|
+
|
|
50
|
+
7 │ // Re-export types for convenience
|
|
51
|
+
8 │ export type UploadInput = File | Blob;
|
|
52
|
+
> 9 │ export type ChunkMetrics = any;
|
|
53
|
+
│ ^^^
|
|
54
|
+
10 │ export type PerformanceInsights = any;
|
|
55
|
+
11 │ export type UploadSessionMetrics = any;
|
|
56
|
+
|
|
57
|
+
i any disables many type checking rules. Its use should be avoided.
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
src/hooks/use-upload.ts:10:35 lint/suspicious/noExplicitAny ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
61
|
+
|
|
62
|
+
! Unexpected any. Specify a different type.
|
|
63
|
+
|
|
64
|
+
8 │ export type UploadInput = File | Blob;
|
|
65
|
+
9 │ export type ChunkMetrics = any;
|
|
66
|
+
> 10 │ export type PerformanceInsights = any;
|
|
67
|
+
│ ^^^
|
|
68
|
+
11 │ export type UploadSessionMetrics = any;
|
|
69
|
+
12 │
|
|
70
|
+
|
|
71
|
+
i any disables many type checking rules. Its use should be avoided.
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
src/hooks/use-upload.ts:11:36 lint/suspicious/noExplicitAny ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
75
|
+
|
|
76
|
+
! Unexpected any. Specify a different type.
|
|
77
|
+
|
|
78
|
+
9 │ export type ChunkMetrics = any;
|
|
79
|
+
10 │ export type PerformanceInsights = any;
|
|
80
|
+
> 11 │ export type UploadSessionMetrics = any;
|
|
81
|
+
│ ^^^
|
|
82
|
+
12 │
|
|
83
|
+
13 │ export type UploadStatus =
|
|
84
|
+
|
|
85
|
+
i any disables many type checking rules. Its use should be avoided.
|
|
86
|
+
Checked 15 files in 38ms. Fixed 2 files.
|
|
87
|
+
Found 6 warnings.
|
|
88
|
+
|
|
89
|
+
|
package/FLOW_UPLOAD.md
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# Flow Upload - React Hooks & Components
|
|
2
|
+
|
|
3
|
+
The Flow Upload feature allows you to upload files through Uploadista flows, enabling post-processing like image optimization, video transcoding, and automatic storage.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Flow uploads combine the robustness of direct upload with the flexibility of flow-based processing:
|
|
8
|
+
|
|
9
|
+
1. **Init**: Client starts flow → streaming-input-node creates upload → flow pauses
|
|
10
|
+
2. **Upload**: Client uploads chunks **directly** to upload API (efficient, full-featured)
|
|
11
|
+
3. **Finalize**: Client notifies flow → flow resumes → processes file through remaining nodes
|
|
12
|
+
|
|
13
|
+
## Hooks
|
|
14
|
+
|
|
15
|
+
### `useFlowUpload`
|
|
16
|
+
|
|
17
|
+
Basic hook for uploading a single file through a flow.
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { useUploadClient, useFlowUpload } from "@uploadista/react";
|
|
21
|
+
|
|
22
|
+
function MyUploadComponent() {
|
|
23
|
+
const client = useUploadClient({
|
|
24
|
+
baseUrl: "http://localhost:4200",
|
|
25
|
+
storageId: "my-storage",
|
|
26
|
+
chunkSize: 5 * 1024 * 1024,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const flowUpload = useFlowUpload(client, {
|
|
30
|
+
flowConfig: {
|
|
31
|
+
flowId: "image-optimization-flow",
|
|
32
|
+
storageId: "my-storage",
|
|
33
|
+
},
|
|
34
|
+
onSuccess: (result) => {
|
|
35
|
+
console.log("Upload complete:", result);
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div>
|
|
41
|
+
<input
|
|
42
|
+
type="file"
|
|
43
|
+
onChange={(e) => {
|
|
44
|
+
const file = e.target.files?.[0];
|
|
45
|
+
if (file) flowUpload.upload(file);
|
|
46
|
+
}}
|
|
47
|
+
/>
|
|
48
|
+
{flowUpload.isUploading && (
|
|
49
|
+
<progress value={flowUpload.state.progress} max={100} />
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### `useMultiFlowUpload`
|
|
57
|
+
|
|
58
|
+
Hook for uploading multiple files through a flow with concurrent upload control.
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { useUploadClient, useMultiFlowUpload } from "@uploadista/react";
|
|
62
|
+
|
|
63
|
+
function BatchUploadComponent() {
|
|
64
|
+
const client = useUploadClient({
|
|
65
|
+
baseUrl: "http://localhost:4200",
|
|
66
|
+
storageId: "my-storage",
|
|
67
|
+
chunkSize: 5 * 1024 * 1024,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const multiUpload = useMultiFlowUpload(client, {
|
|
71
|
+
flowConfig: {
|
|
72
|
+
flowId: "batch-image-optimization",
|
|
73
|
+
storageId: "my-storage",
|
|
74
|
+
},
|
|
75
|
+
maxConcurrent: 3,
|
|
76
|
+
onComplete: (items) => {
|
|
77
|
+
console.log("All uploads complete:", items);
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div>
|
|
83
|
+
<input
|
|
84
|
+
type="file"
|
|
85
|
+
multiple
|
|
86
|
+
onChange={(e) => {
|
|
87
|
+
if (e.target.files) {
|
|
88
|
+
multiUpload.addFiles(e.target.files);
|
|
89
|
+
multiUpload.startUpload();
|
|
90
|
+
}
|
|
91
|
+
}}
|
|
92
|
+
/>
|
|
93
|
+
{multiUpload.state.items.map((item) => (
|
|
94
|
+
<div key={item.id}>
|
|
95
|
+
{item.file.name} - {item.progress}%
|
|
96
|
+
</div>
|
|
97
|
+
))}
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Components
|
|
104
|
+
|
|
105
|
+
### `FlowUploadZone`
|
|
106
|
+
|
|
107
|
+
Render-props component for creating custom drag-and-drop upload zones.
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { FlowUploadZone } from "@uploadista/react";
|
|
111
|
+
|
|
112
|
+
function CustomUploadZone() {
|
|
113
|
+
return (
|
|
114
|
+
<FlowUploadZone
|
|
115
|
+
client={client}
|
|
116
|
+
flowConfig={{
|
|
117
|
+
flowId: "my-flow",
|
|
118
|
+
storageId: "my-storage",
|
|
119
|
+
}}
|
|
120
|
+
accept="image/*"
|
|
121
|
+
>
|
|
122
|
+
{({ isDragging, isUploading, state, getRootProps, getInputProps }) => (
|
|
123
|
+
<div {...getRootProps()}>
|
|
124
|
+
<input {...getInputProps()} />
|
|
125
|
+
{isDragging && <p>Drop files here...</p>}
|
|
126
|
+
{isUploading && <progress value={state.progress} max={100} />}
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
129
|
+
</FlowUploadZone>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### `SimpleFlowUploadZone`
|
|
135
|
+
|
|
136
|
+
Pre-styled upload zone component for quick implementation.
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
import { SimpleFlowUploadZone } from "@uploadista/react";
|
|
140
|
+
|
|
141
|
+
function QuickUploadZone() {
|
|
142
|
+
return (
|
|
143
|
+
<SimpleFlowUploadZone
|
|
144
|
+
client={client}
|
|
145
|
+
flowConfig={{
|
|
146
|
+
flowId: "my-flow",
|
|
147
|
+
storageId: "my-storage",
|
|
148
|
+
}}
|
|
149
|
+
accept="image/*"
|
|
150
|
+
dragText="Drop your images here"
|
|
151
|
+
idleText="Drag & drop images or click to browse"
|
|
152
|
+
options={{
|
|
153
|
+
onSuccess: (result) => console.log("Uploaded:", result),
|
|
154
|
+
}}
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### `FlowUploadList`
|
|
161
|
+
|
|
162
|
+
Render-props component for managing multiple file uploads.
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
import { FlowUploadList } from "@uploadista/react";
|
|
166
|
+
|
|
167
|
+
function CustomUploadList() {
|
|
168
|
+
return (
|
|
169
|
+
<FlowUploadList
|
|
170
|
+
client={client}
|
|
171
|
+
flowConfig={{
|
|
172
|
+
flowId: "batch-flow",
|
|
173
|
+
storageId: "my-storage",
|
|
174
|
+
}}
|
|
175
|
+
options={{ maxConcurrent: 3 }}
|
|
176
|
+
>
|
|
177
|
+
{({ items, addFiles, startUpload, abortUpload, retryUpload }) => (
|
|
178
|
+
<div>
|
|
179
|
+
<input
|
|
180
|
+
type="file"
|
|
181
|
+
multiple
|
|
182
|
+
onChange={(e) => {
|
|
183
|
+
if (e.target.files) {
|
|
184
|
+
addFiles(e.target.files);
|
|
185
|
+
startUpload();
|
|
186
|
+
}
|
|
187
|
+
}}
|
|
188
|
+
/>
|
|
189
|
+
{items.map((item) => (
|
|
190
|
+
<div key={item.id}>
|
|
191
|
+
{item.file.name}
|
|
192
|
+
<progress value={item.progress} max={100} />
|
|
193
|
+
{item.status === "error" && (
|
|
194
|
+
<button onClick={() => retryUpload(item.id)}>Retry</button>
|
|
195
|
+
)}
|
|
196
|
+
</div>
|
|
197
|
+
))}
|
|
198
|
+
</div>
|
|
199
|
+
)}
|
|
200
|
+
</FlowUploadList>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### `SimpleFlowUploadList`
|
|
206
|
+
|
|
207
|
+
Pre-styled upload list component with built-in file input.
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
import { SimpleFlowUploadList } from "@uploadista/react";
|
|
211
|
+
|
|
212
|
+
function QuickUploadList() {
|
|
213
|
+
return (
|
|
214
|
+
<SimpleFlowUploadList
|
|
215
|
+
client={client}
|
|
216
|
+
flowConfig={{
|
|
217
|
+
flowId: "batch-flow",
|
|
218
|
+
storageId: "my-storage",
|
|
219
|
+
}}
|
|
220
|
+
options={{
|
|
221
|
+
maxConcurrent: 3,
|
|
222
|
+
onComplete: (items) => alert(`${items.length} files uploaded!`),
|
|
223
|
+
}}
|
|
224
|
+
accept="image/*"
|
|
225
|
+
/>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Server-Side Flow Setup
|
|
231
|
+
|
|
232
|
+
Your server needs to define flows with a streaming-input-node:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// Server-side flow definition
|
|
236
|
+
import { createFlow } from "@uploadista/core/flow";
|
|
237
|
+
import { createInputNode } from "@uploadista/core/flow/nodes/input-node";
|
|
238
|
+
import { createImageOptimizeNode } from "@uploadista/core/flow/image-nodes/image-optimize-node";
|
|
239
|
+
import { createOutputNode } from "@uploadista/core/flow/output-nodes/output-node";
|
|
240
|
+
|
|
241
|
+
const imageOptimizationFlow = createFlow({
|
|
242
|
+
flowId: "image-optimization-flow",
|
|
243
|
+
name: "Image Upload & Optimization",
|
|
244
|
+
nodes: [
|
|
245
|
+
createInputNode("upload-node"),
|
|
246
|
+
createImageOptimizeNode("optimize-node", {
|
|
247
|
+
quality: 80,
|
|
248
|
+
format: "webp",
|
|
249
|
+
}),
|
|
250
|
+
createOutputNode("save-node", {
|
|
251
|
+
storageId: "my-storage",
|
|
252
|
+
}),
|
|
253
|
+
],
|
|
254
|
+
edges: [
|
|
255
|
+
{ source: "upload-node", target: "optimize-node" },
|
|
256
|
+
{ source: "optimize-node", target: "save-node" },
|
|
257
|
+
],
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Benefits
|
|
262
|
+
|
|
263
|
+
✅ **Efficient**: Chunks upload directly via upload API (not wrapped in flow messages)
|
|
264
|
+
✅ **Full-featured**: Supports all upload features (smart chunking, retry, metrics, parallel)
|
|
265
|
+
✅ **Flow-integrated**: Flow orchestrates and can post-process files
|
|
266
|
+
✅ **Clean separation**: Upload logic in upload layer, flow handles orchestration
|
|
267
|
+
✅ **React-friendly**: Hooks and components follow React best practices
|
|
268
|
+
|
|
269
|
+
## Migration from Regular Upload
|
|
270
|
+
|
|
271
|
+
If you're currently using `useUpload` and want to add flow-based processing:
|
|
272
|
+
|
|
273
|
+
**Before (direct upload):**
|
|
274
|
+
```tsx
|
|
275
|
+
const upload = useUpload(client, {
|
|
276
|
+
onSuccess: (result) => {
|
|
277
|
+
// Manual post-processing needed
|
|
278
|
+
fetch('/api/optimize', { body: result.id });
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**After (flow upload):**
|
|
284
|
+
```tsx
|
|
285
|
+
const flowUpload = useFlowUpload(client, {
|
|
286
|
+
flowConfig: {
|
|
287
|
+
flowId: "auto-optimize-flow", // Flow handles optimization automatically
|
|
288
|
+
storageId: "my-storage",
|
|
289
|
+
},
|
|
290
|
+
onSuccess: (result) => {
|
|
291
|
+
// File is already optimized by the flow!
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## TypeScript Support
|
|
297
|
+
|
|
298
|
+
All hooks and components are fully typed:
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
import type {
|
|
302
|
+
FlowUploadState,
|
|
303
|
+
FlowUploadItem,
|
|
304
|
+
UseFlowUploadOptions,
|
|
305
|
+
FlowUploadZoneRenderProps,
|
|
306
|
+
} from "@uploadista/react";
|
|
307
|
+
```
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 uploadista
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|