@eslamdevui/ui 3.3.2-beta.1 → 3.3.2-beta.2
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/.nuxt/ui/file-upload.ts +37 -8
- package/dist/module.json +1 -1
- package/dist/module.mjs +2 -2
- package/dist/runtime/components/FileUpload.vue +262 -34
- package/dist/runtime/components/FileUpload.vue.d.ts +208 -10
- package/dist/shared/{ui.CgUuvRXi.mjs → ui.DdcmBFzX.mjs} +35 -8
- package/dist/unplugin.mjs +1 -1
- package/dist/vite.mjs +1 -1
- package/package.json +1 -1
package/.nuxt/ui/file-upload.ts
CHANGED
|
@@ -50,7 +50,13 @@ export default {
|
|
|
50
50
|
"fileWrapper": "flex flex-col min-w-0",
|
|
51
51
|
"fileName": "text-default truncate",
|
|
52
52
|
"fileSize": "text-muted truncate",
|
|
53
|
-
"
|
|
53
|
+
"fileProgress": "flex items-center gap-2 mt-1",
|
|
54
|
+
"fileProgressText": "text-xs text-muted whitespace-nowrap",
|
|
55
|
+
"fileError": "text-xs text-red-500 mt-1",
|
|
56
|
+
"fileActions": "flex items-center gap-1",
|
|
57
|
+
"fileTrailingButton": "",
|
|
58
|
+
"fileUploadButton": "",
|
|
59
|
+
"fileCancelButton": ""
|
|
54
60
|
},
|
|
55
61
|
"variants": {
|
|
56
62
|
"color": {
|
|
@@ -104,12 +110,15 @@ export default {
|
|
|
104
110
|
"root": "gap-2 items-start",
|
|
105
111
|
"files": "flex flex-col w-full gap-2",
|
|
106
112
|
"file": "min-w-0 flex items-center border border-default rounded-md w-full",
|
|
107
|
-
"
|
|
113
|
+
"fileActions": "ms-auto"
|
|
108
114
|
},
|
|
109
115
|
"grid": {
|
|
110
116
|
"fileWrapper": "hidden",
|
|
111
117
|
"fileLeadingAvatar": "size-full rounded-lg",
|
|
112
|
-
"
|
|
118
|
+
"fileActions": "absolute -top-1.5 -end-1.5",
|
|
119
|
+
"fileTrailingButton": "p-0 rounded-full border-2 border-bg",
|
|
120
|
+
"fileUploadButton": "p-0 rounded-full border-2 border-bg",
|
|
121
|
+
"fileCancelButton": "p-0 rounded-full border-2 border-bg"
|
|
113
122
|
}
|
|
114
123
|
},
|
|
115
124
|
"position": {
|
|
@@ -130,6 +139,9 @@ export default {
|
|
|
130
139
|
},
|
|
131
140
|
"disabled": {
|
|
132
141
|
"true": "cursor-not-allowed opacity-75"
|
|
142
|
+
},
|
|
143
|
+
"showProgress": {
|
|
144
|
+
"true": ""
|
|
133
145
|
}
|
|
134
146
|
},
|
|
135
147
|
"compoundVariants": [
|
|
@@ -200,35 +212,35 @@ export default {
|
|
|
200
212
|
"size": "xs" as typeof size[number],
|
|
201
213
|
"layout": "list" as typeof layout[number],
|
|
202
214
|
"class": {
|
|
203
|
-
"
|
|
215
|
+
"fileActions": "-me-1"
|
|
204
216
|
}
|
|
205
217
|
},
|
|
206
218
|
{
|
|
207
219
|
"size": "sm" as typeof size[number],
|
|
208
220
|
"layout": "list" as typeof layout[number],
|
|
209
221
|
"class": {
|
|
210
|
-
"
|
|
222
|
+
"fileActions": "-me-1.5"
|
|
211
223
|
}
|
|
212
224
|
},
|
|
213
225
|
{
|
|
214
226
|
"size": "md" as typeof size[number],
|
|
215
227
|
"layout": "list" as typeof layout[number],
|
|
216
228
|
"class": {
|
|
217
|
-
"
|
|
229
|
+
"fileActions": "-me-1.5"
|
|
218
230
|
}
|
|
219
231
|
},
|
|
220
232
|
{
|
|
221
233
|
"size": "lg" as typeof size[number],
|
|
222
234
|
"layout": "list" as typeof layout[number],
|
|
223
235
|
"class": {
|
|
224
|
-
"
|
|
236
|
+
"fileActions": "-me-2"
|
|
225
237
|
}
|
|
226
238
|
},
|
|
227
239
|
{
|
|
228
240
|
"size": "xl" as typeof size[number],
|
|
229
241
|
"layout": "list" as typeof layout[number],
|
|
230
242
|
"class": {
|
|
231
|
-
"
|
|
243
|
+
"fileActions": "-me-2"
|
|
232
244
|
}
|
|
233
245
|
},
|
|
234
246
|
{
|
|
@@ -285,6 +297,23 @@ export default {
|
|
|
285
297
|
"interactive": true,
|
|
286
298
|
"disabled": false,
|
|
287
299
|
"class": "hover:bg-elevated/25"
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
"showProgress": true,
|
|
303
|
+
"layout": "list" as typeof layout[number],
|
|
304
|
+
"class": {
|
|
305
|
+
"fileWrapper": "flex-col",
|
|
306
|
+
"fileProgress": "w-full"
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"showProgress": true,
|
|
311
|
+
"layout": "grid" as typeof layout[number],
|
|
312
|
+
"class": {
|
|
313
|
+
"fileWrapper": "absolute inset-0 bg-black/50 flex flex-col items-center justify-center rounded-lg",
|
|
314
|
+
"fileProgress": "w-3/4",
|
|
315
|
+
"fileProgressText": "text-white font-medium"
|
|
316
|
+
}
|
|
288
317
|
}
|
|
289
318
|
],
|
|
290
319
|
"defaultVariants": {
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineNuxtModule, createResolver, addVitePlugin, addPlugin, hasNuxtModule, addComponentsDir, addImportsDir, installModule } from '@nuxt/kit';
|
|
2
2
|
import { defu } from 'defu';
|
|
3
|
-
import { d as defaultOptions, r as resolveColors, a as getDefaultUiConfig, b as addTemplates } from './shared/ui.
|
|
3
|
+
import { d as defaultOptions, r as resolveColors, a as getDefaultUiConfig, b as addTemplates } from './shared/ui.DdcmBFzX.mjs';
|
|
4
4
|
import '../dist/runtime/utils/index.js';
|
|
5
5
|
import 'node:url';
|
|
6
6
|
import 'scule';
|
|
@@ -8,7 +8,7 @@ import 'tailwindcss/colors';
|
|
|
8
8
|
import 'knitwork';
|
|
9
9
|
|
|
10
10
|
const name = "@eslamdevui/ui";
|
|
11
|
-
const version = "3.3.2-beta.
|
|
11
|
+
const version = "3.3.2-beta.2";
|
|
12
12
|
|
|
13
13
|
function generateProseComponentMap(components) {
|
|
14
14
|
return components.reduce((map, component) => {
|
|
@@ -3,7 +3,7 @@ import theme from "#build/ui/file-upload";
|
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<script setup>
|
|
6
|
-
import { computed, watch } from "vue";
|
|
6
|
+
import { computed, watch, ref, nextTick, readonly } from "vue";
|
|
7
7
|
import { Primitive } from "reka-ui";
|
|
8
8
|
import { createReusableTemplate } from "@vueuse/core";
|
|
9
9
|
import { useAppConfig } from "#imports";
|
|
@@ -13,6 +13,7 @@ import { tv } from "../utils/tv";
|
|
|
13
13
|
import UAvatar from "./Avatar.vue";
|
|
14
14
|
import UButton from "./Button.vue";
|
|
15
15
|
import UIcon from "./Icon.vue";
|
|
16
|
+
import UProgress from "./Progress.vue";
|
|
16
17
|
defineOptions({ inheritAttrs: false });
|
|
17
18
|
const props = defineProps({
|
|
18
19
|
as: { type: null, required: false },
|
|
@@ -37,20 +38,28 @@ const props = defineProps({
|
|
|
37
38
|
fileIcon: { type: String, required: false },
|
|
38
39
|
fileDelete: { type: [Boolean, Object], required: false },
|
|
39
40
|
fileDeleteIcon: { type: String, required: false },
|
|
41
|
+
showProgress: { type: Boolean, required: false, default: false },
|
|
42
|
+
allowCancel: { type: Boolean, required: false, default: true },
|
|
43
|
+
uploadFn: { type: Function, required: false },
|
|
44
|
+
autoUpload: { type: Boolean, required: false, default: false },
|
|
45
|
+
uploadTimeout: { type: Number, required: false, default: 3e4 },
|
|
46
|
+
maxConcurrentUploads: { type: Number, required: false, default: 3 },
|
|
40
47
|
class: { type: null, required: false },
|
|
41
48
|
ui: { type: null, required: false }
|
|
42
49
|
});
|
|
43
|
-
const emits = defineEmits(["update:modelValue", "change"]);
|
|
50
|
+
const emits = defineEmits(["update:modelValue", "change", "upload:start", "upload:progress", "upload:success", "upload:error", "upload:cancel", "upload:complete"]);
|
|
44
51
|
const slots = defineSlots();
|
|
45
52
|
const modelValue = defineModel({ type: null });
|
|
46
53
|
const appConfig = useAppConfig();
|
|
54
|
+
const activeUploads = ref(0);
|
|
55
|
+
const uploadQueue = ref([]);
|
|
47
56
|
const [DefineFilesTemplate, ReuseFilesTemplate] = createReusableTemplate();
|
|
48
57
|
const { isDragging, open, inputRef, dropzoneRef } = useFileUpload({
|
|
49
58
|
accept: props.accept,
|
|
50
59
|
reset: props.reset,
|
|
51
60
|
multiple: props.multiple,
|
|
52
61
|
dropzone: props.dropzone,
|
|
53
|
-
onUpdate
|
|
62
|
+
onUpdate: onFileUpdate
|
|
54
63
|
});
|
|
55
64
|
const { emitFormInput, emitFormChange, id, name, disabled, ariaAttrs } = useFormField(props);
|
|
56
65
|
const variant = computed(() => props.multiple ? "area" : props.variant);
|
|
@@ -64,6 +73,9 @@ const position = computed(() => {
|
|
|
64
73
|
}
|
|
65
74
|
return props.position;
|
|
66
75
|
});
|
|
76
|
+
const shouldShowProgress = computed(() => {
|
|
77
|
+
return props.showProgress || props.autoUpload;
|
|
78
|
+
});
|
|
67
79
|
const ui = computed(() => tv({ extend: tv(theme), ...appConfig.ui?.fileUpload || {} })({
|
|
68
80
|
dropzone: props.dropzone,
|
|
69
81
|
interactive: props.interactive,
|
|
@@ -74,7 +86,8 @@ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.ui?.fileUpload ||
|
|
|
74
86
|
position: position.value,
|
|
75
87
|
multiple: props.multiple,
|
|
76
88
|
highlight: props.highlight,
|
|
77
|
-
disabled: props.disabled
|
|
89
|
+
disabled: props.disabled,
|
|
90
|
+
showProgress: shouldShowProgress.value
|
|
78
91
|
}));
|
|
79
92
|
function createObjectUrl(file) {
|
|
80
93
|
return URL.createObjectURL(file);
|
|
@@ -90,77 +103,265 @@ function formatFileSize(bytes) {
|
|
|
90
103
|
const formattedSize = i === 0 ? size.toString() : size.toFixed(0);
|
|
91
104
|
return `${formattedSize}${sizes[i]}`;
|
|
92
105
|
}
|
|
93
|
-
function
|
|
106
|
+
function createFileUploadObject(file) {
|
|
107
|
+
return {
|
|
108
|
+
file,
|
|
109
|
+
progress: 0,
|
|
110
|
+
uploading: false,
|
|
111
|
+
error: void 0,
|
|
112
|
+
abortController: void 0
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
async function onFileUpdate(files, reset = false) {
|
|
116
|
+
const fileUploads = files.map(createFileUploadObject);
|
|
94
117
|
if (props.multiple) {
|
|
95
118
|
if (reset) {
|
|
96
|
-
modelValue.value =
|
|
119
|
+
modelValue.value = fileUploads;
|
|
97
120
|
} else {
|
|
98
121
|
const existingFiles = modelValue.value || [];
|
|
99
|
-
modelValue.value = [...existingFiles, ...
|
|
122
|
+
modelValue.value = [...existingFiles, ...fileUploads];
|
|
100
123
|
}
|
|
101
124
|
} else {
|
|
102
|
-
modelValue.value =
|
|
125
|
+
modelValue.value = fileUploads?.[0];
|
|
103
126
|
}
|
|
104
127
|
const event = new Event("change", { target: { value: modelValue.value } });
|
|
105
128
|
emits("change", event);
|
|
106
129
|
emitFormChange();
|
|
107
130
|
emitFormInput();
|
|
131
|
+
if (props.autoUpload && props.uploadFn) {
|
|
132
|
+
await nextTick();
|
|
133
|
+
const newFiles = props.multiple ? fileUploads : fileUploads[0] ? [fileUploads[0]] : [];
|
|
134
|
+
for (let i = 0; i < newFiles.length; i++) {
|
|
135
|
+
const actualIndex = props.multiple && !reset ? (modelValue.value?.length || 0) - newFiles.length + i : i;
|
|
136
|
+
if (props.multiple) {
|
|
137
|
+
if (newFiles[i]) {
|
|
138
|
+
queueUpload(actualIndex, newFiles[i]);
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
uploadFile(actualIndex);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function queueUpload(index, fileUpload) {
|
|
147
|
+
if (!props.multiple) {
|
|
148
|
+
uploadFile(index);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (activeUploads.value < props.maxConcurrentUploads) {
|
|
152
|
+
uploadFile(index);
|
|
153
|
+
} else {
|
|
154
|
+
uploadQueue.value.push({ index, fileUpload });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function processUploadQueue() {
|
|
158
|
+
while (uploadQueue.value.length > 0 && activeUploads.value < props.maxConcurrentUploads) {
|
|
159
|
+
const queueItem = uploadQueue.value.shift();
|
|
160
|
+
if (queueItem) {
|
|
161
|
+
uploadFile(queueItem.index);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
108
164
|
}
|
|
109
165
|
function removeFile(index) {
|
|
110
166
|
if (!modelValue.value) {
|
|
111
167
|
return;
|
|
112
168
|
}
|
|
113
169
|
if (!props.multiple || index === void 0) {
|
|
114
|
-
|
|
170
|
+
const singleFile = modelValue.value;
|
|
171
|
+
if (singleFile.abortController) {
|
|
172
|
+
singleFile.abortController.abort();
|
|
173
|
+
}
|
|
174
|
+
onFileUpdate([], true);
|
|
115
175
|
return;
|
|
116
176
|
}
|
|
117
177
|
const files = [...modelValue.value];
|
|
178
|
+
const file = files[index];
|
|
179
|
+
if (file?.abortController) {
|
|
180
|
+
file.abortController.abort();
|
|
181
|
+
}
|
|
182
|
+
uploadQueue.value = uploadQueue.value.filter((item) => item.index !== index);
|
|
118
183
|
files.splice(index, 1);
|
|
119
|
-
|
|
184
|
+
onFileUpdate(files.map((f) => f.file), true);
|
|
185
|
+
}
|
|
186
|
+
async function uploadFile(index) {
|
|
187
|
+
if (!props.uploadFn || !modelValue.value) return;
|
|
188
|
+
const files = props.multiple ? modelValue.value : [modelValue.value];
|
|
189
|
+
const fileUpload = files[index];
|
|
190
|
+
if (!fileUpload || fileUpload.uploading) return;
|
|
191
|
+
activeUploads.value++;
|
|
192
|
+
const abortController = new AbortController();
|
|
193
|
+
fileUpload.abortController = abortController;
|
|
194
|
+
fileUpload.uploading = true;
|
|
195
|
+
fileUpload.progress = 0;
|
|
196
|
+
fileUpload.error = void 0;
|
|
197
|
+
emits("upload:start", fileUpload, index);
|
|
198
|
+
const timeoutId = setTimeout(() => {
|
|
199
|
+
if (!abortController.signal.aborted) {
|
|
200
|
+
abortController.abort();
|
|
201
|
+
}
|
|
202
|
+
}, props.uploadTimeout);
|
|
203
|
+
try {
|
|
204
|
+
const result = await props.uploadFn(
|
|
205
|
+
fileUpload.file,
|
|
206
|
+
(progress) => {
|
|
207
|
+
fileUpload.progress = Math.min(100, Math.max(0, progress));
|
|
208
|
+
emits("upload:progress", fileUpload, fileUpload.progress, index);
|
|
209
|
+
},
|
|
210
|
+
abortController.signal
|
|
211
|
+
);
|
|
212
|
+
clearTimeout(timeoutId);
|
|
213
|
+
fileUpload.uploading = false;
|
|
214
|
+
fileUpload.progress = 100;
|
|
215
|
+
emits("upload:success", fileUpload, result, index);
|
|
216
|
+
} catch (error) {
|
|
217
|
+
clearTimeout(timeoutId);
|
|
218
|
+
fileUpload.uploading = false;
|
|
219
|
+
if (abortController.signal.aborted) {
|
|
220
|
+
emits("upload:cancel", fileUpload, index);
|
|
221
|
+
} else {
|
|
222
|
+
fileUpload.error = error instanceof Error ? error.message : "Upload failed";
|
|
223
|
+
emits("upload:error", fileUpload, fileUpload.error, index);
|
|
224
|
+
}
|
|
225
|
+
} finally {
|
|
226
|
+
fileUpload.abortController = void 0;
|
|
227
|
+
activeUploads.value--;
|
|
228
|
+
if (props.multiple) {
|
|
229
|
+
processUploadQueue();
|
|
230
|
+
}
|
|
231
|
+
if (props.multiple && activeUploads.value === 0 && uploadQueue.value.length === 0) {
|
|
232
|
+
emits("upload:complete", files);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function cancelUpload(index) {
|
|
237
|
+
if (!modelValue.value) return;
|
|
238
|
+
const files = props.multiple ? modelValue.value : [modelValue.value];
|
|
239
|
+
const fileUpload = files[index];
|
|
240
|
+
if (fileUpload?.abortController) {
|
|
241
|
+
fileUpload.abortController.abort();
|
|
242
|
+
}
|
|
243
|
+
uploadQueue.value = uploadQueue.value.filter((item) => item.index !== index);
|
|
244
|
+
}
|
|
245
|
+
function shouldShowFileProgress(fileUpload) {
|
|
246
|
+
return shouldShowProgress.value && (fileUpload.uploading || (fileUpload.progress ?? 0) > 0 || !!fileUpload.error);
|
|
120
247
|
}
|
|
121
248
|
watch(modelValue, (newValue) => {
|
|
122
249
|
const hasModelReset = !Array.isArray(newValue) || !newValue.length;
|
|
123
250
|
if (hasModelReset && inputRef.value) {
|
|
124
251
|
inputRef.value.value = "";
|
|
252
|
+
uploadQueue.value = [];
|
|
253
|
+
activeUploads.value = 0;
|
|
125
254
|
}
|
|
126
255
|
});
|
|
127
256
|
defineExpose({
|
|
128
257
|
inputRef,
|
|
129
|
-
dropzoneRef
|
|
258
|
+
dropzoneRef,
|
|
259
|
+
uploadFile,
|
|
260
|
+
cancelUpload,
|
|
261
|
+
activeUploads: readonly(activeUploads),
|
|
262
|
+
uploadQueue: readonly(uploadQueue)
|
|
130
263
|
});
|
|
131
264
|
</script>
|
|
132
265
|
|
|
133
266
|
<template>
|
|
134
267
|
<DefineFilesTemplate>
|
|
135
268
|
<template v-if="modelValue && (Array.isArray(modelValue) ? modelValue.length : true)">
|
|
136
|
-
<slot
|
|
269
|
+
<slot
|
|
270
|
+
name="files-top"
|
|
271
|
+
:files="modelValue"
|
|
272
|
+
:open="open"
|
|
273
|
+
:remove-file="removeFile"
|
|
274
|
+
:upload-file="uploadFile"
|
|
275
|
+
:cancel-upload="cancelUpload"
|
|
276
|
+
/>
|
|
137
277
|
|
|
138
278
|
<div :class="ui.files({ class: props.ui?.files })">
|
|
139
279
|
<slot name="files" :files="modelValue">
|
|
140
|
-
<div
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
280
|
+
<div
|
|
281
|
+
v-for="(fileUpload, index) in Array.isArray(modelValue) ? modelValue : [modelValue]"
|
|
282
|
+
:key="fileUpload.file.name + index"
|
|
283
|
+
:class="ui.file({ class: props.ui?.file })"
|
|
284
|
+
>
|
|
285
|
+
<slot name="file" :file-upload="fileUpload" :index="index">
|
|
286
|
+
<slot name="file-leading" :file-upload="fileUpload" :index="index">
|
|
287
|
+
<UAvatar
|
|
288
|
+
:src="createObjectUrl(fileUpload.file)"
|
|
289
|
+
:icon="fileIcon || appConfig.ui.icons.file"
|
|
290
|
+
:size="props.size"
|
|
291
|
+
:class="ui.fileLeadingAvatar({ class: props.ui?.fileLeadingAvatar })"
|
|
292
|
+
/>
|
|
144
293
|
</slot>
|
|
145
294
|
|
|
146
295
|
<div :class="ui.fileWrapper({ class: props.ui?.fileWrapper })">
|
|
147
296
|
<span :class="ui.fileName({ class: props.ui?.fileName })">
|
|
148
|
-
<slot name="file-name" :file="
|
|
149
|
-
{{ file.name }}
|
|
297
|
+
<slot name="file-name" :file-upload="fileUpload" :index="index">
|
|
298
|
+
{{ fileUpload.file.name }}
|
|
150
299
|
</slot>
|
|
151
300
|
</span>
|
|
152
301
|
|
|
153
302
|
<span :class="ui.fileSize({ class: props.ui?.fileSize })">
|
|
154
|
-
<slot name="file-size" :file="
|
|
155
|
-
{{ formatFileSize(file.size) }}
|
|
303
|
+
<slot name="file-size" :file-upload="fileUpload" :index="index">
|
|
304
|
+
{{ formatFileSize(fileUpload.file.size) }}
|
|
156
305
|
</slot>
|
|
157
306
|
</span>
|
|
307
|
+
|
|
308
|
+
<!-- Progress bar - Now shows automatically when autoUpload is true -->
|
|
309
|
+
<div
|
|
310
|
+
v-if="shouldShowFileProgress(fileUpload)"
|
|
311
|
+
:class="ui.fileProgress({ class: props.ui?.fileProgress })"
|
|
312
|
+
>
|
|
313
|
+
<slot name="file-progress" :file-upload="fileUpload" :index="index">
|
|
314
|
+
<UProgress
|
|
315
|
+
:model-value="fileUpload.progress || 0"
|
|
316
|
+
:color="fileUpload.error ? 'error' : color"
|
|
317
|
+
:size="size === 'xs' ? 'xs' : 'sm'"
|
|
318
|
+
/>
|
|
319
|
+
<span :class="ui.fileProgressText({ class: props.ui?.fileProgressText })">
|
|
320
|
+
{{ Math.round(fileUpload.progress || 0) }}%
|
|
321
|
+
</span>
|
|
322
|
+
</slot>
|
|
323
|
+
</div>
|
|
324
|
+
|
|
325
|
+
<!-- Error message -->
|
|
326
|
+
<div v-if="fileUpload.error" :class="ui.fileError({ class: props.ui?.fileError })">
|
|
327
|
+
{{ fileUpload.error }}
|
|
328
|
+
</div>
|
|
329
|
+
|
|
330
|
+
<!-- Upload status indicator -->
|
|
331
|
+
<div v-if="autoUpload && fileUpload.uploading" class="text-xs text-gray-500 mt-1">
|
|
332
|
+
Uploading...
|
|
333
|
+
</div>
|
|
158
334
|
</div>
|
|
159
335
|
|
|
160
|
-
<slot name="file-trailing" :file="
|
|
161
|
-
<
|
|
162
|
-
|
|
163
|
-
|
|
336
|
+
<slot name="file-trailing" :file-upload="fileUpload" :index="index">
|
|
337
|
+
<div :class="ui.fileActions({ class: props.ui?.fileActions })">
|
|
338
|
+
<!-- Upload button (if not auto-upload and uploadFn provided) -->
|
|
339
|
+
<UButton
|
|
340
|
+
v-if="uploadFn && !autoUpload && !fileUpload.uploading && !(fileUpload.progress ?? 0)"
|
|
341
|
+
color="primary"
|
|
342
|
+
variant="ghost"
|
|
343
|
+
:size="layout === 'grid' ? 'xs' : size"
|
|
344
|
+
:trailing-icon="appConfig.ui.icons.upload || 'i-lucide-upload'"
|
|
345
|
+
:class="ui.fileUploadButton({ class: props.ui?.fileUploadButton })"
|
|
346
|
+
@click.stop.prevent="uploadFile(index)"
|
|
347
|
+
/>
|
|
348
|
+
|
|
349
|
+
<!-- Cancel button (during upload) -->
|
|
350
|
+
<UButton
|
|
351
|
+
v-if="allowCancel && fileUpload.uploading"
|
|
352
|
+
color="neutral"
|
|
353
|
+
variant="ghost"
|
|
354
|
+
:size="layout === 'grid' ? 'xs' : size"
|
|
355
|
+
:trailing-icon="appConfig.ui.icons.close || 'i-lucide-x'"
|
|
356
|
+
:class="ui.fileCancelButton({ class: props.ui?.fileCancelButton })"
|
|
357
|
+
@click.stop.prevent="cancelUpload(index)"
|
|
358
|
+
/>
|
|
359
|
+
|
|
360
|
+
<!-- Delete button -->
|
|
361
|
+
<UButton
|
|
362
|
+
v-if="!fileUpload.uploading"
|
|
363
|
+
color="neutral"
|
|
364
|
+
v-bind="{
|
|
164
365
|
...layout === 'grid' ? {
|
|
165
366
|
variant: 'solid',
|
|
166
367
|
size: 'xs'
|
|
@@ -170,22 +371,30 @@ defineExpose({
|
|
|
170
371
|
},
|
|
171
372
|
...typeof fileDelete === 'object' ? fileDelete : void 0
|
|
172
373
|
}"
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
374
|
+
:trailing-icon="fileDeleteIcon || appConfig.ui.icons.close"
|
|
375
|
+
:class="ui.fileTrailingButton({ class: props.ui?.fileTrailingButton })"
|
|
376
|
+
@click.stop.prevent="removeFile(index)"
|
|
377
|
+
/>
|
|
378
|
+
</div>
|
|
177
379
|
</slot>
|
|
178
380
|
</slot>
|
|
179
381
|
</div>
|
|
180
382
|
</slot>
|
|
181
383
|
</div>
|
|
182
384
|
|
|
183
|
-
<slot
|
|
385
|
+
<slot
|
|
386
|
+
name="files-bottom"
|
|
387
|
+
:files="modelValue"
|
|
388
|
+
:open="open"
|
|
389
|
+
:remove-file="removeFile"
|
|
390
|
+
:upload-file="uploadFile"
|
|
391
|
+
:cancel-upload="cancelUpload"
|
|
392
|
+
/>
|
|
184
393
|
</template>
|
|
185
394
|
</DefineFilesTemplate>
|
|
186
395
|
|
|
187
396
|
<Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
|
|
188
|
-
<slot :open="open" :remove-file="removeFile">
|
|
397
|
+
<slot :open="open" :remove-file="removeFile" :upload-file="uploadFile" :cancel-upload="cancelUpload">
|
|
189
398
|
<component
|
|
190
399
|
:is="variant === 'button' ? 'button' : 'div'"
|
|
191
400
|
ref="dropzoneRef"
|
|
@@ -197,10 +406,22 @@ defineExpose({
|
|
|
197
406
|
>
|
|
198
407
|
<ReuseFilesTemplate v-if="position === 'inside'" />
|
|
199
408
|
|
|
200
|
-
<div
|
|
409
|
+
<div
|
|
410
|
+
v-if="position === 'inside' ? multiple ? !modelValue?.length : !modelValue : true"
|
|
411
|
+
:class="ui.wrapper({ class: props.ui?.wrapper })"
|
|
412
|
+
>
|
|
201
413
|
<slot name="leading">
|
|
202
|
-
<UIcon
|
|
203
|
-
|
|
414
|
+
<UIcon
|
|
415
|
+
v-if="variant === 'button'"
|
|
416
|
+
:name="icon || appConfig.ui.icons.upload"
|
|
417
|
+
:class="ui.icon({ class: props.ui?.icon })"
|
|
418
|
+
/>
|
|
419
|
+
<UAvatar
|
|
420
|
+
v-else
|
|
421
|
+
:icon="icon || appConfig.ui.icons.upload"
|
|
422
|
+
:size="props.size"
|
|
423
|
+
:class="ui.avatar({ class: props.ui?.avatar })"
|
|
424
|
+
/>
|
|
204
425
|
</slot>
|
|
205
426
|
|
|
206
427
|
<template v-if="variant !== 'button'">
|
|
@@ -216,7 +437,14 @@ defineExpose({
|
|
|
216
437
|
</div>
|
|
217
438
|
|
|
218
439
|
<div v-if="!!slots.actions" :class="ui.actions({ class: props.ui?.actions })">
|
|
219
|
-
<slot
|
|
440
|
+
<slot
|
|
441
|
+
name="actions"
|
|
442
|
+
:files="modelValue"
|
|
443
|
+
:open="open"
|
|
444
|
+
:remove-file="removeFile"
|
|
445
|
+
:upload-file="uploadFile"
|
|
446
|
+
:cancel-upload="cancelUpload"
|
|
447
|
+
/>
|
|
220
448
|
</div>
|
|
221
449
|
</template>
|
|
222
450
|
</div>
|
|
@@ -4,6 +4,13 @@ import theme from '#build/ui/file-upload';
|
|
|
4
4
|
import type { ButtonProps } from '../types';
|
|
5
5
|
import type { ComponentConfig } from '../types/utils';
|
|
6
6
|
type FileUpload = ComponentConfig<typeof theme, AppConfig, 'fileUpload'>;
|
|
7
|
+
export interface FileUploadFile {
|
|
8
|
+
file: File;
|
|
9
|
+
progress?: number;
|
|
10
|
+
uploading?: boolean;
|
|
11
|
+
error?: string;
|
|
12
|
+
abortController?: AbortController;
|
|
13
|
+
}
|
|
7
14
|
export interface FileUploadProps<M extends boolean = false> {
|
|
8
15
|
/**
|
|
9
16
|
* The element or component this component should render as.
|
|
@@ -89,18 +96,55 @@ export interface FileUploadProps<M extends boolean = false> {
|
|
|
89
96
|
* @IconifyIcon
|
|
90
97
|
*/
|
|
91
98
|
fileDeleteIcon?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Enable upload progress tracking
|
|
101
|
+
* @defaultValue false
|
|
102
|
+
*/
|
|
103
|
+
showProgress?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Allow cancelling uploads in progress
|
|
106
|
+
* @defaultValue true
|
|
107
|
+
*/
|
|
108
|
+
allowCancel?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Upload function that returns a promise with progress callback
|
|
111
|
+
*/
|
|
112
|
+
uploadFn?: (file: File, onProgress: (progress: number) => void, signal?: AbortSignal) => Promise<any>;
|
|
113
|
+
/**
|
|
114
|
+
* Auto-start upload when files are selected
|
|
115
|
+
* @defaultValue false
|
|
116
|
+
*/
|
|
117
|
+
autoUpload?: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Upload timeout in milliseconds
|
|
120
|
+
* @defaultValue 30000
|
|
121
|
+
*/
|
|
122
|
+
uploadTimeout?: number;
|
|
123
|
+
/**
|
|
124
|
+
* Max concurrent uploads when multiple is true
|
|
125
|
+
* @defaultValue 3
|
|
126
|
+
*/
|
|
127
|
+
maxConcurrentUploads?: number;
|
|
92
128
|
class?: any;
|
|
93
129
|
ui?: FileUpload['slots'];
|
|
94
130
|
}
|
|
95
131
|
export interface FileUploadEmits<M extends boolean = false> {
|
|
96
|
-
'update:modelValue': [payload: M extends true ?
|
|
132
|
+
'update:modelValue': [payload: M extends true ? FileUploadFile[] : FileUploadFile | null];
|
|
97
133
|
'change': [event: Event];
|
|
134
|
+
'upload:start': [file: FileUploadFile, index: number];
|
|
135
|
+
'upload:progress': [file: FileUploadFile, progress: number, index: number];
|
|
136
|
+
'upload:success': [file: FileUploadFile, result: any, index: number];
|
|
137
|
+
'upload:error': [file: FileUploadFile, error: string, index: number];
|
|
138
|
+
'upload:cancel': [file: FileUploadFile, index: number];
|
|
139
|
+
'upload:complete': [files: FileUploadFile[]];
|
|
98
140
|
}
|
|
99
|
-
type FileUploadFiles<M> = (M extends true ?
|
|
141
|
+
type FileUploadFiles<M> = (M extends true ? FileUploadFile[] : FileUploadFile) | null;
|
|
100
142
|
export interface FileUploadSlots<M extends boolean = false> {
|
|
101
143
|
'default'(props: {
|
|
102
144
|
open: UseFileDialogReturn['open'];
|
|
103
145
|
removeFile: (index?: number) => void;
|
|
146
|
+
uploadFile: (index: number) => void;
|
|
147
|
+
cancelUpload: (index: number) => void;
|
|
104
148
|
}): any;
|
|
105
149
|
'leading'(props?: {}): any;
|
|
106
150
|
'label'(props?: {}): any;
|
|
@@ -109,6 +153,8 @@ export interface FileUploadSlots<M extends boolean = false> {
|
|
|
109
153
|
files?: FileUploadFiles<M>;
|
|
110
154
|
open: UseFileDialogReturn['open'];
|
|
111
155
|
removeFile: (index?: number) => void;
|
|
156
|
+
uploadFile: (index: number) => void;
|
|
157
|
+
cancelUpload: (index: number) => void;
|
|
112
158
|
}): any;
|
|
113
159
|
'files'(props: {
|
|
114
160
|
files?: FileUploadFiles<M>;
|
|
@@ -117,30 +163,38 @@ export interface FileUploadSlots<M extends boolean = false> {
|
|
|
117
163
|
files?: FileUploadFiles<M>;
|
|
118
164
|
open: UseFileDialogReturn['open'];
|
|
119
165
|
removeFile: (index?: number) => void;
|
|
166
|
+
uploadFile: (index: number) => void;
|
|
167
|
+
cancelUpload: (index: number) => void;
|
|
120
168
|
}): any;
|
|
121
169
|
'files-bottom'(props: {
|
|
122
170
|
files?: FileUploadFiles<M>;
|
|
123
171
|
open: UseFileDialogReturn['open'];
|
|
124
172
|
removeFile: (index?: number) => void;
|
|
173
|
+
uploadFile: (index: number) => void;
|
|
174
|
+
cancelUpload: (index: number) => void;
|
|
125
175
|
}): any;
|
|
126
176
|
'file'(props: {
|
|
127
|
-
|
|
177
|
+
fileUpload: FileUploadFile;
|
|
128
178
|
index: number;
|
|
129
179
|
}): any;
|
|
130
180
|
'file-leading'(props: {
|
|
131
|
-
|
|
181
|
+
fileUpload: FileUploadFile;
|
|
132
182
|
index: number;
|
|
133
183
|
}): any;
|
|
134
184
|
'file-name'(props: {
|
|
135
|
-
|
|
185
|
+
fileUpload: FileUploadFile;
|
|
136
186
|
index: number;
|
|
137
187
|
}): any;
|
|
138
188
|
'file-size'(props: {
|
|
139
|
-
|
|
189
|
+
fileUpload: FileUploadFile;
|
|
190
|
+
index: number;
|
|
191
|
+
}): any;
|
|
192
|
+
'file-progress'(props: {
|
|
193
|
+
fileUpload: FileUploadFile;
|
|
140
194
|
index: number;
|
|
141
195
|
}): any;
|
|
142
196
|
'file-trailing'(props: {
|
|
143
|
-
|
|
197
|
+
fileUpload: FileUploadFile;
|
|
144
198
|
index: number;
|
|
145
199
|
}): any;
|
|
146
200
|
}
|
|
@@ -148,16 +202,160 @@ declare const _default: <M extends boolean = false>(__VLS_props: NonNullable<Awa
|
|
|
148
202
|
props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
|
|
149
203
|
readonly onChange?: ((event: Event) => any) | undefined;
|
|
150
204
|
readonly "onUpdate:modelValue"?: ((...args: unknown[]) => any) | undefined;
|
|
151
|
-
|
|
152
|
-
|
|
205
|
+
readonly "onUpload:start"?: ((file: FileUploadFile, index: number) => any) | undefined;
|
|
206
|
+
readonly "onUpload:progress"?: ((file: FileUploadFile, progress: number, index: number) => any) | undefined;
|
|
207
|
+
readonly "onUpload:success"?: ((file: FileUploadFile, result: any, index: number) => any) | undefined;
|
|
208
|
+
readonly "onUpload:error"?: ((file: FileUploadFile, error: string, index: number) => any) | undefined;
|
|
209
|
+
readonly "onUpload:cancel"?: ((file: FileUploadFile, index: number) => any) | undefined;
|
|
210
|
+
readonly "onUpload:complete"?: ((files: FileUploadFile[]) => any) | undefined;
|
|
211
|
+
} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onChange" | "onUpdate:modelValue" | "onUpload:start" | "onUpload:progress" | "onUpload:success" | "onUpload:error" | "onUpload:cancel" | "onUpload:complete"> & (FileUploadProps<M> & {
|
|
212
|
+
modelValue?: (M extends true ? FileUploadFile[] : FileUploadFile) | null;
|
|
153
213
|
}) & Partial<{}>> & import("vue").PublicProps;
|
|
154
214
|
expose(exposed: import("vue").ShallowUnwrapRef<{
|
|
155
215
|
inputRef: import("vue").Ref<HTMLInputElement | undefined, HTMLInputElement | undefined>;
|
|
156
216
|
dropzoneRef: import("vue").Ref<HTMLDivElement | undefined, HTMLDivElement | undefined>;
|
|
217
|
+
uploadFile: (index: number) => Promise<void>;
|
|
218
|
+
cancelUpload: (index: number) => void;
|
|
219
|
+
activeUploads: Readonly<import("vue").Ref<number, number>>;
|
|
220
|
+
uploadQueue: Readonly<import("vue").Ref<readonly {
|
|
221
|
+
readonly index: number;
|
|
222
|
+
readonly fileUpload: {
|
|
223
|
+
readonly file: {
|
|
224
|
+
readonly lastModified: number;
|
|
225
|
+
readonly name: string;
|
|
226
|
+
readonly webkitRelativePath: string;
|
|
227
|
+
readonly size: number;
|
|
228
|
+
readonly type: string;
|
|
229
|
+
readonly arrayBuffer: {
|
|
230
|
+
(): Promise<ArrayBuffer>;
|
|
231
|
+
(): Promise<ArrayBuffer>;
|
|
232
|
+
};
|
|
233
|
+
readonly bytes: {
|
|
234
|
+
(): Promise<Uint8Array>;
|
|
235
|
+
(): Promise<Uint8Array>;
|
|
236
|
+
};
|
|
237
|
+
readonly slice: {
|
|
238
|
+
(start?: number, end?: number, contentType?: string): Blob;
|
|
239
|
+
(start?: number, end?: number, contentType?: string): Blob;
|
|
240
|
+
};
|
|
241
|
+
readonly stream: {
|
|
242
|
+
(): ReadableStream<Uint8Array>;
|
|
243
|
+
(): ReadableStream<Uint8Array>;
|
|
244
|
+
};
|
|
245
|
+
readonly text: {
|
|
246
|
+
(): Promise<string>;
|
|
247
|
+
(): Promise<string>;
|
|
248
|
+
};
|
|
249
|
+
};
|
|
250
|
+
readonly progress?: number | undefined;
|
|
251
|
+
readonly uploading?: boolean | undefined;
|
|
252
|
+
readonly error?: string | undefined;
|
|
253
|
+
readonly abortController?: {
|
|
254
|
+
readonly signal: {
|
|
255
|
+
readonly aborted: boolean;
|
|
256
|
+
readonly onabort: ((this: AbortSignal, ev: Event) => any) | null;
|
|
257
|
+
readonly reason: any;
|
|
258
|
+
readonly throwIfAborted: {
|
|
259
|
+
(): void;
|
|
260
|
+
(): void;
|
|
261
|
+
(): void;
|
|
262
|
+
};
|
|
263
|
+
readonly addEventListener: {
|
|
264
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
265
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
266
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
267
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
268
|
+
};
|
|
269
|
+
readonly removeEventListener: {
|
|
270
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
271
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
272
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
273
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
274
|
+
};
|
|
275
|
+
readonly dispatchEvent: {
|
|
276
|
+
(event: Event): boolean;
|
|
277
|
+
(event: Event): boolean;
|
|
278
|
+
};
|
|
279
|
+
};
|
|
280
|
+
readonly abort: {
|
|
281
|
+
(reason?: any): void;
|
|
282
|
+
(reason?: any): void;
|
|
283
|
+
(reason?: any): void;
|
|
284
|
+
};
|
|
285
|
+
} | undefined;
|
|
286
|
+
};
|
|
287
|
+
}[], readonly {
|
|
288
|
+
readonly index: number;
|
|
289
|
+
readonly fileUpload: {
|
|
290
|
+
readonly file: {
|
|
291
|
+
readonly lastModified: number;
|
|
292
|
+
readonly name: string;
|
|
293
|
+
readonly webkitRelativePath: string;
|
|
294
|
+
readonly size: number;
|
|
295
|
+
readonly type: string;
|
|
296
|
+
readonly arrayBuffer: {
|
|
297
|
+
(): Promise<ArrayBuffer>;
|
|
298
|
+
(): Promise<ArrayBuffer>;
|
|
299
|
+
};
|
|
300
|
+
readonly bytes: {
|
|
301
|
+
(): Promise<Uint8Array>;
|
|
302
|
+
(): Promise<Uint8Array>;
|
|
303
|
+
};
|
|
304
|
+
readonly slice: {
|
|
305
|
+
(start?: number, end?: number, contentType?: string): Blob;
|
|
306
|
+
(start?: number, end?: number, contentType?: string): Blob;
|
|
307
|
+
};
|
|
308
|
+
readonly stream: {
|
|
309
|
+
(): ReadableStream<Uint8Array>;
|
|
310
|
+
(): ReadableStream<Uint8Array>;
|
|
311
|
+
};
|
|
312
|
+
readonly text: {
|
|
313
|
+
(): Promise<string>;
|
|
314
|
+
(): Promise<string>;
|
|
315
|
+
};
|
|
316
|
+
};
|
|
317
|
+
readonly progress?: number | undefined;
|
|
318
|
+
readonly uploading?: boolean | undefined;
|
|
319
|
+
readonly error?: string | undefined;
|
|
320
|
+
readonly abortController?: {
|
|
321
|
+
readonly signal: {
|
|
322
|
+
readonly aborted: boolean;
|
|
323
|
+
readonly onabort: ((this: AbortSignal, ev: Event) => any) | null;
|
|
324
|
+
readonly reason: any;
|
|
325
|
+
readonly throwIfAborted: {
|
|
326
|
+
(): void;
|
|
327
|
+
(): void;
|
|
328
|
+
(): void;
|
|
329
|
+
};
|
|
330
|
+
readonly addEventListener: {
|
|
331
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
332
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
333
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
334
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
335
|
+
};
|
|
336
|
+
readonly removeEventListener: {
|
|
337
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
338
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
339
|
+
<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
340
|
+
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
341
|
+
};
|
|
342
|
+
readonly dispatchEvent: {
|
|
343
|
+
(event: Event): boolean;
|
|
344
|
+
(event: Event): boolean;
|
|
345
|
+
};
|
|
346
|
+
};
|
|
347
|
+
readonly abort: {
|
|
348
|
+
(reason?: any): void;
|
|
349
|
+
(reason?: any): void;
|
|
350
|
+
(reason?: any): void;
|
|
351
|
+
};
|
|
352
|
+
} | undefined;
|
|
353
|
+
};
|
|
354
|
+
}[]>>;
|
|
157
355
|
}>): void;
|
|
158
356
|
attrs: any;
|
|
159
357
|
slots: FileUploadSlots<M>;
|
|
160
|
-
emit: (((evt: "change", event: Event) => void) & ((evt: "update:modelValue", payload: M extends true ?
|
|
358
|
+
emit: (((evt: "change", event: Event) => void) & ((evt: "update:modelValue", payload: M extends true ? FileUploadFile[] : FileUploadFile | null) => void) & ((evt: "upload:start", file: FileUploadFile, index: number) => void) & ((evt: "upload:progress", file: FileUploadFile, progress: number, index: number) => void) & ((evt: "upload:success", file: FileUploadFile, result: any, index: number) => void) & ((evt: "upload:error", file: FileUploadFile, error: string, index: number) => void) & ((evt: "upload:cancel", file: FileUploadFile, index: number) => void) & ((evt: "upload:complete", files: FileUploadFile[]) => void)) & ((evt: "update:modelValue", value: (M extends true ? FileUploadFile[] : FileUploadFile) | null | undefined) => void);
|
|
161
359
|
}>) => import("vue").VNode & {
|
|
162
360
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
163
361
|
};
|
|
@@ -2367,7 +2367,13 @@ const fileUpload = (options) => ({
|
|
|
2367
2367
|
fileWrapper: "flex flex-col min-w-0",
|
|
2368
2368
|
fileName: "text-default truncate",
|
|
2369
2369
|
fileSize: "text-muted truncate",
|
|
2370
|
-
|
|
2370
|
+
fileProgress: "flex items-center gap-2 mt-1",
|
|
2371
|
+
fileProgressText: "text-xs text-muted whitespace-nowrap",
|
|
2372
|
+
fileError: "text-xs text-red-500 mt-1",
|
|
2373
|
+
fileActions: "flex items-center gap-1",
|
|
2374
|
+
fileTrailingButton: "",
|
|
2375
|
+
fileUploadButton: "",
|
|
2376
|
+
fileCancelButton: ""
|
|
2371
2377
|
},
|
|
2372
2378
|
variants: {
|
|
2373
2379
|
color: {
|
|
@@ -2416,12 +2422,15 @@ const fileUpload = (options) => ({
|
|
|
2416
2422
|
root: "gap-2 items-start",
|
|
2417
2423
|
files: "flex flex-col w-full gap-2",
|
|
2418
2424
|
file: "min-w-0 flex items-center border border-default rounded-md w-full",
|
|
2419
|
-
|
|
2425
|
+
fileActions: "ms-auto"
|
|
2420
2426
|
},
|
|
2421
2427
|
grid: {
|
|
2422
2428
|
fileWrapper: "hidden",
|
|
2423
2429
|
fileLeadingAvatar: "size-full rounded-lg",
|
|
2424
|
-
|
|
2430
|
+
fileActions: "absolute -top-1.5 -end-1.5",
|
|
2431
|
+
fileTrailingButton: "p-0 rounded-full border-2 border-bg",
|
|
2432
|
+
fileUploadButton: "p-0 rounded-full border-2 border-bg",
|
|
2433
|
+
fileCancelButton: "p-0 rounded-full border-2 border-bg"
|
|
2425
2434
|
}
|
|
2426
2435
|
},
|
|
2427
2436
|
position: {
|
|
@@ -2442,6 +2451,9 @@ const fileUpload = (options) => ({
|
|
|
2442
2451
|
},
|
|
2443
2452
|
disabled: {
|
|
2444
2453
|
true: "cursor-not-allowed opacity-75"
|
|
2454
|
+
},
|
|
2455
|
+
showProgress: {
|
|
2456
|
+
true: ""
|
|
2445
2457
|
}
|
|
2446
2458
|
},
|
|
2447
2459
|
compoundVariants: [...(options.theme.colors || []).map((color) => ({
|
|
@@ -2462,31 +2474,31 @@ const fileUpload = (options) => ({
|
|
|
2462
2474
|
size: "xs",
|
|
2463
2475
|
layout: "list",
|
|
2464
2476
|
class: {
|
|
2465
|
-
|
|
2477
|
+
fileActions: "-me-1"
|
|
2466
2478
|
}
|
|
2467
2479
|
}, {
|
|
2468
2480
|
size: "sm",
|
|
2469
2481
|
layout: "list",
|
|
2470
2482
|
class: {
|
|
2471
|
-
|
|
2483
|
+
fileActions: "-me-1.5"
|
|
2472
2484
|
}
|
|
2473
2485
|
}, {
|
|
2474
2486
|
size: "md",
|
|
2475
2487
|
layout: "list",
|
|
2476
2488
|
class: {
|
|
2477
|
-
|
|
2489
|
+
fileActions: "-me-1.5"
|
|
2478
2490
|
}
|
|
2479
2491
|
}, {
|
|
2480
2492
|
size: "lg",
|
|
2481
2493
|
layout: "list",
|
|
2482
2494
|
class: {
|
|
2483
|
-
|
|
2495
|
+
fileActions: "-me-2"
|
|
2484
2496
|
}
|
|
2485
2497
|
}, {
|
|
2486
2498
|
size: "xl",
|
|
2487
2499
|
layout: "list",
|
|
2488
2500
|
class: {
|
|
2489
|
-
|
|
2501
|
+
fileActions: "-me-2"
|
|
2490
2502
|
}
|
|
2491
2503
|
}, {
|
|
2492
2504
|
variant: "button",
|
|
@@ -2535,6 +2547,21 @@ const fileUpload = (options) => ({
|
|
|
2535
2547
|
interactive: true,
|
|
2536
2548
|
disabled: false,
|
|
2537
2549
|
class: "hover:bg-elevated/25"
|
|
2550
|
+
}, {
|
|
2551
|
+
showProgress: true,
|
|
2552
|
+
layout: "list",
|
|
2553
|
+
class: {
|
|
2554
|
+
fileWrapper: "flex-col",
|
|
2555
|
+
fileProgress: "w-full"
|
|
2556
|
+
}
|
|
2557
|
+
}, {
|
|
2558
|
+
showProgress: true,
|
|
2559
|
+
layout: "grid",
|
|
2560
|
+
class: {
|
|
2561
|
+
fileWrapper: "absolute inset-0 bg-black/50 flex flex-col items-center justify-center rounded-lg",
|
|
2562
|
+
fileProgress: "w-3/4",
|
|
2563
|
+
fileProgressText: "text-white font-medium"
|
|
2564
|
+
}
|
|
2538
2565
|
}],
|
|
2539
2566
|
defaultVariants: {
|
|
2540
2567
|
color: "primary",
|
package/dist/unplugin.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { join, normalize } from 'pathe';
|
|
|
3
3
|
import { createUnplugin } from 'unplugin';
|
|
4
4
|
import { defu } from 'defu';
|
|
5
5
|
import tailwind from '@tailwindcss/vite';
|
|
6
|
-
import { g as getTemplates, d as defaultOptions, r as resolveColors, a as getDefaultUiConfig } from './shared/ui.
|
|
6
|
+
import { g as getTemplates, d as defaultOptions, r as resolveColors, a as getDefaultUiConfig } from './shared/ui.DdcmBFzX.mjs';
|
|
7
7
|
import { globSync } from 'tinyglobby';
|
|
8
8
|
import { genSafeVariableName } from 'knitwork';
|
|
9
9
|
import MagicString from 'magic-string';
|
package/dist/vite.mjs
CHANGED
package/package.json
CHANGED