@volverjs/ui-vue 0.0.10-beta.3 → 0.0.10-beta.31

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 (294) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +106 -10
  3. package/auto-imports.d.ts +7 -2
  4. package/bin/icons.cjs +1 -1
  5. package/bin/icons.js +22 -15
  6. package/dist/Volver.d.ts +1 -1
  7. package/dist/components/VvAccordion/VvAccordion.es.js +192 -103
  8. package/dist/components/VvAccordion/VvAccordion.umd.js +1 -1
  9. package/dist/components/VvAccordion/VvAccordion.vue.d.ts +160 -8
  10. package/dist/components/VvAccordion/index.d.ts +8 -9
  11. package/dist/components/VvAccordionGroup/VvAccordionGroup.es.js +451 -172
  12. package/dist/components/VvAccordionGroup/VvAccordionGroup.umd.js +1 -1
  13. package/dist/components/VvAccordionGroup/VvAccordionGroup.vue.d.ts +58 -13
  14. package/dist/components/VvAccordionGroup/index.d.ts +14 -6
  15. package/dist/components/VvAction/VvAction.es.js +78 -27
  16. package/dist/components/VvAction/VvAction.umd.js +1 -1
  17. package/dist/components/VvAction/VvAction.vue.d.ts +57 -22
  18. package/dist/components/VvAction/index.d.ts +25 -9
  19. package/dist/components/VvAlert/VvAlert.es.js +204 -171
  20. package/dist/components/VvAlert/VvAlert.umd.js +1 -1
  21. package/dist/components/VvAlert/VvAlert.vue.d.ts +24 -14
  22. package/dist/components/VvAlert/index.d.ts +17 -9
  23. package/dist/components/VvAlertGroup/VvAlertGroup.es.js +250 -188
  24. package/dist/components/VvAlertGroup/VvAlertGroup.umd.js +1 -1
  25. package/dist/components/VvAlertGroup/VvAlertGroup.vue.d.ts +16 -9
  26. package/dist/components/VvAlertGroup/index.d.ts +6 -14
  27. package/dist/components/VvAvatar/VvAvatar.es.js +61 -21
  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 +118 -48
  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 +78 -28
  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 +276 -60
  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 +291 -252
  44. package/dist/components/VvButton/VvButton.umd.js +1 -1
  45. package/dist/components/VvButton/VvButton.vue.d.ts +94 -40
  46. package/dist/components/VvButton/index.d.ts +51 -29
  47. package/dist/components/VvButtonGroup/VvButtonGroup.es.js +81 -40
  48. package/dist/components/VvButtonGroup/VvButtonGroup.umd.js +1 -1
  49. package/dist/components/VvButtonGroup/VvButtonGroup.vue.d.ts +24 -11
  50. package/dist/components/VvButtonGroup/index.d.ts +8 -2
  51. package/dist/components/VvCard/VvCard.es.js +86 -36
  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 +177 -120
  56. package/dist/components/VvCheckbox/VvCheckbox.umd.js +1 -1
  57. package/dist/components/VvCheckbox/VvCheckbox.vue.d.ts +105 -33
  58. package/dist/components/VvCheckbox/index.d.ts +47 -14
  59. package/dist/components/VvCheckboxGroup/VvCheckboxGroup.es.js +190 -107
  60. package/dist/components/VvCheckboxGroup/VvCheckboxGroup.umd.js +1 -1
  61. package/dist/components/VvCheckboxGroup/VvCheckboxGroup.vue.d.ts +101 -30
  62. package/dist/components/VvCheckboxGroup/index.d.ts +45 -12
  63. package/dist/components/VvCombobox/VvCombobox.es.js +806 -581
  64. package/dist/components/VvCombobox/VvCombobox.umd.js +1 -1
  65. package/dist/components/VvCombobox/VvCombobox.vue.d.ts +171 -104
  66. package/dist/components/VvCombobox/index.d.ts +62 -26
  67. package/dist/components/VvDialog/VvDialog.es.js +140 -153
  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 +138 -71
  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 +66 -25
  74. package/dist/components/VvDropdown/VvDropdownItem.vue.d.ts +13 -1
  75. package/dist/components/VvDropdown/VvDropdownOptgroup.vue.d.ts +12 -4
  76. package/dist/components/VvDropdown/VvDropdownOption.vue.d.ts +35 -7
  77. package/dist/components/VvDropdown/index.d.ts +52 -10
  78. package/dist/components/VvDropdownAction/VvDropdownAction.es.js +152 -39
  79. package/dist/components/VvDropdownAction/VvDropdownAction.umd.js +1 -1
  80. package/dist/components/VvDropdownItem/VvDropdownItem.es.js +335 -8
  81. package/dist/components/VvDropdownItem/VvDropdownItem.umd.js +1 -1
  82. package/dist/components/VvDropdownOptgroup/VvDropdownOptgroup.es.js +63 -20
  83. package/dist/components/VvDropdownOptgroup/VvDropdownOptgroup.umd.js +1 -1
  84. package/dist/components/VvDropdownOption/VvDropdownOption.es.js +188 -80
  85. package/dist/components/VvDropdownOption/VvDropdownOption.umd.js +1 -1
  86. package/dist/components/VvIcon/VvIcon.es.js +23 -96
  87. package/dist/components/VvIcon/VvIcon.umd.js +1 -1
  88. package/dist/components/VvIcon/VvIcon.vue.d.ts +23 -66
  89. package/dist/components/VvIcon/index.d.ts +33 -48
  90. package/dist/components/VvInputFile/VvInputFile.es.js +1777 -0
  91. package/dist/components/VvInputFile/VvInputFile.umd.js +1 -0
  92. package/dist/components/VvInputFile/VvInputFile.vue.d.ts +359 -0
  93. package/dist/components/VvInputFile/index.d.ts +200 -0
  94. package/dist/components/VvInputText/VvInputClearAction.d.ts +7 -5
  95. package/dist/components/VvInputText/VvInputPasswordAction.d.ts +10 -8
  96. package/dist/components/VvInputText/VvInputStepAction.d.ts +2 -2
  97. package/dist/components/VvInputText/VvInputText.es.js +1237 -324
  98. package/dist/components/VvInputText/VvInputText.umd.js +1 -1
  99. package/dist/components/VvInputText/VvInputText.vue.d.ts +207 -57
  100. package/dist/components/VvInputText/index.d.ts +99 -29
  101. package/dist/components/VvNav/VvNav.es.js +135 -56
  102. package/dist/components/VvNav/VvNav.umd.js +1 -1
  103. package/dist/components/VvNav/VvNav.vue.d.ts +32 -9
  104. package/dist/components/VvNav/VvNavItem.vue.d.ts +1 -1
  105. package/dist/components/VvNav/VvNavSeparator.vue.d.ts +1 -1
  106. package/dist/components/VvNav/index.d.ts +5 -2
  107. package/dist/components/VvNavItem/VvNavItem.es.js +91 -33
  108. package/dist/components/VvNavItem/VvNavItem.umd.js +1 -1
  109. package/dist/components/VvProgress/VvProgress.es.js +72 -26
  110. package/dist/components/VvProgress/VvProgress.umd.js +1 -1
  111. package/dist/components/VvProgress/VvProgress.vue.d.ts +10 -3
  112. package/dist/components/VvProgress/index.d.ts +4 -1
  113. package/dist/components/VvRadio/VvRadio.es.js +160 -103
  114. package/dist/components/VvRadio/VvRadio.umd.js +1 -1
  115. package/dist/components/VvRadio/VvRadio.vue.d.ts +103 -31
  116. package/dist/components/VvRadio/index.d.ts +50 -17
  117. package/dist/components/VvRadioGroup/VvRadioGroup.es.js +190 -107
  118. package/dist/components/VvRadioGroup/VvRadioGroup.umd.js +1 -1
  119. package/dist/components/VvRadioGroup/VvRadioGroup.vue.d.ts +101 -30
  120. package/dist/components/VvRadioGroup/index.d.ts +45 -12
  121. package/dist/components/VvSelect/VvSelect.es.js +251 -238
  122. package/dist/components/VvSelect/VvSelect.umd.js +1 -1
  123. package/dist/components/VvSelect/VvSelect.vue.d.ts +117 -44
  124. package/dist/components/VvSelect/index.d.ts +48 -14
  125. package/dist/components/VvTab/VvTab.es.js +191 -75
  126. package/dist/components/VvTab/VvTab.umd.js +1 -1
  127. package/dist/components/VvTab/VvTab.vue.d.ts +31 -7
  128. package/dist/components/VvTab/index.d.ts +4 -1
  129. package/dist/components/VvTextarea/VvTextarea.es.js +232 -224
  130. package/dist/components/VvTextarea/VvTextarea.umd.js +1 -1
  131. package/dist/components/VvTextarea/VvTextarea.vue.d.ts +156 -49
  132. package/dist/components/VvTextarea/index.d.ts +68 -19
  133. package/dist/components/VvTooltip/VvTooltip.es.js +79 -29
  134. package/dist/components/VvTooltip/VvTooltip.umd.js +1 -1
  135. package/dist/components/VvTooltip/VvTooltip.vue.d.ts +13 -6
  136. package/dist/components/VvTooltip/index.d.ts +5 -2
  137. package/dist/components/common/HintSlot.d.ts +1 -1
  138. package/dist/components/index.d.ts +10 -0
  139. package/dist/components/index.es.js +3534 -1650
  140. package/dist/components/index.umd.js +1 -1
  141. package/dist/composables/alert/useAlert.d.ts +99 -4
  142. package/dist/composables/alert/useInjectAlert.d.ts +1 -6
  143. package/dist/composables/dropdown/useInjectDropdown.d.ts +3 -23
  144. package/dist/composables/dropdown/useProvideDropdown.d.ts +3 -4
  145. package/dist/composables/group/useInjectedGroupState.d.ts +4 -5
  146. package/dist/composables/group/useProvideGroupState.d.ts +3 -3
  147. package/dist/composables/index.d.ts +1 -0
  148. package/dist/composables/index.es.js +92 -3
  149. package/dist/composables/index.umd.js +1 -1
  150. package/dist/composables/useBlurhash.d.ts +7 -0
  151. package/dist/composables/useComponentIcon.d.ts +9 -8
  152. package/dist/composables/usePersistence.d.ts +2 -0
  153. package/dist/composables/useVolver.d.ts +1 -1
  154. package/dist/constants.d.ts +34 -32
  155. package/dist/directives/index.d.ts +3 -5
  156. package/dist/directives/index.es.js +99 -43
  157. package/dist/directives/index.umd.js +1 -1
  158. package/dist/directives/v-tooltip.es.js +97 -38
  159. package/dist/directives/v-tooltip.umd.js +1 -1
  160. package/dist/icons.es.js +267 -267
  161. package/dist/icons.umd.js +1 -1
  162. package/dist/index.d.ts +3 -1
  163. package/dist/index.es.js +90 -16
  164. package/dist/index.umd.js +1 -1
  165. package/dist/props/index.d.ts +272 -81
  166. package/dist/resolvers/unplugin.d.ts +6 -1
  167. package/dist/resolvers/unplugin.es.js +87 -10
  168. package/dist/resolvers/unplugin.umd.js +1 -1
  169. package/dist/stories/AccordionGroup/AccordionGroup.stories.d.ts +143 -89
  170. package/dist/stories/AccordionGroup/AccordionGroupSlots.stories.d.ts +1075 -492
  171. package/dist/stories/AlertGroup/AlertGroupWithComposable.stories.d.ts +1 -1
  172. package/dist/stories/Blurhash/BlurhashComposable.stories.d.ts +4 -0
  173. package/dist/stories/Button/Button.settings.d.ts +0 -1
  174. package/dist/stories/Combobox/Combobox.settings.d.ts +8 -0
  175. package/dist/stories/Icon/Icon.settings.d.ts +1 -0
  176. package/dist/stories/InputFile/InputFile.settings.d.ts +56 -0
  177. package/dist/stories/InputFile/InputFile.stories.d.ts +12 -0
  178. package/dist/stories/InputFile/InputFileDropArea.stories.d.ts +9 -0
  179. package/dist/stories/InputFile/InputFileIconPosition.stories.d.ts +8 -0
  180. package/dist/stories/InputFile/InputFileSlots.stories.d.ts +7 -0
  181. package/dist/stories/Tab/Tab.settings.d.ts +2 -15
  182. package/dist/test/expect.d.ts +1 -1
  183. package/dist/types/alert.d.ts +9 -7
  184. package/dist/types/blurhash.d.ts +12 -0
  185. package/dist/types/generic.d.ts +1 -2
  186. package/dist/types/group.d.ts +37 -15
  187. package/dist/types/index.d.ts +7 -0
  188. package/dist/types/input-file.d.ts +16 -0
  189. package/dist/types/nav.d.ts +2 -2
  190. package/dist/utils/ObjectUtilities.d.ts +0 -1
  191. package/dist/workers/blurhash.d.ts +1 -0
  192. package/package.json +78 -78
  193. package/src/Volver.ts +31 -20
  194. package/src/assets/icons/detailed.json +1 -1
  195. package/src/assets/icons/normal.json +1 -1
  196. package/src/assets/icons/simple.json +1 -1
  197. package/src/components/VvAccordion/VvAccordion.vue +121 -58
  198. package/src/components/VvAccordion/index.ts +8 -23
  199. package/src/components/VvAccordionGroup/VvAccordionGroup.vue +164 -47
  200. package/src/components/VvAccordionGroup/index.ts +6 -6
  201. package/src/components/VvAction/VvAction.vue +17 -5
  202. package/src/components/VvAlert/index.ts +14 -9
  203. package/src/components/VvAlertGroup/index.ts +3 -15
  204. package/src/components/VvBreadcrumb/VvBreadcrumb.vue +20 -19
  205. package/src/components/VvBreadcrumb/index.ts +2 -8
  206. package/src/components/VvButton/VvButton.vue +17 -7
  207. package/src/components/VvButton/index.ts +9 -16
  208. package/src/components/VvButtonGroup/VvButtonGroup.vue +1 -2
  209. package/src/components/VvCheckbox/index.ts +2 -2
  210. package/src/components/VvCheckboxGroup/VvCheckboxGroup.vue +1 -2
  211. package/src/components/VvCombobox/VvCombobox.vue +15 -12
  212. package/src/components/VvCombobox/index.ts +4 -0
  213. package/src/components/VvDropdown/VvDropdown.vue +11 -2
  214. package/src/components/VvDropdown/VvDropdownItem.vue +4 -1
  215. package/src/components/VvDropdown/VvDropdownOption.vue +4 -21
  216. package/src/components/VvDropdown/index.ts +35 -1
  217. package/src/components/VvIcon/VvIcon.vue +2 -2
  218. package/src/components/VvIcon/index.ts +35 -48
  219. package/src/components/VvInputFile/VvInputFile.vue +395 -0
  220. package/src/components/VvInputFile/index.ts +135 -0
  221. package/src/components/VvInputText/VvInputClearAction.ts +10 -6
  222. package/src/components/VvInputText/VvInputPasswordAction.ts +13 -9
  223. package/src/components/VvInputText/VvInputText.vue +141 -33
  224. package/src/components/VvInputText/index.ts +31 -16
  225. package/src/components/VvNav/VvNav.vue +4 -2
  226. package/src/components/VvRadio/index.ts +5 -5
  227. package/src/components/VvRadioGroup/VvRadioGroup.vue +1 -2
  228. package/src/components/VvSelect/VvSelect.vue +5 -8
  229. package/src/components/VvTab/VvTab.vue +5 -1
  230. package/src/components/VvTextarea/VvTextarea.vue +6 -9
  231. package/src/components/index.ts +10 -0
  232. package/src/composables/alert/useAlert.ts +12 -9
  233. package/src/composables/dropdown/useProvideDropdown.ts +4 -4
  234. package/src/composables/group/useInjectedGroupState.ts +20 -16
  235. package/src/composables/group/useProvideGroupState.ts +10 -15
  236. package/src/composables/index.ts +1 -0
  237. package/src/composables/useBlurhash.ts +76 -0
  238. package/src/composables/useComponentIcon.ts +15 -14
  239. package/src/composables/usePersistence.ts +76 -0
  240. package/src/composables/useUniqueId.ts +2 -2
  241. package/src/constants.ts +47 -32
  242. package/src/directives/index.ts +3 -6
  243. package/src/directives/v-tooltip.ts +19 -10
  244. package/src/index.ts +3 -1
  245. package/src/props/index.ts +108 -31
  246. package/src/resolvers/unplugin.ts +24 -14
  247. package/src/stories/Accordion/Accordion.test.ts +1 -1
  248. package/src/stories/AccordionGroup/AccordionGroup.settings.ts +2 -2
  249. package/src/stories/AccordionGroup/AccordionGroup.test.ts +17 -11
  250. package/src/stories/AccordionGroup/AccordionGroupSlots.stories.ts +1 -1
  251. package/src/stories/Alert/Alert.test.ts +1 -1
  252. package/src/stories/AlertGroup/AlertGroup.test.ts +1 -1
  253. package/src/stories/AlertGroup/AlertGroupWithComposable.stories.ts +2 -2
  254. package/src/stories/Avatar/Avatar.test.ts +1 -1
  255. package/src/stories/AvatarGroup/AvatarGroup.test.ts +1 -1
  256. package/src/stories/Badge/Badge.test.ts +1 -1
  257. package/src/stories/Blurhash/BlurhashComposable.stories.ts +195 -0
  258. package/src/stories/Button/Button.settings.ts +1 -4
  259. package/src/stories/Button/Button.test.ts +1 -1
  260. package/src/stories/ButtonGroup/ButtonGroup.test.ts +1 -1
  261. package/src/stories/Card/Card.test.ts +1 -1
  262. package/src/stories/Checkbox/Checkbox.test.ts +1 -1
  263. package/src/stories/CheckboxGroup/CheckboxGroup.test.ts +1 -1
  264. package/src/stories/Combobox/Combobox.settings.ts +8 -0
  265. package/src/stories/Combobox/Combobox.test.ts +1 -1
  266. package/src/stories/Dialog/Dialog.test.ts +2 -2
  267. package/src/stories/Dropdown/Dropdown.test.ts +1 -1
  268. package/src/stories/Icon/Icon.settings.ts +3 -3
  269. package/src/stories/InputFile/InputFile.settings.ts +36 -0
  270. package/src/stories/InputFile/InputFile.stories.ts +89 -0
  271. package/src/stories/InputFile/InputFileDropArea.stories.ts +56 -0
  272. package/src/stories/InputFile/InputFileIconPosition.stories.ts +43 -0
  273. package/src/stories/InputFile/InputFileSlots.stories.ts +33 -0
  274. package/src/stories/InputText/InputText.test.ts +1 -1
  275. package/src/stories/Nav/Nav.test.ts +1 -1
  276. package/src/stories/Progress/Progress.test.ts +1 -1
  277. package/src/stories/Radio/Radio.test.ts +1 -1
  278. package/src/stories/RadioGroup/RadioGroup.test.ts +1 -1
  279. package/src/stories/Select/Select.test.ts +1 -1
  280. package/src/stories/Tab/Tab.settings.ts +2 -2
  281. package/src/stories/Tab/Tab.test.ts +1 -1
  282. package/src/stories/Textarea/Textarea.test.ts +1 -1
  283. package/src/stories/Tooltip/Tooltip.test.ts +1 -1
  284. package/src/stories/argTypes.ts +1 -1
  285. package/src/test/expect.ts +2 -4
  286. package/src/types/alert.ts +11 -7
  287. package/src/types/blurhash.ts +21 -0
  288. package/src/types/generic.ts +3 -3
  289. package/src/types/group.ts +22 -14
  290. package/src/types/index.ts +7 -0
  291. package/src/types/input-file.ts +18 -0
  292. package/src/types/nav.ts +2 -3
  293. package/src/utils/ObjectUtilities.ts +0 -11
  294. package/src/workers/blurhash.ts +9 -0
@@ -0,0 +1,395 @@
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
+ import Sortable from 'vuedraggable'
16
+
17
+ // props, emit, slots and attrs
18
+ const props = defineProps(VvInputFileProps)
19
+ const emit = defineEmits<VvInputFileEvents>()
20
+ const slots = useSlots()
21
+
22
+ // props merged with volver defaults (now only for labels)
23
+ const propsDefaults = useDefaults<typeof VvInputFileProps>(
24
+ 'VvInputFile',
25
+ VvInputFileProps,
26
+ props,
27
+ )
28
+ const { modifiers, id, readonly, icon, iconPosition, iconDownload } =
29
+ toRefs(props)
30
+ const hasId = useUniqueId(id)
31
+ const hasHintId = computed(() => `${hasId.value}-hint`)
32
+
33
+ const hasProgress = computed(() => {
34
+ if (!props.progress) {
35
+ return false
36
+ }
37
+ const progress =
38
+ typeof props.progress === 'string'
39
+ ? parseInt(props.progress)
40
+ : props.progress
41
+ return progress > 0 && progress < 100
42
+ })
43
+
44
+ const { hasIconBefore, hasIconAfter } = useComponentIcon(icon, iconPosition)
45
+ const { hasIcon: hasIconDownload } = useComponentIcon(iconDownload)
46
+
47
+ // styles
48
+ const bemCssClasses = useModifiers(
49
+ 'vv-input-file',
50
+ modifiers,
51
+ computed(() => ({
52
+ dragging: isDragging.value,
53
+ loading: props.loading && !hasProgress.value,
54
+ valid: props.valid === true,
55
+ invalid: props.invalid === true,
56
+ 'icon-before': !!hasIconBefore.value,
57
+ 'icon-after': !!hasIconAfter.value,
58
+ 'drop-area': hasDropArea.value,
59
+ })),
60
+ )
61
+
62
+ const {
63
+ HintSlot,
64
+ hasHintLabelOrSlot,
65
+ hasInvalidLabelOrSlot,
66
+ hintSlotScope,
67
+ } = HintSlotFactory(propsDefaults, slots)
68
+
69
+ const localModelValue = useVModel(props, 'modelValue', emit)
70
+ const files = computed({
71
+ get: () => {
72
+ if (
73
+ !localModelValue.value ||
74
+ (!Array.isArray(localModelValue.value) &&
75
+ !(localModelValue.value as File)?.name)
76
+ ) {
77
+ return []
78
+ }
79
+ return Array.isArray(localModelValue.value)
80
+ ? localModelValue.value
81
+ : [localModelValue.value]
82
+ },
83
+ set: (value) => {
84
+ if (isMultiple.value) {
85
+ localModelValue.value = value
86
+ return
87
+ }
88
+ localModelValue.value = value?.[0]
89
+ },
90
+ })
91
+
92
+ const hasMax = computed(() => {
93
+ return typeof props.max === 'string' ? parseInt(props.max) : props.max
94
+ })
95
+
96
+ const hasDropArea = computed(() => {
97
+ return props.dropArea && !readonly.value
98
+ })
99
+
100
+ const isMultiple = computed(() => {
101
+ if (!props.multiple) {
102
+ return false
103
+ }
104
+ if (!hasMax.value) {
105
+ return true
106
+ }
107
+ return hasMax.value - files.value.length > 1
108
+ })
109
+
110
+ const isDragging = ref(false)
111
+
112
+ const inputEl = ref<HTMLInputElement>()
113
+ const onDragenter = () => {
114
+ isDragging.value = true
115
+ }
116
+
117
+ const onDragleave = () => {
118
+ isDragging.value = false
119
+ }
120
+
121
+ const onDrop = (event: DragEvent) => {
122
+ if (!event.dataTransfer?.files) {
123
+ return
124
+ }
125
+ isDragging.value = false
126
+ addFiles(event.dataTransfer?.files)
127
+ }
128
+
129
+ const onChange = () => {
130
+ if (!inputEl.value?.files) {
131
+ return
132
+ }
133
+ addFiles(inputEl.value.files)
134
+ inputEl.value.value = ''
135
+ }
136
+
137
+ const addFiles = (uploadedFiles: FileList) => {
138
+ if (!props.multiple) {
139
+ if (Array.isArray(localModelValue.value)) {
140
+ localModelValue.value = [...uploadedFiles]
141
+ return
142
+ }
143
+ localModelValue.value = uploadedFiles[0]
144
+ return
145
+ }
146
+ let toReturn: (File | UploadedFile)[] = []
147
+ if (!Array.isArray(localModelValue.value) && localModelValue.value) {
148
+ toReturn = [localModelValue.value]
149
+ } else {
150
+ toReturn =
151
+ localModelValue.value && Array.isArray(localModelValue.value)
152
+ ? [...localModelValue.value]
153
+ : toReturn
154
+ }
155
+ for (const file of uploadedFiles) {
156
+ if (hasMax.value && toReturn.length >= hasMax.value) {
157
+ break
158
+ }
159
+ toReturn.push(file)
160
+ }
161
+ localModelValue.value = toReturn
162
+ selectedFileIndex.value = toReturn.length - 1
163
+ }
164
+
165
+ const onClickDropArea = () => {
166
+ if (!inputEl.value) {
167
+ return
168
+ }
169
+ if (!readonly.value) {
170
+ inputEl.value.click()
171
+ }
172
+ }
173
+
174
+ const onClickRemoveFile = (index: number) => {
175
+ if (!Array.isArray(localModelValue.value)) {
176
+ localModelValue.value = undefined
177
+ return
178
+ }
179
+ if (selectedFileIndex.value === index) {
180
+ selectedFileIndex.value = 0
181
+ }
182
+ const toReturn = [...localModelValue.value]
183
+ toReturn.splice(index, 1)
184
+ localModelValue.value = toReturn
185
+ }
186
+
187
+ const selectedFileIndex = ref(0)
188
+ const PREVIEW_MIME_TYPES = ['image/jpeg', 'image/png']
189
+ const previewSrc = computed(() => {
190
+ if (files.value.length === 0) {
191
+ return
192
+ }
193
+ if (!files.value[selectedFileIndex.value]) {
194
+ return undefined
195
+ }
196
+ if (files.value[selectedFileIndex.value] instanceof File) {
197
+ const currentFile = files.value[selectedFileIndex.value] as File
198
+ if (!PREVIEW_MIME_TYPES.includes(currentFile.type)) {
199
+ return undefined
200
+ }
201
+ return URL.createObjectURL(currentFile)
202
+ }
203
+ const currentFile = files.value[selectedFileIndex.value] as UploadedFile
204
+ if (currentFile.thumbnailUrl) {
205
+ return currentFile.thumbnailUrl
206
+ }
207
+ if (!PREVIEW_MIME_TYPES.includes(currentFile.type)) {
208
+ return undefined
209
+ }
210
+ return currentFile.url
211
+ })
212
+
213
+ watch(previewSrc, (_newValue, oldValue) => {
214
+ if (oldValue) {
215
+ URL.revokeObjectURL(oldValue)
216
+ }
217
+ })
218
+
219
+ onBeforeUnmount(() => {
220
+ if (previewSrc.value) {
221
+ URL.revokeObjectURL(previewSrc.value)
222
+ }
223
+ })
224
+
225
+ const sizeInKiB = (size?: number) => {
226
+ if (!size) {
227
+ return
228
+ }
229
+ return Math.floor(size / 1024)
230
+ }
231
+
232
+ const onClickDownloadFile = (file: File | UploadedFile) => {
233
+ const link = document.createElement('a')
234
+ if (file instanceof File) {
235
+ link.href = URL.createObjectURL(file)
236
+ } else if (file.url) {
237
+ link.href = file.url
238
+ }
239
+ link.setAttribute('download', file.name)
240
+ document.body.appendChild(link)
241
+ link.click()
242
+ document.body.removeChild(link)
243
+ URL.revokeObjectURL(link.href)
244
+ }
245
+
246
+ const onClickSelectFile = (index: number) => {
247
+ selectedFileIndex.value = index
248
+ }
249
+
250
+ const dropdAreaActionLabel = computed(() => {
251
+ if (files.value.length === 0 || isMultiple.value) {
252
+ return props.labelAdd
253
+ }
254
+ return props.labelReplace
255
+ })
256
+
257
+ const dropAreaActionIcon = computed(() => {
258
+ if (files.value.length === 0 || isMultiple.value) {
259
+ return props.iconAdd
260
+ }
261
+ return props.iconReplace
262
+ })
263
+
264
+ const onSortEnd = ({ newIndex }: { newIndex: number | null }) => {
265
+ if (newIndex !== null) {
266
+ selectedFileIndex.value = newIndex
267
+ }
268
+ }
269
+ </script>
270
+
271
+ <template>
272
+ <div :class="bemCssClasses">
273
+ <label v-if="label" :for="hasId">
274
+ {{ label }}
275
+ </label>
276
+ <div
277
+ v-if="hasDropArea"
278
+ class="vv-input-file__drop-area"
279
+ @dragenter.prevent.stop="onDragenter"
280
+ @dragleave.prevent.stop="onDragleave"
281
+ @drop.prevent.stop="onDrop"
282
+ @dragover.prevent.stop
283
+ @click.stop="onClickDropArea"
284
+ >
285
+ <slot name="drop-area">
286
+ <picture class="vv-input-file__preview">
287
+ <img
288
+ v-if="previewSrc"
289
+ :src="previewSrc"
290
+ :alt="files[selectedFileIndex].name"
291
+ />
292
+ </picture>
293
+ <VvButton
294
+ v-if="!readonly"
295
+ modifiers="action"
296
+ :label="!previewSrc ? dropdAreaActionLabel : undefined"
297
+ :title="previewSrc ? dropdAreaActionLabel : undefined"
298
+ :class="{
299
+ 'vv-input-file__drop-area-action': previewSrc,
300
+ }"
301
+ :icon="dropAreaActionIcon"
302
+ @click.stop="onClickDropArea"
303
+ />
304
+ </slot>
305
+ </div>
306
+ <div class="vv-input-file__wrapper">
307
+ <VvIcon v-if="hasIconBefore" v-bind="hasIconBefore" />
308
+ <input
309
+ :id="hasId"
310
+ ref="inputEl"
311
+ type="file"
312
+ :readonly="readonly"
313
+ :placeholder="placeholder"
314
+ :aria-describedby="hasHintLabelOrSlot ? hasHintId : undefined"
315
+ :aria-invalid="invalid"
316
+ :aria-errormessage="
317
+ hasInvalidLabelOrSlot ? hasHintId : undefined
318
+ "
319
+ :multiple="isMultiple"
320
+ :accept="accept"
321
+ :capture="capture"
322
+ :name="name"
323
+ @change="onChange"
324
+ />
325
+ <progress
326
+ v-if="hasProgress"
327
+ class="vv-input-file__progress"
328
+ :value="progress"
329
+ max="100"
330
+ >
331
+ {{ progress }}%
332
+ </progress>
333
+ <VvIcon v-if="hasIconAfter" v-bind="hasIconAfter" />
334
+ </div>
335
+ <Sortable
336
+ v-model="files"
337
+ tag="ul"
338
+ class="vv-input-file__list"
339
+ item-key="name"
340
+ :move="() => sortable"
341
+ @end="onSortEnd"
342
+ >
343
+ <template #item="{ element: file, index }">
344
+ <li
345
+ class="vv-input-file__item"
346
+ :class="{
347
+ active:
348
+ index === selectedFileIndex &&
349
+ hasDropArea &&
350
+ files.length > 1,
351
+ 'cursor-move': sortable,
352
+ }"
353
+ @click.stop="onClickSelectFile(index)"
354
+ >
355
+ <button
356
+ v-if="hasIconDownload"
357
+ type="button"
358
+ class="vv-input-file__item-action"
359
+ :title="labelDownload"
360
+ @click.stop="onClickDownloadFile(file)"
361
+ >
362
+ <VvIcon v-bind="hasIconDownload" />
363
+ </button>
364
+ <div class="vv-input-file__item-name">
365
+ {{ file.name }}
366
+ </div>
367
+ <small class="vv-input-file__item-info">
368
+ {{ sizeInKiB(file.size) }} KB
369
+ </small>
370
+ <button
371
+ v-if="!readonly"
372
+ type="button"
373
+ class="vv-input-file__item-remove"
374
+ :title="labelRemove"
375
+ @click.stop="onClickRemoveFile(index)"
376
+ />
377
+ </li>
378
+ </template>
379
+ </Sortable>
380
+ <HintSlot :id="hasHintId" class="vv-input-file__hint">
381
+ <template v-if="$slots.hint" #hint>
382
+ <slot name="hint" v-bind="hintSlotScope" />
383
+ </template>
384
+ <template v-if="$slots.loading" #loading>
385
+ <slot name="loading" v-bind="hintSlotScope" />
386
+ </template>
387
+ <template v-if="$slots.valid" #valid>
388
+ <slot name="valid" v-bind="hintSlotScope" />
389
+ </template>
390
+ <template v-if="$slots.invalid" #invalid>
391
+ <slot name="invalid" v-bind="hintSlotScope" />
392
+ </template>
393
+ </HintSlot>
394
+ </div>
395
+ </template>
@@ -0,0 +1,135 @@
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': [
17
+ File | UploadedFile | (File | UploadedFile)[] | undefined,
18
+ ]
19
+ }
20
+
21
+ export const VvInputFileProps = {
22
+ ...IdNameProps,
23
+ ...ModifiersProps,
24
+ ...ValidProps,
25
+ ...InvalidProps,
26
+ ...HintProps,
27
+ ...LabelProps,
28
+ ...LoadingProps,
29
+ ...ReadonlyProps,
30
+ ...IconProps,
31
+ /**
32
+ * Input value
33
+ */
34
+ modelValue: {
35
+ type: Object as PropType<File | UploadedFile | (File | UploadedFile)[]>,
36
+ },
37
+ /**
38
+ * Whether to show progress bar
39
+ */
40
+ progress: { type: [Number, String], default: undefined },
41
+ /**
42
+ * Input
43
+ * Text that appears in the form control when it has no value set
44
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#placeholder
45
+ */
46
+ placeholder: { type: String, default: undefined },
47
+ /**
48
+ * File types to accept
49
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept
50
+ */
51
+ accept: { type: String, default: '*' },
52
+ /**
53
+ * Whether to allow multiple values
54
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#multiple
55
+ */
56
+ multiple: { type: Boolean, default: false },
57
+ /**
58
+ * Front or rear camera
59
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/capture
60
+ */
61
+ capture: {
62
+ type: String as PropType<'user' | 'environment'>,
63
+ default: undefined,
64
+ validation: (value?: string) => {
65
+ if (value === undefined) {
66
+ return true
67
+ }
68
+ return ['user', 'environment'].includes(value)
69
+ },
70
+ },
71
+ /**
72
+ * Max number of files
73
+ */
74
+ max: { type: [Number, String], default: undefined },
75
+ /**
76
+ * Show drop area
77
+ */
78
+ dropArea: { type: Boolean, default: false },
79
+ /**
80
+ * Enable sorting
81
+ */
82
+ sortable: { type: Boolean, default: false },
83
+ /**
84
+ * Label for add button
85
+ */
86
+ labelAdd: {
87
+ type: String,
88
+ default: 'Add file',
89
+ },
90
+ /**
91
+ * VvIcon name for add button
92
+ * @see VVIcon
93
+ */
94
+ iconAdd: {
95
+ type: [String, Object] as PropType<string | VvIconProps>,
96
+ default: ACTION_ICONS.add,
97
+ },
98
+ /**
99
+ * Label for replace button
100
+ */
101
+ labelReplace: {
102
+ type: String,
103
+ default: 'Replace file',
104
+ },
105
+ /**
106
+ * VvIcon name for replace button
107
+ * @see VVIcon
108
+ */
109
+ iconReplace: {
110
+ type: [String, Object] as PropType<string | VvIconProps>,
111
+ default: ACTION_ICONS.edit,
112
+ },
113
+ /**
114
+ * Label for download button
115
+ */
116
+ labelDownload: {
117
+ type: String,
118
+ default: 'Downlaod file',
119
+ },
120
+ /**
121
+ * VvIcon name for download button
122
+ * @see VVIcon
123
+ */
124
+ iconDownload: {
125
+ type: [String, Object] as PropType<string | VvIconProps>,
126
+ default: ACTION_ICONS.download,
127
+ },
128
+ /**
129
+ * Label for remove button
130
+ */
131
+ labelRemove: {
132
+ type: String,
133
+ default: 'Remove file',
134
+ },
135
+ }
@@ -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
  {