comand-component-library 3.1.43 → 3.1.46

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 (236) hide show
  1. package/dist/comand-component-library.css +1 -1
  2. package/dist/comand-component-library.umd.min.js +1 -1
  3. package/package.json +47 -40
  4. package/src/App.vue +379 -136
  5. package/src/ComponentDocumentation.vue +156 -0
  6. package/src/ComponentLibraryHelp.vue +20 -0
  7. package/src/assets/data/accordion.json +21 -24
  8. package/src/assets/data/address-data.json +34 -0
  9. package/src/assets/data/bank-account-data.json +22 -0
  10. package/src/assets/data/box-product.json +14 -4
  11. package/src/assets/data/box-user.json +48 -22
  12. package/src/assets/data/breadcrumbs.json +11 -3
  13. package/src/assets/data/cookie-disclaimer.json +4 -4
  14. package/src/assets/data/fake-select-colors.json +4 -0
  15. package/src/assets/data/fake-select-countries.json +12 -12
  16. package/src/assets/data/fake-select-filter-options.json +14 -0
  17. package/src/assets/data/fake-select-options-with-icons.json +6 -12
  18. package/src/assets/data/fake-select-options.json +3 -3
  19. package/src/assets/data/list-of-links-section-anchors.json +23 -0
  20. package/src/assets/data/list-of-links-top-header-navigation.json +20 -0
  21. package/src/assets/data/list-of-links.json +42 -0
  22. package/src/assets/data/main-navigation.json +48 -0
  23. package/src/assets/data/multistep-form-progress-bar.json +33 -0
  24. package/src/assets/data/select-options.json +4 -0
  25. package/src/assets/data/{share-buttons.json → share-buttons-page-by-json.json} +8 -8
  26. package/src/assets/data/share-buttons-page-by-property.json +30 -0
  27. package/src/assets/data/switch-language.json +20 -0
  28. package/src/assets/data/table-large.json +1 -1
  29. package/src/assets/data/table-small.json +1 -1
  30. package/src/assets/styles/global-styles.scss +43 -14
  31. package/src/assets/styles/transitions.scss +21 -1
  32. package/src/components/CmdAccordion.vue +43 -42
  33. package/src/components/CmdAddressData.vue +124 -56
  34. package/src/components/CmdBackToTopButton.vue +3 -3
  35. package/src/components/CmdBankAccountData.vue +104 -0
  36. package/src/components/CmdBox.vue +253 -56
  37. package/src/components/CmdBoxSiteSearch.vue +138 -39
  38. package/src/components/CmdBoxWrapper.vue +206 -0
  39. package/src/components/CmdBreadcrumbs.vue +29 -13
  40. package/src/components/CmdCompanyLogo.vue +6 -4
  41. package/src/components/CmdCookieDisclaimer.vue +99 -75
  42. package/src/components/CmdCopyrightInformation.vue +1 -1
  43. package/src/components/CmdCustomHeadline.vue +93 -0
  44. package/src/components/CmdFakeSelect.vue +330 -66
  45. package/src/components/CmdFancyBox.vue +47 -33
  46. package/src/components/CmdForm.vue +107 -0
  47. package/src/components/CmdFormElement.vue +528 -83
  48. package/src/components/CmdFormFilters.vue +25 -11
  49. package/src/components/CmdGoogleMaps.vue +9 -3
  50. package/src/components/CmdImageGallery.vue +28 -5
  51. package/src/components/CmdImageZoom.vue +9 -1
  52. package/src/components/CmdListOfLinks.vue +169 -0
  53. package/src/components/CmdLoginForm.vue +146 -66
  54. package/src/components/CmdMainNavigation.vue +140 -42
  55. package/src/components/CmdMultipleSwitch.vue +33 -2
  56. package/src/components/CmdMultistepFormProgressBar.vue +60 -10
  57. package/src/components/CmdOpeningHours.vue +36 -10
  58. package/src/components/CmdPager.vue +7 -5
  59. package/src/components/CmdProgressBar.vue +21 -4
  60. package/src/components/CmdShareButtons.vue +64 -9
  61. package/src/components/CmdSiteHeader.vue +25 -12
  62. package/src/components/CmdSlideButton.vue +5 -2
  63. package/src/components/CmdSlideshow.vue +23 -7
  64. package/src/components/CmdSwitchButton.vue +15 -6
  65. package/src/components/CmdSwitchLanguage.vue +18 -10
  66. package/src/components/CmdSystemMessage.vue +30 -17
  67. package/src/components/CmdTable.vue +15 -7
  68. package/src/components/CmdTabs.vue +43 -3
  69. package/src/components/CmdThumbnailScroller.vue +22 -6
  70. package/src/components/CmdTooltip.vue +184 -11
  71. package/src/components/CmdUploadForm.vue +830 -176
  72. package/src/components/CmdWidthLimitationWrapper.vue +9 -6
  73. package/src/composables/event.js +8 -0
  74. package/src/composables/scrollspy.js +52 -0
  75. package/src/directives/focus.js +19 -0
  76. package/src/directives/telephone.js +1 -1
  77. package/src/documentation/commonProps.js +6 -0
  78. package/src/documentation/components/ComponentCode.vue +50 -0
  79. package/src/documentation/components/ComponentProperties.vue +237 -0
  80. package/src/documentation/components/ExampleSectionWrapper.vue +46 -0
  81. package/src/documentation/components/ViewCodeData.vue +113 -0
  82. package/src/documentation/data/CmdAccordionHelp.js +22 -0
  83. package/src/documentation/data/CmdAddressDataHelp.js +17 -0
  84. package/src/documentation/data/CmdBackToTopButtonHelp.js +3 -0
  85. package/src/documentation/data/CmdBankAccountDataHelp.js +8 -0
  86. package/src/documentation/data/CmdBoxHelp.js +45 -0
  87. package/src/documentation/data/CmdBoxSiteSearchHelp.js +11 -0
  88. package/src/documentation/data/CmdBreadcrumbsHelp.js +6 -0
  89. package/src/documentation/data/CmdCompanyLogoHelp.js +8 -0
  90. package/src/documentation/data/CmdCookieDisclaimerHelp.js +9 -0
  91. package/src/documentation/data/CmdCopyrightInformation.js +2 -0
  92. package/src/documentation/data/CmdCustomHeadlineHelp.js +8 -0
  93. package/src/documentation/data/CmdFakeSelectHelp.js +60 -0
  94. package/src/documentation/data/CmdFancyBoxHelp.js +7 -0
  95. package/src/documentation/data/CmdFooterNavigationHelp.js +5 -0
  96. package/src/documentation/data/CmdFormElementHelp.js +189 -0
  97. package/src/documentation/data/CmdFormFiltersHelp.js +6 -0
  98. package/src/documentation/data/CmdFormHelp.js +10 -0
  99. package/src/documentation/data/CmdGoogleMapsHelp.js +5 -0
  100. package/src/documentation/data/CmdImageGalleryHelp.js +5 -0
  101. package/src/documentation/data/CmdImageZoomHelp.js +6 -0
  102. package/src/documentation/data/CmdListOfLinksHelp.js +24 -0
  103. package/src/documentation/data/CmdLoginFormHelp.js +6 -0
  104. package/src/documentation/data/CmdMainNavigationHelp.js +7 -0
  105. package/src/documentation/data/CmdMultistepFormProgressBarHelp.js +6 -0
  106. package/src/documentation/data/CmdOpeningHoursHelp.js +10 -0
  107. package/src/documentation/data/CmdPagerHelp.js +7 -0
  108. package/src/documentation/data/CmdProgressBarHelp.js +13 -0
  109. package/src/documentation/data/CmdShareButtonsHelp.js +13 -0
  110. package/src/documentation/data/CmdSiteHeaderHelp.js +21 -0
  111. package/src/documentation/data/CmdSlideButtonHelp.js +10 -0
  112. package/src/documentation/data/CmdSlideshowHelp.js +7 -0
  113. package/src/documentation/data/CmdSwitchLanguageHelp.js +6 -0
  114. package/src/documentation/data/CmdSystemMessageHelp.js +32 -0
  115. package/src/documentation/data/CmdTableHelp.js +14 -0
  116. package/src/documentation/data/CmdTabsHelp.js +10 -0
  117. package/src/documentation/data/CmdThumbnailScrollerHelp.js +5 -0
  118. package/src/documentation/data/CmdTooltipHelp.js +13 -0
  119. package/src/documentation/data/CmdUploadFormHelp.js +17 -0
  120. package/src/documentation/data/CmdWidthLimitationWrapperHelp.js +7 -0
  121. package/src/documentation/data/componentsDescription.json +158 -0
  122. package/src/documentation/generated/CmdAccordionPropertyDescriptions.json +57 -0
  123. package/src/documentation/generated/CmdAddressDataPropertyDescriptions.json +32 -0
  124. package/src/documentation/generated/CmdBackToTopButtonPropertyDescriptions.json +12 -0
  125. package/src/documentation/generated/CmdBankAccountDataPropertyDescriptions.json +34 -0
  126. package/src/documentation/generated/CmdBoxPropertyDescriptions.json +91 -0
  127. package/src/documentation/generated/CmdBoxSiteSearchPropertyDescriptions.json +41 -0
  128. package/src/documentation/generated/CmdBoxWrapperPropertyDescriptions.json +47 -0
  129. package/src/documentation/generated/CmdBreadcrumbsPropertyDescriptions.json +17 -0
  130. package/src/documentation/generated/CmdCompanyLogoPropertyDescriptions.json +27 -0
  131. package/src/documentation/generated/CmdCookieDisclaimerPropertyDescriptions.json +22 -0
  132. package/src/documentation/generated/CmdCustomHeadlinePropertyDescriptions.json +22 -0
  133. package/src/documentation/generated/CmdFakeSelectPropertyDescriptions.json +79 -0
  134. package/src/documentation/generated/CmdFancyBoxPropertyDescriptions.json +62 -0
  135. package/src/documentation/generated/CmdFooterNavigationPropertyDescriptions.json +17 -0
  136. package/src/documentation/generated/CmdFormElementPropertyDescriptions.json +178 -0
  137. package/src/documentation/generated/CmdFormFiltersPropertyDescriptions.json +32 -0
  138. package/src/documentation/generated/CmdFormPropertyDescriptions.json +40 -0
  139. package/src/documentation/generated/CmdGoogleMapsPropertyDescriptions.json +7 -0
  140. package/src/documentation/generated/CmdImageGalleryPropertyDescriptions.json +22 -0
  141. package/src/documentation/generated/CmdImageZoomPropertyDescriptions.json +12 -0
  142. package/src/documentation/generated/CmdListOfLinksPropertyDescriptions.json +60 -0
  143. package/src/documentation/generated/CmdLoginFormPropertyDescriptions.json +90 -0
  144. package/src/documentation/generated/CmdMainNavigationPropertyDescriptions.json +62 -0
  145. package/src/documentation/generated/CmdMultipleSwitchPropertyDescriptions.json +52 -0
  146. package/src/documentation/generated/CmdMultistepFormProgressBarPropertyDescriptions.json +17 -0
  147. package/src/documentation/generated/CmdOpeningHoursPropertyDescriptions.json +42 -0
  148. package/src/documentation/generated/CmdPagerPropertyDescriptions.json +37 -0
  149. package/src/documentation/generated/CmdProgressBarPropertyDescriptions.json +32 -0
  150. package/src/documentation/generated/CmdShareButtonsPropertyDescriptions.json +34 -0
  151. package/src/documentation/generated/CmdSiteHeaderPropertyDescriptions.json +27 -0
  152. package/src/documentation/generated/CmdSlideButtonPropertyDescriptions.json +25 -0
  153. package/src/documentation/generated/CmdSlideshowPropertyDescriptions.json +42 -0
  154. package/src/documentation/generated/CmdSwitchButtonPropertyDescriptions.json +79 -0
  155. package/src/documentation/generated/CmdSwitchLanguagePropertyDescriptions.json +7 -0
  156. package/src/documentation/generated/CmdSystemMessagePropertyDescriptions.json +40 -0
  157. package/src/documentation/generated/CmdTablePropertyDescriptions.json +62 -0
  158. package/src/documentation/generated/CmdTabsPropertyDescriptions.json +27 -0
  159. package/src/documentation/generated/CmdThumbnailScrollerPropertyDescriptions.json +32 -0
  160. package/src/documentation/generated/CmdTooltipPropertyDescriptions.json +17 -0
  161. package/src/documentation/generated/CmdUploadFormPropertyDescriptions.json +90 -0
  162. package/src/documentation/generated/CmdWidthLimitationWrapperPropertyDescriptions.json +41 -0
  163. package/src/documentation/generated/frameworkIcons.json +730 -0
  164. package/src/documentation/generated/logosIcons.json +110 -0
  165. package/src/documentation/tabs.js +46 -0
  166. package/src/documentation/views/ContainerPage.vue +237 -0
  167. package/src/documentation/views/HelpHome.vue +13 -0
  168. package/src/documentation/views/IconFont.vue +80 -0
  169. package/src/documentation/views/components/CmdAccordionHelp.vue +78 -0
  170. package/src/documentation/views/components/CmdAddressDataHelp.vue +65 -0
  171. package/src/documentation/views/components/CmdBackToTopButtonHelp.vue +62 -0
  172. package/src/documentation/views/components/CmdBankAccountDataHelp.vue +88 -0
  173. package/src/documentation/views/components/CmdBoxHelp.vue +137 -0
  174. package/src/documentation/views/components/CmdBoxSiteSearchHelp.vue +60 -0
  175. package/src/documentation/views/components/CmdBoxWrapperHelp.vue +111 -0
  176. package/src/documentation/views/components/CmdBreadcrumbsHelp.vue +51 -0
  177. package/src/documentation/views/components/CmdCompanyLogoHelp.vue +48 -0
  178. package/src/documentation/views/components/CmdCookieDisclaimerHelp.vue +105 -0
  179. package/src/documentation/views/components/CmdCustomHeadlineHelp.vue +53 -0
  180. package/src/documentation/views/components/CmdFakeSelectHelp.vue +175 -0
  181. package/src/documentation/views/components/CmdFancyBoxHelp.vue +79 -0
  182. package/src/documentation/views/components/CmdFormElementHelp.vue +412 -0
  183. package/src/documentation/views/components/CmdFormFiltersHelp.vue +69 -0
  184. package/src/documentation/views/components/CmdFormHelp.vue +41 -0
  185. package/src/documentation/views/components/CmdGoogleMapsHelp.vue +55 -0
  186. package/src/documentation/views/components/CmdImageGalleryHelp.vue +46 -0
  187. package/src/documentation/views/components/CmdImageZoomHelp.vue +34 -0
  188. package/src/documentation/views/components/CmdListOfLinksHelp.vue +64 -0
  189. package/src/documentation/views/components/CmdLoginFormHelp.vue +117 -0
  190. package/src/documentation/views/components/CmdMainNavigationHelp.vue +94 -0
  191. package/src/documentation/views/components/CmdMultistepFormProgressBarHelp.vue +49 -0
  192. package/src/documentation/views/components/CmdOpeningHoursHelp.vue +49 -0
  193. package/src/documentation/views/components/CmdPagerHelp.vue +57 -0
  194. package/src/documentation/views/components/CmdProgressBarHelp.vue +47 -0
  195. package/src/documentation/views/components/CmdShareButtonsHelp.vue +65 -0
  196. package/src/documentation/views/components/CmdSiteHeaderHelp.vue +72 -0
  197. package/src/documentation/views/components/CmdSlideButtonHelp.vue +90 -0
  198. package/src/documentation/views/components/CmdSlideshowHelp.vue +60 -0
  199. package/src/documentation/views/components/CmdSwitchLanguageHelp.vue +64 -0
  200. package/src/documentation/views/components/CmdSystemMessageHelp.vue +86 -0
  201. package/src/documentation/views/components/CmdTableHelp.vue +84 -0
  202. package/src/documentation/views/components/CmdTabsHelp.vue +52 -0
  203. package/src/documentation/views/components/CmdThumbnailScrollerHelp.vue +50 -0
  204. package/src/documentation/views/components/CmdTooltipHelp.vue +59 -0
  205. package/src/documentation/views/components/CmdUploadFormHelp.vue +59 -0
  206. package/src/documentation/views/components/CmdWidthLimitationWrapperHelp.vue +46 -0
  207. package/src/index.js +6 -3
  208. package/src/main.js +25 -15
  209. package/src/mixins/CmdAddressData/DefaultMessageProperties.js +17 -0
  210. package/src/mixins/CmdBox/DefaultMessageProperties.js +10 -0
  211. package/src/mixins/CmdFakeSelect/DefaultMessageProperties.js +9 -0
  212. package/src/mixins/CmdFormElement/DefaultMessageProperties.js +9 -0
  213. package/src/mixins/CmdImageGallery/DefaultMessageProperties.js +9 -0
  214. package/src/mixins/CmdSiteSearch/DefaultMessageProperties.js +14 -0
  215. package/src/mixins/CmdUploadForm/DefaultMessageProperties.js +53 -0
  216. package/src/mixins/FieldValidation.js +220 -0
  217. package/src/mixins/GlobalDefaultMessageProperties.js +15 -0
  218. package/src/mixins/I18n.js +56 -0
  219. package/src/mixins/Tooltip.js +26 -0
  220. package/src/router/index.js +67 -0
  221. package/src/utilities.js +3 -6
  222. package/src/utils/GetFileExtension.js +15 -0
  223. package/src/utils/common.js +6 -0
  224. package/src/utils/dom.js +8 -0
  225. package/src/utils/globalSequence.js +13 -0
  226. package/src/utils/string.js +8 -0
  227. package/src/assets/data/address.json +0 -13
  228. package/src/assets/data/footer-navigation.json +0 -38
  229. package/src/assets/data/languages.json +0 -31
  230. package/src/assets/data/multisteps.json +0 -27
  231. package/src/assets/data/navigation.json +0 -47
  232. package/src/assets/data/pager.json +0 -11
  233. package/src/assets/data/top-header-navigation.json +0 -27
  234. package/src/components/CmdFooterNavigation.vue +0 -71
  235. package/src/components/CmdMainHeadline.vue +0 -75
  236. package/src/components/CmdTopHeaderNavigation.vue +0 -88
@@ -1,166 +1,533 @@
1
1
  <template>
2
- <fieldset ref="upload" class=" cmd-upload-form grid-container-create-columns">
3
- <legend>Upload form</legend>
4
- <h2 v-if="headline">{{ headline }}</h2>
5
- <CmdFormElement
6
- element="input"
7
- type="file"
8
- multiple="multiple"
9
- labelText="Choose file(s) with file-explorer:"
10
- @change="filesSelected"
11
- v-show="enableFileSelect"
2
+ <!-- begin advanced mode -->
3
+ <fieldset v-if="advancedMode" :class="['cmd-upload-form flex-container', { 'upload-initiated': uploadInitiated }]">
4
+ <!-- begin CmdCustomHeadline -->
5
+ <CmdCustomHeadline v-if="cmdCustomHeadline"
6
+ v-bind="cmdCustomHeadline"
12
7
  />
13
- <template v-if="enableDragAndDrop">
14
- <span v-show="enableFileSelect">or</span>
15
- <a href="#" :class="['box', {'allow-drop': allowDrop}]" @dragenter="dragEnter" @dragover="dragOver"
16
- @dragleave="dragLeave" @drop="drop($event)" @click.prevent="openFileDialog">
17
- <span>Drag & drop file(s) here</span>
18
- </a>
19
- </template>
20
- <hr/>
21
- <h2>List of files to upload</h2>
22
- <ul v-if="listOfFiles.length" class="list-of-files">
23
- <li v-for="(uploadFile, index) in listOfFiles" :key="index">
24
- <a href="#" class="icon-delete" title="Remove from list" @click.prevent="removeFile(index)"></a>
25
- <span :class="[uploadFile.allowedType ? 'allowed' : 'not-allowed']">
26
- <strong>{{ uploadFile.file.name }}</strong> ({{ uploadFile.file.type }}, {{
27
- formatSize(uploadFile.file.size)
28
- }}<template v-if="uploadFile.width && uploadFile.height">, {{
29
- uploadFile.width
30
- }} px x {{ uploadFile.height }} px</template>)
31
- <span v-if="uploadFile.allowedType" class="icon-check allowed" title="File ready to upload!"></span>
32
- <span v-else class="icon-cancel not-allowed"
33
- title="File type not allowed (file will not be uploaded)!"></span>
34
- </span>
35
- </li>
36
- </ul>
37
- <CmdSystemMessage v-if="!listOfFiles.length" status="warning" :fullWidth="true"
38
- message="No files selected for upload!">
39
- </CmdSystemMessage>
40
- <CmdSystemMessage v-else :status="messageStatusUploadSize()" :fullWidth="true">
41
- <p>Current upload size is {{ formatSize(uploadSize) }} (of max. {{ formatSize(maxUploadSize) }}).</p>
42
- </CmdSystemMessage>
43
- <CmdSystemMessage v-if="uploadSize > maxUploadSize" status="error" :fullWidth="true"
44
- message="Total file size to large!">
8
+ <!-- end CmdCustomHeadline -->
9
+
10
+ <!-- begin CmdSystemMessage -->
11
+ <CmdSystemMessage
12
+ v-if="systemMessageStatus && allSystemMessages.length"
13
+ :iconClose="{ show: false }"
14
+ :validationStatus="systemMessageStatus"
15
+ :systemMessage="allSystemMessages.length === 1 ? allSystemMessages[0] : getMessage('cmduploadform.system_message.the_following_errors_occurred')"
16
+ >
17
+ <ul v-if="allSystemMessages.length > 1">
18
+ <li v-for="(systemMessage, index) in allSystemMessages" :key="index">
19
+ {{ systemMessage }}
20
+ </li>
21
+ </ul>
45
22
  </CmdSystemMessage>
23
+ <!-- end CmdSystemMessage -->
24
+
25
+ <div :class="['box drop-area', { 'allow-drop': allowDrop }]" v-on="dragAndDropHandler">
26
+ <template v-if="!listOfFiles.length">
27
+ <h4 v-if="allowMultipleFileUploads">
28
+ {{ getMessage("cmduploadform.no_files_to_upload") }}
29
+ </h4>
30
+ <h4 v-else>
31
+ {{ getMessage("cmduploadform.no_file_to_upload") }}
32
+ </h4>
33
+ </template>
34
+
35
+ <!-- begin total-upload information -->
36
+ <template v-else>
37
+ <template v-if="showTotalUpload && listOfFiles.length !== 1">
38
+ <h4>{{ getMessage("cmduploadform.headline.summary_of_all_files") }}</h4>
39
+ <ul v-if="showTotalUpload && listOfFiles.length !== 1" class="list-of-files">
40
+ <li class="flex-container no-flex">
41
+ <a
42
+ href="#"
43
+ :title="getMessage('cmduploadform.labeltext.remove_all_files_from_list')"
44
+ @click.prevent="cancelUpload"
45
+ >
46
+ <span :class="deleteIconClass"></span>
47
+ </a>
48
+ <span>
49
+ <strong>{{ listOfFiles.length }}
50
+ <template v-if="!allowMultipleFileUploads">
51
+ {{ getMessage("cmduploadform.labeltext.file_uploading") }}
52
+ </template>
53
+ <template v-else>
54
+ {{ getMessage("cmduploadform.labeltext.files_uploading") }}
55
+ </template>
56
+ <span
57
+ :class="[
58
+ 'text-align-right',
59
+ { error: maxTotalUploadSize > 0 && totalSize > maxTotalUploadSize }
60
+ ]">({{ formatSize(totalSize) }})</span>
61
+ </strong>
62
+ </span>
63
+ <span class="progressbar" v-if="uploadInitiated">
64
+ <span>{{ getPercentage(totalUploadProgress) }}</span>
65
+ <progress
66
+ max="100"
67
+ :value="totalUploadProgress"
68
+ :title="totalBytesUploaded"
69
+ ></progress>
70
+ </span>
71
+ </li>
72
+ </ul>
73
+ <hr/>
74
+ </template>
75
+ <!-- end total-upload information -->
76
+
77
+ <!-- begin list of selected files -->
78
+ <h4>{{ getMessage("cmduploadform.headline.list_of_selected_files") }}</h4>
79
+ <ul class="list-of-files">
80
+ <li
81
+ v-for="(uploadFile, index) in listOfFiles"
82
+ :key="index"
83
+ class="flex-container no-flex"
84
+ >
85
+ <a
86
+ href="#"
87
+ :title="getMessage('cmduploadform.labeltext.remove_file_from_list')"
88
+ @click.prevent="removeFile(index)"
89
+ ><span :class="deleteIconClass"></span>
90
+ </a>
91
+ <span
92
+ :class="[
93
+ 'text-align-right',
94
+ uploadFile.allowedType ? 'allowed' : 'not-allowed',
95
+ { error: uploadFile.error }
96
+ ]"
97
+ >
98
+ {{ uploadFile.file.name }} ({{ formatSize(uploadFile.file.size) }})
99
+ </span>
100
+ <template v-if="uploadInitiated && !uploadFile.error">
101
+ <span class="progressbar">
102
+ <span>{{ getPercentage(uploadFile.progress) }}</span>
103
+ <!-- do not place inside progress-tag (will not be displayed then) -->
104
+ <progress
105
+ max="100"
106
+ :value="uploadFile.progress"
107
+ :title="
108
+ formatSize(uploadFile.uploadedBytes) + '/' + formatSize(uploadFile.file.size)
109
+ "
110
+ ></progress>
111
+ </span>
112
+ </template>
113
+ </li>
114
+ </ul>
115
+ <a
116
+ v-if="failedUpload"
117
+ href="#"
118
+ @click.prevent="cancel"
119
+ :title="getMessage('cmduploadform.all_files_will_be_removed')">
120
+ {{ getMessage("cmduploadform.reset_upload") }}
121
+ </a>
122
+ <hr/>
123
+ </template>
124
+ <!-- end list of selected files -->
125
+
126
+ <!-- begin upload conditions -->
127
+ <h4 v-if="allowMultipleFileUploads && listOfFiles.length">
128
+ {{ getMessage("cmduploadform.headline.select_additional_files") }}
129
+ </h4>
130
+ <h4 v-if="!allowMultipleFileUploads && listOfFiles.length">
131
+ {{ getMessage("cmduploadform.headline.select_new_file") }}
132
+ </h4>
133
+ <dl class="small">
134
+ <template v-if="maxTotalUploadSize > 0">
135
+ <dt :class="{ error: totalSize > maxTotalUploadSize }">
136
+ {{ getMessage("cmduploadform.max_total_upload_size") }}
137
+ </dt>
138
+ <dd :class="['text-align-right', { error: totalSize > maxTotalUploadSize }]">
139
+ {{ formatSize(maxTotalUploadSize) }}
140
+ </dd>
141
+ </template>
142
+ <dt :class="{ error: errors.fileSize }">
143
+ {{ getMessage("cmduploadform.max_file_upload_size") }}
144
+ </dt>
145
+ <dd :class="['text-align-right', { error: errors.fileSize }]">
146
+ {{ formatSize(maxFileUploadSize) }}
147
+ </dd>
148
+ <dt :class="{ error: errors.fileType }">
149
+ {{ getMessage("cmduploadform.allowed_file_types") }}
150
+ </dt>
151
+ <dd>
152
+ <a
153
+ :class="showListOfFileExtensions ? 'icon-not-visible' : 'icon-visible'"
154
+ href="#"
155
+ @click.prevent="showListOfFileExtensions = !showListOfFileExtensions"
156
+ :title="getMessage('cmduploadform.tooltip.toggle_list_of_allowed_file_types')"
157
+ ></a>
158
+ <transition name="fade">
159
+ <ul v-if="showListOfFileExtensions" class="list-of-file-extensions">
160
+ <li
161
+ v-for="(fileExtension, index) in allowedFileExtensions"
162
+ :key="index"
163
+ :class="{ error: errors.fileType }"
164
+ >
165
+ {{ fileExtension }}
166
+ </li>
167
+ </ul>
168
+ </transition>
169
+ </dd>
170
+ </dl>
171
+ <!-- end upload conditions -->
172
+ <button
173
+ type="button"
174
+ :class="['button upload primary', { disabled: uploadInitiated }]"
175
+ :disabled="uploadInitiated"
176
+ @click="selectFiles()"
177
+ >
178
+ <span class="icon-file-upload"></span>
179
+ <span v-if="allowMultipleFileUploads">{{
180
+ getMessage("cmduploadform.labeltext.select_files")
181
+ }}</span>
182
+ <span v-else>{{ getMessage("cmduploadform.labeltext.select_file") }}</span>
183
+ </button>
184
+ <p v-if="enableDragAndDrop" :class="['text-drag-and-drop', { disabled: uploadInitiated }]">
185
+ <span>{{ getMessage("cmduploadform.or") }}</span>
186
+ <strong>
187
+ {{ getMessage("cmduploadform.drag_and_drop") }}
188
+ <template v-if="allowMultipleFileUploads && listOfFiles.length">
189
+ {{ getMessage("cmduploadform.additional") }}
190
+ </template>
191
+ <template v-if="!allowMultipleFileUploads && listOfFiles.length">
192
+ {{ getMessage("cmduploadform.new") }}
193
+ </template>
194
+ {{ getMessage("cmduploadform.files_to_this_area") }}
195
+ </strong>
196
+ </p>
197
+ </div>
198
+ <!-- begin CmdFormElement -->
46
199
  <CmdFormElement
47
200
  v-if="enableComment"
48
201
  element="textarea"
49
- labelText="Comment:"
50
- placeholder="Add a comment"
51
- v-model:value="comment"
202
+ :labelText="getMessage('cmduploadform.labeltext.comment')"
203
+ v-model="comment"
204
+ :required="commentRequired"
205
+ :statusMessage="commentStatusMessage"
206
+ :placeholder="getMessage('cmduploadform.placeholder.comment')"
207
+ :status="commentStatusMessage ? 'error' : ''"
52
208
  />
209
+ <!-- end CmdFormElement -->
210
+
53
211
  <div class="button-wrapper no-flex">
54
- <button :class="['button', 'primary', {disabled: getNumberAllowedFiles < 1}]" @click="uploadFiles">
212
+ <button
213
+ :class="[
214
+ 'button primary',
215
+ {
216
+ disabled:
217
+ listOfFiles.length === 0 ||
218
+ (maxTotalUploadSize > 0 && totalSize > maxTotalUploadSize) ||
219
+ uploadInitiated
220
+ }
221
+ ]"
222
+ :disabled="
223
+ listOfFiles.length === 0 ||
224
+ (maxTotalUploadSize > 0 && totalSize > maxTotalUploadSize) ||
225
+ uploadInitiated
226
+ "
227
+ @click="uploadFiles"
228
+ >
55
229
  <span class="icon-upload"></span>
56
- <span v-if="getNumberAllowedFiles < 1">Nothing to upload</span>
57
- <span v-else>Upload {{ getNumberAllowedFiles }} of {{ listOfFiles.length }} files</span>
230
+ <span v-if="listOfFiles.length === 1 || !allowMultipleFileUploads">
231
+ {{ getMessage("cmduploadform.buttontext.upload_file") }}
232
+ </span>
233
+ <span v-else>{{ getMessage("cmduploadform.buttontext.upload_files") }}</span>
58
234
  </button>
59
- <button class="button" @click="cancelUpload">
60
- <span class="icon-cancel"></span><span>Cancel</span>
235
+ <button :class="['button', { disabled: listOfFiles.length === 0 }]" @click="cancel">
236
+ <span class="icon-cancel"></span>
237
+ <span>{{ getMessage("cmduploadform.buttontext.cancel") }}</span>
61
238
  </button>
62
239
  </div>
63
240
  </fieldset>
241
+ <!-- end advanced mode -->
242
+
243
+ <!-- begin simple mode -->
244
+ <a v-else href="#" @click.prevent="selectFiles" :class="['cmd-upload-form drop-area', {'allow-drop': allowDrop }]" v-on="dragAndDropHandler">
245
+ <span class="progressbar" v-if="uploadInitiated">
246
+ <span>{{ getPercentage(totalUploadProgress) }}</span>
247
+ <progress
248
+ max="100"
249
+ :value="totalUploadProgress"
250
+ :title="totalBytesUploaded">
251
+ </progress>
252
+ </span>
253
+
254
+ <!-- begin slot-content -->
255
+ <slot>
256
+ <template v-if="enableDragAndDrop">
257
+ <template v-if="fileTypeImage">
258
+ <span>{{ getMessage("cmduploadform.select_image") }}</span>
259
+ <span class="icon-image"></span>
260
+ </template>
261
+ <template v-else>
262
+ <span>{{ getMessage("cmduploadform.select_file") }}</span>
263
+ <span class="icon-file"></span>
264
+ </template>
265
+ </template>
266
+ <template v-else>
267
+ <span>{{ getMessage("cmduploadform.drag_and_drop_file_here") }}</span>
268
+ <span class="icon-drag-and-drop"></span>
269
+ </template>
270
+ <small>{{ getMessage("cmduploadform.max_upload_size") }} {{ formatSize(maxFileUploadSize) }}</small>
271
+ <small>{{ getMessage("cmduploadform.allowed_file_types") }} {{ allowedFileExtensions }}</small>
272
+ </slot>
273
+ <!-- end slot-content -->
274
+ </a>
275
+ <!-- end simple mode -->
276
+
277
+ <!-- begin CmdFormElement -->
278
+ <CmdFormElement
279
+ element="input"
280
+ type="file"
281
+ :labelText="getMessage('cmduploadform.labeltext.select_files')"
282
+ :disabled="uploadInitiated"
283
+ :multiple="allowMultipleFileUploads"
284
+ @change="filesSelected"
285
+ ref="formElement"
286
+ />
287
+ <!-- end CmdFormElement -->
64
288
  </template>
65
289
 
66
290
  <script>
291
+ // import mixins
292
+ import I18n from "../mixins/I18n"
293
+ import DefaultMessageProperties from "../mixins/CmdUploadForm/DefaultMessageProperties"
294
+
295
+ import {getFileExtension} from "../utils/GetFileExtension.js"
296
+ import axios from "axios"
297
+
67
298
  // import components
299
+ import CmdCustomHeadline from "./CmdCustomHeadline"
68
300
  import CmdFormElement from "./CmdFormElement"
69
301
  import CmdSystemMessage from "./CmdSystemMessage"
70
302
 
71
303
  export default {
72
304
  name: "CmdUploadForm",
73
- emits: ["click"],
305
+ emits: ["click", "error", "upload-complete", "upload-file-success"],
306
+ mixins: [I18n, DefaultMessageProperties],
307
+ components: {
308
+ CmdCustomHeadline,
309
+ CmdFormElement,
310
+ CmdSystemMessage,
311
+ },
74
312
  data() {
75
313
  return {
76
314
  comment: "",
77
315
  allowDrop: false,
78
- listOfFiles: []
316
+ listOfFiles: [],
317
+ systemMessages: [],
318
+ defaultSystemMessageStatus: "",
319
+ showListOfFileExtensions: false,
320
+ resetForm: {},
321
+ uploadInitiated: false,
322
+ errors: {}
79
323
  }
80
324
  },
81
- components: {
82
- CmdFormElement,
83
- CmdSystemMessage
325
+ created() {
326
+ // Set initial data for resetForm.
327
+ this.resetForm.comment = this.presetComment
328
+ this.resetForm.allowDrop = this.allowDrop
329
+ this.resetForm.listOfFiles = JSON.parse(JSON.stringify(this.listOfFiles))
330
+ this.resetForm.systemMessages = this.systemMessages
331
+ this.resetForm.systemMessageStatus = this.systemMessageStatus
84
332
  },
85
333
  props: {
86
334
  /**
87
- * headline for form
335
+ * set icon class for delete-icons
88
336
  */
89
- headline: {
337
+ deleteIconClass: {
90
338
  type: String,
91
- required: false
339
+ default: "icon-delete"
92
340
  },
93
341
  /**
94
- * url to uplaod files
342
+ * toggle visibility of total upload (number of files, total size, total progress
95
343
  */
96
- uploadURL: {
97
- type: String,
98
- required: false
344
+ showTotalUpload: {
345
+ type: Boolean,
346
+ default: true
99
347
  },
100
348
  /**
101
- * activate if files for upload should be selected by native input (type="file")
349
+ * toggle if upload is handled by component itself or by outer component
102
350
  */
103
- enableFileSelect: {
351
+ componentHandlesUpload: {
104
352
  type: Boolean,
105
353
  default: true
106
354
  },
107
355
  /**
108
- * activate if files should be dragged and dropped from os-file-explorer
356
+ * list of allowed file extensions to upload (all can be selected)
109
357
  */
110
- enableDragAndDrop: {
358
+ allowedFileExtensions: {
359
+ type: Array,
360
+ required: true
361
+ },
362
+ /**
363
+ * activate if the comment given by the user should be mandatory
364
+ *
365
+ * enableComment-property must be set to true
366
+ */
367
+ commentRequired: {
111
368
  type: Boolean,
112
369
  default: true
113
370
  },
114
371
  /**
115
- * enable textarea for additional comments
372
+ * show a message if mandatory comment-textarea is not filled
373
+ *
374
+ * enableComment-property and commentRequired-property must be set to true
375
+ */
376
+ commentStatusMessage: {
377
+ type: String,
378
+ default: ""
379
+ },
380
+ /**
381
+ * properties for CmdCustomHeadline-component
382
+ */
383
+ cmdCustomHeadline: {
384
+ type: Object,
385
+ required: false
386
+ },
387
+ /**
388
+ * enable if files can also be dragged (and dropped) into upload-area
389
+ */
390
+ enableDragAndDrop: {
391
+ type: Boolean,
392
+ default: false
393
+ },
394
+ /**
395
+ * enable if a comment should be possible to left by the user
116
396
  */
117
397
  enableComment: {
118
398
  type: Boolean,
119
399
  default: true
120
400
  },
121
401
  /**
122
- * set upload options
402
+ * preset the comment-textarea
403
+ *
404
+ * enableComment-property must be set to true
123
405
  */
124
- uploadOptions: {
125
- type: Object,
126
- required: false
406
+ presetComment: {
407
+ type: String,
408
+ default: ""
127
409
  },
128
410
  /**
129
- * list of allowed file types to upload (all can be selected)
411
+ * set to 0 if no maximum for total upload size should be set
130
412
  */
131
- allowedFileTypes: {
132
- type: Array,
133
- required: true
413
+ maxTotalUploadSize: {
414
+ type: Number,
415
+ default: 5242880
134
416
  },
135
417
  /**
136
- * set maximum upload size (for all files combined)
418
+ * max file size (in bytes) for each single file
137
419
  */
138
- maxUploadSize: {
420
+ maxFileUploadSize: {
139
421
  type: Number,
140
422
  default: 10485760
423
+ },
424
+ /**
425
+ * enable if more than file should be enabled to be selected for upload
426
+ */
427
+ allowMultipleFileUploads: {
428
+ type: Boolean,
429
+ default: false
430
+ },
431
+ /**
432
+ * defines upload options if component handles upload itself
433
+ *
434
+ * componentHandlesUpload-property must be true
435
+ */
436
+ uploadOptions: {
437
+ type: Object,
438
+ required: false
439
+ },
440
+ /**
441
+ * activate to use full upload-form-style and -functionality
442
+ *
443
+ * @affectsStyling: true
444
+ */
445
+ advancedMode: {
446
+ type: Boolean,
447
+ default: true
141
448
  }
142
449
  },
143
450
  computed: {
144
- uploadSize() {
145
- let uploadSize = 0
451
+ fileTypeImage() {
452
+ return this.allowedFileExtensions.some(extension => extension.includes('jpg'));
453
+
454
+ },
455
+ failedUpload() {
456
+ return this.listOfFiles.some(file => file.error)
457
+ },
458
+ totalBytesUploaded() {
459
+ const bytes = this.listOfFiles
460
+ .map(uploadFile => [
461
+ uploadFile.file.size,
462
+ ((uploadFile.progress || 0) * uploadFile.file.size) / 100
463
+ ])
464
+ .reduce((a, b) => [a[0] + b[0], a[1] + b[1]])
465
+ return this.formatSize(bytes[1]) + "/" + this.formatSize(bytes[0])
466
+ },
467
+ totalSize() {
468
+ let totalSize = 0
146
469
  for (let i = 0; i < this.listOfFiles.length; i++) {
147
- if (this.listOfFiles[i].allowedType) {
148
- uploadSize = uploadSize + this.listOfFiles[i].file.size
149
- }
470
+ totalSize = totalSize + this.listOfFiles[i].file.size
150
471
  }
151
- return uploadSize
472
+ return totalSize
152
473
  },
153
- getNumberAllowedFiles() {
154
- let numberAllowedFiles = 0
155
- for (let i = 0; i < this.listOfFiles.length; i++) {
156
- if (this.listOfFiles[i].allowedType) {
157
- numberAllowedFiles++
474
+ allSystemMessages() {
475
+ if (this.maxTotalUploadSize > 0 && this.totalSize > this.maxTotalUploadSize) {
476
+ return [
477
+ this.getMessage("cmduploadform.system_message_total_size_of_files_too_large"),
478
+ ...this.systemMessages
479
+ ]
480
+ }
481
+ return this.systemMessages
482
+ },
483
+ systemMessageStatus() {
484
+ return this.defaultSystemMessageStatus || (this.allSystemMessages.length ? "error" : "")
485
+ },
486
+ dragAndDropHandler() {
487
+ // register handlers only if drag-and-drop is enabled
488
+ if (this.enableDragAndDrop) {
489
+ return {
490
+ dragenter: this.dragEnter,
491
+ dragover: this.dragOver,
492
+ dragleave: this.dragLeave,
493
+ drop: this.drop
158
494
  }
159
495
  }
160
- return numberAllowedFiles
496
+ return {}
497
+ },
498
+ totalUploadProgress() {
499
+ const progress = this.listOfFiles
500
+ .map(uploadFile => [
501
+ uploadFile.file.size,
502
+ ((uploadFile.progress || 0) * uploadFile.file.size) / 100
503
+ ])
504
+ .reduce((a, b) => [a[0] + b[0], a[1] + b[1]])
505
+ return (progress[1] / progress[0]) * 100
506
+ }
507
+ },
508
+ watch: {
509
+ presetComment: {
510
+ handler(newValue) {
511
+ this.comment = newValue
512
+ },
513
+ immediate: true
161
514
  }
162
515
  },
163
516
  methods: {
517
+ getPercentage(percentage) {
518
+ if (percentage) {
519
+ return percentage.toFixed(2) + "%"
520
+ }
521
+ return "0.00%"
522
+ },
523
+ // use imported function as method (to use in template)
524
+ getFileExtension(filename) {
525
+ return getFileExtension(filename)
526
+ },
527
+ selectFiles() {
528
+ let inputFile = this.$refs.formElement.getDomElement().querySelector("input[type='file']")
529
+ inputFile.click()
530
+ },
164
531
  dragEnter(event) {
165
532
  this.dragOver(event)
166
533
  },
@@ -187,11 +554,11 @@ export default {
187
554
  this.allowDrop = false
188
555
  },
189
556
  /*
190
- drag(event) {
191
- alert("dropped")
192
- event.dataTransfer.setData("text", event.target.id)
193
- },
194
- */
557
+ drag(event) {
558
+ alert("dropped")
559
+ event.dataTransfer.setData("text", event.target.id)
560
+ },
561
+ */
195
562
  drop(event) {
196
563
  this.allowDrop = false
197
564
  if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length > 0) {
@@ -200,144 +567,431 @@ export default {
200
567
  }
201
568
  },
202
569
  cancelUpload() {
570
+ // cancel upload for each file
571
+ this.listOfFiles.forEach(file => {
572
+ if (file.abortController) {
573
+ file.abortController.abort()
574
+ }
575
+ })
576
+
577
+ // clear list of files, remove error-highlighting and hide all system-messages afterwards
578
+ this.errors = {}
203
579
  this.listOfFiles = []
204
- this.comment = ""
205
- this.$emit('click', 'cancel')
580
+ this.hideAllSystemMessages()
581
+
582
+ // set uploadInitiated to false to enable all disabled buttons
583
+ this.uploadInitiated = false
584
+ },
585
+ cancel() {
586
+ this.cancelUpload()
587
+
588
+ // emit click event with argument "cancel" to react in outer component
589
+ this.$emit("click", "cancel")
206
590
  },
207
591
  filesSelected(event) {
208
592
  this.checkFiles(event.target.files)
209
593
  },
210
594
  checkFiles(files) {
595
+ if (!files.length) {
596
+ return
597
+ }
598
+
599
+ this.defaultSystemMessageStatus = ""; // hide systemMessage if already is shown
600
+ this.systemMessages = [] // hide systemMessage if already is shown
601
+ this.errors = {}
602
+
211
603
  for (let i = 0; i < files.length; i++) {
212
- let uploadFile = {
213
- "file": files[i],
214
- "allowedType": false,
215
- width: 0,
216
- height: 0
604
+ // define file-object which will be pushed in listOfFiles
605
+ const uploadFile = {
606
+ file: files[i],
607
+ progress: null,
608
+ uploadedBytes: 0
217
609
  }
218
610
 
219
- if (this.allowedFileTypes.includes(files[i].type)) {
220
- uploadFile.allowedType = true
611
+ // check size for current file
612
+ if (files[i].size > this.maxFileUploadSize) {
613
+ this.errors.fileSize = true
614
+ this.systemMessages.push(
615
+ this.getMessage(
616
+ "cmduploadform.system_message.file_size_too_large",
617
+ files[i].name,
618
+ files[i].size
619
+ )
620
+ )
621
+ continue
221
622
  }
222
623
 
223
- if (files[i].type.slice(0, 6) == "image/" && files[i].size < this.maxUploadSize) {
224
- // get dimensions if image
225
- const reader = new FileReader()
226
-
227
- reader.addEventListener('load', function () {
228
- const img = new Image()
229
- img.src = this.result
230
- img.addEventListener('load', () => {
231
- uploadFile.width = img.width
232
- uploadFile.height = img.height
233
- })
234
- })
235
- reader.readAsDataURL(files[i])
624
+ // check if current file has allowed file-type (else continue with next file)
625
+ if (!this.allowedFileExtensions.includes(getFileExtension(files[i].name))) {
626
+ this.showListOfFileExtensions = true
627
+ this.errors.fileType = true
628
+ this.systemMessages.push(
629
+ this.getMessage(
630
+ "cmduploadform.system_message.not_allowed_file_type",
631
+ files[i].name,
632
+ getFileExtension(files[i].name)
633
+ )
634
+ )
635
+ continue
636
+ }
637
+
638
+ // check (if multiple files can be selected) if current file already exists in listOfFiles
639
+ if (
640
+ this.allowMultipleFileUploads &&
641
+ this.listOfFiles.some(listOfFilesEntry =>
642
+ this.compareFiles(listOfFilesEntry.file, files[i])
643
+ )
644
+ ) {
645
+ this.systemMessages.push(
646
+ this.getMessage(
647
+ "cmduploadform.system_message.duplicate_file",
648
+ files[i].name,
649
+ getFileExtension(files[i].name)
650
+ )
651
+ )
652
+ continue
653
+ }
654
+
655
+ if (this.allowMultipleFileUploads) {
656
+ // push file-object (for each valid file) to listOfFiles-array
657
+ this.listOfFiles.push(uploadFile)
658
+ } else {
659
+ if (files.length > 1) {
660
+ this.systemMessages.push(
661
+ this.getMessage("cmduploadform.system_message.only_one_file_allowed")
662
+ )
663
+ }
664
+ // assign uploadFile-object (which contains current (and valid) file to listOfFiles-array
665
+ this.listOfFiles = [uploadFile]
666
+ break
236
667
  }
237
- this.listOfFiles.push(uploadFile)
238
668
  }
669
+
670
+ if (!this.advancedMode) {
671
+ if (this.systemMessages.length) {
672
+ this.$emit("error", {messages: this.systemMessages})
673
+ return
674
+ }
675
+ if (this.listOfFiles.length) {
676
+ this.uploadFiles()
677
+ }
678
+ }
679
+ },
680
+ compareFiles(file1, file2) {
681
+ return (
682
+ file1.name === file2.name &&
683
+ file1.lastModified === file2.lastModified &&
684
+ file1.size === file2.size
685
+ )
239
686
  },
240
687
  removeFile(index) {
241
- /* remove file from list */
688
+ /* remove specific file from list */
242
689
  this.listOfFiles.splice(index, 1)
690
+ if (!this.listOfFiles.length) {
691
+ this.uploadInitiated = false
692
+ }
693
+ this.hideAllSystemMessages()
694
+ },
695
+ hideAllSystemMessages() {
696
+ // hide all system-messages if all files are removed from list
697
+ if (!this.listOfFiles.length) {
698
+ this.systemMessages = []
699
+ }
243
700
  },
244
701
  formatSize(size) {
245
702
  if (size < 1024) {
246
703
  return size
247
704
  } else if (size < 1048576) {
248
- return size = (Math.round(size / 1024 * 100) / 100) + " KB"
705
+ return (size = Math.round((size / 1024) * 100) / 100 + " KB")
249
706
  } else {
250
- return size = (Math.round(size / 1048576 * 100) / 100) + " MB"
707
+ return (size = Math.round((size / 1048576) * 100) / 100 + " MB")
251
708
  }
252
709
  },
253
710
  uploadFiles() {
254
- if (this.uploadURL) {
255
- let formData = new FormData()
256
- for (let i; i < this.listOfFiles.length; i++) {
257
- if (this.listOfFiles[i].allowedType) {
258
- formData.append("uploadedFiles", this.listOfFiles[i].file)
711
+ this.systemMessages = []
712
+ this.errors = {}
713
+ this.defaultSystemMessageStatus = ""
714
+
715
+ if (
716
+ this.enableComment &&
717
+ !this.comment &&
718
+ this.commentRequired &&
719
+ this.commentStatusMessage
720
+ ) {
721
+ this.defaultSystemMessageStatus = "error"
722
+ this.systemMessages.push(this.getMessage("cmduploadform.system_message.fill_required"))
723
+ } else {
724
+ this.uploadInitiated = true
725
+
726
+ if (this.componentHandlesUpload && this.uploadOptions && this.uploadOptions.url) {
727
+ const url = new URL(this.uploadOptions.url, location.href)
728
+ const formData = new FormData()
729
+
730
+ // // append information about files to formData-object
731
+ // formData.append(
732
+ // this.uploadOptions.filesParam ? this.uploadOptions.filesParam : "files",
733
+ // this.listOfFiles.map(uploadFile => uploadFile.file)
734
+ // )
735
+
736
+ // iterate over additionalParams-object and append to formData-object
737
+ Object.entries(this.uploadOptions.additionalParams || {}).forEach(([key, value]) =>
738
+ formData.append(key, value)
739
+ )
740
+
741
+ // append comment to formData-object
742
+ if (this.enableComment) {
743
+ formData.append("comment", this.comment)
259
744
  }
260
- }
261
745
 
262
- if (formData.has("uploadedFiles")) {
263
- fetch(this.uploadURL, {method: "POST", body: formData})
746
+ const requests = []
747
+
748
+ // iterate over list-of-files to send upload request for each file individually
749
+ this.listOfFiles.forEach(file => {
750
+ requests.push(this.uploadSingleFile(url, file, formData))
751
+ })
752
+
753
+ // check upload-status for each file individually
754
+ Promise.allSettled(requests).then(results => {
755
+ // if status equals "rejected" the upload was not successful and the file will not be deleted from list
756
+ const rejectedFiles = results.filter(result => result.status === "rejected")
757
+ this.uploadInitiated = false
758
+
759
+ if (rejectedFiles.length) {
760
+ this.defaultSystemMessageStatus = "error"
761
+ this.systemMessages.push(
762
+ this.getMessage(
763
+ "cmduploadform.system_message.some_files_are_not_uploaded_successfully"
764
+ )
765
+ )
766
+ } else {
767
+ this.defaultSystemMessageStatus = "success"
768
+ this.systemMessages.push(
769
+ this.getMessage("cmduploadform.system_message.all_files_are_uploaded_successfully")
770
+ )
771
+ }
772
+
773
+ this.$emit("upload-complete", {success: !rejectedFiles.length})
774
+ })
775
+ } else {
776
+ let uploadObj = {}
777
+ uploadObj.listOfFiles = this.listOfFiles
778
+ uploadObj.type = "upload"
779
+ uploadObj.comment = this.comment
780
+
781
+ // emit uploadObj to handle upload by outer component
782
+ this.$emit("click", uploadObj, this.showMessage)
264
783
  }
784
+ }
785
+ },
786
+ onUploadProgress(event, uploadFile) {
787
+ if (event.lengthComputable) {
788
+ uploadFile.progress = (event.loaded / event.total) * 100
789
+ uploadFile.uploadedBytes = event.loaded
265
790
  } else {
266
- this.$emit('click', 'upload')
791
+ uploadFile.progress = null
792
+ uploadFile.uploadedBytes = 0
267
793
  }
794
+ this.$forceUpdate()
268
795
  },
269
- messageStatusUploadSize() {
270
- if (this.uploadSize > this.maxUploadSize || this.uploadSize === 0) {
271
- return "error"
796
+ uploadSingleFile(url, file, formData) {
797
+ file.abortController = new AbortController()
798
+
799
+ // append information about given file to formData-object
800
+ formData.set(
801
+ this.uploadOptions.filesParam ? this.uploadOptions.filesParam : "files",
802
+ file.file
803
+ )
804
+
805
+ return (
806
+ axios
807
+ .post(url, formData, {
808
+ signal: file.abortController.signal,
809
+ onUploadProgress: event => this.onUploadProgress(event, file)
810
+ })
811
+ // emit information about successful-upload + file
812
+ .then(response => {
813
+ this.$emit("upload-file-success", file)
814
+ return response
815
+ })
816
+ // delete uploaded file from list-of-files-array
817
+ .then(response => {
818
+ const positionOfFile = this.listOfFiles.indexOf(file)
819
+ if (positionOfFile !== -1) {
820
+ this.listOfFiles.splice(positionOfFile, 1)
821
+ }
822
+ return response
823
+ })
824
+ .catch(error => {
825
+ this.defaultSystemMessageStatus = "error"
826
+ this.systemMessages.push(error)
827
+ file.error = true
828
+ throw new Error()
829
+ })
830
+ )
831
+ },
832
+ showMessage(result) {
833
+ if (result === true) {
834
+ this.defaultSystemMessageStatus = "success"
835
+ this.systemMessages.push(this.getMessage("cmduploadform.system_message.upload_success"))
836
+ } else if (result === false) {
837
+ this.defaultSystemMessageStatus = "error"
838
+ this.systemMessages.push(this.getMessage("cmduploadform.system_message.upload_failed"))
272
839
  }
273
- return "success"
274
840
  },
275
- openFileDialog() {
276
- this.$refs.upload.querySelector("input[type='file']").click()
841
+ resetFields() {
842
+ if (typeof this.resetForm === "object") {
843
+ const resetArr = Object.keys(this.resetForm)
844
+ for (let key of resetArr) {
845
+ if (typeof this.resetForm[key] === "object") {
846
+ this[key] = JSON.parse(JSON.stringify(this.resetForm[key]))
847
+ } else {
848
+ this[key] = this.resetForm[key]
849
+ }
850
+ }
851
+ }
277
852
  }
278
853
  }
279
854
  }
280
855
  </script>
281
856
 
282
857
  <style lang="scss">
283
- /* begin cmd-upload-form ---------------------------------------------------------------------------------------- */
858
+ /* begin cmd-upload-form -------------------------------------------------------------------------------------------- */
284
859
  .cmd-upload-form {
285
860
  .box {
286
- padding: calc(var(--default-padding) * 3);
287
- text-align: center;
288
- background: rgba(255, 255, 255, .5);
289
- border-style: dashed;
290
-
291
- &.allow-drop {
292
- border-style: solid;
293
- background: rgba(255, 255, 255, 1);
294
- }
295
- }
861
+ background: var(--pure-white-reduced-opacity);
296
862
 
297
- .list-of-files {
298
- margin-bottom: 0;
863
+ dl {
864
+ justify-content: center;
865
+ text-align: left;
299
866
 
300
- li {
301
- list-style-type: none;
302
- margin-left: 0;
867
+ .list-of-file-extensions {
868
+ display: table;
303
869
 
304
- > span {
305
- span[class*="icon"]:last-child {
306
- display: inline-flex;
307
- align-items: center;
308
- justify-content: center;
309
- padding: calc(var(--default-padding) / 2);
870
+ > li:only-child {
871
+ list-style-type: none;
872
+ margin: 0;
310
873
  }
874
+ }
875
+ }
876
+ }
311
877
 
312
- &.allowed {
313
- color: var(--success-color);
878
+ [class*="list-of-file"] {
879
+ max-height: 10rem;
880
+ overflow-x: hidden;
881
+ overflow-y: auto;
882
+ border: var(--default-border);
883
+ padding: calc(var(--default-padding) / 2);
884
+ margin: 0;
314
885
 
315
- span[class*="icon"]:last-child {
316
- border: var(--success-border);
317
- background-color: var(--success-color);
318
- }
319
- }
886
+ > li {
887
+ flex-wrap: nowrap;
888
+ margin-right: var(--default-margin); /* avoids text to be placed below scrollbar */
889
+
890
+ .progressbar {
891
+ display: table;
892
+ align-self: center;
320
893
 
321
- &.not-allowed {
322
- color: var(--error-color);
894
+ progress {
895
+ &[value] {
896
+ background: var(--pure-white);
323
897
 
324
- span[class*="icon"]:last-child {
325
- border: var(--error-border);
326
- background-color: var(--error-color);
898
+ &::-moz-progress-bar {
899
+ border-top-left-radius: var(--border-radius);
900
+ border-bottom-left-radius: var(--border-radius);
901
+ background: var(--primary-color);
902
+ }
327
903
  }
328
904
  }
329
905
 
330
- span[class*="icon"]:last-child {
331
- border: var(--default-border);
332
- border-radius: var(--full-circle);
333
- font-size: 1rem;
334
- font-weight: bold;
335
- color: var(--pure-white);
906
+ & > span {
907
+ position: absolute;
908
+ left: 50%;
909
+ transform: translateX(-50%);
910
+ z-index: 1;
911
+ font-size: 1.2rem;
912
+ display: table;
913
+ top: 0.2rem;
914
+ padding: 0.1rem 0.2rem;
915
+ line-height: 100%;
916
+ background: var(--pure-white);
336
917
  }
337
918
  }
338
919
  }
339
920
  }
921
+
922
+ .list-of-files {
923
+ display: inline-flex;
924
+ flex-direction: column;
925
+ gap: calc(var(--default-gap) / 2);
926
+
927
+ &:last-of-type {
928
+ margin-bottom: var(--default-margin);
929
+ }
930
+
931
+ li {
932
+ list-style-type: none;
933
+ margin-left: 0;
934
+ gap: calc(var(--default-gap) / 2);
935
+ }
936
+
937
+ & + a {
938
+ display: table;
939
+ margin: 0 auto;
940
+ }
941
+ }
942
+
943
+ p {
944
+ &.text-drag-and-drop {
945
+ &.disabled {
946
+ background: none !important; /* overwrite default styling for .disabled */
947
+ }
948
+ }
949
+ }
950
+
951
+ .button.upload {
952
+ align-self: center;
953
+
954
+ & ~ p {
955
+ & > * {
956
+ display: block;
957
+ }
958
+ }
959
+ }
960
+
961
+ .error {
962
+ color: var(--error-color);
963
+ }
964
+
965
+ & + .cmd-form-element {
966
+ display: none;
967
+ }
340
968
  }
341
969
 
342
- /* end cmd-upload-form ------------------------------------------------------------------------------------------ */
970
+ .drop-area {
971
+ border: var(--default-border);
972
+ border-style: dashed;
973
+ background: var(--pure-white);
974
+ padding: (var(--default-padding));
975
+ text-align: center;
976
+
977
+ &.allow-drop {
978
+ border-style: solid;
979
+ }
980
+
981
+ > span[class*="icon"] {
982
+ font-size: 5rem;
983
+ }
984
+ }
985
+
986
+ a.drop-area {
987
+ display: inline-flex;
988
+ flex-direction: column;
989
+ text-decoration: none;
990
+ background: var(--default-background-color);
991
+
992
+ span {
993
+ margin: 0;
994
+ }
995
+ }
996
+ /* end cmd-upload-form ---------------------------------------------------------------------------------------------- */
343
997
  </style>