@uploadista/vue 0.0.20-beta.9 → 0.1.0-beta.5
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/{components-CskPs6sR.css → components-B_L33hsM.css} +33 -33
- package/dist/{components-CskPs6sR.css.map → components-B_L33hsM.css.map} +1 -1
- package/dist/components-MZ9ETx9c.mjs +2 -0
- package/dist/components-MZ9ETx9c.mjs.map +1 -0
- package/dist/composables/index.d.mts +1 -1
- package/dist/composables/index.mjs +1 -1
- package/dist/composables-Dny_9Zrg.mjs +2 -0
- package/dist/composables-Dny_9Zrg.mjs.map +1 -0
- package/dist/index-6Scxoy1b.d.mts +1289 -0
- package/dist/index-6Scxoy1b.d.mts.map +1 -0
- package/dist/{index-B2fUTjNP.d.mts → index-BpCRFLJ5.d.mts} +4 -4
- package/dist/index-BpCRFLJ5.d.mts.map +1 -0
- package/dist/{index-DiRR_Ua6.d.mts → index-RY4FPqAk.d.mts} +431 -432
- package/dist/index-RY4FPqAk.d.mts.map +1 -0
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +1 -1
- package/dist/providers/index.d.mts +1 -1
- package/dist/providers/index.mjs +1 -1
- package/dist/{providers-fqmOwF71.mjs → providers-CjhEBaQV.mjs} +2 -2
- package/dist/providers-CjhEBaQV.mjs.map +1 -0
- package/dist/useUploadistaClient-WVuo8jYH.mjs.map +1 -1
- package/dist/utils/index.d.mts +62 -2
- package/dist/utils/index.d.mts.map +1 -0
- package/package.json +11 -9
- package/src/__tests__/setup.ts +154 -0
- package/src/components/FlowUploadList.vue +25 -24
- package/src/components/UploadList.vue +3 -6
- package/src/components/UploadZone.vue +2 -5
- package/src/components/flow/Flow.vue +16 -4
- package/src/components/flow/FlowDropZone.vue +4 -2
- package/src/components/flow/FlowInput.vue +14 -8
- package/src/components/flow/FlowInputDropZone.vue +4 -2
- package/src/components/flow/FlowInputPreview.vue +3 -1
- package/src/components/flow/FlowProgress.vue +1 -1
- package/src/components/flow/FlowStatus.vue +1 -1
- package/src/components/flow/useFlowContext.ts +7 -5
- package/src/components/index.ts +4 -2
- package/src/components/upload/Upload.vue +146 -0
- package/src/components/upload/UploadCancel.vue +22 -0
- package/src/components/upload/UploadClearCompleted.vue +24 -0
- package/src/components/upload/UploadDropZone.vue +96 -0
- package/src/components/upload/UploadError.vue +42 -0
- package/src/components/upload/UploadItem.vue +54 -0
- package/src/components/upload/UploadItems.vue +33 -0
- package/src/components/upload/UploadProgress.vue +35 -0
- package/src/components/upload/UploadReset.vue +20 -0
- package/src/components/upload/UploadRetry.vue +22 -0
- package/src/components/upload/UploadStartAll.vue +30 -0
- package/src/components/upload/UploadStatus.vue +65 -0
- package/src/components/upload/index.ts +98 -0
- package/src/components/upload/useUploadContext.ts +67 -0
- package/src/composables/eventUtils.test.ts +267 -0
- package/src/composables/eventUtils.ts +5 -4
- package/src/composables/index.ts +1 -1
- package/src/composables/useDragDrop.test.ts +304 -0
- package/src/composables/useFlow.ts +6 -2
- package/src/composables/useFlowManagerContext.ts +5 -1
- package/src/composables/useUploadEvents.ts +1 -4
- package/src/composables/useUploadistaClient.test.ts +152 -0
- package/src/index.ts +65 -4
- package/src/providers/FlowManagerProvider.vue +5 -2
- package/src/utils/index.test.ts +396 -0
- package/src/utils/is-browser-file.test.ts +45 -0
- package/vitest.config.ts +25 -0
- package/dist/components-BxBz_7tS.mjs +0 -2
- package/dist/components-BxBz_7tS.mjs.map +0 -1
- package/dist/composables-BZ2c_WgI.mjs +0 -2
- package/dist/composables-BZ2c_WgI.mjs.map +0 -1
- package/dist/index-B2fUTjNP.d.mts.map +0 -1
- package/dist/index-BLNNvTVx.d.mts +0 -62
- package/dist/index-BLNNvTVx.d.mts.map +0 -1
- package/dist/index-D3PNaPGh.d.mts +0 -787
- package/dist/index-D3PNaPGh.d.mts.map +0 -1
- package/dist/index-DiRR_Ua6.d.mts.map +0 -1
- package/dist/providers-fqmOwF71.mjs.map +0 -1
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
createMockDragEvent,
|
|
4
|
+
createMockFile,
|
|
5
|
+
createMockInputChangeEvent,
|
|
6
|
+
} from "../__tests__/setup";
|
|
7
|
+
import { useDragDrop } from "./useDragDrop";
|
|
8
|
+
|
|
9
|
+
describe("useDragDrop", () => {
|
|
10
|
+
describe("initial state", () => {
|
|
11
|
+
it("should have correct initial state", () => {
|
|
12
|
+
const { state } = useDragDrop();
|
|
13
|
+
|
|
14
|
+
expect(state.value.isDragging).toBe(false);
|
|
15
|
+
expect(state.value.isOver).toBe(false);
|
|
16
|
+
expect(state.value.isValid).toBe(true);
|
|
17
|
+
expect(state.value.errors).toEqual([]);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("drag events", () => {
|
|
22
|
+
it("should set isDragging and isOver on dragenter", () => {
|
|
23
|
+
const onDragStateChange = vi.fn();
|
|
24
|
+
const { state, onDragEnter } = useDragDrop({ onDragStateChange });
|
|
25
|
+
|
|
26
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
27
|
+
|
|
28
|
+
expect(state.value.isDragging).toBe(true);
|
|
29
|
+
expect(state.value.isOver).toBe(true);
|
|
30
|
+
expect(onDragStateChange).toHaveBeenCalledWith(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should not trigger multiple times for nested elements", () => {
|
|
34
|
+
const onDragStateChange = vi.fn();
|
|
35
|
+
const { state, onDragEnter } = useDragDrop({ onDragStateChange });
|
|
36
|
+
|
|
37
|
+
// Multiple dragenter events (simulating nested elements)
|
|
38
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
39
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
40
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
41
|
+
|
|
42
|
+
expect(state.value.isDragging).toBe(true);
|
|
43
|
+
// onDragStateChange should only be called once
|
|
44
|
+
expect(onDragStateChange).toHaveBeenCalledTimes(1);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should reset state on dragleave when counter reaches 0", () => {
|
|
48
|
+
const onDragStateChange = vi.fn();
|
|
49
|
+
const { state, onDragEnter, onDragLeave } = useDragDrop({
|
|
50
|
+
onDragStateChange,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Enter twice (nested elements)
|
|
54
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
55
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
56
|
+
|
|
57
|
+
// Leave once - should still be dragging
|
|
58
|
+
onDragLeave(createMockDragEvent("dragleave"));
|
|
59
|
+
expect(state.value.isDragging).toBe(true);
|
|
60
|
+
|
|
61
|
+
// Leave again - now should be reset
|
|
62
|
+
onDragLeave(createMockDragEvent("dragleave"));
|
|
63
|
+
expect(state.value.isDragging).toBe(false);
|
|
64
|
+
expect(state.value.isOver).toBe(false);
|
|
65
|
+
expect(onDragStateChange).toHaveBeenLastCalledWith(false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should set dropEffect on dragover", () => {
|
|
69
|
+
const { onDragOver } = useDragDrop();
|
|
70
|
+
const event = createMockDragEvent("dragover");
|
|
71
|
+
|
|
72
|
+
onDragOver(event);
|
|
73
|
+
|
|
74
|
+
expect(event.dataTransfer?.dropEffect).toBe("copy");
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("drop handling", () => {
|
|
79
|
+
it("should process files on drop", () => {
|
|
80
|
+
const onFilesReceived = vi.fn();
|
|
81
|
+
const { state, onDragEnter, onDrop } = useDragDrop({ onFilesReceived });
|
|
82
|
+
|
|
83
|
+
const file = createMockFile("test.txt", 100, "text/plain");
|
|
84
|
+
|
|
85
|
+
onDragEnter(createMockDragEvent("dragenter"));
|
|
86
|
+
onDrop(createMockDragEvent("drop", [file]));
|
|
87
|
+
|
|
88
|
+
expect(state.value.isDragging).toBe(false);
|
|
89
|
+
expect(state.value.isOver).toBe(false);
|
|
90
|
+
expect(onFilesReceived).toHaveBeenCalledWith([file]);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should not call onFilesReceived when no files dropped", () => {
|
|
94
|
+
const onFilesReceived = vi.fn();
|
|
95
|
+
const { onDrop } = useDragDrop({ onFilesReceived });
|
|
96
|
+
|
|
97
|
+
onDrop(createMockDragEvent("drop", []));
|
|
98
|
+
|
|
99
|
+
expect(onFilesReceived).not.toHaveBeenCalled();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("file validation", () => {
|
|
104
|
+
it("should validate file type with extension", () => {
|
|
105
|
+
const onFilesReceived = vi.fn();
|
|
106
|
+
const onValidationError = vi.fn();
|
|
107
|
+
const { state, onDrop } = useDragDrop({
|
|
108
|
+
accept: [".txt"],
|
|
109
|
+
onFilesReceived,
|
|
110
|
+
onValidationError,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const validFile = createMockFile("test.txt", 100, "text/plain");
|
|
114
|
+
onDrop(createMockDragEvent("drop", [validFile]));
|
|
115
|
+
|
|
116
|
+
expect(onFilesReceived).toHaveBeenCalledWith([validFile]);
|
|
117
|
+
expect(onValidationError).not.toHaveBeenCalled();
|
|
118
|
+
expect(state.value.isValid).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("should reject invalid file types", () => {
|
|
122
|
+
const onFilesReceived = vi.fn();
|
|
123
|
+
const onValidationError = vi.fn();
|
|
124
|
+
const { state, onDrop } = useDragDrop({
|
|
125
|
+
accept: [".txt"],
|
|
126
|
+
onFilesReceived,
|
|
127
|
+
onValidationError,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const invalidFile = createMockFile("test.pdf", 100, "application/pdf");
|
|
131
|
+
onDrop(createMockDragEvent("drop", [invalidFile]));
|
|
132
|
+
|
|
133
|
+
expect(onFilesReceived).not.toHaveBeenCalled();
|
|
134
|
+
expect(onValidationError).toHaveBeenCalled();
|
|
135
|
+
expect(state.value.isValid).toBe(false);
|
|
136
|
+
expect(state.value.errors.length).toBeGreaterThan(0);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("should validate file type with MIME wildcard", () => {
|
|
140
|
+
const onFilesReceived = vi.fn();
|
|
141
|
+
const { onDrop } = useDragDrop({
|
|
142
|
+
accept: ["image/*"],
|
|
143
|
+
onFilesReceived,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const jpgFile = createMockFile("photo.jpg", 100, "image/jpeg");
|
|
147
|
+
const pngFile = createMockFile("photo.png", 100, "image/png");
|
|
148
|
+
|
|
149
|
+
onDrop(createMockDragEvent("drop", [jpgFile]));
|
|
150
|
+
expect(onFilesReceived).toHaveBeenCalledWith([jpgFile]);
|
|
151
|
+
|
|
152
|
+
onDrop(createMockDragEvent("drop", [pngFile]));
|
|
153
|
+
expect(onFilesReceived).toHaveBeenCalledWith([pngFile]);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should validate max file count", () => {
|
|
157
|
+
const onValidationError = vi.fn();
|
|
158
|
+
const { state, onDrop } = useDragDrop({
|
|
159
|
+
maxFiles: 2,
|
|
160
|
+
onValidationError,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const files = [
|
|
164
|
+
createMockFile("file1.txt", 100, "text/plain"),
|
|
165
|
+
createMockFile("file2.txt", 100, "text/plain"),
|
|
166
|
+
createMockFile("file3.txt", 100, "text/plain"),
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
onDrop(createMockDragEvent("drop", files));
|
|
170
|
+
|
|
171
|
+
expect(onValidationError).toHaveBeenCalled();
|
|
172
|
+
expect(state.value.errors.some((e) => e.includes("Maximum 2 files"))).toBe(
|
|
173
|
+
true,
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("should validate max file size", () => {
|
|
178
|
+
const onValidationError = vi.fn();
|
|
179
|
+
const { state, onDrop } = useDragDrop({
|
|
180
|
+
maxFileSize: 1024, // 1KB
|
|
181
|
+
onValidationError,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const largeFile = createMockFile("large.txt", 2048, "text/plain"); // 2KB
|
|
185
|
+
|
|
186
|
+
onDrop(createMockDragEvent("drop", [largeFile]));
|
|
187
|
+
|
|
188
|
+
expect(onValidationError).toHaveBeenCalled();
|
|
189
|
+
expect(
|
|
190
|
+
state.value.errors.some((e) => e.includes("exceeds maximum size")),
|
|
191
|
+
).toBe(true);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("should run custom validator", () => {
|
|
195
|
+
const onValidationError = vi.fn();
|
|
196
|
+
const customValidator = vi.fn((files: File[]) => {
|
|
197
|
+
if (files.some((f) => f.name.includes("bad"))) {
|
|
198
|
+
return ["File name contains 'bad'"];
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const { state, onDrop } = useDragDrop({
|
|
204
|
+
validator: customValidator,
|
|
205
|
+
onValidationError,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const badFile = createMockFile("bad-file.txt", 100, "text/plain");
|
|
209
|
+
onDrop(createMockDragEvent("drop", [badFile]));
|
|
210
|
+
|
|
211
|
+
expect(customValidator).toHaveBeenCalledWith([badFile]);
|
|
212
|
+
expect(onValidationError).toHaveBeenCalled();
|
|
213
|
+
expect(state.value.errors).toContain("File name contains 'bad'");
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe("input change handling", () => {
|
|
218
|
+
it("should process files from input change", () => {
|
|
219
|
+
const onFilesReceived = vi.fn();
|
|
220
|
+
const { onInputChange } = useDragDrop({ onFilesReceived });
|
|
221
|
+
|
|
222
|
+
const file = createMockFile("test.txt", 100, "text/plain");
|
|
223
|
+
const event = createMockInputChangeEvent([file]);
|
|
224
|
+
|
|
225
|
+
onInputChange(event);
|
|
226
|
+
|
|
227
|
+
expect(onFilesReceived).toHaveBeenCalledWith([file]);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("should reset input value after processing", () => {
|
|
231
|
+
const { onInputChange } = useDragDrop();
|
|
232
|
+
|
|
233
|
+
const file = createMockFile("test.txt", 100, "text/plain");
|
|
234
|
+
const event = createMockInputChangeEvent([file]);
|
|
235
|
+
const input = event.target as HTMLInputElement;
|
|
236
|
+
|
|
237
|
+
onInputChange(event);
|
|
238
|
+
|
|
239
|
+
expect(input.value).toBe("");
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe("inputProps", () => {
|
|
244
|
+
it("should provide correct input props", () => {
|
|
245
|
+
const { inputProps } = useDragDrop({
|
|
246
|
+
multiple: true,
|
|
247
|
+
accept: ["image/*", ".pdf"],
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
expect(inputProps.value.type).toBe("file");
|
|
251
|
+
expect(inputProps.value.multiple).toBe(true);
|
|
252
|
+
expect(inputProps.value.accept).toBe("image/*, .pdf");
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("should default multiple to true", () => {
|
|
256
|
+
const { inputProps } = useDragDrop();
|
|
257
|
+
|
|
258
|
+
expect(inputProps.value.multiple).toBe(true);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("should handle undefined accept", () => {
|
|
262
|
+
const { inputProps } = useDragDrop();
|
|
263
|
+
|
|
264
|
+
expect(inputProps.value.accept).toBeUndefined();
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
describe("reset", () => {
|
|
269
|
+
it("should reset state to initial values", () => {
|
|
270
|
+
const onValidationError = vi.fn();
|
|
271
|
+
const { state, onDrop, reset } = useDragDrop({
|
|
272
|
+
accept: [".txt"],
|
|
273
|
+
onValidationError,
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Trigger validation error
|
|
277
|
+
const invalidFile = createMockFile("test.pdf", 100, "application/pdf");
|
|
278
|
+
onDrop(createMockDragEvent("drop", [invalidFile]));
|
|
279
|
+
|
|
280
|
+
expect(state.value.isValid).toBe(false);
|
|
281
|
+
expect(state.value.errors.length).toBeGreaterThan(0);
|
|
282
|
+
|
|
283
|
+
// Reset
|
|
284
|
+
reset();
|
|
285
|
+
|
|
286
|
+
expect(state.value.isDragging).toBe(false);
|
|
287
|
+
expect(state.value.isOver).toBe(false);
|
|
288
|
+
expect(state.value.isValid).toBe(true);
|
|
289
|
+
expect(state.value.errors).toEqual([]);
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
describe("processFiles", () => {
|
|
294
|
+
it("should allow direct file processing", () => {
|
|
295
|
+
const onFilesReceived = vi.fn();
|
|
296
|
+
const { processFiles } = useDragDrop({ onFilesReceived });
|
|
297
|
+
|
|
298
|
+
const files = [createMockFile("test.txt", 100, "text/plain")];
|
|
299
|
+
processFiles(files);
|
|
300
|
+
|
|
301
|
+
expect(onFilesReceived).toHaveBeenCalledWith(files);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
});
|
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
computed,
|
|
11
11
|
onMounted,
|
|
12
12
|
onUnmounted,
|
|
13
|
+
type Ref,
|
|
13
14
|
readonly,
|
|
14
15
|
ref,
|
|
15
16
|
shallowReadonly,
|
|
16
|
-
type Ref,
|
|
17
17
|
} from "vue";
|
|
18
18
|
import { useFlowManagerContext } from "./useFlowManagerContext";
|
|
19
19
|
import { useUploadistaClient } from "./useUploadistaClient";
|
|
@@ -306,7 +306,11 @@ export function useFlow(options: FlowUploadOptions): UseFlowReturn {
|
|
|
306
306
|
bytesAccepted: number,
|
|
307
307
|
bytesTotal: number | null,
|
|
308
308
|
) => {
|
|
309
|
-
optionsRef.value.onChunkComplete?.(
|
|
309
|
+
optionsRef.value.onChunkComplete?.(
|
|
310
|
+
chunkSize,
|
|
311
|
+
bytesAccepted,
|
|
312
|
+
bytesTotal,
|
|
313
|
+
);
|
|
310
314
|
},
|
|
311
315
|
onFlowComplete: (outputs: TypedOutput[]) => {
|
|
312
316
|
optionsRef.value.onFlowComplete?.(outputs);
|
|
@@ -167,10 +167,7 @@ export function useUploadEvents(options: UseUploadEventsOptions): void {
|
|
|
167
167
|
break;
|
|
168
168
|
case UploadEventType.UPLOAD_PROGRESS:
|
|
169
169
|
options.onUploadProgress?.({
|
|
170
|
-
...(event.data as unknown as Omit<
|
|
171
|
-
UploadProgressEventData,
|
|
172
|
-
"flow"
|
|
173
|
-
>),
|
|
170
|
+
...(event.data as unknown as Omit<UploadProgressEventData, "flow">),
|
|
174
171
|
flow: flowContext,
|
|
175
172
|
});
|
|
176
173
|
break;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { mount } from "@vue/test-utils";
|
|
2
|
+
import { defineComponent, h, ref } from "vue";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import {
|
|
5
|
+
UPLOADISTA_CLIENT_KEY,
|
|
6
|
+
UPLOADISTA_EVENT_SUBSCRIBERS_KEY,
|
|
7
|
+
} from "./plugin";
|
|
8
|
+
import { useUploadistaClient } from "./useUploadistaClient";
|
|
9
|
+
|
|
10
|
+
describe("useUploadistaClient", () => {
|
|
11
|
+
const createMockClient = () => ({
|
|
12
|
+
upload: vi.fn(),
|
|
13
|
+
abort: vi.fn(),
|
|
14
|
+
getProgress: vi.fn(),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const createTestComponent = (setupFn: () => unknown) => {
|
|
18
|
+
return defineComponent({
|
|
19
|
+
setup() {
|
|
20
|
+
const result = setupFn();
|
|
21
|
+
return { result };
|
|
22
|
+
},
|
|
23
|
+
render() {
|
|
24
|
+
return h("div");
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
describe("client injection", () => {
|
|
30
|
+
it("should return injected client", () => {
|
|
31
|
+
const mockClient = createMockClient();
|
|
32
|
+
let result: ReturnType<typeof useUploadistaClient> | null = null;
|
|
33
|
+
|
|
34
|
+
const TestComponent = createTestComponent(() => {
|
|
35
|
+
result = useUploadistaClient();
|
|
36
|
+
return result;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
mount(TestComponent, {
|
|
40
|
+
global: {
|
|
41
|
+
provide: {
|
|
42
|
+
[UPLOADISTA_CLIENT_KEY as symbol]: mockClient,
|
|
43
|
+
[UPLOADISTA_EVENT_SUBSCRIBERS_KEY as symbol]: ref(new Set()),
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(result).not.toBeNull();
|
|
49
|
+
expect(result!.client).toBe(mockClient);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should throw error when used outside provider context", () => {
|
|
53
|
+
const TestComponent = createTestComponent(() => {
|
|
54
|
+
useUploadistaClient();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(() => {
|
|
58
|
+
mount(TestComponent);
|
|
59
|
+
}).toThrow(
|
|
60
|
+
"useUploadistaClient must be used within a component tree that has the Uploadista plugin or provider installed",
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("event subscription", () => {
|
|
66
|
+
it("should add handler to event subscribers", () => {
|
|
67
|
+
const mockClient = createMockClient();
|
|
68
|
+
const eventSubscribers = ref(new Set<(event: unknown) => void>());
|
|
69
|
+
let result: ReturnType<typeof useUploadistaClient> | null = null;
|
|
70
|
+
|
|
71
|
+
const TestComponent = createTestComponent(() => {
|
|
72
|
+
result = useUploadistaClient();
|
|
73
|
+
return result;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
mount(TestComponent, {
|
|
77
|
+
global: {
|
|
78
|
+
provide: {
|
|
79
|
+
[UPLOADISTA_CLIENT_KEY as symbol]: mockClient,
|
|
80
|
+
[UPLOADISTA_EVENT_SUBSCRIBERS_KEY as symbol]: eventSubscribers,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const handler = vi.fn();
|
|
86
|
+
result!.subscribeToEvents(handler);
|
|
87
|
+
|
|
88
|
+
expect(eventSubscribers.value.has(handler)).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should remove handler on unsubscribe", () => {
|
|
92
|
+
const mockClient = createMockClient();
|
|
93
|
+
const eventSubscribers = ref(new Set<(event: unknown) => void>());
|
|
94
|
+
let result: ReturnType<typeof useUploadistaClient> | null = null;
|
|
95
|
+
|
|
96
|
+
const TestComponent = createTestComponent(() => {
|
|
97
|
+
result = useUploadistaClient();
|
|
98
|
+
return result;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
mount(TestComponent, {
|
|
102
|
+
global: {
|
|
103
|
+
provide: {
|
|
104
|
+
[UPLOADISTA_CLIENT_KEY as symbol]: mockClient,
|
|
105
|
+
[UPLOADISTA_EVENT_SUBSCRIBERS_KEY as symbol]: eventSubscribers,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const handler = vi.fn();
|
|
111
|
+
const unsubscribe = result!.subscribeToEvents(handler);
|
|
112
|
+
|
|
113
|
+
expect(eventSubscribers.value.has(handler)).toBe(true);
|
|
114
|
+
|
|
115
|
+
unsubscribe();
|
|
116
|
+
|
|
117
|
+
expect(eventSubscribers.value.has(handler)).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should warn and return no-op when subscribers not available", () => {
|
|
121
|
+
const mockClient = createMockClient();
|
|
122
|
+
let result: ReturnType<typeof useUploadistaClient> | null = null;
|
|
123
|
+
const consoleWarn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
124
|
+
|
|
125
|
+
const TestComponent = createTestComponent(() => {
|
|
126
|
+
result = useUploadistaClient();
|
|
127
|
+
return result;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
mount(TestComponent, {
|
|
131
|
+
global: {
|
|
132
|
+
provide: {
|
|
133
|
+
[UPLOADISTA_CLIENT_KEY as symbol]: mockClient,
|
|
134
|
+
// Not providing UPLOADISTA_EVENT_SUBSCRIBERS_KEY
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const handler = vi.fn();
|
|
140
|
+
const unsubscribe = result!.subscribeToEvents(handler);
|
|
141
|
+
|
|
142
|
+
expect(consoleWarn).toHaveBeenCalledWith(
|
|
143
|
+
expect.stringContaining("subscribeToEvents called but no event subscribers provided"),
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Unsubscribe should be a no-op and not throw
|
|
147
|
+
expect(() => unsubscribe()).not.toThrow();
|
|
148
|
+
|
|
149
|
+
consoleWarn.mockRestore();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -18,10 +18,71 @@
|
|
|
18
18
|
|
|
19
19
|
// Re-export all components
|
|
20
20
|
export * from "./components";
|
|
21
|
-
// Re-export
|
|
22
|
-
export
|
|
23
|
-
//
|
|
24
|
-
|
|
21
|
+
// Re-export types from composables
|
|
22
|
+
export type {
|
|
23
|
+
// Upload types - rename to avoid conflict
|
|
24
|
+
ChunkMetrics,
|
|
25
|
+
// Drag and drop types
|
|
26
|
+
DragDropOptions,
|
|
27
|
+
DragDropState,
|
|
28
|
+
// Metrics types
|
|
29
|
+
FileUploadMetrics,
|
|
30
|
+
// Flow types
|
|
31
|
+
FlowInputMetadata,
|
|
32
|
+
FlowUploadState,
|
|
33
|
+
FlowUploadStatus,
|
|
34
|
+
InputExecutionState,
|
|
35
|
+
// Multi-upload types - rename to avoid conflict
|
|
36
|
+
MultiUploadOptions,
|
|
37
|
+
MultiUploadState,
|
|
38
|
+
PerformanceInsights,
|
|
39
|
+
UploadFailedEventData,
|
|
40
|
+
UploadFileEventData,
|
|
41
|
+
UploadInput,
|
|
42
|
+
UploadItem as MultiUploadItem,
|
|
43
|
+
// Plugin types
|
|
44
|
+
UploadistaPluginOptions,
|
|
45
|
+
UploadProgressEventData,
|
|
46
|
+
UploadSessionMetrics,
|
|
47
|
+
UploadState,
|
|
48
|
+
UploadStatus as UploadStatusType,
|
|
49
|
+
UploadValidationFailedEventData,
|
|
50
|
+
UploadValidationSuccessEventData,
|
|
51
|
+
UploadValidationWarningEventData,
|
|
52
|
+
// Event types
|
|
53
|
+
UseFlowEventsOptions,
|
|
54
|
+
UseFlowReturn,
|
|
55
|
+
UseUploadEventsOptions,
|
|
56
|
+
// Client types
|
|
57
|
+
UseUploadistaClientReturn,
|
|
58
|
+
UseUploadMetricsOptions,
|
|
59
|
+
} from "./composables";
|
|
60
|
+
// Re-export composables with explicit types to avoid conflicts
|
|
61
|
+
// Types with potential conflicts are renamed for clarity
|
|
62
|
+
export {
|
|
63
|
+
// Plugin
|
|
64
|
+
createUploadistaPlugin,
|
|
65
|
+
// Event composables
|
|
66
|
+
isFlowEvent,
|
|
67
|
+
isUploadEvent,
|
|
68
|
+
UPLOADISTA_CLIENT_KEY,
|
|
69
|
+
// Drag and drop
|
|
70
|
+
useDragDrop,
|
|
71
|
+
// Flow composables
|
|
72
|
+
useFlow,
|
|
73
|
+
useFlowEvents,
|
|
74
|
+
// Multi-upload
|
|
75
|
+
useMultiFlowUpload,
|
|
76
|
+
useMultiUpload,
|
|
77
|
+
// Upload composables
|
|
78
|
+
useUpload,
|
|
79
|
+
useUploadEvents,
|
|
80
|
+
// Client
|
|
81
|
+
useUploadistaClient,
|
|
82
|
+
useUploadistaEvents,
|
|
83
|
+
// Metrics
|
|
84
|
+
useUploadMetrics,
|
|
85
|
+
} from "./composables";
|
|
25
86
|
|
|
26
87
|
export * from "./providers";
|
|
27
88
|
// Re-export utilities
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
</template>
|
|
4
4
|
|
|
5
5
|
<script setup lang="ts">
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
BrowserUploadInput,
|
|
8
|
+
UploadistaEvent,
|
|
9
|
+
} from "@uploadista/client-browser";
|
|
7
10
|
import {
|
|
8
11
|
FlowManager,
|
|
9
12
|
type FlowManagerCallbacks,
|
|
@@ -11,7 +14,7 @@ import {
|
|
|
11
14
|
} from "@uploadista/client-core";
|
|
12
15
|
import { EventType, type FlowEvent } from "@uploadista/core/flow";
|
|
13
16
|
import { UploadEventType } from "@uploadista/core/types";
|
|
14
|
-
import {
|
|
17
|
+
import { onBeforeUnmount, onMounted, provide } from "vue";
|
|
15
18
|
import { useUploadistaClient } from "../composables/useUploadistaClient";
|
|
16
19
|
|
|
17
20
|
/**
|