comand-component-library 3.1.43 → 3.1.46

Sign up to get free protection for your applications and to get access to all the features.
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>