@ministryofjustice/frontend 5.0.0 → 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 (112) hide show
  1. package/moj/all.bundle.js +1549 -1062
  2. package/moj/all.bundle.js.map +1 -1
  3. package/moj/all.bundle.mjs +1845 -1054
  4. package/moj/all.bundle.mjs.map +1 -1
  5. package/moj/all.mjs +7 -90
  6. package/moj/all.mjs.map +1 -1
  7. package/moj/all.scss +1 -0
  8. package/moj/all.scss.map +1 -1
  9. package/moj/common/index.mjs +57 -0
  10. package/moj/common/index.mjs.map +1 -0
  11. package/moj/common/moj-frontend-version.mjs +14 -0
  12. package/moj/common/moj-frontend-version.mjs.map +1 -0
  13. package/moj/components/add-another/add-another.bundle.js +105 -76
  14. package/moj/components/add-another/add-another.bundle.js.map +1 -1
  15. package/moj/components/add-another/add-another.bundle.mjs +222 -71
  16. package/moj/components/add-another/add-another.bundle.mjs.map +1 -1
  17. package/moj/components/add-another/add-another.mjs +103 -72
  18. package/moj/components/add-another/add-another.mjs.map +1 -1
  19. package/moj/components/alert/alert.bundle.js +115 -191
  20. package/moj/components/alert/alert.bundle.js.map +1 -1
  21. package/moj/components/alert/alert.bundle.mjs +354 -186
  22. package/moj/components/alert/alert.bundle.mjs.map +1 -1
  23. package/moj/components/alert/alert.mjs +55 -140
  24. package/moj/components/alert/alert.mjs.map +1 -1
  25. package/moj/components/button-menu/README.md +3 -1
  26. package/moj/components/button-menu/button-menu.bundle.js +91 -120
  27. package/moj/components/button-menu/button-menu.bundle.js.map +1 -1
  28. package/moj/components/button-menu/button-menu.bundle.mjs +329 -114
  29. package/moj/components/button-menu/button-menu.bundle.mjs.map +1 -1
  30. package/moj/components/button-menu/button-menu.mjs +89 -116
  31. package/moj/components/button-menu/button-menu.mjs.map +1 -1
  32. package/moj/components/date-picker/date-picker.bundle.js +174 -154
  33. package/moj/components/date-picker/date-picker.bundle.js.map +1 -1
  34. package/moj/components/date-picker/date-picker.bundle.mjs +411 -147
  35. package/moj/components/date-picker/date-picker.bundle.mjs.map +1 -1
  36. package/moj/components/date-picker/date-picker.mjs +172 -150
  37. package/moj/components/date-picker/date-picker.mjs.map +1 -1
  38. package/moj/components/filter/template.njk +1 -1
  39. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js +133 -44
  40. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js.map +1 -1
  41. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs +374 -41
  42. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs.map +1 -1
  43. package/moj/components/filter-toggle-button/filter-toggle-button.mjs +131 -40
  44. package/moj/components/filter-toggle-button/filter-toggle-button.mjs.map +1 -1
  45. package/moj/components/form-validator/form-validator.bundle.js +159 -69
  46. package/moj/components/form-validator/form-validator.bundle.js.map +1 -1
  47. package/moj/components/form-validator/form-validator.bundle.mjs +399 -65
  48. package/moj/components/form-validator/form-validator.bundle.mjs.map +1 -1
  49. package/moj/components/form-validator/form-validator.mjs +134 -54
  50. package/moj/components/form-validator/form-validator.mjs.map +1 -1
  51. package/moj/components/multi-file-upload/multi-file-upload.bundle.js +291 -117
  52. package/moj/components/multi-file-upload/multi-file-upload.bundle.js.map +1 -1
  53. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs +527 -109
  54. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs.map +1 -1
  55. package/moj/components/multi-file-upload/multi-file-upload.mjs +288 -101
  56. package/moj/components/multi-file-upload/multi-file-upload.mjs.map +1 -1
  57. package/moj/components/multi-file-upload/template.njk +1 -1
  58. package/moj/components/multi-select/multi-select.bundle.js +106 -41
  59. package/moj/components/multi-select/multi-select.bundle.js.map +1 -1
  60. package/moj/components/multi-select/multi-select.bundle.mjs +346 -37
  61. package/moj/components/multi-select/multi-select.bundle.mjs.map +1 -1
  62. package/moj/components/multi-select/multi-select.mjs +104 -37
  63. package/moj/components/multi-select/multi-select.mjs.map +1 -1
  64. package/moj/components/password-reveal/_password-reveal.scss +3 -1
  65. package/moj/components/password-reveal/_password-reveal.scss.map +1 -1
  66. package/moj/components/password-reveal/password-reveal.bundle.js +32 -29
  67. package/moj/components/password-reveal/password-reveal.bundle.js.map +1 -1
  68. package/moj/components/password-reveal/password-reveal.bundle.mjs +149 -24
  69. package/moj/components/password-reveal/password-reveal.bundle.mjs.map +1 -1
  70. package/moj/components/password-reveal/password-reveal.mjs +30 -25
  71. package/moj/components/password-reveal/password-reveal.mjs.map +1 -1
  72. package/moj/components/rich-text-editor/README.md +4 -3
  73. package/moj/components/rich-text-editor/rich-text-editor.bundle.js +127 -62
  74. package/moj/components/rich-text-editor/rich-text-editor.bundle.js.map +1 -1
  75. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs +367 -58
  76. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs.map +1 -1
  77. package/moj/components/rich-text-editor/rich-text-editor.mjs +125 -58
  78. package/moj/components/rich-text-editor/rich-text-editor.mjs.map +1 -1
  79. package/moj/components/search-toggle/search-toggle.bundle.js +94 -26
  80. package/moj/components/search-toggle/search-toggle.bundle.js.map +1 -1
  81. package/moj/components/search-toggle/search-toggle.bundle.mjs +334 -22
  82. package/moj/components/search-toggle/search-toggle.bundle.mjs.map +1 -1
  83. package/moj/components/search-toggle/search-toggle.mjs +92 -22
  84. package/moj/components/search-toggle/search-toggle.mjs.map +1 -1
  85. package/moj/components/sortable-table/sortable-table.bundle.js +151 -83
  86. package/moj/components/sortable-table/sortable-table.bundle.js.map +1 -1
  87. package/moj/components/sortable-table/sortable-table.bundle.mjs +390 -78
  88. package/moj/components/sortable-table/sortable-table.bundle.mjs.map +1 -1
  89. package/moj/components/sortable-table/sortable-table.mjs +149 -79
  90. package/moj/components/sortable-table/sortable-table.mjs.map +1 -1
  91. package/moj/core/_all.scss +3 -0
  92. package/moj/core/_all.scss.map +1 -0
  93. package/moj/core/_moj-frontend-properties.scss +7 -0
  94. package/moj/core/_moj-frontend-properties.scss.map +1 -0
  95. package/moj/filters/prototype-kit-13-filters.js +4 -3
  96. package/moj/helpers.bundle.js +22 -77
  97. package/moj/helpers.bundle.js.map +1 -1
  98. package/moj/helpers.bundle.mjs +23 -74
  99. package/moj/helpers.bundle.mjs.map +1 -1
  100. package/moj/helpers.mjs +23 -74
  101. package/moj/helpers.mjs.map +1 -1
  102. package/moj/moj-frontend.min.css +1 -1
  103. package/moj/moj-frontend.min.css.map +1 -1
  104. package/moj/moj-frontend.min.js +1 -1
  105. package/moj/moj-frontend.min.js.map +1 -1
  106. package/package.json +1 -1
  107. package/moj/version.bundle.js +0 -12
  108. package/moj/version.bundle.js.map +0 -1
  109. package/moj/version.bundle.mjs +0 -4
  110. package/moj/version.bundle.mjs.map +0 -1
  111. package/moj/version.mjs +0 -4
  112. package/moj/version.mjs.map +0 -1
@@ -1,74 +1,54 @@
1
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';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('govuk-frontend')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'govuk-frontend'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MOJFrontend = global.MOJFrontend || {}, global.GOVUKFrontend));
5
+ })(this, (function (exports, govukFrontend) { 'use strict';
6
6
 
7
- class ButtonMenu {
7
+ /**
8
+ * @augments {ConfigurableComponent<ButtonMenuConfig>}
9
+ */
10
+ class ButtonMenu extends govukFrontend.ConfigurableComponent {
8
11
  /**
9
- * @param {Element | null} $module - HTML element to use for button menu
12
+ * @param {Element | null} $root - HTML element to use for button menu
10
13
  * @param {ButtonMenuConfig} [config] - Button menu config
11
14
  */
12
- constructor($module, config = {}) {
13
- if (!$module || !($module instanceof HTMLElement)) {
14
- return this;
15
- }
16
- const schema = Object.freeze({
17
- properties: {
18
- buttonText: {
19
- type: 'string'
20
- },
21
- buttonClasses: {
22
- type: 'string'
23
- },
24
- alignMenu: {
25
- type: 'string'
26
- }
27
- }
28
- });
29
- const defaults = {
30
- buttonText: 'Actions',
31
- alignMenu: 'left',
32
- buttonClasses: ''
33
- };
34
- // data attributes override JS config, which overrides defaults
35
- this.config = this.mergeConfigs(defaults, config, this.parseDataset(schema, $module.dataset));
36
- this.$module = $module;
15
+ constructor($root, config = {}) {
16
+ super($root, config);
37
17
 
38
18
  // If only one button is provided, don't initiate a menu and toggle button
39
19
  // if classes have been provided for the toggleButton, apply them to the single item
40
- if (this.$module.children.length === 1) {
41
- const button = this.$module.children[0];
42
- button.classList.forEach(className => {
20
+ if (this.$root.children.length === 1) {
21
+ const $button = this.$root.children[0];
22
+ $button.classList.forEach(className => {
43
23
  if (className.startsWith('govuk-button-')) {
44
- button.classList.remove(className);
24
+ $button.classList.remove(className);
45
25
  }
46
- button.classList.remove('moj-button-menu__item');
47
- button.classList.add('moj-button-menu__single-button');
26
+ $button.classList.remove('moj-button-menu__item');
27
+ $button.classList.add('moj-button-menu__single-button');
48
28
  });
49
29
  if (this.config.buttonClasses) {
50
- button.classList.add(...this.config.buttonClasses.split(' '));
30
+ $button.classList.add(...this.config.buttonClasses.split(' '));
51
31
  }
52
32
  }
53
- // Otherwise intialise a button menu
54
- if (this.$module.children.length > 1) {
33
+ // Otherwise initialise a button menu
34
+ if (this.$root.children.length > 1) {
55
35
  this.initMenu();
56
36
  }
57
37
  }
58
38
  initMenu() {
59
39
  this.$menu = this.createMenu();
60
- this.$module.insertAdjacentHTML('afterbegin', this.toggleTemplate());
40
+ this.$root.insertAdjacentHTML('afterbegin', this.toggleTemplate());
61
41
  this.setupMenuItems();
62
- this.$menuToggle = this.$module.querySelector(':scope > button');
63
- this.items = this.$menu.querySelectorAll('a, button');
42
+ this.$menuToggle = this.$root.querySelector(':scope > button');
43
+ this.$items = this.$menu.querySelectorAll('a, button');
64
44
  this.$menuToggle.addEventListener('click', event => {
65
45
  this.toggleMenu(event);
66
46
  });
67
- this.$module.addEventListener('keydown', event => {
47
+ this.$root.addEventListener('keydown', event => {
68
48
  this.handleKeyDown(event);
69
49
  });
70
50
  document.addEventListener('click', event => {
71
- if (!this.$module.contains(event.target)) {
51
+ if (event.target instanceof Node && !this.$root.contains(event.target)) {
72
52
  this.closeMenu(false);
73
53
  }
74
54
  });
@@ -81,30 +61,30 @@
81
61
  if (this.config.alignMenu === 'right') {
82
62
  $menu.classList.add('moj-button-menu__wrapper--right');
83
63
  }
84
- this.$module.appendChild($menu);
85
- while (this.$module.firstChild !== $menu) {
86
- $menu.appendChild(this.$module.firstChild);
64
+ this.$root.appendChild($menu);
65
+ while (this.$root.firstChild !== $menu) {
66
+ $menu.appendChild(this.$root.firstChild);
87
67
  }
88
68
  return $menu;
89
69
  }
90
70
  setupMenuItems() {
91
- Array.from(this.$menu.children).forEach(item => {
71
+ Array.from(this.$menu.children).forEach($menuItem => {
92
72
  // wrap item in li tag
93
- const listItem = document.createElement('li');
94
- this.$menu.insertBefore(listItem, item);
95
- listItem.appendChild(item);
96
- item.setAttribute('tabindex', '-1');
97
- if (item.tagName === 'BUTTON') {
98
- item.setAttribute('type', 'button');
73
+ const $listItem = document.createElement('li');
74
+ this.$menu.insertBefore($listItem, $menuItem);
75
+ $listItem.appendChild($menuItem);
76
+ $menuItem.setAttribute('tabindex', '-1');
77
+ if ($menuItem.tagName === 'BUTTON') {
78
+ $menuItem.setAttribute('type', 'button');
99
79
  }
100
- item.classList.forEach(className => {
80
+ $menuItem.classList.forEach(className => {
101
81
  if (className.startsWith('govuk-button')) {
102
- item.classList.remove(className);
82
+ $menuItem.classList.remove(className);
103
83
  }
104
84
  });
105
85
 
106
86
  // add a slight delay after click before closing the menu, makes it *feel* better
107
- item.addEventListener('click', () => {
87
+ $menuItem.addEventListener('click', () => {
108
88
  setTimeout(() => {
109
89
  this.closeMenu(false);
110
90
  }, 50);
@@ -129,6 +109,10 @@
129
109
  isOpen() {
130
110
  return this.$menuToggle.getAttribute('aria-expanded') === 'true';
131
111
  }
112
+
113
+ /**
114
+ * @param {MouseEvent} event - Click event
115
+ */
132
116
  toggleMenu(event) {
133
117
  event.preventDefault();
134
118
 
@@ -174,18 +158,22 @@
174
158
  * @param {number} index - the index of the item to focus
175
159
  */
176
160
  focusItem(index) {
177
- if (index >= this.items.length) index = 0;
178
- if (index < 0) index = this.items.length - 1;
179
- const menuItem = this.items.item(index);
180
- if (menuItem) {
181
- menuItem.focus();
161
+ if (index >= this.$items.length) index = 0;
162
+ if (index < 0) index = this.$items.length - 1;
163
+ const $menuItem = this.$items.item(index);
164
+ if ($menuItem) {
165
+ $menuItem.focus();
182
166
  }
183
167
  }
184
168
  currentFocusIndex() {
185
- const activeElement = document.activeElement;
186
- const menuItems = Array.from(this.items);
187
- return menuItems.indexOf(activeElement);
169
+ const $activeElement = document.activeElement;
170
+ const $menuItems = Array.from(this.$items);
171
+ return ($activeElement instanceof HTMLAnchorElement || $activeElement instanceof HTMLButtonElement) && $menuItems.indexOf($activeElement);
188
172
  }
173
+
174
+ /**
175
+ * @param {KeyboardEvent} event - Keydown event
176
+ */
189
177
  handleKeyDown(event) {
190
178
  if (event.target === this.$menuToggle) {
191
179
  switch (event.key) {
@@ -195,11 +183,11 @@
195
183
  break;
196
184
  case 'ArrowUp':
197
185
  event.preventDefault();
198
- this.openMenu(this.items.length - 1);
186
+ this.openMenu(this.$items.length - 1);
199
187
  break;
200
188
  }
201
189
  }
202
- if (this.$menu.contains(event.target) && this.isOpen()) {
190
+ if (event.target instanceof Node && this.$menu.contains(event.target) && this.isOpen()) {
203
191
  switch (event.key) {
204
192
  case 'ArrowDown':
205
193
  event.preventDefault();
@@ -219,7 +207,7 @@
219
207
  break;
220
208
  case 'End':
221
209
  event.preventDefault();
222
- this.focusItem(this.items.length - 1);
210
+ this.focusItem(this.$items.length - 1);
223
211
  break;
224
212
  }
225
213
  }
@@ -232,58 +220,8 @@
232
220
  }
233
221
 
234
222
  /**
235
- * Parse dataset
236
- *
237
- * Loop over an object and normalise each value using {@link normaliseString},
238
- * optionally expanding nested `i18n.field`
239
- *
240
- * @param {Schema} schema - component schema
241
- * @param {DOMStringMap} dataset - HTML element dataset
242
- * @returns {object} Normalised dataset
243
- */
244
- parseDataset(schema, dataset) {
245
- const parsed = {};
246
- for (const [field,,] of Object.entries(schema.properties)) {
247
- if (field in dataset) {
248
- if (dataset[field]) {
249
- parsed[field] = dataset[field];
250
- }
251
- }
252
- }
253
- return parsed;
254
- }
255
-
256
- /**
257
- * Config merging function
258
- *
259
- * Takes any number of objects and combines them together, with
260
- * greatest priority on the LAST item passed in.
261
- *
262
- * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge
263
- * @returns {{ [key: string]: unknown }} A merged config object
223
+ * Name for the component used when initialising using data-module attributes.
264
224
  */
265
- mergeConfigs(...configObjects) {
266
- const formattedConfigObject = {};
267
-
268
- // Loop through each of the passed objects
269
- for (const configObject of configObjects) {
270
- for (const key of Object.keys(configObject)) {
271
- const option = formattedConfigObject[key];
272
- const override = configObject[key];
273
-
274
- // Push their keys one-by-one into formattedConfigObject. Any duplicate
275
- // keys with object values will be merged, otherwise the new value will
276
- // override the existing value.
277
- if (typeof option === 'object' && typeof override === 'object') {
278
- // @ts-expect-error Index signature for type 'string' is missing
279
- formattedConfigObject[key] = this.mergeConfigs(option, override);
280
- } else {
281
- formattedConfigObject[key] = override;
282
- }
283
- }
284
- }
285
- return formattedConfigObject;
286
- }
287
225
  }
288
226
 
289
227
  /**
@@ -293,6 +231,39 @@
293
231
  * @property {string} [buttonClasses='govuk-button--secondary'] - css classes applied to the toggle button
294
232
  */
295
233
 
234
+ /**
235
+ * @import { Schema } from 'govuk-frontend/dist/govuk/common/configuration.mjs'
236
+ */
237
+ ButtonMenu.moduleName = 'moj-button-menu';
238
+ /**
239
+ * Button menu config
240
+ *
241
+ * @type {ButtonMenuConfig}
242
+ */
243
+ ButtonMenu.defaults = Object.freeze({
244
+ buttonText: 'Actions',
245
+ alignMenu: 'left',
246
+ buttonClasses: ''
247
+ });
248
+ /**
249
+ * Button menu config schema
250
+ *
251
+ * @type {Schema<ButtonMenuConfig>}
252
+ */
253
+ ButtonMenu.schema = Object.freeze(/** @type {const} */{
254
+ properties: {
255
+ buttonText: {
256
+ type: 'string'
257
+ },
258
+ buttonClasses: {
259
+ type: 'string'
260
+ },
261
+ alignMenu: {
262
+ type: 'string'
263
+ }
264
+ }
265
+ });
266
+
296
267
  exports.ButtonMenu = ButtonMenu;
297
268
 
298
269
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"button-menu.bundle.js","sources":["../../../../src/moj/components/button-menu/button-menu.mjs"],"sourcesContent":["export class ButtonMenu {\n /**\n * @param {Element | null} $module - HTML element to use for button menu\n * @param {ButtonMenuConfig} [config] - Button menu config\n */\n constructor($module, config = {}) {\n if (!$module || !($module instanceof HTMLElement)) {\n return this\n }\n\n const schema = Object.freeze({\n properties: {\n buttonText: { type: 'string' },\n buttonClasses: { type: 'string' },\n alignMenu: { type: 'string' }\n }\n })\n\n const defaults = {\n buttonText: 'Actions',\n alignMenu: 'left',\n buttonClasses: ''\n }\n // data attributes override JS config, which overrides defaults\n this.config = this.mergeConfigs(\n defaults,\n config,\n this.parseDataset(schema, $module.dataset)\n )\n\n this.$module = $module\n\n // If only one button is provided, don't initiate a menu and toggle button\n // if classes have been provided for the toggleButton, apply them to the single item\n if (this.$module.children.length === 1) {\n const button = this.$module.children[0]\n button.classList.forEach((className) => {\n if (className.startsWith('govuk-button-')) {\n button.classList.remove(className)\n }\n button.classList.remove('moj-button-menu__item')\n button.classList.add('moj-button-menu__single-button')\n })\n if (this.config.buttonClasses) {\n button.classList.add(...this.config.buttonClasses.split(' '))\n }\n }\n // Otherwise intialise a button menu\n if (this.$module.children.length > 1) {\n this.initMenu()\n }\n }\n\n initMenu() {\n this.$menu = this.createMenu()\n this.$module.insertAdjacentHTML('afterbegin', this.toggleTemplate())\n this.setupMenuItems()\n\n this.$menuToggle = this.$module.querySelector(':scope > button')\n this.items = this.$menu.querySelectorAll('a, button')\n\n this.$menuToggle.addEventListener('click', (event) => {\n this.toggleMenu(event)\n })\n\n this.$module.addEventListener('keydown', (event) => {\n this.handleKeyDown(event)\n })\n\n document.addEventListener('click', (event) => {\n if (!this.$module.contains(event.target)) {\n this.closeMenu(false)\n }\n })\n }\n\n createMenu() {\n const $menu = document.createElement('ul')\n $menu.setAttribute('role', 'list')\n $menu.hidden = true\n $menu.classList.add('moj-button-menu__wrapper')\n if (this.config.alignMenu === 'right') {\n $menu.classList.add('moj-button-menu__wrapper--right')\n }\n\n this.$module.appendChild($menu)\n while (this.$module.firstChild !== $menu) {\n $menu.appendChild(this.$module.firstChild)\n }\n\n return $menu\n }\n\n setupMenuItems() {\n Array.from(this.$menu.children).forEach((item) => {\n // wrap item in li tag\n const listItem = document.createElement('li')\n this.$menu.insertBefore(listItem, item)\n listItem.appendChild(item)\n\n item.setAttribute('tabindex', '-1')\n\n if (item.tagName === 'BUTTON') {\n item.setAttribute('type', 'button')\n }\n\n item.classList.forEach((className) => {\n if (className.startsWith('govuk-button')) {\n item.classList.remove(className)\n }\n })\n\n // add a slight delay after click before closing the menu, makes it *feel* better\n item.addEventListener('click', () => {\n setTimeout(() => {\n this.closeMenu(false)\n }, 50)\n })\n })\n }\n\n toggleTemplate() {\n return `\n <button type=\"button\" class=\"govuk-button moj-button-menu__toggle-button ${this.config.buttonClasses || ''}\" aria-haspopup=\"true\" aria-expanded=\"false\">\n <span>\n ${this.config.buttonText}\n <svg width=\"11\" height=\"5\" viewBox=\"0 0 11 5\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M5.5 0L11 5L0 5L5.5 0Z\" fill=\"currentColor\"/>\n </svg>\n </span>\n </button>`\n }\n\n /**\n * @returns {boolean}\n */\n isOpen() {\n return this.$menuToggle.getAttribute('aria-expanded') === 'true'\n }\n\n toggleMenu(event) {\n event.preventDefault()\n\n // If menu is triggered with mouse don't move focus to first item\n const keyboardEvent = event.detail === 0\n const focusIndex = keyboardEvent ? 0 : -1\n\n if (this.isOpen()) {\n this.closeMenu()\n } else {\n this.openMenu(focusIndex)\n }\n }\n\n /**\n * Opens the menu and optionally sets the focus to the item with given index\n *\n * @param {number} focusIndex - The index of the item to focus\n */\n openMenu(focusIndex = 0) {\n this.$menu.hidden = false\n this.$menuToggle.setAttribute('aria-expanded', 'true')\n if (focusIndex !== -1) {\n this.focusItem(focusIndex)\n }\n }\n\n /**\n * Closes the menu and optionally returns focus back to menuToggle\n *\n * @param {boolean} moveFocus - whether to return focus to the toggle button\n */\n closeMenu(moveFocus = true) {\n this.$menu.hidden = true\n this.$menuToggle.setAttribute('aria-expanded', 'false')\n if (moveFocus) {\n this.$menuToggle.focus()\n }\n }\n\n /**\n * Focuses the menu item at the specified index\n *\n * @param {number} index - the index of the item to focus\n */\n focusItem(index) {\n if (index >= this.items.length) index = 0\n if (index < 0) index = this.items.length - 1\n\n const menuItem = this.items.item(index)\n if (menuItem) {\n menuItem.focus()\n }\n }\n\n currentFocusIndex() {\n const activeElement = document.activeElement\n const menuItems = Array.from(this.items)\n\n return menuItems.indexOf(activeElement)\n }\n\n handleKeyDown(event) {\n if (event.target === this.$menuToggle) {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n this.openMenu()\n break\n case 'ArrowUp':\n event.preventDefault()\n this.openMenu(this.items.length - 1)\n break\n }\n }\n\n if (this.$menu.contains(event.target) && this.isOpen()) {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n if (this.currentFocusIndex() !== -1) {\n this.focusItem(this.currentFocusIndex() + 1)\n }\n break\n case 'ArrowUp':\n event.preventDefault()\n if (this.currentFocusIndex() !== -1) {\n this.focusItem(this.currentFocusIndex() - 1)\n }\n break\n case 'Home':\n event.preventDefault()\n this.focusItem(0)\n break\n case 'End':\n event.preventDefault()\n this.focusItem(this.items.length - 1)\n break\n }\n }\n\n if (event.key === 'Escape' && this.isOpen()) {\n this.closeMenu()\n }\n if (event.key === 'Tab' && this.isOpen()) {\n this.closeMenu(false)\n }\n }\n\n /**\n * Parse dataset\n *\n * Loop over an object and normalise each value using {@link normaliseString},\n * optionally expanding nested `i18n.field`\n *\n * @param {Schema} schema - component schema\n * @param {DOMStringMap} dataset - HTML element dataset\n * @returns {object} Normalised dataset\n */\n parseDataset(schema, dataset) {\n const parsed = {}\n\n for (const [field, ,] of Object.entries(schema.properties)) {\n if (field in dataset) {\n if (dataset[field]) {\n parsed[field] = dataset[field]\n }\n }\n }\n\n return parsed\n }\n\n /**\n * Config merging function\n *\n * Takes any number of objects and combines them together, with\n * greatest priority on the LAST item passed in.\n *\n * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge\n * @returns {{ [key: string]: unknown }} A merged config object\n */\n mergeConfigs(...configObjects) {\n const formattedConfigObject = {}\n\n // Loop through each of the passed objects\n for (const configObject of configObjects) {\n for (const key of Object.keys(configObject)) {\n const option = formattedConfigObject[key]\n const override = configObject[key]\n\n // Push their keys one-by-one into formattedConfigObject. Any duplicate\n // keys with object values will be merged, otherwise the new value will\n // override the existing value.\n if (typeof option === 'object' && typeof override === 'object') {\n // @ts-expect-error Index signature for type 'string' is missing\n formattedConfigObject[key] = this.mergeConfigs(option, override)\n } else {\n formattedConfigObject[key] = override\n }\n }\n }\n\n return formattedConfigObject\n }\n}\n\n/**\n * @typedef {object} ButtonMenuConfig\n * @property {string} [buttonText='Actions'] - Label for the toggle button\n * @property {\"left\" | \"right\"} [alignMenu='left'] - the alignment of the menu\n * @property {string} [buttonClasses='govuk-button--secondary'] - css classes applied to the toggle button\n */\n"],"names":["ButtonMenu","constructor","$module","config","HTMLElement","schema","Object","freeze","properties","buttonText","type","buttonClasses","alignMenu","defaults","mergeConfigs","parseDataset","dataset","children","length","button","classList","forEach","className","startsWith","remove","add","split","initMenu","$menu","createMenu","insertAdjacentHTML","toggleTemplate","setupMenuItems","$menuToggle","querySelector","items","querySelectorAll","addEventListener","event","toggleMenu","handleKeyDown","document","contains","target","closeMenu","createElement","setAttribute","hidden","appendChild","firstChild","Array","from","item","listItem","insertBefore","tagName","setTimeout","isOpen","getAttribute","preventDefault","keyboardEvent","detail","focusIndex","openMenu","focusItem","moveFocus","focus","index","menuItem","currentFocusIndex","activeElement","menuItems","indexOf","key","parsed","field","entries","configObjects","formattedConfigObject","configObject","keys","option","override"],"mappings":"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"}
1
+ {"version":3,"file":"button-menu.bundle.js","sources":["../../../../src/moj/components/button-menu/button-menu.mjs"],"sourcesContent":["import { ConfigurableComponent } from 'govuk-frontend'\n\n/**\n * @augments {ConfigurableComponent<ButtonMenuConfig>}\n */\nexport class ButtonMenu extends ConfigurableComponent {\n /**\n * @param {Element | null} $root - HTML element to use for button menu\n * @param {ButtonMenuConfig} [config] - Button menu config\n */\n constructor($root, config = {}) {\n super($root, config)\n\n // If only one button is provided, don't initiate a menu and toggle button\n // if classes have been provided for the toggleButton, apply them to the single item\n if (this.$root.children.length === 1) {\n const $button = this.$root.children[0]\n\n $button.classList.forEach((className) => {\n if (className.startsWith('govuk-button-')) {\n $button.classList.remove(className)\n }\n\n $button.classList.remove('moj-button-menu__item')\n $button.classList.add('moj-button-menu__single-button')\n })\n\n if (this.config.buttonClasses) {\n $button.classList.add(...this.config.buttonClasses.split(' '))\n }\n }\n // Otherwise initialise a button menu\n if (this.$root.children.length > 1) {\n this.initMenu()\n }\n }\n\n initMenu() {\n this.$menu = this.createMenu()\n this.$root.insertAdjacentHTML('afterbegin', this.toggleTemplate())\n this.setupMenuItems()\n\n this.$menuToggle = this.$root.querySelector(':scope > button')\n this.$items = this.$menu.querySelectorAll('a, button')\n\n this.$menuToggle.addEventListener('click', (event) => {\n this.toggleMenu(event)\n })\n\n this.$root.addEventListener('keydown', (event) => {\n this.handleKeyDown(event)\n })\n\n document.addEventListener('click', (event) => {\n if (event.target instanceof Node && !this.$root.contains(event.target)) {\n this.closeMenu(false)\n }\n })\n }\n\n createMenu() {\n const $menu = document.createElement('ul')\n\n $menu.setAttribute('role', 'list')\n $menu.hidden = true\n $menu.classList.add('moj-button-menu__wrapper')\n\n if (this.config.alignMenu === 'right') {\n $menu.classList.add('moj-button-menu__wrapper--right')\n }\n\n this.$root.appendChild($menu)\n\n while (this.$root.firstChild !== $menu) {\n $menu.appendChild(this.$root.firstChild)\n }\n\n return $menu\n }\n\n setupMenuItems() {\n Array.from(this.$menu.children).forEach(($menuItem) => {\n // wrap item in li tag\n const $listItem = document.createElement('li')\n this.$menu.insertBefore($listItem, $menuItem)\n $listItem.appendChild($menuItem)\n\n $menuItem.setAttribute('tabindex', '-1')\n\n if ($menuItem.tagName === 'BUTTON') {\n $menuItem.setAttribute('type', 'button')\n }\n\n $menuItem.classList.forEach((className) => {\n if (className.startsWith('govuk-button')) {\n $menuItem.classList.remove(className)\n }\n })\n\n // add a slight delay after click before closing the menu, makes it *feel* better\n $menuItem.addEventListener('click', () => {\n setTimeout(() => {\n this.closeMenu(false)\n }, 50)\n })\n })\n }\n\n toggleTemplate() {\n return `\n <button type=\"button\" class=\"govuk-button moj-button-menu__toggle-button ${this.config.buttonClasses || ''}\" aria-haspopup=\"true\" aria-expanded=\"false\">\n <span>\n ${this.config.buttonText}\n <svg width=\"11\" height=\"5\" viewBox=\"0 0 11 5\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M5.5 0L11 5L0 5L5.5 0Z\" fill=\"currentColor\"/>\n </svg>\n </span>\n </button>`\n }\n\n /**\n * @returns {boolean}\n */\n isOpen() {\n return this.$menuToggle.getAttribute('aria-expanded') === 'true'\n }\n\n /**\n * @param {MouseEvent} event - Click event\n */\n toggleMenu(event) {\n event.preventDefault()\n\n // If menu is triggered with mouse don't move focus to first item\n const keyboardEvent = event.detail === 0\n const focusIndex = keyboardEvent ? 0 : -1\n\n if (this.isOpen()) {\n this.closeMenu()\n } else {\n this.openMenu(focusIndex)\n }\n }\n\n /**\n * Opens the menu and optionally sets the focus to the item with given index\n *\n * @param {number} focusIndex - The index of the item to focus\n */\n openMenu(focusIndex = 0) {\n this.$menu.hidden = false\n this.$menuToggle.setAttribute('aria-expanded', 'true')\n if (focusIndex !== -1) {\n this.focusItem(focusIndex)\n }\n }\n\n /**\n * Closes the menu and optionally returns focus back to menuToggle\n *\n * @param {boolean} moveFocus - whether to return focus to the toggle button\n */\n closeMenu(moveFocus = true) {\n this.$menu.hidden = true\n this.$menuToggle.setAttribute('aria-expanded', 'false')\n if (moveFocus) {\n this.$menuToggle.focus()\n }\n }\n\n /**\n * Focuses the menu item at the specified index\n *\n * @param {number} index - the index of the item to focus\n */\n focusItem(index) {\n if (index >= this.$items.length) index = 0\n if (index < 0) index = this.$items.length - 1\n\n const $menuItem = this.$items.item(index)\n if ($menuItem) {\n $menuItem.focus()\n }\n }\n\n currentFocusIndex() {\n const $activeElement = document.activeElement\n const $menuItems = Array.from(this.$items)\n\n return (\n ($activeElement instanceof HTMLAnchorElement ||\n $activeElement instanceof HTMLButtonElement) &&\n $menuItems.indexOf($activeElement)\n )\n }\n\n /**\n * @param {KeyboardEvent} event - Keydown event\n */\n handleKeyDown(event) {\n if (event.target === this.$menuToggle) {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n this.openMenu()\n break\n case 'ArrowUp':\n event.preventDefault()\n this.openMenu(this.$items.length - 1)\n break\n }\n }\n\n if (\n event.target instanceof Node &&\n this.$menu.contains(event.target) &&\n this.isOpen()\n ) {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n if (this.currentFocusIndex() !== -1) {\n this.focusItem(this.currentFocusIndex() + 1)\n }\n break\n case 'ArrowUp':\n event.preventDefault()\n if (this.currentFocusIndex() !== -1) {\n this.focusItem(this.currentFocusIndex() - 1)\n }\n break\n case 'Home':\n event.preventDefault()\n this.focusItem(0)\n break\n case 'End':\n event.preventDefault()\n this.focusItem(this.$items.length - 1)\n break\n }\n }\n\n if (event.key === 'Escape' && this.isOpen()) {\n this.closeMenu()\n }\n if (event.key === 'Tab' && this.isOpen()) {\n this.closeMenu(false)\n }\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'moj-button-menu'\n\n /**\n * Button menu config\n *\n * @type {ButtonMenuConfig}\n */\n static defaults = Object.freeze({\n buttonText: 'Actions',\n alignMenu: 'left',\n buttonClasses: ''\n })\n\n /**\n * Button menu config schema\n *\n * @type {Schema<ButtonMenuConfig>}\n */\n static schema = Object.freeze(\n /** @type {const} */ ({\n properties: {\n buttonText: { type: 'string' },\n buttonClasses: { type: 'string' },\n alignMenu: { type: 'string' }\n }\n })\n )\n}\n\n/**\n * @typedef {object} ButtonMenuConfig\n * @property {string} [buttonText='Actions'] - Label for the toggle button\n * @property {\"left\" | \"right\"} [alignMenu='left'] - the alignment of the menu\n * @property {string} [buttonClasses='govuk-button--secondary'] - css classes applied to the toggle button\n */\n\n/**\n * @import { Schema } from 'govuk-frontend/dist/govuk/common/configuration.mjs'\n */\n"],"names":["ButtonMenu","ConfigurableComponent","constructor","$root","config","children","length","$button","classList","forEach","className","startsWith","remove","add","buttonClasses","split","initMenu","$menu","createMenu","insertAdjacentHTML","toggleTemplate","setupMenuItems","$menuToggle","querySelector","$items","querySelectorAll","addEventListener","event","toggleMenu","handleKeyDown","document","target","Node","contains","closeMenu","createElement","setAttribute","hidden","alignMenu","appendChild","firstChild","Array","from","$menuItem","$listItem","insertBefore","tagName","setTimeout","buttonText","isOpen","getAttribute","preventDefault","keyboardEvent","detail","focusIndex","openMenu","focusItem","moveFocus","focus","index","item","currentFocusIndex","$activeElement","activeElement","$menuItems","HTMLAnchorElement","HTMLButtonElement","indexOf","key","moduleName","defaults","Object","freeze","schema","properties","type"],"mappings":"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"}