vueless 0.0.103 → 0.0.105
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/package.json +1 -1
- package/ui.button-toggle/index.vue +1 -0
- package/ui.form-input-file/composables/attrs.composable.js +23 -10
- package/ui.form-input-file/configs/default.config.js +19 -9
- package/ui.form-input-file/index.vue +97 -67
- package/web-types.json +20 -6
- package/assets/icons/upload_file.svg +0 -1
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@ import defaultConfig from "../configs/default.config";
|
|
|
6
6
|
|
|
7
7
|
export function useAttrs(props) {
|
|
8
8
|
const { config, getAttrs } = useUI(defaultConfig, () => props.config);
|
|
9
|
-
const { dropzoneWrapper, placeholder } = config.value;
|
|
9
|
+
const { dropzoneWrapper, placeholder, selectedItem } = config.value;
|
|
10
10
|
|
|
11
11
|
const cvaDropzoneWrapper = cva({
|
|
12
12
|
base: dropzoneWrapper.base,
|
|
@@ -20,6 +20,12 @@ export function useAttrs(props) {
|
|
|
20
20
|
compoundVariants: placeholder.compoundVariants,
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
+
const cvaSelectedItem = cva({
|
|
24
|
+
base: selectedItem.base,
|
|
25
|
+
variants: selectedItem.variants,
|
|
26
|
+
compoundVariants: selectedItem.compoundVariants,
|
|
27
|
+
});
|
|
28
|
+
|
|
23
29
|
const dropzoneWrapperClasses = computed(() =>
|
|
24
30
|
cvaDropzoneWrapper({
|
|
25
31
|
error: Boolean(props.error),
|
|
@@ -35,20 +41,25 @@ export function useAttrs(props) {
|
|
|
35
41
|
}),
|
|
36
42
|
);
|
|
37
43
|
|
|
44
|
+
const selectedItemClasses = computed(() =>
|
|
45
|
+
cvaSelectedItem({
|
|
46
|
+
size: props.size,
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
|
|
38
50
|
const labelAttrs = getAttrs("label", { isComponent: true });
|
|
39
51
|
const buttonAttrs = getAttrs("button", { isComponent: true });
|
|
40
52
|
const dropzoneWrapperAttrs = getAttrs("dropzoneWrapper", { classes: dropzoneWrapperClasses });
|
|
41
53
|
const descriptionAttrs = getAttrs("description", { isComponent: true });
|
|
54
|
+
const contentWrapperAttrs = getAttrs("contentWrapper");
|
|
42
55
|
const buttonWrapperAttrs = getAttrs("buttonWrapper");
|
|
43
|
-
const
|
|
44
|
-
const placeholderAttrs = getAttrs("placeholder", {
|
|
45
|
-
classes: placeholderClasses,
|
|
46
|
-
isComponent: true,
|
|
47
|
-
});
|
|
48
|
-
const placeholderIconAttrs = getAttrs("placeholderIcon", { isComponent: true });
|
|
56
|
+
const placeholderAttrs = getAttrs("placeholder", { classes: placeholderClasses });
|
|
49
57
|
const clearIconAttrs = getAttrs("clearIcon", { isComponent: true });
|
|
58
|
+
const chooseFileIconAttrs = getAttrs("chooseFileIcon", { isComponent: true });
|
|
50
59
|
const chooseFileIconNameAttrs = getAttrs("chooseFileIconName", { isComponent: true });
|
|
51
60
|
const inputAttrs = getAttrs("input");
|
|
61
|
+
const fileListAttrs = getAttrs("fileList");
|
|
62
|
+
const selectedItemAttrs = getAttrs("selectedItem", { classes: selectedItemClasses });
|
|
52
63
|
|
|
53
64
|
return {
|
|
54
65
|
config,
|
|
@@ -57,11 +68,13 @@ export function useAttrs(props) {
|
|
|
57
68
|
buttonAttrs,
|
|
58
69
|
dropzoneWrapperAttrs,
|
|
59
70
|
descriptionAttrs,
|
|
60
|
-
|
|
61
|
-
placeholderWrapperAttrs,
|
|
62
|
-
placeholderIconAttrs,
|
|
71
|
+
contentWrapperAttrs,
|
|
63
72
|
clearIconAttrs,
|
|
64
73
|
chooseFileIconNameAttrs,
|
|
65
74
|
placeholderAttrs,
|
|
75
|
+
fileListAttrs,
|
|
76
|
+
buttonWrapperAttrs,
|
|
77
|
+
selectedItemAttrs,
|
|
78
|
+
chooseFileIconAttrs,
|
|
66
79
|
};
|
|
67
80
|
}
|
|
@@ -26,10 +26,20 @@ export default /*tw*/ {
|
|
|
26
26
|
],
|
|
27
27
|
},
|
|
28
28
|
description: "text-gray-700",
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
contentWrapper: "relative mt-3 flex w-full gap-6 justify-between items-start rounded bg-brand-50 p-4",
|
|
30
|
+
fileList: "pr-4 shrink-0 text-gray-700 flex-grow flex flex-col gap-4",
|
|
31
31
|
placeholder: {
|
|
32
|
-
base: "pr-4 shrink-0 text-
|
|
32
|
+
base: "pr-4 shrink-0 text-gray-700 flex-grow",
|
|
33
|
+
variants: {
|
|
34
|
+
size: {
|
|
35
|
+
sm: "text-sm",
|
|
36
|
+
md: "text-base",
|
|
37
|
+
lg: "text-lg",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
selectedItem: {
|
|
42
|
+
base: "pr-4 shrink-0 text-gray-700 flex-grow",
|
|
33
43
|
variants: {
|
|
34
44
|
size: {
|
|
35
45
|
sm: "text-sm",
|
|
@@ -39,25 +49,25 @@ export default /*tw*/ {
|
|
|
39
49
|
},
|
|
40
50
|
},
|
|
41
51
|
button: "hover:cursor-pointer",
|
|
42
|
-
placeholderIcon: "-rotate-45",
|
|
43
|
-
placeholderIconName: "attach_file",
|
|
44
52
|
chooseFileIcon: "",
|
|
45
|
-
chooseFileIconName: "
|
|
53
|
+
chooseFileIconName: "attach_file",
|
|
46
54
|
clearIcon: "",
|
|
47
55
|
clearIconName: "close",
|
|
48
|
-
dropzoneWrapperHover: "border-gray-400
|
|
56
|
+
dropzoneWrapperHover: "border-gray-400",
|
|
49
57
|
dropzoneWrapperError: "hover:border-red-400 border-red-300",
|
|
50
58
|
input: "sr-only pointer-events-none size-0 opacity-0",
|
|
59
|
+
buttonWrapper: "flex gap-4 items-center",
|
|
51
60
|
i18n: {
|
|
52
61
|
sizeError: "File size is too big.",
|
|
53
62
|
formatError: "Format is not supported.",
|
|
54
63
|
noFile: "No file selected",
|
|
55
|
-
uploadFile: "
|
|
64
|
+
uploadFile: "Choose file",
|
|
56
65
|
},
|
|
57
66
|
defaultVariants: {
|
|
58
67
|
size: "md",
|
|
59
68
|
labelAlign: "topInside",
|
|
60
69
|
allowedFileTypes: [],
|
|
61
|
-
|
|
70
|
+
multiple: false,
|
|
71
|
+
maxFileSize: 0,
|
|
62
72
|
},
|
|
63
73
|
};
|
|
@@ -3,58 +3,64 @@
|
|
|
3
3
|
<div ref="dropZoneRef" :ondrop="onDrop" v-bind="dropzoneWrapperAttrs">
|
|
4
4
|
<UText :size="nestedComponentSize" v-bind="descriptionAttrs" :html="description" />
|
|
5
5
|
|
|
6
|
-
<div v-bind="
|
|
7
|
-
<
|
|
6
|
+
<div v-bind="contentWrapperAttrs">
|
|
7
|
+
<slot name="left" />
|
|
8
|
+
<span v-if="!isValue" v-bind="placeholderAttrs" v-text="currentLocale.noFile" />
|
|
9
|
+
<div v-else v-bind="fileListAttrs">
|
|
10
|
+
<template v-if="props.multiple">
|
|
11
|
+
<span
|
|
12
|
+
v-for="(file, idx) in currentFiles"
|
|
13
|
+
v-bind="selectedItemAttrs"
|
|
14
|
+
:key="idx"
|
|
15
|
+
v-text="file.name"
|
|
16
|
+
/>
|
|
17
|
+
</template>
|
|
18
|
+
<span v-else v-bind="selectedItemAttrs" v-text="currentFiles.name" />
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div v-bind="buttonWrapperAttrs">
|
|
22
|
+
<template v-if="Array.isArray(currentFiles) || !currentFiles">
|
|
23
|
+
<UButton
|
|
24
|
+
class="hover:cursor-pointer"
|
|
25
|
+
:label="currentLocale.uploadFile"
|
|
26
|
+
variant="thirdary"
|
|
27
|
+
filled
|
|
28
|
+
:size="nestedComponentSize"
|
|
29
|
+
v-bind="buttonAttrs"
|
|
30
|
+
tag="label"
|
|
31
|
+
:for="id"
|
|
32
|
+
>
|
|
33
|
+
<template #right>
|
|
34
|
+
<UIcon
|
|
35
|
+
internal
|
|
36
|
+
:size="nestedComponentSize"
|
|
37
|
+
:name="config.chooseFileIconName"
|
|
38
|
+
v-bind="chooseFileIconAttrs"
|
|
39
|
+
/>
|
|
40
|
+
</template>
|
|
41
|
+
</UButton>
|
|
42
|
+
|
|
43
|
+
<input
|
|
44
|
+
:id="id"
|
|
45
|
+
ref="fileInputRef"
|
|
46
|
+
:multiple="multiple"
|
|
47
|
+
type="file"
|
|
48
|
+
:accept="accept"
|
|
49
|
+
v-bind="inputAttrs"
|
|
50
|
+
@change="onChangeFile"
|
|
51
|
+
/>
|
|
52
|
+
</template>
|
|
8
53
|
<UIcon
|
|
9
|
-
|
|
54
|
+
v-if="isValue"
|
|
55
|
+
interactive
|
|
10
56
|
internal
|
|
11
57
|
:size="nestedComponentSize"
|
|
12
|
-
:name="config.
|
|
13
|
-
|
|
58
|
+
:name="config.clearIconName"
|
|
59
|
+
pill
|
|
60
|
+
v-bind="clearIconAttrs"
|
|
61
|
+
@click="onClickResetFiles"
|
|
14
62
|
/>
|
|
15
|
-
<span v-bind="placeholderAttrs" v-text="placeholder" />
|
|
16
63
|
</div>
|
|
17
|
-
|
|
18
|
-
<template v-if="!currentFiles.length">
|
|
19
|
-
<UButton
|
|
20
|
-
class="hover:cursor-pointer"
|
|
21
|
-
:label="currentLocale.uploadFile"
|
|
22
|
-
variant="thirdary"
|
|
23
|
-
filled
|
|
24
|
-
:size="nestedComponentSize"
|
|
25
|
-
v-bind="buttonAttrs"
|
|
26
|
-
tag="label"
|
|
27
|
-
:for="id"
|
|
28
|
-
>
|
|
29
|
-
<template #right>
|
|
30
|
-
<UIcon
|
|
31
|
-
internal
|
|
32
|
-
:size="nestedComponentSize"
|
|
33
|
-
:name="config.chooseFileIconName"
|
|
34
|
-
v-bind="chooseFileIconAttrs"
|
|
35
|
-
/>
|
|
36
|
-
</template>
|
|
37
|
-
</UButton>
|
|
38
|
-
|
|
39
|
-
<input
|
|
40
|
-
:id="id"
|
|
41
|
-
ref="fileInputRef"
|
|
42
|
-
type="file"
|
|
43
|
-
:accept="accept"
|
|
44
|
-
v-bind="inputAttrs"
|
|
45
|
-
@change="onChangeFile"
|
|
46
|
-
/>
|
|
47
|
-
</template>
|
|
48
|
-
<UIcon
|
|
49
|
-
v-else
|
|
50
|
-
interactive
|
|
51
|
-
internal
|
|
52
|
-
:size="nestedComponentSize"
|
|
53
|
-
:name="config.clearIconName"
|
|
54
|
-
pill
|
|
55
|
-
v-bind="clearIconAttrs"
|
|
56
|
-
@click="onClickResetFiles"
|
|
57
|
-
/>
|
|
58
64
|
</div>
|
|
59
65
|
</div>
|
|
60
66
|
</ULabel>
|
|
@@ -87,7 +93,7 @@ const props = defineProps({
|
|
|
87
93
|
*/
|
|
88
94
|
label: {
|
|
89
95
|
type: String,
|
|
90
|
-
default: "
|
|
96
|
+
default: "",
|
|
91
97
|
},
|
|
92
98
|
|
|
93
99
|
/**
|
|
@@ -95,7 +101,7 @@ const props = defineProps({
|
|
|
95
101
|
*/
|
|
96
102
|
description: {
|
|
97
103
|
type: String,
|
|
98
|
-
default: "
|
|
104
|
+
default: "",
|
|
99
105
|
},
|
|
100
106
|
|
|
101
107
|
/**
|
|
@@ -108,8 +114,16 @@ const props = defineProps({
|
|
|
108
114
|
},
|
|
109
115
|
|
|
110
116
|
modelValue: {
|
|
111
|
-
type: Array,
|
|
112
|
-
default:
|
|
117
|
+
type: [Array, File],
|
|
118
|
+
default: null,
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Allow select multiple files.
|
|
123
|
+
*/
|
|
124
|
+
multiple: {
|
|
125
|
+
type: Boolean,
|
|
126
|
+
default: UIService.get(defaultConfig, UInputFile).default.multiple,
|
|
113
127
|
},
|
|
114
128
|
|
|
115
129
|
/**
|
|
@@ -175,13 +189,14 @@ const {
|
|
|
175
189
|
buttonAttrs,
|
|
176
190
|
dropzoneWrapperAttrs,
|
|
177
191
|
descriptionAttrs,
|
|
178
|
-
|
|
179
|
-
placeholderWrapperAttrs,
|
|
180
|
-
placeholderIconAttrs,
|
|
192
|
+
contentWrapperAttrs,
|
|
181
193
|
clearIconAttrs,
|
|
182
194
|
chooseFileIconAttrs,
|
|
183
195
|
placeholderAttrs,
|
|
184
196
|
inputAttrs,
|
|
197
|
+
fileListAttrs,
|
|
198
|
+
buttonWrapperAttrs,
|
|
199
|
+
selectedItemAttrs,
|
|
185
200
|
} = useAttrs(props);
|
|
186
201
|
|
|
187
202
|
const i18nGlobal = tm(UInputFile);
|
|
@@ -189,7 +204,11 @@ const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props
|
|
|
189
204
|
|
|
190
205
|
const currentFiles = computed({
|
|
191
206
|
get: () => props.modelValue,
|
|
192
|
-
set: (newValue) =>
|
|
207
|
+
set: (newValue) => {
|
|
208
|
+
const fallbackValue = props.multiple ? [] : null;
|
|
209
|
+
|
|
210
|
+
emit("update:modelValue", newValue || fallbackValue);
|
|
211
|
+
},
|
|
193
212
|
});
|
|
194
213
|
|
|
195
214
|
const currentError = computed({
|
|
@@ -197,16 +216,23 @@ const currentError = computed({
|
|
|
197
216
|
set: (newValue) => emit("update:error", newValue),
|
|
198
217
|
});
|
|
199
218
|
|
|
200
|
-
const accept = computed(() => {
|
|
201
|
-
return props.allowedFileTypes.join(",");
|
|
202
|
-
});
|
|
203
|
-
|
|
204
219
|
const extensionNames = computed(() => {
|
|
205
220
|
return props.allowedFileTypes.map((type) => type.replace(".", ""));
|
|
206
221
|
});
|
|
207
222
|
|
|
208
|
-
const
|
|
209
|
-
return
|
|
223
|
+
const allowedFileTypeFormats = computed(() => {
|
|
224
|
+
return props.allowedFileTypes.map((type) => (type.startsWith(".") ? type : `.${type}`));
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const accept = computed(() => {
|
|
228
|
+
return allowedFileTypeFormats.value.join(",");
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const isValue = computed(() => {
|
|
232
|
+
return (
|
|
233
|
+
(Array.isArray(currentFiles.value) && currentFiles.value.length) ||
|
|
234
|
+
(!Array.isArray(currentFiles.value) && currentFiles.value)
|
|
235
|
+
);
|
|
210
236
|
});
|
|
211
237
|
|
|
212
238
|
const nestedComponentSize = computed(() => {
|
|
@@ -238,7 +264,7 @@ function validate(file) {
|
|
|
238
264
|
|
|
239
265
|
const isValidSize = targetFileSize <= props.maxFileSize;
|
|
240
266
|
|
|
241
|
-
if (!isValidSize) {
|
|
267
|
+
if (!isValidSize && props.maxFileSize) {
|
|
242
268
|
currentError.value = currentLocale.value.sizeError;
|
|
243
269
|
}
|
|
244
270
|
|
|
@@ -256,11 +282,15 @@ function onChangeFile(event) {
|
|
|
256
282
|
return;
|
|
257
283
|
}
|
|
258
284
|
|
|
259
|
-
currentFiles.value =
|
|
285
|
+
currentFiles.value = props.multiple
|
|
286
|
+
? [...currentFiles.value, Array.from(event.target.files).at(0)]
|
|
287
|
+
: Array.from(event.target.files).at(0);
|
|
288
|
+
|
|
289
|
+
if (fileInputRef.value) fileInputRef.value.value = "";
|
|
260
290
|
}
|
|
261
291
|
|
|
262
292
|
function onClickResetFiles() {
|
|
263
|
-
currentFiles.value =
|
|
293
|
+
currentFiles.value = null;
|
|
264
294
|
|
|
265
295
|
if (fileInputRef.value) fileInputRef.value.value = "";
|
|
266
296
|
}
|
|
@@ -268,13 +298,13 @@ function onClickResetFiles() {
|
|
|
268
298
|
function onDragOver(event) {
|
|
269
299
|
event.preventDefault();
|
|
270
300
|
|
|
271
|
-
dropZoneRef.value.classList.add(config.value.dropzoneWrapperHover.split(" "));
|
|
301
|
+
dropZoneRef.value.classList.add(...config.value.dropzoneWrapperHover.split(" "));
|
|
272
302
|
}
|
|
273
303
|
|
|
274
304
|
function onDragLeave(event) {
|
|
275
305
|
event.preventDefault();
|
|
276
306
|
|
|
277
|
-
dropZoneRef.value.classList.remove(config.value.dropzoneWrapperHover.split(" "));
|
|
307
|
+
dropZoneRef.value.classList.remove(...config.value.dropzoneWrapperHover.split(" "));
|
|
278
308
|
}
|
|
279
309
|
|
|
280
310
|
function onDrop(event) {
|
|
@@ -297,7 +327,7 @@ function onDrop(event) {
|
|
|
297
327
|
return;
|
|
298
328
|
}
|
|
299
329
|
|
|
300
|
-
currentFiles.value = [targetFile];
|
|
330
|
+
currentFiles.value = props.multiple ? [...currentFiles.value, targetFile] : targetFile;
|
|
301
331
|
});
|
|
302
332
|
}
|
|
303
333
|
</script>
|
package/web-types.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"framework": "vue",
|
|
3
3
|
"name": "vueless",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.105",
|
|
5
5
|
"contributions": {
|
|
6
6
|
"html": {
|
|
7
7
|
"description-markup": "markdown",
|
|
@@ -3448,7 +3448,7 @@
|
|
|
3448
3448
|
"kind": "expression",
|
|
3449
3449
|
"type": "string"
|
|
3450
3450
|
},
|
|
3451
|
-
"default": "\"
|
|
3451
|
+
"default": "\"\""
|
|
3452
3452
|
},
|
|
3453
3453
|
{
|
|
3454
3454
|
"name": "description",
|
|
@@ -3457,7 +3457,7 @@
|
|
|
3457
3457
|
"kind": "expression",
|
|
3458
3458
|
"type": "string"
|
|
3459
3459
|
},
|
|
3460
|
-
"default": "\"
|
|
3460
|
+
"default": "\"\""
|
|
3461
3461
|
},
|
|
3462
3462
|
{
|
|
3463
3463
|
"name": "labelAlign",
|
|
@@ -3472,9 +3472,18 @@
|
|
|
3472
3472
|
"name": "modelValue",
|
|
3473
3473
|
"value": {
|
|
3474
3474
|
"kind": "expression",
|
|
3475
|
-
"type": "array"
|
|
3475
|
+
"type": "array|File"
|
|
3476
3476
|
},
|
|
3477
|
-
"default": "
|
|
3477
|
+
"default": "null"
|
|
3478
|
+
},
|
|
3479
|
+
{
|
|
3480
|
+
"name": "multiple",
|
|
3481
|
+
"description": "Allow select multiple files.",
|
|
3482
|
+
"value": {
|
|
3483
|
+
"kind": "expression",
|
|
3484
|
+
"type": "boolean"
|
|
3485
|
+
},
|
|
3486
|
+
"default": "false"
|
|
3478
3487
|
},
|
|
3479
3488
|
{
|
|
3480
3489
|
"name": "maxFileSize",
|
|
@@ -3483,7 +3492,7 @@
|
|
|
3483
3492
|
"kind": "expression",
|
|
3484
3493
|
"type": "number"
|
|
3485
3494
|
},
|
|
3486
|
-
"default": "
|
|
3495
|
+
"default": "0"
|
|
3487
3496
|
},
|
|
3488
3497
|
{
|
|
3489
3498
|
"name": "allowedFileTypes",
|
|
@@ -3539,6 +3548,11 @@
|
|
|
3539
3548
|
"name": "update:error"
|
|
3540
3549
|
}
|
|
3541
3550
|
],
|
|
3551
|
+
"slots": [
|
|
3552
|
+
{
|
|
3553
|
+
"name": "left"
|
|
3554
|
+
}
|
|
3555
|
+
],
|
|
3542
3556
|
"source": {
|
|
3543
3557
|
"module": "vueless/ui.form-input-file/index.vue",
|
|
3544
3558
|
"symbol": "default"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 -960 960 960"><path d="M452-203.43h60v-201.96l82.48 82.48 42-42L480-517.15 325.76-362.91l42 42L452-405.39v201.96ZM222.15-74.02q-27.6 0-47.86-20.27-20.27-20.26-20.27-47.86v-675.7q0-27.7 20.27-48.03 20.26-20.34 47.86-20.34h361.48l222.59 222.59v521.48q0 27.6-20.34 47.86-20.33 20.27-48.03 20.27h-515.7Zm326.7-557.83v-186h-326.7v675.7h515.7v-489.7h-189Zm-326.7-186v186-186 675.7-675.7Z"/></svg>
|