@ministryofjustice/frontend 4.0.1 → 5.1.0

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 (256) hide show
  1. package/govuk-prototype-kit.config.json +19 -4
  2. package/moj/_base.scss +2 -0
  3. package/moj/_base.scss.map +1 -0
  4. package/moj/all.bundle.js +3010 -0
  5. package/moj/all.bundle.js.map +1 -0
  6. package/moj/all.bundle.mjs +3293 -0
  7. package/moj/all.bundle.mjs.map +1 -0
  8. package/moj/all.mjs +17 -110
  9. package/moj/all.mjs.map +1 -1
  10. package/moj/all.scss +3 -0
  11. package/moj/all.scss.map +1 -0
  12. package/moj/common/index.mjs +57 -0
  13. package/moj/common/index.mjs.map +1 -0
  14. package/moj/common/moj-frontend-version.mjs +14 -0
  15. package/moj/common/moj-frontend-version.mjs.map +1 -0
  16. package/moj/components/_all.scss +2 -0
  17. package/moj/components/_all.scss.map +1 -0
  18. package/moj/components/action-bar/_action-bar.scss +2 -0
  19. package/moj/components/action-bar/_action-bar.scss.map +1 -0
  20. package/moj/components/add-another/_add-another.scss +2 -0
  21. package/moj/components/add-another/_add-another.scss.map +1 -0
  22. package/moj/components/add-another/add-another.bundle.js +157 -0
  23. package/moj/components/add-another/add-another.bundle.js.map +1 -0
  24. package/moj/components/add-another/add-another.bundle.mjs +271 -0
  25. package/moj/components/add-another/add-another.bundle.mjs.map +1 -0
  26. package/moj/components/add-another/add-another.mjs +135 -91
  27. package/moj/components/add-another/add-another.mjs.map +1 -1
  28. package/moj/components/alert/_alert.scss +4 -0
  29. package/moj/components/alert/_alert.scss.map +1 -0
  30. package/moj/components/alert/alert.bundle.js +254 -0
  31. package/moj/components/alert/alert.bundle.js.map +1 -0
  32. package/moj/components/alert/alert.bundle.mjs +490 -0
  33. package/moj/components/alert/alert.bundle.mjs.map +1 -0
  34. package/moj/components/alert/alert.mjs +97 -218
  35. package/moj/components/alert/alert.mjs.map +1 -1
  36. package/moj/components/alert/{alert.spec.helper.js → alert.spec.helper.bundle.js} +1 -1
  37. package/moj/components/alert/alert.spec.helper.bundle.js.map +1 -0
  38. package/moj/components/alert/alert.spec.helper.bundle.mjs +67 -0
  39. package/moj/components/alert/alert.spec.helper.bundle.mjs.map +1 -0
  40. package/moj/components/alert/alert.spec.helper.mjs.map +1 -1
  41. package/moj/components/badge/_badge.scss +2 -0
  42. package/moj/components/badge/_badge.scss.map +1 -0
  43. package/moj/components/banner/_banner.scss +2 -0
  44. package/moj/components/banner/_banner.scss.map +1 -0
  45. package/moj/components/button-menu/README.md +12 -6
  46. package/moj/components/button-menu/_button-menu.scss +4 -1
  47. package/moj/components/button-menu/_button-menu.scss.map +1 -0
  48. package/moj/components/button-menu/button-menu.bundle.js +270 -0
  49. package/moj/components/button-menu/button-menu.bundle.js.map +1 -0
  50. package/moj/components/button-menu/button-menu.bundle.mjs +506 -0
  51. package/moj/components/button-menu/button-menu.bundle.mjs.map +1 -0
  52. package/moj/components/button-menu/button-menu.mjs +214 -280
  53. package/moj/components/button-menu/button-menu.mjs.map +1 -1
  54. package/moj/components/cookie-banner/_cookie-banner.scss +2 -0
  55. package/moj/components/cookie-banner/_cookie-banner.scss.map +1 -0
  56. package/moj/components/currency-input/_currency-input.scss +2 -0
  57. package/moj/components/currency-input/_currency-input.scss.map +1 -0
  58. package/moj/components/date-picker/_date-picker.scss +2 -0
  59. package/moj/components/date-picker/_date-picker.scss.map +1 -0
  60. package/moj/components/date-picker/date-picker.bundle.js +804 -0
  61. package/moj/components/date-picker/date-picker.bundle.js.map +1 -0
  62. package/moj/components/date-picker/date-picker.bundle.mjs +1040 -0
  63. package/moj/components/date-picker/date-picker.bundle.mjs.map +1 -0
  64. package/moj/components/date-picker/date-picker.mjs +663 -827
  65. package/moj/components/date-picker/date-picker.mjs.map +1 -1
  66. package/moj/components/filter/_filter.scss +2 -0
  67. package/moj/components/filter/_filter.scss.map +1 -0
  68. package/moj/components/filter/template.njk +1 -1
  69. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js +185 -0
  70. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js.map +1 -0
  71. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs +421 -0
  72. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs.map +1 -0
  73. package/moj/components/filter-toggle-button/filter-toggle-button.mjs +166 -81
  74. package/moj/components/filter-toggle-button/filter-toggle-button.mjs.map +1 -1
  75. package/moj/components/form-validator/form-validator.bundle.js +288 -0
  76. package/moj/components/form-validator/form-validator.bundle.js.map +1 -0
  77. package/moj/components/form-validator/form-validator.bundle.mjs +524 -0
  78. package/moj/components/form-validator/form-validator.bundle.mjs.map +1 -0
  79. package/moj/components/form-validator/form-validator.mjs +226 -149
  80. package/moj/components/form-validator/form-validator.mjs.map +1 -1
  81. package/moj/components/header/_header.scss +2 -0
  82. package/moj/components/header/_header.scss.map +1 -0
  83. package/moj/components/identity-bar/_identity-bar.scss +2 -0
  84. package/moj/components/identity-bar/_identity-bar.scss.map +1 -0
  85. package/moj/components/interruption-card/_interruption-card.scss +2 -0
  86. package/moj/components/interruption-card/_interruption-card.scss.map +1 -0
  87. package/moj/components/messages/_messages.scss +2 -0
  88. package/moj/components/messages/_messages.scss.map +1 -0
  89. package/moj/components/multi-file-upload/_multi-file-upload.scss +2 -0
  90. package/moj/components/multi-file-upload/_multi-file-upload.scss.map +1 -0
  91. package/moj/components/multi-file-upload/multi-file-upload.bundle.js +397 -0
  92. package/moj/components/multi-file-upload/multi-file-upload.bundle.js.map +1 -0
  93. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs +633 -0
  94. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs.map +1 -0
  95. package/moj/components/multi-file-upload/multi-file-upload.mjs +384 -213
  96. package/moj/components/multi-file-upload/multi-file-upload.mjs.map +1 -1
  97. package/moj/components/multi-file-upload/template.njk +1 -1
  98. package/moj/components/multi-select/_multi-select.scss +2 -0
  99. package/moj/components/multi-select/_multi-select.scss.map +1 -0
  100. package/moj/components/multi-select/multi-select.bundle.js +143 -0
  101. package/moj/components/multi-select/multi-select.bundle.js.map +1 -0
  102. package/moj/components/multi-select/multi-select.bundle.mjs +379 -0
  103. package/moj/components/multi-select/multi-select.bundle.mjs.map +1 -0
  104. package/moj/components/multi-select/multi-select.mjs +123 -64
  105. package/moj/components/multi-select/multi-select.mjs.map +1 -1
  106. package/moj/components/notification-badge/_notification-badge.scss +2 -0
  107. package/moj/components/notification-badge/_notification-badge.scss.map +1 -0
  108. package/moj/components/organisation-switcher/_organisation-switcher.scss +2 -0
  109. package/moj/components/organisation-switcher/_organisation-switcher.scss.map +1 -0
  110. package/moj/components/page-header-actions/_page-header-actions.scss +2 -0
  111. package/moj/components/page-header-actions/_page-header-actions.scss.map +1 -0
  112. package/moj/components/pagination/_pagination.scss +2 -2
  113. package/moj/components/pagination/_pagination.scss.map +1 -0
  114. package/moj/components/password-reveal/_password-reveal.scss +5 -1
  115. package/moj/components/password-reveal/_password-reveal.scss.map +1 -0
  116. package/moj/components/password-reveal/password-reveal.bundle.js +52 -0
  117. package/moj/components/password-reveal/password-reveal.bundle.js.map +1 -0
  118. package/moj/components/password-reveal/password-reveal.bundle.mjs +166 -0
  119. package/moj/components/password-reveal/password-reveal.bundle.mjs.map +1 -0
  120. package/moj/components/password-reveal/password-reveal.mjs +39 -29
  121. package/moj/components/password-reveal/password-reveal.mjs.map +1 -1
  122. package/moj/components/primary-navigation/_primary-navigation.scss +2 -0
  123. package/moj/components/primary-navigation/_primary-navigation.scss.map +1 -0
  124. package/moj/components/progress-bar/_progress-bar.scss +2 -0
  125. package/moj/components/progress-bar/_progress-bar.scss.map +1 -0
  126. package/moj/components/rich-text-editor/README.md +16 -9
  127. package/moj/components/rich-text-editor/_rich-text-editor.scss +2 -0
  128. package/moj/components/rich-text-editor/_rich-text-editor.scss.map +1 -0
  129. package/moj/components/rich-text-editor/rich-text-editor.bundle.js +210 -0
  130. package/moj/components/rich-text-editor/rich-text-editor.bundle.js.map +1 -0
  131. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs +446 -0
  132. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs.map +1 -0
  133. package/moj/components/rich-text-editor/rich-text-editor.mjs +186 -140
  134. package/moj/components/rich-text-editor/rich-text-editor.mjs.map +1 -1
  135. package/moj/components/search/_search.scss +2 -0
  136. package/moj/components/search/_search.scss.map +1 -0
  137. package/moj/components/search-toggle/{search-toggle.scss → _search-toggle.scss} +2 -0
  138. package/moj/components/search-toggle/_search-toggle.scss.map +1 -0
  139. package/moj/components/search-toggle/search-toggle.bundle.js +122 -0
  140. package/moj/components/search-toggle/search-toggle.bundle.js.map +1 -0
  141. package/moj/components/search-toggle/search-toggle.bundle.mjs +358 -0
  142. package/moj/components/search-toggle/search-toggle.bundle.mjs.map +1 -0
  143. package/moj/components/search-toggle/search-toggle.mjs +104 -43
  144. package/moj/components/search-toggle/search-toggle.mjs.map +1 -1
  145. package/moj/components/side-navigation/_side-navigation.scss +2 -0
  146. package/moj/components/side-navigation/_side-navigation.scss.map +1 -0
  147. package/moj/components/sortable-table/_sortable-table.scss +2 -2
  148. package/moj/components/sortable-table/_sortable-table.scss.map +1 -0
  149. package/moj/components/sortable-table/sortable-table.bundle.js +202 -0
  150. package/moj/components/sortable-table/sortable-table.bundle.js.map +1 -0
  151. package/moj/components/sortable-table/sortable-table.bundle.mjs +438 -0
  152. package/moj/components/sortable-table/sortable-table.bundle.mjs.map +1 -0
  153. package/moj/components/sortable-table/sortable-table.mjs +179 -122
  154. package/moj/components/sortable-table/sortable-table.mjs.map +1 -1
  155. package/moj/components/sub-navigation/_sub-navigation.scss +2 -0
  156. package/moj/components/sub-navigation/_sub-navigation.scss.map +1 -0
  157. package/moj/components/tag/_tag.scss +2 -0
  158. package/moj/components/tag/_tag.scss.map +1 -0
  159. package/moj/components/task-list/_task-list.scss +2 -0
  160. package/moj/components/task-list/_task-list.scss.map +1 -0
  161. package/moj/components/ticket-panel/_ticket-panel.scss +2 -0
  162. package/moj/components/ticket-panel/_ticket-panel.scss.map +1 -0
  163. package/moj/components/timeline/_timeline.scss +2 -0
  164. package/moj/components/timeline/_timeline.scss.map +1 -0
  165. package/moj/core/_all.scss +3 -0
  166. package/moj/core/_all.scss.map +1 -0
  167. package/moj/core/_moj-frontend-properties.scss +7 -0
  168. package/moj/core/_moj-frontend-properties.scss.map +1 -0
  169. package/moj/filters/all.js +44 -22
  170. package/moj/filters/prototype-kit-13-filters.js +4 -3
  171. package/moj/helpers/_all.scss +2 -0
  172. package/moj/helpers/_all.scss.map +1 -0
  173. package/moj/helpers/_hidden.scss +2 -0
  174. package/moj/helpers/_hidden.scss.map +1 -0
  175. package/moj/helpers/_links.scss +2 -0
  176. package/moj/helpers/_links.scss.map +1 -0
  177. package/moj/helpers.bundle.js +140 -0
  178. package/moj/helpers.bundle.js.map +1 -0
  179. package/moj/helpers.bundle.mjs +128 -0
  180. package/moj/helpers.bundle.mjs.map +1 -0
  181. package/moj/helpers.mjs +50 -77
  182. package/moj/helpers.mjs.map +1 -1
  183. package/moj/init.js +11 -2
  184. package/moj/moj-frontend.min.css +1 -1
  185. package/moj/moj-frontend.min.css.map +1 -1
  186. package/moj/moj-frontend.min.js +1 -1
  187. package/moj/moj-frontend.min.js.map +1 -1
  188. package/moj/objects/_all.scss +2 -0
  189. package/moj/objects/_all.scss.map +1 -0
  190. package/moj/objects/_button-group.scss +2 -0
  191. package/moj/objects/_button-group.scss.map +1 -0
  192. package/moj/objects/_filter-layout.scss +2 -0
  193. package/moj/objects/_filter-layout.scss.map +1 -0
  194. package/moj/objects/_scrollable-pane.scss +2 -0
  195. package/moj/objects/_scrollable-pane.scss.map +1 -0
  196. package/moj/objects/_width-container.scss +2 -0
  197. package/moj/objects/_width-container.scss.map +1 -0
  198. package/moj/settings/_all.scss +2 -0
  199. package/moj/settings/_all.scss.map +1 -0
  200. package/moj/settings/_assets.scss +2 -0
  201. package/moj/settings/_assets.scss.map +1 -0
  202. package/moj/settings/_colours.scss +2 -0
  203. package/moj/settings/_colours.scss.map +1 -0
  204. package/moj/settings/_measurements.scss +2 -0
  205. package/moj/settings/_measurements.scss.map +1 -0
  206. package/moj/settings/_typography.scss +2 -0
  207. package/moj/settings/_typography.scss.map +1 -0
  208. package/moj/template.njk +13 -0
  209. package/moj/utilities/_all.scss +2 -0
  210. package/moj/utilities/_all.scss.map +1 -0
  211. package/moj/utilities/_hidden.scss +2 -0
  212. package/moj/utilities/_hidden.scss.map +1 -0
  213. package/moj/utilities/_width-container.scss +2 -0
  214. package/moj/utilities/_width-container.scss.map +1 -0
  215. package/moj/vendor/govuk-frontend/_base.scss +2 -0
  216. package/moj/vendor/govuk-frontend/_base.scss.map +1 -0
  217. package/moj/vendor/govuk-frontend/_index.scss +2 -0
  218. package/moj/vendor/govuk-frontend/_index.scss.map +1 -0
  219. package/package.json +5 -6
  220. package/moj/all.jquery.min.js +0 -1
  221. package/moj/all.jquery.min.js.map +0 -1
  222. package/moj/all.js +0 -2662
  223. package/moj/all.js.map +0 -1
  224. package/moj/components/add-another/add-another.js +0 -115
  225. package/moj/components/add-another/add-another.js.map +0 -1
  226. package/moj/components/alert/alert.js +0 -356
  227. package/moj/components/alert/alert.js.map +0 -1
  228. package/moj/components/alert/alert.spec.helper.js.map +0 -1
  229. package/moj/components/button-menu/button-menu.js +0 -338
  230. package/moj/components/button-menu/button-menu.js.map +0 -1
  231. package/moj/components/date-picker/date-picker.js +0 -970
  232. package/moj/components/date-picker/date-picker.js.map +0 -1
  233. package/moj/components/filter-toggle-button/filter-toggle-button.js +0 -102
  234. package/moj/components/filter-toggle-button/filter-toggle-button.js.map +0 -1
  235. package/moj/components/form-validator/form-validator.js +0 -205
  236. package/moj/components/form-validator/form-validator.js.map +0 -1
  237. package/moj/components/multi-file-upload/multi-file-upload.js +0 -241
  238. package/moj/components/multi-file-upload/multi-file-upload.js.map +0 -1
  239. package/moj/components/multi-select/multi-select.js +0 -86
  240. package/moj/components/multi-select/multi-select.js.map +0 -1
  241. package/moj/components/password-reveal/password-reveal.js +0 -44
  242. package/moj/components/password-reveal/password-reveal.js.map +0 -1
  243. package/moj/components/rich-text-editor/rich-text-editor.js +0 -166
  244. package/moj/components/rich-text-editor/rich-text-editor.js.map +0 -1
  245. package/moj/components/search-toggle/search-toggle.js +0 -63
  246. package/moj/components/search-toggle/search-toggle.js.map +0 -1
  247. package/moj/components/sortable-table/sortable-table.js +0 -147
  248. package/moj/components/sortable-table/sortable-table.js.map +0 -1
  249. package/moj/helpers.js +0 -200
  250. package/moj/helpers.js.map +0 -1
  251. package/moj/vendor/html5shiv.js +0 -326
  252. package/moj/vendor/jquery.js +0 -9300
  253. package/moj/version.js +0 -12
  254. package/moj/version.js.map +0 -1
  255. package/moj/version.mjs +0 -4
  256. package/moj/version.mjs.map +0 -1
@@ -1,220 +1,391 @@
1
- import { dragAndDropSupported, formDataSupported, fileApiSupported } from '../../helpers.mjs';
2
-
3
- function MultiFileUpload(params) {
4
- if (!(dragAndDropSupported() && formDataSupported() && fileApiSupported())) {
5
- return
6
- }
7
-
8
- this.defaultParams = {
9
- uploadFileEntryHook: $.noop,
10
- uploadFileExitHook: $.noop,
11
- uploadFileErrorHook: $.noop,
12
- fileDeleteHook: $.noop,
13
- uploadStatusText: 'Uploading files, please wait',
14
- dropzoneHintText: 'Drag and drop files here or',
15
- dropzoneButtonText: 'Choose files'
16
- };
17
-
18
- this.params = $.extend({}, this.defaultParams, params);
19
- this.container = $(this.params.container);
20
-
21
- this.container.addClass('moj-multi-file-upload--enhanced');
22
-
23
- this.feedbackContainer = this.container.find(
24
- '.moj-multi-file__uploaded-files'
25
- );
26
- this.setupFileInput();
27
- this.setupDropzone();
28
- this.setupLabel();
29
- this.setupStatusBox();
30
- this.container.on(
31
- 'click',
32
- '.moj-multi-file-upload__delete',
33
- $.proxy(this, 'onFileDeleteClick')
34
- );
35
- }
1
+ import { ConfigurableComponent } from 'govuk-frontend';
2
+
3
+ /* eslint-disable @typescript-eslint/no-empty-function */
4
+
5
+
6
+ /**
7
+ * @augments {ConfigurableComponent<MultiFileUploadConfig>}
8
+ */
9
+ class MultiFileUpload extends ConfigurableComponent {
10
+ /**
11
+ * @param {Element | null} $root - HTML element to use for multi file upload
12
+ * @param {MultiFileUploadConfig} [config] - Multi file upload config
13
+ */
14
+ constructor($root, config = {}) {
15
+ var _this$config$feedback;
16
+ super($root, config);
17
+ if (!MultiFileUpload.isSupported()) {
18
+ return this;
19
+ }
20
+ const $feedbackContainer = (_this$config$feedback = this.config.feedbackContainer.element) != null ? _this$config$feedback : this.$root.querySelector(this.config.feedbackContainer.selector);
21
+ if (!$feedbackContainer || !($feedbackContainer instanceof HTMLElement)) {
22
+ return this;
23
+ }
24
+ this.$feedbackContainer = $feedbackContainer;
25
+ this.setupFileInput();
26
+ this.setupDropzone();
27
+ this.setupLabel();
28
+ this.setupStatusBox();
29
+ this.$root.addEventListener('click', this.onFileDeleteClick.bind(this));
30
+ this.$root.classList.add('moj-multi-file-upload--enhanced');
31
+ }
32
+ setupDropzone() {
33
+ this.$dropzone = document.createElement('div');
34
+ this.$dropzone.classList.add('moj-multi-file-upload__dropzone');
35
+ this.$dropzone.addEventListener('dragover', this.onDragOver.bind(this));
36
+ this.$dropzone.addEventListener('dragleave', this.onDragLeave.bind(this));
37
+ this.$dropzone.addEventListener('drop', this.onDrop.bind(this));
38
+ this.$fileInput.replaceWith(this.$dropzone);
39
+ this.$dropzone.appendChild(this.$fileInput);
40
+ }
41
+ setupLabel() {
42
+ const $label = document.createElement('label');
43
+ $label.setAttribute('for', this.$fileInput.id);
44
+ $label.classList.add('govuk-button', 'govuk-button--secondary');
45
+ $label.textContent = this.config.dropzoneButtonText;
46
+ const $hint = document.createElement('p');
47
+ $hint.classList.add('govuk-body');
48
+ $hint.textContent = this.config.dropzoneHintText;
49
+ this.$label = $label;
50
+ this.$dropzone.append($hint);
51
+ this.$dropzone.append($label);
52
+ }
53
+ setupFileInput() {
54
+ this.$fileInput = /** @type {HTMLInputElement} */
55
+ this.$root.querySelector('.moj-multi-file-upload__input');
56
+ this.$fileInput.addEventListener('change', this.onFileChange.bind(this));
57
+ this.$fileInput.addEventListener('focus', this.onFileFocus.bind(this));
58
+ this.$fileInput.addEventListener('blur', this.onFileBlur.bind(this));
59
+ }
60
+ setupStatusBox() {
61
+ this.$status = document.createElement('div');
62
+ this.$status.classList.add('govuk-visually-hidden');
63
+ this.$status.setAttribute('aria-live', 'polite');
64
+ this.$status.setAttribute('role', 'status');
65
+ this.$dropzone.append(this.$status);
66
+ }
67
+
68
+ /**
69
+ * @param {DragEvent} event - Drag event
70
+ */
71
+ onDragOver(event) {
72
+ event.preventDefault();
73
+ this.$dropzone.classList.add('moj-multi-file-upload--dragover');
74
+ }
75
+ onDragLeave() {
76
+ this.$dropzone.classList.remove('moj-multi-file-upload--dragover');
77
+ }
78
+
79
+ /**
80
+ * @param {DragEvent} event - Drag event
81
+ */
82
+ onDrop(event) {
83
+ event.preventDefault();
84
+ this.$dropzone.classList.remove('moj-multi-file-upload--dragover');
85
+ this.$feedbackContainer.classList.remove('moj-hidden');
86
+ this.$status.textContent = this.config.uploadStatusText;
87
+ this.uploadFiles(event.dataTransfer.files);
88
+ }
89
+
90
+ /**
91
+ * @param {FileList} files - File list
92
+ */
93
+ uploadFiles(files) {
94
+ for (const file of Array.from(files)) {
95
+ this.uploadFile(file);
96
+ }
97
+ }
98
+ onFileChange() {
99
+ this.$feedbackContainer.classList.remove('moj-hidden');
100
+ this.$status.textContent = this.config.uploadStatusText;
101
+ this.uploadFiles(this.$fileInput.files);
102
+ const $fileInput = this.$fileInput.cloneNode(true);
103
+ if (!$fileInput || !($fileInput instanceof HTMLInputElement)) {
104
+ return;
105
+ }
106
+ $fileInput.value = '';
107
+ this.$fileInput.replaceWith($fileInput);
108
+ this.setupFileInput();
109
+ this.$fileInput.focus();
110
+ }
111
+ onFileFocus() {
112
+ this.$label.classList.add('moj-multi-file-upload--focused');
113
+ }
114
+ onFileBlur() {
115
+ this.$label.classList.remove('moj-multi-file-upload--focused');
116
+ }
117
+
118
+ /**
119
+ * @param {UploadResponseSuccess['success']} success
120
+ */
121
+ getSuccessHtml(success) {
122
+ return `<span class="moj-multi-file-upload__success"> <svg class="moj-banner__icon" fill="currentColor" role="presentation" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 25" height="25" width="25"><path d="M25,6.2L8.7,23.2L0,14.1l4-4.2l4.7,4.9L21,2L25,6.2z"/></svg>${success.messageHtml}</span>`;
123
+ }
124
+
125
+ /**
126
+ * @param {UploadResponseError['error']} error
127
+ */
128
+ getErrorHtml(error) {
129
+ return `<span class="moj-multi-file-upload__error"> <svg class="moj-banner__icon" fill="currentColor" role="presentation" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 25" height="25" width="25"><path d="M13.6,15.4h-2.3v-4.5h2.3V15.4z M13.6,19.8h-2.3v-2.2h2.3V19.8z M0,23.2h25L12.5,2L0,23.2z"/></svg>${error.message}</span>`;
130
+ }
131
+
132
+ /**
133
+ * @param {File} file
134
+ */
135
+ getFileRow(file) {
136
+ const $row = document.createElement('div');
137
+ $row.classList.add('govuk-summary-list__row', 'moj-multi-file-upload__row');
138
+ $row.innerHTML = `
139
+ <div class="govuk-summary-list__value moj-multi-file-upload__message">
140
+ <span class="moj-multi-file-upload__filename">${file.name}</span>
141
+ <span class="moj-multi-file-upload__progress">0%</span>
142
+ </div>
143
+ <div class="govuk-summary-list__actions moj-multi-file-upload__actions"></div>
144
+ `;
145
+ return $row;
146
+ }
147
+
148
+ /**
149
+ * @param {UploadResponseFile} file
150
+ */
151
+ getDeleteButton(file) {
152
+ const $button = document.createElement('button');
153
+ $button.setAttribute('type', 'button');
154
+ $button.setAttribute('name', 'delete');
155
+ $button.setAttribute('value', file.filename);
156
+ $button.classList.add('moj-multi-file-upload__delete', 'govuk-button', 'govuk-button--secondary', 'govuk-!-margin-bottom-0');
157
+ $button.innerHTML = `Delete <span class="govuk-visually-hidden">${file.originalname}</span>`;
158
+ return $button;
159
+ }
36
160
 
37
- MultiFileUpload.prototype.setupDropzone = function () {
38
- this.fileInput.wrap('<div class="moj-multi-file-upload__dropzone" />');
39
- this.dropzone = this.container.find('.moj-multi-file-upload__dropzone');
40
- this.dropzone.on('dragover', $.proxy(this, 'onDragOver'));
41
- this.dropzone.on('dragleave', $.proxy(this, 'onDragLeave'));
42
- this.dropzone.on('drop', $.proxy(this, 'onDrop'));
43
- };
44
-
45
- MultiFileUpload.prototype.setupLabel = function () {
46
- this.label = $(
47
- `<label for="${this.fileInput[0].id}" class="govuk-button govuk-button--secondary">${this.params.dropzoneButtonText}</label>`
48
- );
49
- this.dropzone.append(
50
- `<p class="govuk-body">${this.params.dropzoneHintText}</p>`
51
- );
52
- this.dropzone.append(this.label);
53
- };
54
-
55
- MultiFileUpload.prototype.setupFileInput = function () {
56
- this.fileInput = this.container.find('.moj-multi-file-upload__input');
57
- this.fileInput.on('change', $.proxy(this, 'onFileChange'));
58
- this.fileInput.on('focus', $.proxy(this, 'onFileFocus'));
59
- this.fileInput.on('blur', $.proxy(this, 'onFileBlur'));
60
- };
61
-
62
- MultiFileUpload.prototype.setupStatusBox = function () {
63
- this.status = $(
64
- '<div aria-live="polite" role="status" class="govuk-visually-hidden" />'
65
- );
66
- this.dropzone.append(this.status);
67
- };
68
-
69
- MultiFileUpload.prototype.onDragOver = function (e) {
70
- e.preventDefault();
71
- this.dropzone.addClass('moj-multi-file-upload--dragover');
72
- };
73
-
74
- MultiFileUpload.prototype.onDragLeave = function () {
75
- this.dropzone.removeClass('moj-multi-file-upload--dragover');
76
- };
77
-
78
- MultiFileUpload.prototype.onDrop = function (e) {
79
- e.preventDefault();
80
- this.dropzone.removeClass('moj-multi-file-upload--dragover');
81
- this.feedbackContainer.removeClass('moj-hidden');
82
- this.status.html(this.params.uploadStatusText);
83
- this.uploadFiles(e.originalEvent.dataTransfer.files);
84
- };
85
-
86
- MultiFileUpload.prototype.uploadFiles = function (files) {
87
- for (let i = 0; i < files.length; i++) {
88
- this.uploadFile(files[i]);
89
- }
90
- };
91
-
92
- MultiFileUpload.prototype.onFileChange = function (e) {
93
- this.feedbackContainer.removeClass('moj-hidden');
94
- this.status.html(this.params.uploadStatusText);
95
- this.uploadFiles(e.currentTarget.files);
96
- this.fileInput.replaceWith($(e.currentTarget).val('').clone(true));
97
- this.setupFileInput();
98
- this.fileInput.get(0).focus();
99
- };
100
-
101
- MultiFileUpload.prototype.onFileFocus = function (e) {
102
- this.label.addClass('moj-multi-file-upload--focused');
103
- };
104
-
105
- MultiFileUpload.prototype.onFileBlur = function (e) {
106
- this.label.removeClass('moj-multi-file-upload--focused');
107
- };
108
-
109
- MultiFileUpload.prototype.getSuccessHtml = function (success) {
110
- return `<span class="moj-multi-file-upload__success"> <svg class="moj-banner__icon" fill="currentColor" role="presentation" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 25" height="25" width="25"><path d="M25,6.2L8.7,23.2L0,14.1l4-4.2l4.7,4.9L21,2L25,6.2z"/></svg>${success.messageHtml}</span>`
111
- };
112
-
113
- MultiFileUpload.prototype.getErrorHtml = function (error) {
114
- return `<span class="moj-multi-file-upload__error"> <svg class="moj-banner__icon" fill="currentColor" role="presentation" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 25" height="25" width="25"><path d="M13.6,15.4h-2.3v-4.5h2.3V15.4z M13.6,19.8h-2.3v-2.2h2.3V19.8z M0,23.2h25L12.5,2L0,23.2z"/></svg>${error.message}</span>`
115
- };
116
-
117
- MultiFileUpload.prototype.getFileRowHtml = function (file) {
118
- const html = `
119
- <div class="govuk-summary-list__row moj-multi-file-upload__row">
120
- <div class="govuk-summary-list__value moj-multi-file-upload__message">
121
- <span class="moj-multi-file-upload__filename">${file.name}</span>
122
- <span class="moj-multi-file-upload__progress">0%</span>
123
- </div>
124
- <div class="govuk-summary-list__actions moj-multi-file-upload__actions"></div>
125
- </div>`;
126
- return html
127
- };
128
-
129
- MultiFileUpload.prototype.getDeleteButtonHtml = function (file) {
130
- return `<button class="moj-multi-file-upload__delete govuk-button govuk-button--secondary govuk-!-margin-bottom-0" type="button" name="delete" value="${file.filename}">
131
- Delete <span class="govuk-visually-hidden">${file.originalname}</span>
132
- </button>`
133
- };
134
-
135
- MultiFileUpload.prototype.uploadFile = function (file) {
136
- this.params.uploadFileEntryHook(this, file);
137
- const item = $(this.getFileRowHtml(file));
138
- const formData = new FormData();
139
- formData.append('documents', file);
140
- this.feedbackContainer.find('.moj-multi-file-upload__list').append(item);
141
-
142
- $.ajax({
143
- url: this.params.uploadUrl,
144
- type: 'post',
145
- data: formData,
146
- processData: false,
147
- contentType: false,
148
- success: $.proxy(function (response) {
149
- if (response.error) {
150
- item
151
- .find('.moj-multi-file-upload__message')
152
- .html(this.getErrorHtml(response.error));
153
- this.status.html(response.error.message);
154
- } else {
155
- item
156
- .find('.moj-multi-file-upload__message')
157
- .html(this.getSuccessHtml(response.success));
158
- this.status.html(response.success.messageText);
161
+ /**
162
+ * @param {File} file
163
+ */
164
+ uploadFile(file) {
165
+ this.config.hooks.entryHook(this, file);
166
+ const $item = this.getFileRow(file);
167
+ const $message = $item.querySelector('.moj-multi-file-upload__message');
168
+ const $actions = $item.querySelector('.moj-multi-file-upload__actions');
169
+ const $progress = $item.querySelector('.moj-multi-file-upload__progress');
170
+ const formData = new FormData();
171
+ formData.append('documents', file);
172
+ this.$feedbackContainer.querySelector('.moj-multi-file-upload__list').append($item);
173
+ const xhr = new XMLHttpRequest();
174
+ const onLoad = () => {
175
+ if (xhr.status < 200 || xhr.status >= 300 || !('success' in xhr.response)) {
176
+ return onError();
177
+ }
178
+ $message.innerHTML = this.getSuccessHtml(xhr.response.success);
179
+ this.$status.textContent = xhr.response.success.messageText;
180
+ $actions.append(this.getDeleteButton(xhr.response.file));
181
+ this.config.hooks.exitHook(this, file, xhr, xhr.responseText);
182
+ };
183
+ const onError = () => {
184
+ const error = new Error(xhr.response && 'error' in xhr.response ? xhr.response.error.message : xhr.statusText || 'Upload failed');
185
+ $message.innerHTML = this.getErrorHtml(error);
186
+ this.$status.textContent = error.message;
187
+ this.config.hooks.errorHook(this, file, xhr, xhr.responseText, error);
188
+ };
189
+ xhr.addEventListener('load', onLoad);
190
+ xhr.addEventListener('error', onError);
191
+ xhr.upload.addEventListener('progress', event => {
192
+ if (!event.lengthComputable) {
193
+ return;
159
194
  }
160
- item
161
- .find('.moj-multi-file-upload__actions')
162
- .append(this.getDeleteButtonHtml(response.file));
163
- this.params.uploadFileExitHook(this, file, response);
164
- }, this),
165
- error: $.proxy(function (jqXHR, textStatus, errorThrown) {
166
- this.params.uploadFileErrorHook(
167
- this,
168
- file,
169
- jqXHR,
170
- textStatus,
171
- errorThrown
172
- );
173
- }, this),
174
- xhr: function () {
175
- const xhr = new XMLHttpRequest();
176
- xhr.upload.addEventListener(
177
- 'progress',
178
- function (e) {
179
- if (e.lengthComputable) {
180
- let percentComplete = e.loaded / e.total;
181
- percentComplete = parseInt(percentComplete * 100, 10);
182
- item
183
- .find('.moj-multi-file-upload__progress')
184
- .text(` ${percentComplete}%`);
185
- }
186
- },
187
- false
188
- );
189
- return xhr
195
+ const percentComplete = Math.round(event.loaded / event.total * 100);
196
+ $progress.textContent = ` ${percentComplete}%`;
197
+ });
198
+ xhr.open('POST', this.config.uploadUrl);
199
+ xhr.responseType = 'json';
200
+ xhr.send(formData);
201
+ }
202
+
203
+ /**
204
+ * @param {MouseEvent} event - Click event
205
+ */
206
+ onFileDeleteClick(event) {
207
+ const $button = event.target;
208
+ if (!$button || !($button instanceof HTMLButtonElement) || !$button.classList.contains('moj-multi-file-upload__delete')) {
209
+ return;
190
210
  }
191
- });
192
- };
193
-
194
- MultiFileUpload.prototype.onFileDeleteClick = function (e) {
195
- e.preventDefault(); // if user refreshes page and then deletes
196
- const button = $(e.currentTarget);
197
- const data = {};
198
- data[button[0].name] = button[0].value;
199
- $.ajax({
200
- url: this.params.deleteUrl,
201
- type: 'post',
202
- dataType: 'json',
203
- data,
204
- success: $.proxy(function (response) {
205
- if (response.error) ; else {
206
- button.parents('.moj-multi-file-upload__row').remove();
207
- if (
208
- this.feedbackContainer.find('.moj-multi-file-upload__row').length ===
209
- 0
210
- ) {
211
- this.feedbackContainer.addClass('moj-hidden');
212
- }
211
+ event.preventDefault(); // if user refreshes page and then deletes
212
+
213
+ const xhr = new XMLHttpRequest();
214
+ xhr.addEventListener('load', () => {
215
+ if (xhr.status < 200 || xhr.status >= 300) {
216
+ return;
213
217
  }
214
- this.params.fileDeleteHook(this, response);
215
- }, this)
216
- });
217
- };
218
+ const $rows = Array.from(this.$feedbackContainer.querySelectorAll('.moj-multi-file-upload__row'));
219
+ if ($rows.length === 1) {
220
+ this.$feedbackContainer.classList.add('moj-hidden');
221
+ }
222
+ const $rowDelete = $rows.find($row => $row.contains($button));
223
+ if ($rowDelete) $rowDelete.remove();
224
+ this.config.hooks.deleteHook(this, undefined, xhr, xhr.responseText);
225
+ });
226
+ xhr.open('POST', this.config.deleteUrl);
227
+ xhr.setRequestHeader('Content-Type', 'application/json');
228
+ xhr.responseType = 'json';
229
+ xhr.send(JSON.stringify({
230
+ [$button.name]: $button.value
231
+ }));
232
+ }
233
+ static isSupported() {
234
+ return this.isDragAndDropSupported() && this.isFormDataSupported() && this.isFileApiSupported();
235
+ }
236
+ static isDragAndDropSupported() {
237
+ const div = document.createElement('div');
238
+ return typeof div.ondrop !== 'undefined';
239
+ }
240
+ static isFormDataSupported() {
241
+ return typeof FormData === 'function';
242
+ }
243
+ static isFileApiSupported() {
244
+ const input = document.createElement('input');
245
+ input.type = 'file';
246
+ return typeof input.files !== 'undefined';
247
+ }
248
+
249
+ /**
250
+ * Name for the component used when initialising using data-module attributes.
251
+ */
252
+ }
253
+
254
+ /**
255
+ * Multi file upload config
256
+ *
257
+ * @typedef {object} MultiFileUploadConfig
258
+ * @property {string} [uploadUrl] - File upload URL
259
+ * @property {string} [deleteUrl] - File delete URL
260
+ * @property {string} [uploadStatusText] - Upload status text
261
+ * @property {string} [dropzoneHintText] - Dropzone hint text
262
+ * @property {string} [dropzoneButtonText] - Dropzone button text
263
+ * @property {object} [feedbackContainer] - Feedback container config
264
+ * @property {string} [feedbackContainer.selector] - Selector for feedback container
265
+ * @property {Element | null} [feedbackContainer.element] - HTML element for feedback container
266
+ * @property {MultiFileUploadHooks} [hooks] - Upload hooks
267
+ */
268
+
269
+ /**
270
+ * Multi file upload hooks
271
+ *
272
+ * @typedef {object} MultiFileUploadHooks
273
+ * @property {OnUploadFileEntryHook} [entryHook] - File upload entry hook
274
+ * @property {OnUploadFileExitHook} [exitHook] - File upload exit hook
275
+ * @property {OnUploadFileErrorHook} [errorHook] - File upload error hook
276
+ * @property {OnUploadFileDeleteHook} [deleteHook] - File delete hook
277
+ */
278
+
279
+ /**
280
+ * Upload hook: File entry
281
+ *
282
+ * @callback OnUploadFileEntryHook
283
+ * @param {InstanceType<typeof MultiFileUpload>} upload - Multi file upload
284
+ * @param {File} file - File upload
285
+ */
286
+
287
+ /**
288
+ * Upload hook: File exit
289
+ *
290
+ * @callback OnUploadFileExitHook
291
+ * @param {InstanceType<typeof MultiFileUpload>} upload - Multi file upload
292
+ * @param {File} file - File upload
293
+ * @param {XMLHttpRequest} xhr - XMLHttpRequest
294
+ * @param {string} textStatus - Text status
295
+ */
296
+
297
+ /**
298
+ * Upload hook: File error
299
+ *
300
+ * @callback OnUploadFileErrorHook
301
+ * @param {InstanceType<typeof MultiFileUpload>} upload - Multi file upload
302
+ * @param {File} file - File upload
303
+ * @param {XMLHttpRequest} xhr - XMLHttpRequest
304
+ * @param {string} textStatus - Text status
305
+ * @param {Error} errorThrown - Error thrown
306
+ */
307
+
308
+ /**
309
+ * Upload hook: File delete
310
+ *
311
+ * @callback OnUploadFileDeleteHook
312
+ * @param {InstanceType<typeof MultiFileUpload>} upload - Multi file upload
313
+ * @param {File} [file] - File upload
314
+ * @param {XMLHttpRequest} xhr - XMLHttpRequest
315
+ * @param {string} textStatus - Text status
316
+ */
317
+
318
+ /**
319
+ * @typedef {object} UploadResponseSuccess
320
+ * @property {{ messageText: string, messageHtml: string }} success - Response success
321
+ * @property {UploadResponseFile} file - Response file
322
+ */
323
+
324
+ /**
325
+ * @typedef {object} UploadResponseError
326
+ * @property {{ message: string }} error - Response error
327
+ * @property {UploadResponseFile} file - Response file
328
+ */
329
+
330
+ /**
331
+ * @typedef {object} UploadResponseFile
332
+ * @property {string} filename - File name
333
+ * @property {string} originalname - Original file name
334
+ */
335
+
336
+ /**
337
+ * @import { Schema } from 'govuk-frontend/dist/govuk/common/configuration.mjs'
338
+ */
339
+ MultiFileUpload.moduleName = 'moj-multi-file-upload';
340
+ /**
341
+ * Multi file upload default config
342
+ *
343
+ * @type {MultiFileUploadConfig}
344
+ */
345
+ MultiFileUpload.defaults = Object.freeze({
346
+ uploadStatusText: 'Uploading files, please wait',
347
+ dropzoneHintText: 'Drag and drop files here or',
348
+ dropzoneButtonText: 'Choose files',
349
+ feedbackContainer: {
350
+ selector: '.moj-multi-file__uploaded-files'
351
+ },
352
+ hooks: {
353
+ entryHook: () => {},
354
+ exitHook: () => {},
355
+ errorHook: () => {},
356
+ deleteHook: () => {}
357
+ }
358
+ });
359
+ /**
360
+ * Multi file upload config schema
361
+ *
362
+ * @satisfies {Schema<MultiFileUploadConfig>}
363
+ */
364
+ MultiFileUpload.schema = Object.freeze(/** @type {const} */{
365
+ properties: {
366
+ uploadUrl: {
367
+ type: 'string'
368
+ },
369
+ deleteUrl: {
370
+ type: 'string'
371
+ },
372
+ uploadStatusText: {
373
+ type: 'string'
374
+ },
375
+ dropzoneHintText: {
376
+ type: 'string'
377
+ },
378
+ dropzoneButtonText: {
379
+ type: 'string'
380
+ },
381
+ feedbackContainer: {
382
+ type: 'object'
383
+ },
384
+ hooks: {
385
+ type: 'object'
386
+ }
387
+ }
388
+ });
218
389
 
219
390
  export { MultiFileUpload };
220
391
  //# sourceMappingURL=multi-file-upload.mjs.map