vueless 0.0.569 → 0.0.571

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.
Files changed (81) hide show
  1. package/package.json +1 -1
  2. package/types.ts +24 -3
  3. package/ui.button/types.ts +2 -2
  4. package/ui.container-accordion/UAccordion.vue +2 -2
  5. package/ui.container-accordion/types.ts +1 -0
  6. package/ui.container-modal/UModal.vue +15 -8
  7. package/ui.container-page/UPage.vue +10 -6
  8. package/ui.form-checkbox/types.ts +7 -0
  9. package/ui.form-checkbox-group/UCheckboxGroup.vue +1 -1
  10. package/ui.form-checkbox-multi-state/UCheckboxMultiState.vue +4 -1
  11. package/ui.form-color-picker/UColorPicker.vue +67 -147
  12. package/ui.form-color-picker/storybook/Docs.mdx +2 -2
  13. package/ui.form-color-picker/storybook/{stories.js → stories.ts} +13 -5
  14. package/ui.form-color-picker/types.ts +64 -0
  15. package/ui.form-color-picker/useAttrs.ts +15 -0
  16. package/ui.form-input/UInput.vue +162 -321
  17. package/ui.form-input/{config.js → config.ts} +3 -0
  18. package/ui.form-input/storybook/Docs.mdx +2 -2
  19. package/ui.form-input/storybook/{stories.js → stories.ts} +14 -6
  20. package/ui.form-input/types.ts +103 -0
  21. package/ui.form-input/useAttrs.ts +31 -0
  22. package/ui.form-input-file/UInputFile.vue +188 -245
  23. package/ui.form-input-file/storybook/Docs.mdx +2 -2
  24. package/ui.form-input-file/storybook/{stories.js → stories.ts} +13 -5
  25. package/ui.form-input-file/types.ts +72 -0
  26. package/ui.form-input-file/useAttrs.ts +21 -0
  27. package/ui.form-input-file/{utilFileForm.js → utilFileForm.ts} +1 -1
  28. package/ui.form-input-money/UInputMoney.vue +76 -223
  29. package/ui.form-input-money/storybook/Docs.mdx +2 -2
  30. package/ui.form-input-money/storybook/{stories.js → stories.ts} +14 -6
  31. package/ui.form-input-money/types.ts +118 -0
  32. package/ui.form-input-money/useAttrs.ts +15 -0
  33. package/ui.form-input-money/{useFormatCurrency.js → useFormatCurrency.ts} +28 -17
  34. package/ui.form-input-money/utilFormat.ts +83 -0
  35. package/ui.form-input-number/UInputNumber.vue +69 -156
  36. package/ui.form-input-number/storybook/Docs.mdx +2 -2
  37. package/ui.form-input-number/storybook/{stories.js → stories.ts} +17 -9
  38. package/ui.form-input-number/types.ts +65 -0
  39. package/ui.form-input-number/useAttrs.ts +15 -0
  40. package/ui.form-input-rating/UInputRating.vue +70 -158
  41. package/ui.form-input-rating/storybook/Docs.mdx +2 -2
  42. package/ui.form-input-rating/storybook/{stories.js → stories.ts} +14 -6
  43. package/ui.form-input-rating/types.ts +67 -0
  44. package/ui.form-input-rating/useAttrs.ts +14 -0
  45. package/ui.form-input-search/UInputSearch.vue +97 -224
  46. package/ui.form-input-search/storybook/Docs.mdx +2 -2
  47. package/ui.form-input-search/storybook/{stories.js → stories.ts} +13 -5
  48. package/ui.form-input-search/types.ts +93 -0
  49. package/ui.form-input-search/useAttrs.ts +15 -0
  50. package/ui.form-radio/URadio.vue +1 -1
  51. package/ui.form-radio-group/URadioGroup.vue +1 -1
  52. package/ui.navigation-pagination/UPagination.vue +15 -15
  53. package/ui.navigation-pagination/types.ts +3 -0
  54. package/ui.navigation-progress/UProgress.vue +16 -2
  55. package/ui.navigation-progress/types.ts +2 -0
  56. package/ui.text-files/UFiles.vue +20 -16
  57. package/ui.text-files/types.ts +1 -1
  58. package/ui.text-notify/UNotify.vue +38 -48
  59. package/ui.text-notify/types.ts +24 -0
  60. package/web-types.json +227 -138
  61. package/ui.form-color-picker/useAttrs.js +0 -9
  62. package/ui.form-input/useAttrs.js +0 -15
  63. package/ui.form-input-file/useAttrs.js +0 -15
  64. package/ui.form-input-money/useAttrs.js +0 -9
  65. package/ui.form-input-money/utilFormat.js +0 -75
  66. package/ui.form-input-number/useAttrs.js +0 -9
  67. package/ui.form-input-rating/useAttrs.js +0 -8
  68. package/ui.form-input-search/useAttrs.js +0 -9
  69. /package/ui.form-color-picker/{config.js → config.ts} +0 -0
  70. /package/ui.form-color-picker/{constants.js → constants.ts} +0 -0
  71. /package/ui.form-input/{constants.js → constants.ts} +0 -0
  72. /package/ui.form-input-file/{config.js → config.ts} +0 -0
  73. /package/ui.form-input-file/{constants.js → constants.ts} +0 -0
  74. /package/ui.form-input-money/{config.js → config.ts} +0 -0
  75. /package/ui.form-input-money/{constants.js → constants.ts} +0 -0
  76. /package/ui.form-input-number/{config.js → config.ts} +0 -0
  77. /package/ui.form-input-number/{constants.js → constants.ts} +0 -0
  78. /package/ui.form-input-rating/{config.js → config.ts} +0 -0
  79. /package/ui.form-input-rating/{constants.js → constants.ts} +0 -0
  80. /package/ui.form-input-search/{config.js → config.ts} +0 -0
  81. /package/ui.form-input-search/{constants.js → constants.ts} +0 -0
@@ -0,0 +1,103 @@
1
+ import defaultConfig from "./config.ts";
2
+
3
+ export type Config = Partial<typeof defaultConfig>;
4
+
5
+ export type IconSize = "xs" | "sm" | "md";
6
+
7
+ export interface UInputProps {
8
+ /**
9
+ * Input value.
10
+ */
11
+ modelValue?: string | number;
12
+
13
+ /**
14
+ * Input label.
15
+ */
16
+ label?: string;
17
+
18
+ /**
19
+ * Label placement.
20
+ */
21
+ labelAlign?: "top" | "topInside" | "topWithDesc" | "left" | "right";
22
+
23
+ /**
24
+ * Input placeholder.
25
+ */
26
+ placeholder?: string;
27
+
28
+ /**
29
+ * Input description.
30
+ */
31
+ description?: string;
32
+
33
+ /**
34
+ * Error message.
35
+ */
36
+ error?: string;
37
+
38
+ /**
39
+ * Input size.
40
+ */
41
+ size?: "sm" | "md" | "lg";
42
+
43
+ /**
44
+ * Left icon name.
45
+ */
46
+ leftIcon?: string;
47
+
48
+ /**
49
+ * Right icon name.
50
+ */
51
+ rightIcon?: string;
52
+
53
+ /**
54
+ * Maximum character length.
55
+ */
56
+ maxLength?: string | number;
57
+
58
+ /**
59
+ * Prevents some characters from input.
60
+ * You can use predefined values or own RegExp.
61
+ */
62
+ validationRule?: "symbol" | "string" | "stringAndNumber" | "number" | "integer";
63
+
64
+ /**
65
+ * Input type.
66
+ */
67
+ type?: "text" | "number" | "tel" | "email" | "url" | "search" | "password";
68
+
69
+ /**
70
+ * Set specific keyboard for mobile devices.
71
+ */
72
+ inputmode?: "text" | "decimal" | "numeric" | "tel" | "email" | "url" | "search" | "none";
73
+
74
+ /**
75
+ * Make input read-only.
76
+ */
77
+ readonly?: boolean;
78
+
79
+ /**
80
+ * Disable the input.
81
+ */
82
+ disabled?: boolean;
83
+
84
+ /**
85
+ * Disable browsers autocomplete.
86
+ */
87
+ noAutocomplete?: boolean;
88
+
89
+ /**
90
+ * Unique element id.
91
+ */
92
+ id?: string;
93
+
94
+ /**
95
+ * Component config object.
96
+ */
97
+ config?: Config;
98
+
99
+ /**
100
+ * Data-test attribute for automated testing.
101
+ */
102
+ dataTest?: string;
103
+ }
@@ -0,0 +1,31 @@
1
+ import { computed } from "vue";
2
+ import useUI from "../composables/useUI.ts";
3
+
4
+ import defaultConfig from "./config.ts";
5
+
6
+ import type { ComputedRef } from "vue";
7
+ import type { UseAttrs } from "../types.ts";
8
+ import type { UInputProps, Config } from "./types.ts";
9
+
10
+ type ComponentState = {
11
+ applyPasswordClasses: ComputedRef<boolean>;
12
+ };
13
+
14
+ export default function useAttrs(
15
+ props: UInputProps,
16
+ { applyPasswordClasses }: ComponentState,
17
+ ): UseAttrs<Config> {
18
+ const { config, getKeysAttrs } = useUI<Config>(defaultConfig, () => props.config);
19
+
20
+ const mutatedProps = computed(() => ({
21
+ error: Boolean(props.error),
22
+ label: Boolean(props.label),
23
+ /* component state, not a props */
24
+ typePassword: applyPasswordClasses.value,
25
+ }));
26
+
27
+ return {
28
+ config,
29
+ ...getKeysAttrs(mutatedProps),
30
+ };
31
+ }
@@ -1,92 +1,4 @@
1
- <template>
2
- <ULabel
3
- :for="elementId"
4
- :size="size"
5
- :label="label"
6
- :error="error"
7
- :align="labelAlign"
8
- :disabled="disabled"
9
- :description="description"
10
- v-bind="inputLabelAttrs"
11
- >
12
- <div ref="dropZoneRef" :ondrop="onDrop" v-bind="dropzoneAttrs">
13
- <UText v-if="hasSlotContent($slots['top'])" :size="size" v-bind="descriptionTopAttrs">
14
- <!-- @slot Use it to add something above the component content. -->
15
- <slot name="top" />
16
- </UText>
17
-
18
- <div v-bind="contentAttrs">
19
- <!-- @slot Use it to add something before the placeholder. -->
20
- <slot name="left" />
21
-
22
- <span v-if="!isValue" v-bind="placeholderAttrs" v-text="currentLocale.noFile" />
23
-
24
- <UFiles
25
- :size="size"
26
- v-bind="fileListAttrs"
27
- :file-list="fileList"
28
- :removable="multiple && !disabled"
29
- @remove="onClickRemoveItem"
30
- >
31
- <template #right="{ file }">
32
- <slot name="right" :file="file" />
33
- </template>
34
- </UFiles>
35
-
36
- <div v-bind="buttonsAttrs">
37
- <template v-if="Array.isArray(currentFiles) || !currentFiles">
38
- <UButton
39
- filled
40
- no-ring
41
- :for="elementId"
42
- tag="label"
43
- variant="thirdary"
44
- :size="buttonSize"
45
- :right-icon="config.defaults.chooseFileIcon"
46
- :label="currentLocale.uploadFile"
47
- :disabled="disabled"
48
- v-bind="chooseFileButtonAttrs"
49
- :data-test="`${dataTest}-upload`"
50
- />
51
-
52
- <input
53
- :id="elementId"
54
- ref="fileInputRef"
55
- type="file"
56
- :disabled="disabled"
57
- :accept="accept"
58
- :multiple="multiple"
59
- v-bind="inputAttrs"
60
- @change="onChangeFile"
61
- />
62
- </template>
63
-
64
- <UButton
65
- v-if="isValue && !disabled"
66
- round
67
- square
68
- filled
69
- no-ring
70
- variant="thirdary"
71
- :size="buttonSize"
72
- :disabled="disabled"
73
- :left-icon="config.defaults.clearIcon"
74
- v-bind="clearButtonAttrs"
75
- :data-test="`${dataTest}-clear`"
76
- @click="onClickResetFiles"
77
- />
78
- </div>
79
- </div>
80
-
81
- <UText v-if="hasSlotContent($slots['bottom'])" :size="size" v-bind="descriptionBottomAttrs">
82
- <!-- @slot Use it to add something below the component content. -->
83
- <slot name="bottom" />
84
- </UText>
85
- </div>
86
- </ULabel>
87
- </template>
88
-
89
- <script setup>
1
+ <script setup lang="ts">
90
2
  import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch, useId } from "vue";
91
3
  import { merge } from "lodash-es";
92
4
 
@@ -97,120 +9,27 @@ import UFiles from "../ui.text-files/UFiles.vue";
97
9
 
98
10
  import { getDefault } from "../utils/ui.ts";
99
11
  import { hasSlotContent } from "../utils/helper.ts";
100
- import { useLocale } from "../composables/useLocale.ts";
101
-
102
- import useAttrs from "./useAttrs.js";
103
- import { getFileMbSize } from "./utilFileForm.js";
104
- import { UInputFile } from "./constants.js";
105
- import defaultConfig from "./config.js";
106
-
107
- defineOptions({ inheritAttrs: false });
108
-
109
- const props = defineProps({
110
- /**
111
- * Input value.
112
- */
113
- modelValue: {
114
- type: [Array, File],
115
- default: null,
116
- },
117
-
118
- /**
119
- * Input label.
120
- */
121
- label: {
122
- type: String,
123
- default: "",
124
- },
125
-
126
- /**
127
- * Input description.
128
- */
129
- description: {
130
- type: String,
131
- default: "",
132
- },
133
-
134
- /**
135
- * Error message.
136
- */
137
- error: {
138
- type: String,
139
- default: "",
140
- },
12
+ import { getFileMbSize } from "./utilFileForm.ts";
141
13
 
142
- /**
143
- * Label placement.
144
- * @values top, topInside, topWithDesc
145
- */
146
- labelAlign: {
147
- type: String,
148
- default: getDefault(defaultConfig, UInputFile).labelAlign,
149
- },
150
-
151
- /**
152
- * Input size.
153
- * @values sm, md, lg
154
- */
155
- size: {
156
- type: String,
157
- default: getDefault(defaultConfig, UInputFile).size,
158
- },
159
-
160
- /**
161
- * Max file size in megabytes.
162
- */
163
- maxFileSize: {
164
- type: Number,
165
- default: getDefault(defaultConfig, UInputFile).maxFileSize,
166
- },
14
+ import useAttrs from "./useAttrs.ts";
15
+ import { useLocale } from "../composables/useLocale.ts";
167
16
 
168
- /**
169
- * Allowed file types.
170
- */
171
- allowedFileTypes: {
172
- type: Array,
173
- default: () => getDefault(defaultConfig, UInputFile).allowedFileTypes,
174
- },
17
+ import { UInputFile } from "./constants.ts";
18
+ import defaultConfig from "./config.ts";
175
19
 
176
- /**
177
- * Allow selecting multiple files.
178
- */
179
- multiple: {
180
- type: Boolean,
181
- default: getDefault(defaultConfig, UInputFile).multiple,
182
- },
20
+ import type { UInputFileProps, ButtonSize } from "./types.ts";
183
21
 
184
- /**
185
- * Disable the input.
186
- */
187
- disabled: {
188
- type: Boolean,
189
- default: getDefault(defaultConfig, UInputFile).disabled,
190
- },
191
-
192
- /**
193
- * Unique element id.
194
- */
195
- id: {
196
- type: String,
197
- default: "",
198
- },
199
- /**
200
- * Component config object.
201
- */
202
- config: {
203
- type: Object,
204
- default: () => ({}),
205
- },
22
+ defineOptions({ inheritAttrs: false });
206
23
 
207
- /**
208
- * Data-test attribute for automated testing.
209
- */
210
- dataTest: {
211
- type: String,
212
- default: "",
213
- },
24
+ const props = withDefaults(defineProps<UInputFileProps>(), {
25
+ labelAlign: getDefault<UInputFileProps>(defaultConfig, UInputFile).labelAlign,
26
+ size: getDefault<UInputFileProps>(defaultConfig, UInputFile).size,
27
+ maxFileSize: getDefault<UInputFileProps>(defaultConfig, UInputFile).maxFileSize,
28
+ allowedFileTypes: getDefault<UInputFileProps>(defaultConfig, UInputFile).allowedFileTypes,
29
+ multiple: getDefault<UInputFileProps>(defaultConfig, UInputFile).multiple,
30
+ disabled: getDefault<UInputFileProps>(defaultConfig, UInputFile).disabled,
31
+ dataTest: "",
32
+ config: () => ({}),
214
33
  });
215
34
 
216
35
  const emit = defineEmits([
@@ -229,8 +48,8 @@ const emit = defineEmits([
229
48
 
230
49
  const { tm } = useLocale();
231
50
 
232
- const dropZoneRef = ref(null);
233
- const fileInputRef = ref(null);
51
+ const dropZoneRef = ref<HTMLDivElement | null>(null);
52
+ const fileInputRef = ref<HTMLInputElement | null>(null);
234
53
 
235
54
  const elementId = props.id || useId();
236
55
 
@@ -252,7 +71,7 @@ const {
252
71
  const i18nGlobal = tm(UInputFile);
253
72
  const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
254
73
 
255
- const currentFiles = computed({
74
+ const currentFiles = computed<File | File[] | null>({
256
75
  get: () => props.modelValue,
257
76
  set: (newValue) => {
258
77
  const fallbackValue = props.multiple ? [] : null;
@@ -297,44 +116,49 @@ const buttonSize = computed(() => {
297
116
  lg: "md",
298
117
  };
299
118
 
300
- return sizes[props.size];
119
+ return sizes[props.size] as ButtonSize;
301
120
  });
302
121
 
303
122
  onMounted(() => {
304
- dropZoneRef.value.addEventListener("dragover", onDragOver);
305
- dropZoneRef.value.addEventListener("dragleave", onDragLeave);
123
+ if (dropZoneRef.value) {
124
+ dropZoneRef.value.addEventListener("dragover", onDragOver);
125
+ dropZoneRef.value.addEventListener("dragleave", onDragLeave);
126
+ }
306
127
  });
307
128
 
308
129
  onBeforeUnmount(() => {
309
- dropZoneRef.value.removeEventListener("dragover", onDragOver);
310
- dropZoneRef.value.removeEventListener("dragleave", onDragLeave);
130
+ if (dropZoneRef.value) {
131
+ dropZoneRef.value.removeEventListener("dragover", onDragOver);
132
+ dropZoneRef.value.removeEventListener("dragleave", onDragLeave);
133
+ }
311
134
  });
312
135
 
313
- watch(
314
- () => props.multiple,
315
- () => {
316
- if (!props.multiple && Array.isArray(currentFiles.value)) {
317
- currentFiles.value = currentFiles.value[0];
318
- }
136
+ watch(() => props.multiple, normalizeFilesForMultipleMode);
319
137
 
320
- if (props.multiple && !Array.isArray(currentFiles.value)) {
321
- currentFiles.value = currentFiles.value ? [currentFiles.value] : [];
322
- }
323
- },
324
- );
138
+ function normalizeFilesForMultipleMode() {
139
+ if (!props.multiple) return;
140
+
141
+ if (!Array.isArray(currentFiles.value)) {
142
+ currentFiles.value = currentFiles.value ? [currentFiles.value] : [];
143
+ }
144
+ }
325
145
 
326
- function removeDuplicates(files) {
327
- return files.filter((file) => !fileList.value.find((item) => item.name === file.name));
146
+ function removeDuplicates(files: File[]) {
147
+ return files.filter(
148
+ (file) =>
149
+ file instanceof File &&
150
+ !fileList.value.some((item) => item instanceof File && item.name === file.name),
151
+ );
328
152
  }
329
153
 
330
- function validate(file) {
154
+ function validate(file: File) {
331
155
  const targetFileSize = getFileMbSize(file);
332
156
 
333
157
  const isValidType = extensionNames.value.length
334
158
  ? extensionNames.value.some((item) => file.type.includes(item))
335
159
  : true;
336
160
 
337
- const isValidSize = targetFileSize <= props.maxFileSize;
161
+ const isValidSize = Number(targetFileSize) <= props.maxFileSize;
338
162
 
339
163
  if (!isValidSize && props.maxFileSize) {
340
164
  currentError.value = currentLocale.value.sizeError;
@@ -345,20 +169,37 @@ function validate(file) {
345
169
  }
346
170
  }
347
171
 
348
- function onChangeFile(event) {
349
- validate(event.target.files[0]);
172
+ function onChangeFile(event: Event) {
173
+ const target = event.target as HTMLInputElement | null;
350
174
 
351
- if (currentError.value) {
352
- onClickResetFiles();
175
+ if (target && target.files) {
176
+ const file = target.files[0];
353
177
 
354
- return;
355
- }
178
+ validate(file);
356
179
 
357
- currentFiles.value = props.multiple
358
- ? [...(currentFiles.value || []), ...removeDuplicates(Array.from(event.target.files))]
359
- : Array.from(event.target.files).at(0);
180
+ if (currentError.value) {
181
+ onClickResetFiles();
360
182
 
361
- if (fileInputRef.value) fileInputRef.value.value = "";
183
+ return;
184
+ }
185
+
186
+ if (props.multiple) {
187
+ if (!Array.isArray(currentFiles.value)) {
188
+ currentFiles.value = currentFiles.value instanceof File ? [currentFiles.value] : [];
189
+ }
190
+
191
+ currentFiles.value = [
192
+ ...currentFiles.value,
193
+ ...removeDuplicates(
194
+ Array.from(target.files).filter((file): file is File => file instanceof File),
195
+ ),
196
+ ];
197
+ } else {
198
+ currentFiles.value = file instanceof File ? file : null;
199
+ }
200
+
201
+ if (fileInputRef.value) fileInputRef.value.value = "";
202
+ }
362
203
  }
363
204
 
364
205
  function onClickResetFiles() {
@@ -367,32 +208,39 @@ function onClickResetFiles() {
367
208
  if (fileInputRef.value) fileInputRef.value.value = "";
368
209
  }
369
210
 
370
- function onDragOver(event) {
211
+ function onDragOver(event: DragEvent) {
371
212
  event.preventDefault();
372
213
 
373
- dropZoneRef.value.classList.add(...config.value.dropzoneHover.split(" "));
214
+ if (dropZoneRef.value && config.value && config.value.dropzoneHover) {
215
+ dropZoneRef.value.classList.add(...config.value.dropzoneHover.split(" "));
216
+ }
374
217
  }
375
218
 
376
- function onDragLeave(event) {
219
+ function onDragLeave(event: DragEvent) {
377
220
  event.preventDefault();
378
221
 
379
- dropZoneRef.value.classList.remove(...config.value.dropzoneHover.split(" "));
222
+ if (dropZoneRef.value && config.value && config.value.dropzoneHover) {
223
+ dropZoneRef.value.classList.remove(...config.value.dropzoneHover.split(" "));
224
+ }
380
225
  }
381
226
 
382
- function onDrop(event) {
227
+ function onDrop(event: DragEvent) {
383
228
  event.preventDefault();
384
229
 
385
- let targetFiles = null;
230
+ let targetFiles: (File | null)[] = [];
386
231
 
387
- if (event.dataTransfer.items) {
232
+ if (event.dataTransfer && event.dataTransfer.items) {
388
233
  targetFiles = [...event.dataTransfer.items]
389
234
  .filter((item) => item.kind === "file")
390
- .map((item) => item.getAsFile());
391
- } else {
235
+ .map((item) => item.getAsFile())
236
+ .filter((file): file is File => !file);
237
+ } else if (event.dataTransfer && event.dataTransfer.files) {
392
238
  targetFiles = [...event.dataTransfer.files];
393
239
  }
394
240
 
395
- if (targetFiles.length) targetFiles.forEach(validate);
241
+ if (targetFiles.length) {
242
+ targetFiles.filter((file): file is File => !file).forEach(validate);
243
+ }
396
244
 
397
245
  nextTick(() => {
398
246
  if (currentError.value || !targetFiles.length) {
@@ -401,13 +249,108 @@ function onDrop(event) {
401
249
  return;
402
250
  }
403
251
 
252
+ const validFiles = targetFiles.filter((file): file is File => !file);
253
+
404
254
  currentFiles.value = props.multiple
405
- ? [...(currentFiles.value || []), ...removeDuplicates(targetFiles)]
406
- : targetFiles[0];
255
+ ? [
256
+ ...(Array.isArray(currentFiles.value) ? currentFiles.value : []),
257
+ ...removeDuplicates(validFiles),
258
+ ]
259
+ : validFiles[0];
407
260
  });
408
261
  }
409
262
 
410
- function onClickRemoveItem(id) {
411
- currentFiles.value = currentFiles.value?.filter((file) => file.name !== id);
263
+ function onClickRemoveItem(id: string | number) {
264
+ if (Array.isArray(currentFiles.value)) {
265
+ currentFiles.value = currentFiles.value.filter((file) => file.name !== id);
266
+ }
412
267
  }
413
268
  </script>
269
+
270
+ <template>
271
+ <ULabel
272
+ :for="elementId"
273
+ :size="size"
274
+ :label="label"
275
+ :error="error"
276
+ :align="labelAlign"
277
+ :disabled="disabled"
278
+ :description="description"
279
+ v-bind="inputLabelAttrs"
280
+ >
281
+ <div ref="dropZoneRef" :ondrop="onDrop" v-bind="dropzoneAttrs">
282
+ <UText v-if="hasSlotContent($slots['top'])" :size="size" v-bind="descriptionTopAttrs">
283
+ <!-- @slot Use it to add something above the component content. -->
284
+ <slot name="top" />
285
+ </UText>
286
+
287
+ <div v-bind="contentAttrs">
288
+ <!-- @slot Use it to add something before the placeholder. -->
289
+ <slot name="left" />
290
+
291
+ <span v-if="!isValue" v-bind="placeholderAttrs" v-text="currentLocale.noFile" />
292
+
293
+ <UFiles
294
+ :size="size"
295
+ v-bind="fileListAttrs"
296
+ :file-list="fileList"
297
+ :removable="multiple && !disabled"
298
+ @remove="onClickRemoveItem"
299
+ >
300
+ <template #right="{ file }">
301
+ <slot name="right" :file="file" />
302
+ </template>
303
+ </UFiles>
304
+
305
+ <div v-bind="buttonsAttrs">
306
+ <template v-if="Array.isArray(currentFiles) || !currentFiles">
307
+ <UButton
308
+ filled
309
+ no-ring
310
+ :for="elementId"
311
+ tag="label"
312
+ variant="thirdary"
313
+ :size="buttonSize"
314
+ :right-icon="config.defaults?.chooseFileIcon"
315
+ :label="currentLocale.uploadFile"
316
+ :disabled="disabled"
317
+ v-bind="chooseFileButtonAttrs"
318
+ :data-test="`${dataTest}-upload`"
319
+ />
320
+
321
+ <input
322
+ :id="elementId"
323
+ ref="fileInputRef"
324
+ type="file"
325
+ :disabled="disabled"
326
+ :accept="accept"
327
+ :multiple="multiple"
328
+ v-bind="inputAttrs"
329
+ @change="onChangeFile"
330
+ />
331
+ </template>
332
+
333
+ <UButton
334
+ v-if="isValue && !disabled"
335
+ round
336
+ square
337
+ filled
338
+ no-ring
339
+ variant="thirdary"
340
+ :size="buttonSize"
341
+ :disabled="disabled"
342
+ :left-icon="config.defaults?.clearIcon"
343
+ v-bind="clearButtonAttrs"
344
+ :data-test="`${dataTest}-clear`"
345
+ @click="onClickResetFiles"
346
+ />
347
+ </div>
348
+ </div>
349
+
350
+ <UText v-if="hasSlotContent($slots['bottom'])" :size="size" v-bind="descriptionBottomAttrs">
351
+ <!-- @slot Use it to add something below the component content. -->
352
+ <slot name="bottom" />
353
+ </UText>
354
+ </div>
355
+ </ULabel>
356
+ </template>
@@ -1,8 +1,8 @@
1
1
  import { Meta, Title, Subtitle, Description, Primary, Controls, Stories, Source } from "@storybook/blocks";
2
2
  import { getSource } from "../../utils/storybook.ts";
3
3
 
4
- import * as stories from "./stories.js";
5
- import defaultConfig from "../config.js?raw"
4
+ import * as stories from "./stories.ts";
5
+ import defaultConfig from "../config.ts?raw"
6
6
 
7
7
  <Meta of={stories} />
8
8
  <Title of={stories} />