@volverjs/ui-vue 0.0.10-beta.2 → 0.0.10-beta.21

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 (258) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +98 -3
  3. package/auto-imports.d.ts +6 -2
  4. package/bin/icons.cjs +1 -1
  5. package/bin/icons.js +23 -16
  6. package/dist/Volver.d.ts +1 -1
  7. package/dist/components/VvAccordion/VvAccordion.es.js +70 -14
  8. package/dist/components/VvAccordion/VvAccordion.umd.js +1 -1
  9. package/dist/components/VvAccordion/VvAccordion.vue.d.ts +13 -6
  10. package/dist/components/VvAccordion/index.d.ts +4 -1
  11. package/dist/components/VvAccordionGroup/VvAccordionGroup.es.js +117 -38
  12. package/dist/components/VvAccordionGroup/VvAccordionGroup.umd.js +1 -1
  13. package/dist/components/VvAccordionGroup/VvAccordionGroup.vue.d.ts +15 -8
  14. package/dist/components/VvAccordionGroup/index.d.ts +4 -1
  15. package/dist/components/VvAction/VvAction.es.js +58 -13
  16. package/dist/components/VvAction/VvAction.umd.js +1 -1
  17. package/dist/components/VvAction/VvAction.vue.d.ts +59 -12
  18. package/dist/components/VvAction/index.d.ts +25 -4
  19. package/dist/components/VvAlert/VvAlert.es.js +195 -152
  20. package/dist/components/VvAlert/VvAlert.umd.js +1 -1
  21. package/dist/components/VvAlert/VvAlert.vue.d.ts +18 -8
  22. package/dist/components/VvAlert/index.d.ts +9 -5
  23. package/dist/components/VvAlertGroup/VvAlertGroup.es.js +240 -174
  24. package/dist/components/VvAlertGroup/VvAlertGroup.umd.js +1 -1
  25. package/dist/components/VvAlertGroup/VvAlertGroup.vue.d.ts +13 -6
  26. package/dist/components/VvAlertGroup/index.d.ts +6 -2
  27. package/dist/components/VvAvatar/VvAvatar.es.js +54 -9
  28. package/dist/components/VvAvatar/VvAvatar.umd.js +1 -1
  29. package/dist/components/VvAvatar/VvAvatar.vue.d.ts +12 -4
  30. package/dist/components/VvAvatar/index.d.ts +4 -1
  31. package/dist/components/VvAvatarGroup/VvAvatarGroup.es.js +111 -36
  32. package/dist/components/VvAvatarGroup/VvAvatarGroup.umd.js +1 -1
  33. package/dist/components/VvAvatarGroup/VvAvatarGroup.vue.d.ts +10 -3
  34. package/dist/components/VvAvatarGroup/index.d.ts +4 -1
  35. package/dist/components/VvBadge/VvBadge.es.js +73 -17
  36. package/dist/components/VvBadge/VvBadge.umd.js +1 -1
  37. package/dist/components/VvBadge/VvBadge.vue.d.ts +12 -4
  38. package/dist/components/VvBadge/index.d.ts +4 -1
  39. package/dist/components/VvBreadcrumb/VvBreadcrumb.es.js +259 -49
  40. package/dist/components/VvBreadcrumb/VvBreadcrumb.umd.js +1 -1
  41. package/dist/components/VvBreadcrumb/VvBreadcrumb.vue.d.ts +27 -7
  42. package/dist/components/VvBreadcrumb/index.d.ts +6 -10
  43. package/dist/components/VvButton/VvButton.es.js +187 -141
  44. package/dist/components/VvButton/VvButton.umd.js +1 -1
  45. package/dist/components/VvButton/VvButton.vue.d.ts +101 -27
  46. package/dist/components/VvButton/index.d.ts +41 -14
  47. package/dist/components/VvButtonGroup/VvButtonGroup.es.js +69 -16
  48. package/dist/components/VvButtonGroup/VvButtonGroup.umd.js +1 -1
  49. package/dist/components/VvButtonGroup/VvButtonGroup.vue.d.ts +23 -10
  50. package/dist/components/VvButtonGroup/index.d.ts +8 -2
  51. package/dist/components/VvCard/VvCard.es.js +84 -25
  52. package/dist/components/VvCard/VvCard.umd.js +1 -1
  53. package/dist/components/VvCard/VvCard.vue.d.ts +12 -4
  54. package/dist/components/VvCard/index.d.ts +4 -1
  55. package/dist/components/VvCheckbox/VvCheckbox.es.js +91 -22
  56. package/dist/components/VvCheckbox/VvCheckbox.umd.js +1 -1
  57. package/dist/components/VvCheckbox/VvCheckbox.vue.d.ts +104 -32
  58. package/dist/components/VvCheckbox/index.d.ts +45 -12
  59. package/dist/components/VvCheckboxGroup/VvCheckboxGroup.es.js +180 -67
  60. package/dist/components/VvCheckboxGroup/VvCheckboxGroup.umd.js +1 -1
  61. package/dist/components/VvCheckboxGroup/VvCheckboxGroup.vue.d.ts +100 -29
  62. package/dist/components/VvCheckboxGroup/index.d.ts +45 -12
  63. package/dist/components/VvCombobox/VvCombobox.es.js +758 -531
  64. package/dist/components/VvCombobox/VvCombobox.umd.js +1 -1
  65. package/dist/components/VvCombobox/VvCombobox.vue.d.ts +181 -108
  66. package/dist/components/VvCombobox/index.d.ts +53 -22
  67. package/dist/components/VvDialog/VvDialog.es.js +136 -141
  68. package/dist/components/VvDialog/VvDialog.umd.js +1 -1
  69. package/dist/components/VvDialog/VvDialog.vue.d.ts +4 -4
  70. package/dist/components/VvDropdown/VvDropdown.es.js +121 -55
  71. package/dist/components/VvDropdown/VvDropdown.umd.js +1 -1
  72. package/dist/components/VvDropdown/VvDropdown.vue.d.ts +101 -75
  73. package/dist/components/VvDropdown/VvDropdownAction.vue.d.ts +72 -11
  74. package/dist/components/VvDropdown/VvDropdownItem.vue.d.ts +1 -1
  75. package/dist/components/VvDropdown/VvDropdownOptgroup.vue.d.ts +12 -4
  76. package/dist/components/VvDropdown/VvDropdownOption.vue.d.ts +26 -7
  77. package/dist/components/VvDropdown/index.d.ts +16 -11
  78. package/dist/components/VvDropdownAction/VvDropdownAction.es.js +82 -22
  79. package/dist/components/VvDropdownAction/VvDropdownAction.umd.js +1 -1
  80. package/dist/components/VvDropdownItem/VvDropdownItem.es.js +13 -7
  81. package/dist/components/VvDropdownOptgroup/VvDropdownOptgroup.es.js +56 -8
  82. package/dist/components/VvDropdownOptgroup/VvDropdownOptgroup.umd.js +1 -1
  83. package/dist/components/VvDropdownOption/VvDropdownOption.es.js +76 -17
  84. package/dist/components/VvDropdownOption/VvDropdownOption.umd.js +1 -1
  85. package/dist/components/VvIcon/VvIcon.es.js +23 -96
  86. package/dist/components/VvIcon/VvIcon.umd.js +1 -1
  87. package/dist/components/VvIcon/VvIcon.vue.d.ts +23 -66
  88. package/dist/components/VvIcon/index.d.ts +33 -48
  89. package/dist/components/VvInputFile/VvInputFile.es.js +1734 -0
  90. package/dist/components/VvInputFile/VvInputFile.umd.js +1 -0
  91. package/dist/components/VvInputFile/VvInputFile.vue.d.ts +313 -0
  92. package/dist/components/VvInputFile/index.d.ts +179 -0
  93. package/dist/components/VvInputText/VvInputClearAction.d.ts +7 -5
  94. package/dist/components/VvInputText/VvInputPasswordAction.d.ts +10 -8
  95. package/dist/components/VvInputText/VvInputStepAction.d.ts +1 -1
  96. package/dist/components/VvInputText/VvInputText.es.js +331 -293
  97. package/dist/components/VvInputText/VvInputText.umd.js +1 -1
  98. package/dist/components/VvInputText/VvInputText.vue.d.ts +162 -55
  99. package/dist/components/VvInputText/index.d.ts +71 -29
  100. package/dist/components/VvNav/VvNav.es.js +151 -73
  101. package/dist/components/VvNav/VvNav.umd.js +1 -1
  102. package/dist/components/VvNav/VvNav.vue.d.ts +41 -14
  103. package/dist/components/VvNav/VvNavItem.vue.d.ts +9 -0
  104. package/dist/components/VvNav/VvNavSeparator.vue.d.ts +2 -0
  105. package/dist/components/VvNav/index.d.ts +5 -13
  106. package/dist/components/VvNavItem/VvNavItem.es.js +436 -0
  107. package/dist/components/VvNavItem/VvNavItem.umd.js +1 -0
  108. package/dist/components/VvNavSeparator/VvNavSeparator.es.js +24 -0
  109. package/dist/components/VvNavSeparator/VvNavSeparator.umd.js +1 -0
  110. package/dist/components/VvProgress/VvProgress.es.js +65 -14
  111. package/dist/components/VvProgress/VvProgress.umd.js +1 -1
  112. package/dist/components/VvProgress/VvProgress.vue.d.ts +10 -3
  113. package/dist/components/VvProgress/index.d.ts +4 -1
  114. package/dist/components/VvRadio/VvRadio.es.js +89 -21
  115. package/dist/components/VvRadio/VvRadio.umd.js +1 -1
  116. package/dist/components/VvRadio/VvRadio.vue.d.ts +102 -30
  117. package/dist/components/VvRadio/index.d.ts +44 -11
  118. package/dist/components/VvRadioGroup/VvRadioGroup.es.js +180 -66
  119. package/dist/components/VvRadioGroup/VvRadioGroup.umd.js +1 -1
  120. package/dist/components/VvRadioGroup/VvRadioGroup.vue.d.ts +100 -29
  121. package/dist/components/VvRadioGroup/index.d.ts +45 -12
  122. package/dist/components/VvSelect/VvSelect.es.js +248 -226
  123. package/dist/components/VvSelect/VvSelect.umd.js +1 -1
  124. package/dist/components/VvSelect/VvSelect.vue.d.ts +112 -39
  125. package/dist/components/VvSelect/index.d.ts +48 -14
  126. package/dist/components/VvTab/VvTab.es.js +256 -110
  127. package/dist/components/VvTab/VvTab.umd.js +1 -1
  128. package/dist/components/VvTab/VvTab.vue.d.ts +50 -13
  129. package/dist/components/VvTab/index.d.ts +13 -4
  130. package/dist/components/VvTextarea/VvTextarea.es.js +229 -212
  131. package/dist/components/VvTextarea/VvTextarea.umd.js +1 -1
  132. package/dist/components/VvTextarea/VvTextarea.vue.d.ts +155 -48
  133. package/dist/components/VvTextarea/index.d.ts +68 -19
  134. package/dist/components/VvTooltip/VvTooltip.es.js +72 -17
  135. package/dist/components/VvTooltip/VvTooltip.umd.js +1 -1
  136. package/dist/components/VvTooltip/VvTooltip.vue.d.ts +10 -3
  137. package/dist/components/VvTooltip/index.d.ts +4 -1
  138. package/dist/components/common/HintSlot.d.ts +1 -1
  139. package/dist/components/index.d.ts +10 -0
  140. package/dist/components/index.es.js +2902 -1329
  141. package/dist/components/index.umd.js +1 -1
  142. package/dist/composables/alert/useAlert.d.ts +37 -4
  143. package/dist/composables/dropdown/useProvideDropdown.d.ts +1 -1
  144. package/dist/composables/index.d.ts +1 -0
  145. package/dist/composables/index.es.js +88 -1
  146. package/dist/composables/index.umd.js +1 -1
  147. package/dist/composables/useBlurhash.d.ts +7 -0
  148. package/dist/composables/useComponentIcon.d.ts +9 -8
  149. package/dist/composables/useVolver.d.ts +1 -1
  150. package/dist/directives/index.d.ts +3 -5
  151. package/dist/directives/index.es.js +92 -31
  152. package/dist/directives/index.umd.js +1 -1
  153. package/dist/directives/v-tooltip.es.js +90 -26
  154. package/dist/directives/v-tooltip.umd.js +1 -1
  155. package/dist/icons.es.js +210 -210
  156. package/dist/icons.umd.js +1 -1
  157. package/dist/index.d.ts +3 -1
  158. package/dist/index.es.js +81 -16
  159. package/dist/index.umd.js +1 -1
  160. package/dist/props/index.d.ts +287 -73
  161. package/dist/resolvers/unplugin.d.ts +6 -1
  162. package/dist/resolvers/unplugin.es.js +78 -10
  163. package/dist/resolvers/unplugin.umd.js +1 -1
  164. package/dist/stories/AccordionGroup/AccordionGroup.stories.d.ts +72 -84
  165. package/dist/stories/AccordionGroup/AccordionGroupSlots.stories.d.ts +623 -461
  166. package/dist/stories/AlertGroup/AlertGroupWithComposable.stories.d.ts +1 -1
  167. package/dist/stories/Blurhash/BlurhashComposable.stories.d.ts +4 -0
  168. package/dist/stories/Combobox/Combobox.settings.d.ts +8 -0
  169. package/dist/stories/Icon/Icon.settings.d.ts +1 -0
  170. package/dist/stories/InputFile/InputFile.settings.d.ts +56 -0
  171. package/dist/stories/InputFile/InputFile.stories.d.ts +12 -0
  172. package/dist/stories/InputFile/InputFileDropArea.stories.d.ts +9 -0
  173. package/dist/stories/InputFile/InputFileIconPosition.stories.d.ts +8 -0
  174. package/dist/stories/InputFile/InputFileSlots.stories.d.ts +7 -0
  175. package/dist/stories/Tab/Tab.settings.d.ts +4 -37
  176. package/dist/types/alert.d.ts +13 -0
  177. package/dist/types/blurhash.d.ts +12 -0
  178. package/dist/types/floating-ui.d.ts +6 -0
  179. package/dist/types/generic.d.ts +4 -0
  180. package/dist/types/group.d.ts +37 -0
  181. package/dist/types/index.d.ts +7 -0
  182. package/dist/types/input-file.d.ts +16 -0
  183. package/dist/types/nav.d.ts +18 -0
  184. package/dist/utils/ObjectUtilities.d.ts +0 -1
  185. package/dist/workers/blurhash.d.ts +1 -0
  186. package/package.json +97 -80
  187. package/src/Volver.ts +31 -20
  188. package/src/assets/icons/detailed.json +1 -1
  189. package/src/assets/icons/normal.json +1 -1
  190. package/src/assets/icons/simple.json +1 -1
  191. package/src/components/VvAccordion/VvAccordion.vue +2 -2
  192. package/src/components/VvAction/VvAction.vue +5 -2
  193. package/src/components/VvAlert/index.ts +1 -3
  194. package/src/components/VvAlertGroup/index.ts +2 -1
  195. package/src/components/VvBreadcrumb/VvBreadcrumb.vue +20 -19
  196. package/src/components/VvBreadcrumb/index.ts +2 -8
  197. package/src/components/VvButton/VvButton.vue +6 -6
  198. package/src/components/VvButton/index.ts +2 -4
  199. package/src/components/VvCombobox/VvCombobox.vue +24 -16
  200. package/src/components/VvCombobox/index.ts +4 -0
  201. package/src/components/VvIcon/VvIcon.vue +2 -2
  202. package/src/components/VvIcon/index.ts +35 -48
  203. package/src/components/VvInputFile/VvInputFile.vue +365 -0
  204. package/src/components/VvInputFile/index.ts +116 -0
  205. package/src/components/VvInputText/VvInputClearAction.ts +10 -6
  206. package/src/components/VvInputText/VvInputPasswordAction.ts +13 -9
  207. package/src/components/VvInputText/VvInputText.vue +17 -18
  208. package/src/components/VvInputText/index.ts +7 -15
  209. package/src/components/VvNav/VvNav.vue +30 -50
  210. package/src/components/VvNav/VvNavItem.vue +18 -0
  211. package/src/components/VvNav/VvNavSeparator.vue +11 -0
  212. package/src/components/VvNav/index.ts +2 -15
  213. package/src/components/VvSelect/VvSelect.vue +5 -8
  214. package/src/components/VvTab/VvTab.vue +63 -35
  215. package/src/components/VvTab/index.ts +10 -4
  216. package/src/components/VvTextarea/VvTextarea.vue +6 -9
  217. package/src/components/index.ts +10 -0
  218. package/src/composables/index.ts +1 -0
  219. package/src/composables/useBlurhash.ts +76 -0
  220. package/src/composables/useComponentIcon.ts +15 -14
  221. package/src/composables/useUniqueId.ts +2 -2
  222. package/src/directives/index.ts +3 -6
  223. package/src/directives/v-tooltip.ts +19 -10
  224. package/src/index.ts +3 -1
  225. package/src/props/index.ts +115 -27
  226. package/src/resolvers/unplugin.ts +24 -14
  227. package/src/stories/AlertGroup/AlertGroupWithComposable.stories.ts +2 -2
  228. package/src/stories/Blurhash/BlurhashComposable.stories.ts +195 -0
  229. package/src/stories/Combobox/Combobox.settings.ts +8 -0
  230. package/src/stories/Icon/Icon.settings.ts +3 -3
  231. package/src/stories/InputFile/InputFile.settings.ts +36 -0
  232. package/src/stories/InputFile/InputFile.stories.ts +89 -0
  233. package/src/stories/InputFile/InputFileDropArea.stories.ts +56 -0
  234. package/src/stories/InputFile/InputFileIconPosition.stories.ts +43 -0
  235. package/src/stories/InputFile/InputFileSlots.stories.ts +33 -0
  236. package/src/stories/Nav/Nav.settings.ts +3 -4
  237. package/src/stories/Nav/Nav.test.ts +4 -15
  238. package/src/stories/Tab/Tab.settings.ts +9 -9
  239. package/src/stories/Tab/Tab.stories.ts +2 -2
  240. package/src/stories/Tab/Tab.test.ts +6 -14
  241. package/src/stories/argTypes.ts +1 -1
  242. package/src/types/blurhash.ts +21 -0
  243. package/src/types/generic.ts +6 -0
  244. package/src/types/index.ts +7 -0
  245. package/src/types/input-file.ts +18 -0
  246. package/src/types/nav.ts +20 -0
  247. package/src/utils/ObjectUtilities.ts +0 -11
  248. package/src/workers/blurhash.ts +9 -0
  249. package/dist/components/VvNav/VvNavItemTitle.vue.d.ts +0 -6
  250. package/dist/components/VvNav/VvNavSeparator.d.ts +0 -2
  251. package/dist/components/VvNavItemTitle/VvNavItemTitle.es.js +0 -19
  252. package/dist/components/VvNavItemTitle/VvNavItemTitle.umd.js +0 -1
  253. package/src/components/VvNav/VvNavItemTitle.vue +0 -11
  254. package/src/components/VvNav/VvNavSeparator.ts +0 -8
  255. package/src/types/generic.d.ts +0 -6
  256. /package/src/types/{alert.d.ts → alert.ts} +0 -0
  257. /package/src/types/{floating-ui.d.ts → floating-ui.ts} +0 -0
  258. /package/src/types/{group.d.ts → group.ts} +0 -0
@@ -0,0 +1,365 @@
1
+ <script lang="ts">
2
+ export default {
3
+ name: 'VvInputFile',
4
+ }
5
+ </script>
6
+
7
+ <script setup lang="ts">
8
+ import { useVModel } from '@vueuse/core'
9
+ import type { UploadedFile } from '../../types'
10
+ import { computed, onBeforeUnmount, ref } from 'vue'
11
+ import VvButton from '../VvButton/VvButton.vue'
12
+ import VvIcon from '../VvIcon/VvIcon.vue'
13
+ import HintSlotFactory from '../common/HintSlot'
14
+ import { VvInputFileProps, type VvInputFileEvents } from '.'
15
+
16
+ // props, emit, slots and attrs
17
+ const props = defineProps(VvInputFileProps)
18
+ const emit = defineEmits<VvInputFileEvents>()
19
+ const slots = useSlots()
20
+
21
+ // props merged with volver defaults (now only for labels)
22
+ const propsDefaults = useDefaults<typeof VvInputFileProps>(
23
+ 'VvInputFile',
24
+ VvInputFileProps,
25
+ props,
26
+ )
27
+ const { modifiers, id, readonly, icon, iconPosition, iconDownload } =
28
+ toRefs(props)
29
+ const hasId = useUniqueId(id)
30
+ const hasHintId = computed(() => `${hasId.value}-hint`)
31
+
32
+ const hasProgress = computed(() => {
33
+ if (!props.progress) {
34
+ return false
35
+ }
36
+ const progress =
37
+ typeof props.progress === 'string'
38
+ ? parseInt(props.progress)
39
+ : props.progress
40
+ return progress > 0 && progress < 100
41
+ })
42
+
43
+ const { hasIconBefore, hasIconAfter } = useComponentIcon(icon, iconPosition)
44
+ const { hasIcon: hasIconDownload } = useComponentIcon(iconDownload)
45
+
46
+ // styles
47
+ const bemCssClasses = useModifiers(
48
+ 'vv-input-file',
49
+ modifiers,
50
+ computed(() => ({
51
+ dragging: isDragging.value,
52
+ loading: props.loading && !hasProgress.value,
53
+ valid: props.valid === true,
54
+ invalid: props.invalid === true,
55
+ 'icon-before': !!hasIconBefore.value,
56
+ 'icon-after': !!hasIconAfter.value,
57
+ 'drop-area': hasDropArea.value,
58
+ })),
59
+ )
60
+
61
+ const {
62
+ HintSlot,
63
+ hasHintLabelOrSlot,
64
+ hasInvalidLabelOrSlot,
65
+ hintSlotScope,
66
+ } = HintSlotFactory(propsDefaults, slots)
67
+
68
+ const localModelValue = useVModel(props, 'modelValue', emit)
69
+ const files = computed(() => {
70
+ if (
71
+ !localModelValue.value ||
72
+ (!Array.isArray(localModelValue.value) &&
73
+ !(localModelValue.value as File)?.name)
74
+ ) {
75
+ return []
76
+ }
77
+ return Array.isArray(localModelValue.value)
78
+ ? localModelValue.value
79
+ : [localModelValue.value]
80
+ })
81
+
82
+ const hasMax = computed(() => {
83
+ return typeof props.max === 'string' ? parseInt(props.max) : props.max
84
+ })
85
+
86
+ const hasDropArea = computed(() => {
87
+ return props.dropArea && !readonly.value
88
+ })
89
+
90
+ const isMultiple = computed(() => {
91
+ if (!props.multiple) {
92
+ return false
93
+ }
94
+ if (!hasMax.value) {
95
+ return true
96
+ }
97
+ return hasMax.value - files.value.length > 1
98
+ })
99
+
100
+ const isDragging = ref(false)
101
+
102
+ const inputEl = ref<HTMLInputElement>()
103
+ const onDragenter = () => {
104
+ isDragging.value = true
105
+ }
106
+
107
+ const onDragleave = () => {
108
+ isDragging.value = false
109
+ }
110
+
111
+ const onDrop = (event: DragEvent) => {
112
+ if (!event.dataTransfer?.files) {
113
+ return
114
+ }
115
+ isDragging.value = false
116
+ addFiles(event.dataTransfer?.files)
117
+ }
118
+
119
+ const onChange = () => {
120
+ if (!inputEl.value?.files) {
121
+ return
122
+ }
123
+ addFiles(inputEl.value.files)
124
+ inputEl.value.value = ''
125
+ }
126
+
127
+ const addFiles = (uploadedFiles: FileList) => {
128
+ if (!props.multiple) {
129
+ if (Array.isArray(localModelValue.value)) {
130
+ localModelValue.value = [...uploadedFiles]
131
+ return
132
+ }
133
+ localModelValue.value = uploadedFiles[0]
134
+ return
135
+ }
136
+ let toReturn: (File | UploadedFile)[] = []
137
+ if (!Array.isArray(localModelValue.value) && localModelValue.value) {
138
+ toReturn = [localModelValue.value]
139
+ } else {
140
+ toReturn =
141
+ localModelValue.value && Array.isArray(localModelValue.value)
142
+ ? [...localModelValue.value]
143
+ : toReturn
144
+ }
145
+ for (const file of uploadedFiles) {
146
+ if (hasMax.value && toReturn.length >= hasMax.value) {
147
+ break
148
+ }
149
+ toReturn.push(file)
150
+ }
151
+ localModelValue.value = toReturn
152
+ selectedFileIndex.value = toReturn.length - 1
153
+ }
154
+
155
+ const onClickDropArea = () => {
156
+ if (!inputEl.value) {
157
+ return
158
+ }
159
+ if (!readonly.value) {
160
+ inputEl.value.click()
161
+ }
162
+ }
163
+
164
+ const onClickRemoveFile = (index: number) => {
165
+ if (!Array.isArray(localModelValue.value)) {
166
+ localModelValue.value = undefined
167
+ return
168
+ }
169
+ const toReturn = [...localModelValue.value]
170
+ toReturn.splice(index, 1)
171
+ localModelValue.value = toReturn
172
+ }
173
+
174
+ const selectedFileIndex = ref(0)
175
+ const PREVIEW_MIME_TYPES = ['image/jpeg', 'image/png']
176
+ const previewSrc = computed(() => {
177
+ if (files.value.length === 0) {
178
+ return
179
+ }
180
+
181
+ if (files.value[selectedFileIndex.value] instanceof File) {
182
+ const currentFile = files.value[selectedFileIndex.value] as File
183
+ if (!PREVIEW_MIME_TYPES.includes(currentFile.type)) {
184
+ return undefined
185
+ }
186
+ return URL.createObjectURL(currentFile)
187
+ }
188
+ const currentFile = files.value[selectedFileIndex.value] as UploadedFile
189
+ if (currentFile.thumbnailUrl) {
190
+ return currentFile.thumbnailUrl
191
+ }
192
+ if (!PREVIEW_MIME_TYPES.includes(currentFile.type)) {
193
+ return undefined
194
+ }
195
+ return currentFile.url
196
+ })
197
+
198
+ watch(previewSrc, (_newValue, oldValue) => {
199
+ if (oldValue) {
200
+ URL.revokeObjectURL(oldValue)
201
+ }
202
+ })
203
+
204
+ onBeforeUnmount(() => {
205
+ if (previewSrc.value) {
206
+ URL.revokeObjectURL(previewSrc.value)
207
+ }
208
+ })
209
+
210
+ const sizeInKiB = (size?: number) => {
211
+ if (!size) {
212
+ return
213
+ }
214
+ return Math.floor(size / 1024)
215
+ }
216
+
217
+ const onClickDownloadFile = (file: File | UploadedFile) => {
218
+ const link = document.createElement('a')
219
+ if (file instanceof File) {
220
+ link.href = URL.createObjectURL(file)
221
+ } else if (file.url) {
222
+ link.href = file.url
223
+ }
224
+ link.setAttribute('download', file.name)
225
+ document.body.appendChild(link)
226
+ link.click()
227
+ document.body.removeChild(link)
228
+ URL.revokeObjectURL(link.href)
229
+ }
230
+
231
+ const onClickSelectFile = (index: number) => {
232
+ selectedFileIndex.value = index
233
+ }
234
+
235
+ const dropdAreaActionLabel = computed(() => {
236
+ if (files.value.length === 0 || isMultiple.value) {
237
+ return props.labelAdd
238
+ }
239
+ return props.labelReplace
240
+ })
241
+
242
+ const dropAreaActionIcon = computed(() => {
243
+ if (files.value.length === 0 || isMultiple.value) {
244
+ return props.iconAdd
245
+ }
246
+ return props.iconReplace
247
+ })
248
+ </script>
249
+
250
+ <template>
251
+ <div :class="bemCssClasses">
252
+ <label v-if="label" :for="hasId">
253
+ {{ label }}
254
+ </label>
255
+ <div
256
+ v-if="hasDropArea"
257
+ class="vv-input-file__drop-area"
258
+ @dragenter.prevent.stop="onDragenter"
259
+ @dragleave.prevent.stop="onDragleave"
260
+ @drop.prevent.stop="onDrop"
261
+ @dragover.prevent.stop
262
+ @click.stop="onClickDropArea"
263
+ >
264
+ <slot name="drop-area">
265
+ <picture class="vv-input-file__preview">
266
+ <img
267
+ v-if="previewSrc"
268
+ :src="previewSrc"
269
+ :alt="files[selectedFileIndex].name"
270
+ />
271
+ </picture>
272
+ <VvButton
273
+ v-if="!readonly"
274
+ modifiers="action"
275
+ :label="!previewSrc ? dropdAreaActionLabel : undefined"
276
+ :title="previewSrc ? dropdAreaActionLabel : undefined"
277
+ :class="{
278
+ 'vv-input-file__drop-area-action': previewSrc,
279
+ }"
280
+ :icon="dropAreaActionIcon"
281
+ @click.stop="onClickDropArea"
282
+ />
283
+ </slot>
284
+ </div>
285
+ <div class="vv-input-file__wrapper">
286
+ <VvIcon v-if="hasIconBefore" v-bind="hasIconBefore" />
287
+ <input
288
+ :id="hasId"
289
+ ref="inputEl"
290
+ type="file"
291
+ :readonly="readonly"
292
+ :placeholder="placeholder"
293
+ :aria-describedby="hasHintLabelOrSlot ? hasHintId : undefined"
294
+ :aria-invalid="invalid"
295
+ :aria-errormessage="
296
+ hasInvalidLabelOrSlot ? hasHintId : undefined
297
+ "
298
+ :multiple="isMultiple"
299
+ :accept="accept"
300
+ :name="name"
301
+ @change="onChange"
302
+ />
303
+ <progress
304
+ v-if="hasProgress"
305
+ class="vv-input-file__progress"
306
+ :value="progress"
307
+ max="100"
308
+ >
309
+ {{ progress }}%
310
+ </progress>
311
+ <VvIcon v-if="hasIconAfter" v-bind="hasIconAfter" />
312
+ </div>
313
+ <ul class="vv-input-file__list">
314
+ <li
315
+ v-for="(file, index) in files"
316
+ :key="index"
317
+ class="vv-input-file__item"
318
+ :class="{
319
+ active:
320
+ index === selectedFileIndex &&
321
+ hasDropArea &&
322
+ files.length > 1,
323
+ }"
324
+ @click.stop="onClickSelectFile(index)"
325
+ >
326
+ <button
327
+ v-if="hasIconDownload"
328
+ type="button"
329
+ class="vv-input-file__item-action"
330
+ :title="labelDownload"
331
+ @click.stop="onClickDownloadFile(file)"
332
+ >
333
+ <VvIcon v-bind="hasIconDownload" />
334
+ </button>
335
+ <div class="vv-input-file__item-name">
336
+ {{ file.name }}
337
+ </div>
338
+ <small class="vv-input-file__item-info">
339
+ {{ sizeInKiB(file.size) }} KB
340
+ </small>
341
+ <button
342
+ v-if="!readonly"
343
+ type="button"
344
+ class="vv-input-file__item-remove"
345
+ :title="labelRemove"
346
+ @click.stop="onClickRemoveFile(index)"
347
+ />
348
+ </li>
349
+ </ul>
350
+ <HintSlot :id="hasHintId" class="vv-input-file__hint">
351
+ <template v-if="$slots.hint" #hint>
352
+ <slot name="hint" v-bind="hintSlotScope" />
353
+ </template>
354
+ <template v-if="$slots.loading" #loading>
355
+ <slot name="loading" v-bind="hintSlotScope" />
356
+ </template>
357
+ <template v-if="$slots.valid" #valid>
358
+ <slot name="valid" v-bind="hintSlotScope" />
359
+ </template>
360
+ <template v-if="$slots.invalid" #invalid>
361
+ <slot name="invalid" v-bind="hintSlotScope" />
362
+ </template>
363
+ </HintSlot>
364
+ </div>
365
+ </template>
@@ -0,0 +1,116 @@
1
+ import type { UploadedFile } from '@/types'
2
+ import {
3
+ IdNameProps,
4
+ ModifiersProps,
5
+ ValidProps,
6
+ InvalidProps,
7
+ HintProps,
8
+ LabelProps,
9
+ LoadingProps,
10
+ ReadonlyProps,
11
+ IconProps,
12
+ } from '../../props'
13
+ import { type VvIconProps, ACTION_ICONS } from '../VvIcon'
14
+
15
+ export type VvInputFileEvents = {
16
+ 'update:modelValue': [File | undefined]
17
+ }
18
+
19
+ export const VvInputFileProps = {
20
+ ...IdNameProps,
21
+ ...ModifiersProps,
22
+ ...ValidProps,
23
+ ...InvalidProps,
24
+ ...HintProps,
25
+ ...LabelProps,
26
+ ...LoadingProps,
27
+ ...ReadonlyProps,
28
+ ...IconProps,
29
+ /**
30
+ * Input value
31
+ */
32
+ modelValue: {
33
+ type: Object as PropType<File | (File | UploadedFile)[] | UploadedFile>,
34
+ required: true,
35
+ },
36
+ /**
37
+ * Whether to show progress bar
38
+ */
39
+ progress: { type: [Number, String], default: undefined },
40
+ /**
41
+ * Input
42
+ * Text that appears in the form control when it has no value set
43
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#placeholder
44
+ */
45
+ placeholder: { type: String, default: undefined },
46
+ /**
47
+ * File types to accept
48
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept
49
+ */
50
+ accept: { type: String, default: '*' },
51
+ /**
52
+ * Whether to allow multiple values
53
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#multiple
54
+ */
55
+ multiple: { type: Boolean, default: false },
56
+ /**
57
+ * Max number of files
58
+ */
59
+ max: { type: [Number, String], default: undefined },
60
+ /**
61
+ * Show drop area
62
+ */
63
+ dropArea: { type: Boolean, default: false },
64
+ /**
65
+ * Label for add button
66
+ */
67
+ labelAdd: {
68
+ type: String,
69
+ default: 'Add file',
70
+ },
71
+ /**
72
+ * VvIcon name for add button
73
+ * @see VVIcon
74
+ */
75
+ iconAdd: {
76
+ type: [String, Object] as PropType<string | VvIconProps>,
77
+ default: ACTION_ICONS.add,
78
+ },
79
+ /**
80
+ * Label for replace button
81
+ */
82
+ labelReplace: {
83
+ type: String,
84
+ default: 'Replace file',
85
+ },
86
+ /**
87
+ * VvIcon name for replace button
88
+ * @see VVIcon
89
+ */
90
+ iconReplace: {
91
+ type: [String, Object] as PropType<string | VvIconProps>,
92
+ default: ACTION_ICONS.edit,
93
+ },
94
+ /**
95
+ * Label for download button
96
+ */
97
+ labelDownload: {
98
+ type: String,
99
+ default: 'Downlaod file',
100
+ },
101
+ /**
102
+ * VvIcon name for download button
103
+ * @see VVIcon
104
+ */
105
+ iconDownload: {
106
+ type: [String, Object] as PropType<string | VvIconProps>,
107
+ default: ACTION_ICONS.download,
108
+ },
109
+ /**
110
+ * Label for remove button
111
+ */
112
+ labelRemove: {
113
+ type: String,
114
+ default: 'Remove file',
115
+ },
116
+ }
@@ -1,3 +1,4 @@
1
+ import type { VvIconProps } from '../VvIcon'
1
2
  import VvIcon from '../VvIcon/VvIcon.vue'
2
3
 
3
4
  export default defineComponent({
@@ -14,28 +15,31 @@ export default defineComponent({
14
15
  default: 'Clear',
15
16
  },
16
17
  icon: {
17
- type: String,
18
+ type: [String, Object] as PropType<string | VvIconProps>,
18
19
  default: 'close',
19
20
  },
20
21
  },
21
22
  emits: ['clear'],
22
23
  setup(props, { emit }) {
24
+ const { hasIcon } = useComponentIcon(computed(() => props.icon))
23
25
  function onClick(e: Event) {
24
26
  e?.stopPropagation()
25
27
  if (!props.disabled) {
26
28
  emit('clear')
27
29
  }
28
30
  }
29
-
30
31
  return {
32
+ hasIcon,
31
33
  onClick,
32
34
  }
33
35
  },
34
36
  render() {
35
- const icon = h(VvIcon, {
36
- name: this.icon,
37
- class: 'vv-input-text__icon',
38
- })
37
+ const icon = this.hasIcon
38
+ ? h(VvIcon, {
39
+ ...this.hasIcon,
40
+ class: 'vv-input-text__icon',
41
+ })
42
+ : undefined
39
43
 
40
44
  return h(
41
45
  'button',
@@ -1,5 +1,5 @@
1
- import { TYPES_ICON } from '../VvInputText'
2
1
  import VvIcon from '../VvIcon/VvIcon.vue'
2
+ import { type VvIconProps, ACTION_ICONS } from '../VvIcon'
3
3
 
4
4
  export default defineComponent({
5
5
  components: {
@@ -19,12 +19,12 @@ export default defineComponent({
19
19
  default: 'Hide password',
20
20
  },
21
21
  iconShow: {
22
- type: String,
23
- default: TYPES_ICON.PASSWORD_SHOW,
22
+ type: [String, Object] as PropType<string | VvIconProps>,
23
+ default: ACTION_ICONS.showPassword,
24
24
  },
25
25
  iconHide: {
26
- type: String,
27
- default: TYPES_ICON.PASSWORD_HIDE,
26
+ type: [String, Object] as PropType<string | VvIconProps>,
27
+ default: ACTION_ICONS.hidePassword,
28
28
  },
29
29
  },
30
30
  emits: ['toggle-password'],
@@ -33,6 +33,7 @@ export default defineComponent({
33
33
  const activeIcon = computed(() =>
34
34
  active.value ? props.iconHide : props.iconShow,
35
35
  )
36
+ const { hasIcon } = useComponentIcon(activeIcon)
36
37
 
37
38
  function onClick(e: Event) {
38
39
  e?.stopPropagation()
@@ -45,14 +46,17 @@ export default defineComponent({
45
46
  return {
46
47
  active,
47
48
  activeIcon,
49
+ hasIcon,
48
50
  onClick,
49
51
  }
50
52
  },
51
53
  render() {
52
- const icon = h(VvIcon, {
53
- name: this.activeIcon,
54
- class: 'vv-input-text__icon',
55
- })
54
+ const icon = this.hasIcon
55
+ ? h(VvIcon, {
56
+ ...this.hasIcon,
57
+ class: 'vv-input-text__icon',
58
+ })
59
+ : undefined
56
60
  return h(
57
61
  'button',
58
62
  {
@@ -9,12 +9,12 @@
9
9
  import { useIMask } from 'vue-imask'
10
10
  import HintSlotFactory from '../common/HintSlot'
11
11
  import VvIcon from '../VvIcon/VvIcon.vue'
12
+ import { ACTION_ICONS } from '../VvIcon'
12
13
  import VvInputTextActionsFactory from '../VvInputText/VvInputTextActions'
13
14
  import {
14
15
  VvInputTextEvents,
15
16
  VvInputTextProps,
16
17
  INPUT_TYPES,
17
- TYPES_ICON,
18
18
  } from '../VvInputText'
19
19
 
20
20
  // props, emit, slots and attrs
@@ -227,7 +227,7 @@
227
227
  return
228
228
  }
229
229
  inputEl.value.stepUp()
230
- localModelValue.value = unref(inputEl).value
230
+ localModelValue.value = Number(unref(inputEl).value)
231
231
  }
232
232
  }
233
233
  const onStepDown = () => {
@@ -238,7 +238,7 @@
238
238
  return
239
239
  }
240
240
  inputEl.value.stepDown()
241
- localModelValue.value = unref(inputEl).value
241
+ localModelValue.value = Number(unref(inputEl).value)
242
242
  }
243
243
  }
244
244
 
@@ -249,24 +249,23 @@
249
249
  }
250
250
 
251
251
  // icons
252
- const { hasIcon, hasIconBefore, hasIconAfter } = useComponentIcon(
253
- icon,
254
- iconPosition,
255
- )
256
- const defaultAfterIcon = computed(() => {
252
+ const { hasIconBefore, hasIconAfter } = useComponentIcon(icon, iconPosition)
253
+ const iconAfter = computed(() => {
254
+ if (hasIconAfter.value !== undefined) {
255
+ return hasIconAfter.value
256
+ }
257
257
  switch (props.type) {
258
258
  case INPUT_TYPES.COLOR:
259
- return { name: TYPES_ICON.COLOR }
259
+ return { name: ACTION_ICONS.showColorPicker }
260
260
  case INPUT_TYPES.DATE:
261
261
  case INPUT_TYPES.DATETIME_LOCAL:
262
262
  case INPUT_TYPES.WEEK:
263
263
  case INPUT_TYPES.MONTH:
264
- return { name: TYPES_ICON.DATE }
264
+ return { name: ACTION_ICONS.showDatePicker }
265
265
  case INPUT_TYPES.TIME:
266
- return { name: TYPES_ICON.TIME }
267
- default:
268
- return ''
266
+ return { name: ACTION_ICONS.showTimePicker }
269
267
  }
268
+ return undefined
270
269
  })
271
270
 
272
271
  // count
@@ -307,8 +306,8 @@
307
306
  loading: loading.value,
308
307
  disabled: props.disabled,
309
308
  readonly: props.readonly,
310
- 'icon-before': hasIconBefore.value,
311
- 'icon-after': hasIconAfter.value || !isEmpty(defaultAfterIcon),
309
+ 'icon-before': !!hasIconBefore.value,
310
+ 'icon-after': !!iconAfter.value,
312
311
  floating: props.floating && !isEmpty(props.label),
313
312
  dirty: isDirty.value,
314
313
  focus: isFocused.value,
@@ -476,8 +475,8 @@
476
475
  >
477
476
  <VvIcon
478
477
  v-if="hasIconBefore"
478
+ v-bind="hasIconBefore"
479
479
  class="vv-input-text__icon"
480
- v-bind="hasIcon"
481
480
  />
482
481
  <input
483
482
  :id="hasId"
@@ -500,9 +499,9 @@
500
499
  </div>
501
500
  <!-- @slot Slot to replace right icon -->
502
501
  <VvIcon
503
- v-if="hasIconAfter || defaultAfterIcon"
502
+ v-if="iconAfter"
503
+ v-bind="iconAfter"
504
504
  class="vv-input-text__icon vv-input-text__icon-after"
505
- v-bind="hasIconAfter ? hasIcon : defaultAfterIcon"
506
505
  />
507
506
  <PasswordInputActions
508
507
  v-else-if="isPassword && !hideActions && isClickable"