nuxt-ui-elements 0.1.25 → 0.1.27
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/module.json +1 -1
- package/dist/module.mjs +2 -2
- package/dist/runtime/composables/{useUploader → useUploadManager}/index.d.ts +61 -65
- package/dist/runtime/composables/{useUploader → useUploadManager}/index.js +124 -29
- package/dist/runtime/composables/{useUploader → useUploadManager}/plugins/image-compressor.d.ts +23 -16
- package/dist/runtime/composables/{useUploader → useUploadManager}/plugins/image-compressor.js +27 -5
- package/dist/runtime/composables/{useUploader → useUploadManager}/plugins/index.d.ts +1 -0
- package/dist/runtime/composables/{useUploader → useUploadManager}/plugins/index.js +1 -0
- package/dist/runtime/composables/useUploadManager/plugins/storage/azure-datalake.d.ts +45 -0
- package/dist/runtime/composables/useUploadManager/plugins/storage/azure-datalake.js +108 -0
- package/dist/runtime/composables/useUploadManager/plugins/storage/index.d.ts +10 -0
- package/dist/runtime/composables/useUploadManager/plugins/storage/index.js +1 -0
- package/dist/runtime/composables/useUploadManager/plugins/thumbnail-generator.d.ts +7 -0
- package/dist/runtime/composables/{useUploader → useUploadManager}/plugins/thumbnail-generator.js +4 -3
- package/dist/runtime/composables/useUploadManager/types.d.ts +220 -0
- package/dist/runtime/composables/useUploadManager/types.js +3 -0
- package/dist/runtime/composables/useUploadManager/validators/allowed-file-types.d.ts +5 -0
- package/dist/runtime/composables/{useUploader → useUploadManager}/validators/allowed-file-types.js +4 -3
- package/dist/runtime/composables/useUploadManager/validators/duplicate-file.d.ts +13 -0
- package/dist/runtime/composables/{useUploader → useUploadManager}/validators/duplicate-file.js +5 -4
- package/dist/runtime/composables/useUploadManager/validators/max-file-size.d.ts +5 -0
- package/dist/runtime/composables/{useUploader → useUploadManager}/validators/max-file-size.js +4 -3
- package/dist/runtime/composables/useUploadManager/validators/max-files.d.ts +5 -0
- package/dist/runtime/composables/useUploadManager/validators/max-files.js +13 -0
- package/dist/runtime/types/index.d.ts +3 -1
- package/dist/runtime/types/index.js +3 -1
- package/package.json +7 -1
- package/dist/runtime/composables/useUploader/plugins/thumbnail-generator.d.ts +0 -8
- package/dist/runtime/composables/useUploader/types.d.ts +0 -155
- package/dist/runtime/composables/useUploader/types.js +0 -0
- package/dist/runtime/composables/useUploader/validators/allowed-file-types.d.ts +0 -6
- package/dist/runtime/composables/useUploader/validators/duplicate-file.d.ts +0 -27
- package/dist/runtime/composables/useUploader/validators/max-file-size.d.ts +0 -6
- package/dist/runtime/composables/useUploader/validators/max-files.d.ts +0 -6
- package/dist/runtime/composables/useUploader/validators/max-files.js +0 -12
- /package/dist/runtime/composables/{useUploader → useUploadManager}/validators/index.d.ts +0 -0
- /package/dist/runtime/composables/{useUploader → useUploadManager}/validators/index.js +0 -0
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -253,8 +253,8 @@ const module$1 = defineNuxtModule({
|
|
|
253
253
|
addImportsDir(resolver.resolve("./runtime/composables"));
|
|
254
254
|
addImports([
|
|
255
255
|
{
|
|
256
|
-
name: "
|
|
257
|
-
from: resolver.resolve("./runtime/composables/
|
|
256
|
+
name: "useUploadManager",
|
|
257
|
+
from: resolver.resolve("./runtime/composables/useUploadManager")
|
|
258
258
|
}
|
|
259
259
|
]);
|
|
260
260
|
nuxt.options.alias["#std"] = resolver.resolve("./runtime/utils/std");
|
|
@@ -1,152 +1,148 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare const
|
|
3
|
-
files:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import type { UploaderEvents, UploadFile, UploadOptions, UploadStatus, UploadFn, GetRemoteFileFn, Plugin as UploaderPlugin } from "./types.js";
|
|
2
|
+
export declare const useUploadManager: <TUploadResult = any>(_options?: UploadOptions) => {
|
|
3
|
+
files: import("vue").Ref<{
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
size: number;
|
|
7
|
+
mimeType: string;
|
|
8
|
+
data: {
|
|
9
9
|
readonly lastModified: number;
|
|
10
10
|
readonly name: string;
|
|
11
11
|
readonly webkitRelativePath: string;
|
|
12
12
|
readonly size: number;
|
|
13
13
|
readonly type: string;
|
|
14
|
-
|
|
14
|
+
arrayBuffer: {
|
|
15
15
|
(): Promise<ArrayBuffer>;
|
|
16
16
|
(): Promise<ArrayBuffer>;
|
|
17
17
|
};
|
|
18
|
-
|
|
18
|
+
bytes: {
|
|
19
19
|
(): Promise<Uint8Array<ArrayBuffer>>;
|
|
20
20
|
(): Promise<Uint8Array<ArrayBuffer>>;
|
|
21
21
|
};
|
|
22
|
-
|
|
22
|
+
slice: {
|
|
23
23
|
(start?: number, end?: number, contentType?: string): Blob;
|
|
24
24
|
(start?: number, end?: number, contentType?: string): Blob;
|
|
25
25
|
};
|
|
26
|
-
|
|
26
|
+
stream: {
|
|
27
27
|
(): ReadableStream<Uint8Array<ArrayBuffer>>;
|
|
28
28
|
(): ReadableStream<Uint8Array<ArrayBuffer>>;
|
|
29
29
|
};
|
|
30
|
-
|
|
30
|
+
text: {
|
|
31
31
|
(): Promise<string>;
|
|
32
32
|
(): Promise<string>;
|
|
33
33
|
};
|
|
34
34
|
} | {
|
|
35
35
|
readonly size: number;
|
|
36
36
|
readonly type: string;
|
|
37
|
-
|
|
37
|
+
arrayBuffer: {
|
|
38
38
|
(): Promise<ArrayBuffer>;
|
|
39
39
|
(): Promise<ArrayBuffer>;
|
|
40
40
|
};
|
|
41
|
-
|
|
41
|
+
bytes: {
|
|
42
42
|
(): Promise<Uint8Array<ArrayBuffer>>;
|
|
43
43
|
(): Promise<Uint8Array<ArrayBuffer>>;
|
|
44
44
|
};
|
|
45
|
-
|
|
45
|
+
slice: {
|
|
46
46
|
(start?: number, end?: number, contentType?: string): Blob;
|
|
47
47
|
(start?: number, end?: number, contentType?: string): Blob;
|
|
48
48
|
};
|
|
49
|
-
|
|
49
|
+
stream: {
|
|
50
50
|
(): ReadableStream<Uint8Array<ArrayBuffer>>;
|
|
51
51
|
(): ReadableStream<Uint8Array<ArrayBuffer>>;
|
|
52
52
|
};
|
|
53
|
-
|
|
53
|
+
text: {
|
|
54
54
|
(): Promise<string>;
|
|
55
55
|
(): Promise<string>;
|
|
56
56
|
};
|
|
57
57
|
};
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
status: import("./types.js").FileStatus;
|
|
59
|
+
preview?: string | undefined;
|
|
60
|
+
progress: {
|
|
61
|
+
percentage: number;
|
|
62
62
|
};
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
error?: {
|
|
64
|
+
message: string;
|
|
65
|
+
details?: unknown;
|
|
66
66
|
} | undefined;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
readonly mimeType: string;
|
|
78
|
-
readonly data: {
|
|
67
|
+
uploadResult?: import("vue").UnwrapRef<TUploadResult> | undefined;
|
|
68
|
+
isRemote?: boolean | undefined;
|
|
69
|
+
remoteUrl?: string | undefined;
|
|
70
|
+
meta: Record<string, unknown>;
|
|
71
|
+
}[], UploadFile<TUploadResult>[] | {
|
|
72
|
+
id: string;
|
|
73
|
+
name: string;
|
|
74
|
+
size: number;
|
|
75
|
+
mimeType: string;
|
|
76
|
+
data: {
|
|
79
77
|
readonly lastModified: number;
|
|
80
78
|
readonly name: string;
|
|
81
79
|
readonly webkitRelativePath: string;
|
|
82
80
|
readonly size: number;
|
|
83
81
|
readonly type: string;
|
|
84
|
-
|
|
82
|
+
arrayBuffer: {
|
|
85
83
|
(): Promise<ArrayBuffer>;
|
|
86
84
|
(): Promise<ArrayBuffer>;
|
|
87
85
|
};
|
|
88
|
-
|
|
86
|
+
bytes: {
|
|
89
87
|
(): Promise<Uint8Array<ArrayBuffer>>;
|
|
90
88
|
(): Promise<Uint8Array<ArrayBuffer>>;
|
|
91
89
|
};
|
|
92
|
-
|
|
90
|
+
slice: {
|
|
93
91
|
(start?: number, end?: number, contentType?: string): Blob;
|
|
94
92
|
(start?: number, end?: number, contentType?: string): Blob;
|
|
95
93
|
};
|
|
96
|
-
|
|
94
|
+
stream: {
|
|
97
95
|
(): ReadableStream<Uint8Array<ArrayBuffer>>;
|
|
98
96
|
(): ReadableStream<Uint8Array<ArrayBuffer>>;
|
|
99
97
|
};
|
|
100
|
-
|
|
98
|
+
text: {
|
|
101
99
|
(): Promise<string>;
|
|
102
100
|
(): Promise<string>;
|
|
103
101
|
};
|
|
104
102
|
} | {
|
|
105
103
|
readonly size: number;
|
|
106
104
|
readonly type: string;
|
|
107
|
-
|
|
105
|
+
arrayBuffer: {
|
|
108
106
|
(): Promise<ArrayBuffer>;
|
|
109
107
|
(): Promise<ArrayBuffer>;
|
|
110
108
|
};
|
|
111
|
-
|
|
109
|
+
bytes: {
|
|
112
110
|
(): Promise<Uint8Array<ArrayBuffer>>;
|
|
113
111
|
(): Promise<Uint8Array<ArrayBuffer>>;
|
|
114
112
|
};
|
|
115
|
-
|
|
113
|
+
slice: {
|
|
116
114
|
(start?: number, end?: number, contentType?: string): Blob;
|
|
117
115
|
(start?: number, end?: number, contentType?: string): Blob;
|
|
118
116
|
};
|
|
119
|
-
|
|
117
|
+
stream: {
|
|
120
118
|
(): ReadableStream<Uint8Array<ArrayBuffer>>;
|
|
121
119
|
(): ReadableStream<Uint8Array<ArrayBuffer>>;
|
|
122
120
|
};
|
|
123
|
-
|
|
121
|
+
text: {
|
|
124
122
|
(): Promise<string>;
|
|
125
123
|
(): Promise<string>;
|
|
126
124
|
};
|
|
127
125
|
};
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
126
|
+
status: import("./types.js").FileStatus;
|
|
127
|
+
preview?: string | undefined;
|
|
128
|
+
progress: {
|
|
129
|
+
percentage: number;
|
|
132
130
|
};
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
131
|
+
error?: {
|
|
132
|
+
message: string;
|
|
133
|
+
details?: unknown;
|
|
136
134
|
} | undefined;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
};
|
|
143
|
-
}[]>>;
|
|
135
|
+
uploadResult?: import("vue").UnwrapRef<TUploadResult> | undefined;
|
|
136
|
+
isRemote?: boolean | undefined;
|
|
137
|
+
remoteUrl?: string | undefined;
|
|
138
|
+
meta: Record<string, unknown>;
|
|
139
|
+
}[]>;
|
|
144
140
|
totalProgress: import("vue").ComputedRef<number>;
|
|
145
141
|
addFiles: (newFiles: File[]) => Promise<(UploadFile<any> | null)[]>;
|
|
146
142
|
addFile: (file: File) => Promise<UploadFile<any> | null>;
|
|
147
143
|
onGetRemoteFile: (fn: GetRemoteFileFn) => void;
|
|
148
144
|
onUpload: (fn: UploadFn<TUploadResult>) => void;
|
|
149
|
-
removeFile: (fileId: string) => void
|
|
145
|
+
removeFile: (fileId: string) => Promise<void>;
|
|
150
146
|
removeFiles: (fileIds: string[]) => {
|
|
151
147
|
id: string;
|
|
152
148
|
name: string;
|
|
@@ -360,9 +356,9 @@ export declare const useUploader: <TUploadResult = any>(_options?: UploadOptions
|
|
|
360
356
|
status: import("vue").Ref<UploadStatus, UploadStatus>;
|
|
361
357
|
updateFile: (fileId: string, updatedFile: Partial<UploadFile<TUploadResult>>) => void;
|
|
362
358
|
initializeExistingFiles: (initialFiles: Array<Partial<UploadFile>>) => Promise<void>;
|
|
363
|
-
addPlugin:
|
|
359
|
+
addPlugin: (plugin: UploaderPlugin<any, any>) => void;
|
|
364
360
|
on: {
|
|
365
|
-
<
|
|
366
|
-
(type:
|
|
361
|
+
<K extends keyof UploaderEvents<TUploadResult>>(type: K, handler: (event: UploaderEvents<TUploadResult>[K]) => void): void;
|
|
362
|
+
(type: string, handler: (event: any) => void): void;
|
|
367
363
|
};
|
|
368
364
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import mitt from "mitt";
|
|
2
|
-
import { computed,
|
|
2
|
+
import { computed, ref } from "vue";
|
|
3
3
|
import { ValidatorAllowedFileTypes, ValidatorMaxfileSize, ValidatorMaxFiles } from "./validators/index.js";
|
|
4
4
|
import { PluginThumbnailGenerator, PluginImageCompressor } from "./plugins/index.js";
|
|
5
5
|
function getExtension(fullFileName) {
|
|
@@ -18,11 +18,23 @@ const defaultOptions = {
|
|
|
18
18
|
imageCompression: false,
|
|
19
19
|
autoProceed: false
|
|
20
20
|
};
|
|
21
|
-
export const
|
|
21
|
+
export const useUploadManager = (_options = {}) => {
|
|
22
22
|
const options = { ...defaultOptions, ..._options };
|
|
23
23
|
const files = ref([]);
|
|
24
24
|
const emitter = mitt();
|
|
25
25
|
const status = ref("waiting");
|
|
26
|
+
const pluginEmitFunctions = /* @__PURE__ */ new Map();
|
|
27
|
+
const getPluginEmitFn = (pluginId) => {
|
|
28
|
+
let emitFn = pluginEmitFunctions.get(pluginId);
|
|
29
|
+
if (!emitFn) {
|
|
30
|
+
emitFn = (event, payload) => {
|
|
31
|
+
const prefixedEvent = `${pluginId}:${String(event)}`;
|
|
32
|
+
emitter.emit(prefixedEvent, payload);
|
|
33
|
+
};
|
|
34
|
+
pluginEmitFunctions.set(pluginId, emitFn);
|
|
35
|
+
}
|
|
36
|
+
return emitFn;
|
|
37
|
+
};
|
|
26
38
|
let uploadFn = async () => {
|
|
27
39
|
throw new Error("No uploader configured");
|
|
28
40
|
};
|
|
@@ -34,37 +46,71 @@ export const useUploader = (_options = {}) => {
|
|
|
34
46
|
const sum = files.value.reduce((acc, file) => acc + file.progress.percentage, 0);
|
|
35
47
|
return Math.round(sum / files.value.length);
|
|
36
48
|
});
|
|
37
|
-
const
|
|
38
|
-
|
|
49
|
+
const isStoragePlugin = (plugin) => {
|
|
50
|
+
return !!(plugin.hooks.upload || plugin.hooks.getRemoteFile || plugin.hooks.remove);
|
|
51
|
+
};
|
|
52
|
+
const getStoragePlugin = () => {
|
|
53
|
+
if (!options.plugins) return null;
|
|
54
|
+
for (let i = options.plugins.length - 1; i >= 0; i--) {
|
|
55
|
+
const plugin = options.plugins[i];
|
|
56
|
+
if (plugin && isStoragePlugin(plugin)) {
|
|
57
|
+
return plugin;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
};
|
|
62
|
+
const addPlugin = (plugin) => {
|
|
63
|
+
if (isStoragePlugin(plugin)) {
|
|
64
|
+
const existingStorage = options.plugins?.find((p) => isStoragePlugin(p));
|
|
65
|
+
if (existingStorage && import.meta.dev) {
|
|
66
|
+
console.warn(
|
|
67
|
+
`[useUploadManager] Multiple storage plugins detected!
|
|
68
|
+
|
|
69
|
+
You're trying to add "${plugin.id}" but "${existingStorage.id}" is already registered.
|
|
70
|
+
The LAST storage plugin ("${plugin.id}") will be used for uploads.
|
|
71
|
+
|
|
72
|
+
\u{1F4A1} If you need multiple storage destinations, create separate uploader instances:
|
|
73
|
+
|
|
74
|
+
const s3Uploader = useUploadManager({ plugins: [PluginS3Storage({ ... })] })
|
|
75
|
+
const azureUploader = useUploadManager({ plugins: [PluginAzureStorage({ ... })] })
|
|
76
|
+
`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
;
|
|
39
81
|
options.plugins?.push(plugin);
|
|
40
82
|
};
|
|
41
83
|
if (options.maxFiles !== false && options.maxFiles !== void 0) {
|
|
42
|
-
addPlugin(ValidatorMaxFiles
|
|
84
|
+
addPlugin(ValidatorMaxFiles({ maxFiles: options.maxFiles }));
|
|
43
85
|
}
|
|
44
86
|
if (options.maxFileSize !== false && options.maxFileSize !== void 0) {
|
|
45
|
-
addPlugin(ValidatorMaxfileSize
|
|
87
|
+
addPlugin(ValidatorMaxfileSize({ maxFileSize: options.maxFileSize }));
|
|
46
88
|
}
|
|
47
89
|
if (options.allowedFileTypes !== false && options.allowedFileTypes !== void 0 && options.allowedFileTypes.length > 0) {
|
|
48
|
-
addPlugin(ValidatorAllowedFileTypes
|
|
90
|
+
addPlugin(ValidatorAllowedFileTypes({ allowedFileTypes: options.allowedFileTypes }));
|
|
49
91
|
}
|
|
50
92
|
if (options.thumbnails !== false && options.thumbnails !== void 0) {
|
|
51
93
|
const thumbOpts = options.thumbnails === true ? {} : options.thumbnails || {};
|
|
52
|
-
addPlugin(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
94
|
+
addPlugin(
|
|
95
|
+
PluginThumbnailGenerator({
|
|
96
|
+
width: thumbOpts.width ?? 128,
|
|
97
|
+
height: thumbOpts.height ?? 128,
|
|
98
|
+
quality: thumbOpts.quality ?? 1
|
|
99
|
+
})
|
|
100
|
+
);
|
|
57
101
|
}
|
|
58
102
|
if (options.imageCompression !== false && options.imageCompression !== void 0) {
|
|
59
103
|
const compressionOpts = options.imageCompression === true ? {} : options.imageCompression || {};
|
|
60
|
-
addPlugin(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
104
|
+
addPlugin(
|
|
105
|
+
PluginImageCompressor({
|
|
106
|
+
maxWidth: compressionOpts.maxWidth ?? 1920,
|
|
107
|
+
maxHeight: compressionOpts.maxHeight ?? 1920,
|
|
108
|
+
quality: compressionOpts.quality ?? 0.85,
|
|
109
|
+
outputFormat: compressionOpts.outputFormat ?? "auto",
|
|
110
|
+
minSizeToCompress: compressionOpts.minSizeToCompress ?? 1e5,
|
|
111
|
+
preserveMetadata: compressionOpts.preserveMetadata ?? true
|
|
112
|
+
})
|
|
113
|
+
);
|
|
68
114
|
}
|
|
69
115
|
emitter.on("upload:progress", ({ file, progress }) => {
|
|
70
116
|
updateFile(file.id, { progress: { percentage: progress } });
|
|
@@ -82,7 +128,21 @@ export const useUploader = (_options = {}) => {
|
|
|
82
128
|
const initializedfiles = await Promise.all(
|
|
83
129
|
initialFiles.map(async (file) => {
|
|
84
130
|
if (!file.id) return null;
|
|
85
|
-
const
|
|
131
|
+
const storagePlugin = getStoragePlugin();
|
|
132
|
+
let remoteFileData;
|
|
133
|
+
if (storagePlugin?.hooks.getRemoteFile) {
|
|
134
|
+
const context = {
|
|
135
|
+
files: files.value,
|
|
136
|
+
options,
|
|
137
|
+
emit: (event, payload) => {
|
|
138
|
+
const prefixedEvent = `${storagePlugin.id}:${String(event)}`;
|
|
139
|
+
emitter.emit(prefixedEvent, payload);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
remoteFileData = await storagePlugin.hooks.getRemoteFile(file.id, context);
|
|
143
|
+
} else {
|
|
144
|
+
remoteFileData = await getRemoteFileFn(file.id);
|
|
145
|
+
}
|
|
86
146
|
const existingFile = {
|
|
87
147
|
...file,
|
|
88
148
|
id: file.id,
|
|
@@ -91,9 +151,9 @@ export const useUploader = (_options = {}) => {
|
|
|
91
151
|
status: "complete",
|
|
92
152
|
progress: { percentage: 100 },
|
|
93
153
|
meta: {},
|
|
94
|
-
size,
|
|
95
|
-
mimeType,
|
|
96
|
-
remoteUrl
|
|
154
|
+
size: remoteFileData.size,
|
|
155
|
+
mimeType: remoteFileData.mimeType,
|
|
156
|
+
remoteUrl: remoteFileData.remoteUrl
|
|
97
157
|
};
|
|
98
158
|
const processedFile = await runPluginStage("process", existingFile);
|
|
99
159
|
if (!processedFile) return null;
|
|
@@ -134,9 +194,25 @@ export const useUploader = (_options = {}) => {
|
|
|
134
194
|
const addFiles = (newFiles) => {
|
|
135
195
|
return Promise.all(newFiles.map((file) => addFile(file)));
|
|
136
196
|
};
|
|
137
|
-
const removeFile = (fileId) => {
|
|
197
|
+
const removeFile = async (fileId) => {
|
|
138
198
|
const file = files.value.find((f) => f.id === fileId);
|
|
139
199
|
if (!file) return;
|
|
200
|
+
const storagePlugin = getStoragePlugin();
|
|
201
|
+
if (storagePlugin?.hooks.remove) {
|
|
202
|
+
try {
|
|
203
|
+
const context = {
|
|
204
|
+
files: files.value,
|
|
205
|
+
options,
|
|
206
|
+
emit: (event, payload) => {
|
|
207
|
+
const prefixedEvent = `${storagePlugin.id}:${String(event)}`;
|
|
208
|
+
emitter.emit(prefixedEvent, payload);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
await storagePlugin.hooks.remove(file, context);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error(`Storage plugin remove error:`, error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
140
216
|
files.value = files.value.filter((f) => f.id !== fileId);
|
|
141
217
|
emitter.emit("file:removed", file);
|
|
142
218
|
};
|
|
@@ -188,7 +264,22 @@ export const useUploader = (_options = {}) => {
|
|
|
188
264
|
updateFile(file.id, { progress: { percentage: progress } });
|
|
189
265
|
emitter.emit("upload:progress", { file, progress });
|
|
190
266
|
};
|
|
191
|
-
const
|
|
267
|
+
const storagePlugin = getStoragePlugin();
|
|
268
|
+
let uploadResult;
|
|
269
|
+
if (storagePlugin?.hooks.upload) {
|
|
270
|
+
const context = {
|
|
271
|
+
files: files.value,
|
|
272
|
+
options,
|
|
273
|
+
onProgress,
|
|
274
|
+
emit: (event, payload) => {
|
|
275
|
+
const prefixedEvent = `${storagePlugin.id}:${String(event)}`;
|
|
276
|
+
emitter.emit(prefixedEvent, payload);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
uploadResult = await storagePlugin.hooks.upload(file, context);
|
|
280
|
+
} else {
|
|
281
|
+
uploadResult = await uploadFn(file, onProgress);
|
|
282
|
+
}
|
|
192
283
|
updateFile(file.id, { status: "complete", uploadResult });
|
|
193
284
|
} catch (err) {
|
|
194
285
|
const error = {
|
|
@@ -225,12 +316,16 @@ export const useUploader = (_options = {}) => {
|
|
|
225
316
|
};
|
|
226
317
|
async function runPluginStage(stage, file) {
|
|
227
318
|
if (!options.plugins) return file;
|
|
228
|
-
const context = { files: files.value, options };
|
|
229
319
|
let currentFile = file;
|
|
230
320
|
for (const plugin of options.plugins) {
|
|
231
321
|
const hook = plugin.hooks[stage];
|
|
232
322
|
if (hook) {
|
|
233
323
|
try {
|
|
324
|
+
const context = {
|
|
325
|
+
files: files.value,
|
|
326
|
+
options,
|
|
327
|
+
emit: getPluginEmitFn(plugin.id)
|
|
328
|
+
};
|
|
234
329
|
const result = await callPluginHook(hook, stage, currentFile, context);
|
|
235
330
|
if (!result) continue;
|
|
236
331
|
if (currentFile && "id" in result) {
|
|
@@ -249,7 +344,7 @@ export const useUploader = (_options = {}) => {
|
|
|
249
344
|
}
|
|
250
345
|
return {
|
|
251
346
|
// State
|
|
252
|
-
files
|
|
347
|
+
files,
|
|
253
348
|
totalProgress,
|
|
254
349
|
// Core Methods
|
|
255
350
|
addFiles,
|
|
@@ -268,7 +363,7 @@ export const useUploader = (_options = {}) => {
|
|
|
268
363
|
initializeExistingFiles,
|
|
269
364
|
// Utilities
|
|
270
365
|
addPlugin,
|
|
271
|
-
// Events
|
|
366
|
+
// Events - autocomplete for core events, allow arbitrary strings for plugin events
|
|
272
367
|
on: emitter.on
|
|
273
368
|
};
|
|
274
369
|
};
|
package/dist/runtime/composables/{useUploader → useUploadManager}/plugins/image-compressor.d.ts
RENAMED
|
@@ -1,4 +1,25 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { UploadFile } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Events emitted by the image compressor plugin
|
|
4
|
+
* Note: These are automatically prefixed with "image-compressor:" by the plugin system
|
|
5
|
+
* e.g., "start" becomes "image-compressor:start"
|
|
6
|
+
*/
|
|
7
|
+
type ImageCompressorEvents = {
|
|
8
|
+
start: {
|
|
9
|
+
file: UploadFile;
|
|
10
|
+
originalSize: number;
|
|
11
|
+
};
|
|
12
|
+
complete: {
|
|
13
|
+
file: UploadFile;
|
|
14
|
+
originalSize: number;
|
|
15
|
+
compressedSize: number;
|
|
16
|
+
savedBytes: number;
|
|
17
|
+
};
|
|
18
|
+
skip: {
|
|
19
|
+
file: UploadFile;
|
|
20
|
+
reason: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
2
23
|
interface ImageCompressorOptions {
|
|
3
24
|
/**
|
|
4
25
|
* Maximum width in pixels. Images wider than this will be scaled down.
|
|
@@ -31,19 +52,5 @@ interface ImageCompressorOptions {
|
|
|
31
52
|
*/
|
|
32
53
|
preserveMetadata?: boolean;
|
|
33
54
|
}
|
|
34
|
-
|
|
35
|
-
* Compresses images before upload to reduce bandwidth and storage costs.
|
|
36
|
-
* Particularly useful for social media scheduling where images are often large.
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```ts
|
|
40
|
-
* const uploader = useUpload()
|
|
41
|
-
* uploader.addPlugin(PluginImageCompressor, {
|
|
42
|
-
* maxWidth: 2048,
|
|
43
|
-
* quality: 0.85,
|
|
44
|
-
* outputFormat: 'webp'
|
|
45
|
-
* })
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
|
-
export declare const PluginImageCompressor: PluginFn<ImageCompressorOptions>;
|
|
55
|
+
export declare const PluginImageCompressor: (options: ImageCompressorOptions) => import("../types.js").Plugin<any, ImageCompressorEvents>;
|
|
49
56
|
export {};
|
package/dist/runtime/composables/{useUploader → useUploadManager}/plugins/image-compressor.js
RENAMED
|
@@ -1,29 +1,36 @@
|
|
|
1
|
-
|
|
1
|
+
import { defineUploaderPlugin } from "../types.js";
|
|
2
|
+
export const PluginImageCompressor = defineUploaderPlugin((pluginOptions) => {
|
|
2
3
|
const {
|
|
3
4
|
maxWidth = 1920,
|
|
4
5
|
maxHeight = 1920,
|
|
5
6
|
quality = 0.85,
|
|
6
7
|
outputFormat = "auto",
|
|
7
|
-
minSizeToCompress = 1e5
|
|
8
|
+
minSizeToCompress = 1e5
|
|
8
9
|
// 100KB
|
|
9
|
-
preserveMetadata = true
|
|
10
10
|
} = pluginOptions;
|
|
11
11
|
return {
|
|
12
12
|
id: "image-compressor",
|
|
13
13
|
hooks: {
|
|
14
|
-
process: async (file) => {
|
|
14
|
+
process: async (file, context) => {
|
|
15
15
|
if (!file.mimeType.startsWith("image/")) {
|
|
16
16
|
return file;
|
|
17
17
|
}
|
|
18
18
|
if (file.mimeType === "image/gif") {
|
|
19
|
+
context.emit("skip", { file, reason: "GIF format not supported" });
|
|
19
20
|
return file;
|
|
20
21
|
}
|
|
21
22
|
if (file.mimeType === "image/svg+xml") {
|
|
23
|
+
context.emit("skip", { file, reason: "SVG already optimized" });
|
|
22
24
|
return file;
|
|
23
25
|
}
|
|
24
26
|
if (file.size < minSizeToCompress) {
|
|
27
|
+
context.emit("skip", {
|
|
28
|
+
file,
|
|
29
|
+
reason: `File size (${file.size} bytes) below minimum threshold`
|
|
30
|
+
});
|
|
25
31
|
return file;
|
|
26
32
|
}
|
|
33
|
+
context.emit("start", { file, originalSize: file.size });
|
|
27
34
|
try {
|
|
28
35
|
const sourceUrl = URL.createObjectURL(file.data);
|
|
29
36
|
const image = new Image();
|
|
@@ -35,6 +42,10 @@ export const PluginImageCompressor = (_, pluginOptions) => {
|
|
|
35
42
|
const needsResize = image.width > maxWidth || image.height > maxHeight;
|
|
36
43
|
if (!needsResize && outputFormat === "auto") {
|
|
37
44
|
URL.revokeObjectURL(sourceUrl);
|
|
45
|
+
context.emit("skip", {
|
|
46
|
+
file,
|
|
47
|
+
reason: "Image within size limits and format is auto"
|
|
48
|
+
});
|
|
38
49
|
return file;
|
|
39
50
|
}
|
|
40
51
|
let targetWidth = image.width;
|
|
@@ -79,6 +90,13 @@ export const PluginImageCompressor = (_, pluginOptions) => {
|
|
|
79
90
|
});
|
|
80
91
|
URL.revokeObjectURL(sourceUrl);
|
|
81
92
|
if (compressedBlob.size < file.size) {
|
|
93
|
+
const savedBytes = file.size - compressedBlob.size;
|
|
94
|
+
context.emit("complete", {
|
|
95
|
+
file,
|
|
96
|
+
originalSize: file.size,
|
|
97
|
+
compressedSize: compressedBlob.size,
|
|
98
|
+
savedBytes
|
|
99
|
+
});
|
|
82
100
|
let newId = file.id;
|
|
83
101
|
if (outputFormat !== "auto" && outputFormat !== file.meta.extension) {
|
|
84
102
|
const extension = outputFormat === "jpeg" ? "jpg" : outputFormat;
|
|
@@ -100,6 +118,10 @@ export const PluginImageCompressor = (_, pluginOptions) => {
|
|
|
100
118
|
}
|
|
101
119
|
};
|
|
102
120
|
}
|
|
121
|
+
context.emit("skip", {
|
|
122
|
+
file,
|
|
123
|
+
reason: "Compressed version larger than original"
|
|
124
|
+
});
|
|
103
125
|
return file;
|
|
104
126
|
} catch (error) {
|
|
105
127
|
console.warn(`Image compression failed for ${file.name}:`, error);
|
|
@@ -108,4 +130,4 @@ export const PluginImageCompressor = (_, pluginOptions) => {
|
|
|
108
130
|
}
|
|
109
131
|
}
|
|
110
132
|
};
|
|
111
|
-
};
|
|
133
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
type PathHttpHeaders = import("@azure/storage-file-datalake").PathHttpHeaders;
|
|
2
|
+
export interface AzureDataLakeOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Static SAS URL for Azure Data Lake Storage
|
|
5
|
+
*/
|
|
6
|
+
sasURL?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Function to dynamically fetch SAS URL
|
|
9
|
+
* Use this to handle token expiration/refreshing.
|
|
10
|
+
* If provided, it will be called before every file operation.
|
|
11
|
+
*/
|
|
12
|
+
getSASUrl?: () => Promise<string>;
|
|
13
|
+
/**
|
|
14
|
+
* Optional subdirectory path within the container
|
|
15
|
+
* @example "uploads/images"
|
|
16
|
+
*/
|
|
17
|
+
path?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Custom metadata to attach to uploaded files
|
|
20
|
+
*/
|
|
21
|
+
metadata?: Record<string, string>;
|
|
22
|
+
/**
|
|
23
|
+
* Custom HTTP headers for uploaded files
|
|
24
|
+
*/
|
|
25
|
+
pathHttpHeaders?: Omit<PathHttpHeaders, "contentType">;
|
|
26
|
+
/**
|
|
27
|
+
* Automatically try to create the directory if it doesn't exist.
|
|
28
|
+
* Disable this if your SAS token only has 'Write' (Blob) permissions
|
|
29
|
+
* and not 'Create' (Directory) permissions.
|
|
30
|
+
* @default true
|
|
31
|
+
*/
|
|
32
|
+
autoCreateDirectory?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface AzureUploadResult {
|
|
35
|
+
/**
|
|
36
|
+
* Full URL to the uploaded file
|
|
37
|
+
*/
|
|
38
|
+
url: string;
|
|
39
|
+
/**
|
|
40
|
+
* File name/path in the storage
|
|
41
|
+
*/
|
|
42
|
+
blobPath: string;
|
|
43
|
+
}
|
|
44
|
+
export declare const PluginAzureDataLake: (options: AzureDataLakeOptions) => import("../../types.js").Plugin<any, Record<string, never>>;
|
|
45
|
+
export {};
|