@uploadista/vue 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 +240 -0
- package/LICENSE +21 -0
- package/README.md +554 -0
- package/package.json +36 -0
- package/src/components/FlowUploadList.vue +342 -0
- package/src/components/FlowUploadZone.vue +305 -0
- package/src/components/UploadList.vue +303 -0
- package/src/components/UploadZone.vue +254 -0
- package/src/components/index.ts +11 -0
- package/src/composables/index.ts +44 -0
- package/src/composables/plugin.ts +76 -0
- package/src/composables/useDragDrop.ts +343 -0
- package/src/composables/useFlowUpload.ts +431 -0
- package/src/composables/useMultiFlowUpload.ts +322 -0
- package/src/composables/useMultiUpload.ts +546 -0
- package/src/composables/useUpload.ts +300 -0
- package/src/composables/useUploadMetrics.ts +502 -0
- package/src/composables/useUploadistaClient.ts +73 -0
- package/src/index.ts +28 -0
- package/src/providers/UploadistaProvider.vue +69 -0
- package/src/providers/index.ts +1 -0
- package/src/utils/index.ts +156 -0
- package/src/utils/is-browser-file.ts +2 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
UploadistaEvent,
|
|
3
|
+
UploadOptions,
|
|
4
|
+
} from "@uploadista/client-browser";
|
|
5
|
+
import type { UploadFile } from "@uploadista/core/types";
|
|
6
|
+
import { UploadEventType } from "@uploadista/core/types";
|
|
7
|
+
import { computed, onUnmounted, readonly, ref } from "vue";
|
|
8
|
+
import { useUploadistaClient } from "./useUploadistaClient";
|
|
9
|
+
|
|
10
|
+
// Re-export types for convenience
|
|
11
|
+
export type UploadInput = File | Blob;
|
|
12
|
+
export type ChunkMetrics = any;
|
|
13
|
+
export type PerformanceInsights = any;
|
|
14
|
+
export type UploadSessionMetrics = any;
|
|
15
|
+
|
|
16
|
+
export type UploadStatus =
|
|
17
|
+
| "idle"
|
|
18
|
+
| "uploading"
|
|
19
|
+
| "success"
|
|
20
|
+
| "error"
|
|
21
|
+
| "aborted";
|
|
22
|
+
|
|
23
|
+
export interface UploadState {
|
|
24
|
+
status: UploadStatus;
|
|
25
|
+
progress: number;
|
|
26
|
+
bytesUploaded: number;
|
|
27
|
+
totalBytes: number | null;
|
|
28
|
+
error: Error | null;
|
|
29
|
+
result: UploadFile | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface UploadMetrics {
|
|
33
|
+
/**
|
|
34
|
+
* Get performance insights from the upload client
|
|
35
|
+
*/
|
|
36
|
+
getInsights: () => PerformanceInsights;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Export detailed metrics from the upload client
|
|
40
|
+
*/
|
|
41
|
+
exportMetrics: () => {
|
|
42
|
+
session: Partial<UploadSessionMetrics>;
|
|
43
|
+
chunks: ChunkMetrics[];
|
|
44
|
+
insights: PerformanceInsights;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get current network metrics
|
|
49
|
+
*/
|
|
50
|
+
getNetworkMetrics: () => unknown;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get current network condition
|
|
54
|
+
*/
|
|
55
|
+
getNetworkCondition: () => unknown;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Reset all metrics
|
|
59
|
+
*/
|
|
60
|
+
resetMetrics: () => void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const initialState: UploadState = {
|
|
64
|
+
status: "idle",
|
|
65
|
+
progress: 0,
|
|
66
|
+
bytesUploaded: 0,
|
|
67
|
+
totalBytes: null,
|
|
68
|
+
error: null,
|
|
69
|
+
result: null,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Vue composable for managing individual file uploads with full state management.
|
|
74
|
+
* Provides upload progress tracking, error handling, abort functionality, and retry logic.
|
|
75
|
+
*
|
|
76
|
+
* Must be used within a component tree that has the Uploadista plugin installed.
|
|
77
|
+
*
|
|
78
|
+
* @param options - Upload configuration and event handlers
|
|
79
|
+
* @returns Upload state and control methods
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```vue
|
|
83
|
+
* <script setup lang="ts">
|
|
84
|
+
* import { useUpload } from '@uploadista/vue';
|
|
85
|
+
*
|
|
86
|
+
* const upload = useUpload({
|
|
87
|
+
* onSuccess: (result) => console.log('Upload complete:', result),
|
|
88
|
+
* onError: (error) => console.error('Upload failed:', error),
|
|
89
|
+
* onProgress: (progress) => console.log('Progress:', progress + '%'),
|
|
90
|
+
* });
|
|
91
|
+
*
|
|
92
|
+
* const handleFileChange = (event: Event) => {
|
|
93
|
+
* const file = (event.target as HTMLInputElement).files?.[0];
|
|
94
|
+
* if (file) upload.upload(file);
|
|
95
|
+
* };
|
|
96
|
+
* </script>
|
|
97
|
+
*
|
|
98
|
+
* <template>
|
|
99
|
+
* <div>
|
|
100
|
+
* <input type="file" @change="handleFileChange" />
|
|
101
|
+
* <div v-if="upload.isUploading">Progress: {{ upload.state.progress }}%</div>
|
|
102
|
+
* <div v-if="upload.state.error">Error: {{ upload.state.error.message }}</div>
|
|
103
|
+
* <button v-if="upload.canRetry" @click="upload.retry">Retry</button>
|
|
104
|
+
* <button @click="upload.abort" :disabled="!upload.isUploading">Abort</button>
|
|
105
|
+
* </div>
|
|
106
|
+
* </template>
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export function useUpload(options: UploadOptions = {}) {
|
|
110
|
+
const uploadistaClient = useUploadistaClient();
|
|
111
|
+
const state = ref<UploadState>({ ...initialState });
|
|
112
|
+
const abortController = ref<{ abort: () => void } | null>(null);
|
|
113
|
+
const lastFile = ref<UploadInput | null>(null);
|
|
114
|
+
|
|
115
|
+
const updateState = (update: Partial<UploadState>) => {
|
|
116
|
+
state.value = { ...state.value, ...update };
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const reset = () => {
|
|
120
|
+
if (abortController.value) {
|
|
121
|
+
abortController.value.abort();
|
|
122
|
+
abortController.value = null;
|
|
123
|
+
}
|
|
124
|
+
state.value = { ...initialState };
|
|
125
|
+
lastFile.value = null;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const abort = () => {
|
|
129
|
+
if (abortController.value) {
|
|
130
|
+
abortController.value.abort();
|
|
131
|
+
abortController.value = null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
updateState({
|
|
135
|
+
status: "aborted",
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
options.onAbort?.();
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const upload = (file: UploadInput) => {
|
|
142
|
+
// Reset any previous state but keep the file reference for retries
|
|
143
|
+
state.value = {
|
|
144
|
+
...initialState,
|
|
145
|
+
status: "uploading",
|
|
146
|
+
totalBytes: file instanceof File ? file.size : null,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
lastFile.value = file;
|
|
150
|
+
|
|
151
|
+
// Start the upload and handle the promise
|
|
152
|
+
const uploadPromise = uploadistaClient.client.upload(file, {
|
|
153
|
+
metadata: options.metadata,
|
|
154
|
+
uploadLengthDeferred: options.uploadLengthDeferred,
|
|
155
|
+
uploadSize: options.uploadSize,
|
|
156
|
+
|
|
157
|
+
onProgress: (
|
|
158
|
+
_uploadId: string,
|
|
159
|
+
bytesUploaded: number,
|
|
160
|
+
totalBytes: number | null,
|
|
161
|
+
) => {
|
|
162
|
+
const progress = totalBytes
|
|
163
|
+
? Math.round((bytesUploaded / totalBytes) * 100)
|
|
164
|
+
: 0;
|
|
165
|
+
|
|
166
|
+
updateState({
|
|
167
|
+
progress,
|
|
168
|
+
bytesUploaded,
|
|
169
|
+
totalBytes,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
options.onProgress?.(progress, bytesUploaded, totalBytes);
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
onChunkComplete: (
|
|
176
|
+
chunkSize: number,
|
|
177
|
+
bytesAccepted: number,
|
|
178
|
+
bytesTotal: number | null,
|
|
179
|
+
) => {
|
|
180
|
+
options.onChunkComplete?.(chunkSize, bytesAccepted, bytesTotal);
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
onSuccess: (result: UploadFile) => {
|
|
184
|
+
updateState({
|
|
185
|
+
status: "success",
|
|
186
|
+
result,
|
|
187
|
+
progress: 100,
|
|
188
|
+
bytesUploaded: result.size || 0,
|
|
189
|
+
totalBytes: result.size || null,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
options.onSuccess?.(result);
|
|
193
|
+
abortController.value = null;
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
onError: (error: Error) => {
|
|
197
|
+
updateState({
|
|
198
|
+
status: "error",
|
|
199
|
+
error,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
options.onError?.(error);
|
|
203
|
+
abortController.value = null;
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
onShouldRetry: options.onShouldRetry,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Handle the promise to get the abort controller
|
|
210
|
+
uploadPromise
|
|
211
|
+
.then((controller) => {
|
|
212
|
+
abortController.value = controller;
|
|
213
|
+
})
|
|
214
|
+
.catch((error) => {
|
|
215
|
+
updateState({
|
|
216
|
+
status: "error",
|
|
217
|
+
error: error as Error,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
options.onError?.(error as Error);
|
|
221
|
+
abortController.value = null;
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const retry = () => {
|
|
226
|
+
if (
|
|
227
|
+
lastFile.value &&
|
|
228
|
+
(state.value.status === "error" || state.value.status === "aborted")
|
|
229
|
+
) {
|
|
230
|
+
upload(lastFile.value);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// Subscribe to events from context (WebSocket events)
|
|
235
|
+
const unsubscribe = uploadistaClient.subscribeToEvents(
|
|
236
|
+
(event: UploadistaEvent) => {
|
|
237
|
+
console.log("useUpload - subscribeToEvents", event);
|
|
238
|
+
// Handle upload progress events
|
|
239
|
+
const uploadEvent = event as {
|
|
240
|
+
type: string;
|
|
241
|
+
data?: { id: string; progress: number; total: number };
|
|
242
|
+
};
|
|
243
|
+
if (
|
|
244
|
+
uploadEvent.type === UploadEventType.UPLOAD_PROGRESS &&
|
|
245
|
+
uploadEvent.data
|
|
246
|
+
) {
|
|
247
|
+
const { progress: bytesUploaded, total: totalBytes } = uploadEvent.data;
|
|
248
|
+
|
|
249
|
+
// Update state for this upload
|
|
250
|
+
// Note: We update for all uploads since we don't track upload IDs in single upload mode
|
|
251
|
+
const progress = totalBytes
|
|
252
|
+
? Math.round((bytesUploaded / totalBytes) * 100)
|
|
253
|
+
: 0;
|
|
254
|
+
|
|
255
|
+
// Only update if we're currently uploading
|
|
256
|
+
if (state.value.status === "uploading") {
|
|
257
|
+
updateState({
|
|
258
|
+
progress,
|
|
259
|
+
bytesUploaded,
|
|
260
|
+
totalBytes,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
options.onProgress?.(progress, bytesUploaded, totalBytes);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// Cleanup on unmount
|
|
270
|
+
onUnmounted(() => {
|
|
271
|
+
unsubscribe();
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const isUploading = computed(() => state.value.status === "uploading");
|
|
275
|
+
const canRetry = computed(
|
|
276
|
+
() =>
|
|
277
|
+
(state.value.status === "error" || state.value.status === "aborted") &&
|
|
278
|
+
lastFile.value !== null,
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// Create metrics object that delegates to the upload client
|
|
282
|
+
const metrics: UploadMetrics = {
|
|
283
|
+
getInsights: () => uploadistaClient.client.getChunkingInsights(),
|
|
284
|
+
exportMetrics: () => uploadistaClient.client.exportMetrics(),
|
|
285
|
+
getNetworkMetrics: () => uploadistaClient.client.getNetworkMetrics(),
|
|
286
|
+
getNetworkCondition: () => uploadistaClient.client.getNetworkCondition(),
|
|
287
|
+
resetMetrics: () => uploadistaClient.client.resetMetrics(),
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
state: readonly(state),
|
|
292
|
+
upload,
|
|
293
|
+
abort,
|
|
294
|
+
reset,
|
|
295
|
+
retry,
|
|
296
|
+
isUploading,
|
|
297
|
+
canRetry,
|
|
298
|
+
metrics,
|
|
299
|
+
};
|
|
300
|
+
}
|