@ministryofjustice/frontend 4.0.0 → 5.0.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 (245) 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 +2523 -0
  5. package/moj/all.bundle.js.map +1 -0
  6. package/moj/all.bundle.mjs +2502 -0
  7. package/moj/all.bundle.mjs.map +1 -0
  8. package/moj/all.mjs +59 -69
  9. package/moj/all.mjs.map +1 -1
  10. package/moj/all.scss +2 -0
  11. package/moj/all.scss.map +1 -0
  12. package/moj/components/_all.scss +2 -0
  13. package/moj/components/_all.scss.map +1 -0
  14. package/moj/components/action-bar/_action-bar.scss +2 -0
  15. package/moj/components/action-bar/_action-bar.scss.map +1 -0
  16. package/moj/components/add-another/_add-another.scss +2 -0
  17. package/moj/components/add-another/_add-another.scss.map +1 -0
  18. package/moj/components/add-another/add-another.bundle.js +128 -0
  19. package/moj/components/add-another/add-another.bundle.js.map +1 -0
  20. package/moj/components/add-another/add-another.bundle.mjs +120 -0
  21. package/moj/components/add-another/add-another.bundle.mjs.map +1 -0
  22. package/moj/components/add-another/add-another.mjs +112 -99
  23. package/moj/components/add-another/add-another.mjs.map +1 -1
  24. package/moj/components/alert/_alert.scss +4 -0
  25. package/moj/components/alert/_alert.scss.map +1 -0
  26. package/moj/components/alert/alert.bundle.js +330 -0
  27. package/moj/components/alert/alert.bundle.js.map +1 -0
  28. package/moj/components/alert/alert.bundle.mjs +322 -0
  29. package/moj/components/alert/alert.bundle.mjs.map +1 -0
  30. package/moj/components/alert/alert.mjs +181 -217
  31. package/moj/components/alert/alert.mjs.map +1 -1
  32. package/moj/components/alert/{alert.spec.helper.js → alert.spec.helper.bundle.js} +1 -1
  33. package/moj/components/alert/alert.spec.helper.bundle.js.map +1 -0
  34. package/moj/components/alert/alert.spec.helper.bundle.mjs +67 -0
  35. package/moj/components/alert/alert.spec.helper.bundle.mjs.map +1 -0
  36. package/moj/components/alert/alert.spec.helper.mjs.map +1 -1
  37. package/moj/components/badge/_badge.scss +2 -0
  38. package/moj/components/badge/_badge.scss.map +1 -0
  39. package/moj/components/banner/_banner.scss +2 -0
  40. package/moj/components/banner/_banner.scss.map +1 -0
  41. package/moj/components/button-menu/README.md +10 -6
  42. package/moj/components/button-menu/_button-menu.scss +10 -3
  43. package/moj/components/button-menu/_button-menu.scss.map +1 -0
  44. package/moj/components/button-menu/button-menu.bundle.js +299 -0
  45. package/moj/components/button-menu/button-menu.bundle.js.map +1 -0
  46. package/moj/components/button-menu/{button-menu.js → button-menu.bundle.mjs} +74 -121
  47. package/moj/components/button-menu/button-menu.bundle.mjs.map +1 -0
  48. package/moj/components/button-menu/button-menu.mjs +246 -285
  49. package/moj/components/button-menu/button-menu.mjs.map +1 -1
  50. package/moj/components/cookie-banner/_cookie-banner.scss +2 -0
  51. package/moj/components/cookie-banner/_cookie-banner.scss.map +1 -0
  52. package/moj/components/currency-input/_currency-input.scss +2 -0
  53. package/moj/components/currency-input/_currency-input.scss.map +1 -0
  54. package/moj/components/date-picker/_date-picker.scss +2 -0
  55. package/moj/components/date-picker/_date-picker.scss.map +1 -0
  56. package/moj/components/date-picker/date-picker.bundle.js +784 -0
  57. package/moj/components/date-picker/date-picker.bundle.js.map +1 -0
  58. package/moj/components/date-picker/{date-picker.js → date-picker.bundle.mjs} +245 -439
  59. package/moj/components/date-picker/date-picker.bundle.mjs.map +1 -0
  60. package/moj/components/date-picker/date-picker.mjs +654 -840
  61. package/moj/components/date-picker/date-picker.mjs.map +1 -1
  62. package/moj/components/filter/_filter.scss +2 -0
  63. package/moj/components/filter/_filter.scss.map +1 -0
  64. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js +96 -0
  65. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js.map +1 -0
  66. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs +88 -0
  67. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs.map +1 -0
  68. package/moj/components/filter-toggle-button/filter-toggle-button.mjs +78 -84
  69. package/moj/components/filter-toggle-button/filter-toggle-button.mjs.map +1 -1
  70. package/moj/components/form-validator/form-validator.bundle.js +198 -0
  71. package/moj/components/form-validator/form-validator.bundle.js.map +1 -0
  72. package/moj/components/form-validator/form-validator.bundle.mjs +190 -0
  73. package/moj/components/form-validator/form-validator.bundle.mjs.map +1 -0
  74. package/moj/components/form-validator/form-validator.mjs +149 -152
  75. package/moj/components/form-validator/form-validator.mjs.map +1 -1
  76. package/moj/components/header/_header.scss +2 -0
  77. package/moj/components/header/_header.scss.map +1 -0
  78. package/moj/components/identity-bar/_identity-bar.scss +2 -0
  79. package/moj/components/identity-bar/_identity-bar.scss.map +1 -0
  80. package/moj/components/interruption-card/_interruption-card.scss +2 -0
  81. package/moj/components/interruption-card/_interruption-card.scss.map +1 -0
  82. package/moj/components/messages/_messages.scss +2 -0
  83. package/moj/components/messages/_messages.scss.map +1 -0
  84. package/moj/components/multi-file-upload/_multi-file-upload.scss +2 -0
  85. package/moj/components/multi-file-upload/_multi-file-upload.scss.map +1 -0
  86. package/moj/components/multi-file-upload/multi-file-upload.bundle.js +223 -0
  87. package/moj/components/multi-file-upload/multi-file-upload.bundle.js.map +1 -0
  88. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs +215 -0
  89. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs.map +1 -0
  90. package/moj/components/multi-file-upload/multi-file-upload.mjs +193 -209
  91. package/moj/components/multi-file-upload/multi-file-upload.mjs.map +1 -1
  92. package/moj/components/multi-select/_multi-select.scss +2 -0
  93. package/moj/components/multi-select/_multi-select.scss.map +1 -0
  94. package/moj/components/multi-select/multi-select.bundle.js +78 -0
  95. package/moj/components/multi-select/multi-select.bundle.js.map +1 -0
  96. package/moj/components/multi-select/multi-select.bundle.mjs +70 -0
  97. package/moj/components/multi-select/multi-select.bundle.mjs.map +1 -0
  98. package/moj/components/multi-select/multi-select.mjs +59 -67
  99. package/moj/components/multi-select/multi-select.mjs.map +1 -1
  100. package/moj/components/notification-badge/_notification-badge.scss +2 -0
  101. package/moj/components/notification-badge/_notification-badge.scss.map +1 -0
  102. package/moj/components/organisation-switcher/_organisation-switcher.scss +2 -0
  103. package/moj/components/organisation-switcher/_organisation-switcher.scss.map +1 -0
  104. package/moj/components/page-header-actions/_page-header-actions.scss +2 -0
  105. package/moj/components/page-header-actions/_page-header-actions.scss.map +1 -0
  106. package/moj/components/page-header-actions/template.njk +1 -1
  107. package/moj/components/pagination/_pagination.scss +2 -2
  108. package/moj/components/pagination/_pagination.scss.map +1 -0
  109. package/moj/components/password-reveal/_password-reveal.scss +2 -0
  110. package/moj/components/password-reveal/_password-reveal.scss.map +1 -0
  111. package/moj/components/password-reveal/password-reveal.bundle.js +49 -0
  112. package/moj/components/password-reveal/password-reveal.bundle.js.map +1 -0
  113. package/moj/components/password-reveal/password-reveal.bundle.mjs +41 -0
  114. package/moj/components/password-reveal/password-reveal.bundle.mjs.map +1 -0
  115. package/moj/components/password-reveal/password-reveal.mjs +36 -31
  116. package/moj/components/password-reveal/password-reveal.mjs.map +1 -1
  117. package/moj/components/primary-navigation/_primary-navigation.scss +2 -0
  118. package/moj/components/primary-navigation/_primary-navigation.scss.map +1 -0
  119. package/moj/components/progress-bar/_progress-bar.scss +2 -0
  120. package/moj/components/progress-bar/_progress-bar.scss.map +1 -0
  121. package/moj/components/rich-text-editor/README.md +15 -9
  122. package/moj/components/rich-text-editor/_rich-text-editor.scss +2 -0
  123. package/moj/components/rich-text-editor/_rich-text-editor.scss.map +1 -0
  124. package/moj/components/rich-text-editor/rich-text-editor.bundle.js +145 -0
  125. package/moj/components/rich-text-editor/rich-text-editor.bundle.js.map +1 -0
  126. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs +137 -0
  127. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs.map +1 -0
  128. package/moj/components/rich-text-editor/rich-text-editor.mjs +124 -145
  129. package/moj/components/rich-text-editor/rich-text-editor.mjs.map +1 -1
  130. package/moj/components/search/_search.scss +2 -0
  131. package/moj/components/search/_search.scss.map +1 -0
  132. package/moj/components/search-toggle/{search-toggle.scss → _search-toggle.scss} +2 -0
  133. package/moj/components/search-toggle/_search-toggle.scss.map +1 -0
  134. package/moj/components/search-toggle/search-toggle.bundle.js +54 -0
  135. package/moj/components/search-toggle/search-toggle.bundle.js.map +1 -0
  136. package/moj/components/search-toggle/search-toggle.bundle.mjs +46 -0
  137. package/moj/components/search-toggle/search-toggle.bundle.mjs.map +1 -0
  138. package/moj/components/search-toggle/search-toggle.mjs +40 -49
  139. package/moj/components/search-toggle/search-toggle.mjs.map +1 -1
  140. package/moj/components/side-navigation/_side-navigation.scss +2 -0
  141. package/moj/components/side-navigation/_side-navigation.scss.map +1 -0
  142. package/moj/components/sortable-table/_sortable-table.scss +2 -2
  143. package/moj/components/sortable-table/_sortable-table.scss.map +1 -0
  144. package/moj/components/sortable-table/sortable-table.bundle.js +134 -0
  145. package/moj/components/sortable-table/sortable-table.bundle.js.map +1 -0
  146. package/moj/components/sortable-table/sortable-table.bundle.mjs +126 -0
  147. package/moj/components/sortable-table/sortable-table.bundle.mjs.map +1 -0
  148. package/moj/components/sortable-table/sortable-table.mjs +117 -130
  149. package/moj/components/sortable-table/sortable-table.mjs.map +1 -1
  150. package/moj/components/sub-navigation/_sub-navigation.scss +2 -0
  151. package/moj/components/sub-navigation/_sub-navigation.scss.map +1 -0
  152. package/moj/components/tag/_tag.scss +2 -0
  153. package/moj/components/tag/_tag.scss.map +1 -0
  154. package/moj/components/task-list/_task-list.scss +2 -0
  155. package/moj/components/task-list/_task-list.scss.map +1 -0
  156. package/moj/components/ticket-panel/_ticket-panel.scss +2 -0
  157. package/moj/components/ticket-panel/_ticket-panel.scss.map +1 -0
  158. package/moj/components/timeline/_timeline.scss +2 -0
  159. package/moj/components/timeline/_timeline.scss.map +1 -0
  160. package/moj/filters/all.js +44 -22
  161. package/moj/helpers/_all.scss +2 -0
  162. package/moj/helpers/_all.scss.map +1 -0
  163. package/moj/helpers/_hidden.scss +2 -0
  164. package/moj/helpers/_hidden.scss.map +1 -0
  165. package/moj/helpers/_links.scss +2 -0
  166. package/moj/helpers/_links.scss.map +1 -0
  167. package/moj/{helpers.js → helpers.bundle.js} +37 -42
  168. package/moj/helpers.bundle.js.map +1 -0
  169. package/moj/helpers.bundle.mjs +179 -0
  170. package/moj/helpers.bundle.mjs.map +1 -0
  171. package/moj/helpers.mjs +52 -28
  172. package/moj/helpers.mjs.map +1 -1
  173. package/moj/init.js +11 -2
  174. package/moj/moj-frontend.min.css +1 -1
  175. package/moj/moj-frontend.min.css.map +1 -1
  176. package/moj/moj-frontend.min.js +1 -1
  177. package/moj/moj-frontend.min.js.map +1 -1
  178. package/moj/objects/_all.scss +2 -0
  179. package/moj/objects/_all.scss.map +1 -0
  180. package/moj/objects/_button-group.scss +17 -1
  181. package/moj/objects/_button-group.scss.map +1 -0
  182. package/moj/objects/_filter-layout.scss +2 -0
  183. package/moj/objects/_filter-layout.scss.map +1 -0
  184. package/moj/objects/_scrollable-pane.scss +2 -0
  185. package/moj/objects/_scrollable-pane.scss.map +1 -0
  186. package/moj/objects/_width-container.scss +2 -0
  187. package/moj/objects/_width-container.scss.map +1 -0
  188. package/moj/settings/_all.scss +2 -0
  189. package/moj/settings/_all.scss.map +1 -0
  190. package/moj/settings/_assets.scss +2 -0
  191. package/moj/settings/_assets.scss.map +1 -0
  192. package/moj/settings/_colours.scss +2 -0
  193. package/moj/settings/_colours.scss.map +1 -0
  194. package/moj/settings/_measurements.scss +2 -0
  195. package/moj/settings/_measurements.scss.map +1 -0
  196. package/moj/settings/_typography.scss +2 -0
  197. package/moj/settings/_typography.scss.map +1 -0
  198. package/moj/template.njk +13 -0
  199. package/moj/utilities/_all.scss +2 -0
  200. package/moj/utilities/_all.scss.map +1 -0
  201. package/moj/utilities/_hidden.scss +2 -0
  202. package/moj/utilities/_hidden.scss.map +1 -0
  203. package/moj/utilities/_width-container.scss +2 -0
  204. package/moj/utilities/_width-container.scss.map +1 -0
  205. package/moj/vendor/govuk-frontend/_base.scss +2 -0
  206. package/moj/vendor/govuk-frontend/_base.scss.map +1 -0
  207. package/moj/vendor/govuk-frontend/_index.scss +2 -0
  208. package/moj/vendor/govuk-frontend/_index.scss.map +1 -0
  209. package/moj/{version.js → version.bundle.js} +1 -1
  210. package/moj/version.bundle.js.map +1 -0
  211. package/moj/version.bundle.mjs +4 -0
  212. package/moj/version.bundle.mjs.map +1 -0
  213. package/moj/version.mjs.map +1 -1
  214. package/package.json +5 -6
  215. package/moj/all.jquery.min.js +0 -1
  216. package/moj/all.jquery.min.js.map +0 -1
  217. package/moj/all.js +0 -2662
  218. package/moj/all.js.map +0 -1
  219. package/moj/components/add-another/add-another.js +0 -115
  220. package/moj/components/add-another/add-another.js.map +0 -1
  221. package/moj/components/alert/alert.js +0 -356
  222. package/moj/components/alert/alert.js.map +0 -1
  223. package/moj/components/alert/alert.spec.helper.js.map +0 -1
  224. package/moj/components/button-menu/button-menu.js.map +0 -1
  225. package/moj/components/date-picker/date-picker.js.map +0 -1
  226. package/moj/components/filter-toggle-button/filter-toggle-button.js +0 -102
  227. package/moj/components/filter-toggle-button/filter-toggle-button.js.map +0 -1
  228. package/moj/components/form-validator/form-validator.js +0 -205
  229. package/moj/components/form-validator/form-validator.js.map +0 -1
  230. package/moj/components/multi-file-upload/multi-file-upload.js +0 -241
  231. package/moj/components/multi-file-upload/multi-file-upload.js.map +0 -1
  232. package/moj/components/multi-select/multi-select.js +0 -86
  233. package/moj/components/multi-select/multi-select.js.map +0 -1
  234. package/moj/components/password-reveal/password-reveal.js +0 -44
  235. package/moj/components/password-reveal/password-reveal.js.map +0 -1
  236. package/moj/components/rich-text-editor/rich-text-editor.js +0 -166
  237. package/moj/components/rich-text-editor/rich-text-editor.js.map +0 -1
  238. package/moj/components/search-toggle/search-toggle.js +0 -63
  239. package/moj/components/search-toggle/search-toggle.js.map +0 -1
  240. package/moj/components/sortable-table/sortable-table.js +0 -147
  241. package/moj/components/sortable-table/sortable-table.js.map +0 -1
  242. package/moj/helpers.js.map +0 -1
  243. package/moj/vendor/html5shiv.js +0 -326
  244. package/moj/vendor/jquery.js +0 -9300
  245. package/moj/version.js.map +0 -1
@@ -1,107 +1,120 @@
1
- function AddAnother(container) {
2
- this.container = $(container);
3
-
4
- if (this.container.data('moj-add-another-initialised')) {
5
- return
1
+ class AddAnother {
2
+ constructor(container) {
3
+ this.container = container;
4
+ if (this.container.hasAttribute('data-moj-add-another-init')) {
5
+ return this;
6
+ }
7
+ this.container.setAttribute('data-moj-add-another-init', '');
8
+ this.container.addEventListener('click', this.onRemoveButtonClick.bind(this));
9
+ this.container.addEventListener('click', this.onAddButtonClick.bind(this));
10
+ const buttons = this.container.querySelectorAll('.moj-add-another__add-button, moj-add-another__remove-button');
11
+ buttons.forEach(button => {
12
+ if (!(button instanceof HTMLButtonElement)) {
13
+ return;
14
+ }
15
+ button.type = 'button';
16
+ });
6
17
  }
7
-
8
- this.container.data('moj-add-another-initialised', true);
9
-
10
- this.container.on(
11
- 'click',
12
- '.moj-add-another__remove-button',
13
- $.proxy(this, 'onRemoveButtonClick')
14
- );
15
- this.container.on(
16
- 'click',
17
- '.moj-add-another__add-button',
18
- $.proxy(this, 'onAddButtonClick')
19
- );
20
- this.container
21
- .find('.moj-add-another__add-button, moj-add-another__remove-button')
22
- .prop('type', 'button');
23
- }
24
-
25
- AddAnother.prototype.onAddButtonClick = function (e) {
26
- const item = this.getNewItem();
27
- this.updateAttributes(this.getItems().length, item);
28
- this.resetItem(item);
29
- const firstItem = this.getItems().first();
30
- if (!this.hasRemoveButton(firstItem)) {
31
- this.createRemoveButton(firstItem);
18
+ onAddButtonClick(event) {
19
+ const button = event.target;
20
+ if (!button || !(button instanceof HTMLButtonElement) || !button.classList.contains('moj-add-another__add-button')) {
21
+ return;
22
+ }
23
+ const items = this.getItems();
24
+ const item = this.getNewItem();
25
+ if (!item || !(item instanceof HTMLElement)) {
26
+ return;
27
+ }
28
+ this.updateAttributes(item, items.length);
29
+ this.resetItem(item);
30
+ const firstItem = items[0];
31
+ if (!this.hasRemoveButton(firstItem)) {
32
+ this.createRemoveButton(firstItem);
33
+ }
34
+ items[items.length - 1].after(item);
35
+ const input = item.querySelector('input, textarea, select');
36
+ if (input && input instanceof HTMLInputElement) {
37
+ input.focus();
38
+ }
32
39
  }
33
- this.getItems().last().after(item);
34
- item.find('input, textarea, select').first().focus();
35
- };
36
-
37
- AddAnother.prototype.hasRemoveButton = function (item) {
38
- return item.find('.moj-add-another__remove-button').length
39
- };
40
-
41
- AddAnother.prototype.getItems = function () {
42
- return this.container.find('.moj-add-another__item')
43
- };
44
-
45
- AddAnother.prototype.getNewItem = function () {
46
- const item = this.getItems().first().clone();
47
- if (!this.hasRemoveButton(item)) {
48
- this.createRemoveButton(item);
40
+ hasRemoveButton(item) {
41
+ return item.querySelectorAll('.moj-add-another__remove-button').length;
49
42
  }
50
- return item
51
- };
52
-
53
- AddAnother.prototype.updateAttributes = function (index, item) {
54
- item.find('[data-name]').each(function (i, el) {
55
- const originalId = el.id;
56
-
57
- el.name = $(el)
58
- .attr('data-name')
59
- .replace(/%index%/, index);
60
- el.id = $(el)
61
- .attr('data-id')
62
- .replace(/%index%/, index);
63
-
64
- const label =
65
- $(el).siblings('label')[0] ||
66
- $(el).parents('label')[0] ||
67
- item.find(`[for="${originalId}"]`)[0];
68
- label.htmlFor = el.id;
69
- });
70
- };
71
-
72
- AddAnother.prototype.createRemoveButton = function (item) {
73
- item.append(
74
- '<button type="button" class="govuk-button govuk-button--secondary moj-add-another__remove-button">Remove</button>'
75
- );
76
- };
77
-
78
- AddAnother.prototype.resetItem = function (item) {
79
- item.find('[data-name], [data-id]').each(function (index, el) {
80
- if (el.type === 'checkbox' || el.type === 'radio') {
81
- el.checked = false;
82
- } else {
83
- el.value = '';
43
+ getItems() {
44
+ if (!this.container) {
45
+ return [];
84
46
  }
85
- });
86
- };
87
-
88
- AddAnother.prototype.onRemoveButtonClick = function (e) {
89
- $(e.currentTarget).parents('.moj-add-another__item').remove();
90
- const items = this.getItems();
91
- if (items.length === 1) {
92
- items.find('.moj-add-another__remove-button').remove();
47
+ const items = Array.from(this.container.querySelectorAll('.moj-add-another__item'));
48
+ return items.filter(item => item instanceof HTMLElement);
93
49
  }
94
- items.each(
95
- $.proxy(function (index, el) {
96
- this.updateAttributes(index, $(el));
97
- }, this)
98
- );
99
- this.focusHeading();
100
- };
101
-
102
- AddAnother.prototype.focusHeading = function () {
103
- this.container.find('.moj-add-another__heading').get(0).focus();
104
- };
50
+ getNewItem() {
51
+ const items = this.getItems();
52
+ const item = items[0].cloneNode(true);
53
+ if (!item || !(item instanceof HTMLElement)) {
54
+ return;
55
+ }
56
+ if (!this.hasRemoveButton(item)) {
57
+ this.createRemoveButton(item);
58
+ }
59
+ return item;
60
+ }
61
+ updateAttributes(item, index) {
62
+ item.querySelectorAll('[data-name]').forEach(el => {
63
+ if (!(el instanceof HTMLInputElement)) {
64
+ return;
65
+ }
66
+ const name = el.getAttribute('data-name') || '';
67
+ const id = el.getAttribute('data-id') || '';
68
+ const originalId = el.id;
69
+ el.name = name.replace(/%index%/, `${index}`);
70
+ el.id = id.replace(/%index%/, `${index}`);
71
+ const label = el.parentElement.querySelector('label') || el.closest('label') || item.querySelector(`[for="${originalId}"]`);
72
+ if (label && label instanceof HTMLLabelElement) {
73
+ label.htmlFor = el.id;
74
+ }
75
+ });
76
+ }
77
+ createRemoveButton(item) {
78
+ const button = document.createElement('button');
79
+ button.type = 'button';
80
+ button.classList.add('govuk-button', 'govuk-button--secondary', 'moj-add-another__remove-button');
81
+ button.textContent = 'Remove';
82
+ item.append(button);
83
+ }
84
+ resetItem(item) {
85
+ item.querySelectorAll('[data-name], [data-id]').forEach(el => {
86
+ if (!(el instanceof HTMLInputElement)) {
87
+ return;
88
+ }
89
+ if (el.type === 'checkbox' || el.type === 'radio') {
90
+ el.checked = false;
91
+ } else {
92
+ el.value = '';
93
+ }
94
+ });
95
+ }
96
+ onRemoveButtonClick(event) {
97
+ const button = event.target;
98
+ if (!button || !(button instanceof HTMLButtonElement) || !button.classList.contains('moj-add-another__remove-button')) {
99
+ return;
100
+ }
101
+ button.closest('.moj-add-another__item').remove();
102
+ const items = this.getItems();
103
+ if (items.length === 1) {
104
+ items[0].querySelector('.moj-add-another__remove-button').remove();
105
+ }
106
+ items.forEach((el, index) => {
107
+ this.updateAttributes(el, index);
108
+ });
109
+ this.focusHeading();
110
+ }
111
+ focusHeading() {
112
+ const heading = this.container.querySelector('.moj-add-another__heading');
113
+ if (heading && heading instanceof HTMLElement) {
114
+ heading.focus();
115
+ }
116
+ }
117
+ }
105
118
 
106
119
  export { AddAnother };
107
120
  //# sourceMappingURL=add-another.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"add-another.mjs","sources":["../../../../src/moj/components/add-another/add-another.mjs"],"sourcesContent":["import $ from 'jquery'\n\nexport function AddAnother(container) {\n this.container = $(container)\n\n if (this.container.data('moj-add-another-initialised')) {\n return\n }\n\n this.container.data('moj-add-another-initialised', true)\n\n this.container.on(\n 'click',\n '.moj-add-another__remove-button',\n $.proxy(this, 'onRemoveButtonClick')\n )\n this.container.on(\n 'click',\n '.moj-add-another__add-button',\n $.proxy(this, 'onAddButtonClick')\n )\n this.container\n .find('.moj-add-another__add-button, moj-add-another__remove-button')\n .prop('type', 'button')\n}\n\nAddAnother.prototype.onAddButtonClick = function (e) {\n const item = this.getNewItem()\n this.updateAttributes(this.getItems().length, item)\n this.resetItem(item)\n const firstItem = this.getItems().first()\n if (!this.hasRemoveButton(firstItem)) {\n this.createRemoveButton(firstItem)\n }\n this.getItems().last().after(item)\n item.find('input, textarea, select').first().focus()\n}\n\nAddAnother.prototype.hasRemoveButton = function (item) {\n return item.find('.moj-add-another__remove-button').length\n}\n\nAddAnother.prototype.getItems = function () {\n return this.container.find('.moj-add-another__item')\n}\n\nAddAnother.prototype.getNewItem = function () {\n const item = this.getItems().first().clone()\n if (!this.hasRemoveButton(item)) {\n this.createRemoveButton(item)\n }\n return item\n}\n\nAddAnother.prototype.updateAttributes = function (index, item) {\n item.find('[data-name]').each(function (i, el) {\n const originalId = el.id\n\n el.name = $(el)\n .attr('data-name')\n .replace(/%index%/, index)\n el.id = $(el)\n .attr('data-id')\n .replace(/%index%/, index)\n\n const label =\n $(el).siblings('label')[0] ||\n $(el).parents('label')[0] ||\n item.find(`[for=\"${originalId}\"]`)[0]\n label.htmlFor = el.id\n })\n}\n\nAddAnother.prototype.createRemoveButton = function (item) {\n item.append(\n '<button type=\"button\" class=\"govuk-button govuk-button--secondary moj-add-another__remove-button\">Remove</button>'\n )\n}\n\nAddAnother.prototype.resetItem = function (item) {\n item.find('[data-name], [data-id]').each(function (index, el) {\n if (el.type === 'checkbox' || el.type === 'radio') {\n el.checked = false\n } else {\n el.value = ''\n }\n })\n}\n\nAddAnother.prototype.onRemoveButtonClick = function (e) {\n $(e.currentTarget).parents('.moj-add-another__item').remove()\n const items = this.getItems()\n if (items.length === 1) {\n items.find('.moj-add-another__remove-button').remove()\n }\n items.each(\n $.proxy(function (index, el) {\n this.updateAttributes(index, $(el))\n }, this)\n )\n this.focusHeading()\n}\n\nAddAnother.prototype.focusHeading = function () {\n this.container.find('.moj-add-another__heading').get(0).focus()\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"add-another.mjs","sources":["../../../../src/moj/components/add-another/add-another.mjs"],"sourcesContent":["export class AddAnother {\n constructor(container) {\n this.container = container\n\n if (this.container.hasAttribute('data-moj-add-another-init')) {\n return this\n }\n\n this.container.setAttribute('data-moj-add-another-init', '')\n\n this.container.addEventListener(\n 'click',\n this.onRemoveButtonClick.bind(this)\n )\n this.container.addEventListener('click', this.onAddButtonClick.bind(this))\n\n const buttons = this.container.querySelectorAll(\n '.moj-add-another__add-button, moj-add-another__remove-button'\n )\n\n buttons.forEach((button) => {\n if (!(button instanceof HTMLButtonElement)) {\n return\n }\n\n button.type = 'button'\n })\n }\n\n onAddButtonClick(event) {\n const button = event.target\n\n if (\n !button ||\n !(button instanceof HTMLButtonElement) ||\n !button.classList.contains('moj-add-another__add-button')\n ) {\n return\n }\n\n const items = this.getItems()\n const item = this.getNewItem()\n\n if (!item || !(item instanceof HTMLElement)) {\n return\n }\n\n this.updateAttributes(item, items.length)\n this.resetItem(item)\n\n const firstItem = items[0]\n if (!this.hasRemoveButton(firstItem)) {\n this.createRemoveButton(firstItem)\n }\n\n items[items.length - 1].after(item)\n\n const input = item.querySelector('input, textarea, select')\n if (input && input instanceof HTMLInputElement) {\n input.focus()\n }\n }\n\n hasRemoveButton(item) {\n return item.querySelectorAll('.moj-add-another__remove-button').length\n }\n\n getItems() {\n if (!this.container) {\n return []\n }\n\n const items = Array.from(\n this.container.querySelectorAll('.moj-add-another__item')\n )\n\n return items.filter((item) => item instanceof HTMLElement)\n }\n\n getNewItem() {\n const items = this.getItems()\n const item = items[0].cloneNode(true)\n\n if (!item || !(item instanceof HTMLElement)) {\n return\n }\n\n if (!this.hasRemoveButton(item)) {\n this.createRemoveButton(item)\n }\n\n return item\n }\n\n updateAttributes(item, index) {\n item.querySelectorAll('[data-name]').forEach((el) => {\n if (!(el instanceof HTMLInputElement)) {\n return\n }\n\n const name = el.getAttribute('data-name') || ''\n const id = el.getAttribute('data-id') || ''\n const originalId = el.id\n\n el.name = name.replace(/%index%/, `${index}`)\n el.id = id.replace(/%index%/, `${index}`)\n\n const label =\n el.parentElement.querySelector('label') ||\n el.closest('label') ||\n item.querySelector(`[for=\"${originalId}\"]`)\n\n if (label && label instanceof HTMLLabelElement) {\n label.htmlFor = el.id\n }\n })\n }\n\n createRemoveButton(item) {\n const button = document.createElement('button')\n button.type = 'button'\n\n button.classList.add(\n 'govuk-button',\n 'govuk-button--secondary',\n 'moj-add-another__remove-button'\n )\n\n button.textContent = 'Remove'\n\n item.append(button)\n }\n\n resetItem(item) {\n item.querySelectorAll('[data-name], [data-id]').forEach((el) => {\n if (!(el instanceof HTMLInputElement)) {\n return\n }\n\n if (el.type === 'checkbox' || el.type === 'radio') {\n el.checked = false\n } else {\n el.value = ''\n }\n })\n }\n\n onRemoveButtonClick(event) {\n const button = event.target\n\n if (\n !button ||\n !(button instanceof HTMLButtonElement) ||\n !button.classList.contains('moj-add-another__remove-button')\n ) {\n return\n }\n\n button.closest('.moj-add-another__item').remove()\n\n const items = this.getItems()\n\n if (items.length === 1) {\n items[0].querySelector('.moj-add-another__remove-button').remove()\n }\n\n items.forEach((el, index) => {\n this.updateAttributes(el, index)\n })\n\n this.focusHeading()\n }\n\n focusHeading() {\n const heading = this.container.querySelector('.moj-add-another__heading')\n\n if (heading && heading instanceof HTMLElement) {\n heading.focus()\n }\n }\n}\n"],"names":["AddAnother","constructor","container","hasAttribute","setAttribute","addEventListener","onRemoveButtonClick","bind","onAddButtonClick","buttons","querySelectorAll","forEach","button","HTMLButtonElement","type","event","target","classList","contains","items","getItems","item","getNewItem","HTMLElement","updateAttributes","length","resetItem","firstItem","hasRemoveButton","createRemoveButton","after","input","querySelector","HTMLInputElement","focus","Array","from","filter","cloneNode","index","el","name","getAttribute","id","originalId","replace","label","parentElement","closest","HTMLLabelElement","htmlFor","document","createElement","add","textContent","append","checked","value","remove","focusHeading","heading"],"mappings":"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"}
@@ -11,6 +11,7 @@
11
11
  border: 5px solid transparent;
12
12
  background: govuk-colour("white");
13
13
  -ms-grid-columns: min-content fit-content(960px);
14
+ grid-template-columns: -webkit-min-content fit-content(960px);
14
15
  grid-template-columns: min-content fit-content(960px);
15
16
  gap: govuk-spacing(2);
16
17
  @include govuk-font($size: false);
@@ -22,6 +23,7 @@
22
23
 
23
24
  .moj-alert[data-dismissible] {
24
25
  @include govuk-media-query($from: tablet) {
26
+ grid-template-columns: -webkit-min-content fit-content(920px) auto;
25
27
  grid-template-columns: min-content fit-content(920px) auto;
26
28
  }
27
29
  }
@@ -143,3 +145,5 @@
143
145
  @include govuk-link-style-error;
144
146
  }
145
147
  }
148
+
149
+ /*# sourceMappingURL=_alert.scss.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/moj/components/alert/_alert.scss"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,yCAAyC;AACzC,kCAAkC;AAClC,uCAAuC;;AAEvC;EACE,iBAAiB;EACjB,aAAa;EACb,+BAA+B;EAC/B,yBAAyB;EACzB,6BAA6B;EAC7B,iCAAiC;EACjC,gDAAgD;EAChD,6DAAqD;EAArD,qDAAqD;EACrD,qBAAqB;EACrB,iCAAiC;;EAEjC;IACE,qDAAqD;EACvD;AACF;;AAEA;EACE;IACE,kEAA0D;IAA1D,0DAA0D;EAC5D;AACF;;AAEA;EACE,cAAc;EACd,4BAA4B;EAC5B,6BAA6B;EAC7B,kBAAkB;AACpB;;AAEA;EACE,cAAc;EACd,iCAAiC;EACjC,iCAAiC;EACjC,gBAAgB;EAChB,4BAA4B;EAC5B,mCAAmC;;EAEnC;IACE,iCAAiC;EACnC;AACF;;AAEA;EACE,oBAAoB;EACpB,mBAAmB;;EAEnB;IACE,oBAAoB;IACpB,mBAAmB;EACrB;AACF;;AAEA;EACE,8BAA8B;EAC9B,gBAAgB;EAChB,UAAU;EACV,YAAY;EACZ,YAAY;EACZ,6BAA6B;EAC7B,wBAAwB;EACxB,eAAe;EACf,gBAAgB;EAChB,mCAAmC;EACnC,0BAA0B;EAC1B,iCAAiC;EACjC,kCAAkC;AACpC;;AAEA;EACE,cAAc;EACd,aAAa;EACb,mCAAmC;EACnC,0BAA0B;EAC1B,mCAAmC;EACnC,qCAAqC;;EAErC;IACE,mCAAmC;EACrC;AACF;;AAEA;;;EAGE,gBAAgB;AAClB;;AAEA;+EAC+E;AAC/E;EACE,iCAAiC;EACjC,0BAA0B;;EAE1B;;IAEE,0BAA0B;IAC1B,iCAAiC;EACnC;AACF;;AAEA;EACE,mCAAmC;EACnC,4BAA4B;;EAE5B;;;IAGE,0BAA0B;IAC1B,iCAAiC;EACnC;AACF;;AAEA;EACE,iCAAiC;EACjC,0BAA0B;;EAE1B;IACE,+BAA+B;EACjC;;EAEA;;;IAGE,0BAA0B;IAC1B,+BAA+B;EACjC;AACF;;AAEA;EACE,iCAAiC;EACjC,0BAA0B;;EAE1B;;;IAGE,0BAA0B;IAC1B,+BAA+B;EACjC;AACF","file":"_alert.scss","sourcesContent":["@use \"../../helpers/links\" as *;\n@use \"../../objects/width-container\" as *;\n@use \"../../settings/colours\" as *;\n@use \"../../vendor/govuk-frontend\" as *;\n\n.moj-alert {\n display: -ms-grid;\n display: grid;\n margin-bottom: govuk-spacing(6);\n padding: govuk-spacing(2);\n border: 5px solid transparent;\n background: govuk-colour(\"white\");\n -ms-grid-columns: min-content fit-content(960px);\n grid-template-columns: min-content fit-content(960px);\n gap: govuk-spacing(2);\n @include govuk-font($size: false);\n\n &:focus {\n outline: $govuk-focus-width solid $govuk-focus-colour;\n }\n}\n\n.moj-alert[data-dismissible] {\n @include govuk-media-query($from: tablet) {\n grid-template-columns: min-content fit-content(920px) auto;\n }\n}\n\n.moj-alert__icon {\n display: block;\n width: govuk-px-to-rem(30px);\n height: govuk-px-to-rem(30px);\n fill: currentcolor;\n}\n\n.moj-alert__content {\n display: block;\n // Padding to align text with icon\n padding-top: govuk-px-to-rem(5px);\n overflow: hidden;\n color: govuk-colour(\"black\");\n @include govuk-font-size($size: 19);\n\n @include govuk-media-query($from: tablet) {\n padding-top: govuk-px-to-rem(2px);\n }\n}\n\n.moj-alert__action {\n grid-column-start: 2;\n grid-column-end: -1;\n\n @include govuk-media-query($from: tablet) {\n grid-column-start: 3;\n justify-self: right;\n }\n}\n\n.moj-alert__dismiss {\n // Give the button link styling\n margin-bottom: 0;\n padding: 0;\n border: none;\n color: unset;\n background-color: transparent;\n -webkit-appearance: none;\n cursor: pointer;\n appearance: none;\n @include govuk-font-size($size: 19);\n @include govuk-link-common;\n @include govuk-link-style-default;\n @include govuk-link-print-friendly;\n}\n\n.moj-alert__heading {\n display: block;\n margin-top: 0;\n margin-bottom: govuk-px-to-rem(5px);\n @include govuk-text-colour;\n @include govuk-font-size($size: 24);\n @include govuk-typography-weight-bold;\n\n @include govuk-media-query($from: tablet) {\n margin-bottom: govuk-px-to-rem(3px);\n }\n}\n\n.moj-alert__content p:last-child,\n.moj-alert__content a:last-child,\n.moj-alert__content ul:last-child {\n margin-bottom: 0;\n}\n\n/* Style variants\n ========================================================================== */\n.moj-alert--information {\n border-color: $govuk-brand-colour;\n color: $govuk-brand-colour;\n\n .moj-alert__dismiss,\n .moj-alert__content a {\n @include govuk-link-common;\n @include govuk-link-style-default;\n }\n}\n\n.moj-alert--success {\n border-color: govuk-colour(\"green\");\n color: govuk-colour(\"green\");\n\n .moj-alert__dismiss,\n .moj-alert__content a,\n .moj-alert__content .govuk-link {\n @include govuk-link-common;\n @include govuk-link-style-success;\n }\n}\n\n.moj-alert--warning {\n border-color: $moj-warning-colour;\n color: $moj-warning-colour;\n\n .moj-alert__dismiss {\n color: $moj-warning-link-colour;\n }\n\n .moj-alert__dismiss,\n .moj-alert__content a,\n .moj-alert__content .govuk-link {\n @include govuk-link-common;\n @include moj-link-style-warning;\n }\n}\n\n.moj-alert--error {\n border-color: govuk-colour(\"red\");\n color: govuk-colour(\"red\");\n\n .moj-alert__dismiss,\n .moj-alert__content a,\n .moj-alert__content .govuk-link {\n @include govuk-link-common;\n @include govuk-link-style-error;\n }\n}\n"]}
@@ -0,0 +1,330 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MOJFrontend = global.MOJFrontend || {}));
5
+ })(this, (function (exports) { 'use strict';
6
+
7
+ /**
8
+ * Find an elements preceding sibling
9
+ *
10
+ * Utility function to find an elements previous sibling matching the provided
11
+ * selector.
12
+ *
13
+ * @param {Element | null} $element - Element to find siblings for
14
+ * @param {string} [selector] - selector for required sibling
15
+ */
16
+ function getPreviousSibling($element, selector) {
17
+ if (!$element || !($element instanceof HTMLElement)) {
18
+ return;
19
+ }
20
+
21
+ // Get the previous sibling element
22
+ let $sibling = $element.previousElementSibling;
23
+
24
+ // If the sibling matches our selector, use it
25
+ // If not, jump to the next sibling and continue the loop
26
+ while ($sibling) {
27
+ if ($sibling.matches(selector)) return $sibling;
28
+ $sibling = $sibling.previousElementSibling;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * @param {Element | null} $element
34
+ * @param {string} [selector]
35
+ */
36
+ function findNearestMatchingElement($element, selector) {
37
+ // If no element or selector is provided, return
38
+ if (!$element || !($element instanceof HTMLElement) || false) {
39
+ return;
40
+ }
41
+
42
+ // Start with the current element
43
+ let $currentElement = $element;
44
+ while ($currentElement) {
45
+ // First check the current element
46
+ if ($currentElement.matches(selector)) {
47
+ return $currentElement;
48
+ }
49
+
50
+ // Check all previous siblings
51
+ let $sibling = $currentElement.previousElementSibling;
52
+ while ($sibling) {
53
+ // Check if the sibling itself is a heading
54
+ if ($sibling.matches(selector)) {
55
+ return $sibling;
56
+ }
57
+ $sibling = $sibling.previousElementSibling;
58
+ }
59
+
60
+ // If no match found in siblings, move up to parent
61
+ $currentElement = $currentElement.parentElement;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Move focus to element
67
+ *
68
+ * Sets tabindex to -1 to make the element programmatically focusable,
69
+ * but removes it on blur as the element doesn't need to be focused again.
70
+ *
71
+ * @param {HTMLElement} $element - HTML element
72
+ * @param {object} [options] - Handler options
73
+ * @param {function(this: HTMLElement): void} [options.onBeforeFocus] - Callback before focus
74
+ * @param {function(this: HTMLElement): void} [options.onBlur] - Callback on blur
75
+ */
76
+ function setFocus($element, options = {}) {
77
+ const isFocusable = $element.getAttribute('tabindex');
78
+ if (!isFocusable) {
79
+ $element.setAttribute('tabindex', '-1');
80
+ }
81
+
82
+ /**
83
+ * Handle element focus
84
+ */
85
+ function onFocus() {
86
+ $element.addEventListener('blur', onBlur, {
87
+ once: true
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Handle element blur
93
+ */
94
+ function onBlur() {
95
+ if (options.onBlur) {
96
+ options.onBlur.call($element);
97
+ }
98
+ if (!isFocusable) {
99
+ $element.removeAttribute('tabindex');
100
+ }
101
+ }
102
+
103
+ // Add listener to reset element on blur, after focus
104
+ $element.addEventListener('focus', onFocus, {
105
+ once: true
106
+ });
107
+
108
+ // Focus element
109
+ if (options.onBeforeFocus) {
110
+ options.onBeforeFocus.call($element);
111
+ }
112
+ $element.focus();
113
+ }
114
+
115
+ class Alert {
116
+ /**
117
+ * @param {Element | null} $module - HTML element to use for alert
118
+ * @param {AlertConfig} [config] - Alert config
119
+ */
120
+ constructor($module, config = {}) {
121
+ if (!$module || !($module instanceof HTMLElement)) {
122
+ return this;
123
+ }
124
+ const schema = Object.freeze({
125
+ properties: {
126
+ dismissible: {
127
+ type: 'boolean'
128
+ },
129
+ dismissText: {
130
+ type: 'string'
131
+ },
132
+ disableAutoFocus: {
133
+ type: 'boolean'
134
+ },
135
+ focusOnDismissSelector: {
136
+ type: 'string'
137
+ }
138
+ }
139
+ });
140
+ const defaults = {
141
+ dismissible: false,
142
+ dismissText: 'Dismiss',
143
+ disableAutoFocus: false
144
+ };
145
+
146
+ // data attributes override JS config, which overrides defaults
147
+ this.config = this.mergeConfigs(defaults, config, this.parseDataset(schema, $module.dataset));
148
+ this.$module = $module;
149
+
150
+ /**
151
+ * Focus the alert
152
+ *
153
+ * If `role="alert"` is set, focus the element to help some assistive
154
+ * technologies prioritise announcing it.
155
+ *
156
+ * You can turn off the auto-focus functionality by setting
157
+ * `data-disable-auto-focus="true"` in the component HTML. You might wish to
158
+ * do this based on user research findings, or to avoid a clash with another
159
+ * element which should be focused when the page loads.
160
+ */
161
+ if (this.$module.getAttribute('role') === 'alert' && !this.config.disableAutoFocus) {
162
+ setFocus(this.$module);
163
+ }
164
+ this.$dismissButton = this.$module.querySelector('.moj-alert__dismiss');
165
+ if (this.config.dismissible && this.$dismissButton) {
166
+ this.$dismissButton.innerHTML = this.config.dismissText;
167
+ this.$dismissButton.removeAttribute('hidden');
168
+ this.$module.addEventListener('click', event => {
169
+ if (event.target instanceof Node && this.$dismissButton.contains(event.target)) {
170
+ this.dimiss();
171
+ }
172
+ });
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Handle dismissing the alert
178
+ */
179
+ dimiss() {
180
+ let $elementToRecieveFocus;
181
+
182
+ // If a selector has been provided, attempt to find that element
183
+ if (this.config.focusOnDismissSelector) {
184
+ $elementToRecieveFocus = document.querySelector(this.config.focusOnDismissSelector);
185
+ }
186
+
187
+ // Is the next sibling another alert
188
+ if (!$elementToRecieveFocus) {
189
+ const $nextSibling = this.$module.nextElementSibling;
190
+ if ($nextSibling && $nextSibling.matches('.moj-alert')) {
191
+ $elementToRecieveFocus = $nextSibling;
192
+ }
193
+ }
194
+
195
+ // Else try to find any preceding sibling alert or heading
196
+ if (!$elementToRecieveFocus) {
197
+ $elementToRecieveFocus = getPreviousSibling(this.$module, '.moj-alert, h1, h2, h3, h4, h5, h6');
198
+ }
199
+
200
+ // Else find the closest ancestor heading, or fallback to main, or last resort
201
+ // use the body element
202
+ if (!$elementToRecieveFocus) {
203
+ $elementToRecieveFocus = findNearestMatchingElement(this.$module, 'h1, h2, h3, h4, h5, h6, main, body');
204
+ }
205
+
206
+ // If we have an element, place focus on it
207
+ if ($elementToRecieveFocus instanceof HTMLElement) {
208
+ setFocus($elementToRecieveFocus);
209
+ }
210
+
211
+ // Remove the alert
212
+ this.$module.remove();
213
+ }
214
+
215
+ /**
216
+ * Normalise string
217
+ *
218
+ * 'If it looks like a duck, and it quacks like a duck…' 🦆
219
+ *
220
+ * If the passed value looks like a boolean or a number, convert it to a boolean
221
+ * or number.
222
+ *
223
+ * Designed to be used to convert config passed via data attributes (which are
224
+ * always strings) into something sensible.
225
+ *
226
+ * @internal
227
+ * @param {DOMStringMap[string]} value - The value to normalise
228
+ * @param {SchemaProperty} [property] - Component schema property
229
+ * @returns {string | boolean | number | undefined} Normalised data
230
+ */
231
+ normaliseString(value, property) {
232
+ const trimmedValue = value ? value.trim() : '';
233
+ let output;
234
+ let outputType;
235
+ if (property && property.type) {
236
+ outputType = property.type;
237
+ }
238
+
239
+ // No schema type set? Determine automatically
240
+ if (!outputType) {
241
+ if (['true', 'false'].includes(trimmedValue)) {
242
+ outputType = 'boolean';
243
+ }
244
+
245
+ // Empty / whitespace-only strings are considered finite so we need to check
246
+ // the length of the trimmed string as well
247
+ if (trimmedValue.length > 0 && Number.isFinite(Number(trimmedValue))) {
248
+ outputType = 'number';
249
+ }
250
+ }
251
+ switch (outputType) {
252
+ case 'boolean':
253
+ output = trimmedValue === 'true';
254
+ break;
255
+ case 'number':
256
+ output = Number(trimmedValue);
257
+ break;
258
+ default:
259
+ output = value;
260
+ }
261
+ return output;
262
+ }
263
+
264
+ /**
265
+ * Parse dataset
266
+ *
267
+ * Loop over an object and normalise each value using {@link normaliseString},
268
+ * optionally expanding nested `i18n.field`
269
+ *
270
+ * @param {Schema} schema - component schema
271
+ * @param {DOMStringMap} dataset - HTML element dataset
272
+ * @returns {object} Normalised dataset
273
+ */
274
+ parseDataset(schema, dataset) {
275
+ const parsed = {};
276
+ for (const [field, property] of Object.entries(schema.properties)) {
277
+ if (field in dataset) {
278
+ if (dataset[field]) {
279
+ parsed[field] = this.normaliseString(dataset[field], property);
280
+ }
281
+ }
282
+ }
283
+ return parsed;
284
+ }
285
+
286
+ /**
287
+ * Config merging function
288
+ *
289
+ * Takes any number of objects and combines them together, with
290
+ * greatest priority on the LAST item passed in.
291
+ *
292
+ * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge
293
+ * @returns {{ [key: string]: unknown }} A merged config object
294
+ */
295
+ mergeConfigs(...configObjects) {
296
+ const formattedConfigObject = {};
297
+
298
+ // Loop through each of the passed objects
299
+ for (const configObject of configObjects) {
300
+ for (const key of Object.keys(configObject)) {
301
+ const option = formattedConfigObject[key];
302
+ const override = configObject[key];
303
+
304
+ // Push their keys one-by-one into formattedConfigObject. Any duplicate
305
+ // keys with object values will be merged, otherwise the new value will
306
+ // override the existing value.
307
+ if (typeof option === 'object' && typeof override === 'object') {
308
+ // @ts-expect-error Index signature for type 'string' is missing
309
+ formattedConfigObject[key] = this.mergeConfigs(option, override);
310
+ } else {
311
+ formattedConfigObject[key] = override;
312
+ }
313
+ }
314
+ }
315
+ return formattedConfigObject;
316
+ }
317
+ }
318
+
319
+ /**
320
+ * @typedef {object} AlertConfig
321
+ * @property {boolean} [dismissible=false] - Can the alert be dismissed by the user
322
+ * @property {string} [dismissText=Dismiss] - the label text for the dismiss button
323
+ * @property {boolean} [disableAutoFocus=false] - whether the alert will be autofocused
324
+ * @property {string} [focusOnDismissSelector] - CSS Selector for element to be focused on dismiss
325
+ */
326
+
327
+ exports.Alert = Alert;
328
+
329
+ }));
330
+ //# sourceMappingURL=alert.bundle.js.map