@ministryofjustice/frontend 4.0.1 → 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.
- package/govuk-prototype-kit.config.json +19 -4
- package/moj/_base.scss +2 -0
- package/moj/_base.scss.map +1 -0
- package/moj/all.bundle.js +2523 -0
- package/moj/all.bundle.js.map +1 -0
- package/moj/all.bundle.mjs +2502 -0
- package/moj/all.bundle.mjs.map +1 -0
- package/moj/all.mjs +59 -69
- package/moj/all.mjs.map +1 -1
- package/moj/all.scss +2 -0
- package/moj/all.scss.map +1 -0
- package/moj/components/_all.scss +2 -0
- package/moj/components/_all.scss.map +1 -0
- package/moj/components/action-bar/_action-bar.scss +2 -0
- package/moj/components/action-bar/_action-bar.scss.map +1 -0
- package/moj/components/add-another/_add-another.scss +2 -0
- package/moj/components/add-another/_add-another.scss.map +1 -0
- package/moj/components/add-another/add-another.bundle.js +128 -0
- package/moj/components/add-another/add-another.bundle.js.map +1 -0
- package/moj/components/add-another/add-another.bundle.mjs +120 -0
- package/moj/components/add-another/add-another.bundle.mjs.map +1 -0
- package/moj/components/add-another/add-another.mjs +112 -99
- package/moj/components/add-another/add-another.mjs.map +1 -1
- package/moj/components/alert/_alert.scss +4 -0
- package/moj/components/alert/_alert.scss.map +1 -0
- package/moj/components/alert/alert.bundle.js +330 -0
- package/moj/components/alert/alert.bundle.js.map +1 -0
- package/moj/components/alert/alert.bundle.mjs +322 -0
- package/moj/components/alert/alert.bundle.mjs.map +1 -0
- package/moj/components/alert/alert.mjs +181 -217
- package/moj/components/alert/alert.mjs.map +1 -1
- package/moj/components/alert/{alert.spec.helper.js → alert.spec.helper.bundle.js} +1 -1
- package/moj/components/alert/alert.spec.helper.bundle.js.map +1 -0
- package/moj/components/alert/alert.spec.helper.bundle.mjs +67 -0
- package/moj/components/alert/alert.spec.helper.bundle.mjs.map +1 -0
- package/moj/components/alert/alert.spec.helper.mjs.map +1 -1
- package/moj/components/badge/_badge.scss +2 -0
- package/moj/components/badge/_badge.scss.map +1 -0
- package/moj/components/banner/_banner.scss +2 -0
- package/moj/components/banner/_banner.scss.map +1 -0
- package/moj/components/button-menu/README.md +10 -6
- package/moj/components/button-menu/_button-menu.scss +4 -1
- package/moj/components/button-menu/_button-menu.scss.map +1 -0
- package/moj/components/button-menu/button-menu.bundle.js +299 -0
- package/moj/components/button-menu/button-menu.bundle.js.map +1 -0
- package/moj/components/button-menu/{button-menu.js → button-menu.bundle.mjs} +74 -121
- package/moj/components/button-menu/button-menu.bundle.mjs.map +1 -0
- package/moj/components/button-menu/button-menu.mjs +246 -285
- package/moj/components/button-menu/button-menu.mjs.map +1 -1
- package/moj/components/cookie-banner/_cookie-banner.scss +2 -0
- package/moj/components/cookie-banner/_cookie-banner.scss.map +1 -0
- package/moj/components/currency-input/_currency-input.scss +2 -0
- package/moj/components/currency-input/_currency-input.scss.map +1 -0
- package/moj/components/date-picker/_date-picker.scss +2 -0
- package/moj/components/date-picker/_date-picker.scss.map +1 -0
- package/moj/components/date-picker/date-picker.bundle.js +784 -0
- package/moj/components/date-picker/date-picker.bundle.js.map +1 -0
- package/moj/components/date-picker/{date-picker.js → date-picker.bundle.mjs} +245 -439
- package/moj/components/date-picker/date-picker.bundle.mjs.map +1 -0
- package/moj/components/date-picker/date-picker.mjs +654 -840
- package/moj/components/date-picker/date-picker.mjs.map +1 -1
- package/moj/components/filter/_filter.scss +2 -0
- package/moj/components/filter/_filter.scss.map +1 -0
- package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js +96 -0
- package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js.map +1 -0
- package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs +88 -0
- package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs.map +1 -0
- package/moj/components/filter-toggle-button/filter-toggle-button.mjs +78 -84
- package/moj/components/filter-toggle-button/filter-toggle-button.mjs.map +1 -1
- package/moj/components/form-validator/form-validator.bundle.js +198 -0
- package/moj/components/form-validator/form-validator.bundle.js.map +1 -0
- package/moj/components/form-validator/form-validator.bundle.mjs +190 -0
- package/moj/components/form-validator/form-validator.bundle.mjs.map +1 -0
- package/moj/components/form-validator/form-validator.mjs +149 -152
- package/moj/components/form-validator/form-validator.mjs.map +1 -1
- package/moj/components/header/_header.scss +2 -0
- package/moj/components/header/_header.scss.map +1 -0
- package/moj/components/identity-bar/_identity-bar.scss +2 -0
- package/moj/components/identity-bar/_identity-bar.scss.map +1 -0
- package/moj/components/interruption-card/_interruption-card.scss +2 -0
- package/moj/components/interruption-card/_interruption-card.scss.map +1 -0
- package/moj/components/messages/_messages.scss +2 -0
- package/moj/components/messages/_messages.scss.map +1 -0
- package/moj/components/multi-file-upload/_multi-file-upload.scss +2 -0
- package/moj/components/multi-file-upload/_multi-file-upload.scss.map +1 -0
- package/moj/components/multi-file-upload/multi-file-upload.bundle.js +223 -0
- package/moj/components/multi-file-upload/multi-file-upload.bundle.js.map +1 -0
- package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs +215 -0
- package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs.map +1 -0
- package/moj/components/multi-file-upload/multi-file-upload.mjs +193 -209
- package/moj/components/multi-file-upload/multi-file-upload.mjs.map +1 -1
- package/moj/components/multi-select/_multi-select.scss +2 -0
- package/moj/components/multi-select/_multi-select.scss.map +1 -0
- package/moj/components/multi-select/multi-select.bundle.js +78 -0
- package/moj/components/multi-select/multi-select.bundle.js.map +1 -0
- package/moj/components/multi-select/multi-select.bundle.mjs +70 -0
- package/moj/components/multi-select/multi-select.bundle.mjs.map +1 -0
- package/moj/components/multi-select/multi-select.mjs +59 -67
- package/moj/components/multi-select/multi-select.mjs.map +1 -1
- package/moj/components/notification-badge/_notification-badge.scss +2 -0
- package/moj/components/notification-badge/_notification-badge.scss.map +1 -0
- package/moj/components/organisation-switcher/_organisation-switcher.scss +2 -0
- package/moj/components/organisation-switcher/_organisation-switcher.scss.map +1 -0
- package/moj/components/page-header-actions/_page-header-actions.scss +2 -0
- package/moj/components/page-header-actions/_page-header-actions.scss.map +1 -0
- package/moj/components/pagination/_pagination.scss +2 -2
- package/moj/components/pagination/_pagination.scss.map +1 -0
- package/moj/components/password-reveal/_password-reveal.scss +2 -0
- package/moj/components/password-reveal/_password-reveal.scss.map +1 -0
- package/moj/components/password-reveal/password-reveal.bundle.js +49 -0
- package/moj/components/password-reveal/password-reveal.bundle.js.map +1 -0
- package/moj/components/password-reveal/password-reveal.bundle.mjs +41 -0
- package/moj/components/password-reveal/password-reveal.bundle.mjs.map +1 -0
- package/moj/components/password-reveal/password-reveal.mjs +36 -31
- package/moj/components/password-reveal/password-reveal.mjs.map +1 -1
- package/moj/components/primary-navigation/_primary-navigation.scss +2 -0
- package/moj/components/primary-navigation/_primary-navigation.scss.map +1 -0
- package/moj/components/progress-bar/_progress-bar.scss +2 -0
- package/moj/components/progress-bar/_progress-bar.scss.map +1 -0
- package/moj/components/rich-text-editor/README.md +15 -9
- package/moj/components/rich-text-editor/_rich-text-editor.scss +2 -0
- package/moj/components/rich-text-editor/_rich-text-editor.scss.map +1 -0
- package/moj/components/rich-text-editor/rich-text-editor.bundle.js +145 -0
- package/moj/components/rich-text-editor/rich-text-editor.bundle.js.map +1 -0
- package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs +137 -0
- package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs.map +1 -0
- package/moj/components/rich-text-editor/rich-text-editor.mjs +124 -145
- package/moj/components/rich-text-editor/rich-text-editor.mjs.map +1 -1
- package/moj/components/search/_search.scss +2 -0
- package/moj/components/search/_search.scss.map +1 -0
- package/moj/components/search-toggle/{search-toggle.scss → _search-toggle.scss} +2 -0
- package/moj/components/search-toggle/_search-toggle.scss.map +1 -0
- package/moj/components/search-toggle/search-toggle.bundle.js +54 -0
- package/moj/components/search-toggle/search-toggle.bundle.js.map +1 -0
- package/moj/components/search-toggle/search-toggle.bundle.mjs +46 -0
- package/moj/components/search-toggle/search-toggle.bundle.mjs.map +1 -0
- package/moj/components/search-toggle/search-toggle.mjs +40 -49
- package/moj/components/search-toggle/search-toggle.mjs.map +1 -1
- package/moj/components/side-navigation/_side-navigation.scss +2 -0
- package/moj/components/side-navigation/_side-navigation.scss.map +1 -0
- package/moj/components/sortable-table/_sortable-table.scss +2 -2
- package/moj/components/sortable-table/_sortable-table.scss.map +1 -0
- package/moj/components/sortable-table/sortable-table.bundle.js +134 -0
- package/moj/components/sortable-table/sortable-table.bundle.js.map +1 -0
- package/moj/components/sortable-table/sortable-table.bundle.mjs +126 -0
- package/moj/components/sortable-table/sortable-table.bundle.mjs.map +1 -0
- package/moj/components/sortable-table/sortable-table.mjs +117 -130
- package/moj/components/sortable-table/sortable-table.mjs.map +1 -1
- package/moj/components/sub-navigation/_sub-navigation.scss +2 -0
- package/moj/components/sub-navigation/_sub-navigation.scss.map +1 -0
- package/moj/components/tag/_tag.scss +2 -0
- package/moj/components/tag/_tag.scss.map +1 -0
- package/moj/components/task-list/_task-list.scss +2 -0
- package/moj/components/task-list/_task-list.scss.map +1 -0
- package/moj/components/ticket-panel/_ticket-panel.scss +2 -0
- package/moj/components/ticket-panel/_ticket-panel.scss.map +1 -0
- package/moj/components/timeline/_timeline.scss +2 -0
- package/moj/components/timeline/_timeline.scss.map +1 -0
- package/moj/filters/all.js +44 -22
- package/moj/helpers/_all.scss +2 -0
- package/moj/helpers/_all.scss.map +1 -0
- package/moj/helpers/_hidden.scss +2 -0
- package/moj/helpers/_hidden.scss.map +1 -0
- package/moj/helpers/_links.scss +2 -0
- package/moj/helpers/_links.scss.map +1 -0
- package/moj/{helpers.js → helpers.bundle.js} +37 -42
- package/moj/helpers.bundle.js.map +1 -0
- package/moj/helpers.bundle.mjs +179 -0
- package/moj/helpers.bundle.mjs.map +1 -0
- package/moj/helpers.mjs +52 -28
- package/moj/helpers.mjs.map +1 -1
- package/moj/init.js +11 -2
- package/moj/moj-frontend.min.css +1 -1
- package/moj/moj-frontend.min.css.map +1 -1
- package/moj/moj-frontend.min.js +1 -1
- package/moj/moj-frontend.min.js.map +1 -1
- package/moj/objects/_all.scss +2 -0
- package/moj/objects/_all.scss.map +1 -0
- package/moj/objects/_button-group.scss +2 -0
- package/moj/objects/_button-group.scss.map +1 -0
- package/moj/objects/_filter-layout.scss +2 -0
- package/moj/objects/_filter-layout.scss.map +1 -0
- package/moj/objects/_scrollable-pane.scss +2 -0
- package/moj/objects/_scrollable-pane.scss.map +1 -0
- package/moj/objects/_width-container.scss +2 -0
- package/moj/objects/_width-container.scss.map +1 -0
- package/moj/settings/_all.scss +2 -0
- package/moj/settings/_all.scss.map +1 -0
- package/moj/settings/_assets.scss +2 -0
- package/moj/settings/_assets.scss.map +1 -0
- package/moj/settings/_colours.scss +2 -0
- package/moj/settings/_colours.scss.map +1 -0
- package/moj/settings/_measurements.scss +2 -0
- package/moj/settings/_measurements.scss.map +1 -0
- package/moj/settings/_typography.scss +2 -0
- package/moj/settings/_typography.scss.map +1 -0
- package/moj/template.njk +13 -0
- package/moj/utilities/_all.scss +2 -0
- package/moj/utilities/_all.scss.map +1 -0
- package/moj/utilities/_hidden.scss +2 -0
- package/moj/utilities/_hidden.scss.map +1 -0
- package/moj/utilities/_width-container.scss +2 -0
- package/moj/utilities/_width-container.scss.map +1 -0
- package/moj/vendor/govuk-frontend/_base.scss +2 -0
- package/moj/vendor/govuk-frontend/_base.scss.map +1 -0
- package/moj/vendor/govuk-frontend/_index.scss +2 -0
- package/moj/vendor/govuk-frontend/_index.scss.map +1 -0
- package/moj/{version.js → version.bundle.js} +1 -1
- package/moj/version.bundle.js.map +1 -0
- package/moj/version.bundle.mjs +4 -0
- package/moj/version.bundle.mjs.map +1 -0
- package/moj/version.mjs.map +1 -1
- package/package.json +5 -6
- package/moj/all.jquery.min.js +0 -1
- package/moj/all.jquery.min.js.map +0 -1
- package/moj/all.js +0 -2662
- package/moj/all.js.map +0 -1
- package/moj/components/add-another/add-another.js +0 -115
- package/moj/components/add-another/add-another.js.map +0 -1
- package/moj/components/alert/alert.js +0 -356
- package/moj/components/alert/alert.js.map +0 -1
- package/moj/components/alert/alert.spec.helper.js.map +0 -1
- package/moj/components/button-menu/button-menu.js.map +0 -1
- package/moj/components/date-picker/date-picker.js.map +0 -1
- package/moj/components/filter-toggle-button/filter-toggle-button.js +0 -102
- package/moj/components/filter-toggle-button/filter-toggle-button.js.map +0 -1
- package/moj/components/form-validator/form-validator.js +0 -205
- package/moj/components/form-validator/form-validator.js.map +0 -1
- package/moj/components/multi-file-upload/multi-file-upload.js +0 -241
- package/moj/components/multi-file-upload/multi-file-upload.js.map +0 -1
- package/moj/components/multi-select/multi-select.js +0 -86
- package/moj/components/multi-select/multi-select.js.map +0 -1
- package/moj/components/password-reveal/password-reveal.js +0 -44
- package/moj/components/password-reveal/password-reveal.js.map +0 -1
- package/moj/components/rich-text-editor/rich-text-editor.js +0 -166
- package/moj/components/rich-text-editor/rich-text-editor.js.map +0 -1
- package/moj/components/search-toggle/search-toggle.js +0 -63
- package/moj/components/search-toggle/search-toggle.js.map +0 -1
- package/moj/components/sortable-table/sortable-table.js +0 -147
- package/moj/components/sortable-table/sortable-table.js.map +0 -1
- package/moj/helpers.js.map +0 -1
- package/moj/vendor/html5shiv.js +0 -326
- package/moj/vendor/jquery.js +0 -9300
- package/moj/version.js.map +0 -1
|
@@ -1,134 +1,112 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* @param {HTMLElement} $module
|
|
10
|
-
* @param {ButtonMenuConfig} config
|
|
11
|
-
* @class
|
|
12
|
-
*/
|
|
13
|
-
function ButtonMenu($module, config = {}) {
|
|
14
|
-
if (!$module) {
|
|
15
|
-
return this
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const schema = Object.freeze({
|
|
19
|
-
properties: {
|
|
20
|
-
buttonText: { type: 'string' },
|
|
21
|
-
buttonClasses: { type: 'string' },
|
|
22
|
-
alignMenu: { type: 'string' }
|
|
1
|
+
class ButtonMenu {
|
|
2
|
+
/**
|
|
3
|
+
* @param {Element | null} $module - HTML element to use for button menu
|
|
4
|
+
* @param {ButtonMenuConfig} [config] - Button menu config
|
|
5
|
+
*/
|
|
6
|
+
constructor($module, config = {}) {
|
|
7
|
+
if (!$module || !($module instanceof HTMLElement)) {
|
|
8
|
+
return this;
|
|
23
9
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this.parseDataset(schema, $module.dataset)
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
this.$module = $module;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
ButtonMenu.prototype.init = function () {
|
|
42
|
-
// If only one button is provided, don't initiate a menu and toggle button
|
|
43
|
-
// if classes have been provided for the toggleButton, apply them to the single item
|
|
44
|
-
if (this.$module.children.length === 1) {
|
|
45
|
-
const button = this.$module.children[0];
|
|
46
|
-
button.classList.forEach((className) => {
|
|
47
|
-
if (className.startsWith('govuk-button-')) {
|
|
48
|
-
button.classList.remove(className);
|
|
10
|
+
const schema = Object.freeze({
|
|
11
|
+
properties: {
|
|
12
|
+
buttonText: {
|
|
13
|
+
type: 'string'
|
|
14
|
+
},
|
|
15
|
+
buttonClasses: {
|
|
16
|
+
type: 'string'
|
|
17
|
+
},
|
|
18
|
+
alignMenu: {
|
|
19
|
+
type: 'string'
|
|
20
|
+
}
|
|
49
21
|
}
|
|
50
|
-
button.classList.remove('moj-button-menu__item');
|
|
51
22
|
});
|
|
52
|
-
|
|
53
|
-
|
|
23
|
+
const defaults = {
|
|
24
|
+
buttonText: 'Actions',
|
|
25
|
+
alignMenu: 'left',
|
|
26
|
+
buttonClasses: ''
|
|
27
|
+
};
|
|
28
|
+
// data attributes override JS config, which overrides defaults
|
|
29
|
+
this.config = this.mergeConfigs(defaults, config, this.parseDataset(schema, $module.dataset));
|
|
30
|
+
this.$module = $module;
|
|
31
|
+
|
|
32
|
+
// If only one button is provided, don't initiate a menu and toggle button
|
|
33
|
+
// if classes have been provided for the toggleButton, apply them to the single item
|
|
34
|
+
if (this.$module.children.length === 1) {
|
|
35
|
+
const button = this.$module.children[0];
|
|
36
|
+
button.classList.forEach(className => {
|
|
37
|
+
if (className.startsWith('govuk-button-')) {
|
|
38
|
+
button.classList.remove(className);
|
|
39
|
+
}
|
|
40
|
+
button.classList.remove('moj-button-menu__item');
|
|
41
|
+
button.classList.add('moj-button-menu__single-button');
|
|
42
|
+
});
|
|
43
|
+
if (this.config.buttonClasses) {
|
|
44
|
+
button.classList.add(...this.config.buttonClasses.split(' '));
|
|
45
|
+
}
|
|
54
46
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
this.initMenu();
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
ButtonMenu.prototype.initMenu = function () {
|
|
63
|
-
this.$menu = this.createMenu();
|
|
64
|
-
this.$module.insertAdjacentHTML('afterbegin', this.toggleTemplate());
|
|
65
|
-
this.setupMenuItems();
|
|
66
|
-
|
|
67
|
-
this.$menuToggle = this.$module.querySelector(':scope > button');
|
|
68
|
-
this.items = this.$menu.querySelectorAll('a, button');
|
|
69
|
-
|
|
70
|
-
this.$menuToggle.addEventListener('click', (event) => {
|
|
71
|
-
this.toggleMenu(event);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
this.$module.addEventListener('keydown', (event) => {
|
|
75
|
-
this.handleKeyDown(event);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
document.addEventListener('click', (event) => {
|
|
79
|
-
if (!this.$module.contains(event.target)) {
|
|
80
|
-
this.closeMenu(false);
|
|
47
|
+
// Otherwise intialise a button menu
|
|
48
|
+
if (this.$module.children.length > 1) {
|
|
49
|
+
this.initMenu();
|
|
81
50
|
}
|
|
82
|
-
});
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
ButtonMenu.prototype.createMenu = function () {
|
|
86
|
-
const $menu = document.createElement('ul');
|
|
87
|
-
$menu.setAttribute('role', 'list');
|
|
88
|
-
$menu.hidden = true;
|
|
89
|
-
$menu.classList.add('moj-button-menu__wrapper');
|
|
90
|
-
if (this.config.alignMenu === 'right') {
|
|
91
|
-
$menu.classList.add('moj-button-menu__wrapper--right');
|
|
92
51
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
52
|
+
initMenu() {
|
|
53
|
+
this.$menu = this.createMenu();
|
|
54
|
+
this.$module.insertAdjacentHTML('afterbegin', this.toggleTemplate());
|
|
55
|
+
this.setupMenuItems();
|
|
56
|
+
this.$menuToggle = this.$module.querySelector(':scope > button');
|
|
57
|
+
this.items = this.$menu.querySelectorAll('a, button');
|
|
58
|
+
this.$menuToggle.addEventListener('click', event => {
|
|
59
|
+
this.toggleMenu(event);
|
|
60
|
+
});
|
|
61
|
+
this.$module.addEventListener('keydown', event => {
|
|
62
|
+
this.handleKeyDown(event);
|
|
63
|
+
});
|
|
64
|
+
document.addEventListener('click', event => {
|
|
65
|
+
if (!this.$module.contains(event.target)) {
|
|
66
|
+
this.closeMenu(false);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
97
69
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const listItem = document.createElement('li');
|
|
106
|
-
this.$menu.insertBefore(listItem, item);
|
|
107
|
-
listItem.appendChild(item);
|
|
108
|
-
|
|
109
|
-
item.setAttribute('tabindex', -1);
|
|
110
|
-
|
|
111
|
-
if (item.tagName === 'BUTTON') {
|
|
112
|
-
item.setAttribute('type', 'button');
|
|
70
|
+
createMenu() {
|
|
71
|
+
const $menu = document.createElement('ul');
|
|
72
|
+
$menu.setAttribute('role', 'list');
|
|
73
|
+
$menu.hidden = true;
|
|
74
|
+
$menu.classList.add('moj-button-menu__wrapper');
|
|
75
|
+
if (this.config.alignMenu === 'right') {
|
|
76
|
+
$menu.classList.add('moj-button-menu__wrapper--right');
|
|
113
77
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
78
|
+
this.$module.appendChild($menu);
|
|
79
|
+
while (this.$module.firstChild !== $menu) {
|
|
80
|
+
$menu.appendChild(this.$module.firstChild);
|
|
81
|
+
}
|
|
82
|
+
return $menu;
|
|
83
|
+
}
|
|
84
|
+
setupMenuItems() {
|
|
85
|
+
Array.from(this.$menu.children).forEach(item => {
|
|
86
|
+
// wrap item in li tag
|
|
87
|
+
const listItem = document.createElement('li');
|
|
88
|
+
this.$menu.insertBefore(listItem, item);
|
|
89
|
+
listItem.appendChild(item);
|
|
90
|
+
item.setAttribute('tabindex', '-1');
|
|
91
|
+
if (item.tagName === 'BUTTON') {
|
|
92
|
+
item.setAttribute('type', 'button');
|
|
118
93
|
}
|
|
94
|
+
item.classList.forEach(className => {
|
|
95
|
+
if (className.startsWith('govuk-button')) {
|
|
96
|
+
item.classList.remove(className);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// add a slight delay after click before closing the menu, makes it *feel* better
|
|
101
|
+
item.addEventListener('click', () => {
|
|
102
|
+
setTimeout(() => {
|
|
103
|
+
this.closeMenu(false);
|
|
104
|
+
}, 50);
|
|
105
|
+
});
|
|
119
106
|
});
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
setTimeout(() => {
|
|
124
|
-
this.closeMenu(false);
|
|
125
|
-
}, 50);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
ButtonMenu.prototype.toggleTemplate = function () {
|
|
131
|
-
return `
|
|
107
|
+
}
|
|
108
|
+
toggleTemplate() {
|
|
109
|
+
return `
|
|
132
110
|
<button type="button" class="govuk-button moj-button-menu__toggle-button ${this.config.buttonClasses || ''}" aria-haspopup="true" aria-expanded="false">
|
|
133
111
|
<span>
|
|
134
112
|
${this.config.buttonText}
|
|
@@ -136,194 +114,177 @@ ButtonMenu.prototype.toggleTemplate = function () {
|
|
|
136
114
|
<path d="M5.5 0L11 5L0 5L5.5 0Z" fill="currentColor"/>
|
|
137
115
|
</svg>
|
|
138
116
|
</span>
|
|
139
|
-
</button
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* @returns {boolean}
|
|
144
|
-
*/
|
|
145
|
-
ButtonMenu.prototype.isOpen = function () {
|
|
146
|
-
return this.$menuToggle.getAttribute('aria-expanded') === 'true'
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
ButtonMenu.prototype.toggleMenu = function (event) {
|
|
150
|
-
event.preventDefault();
|
|
151
|
-
|
|
152
|
-
// If menu is triggered with mouse don't move focus to first item
|
|
153
|
-
const keyboardEvent = event.detail === 0;
|
|
154
|
-
const focusIndex = keyboardEvent ? 0 : -1;
|
|
155
|
-
|
|
156
|
-
if (this.isOpen()) {
|
|
157
|
-
this.closeMenu();
|
|
158
|
-
} else {
|
|
159
|
-
this.openMenu(focusIndex);
|
|
117
|
+
</button>`;
|
|
160
118
|
}
|
|
161
|
-
};
|
|
162
119
|
|
|
163
|
-
/**
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
ButtonMenu.prototype.openMenu = function (focusIndex = 0) {
|
|
169
|
-
this.$menu.hidden = false;
|
|
170
|
-
this.$menuToggle.setAttribute('aria-expanded', 'true');
|
|
171
|
-
if (focusIndex !== -1) {
|
|
172
|
-
this.focusItem(focusIndex);
|
|
120
|
+
/**
|
|
121
|
+
* @returns {boolean}
|
|
122
|
+
*/
|
|
123
|
+
isOpen() {
|
|
124
|
+
return this.$menuToggle.getAttribute('aria-expanded') === 'true';
|
|
173
125
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
this.$menuToggle.focus();
|
|
126
|
+
toggleMenu(event) {
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
|
|
129
|
+
// If menu is triggered with mouse don't move focus to first item
|
|
130
|
+
const keyboardEvent = event.detail === 0;
|
|
131
|
+
const focusIndex = keyboardEvent ? 0 : -1;
|
|
132
|
+
if (this.isOpen()) {
|
|
133
|
+
this.closeMenu();
|
|
134
|
+
} else {
|
|
135
|
+
this.openMenu(focusIndex);
|
|
136
|
+
}
|
|
186
137
|
}
|
|
187
|
-
};
|
|
188
138
|
|
|
189
|
-
/**
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
menuItem.focus();
|
|
139
|
+
/**
|
|
140
|
+
* Opens the menu and optionally sets the focus to the item with given index
|
|
141
|
+
*
|
|
142
|
+
* @param {number} focusIndex - The index of the item to focus
|
|
143
|
+
*/
|
|
144
|
+
openMenu(focusIndex = 0) {
|
|
145
|
+
this.$menu.hidden = false;
|
|
146
|
+
this.$menuToggle.setAttribute('aria-expanded', 'true');
|
|
147
|
+
if (focusIndex !== -1) {
|
|
148
|
+
this.focusItem(focusIndex);
|
|
149
|
+
}
|
|
201
150
|
}
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
ButtonMenu.prototype.currentFocusIndex = function () {
|
|
205
|
-
const activeElement = document.activeElement;
|
|
206
|
-
const menuItems = Array.from(this.items);
|
|
207
|
-
|
|
208
|
-
return menuItems.indexOf(activeElement)
|
|
209
|
-
};
|
|
210
151
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
break
|
|
152
|
+
/**
|
|
153
|
+
* Closes the menu and optionally returns focus back to menuToggle
|
|
154
|
+
*
|
|
155
|
+
* @param {boolean} moveFocus - whether to return focus to the toggle button
|
|
156
|
+
*/
|
|
157
|
+
closeMenu(moveFocus = true) {
|
|
158
|
+
this.$menu.hidden = true;
|
|
159
|
+
this.$menuToggle.setAttribute('aria-expanded', 'false');
|
|
160
|
+
if (moveFocus) {
|
|
161
|
+
this.$menuToggle.focus();
|
|
222
162
|
}
|
|
223
163
|
}
|
|
224
164
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
this.focusItem(this.currentFocusIndex() - 1);
|
|
237
|
-
}
|
|
238
|
-
break
|
|
239
|
-
case 'Home':
|
|
240
|
-
event.preventDefault();
|
|
241
|
-
this.focusItem(0);
|
|
242
|
-
break
|
|
243
|
-
case 'End':
|
|
244
|
-
event.preventDefault();
|
|
245
|
-
this.focusItem(this.items.length - 1);
|
|
246
|
-
break
|
|
165
|
+
/**
|
|
166
|
+
* Focuses the menu item at the specified index
|
|
167
|
+
*
|
|
168
|
+
* @param {number} index - the index of the item to focus
|
|
169
|
+
*/
|
|
170
|
+
focusItem(index) {
|
|
171
|
+
if (index >= this.items.length) index = 0;
|
|
172
|
+
if (index < 0) index = this.items.length - 1;
|
|
173
|
+
const menuItem = this.items.item(index);
|
|
174
|
+
if (menuItem) {
|
|
175
|
+
menuItem.focus();
|
|
247
176
|
}
|
|
248
177
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
this.
|
|
178
|
+
currentFocusIndex() {
|
|
179
|
+
const activeElement = document.activeElement;
|
|
180
|
+
const menuItems = Array.from(this.items);
|
|
181
|
+
return menuItems.indexOf(activeElement);
|
|
252
182
|
}
|
|
253
|
-
|
|
254
|
-
this
|
|
183
|
+
handleKeyDown(event) {
|
|
184
|
+
if (event.target === this.$menuToggle) {
|
|
185
|
+
switch (event.key) {
|
|
186
|
+
case 'ArrowDown':
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
this.openMenu();
|
|
189
|
+
break;
|
|
190
|
+
case 'ArrowUp':
|
|
191
|
+
event.preventDefault();
|
|
192
|
+
this.openMenu(this.items.length - 1);
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (this.$menu.contains(event.target) && this.isOpen()) {
|
|
197
|
+
switch (event.key) {
|
|
198
|
+
case 'ArrowDown':
|
|
199
|
+
event.preventDefault();
|
|
200
|
+
if (this.currentFocusIndex() !== -1) {
|
|
201
|
+
this.focusItem(this.currentFocusIndex() + 1);
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
case 'ArrowUp':
|
|
205
|
+
event.preventDefault();
|
|
206
|
+
if (this.currentFocusIndex() !== -1) {
|
|
207
|
+
this.focusItem(this.currentFocusIndex() - 1);
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
case 'Home':
|
|
211
|
+
event.preventDefault();
|
|
212
|
+
this.focusItem(0);
|
|
213
|
+
break;
|
|
214
|
+
case 'End':
|
|
215
|
+
event.preventDefault();
|
|
216
|
+
this.focusItem(this.items.length - 1);
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (event.key === 'Escape' && this.isOpen()) {
|
|
221
|
+
this.closeMenu();
|
|
222
|
+
}
|
|
223
|
+
if (event.key === 'Tab' && this.isOpen()) {
|
|
224
|
+
this.closeMenu(false);
|
|
225
|
+
}
|
|
255
226
|
}
|
|
256
|
-
};
|
|
257
227
|
|
|
258
|
-
/**
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
228
|
+
/**
|
|
229
|
+
* Parse dataset
|
|
230
|
+
*
|
|
231
|
+
* Loop over an object and normalise each value using {@link normaliseString},
|
|
232
|
+
* optionally expanding nested `i18n.field`
|
|
233
|
+
*
|
|
234
|
+
* @param {Schema} schema - component schema
|
|
235
|
+
* @param {DOMStringMap} dataset - HTML element dataset
|
|
236
|
+
* @returns {object} Normalised dataset
|
|
237
|
+
*/
|
|
238
|
+
parseDataset(schema, dataset) {
|
|
239
|
+
const parsed = {};
|
|
240
|
+
for (const [field,,] of Object.entries(schema.properties)) {
|
|
241
|
+
if (field in dataset) {
|
|
242
|
+
if (dataset[field]) {
|
|
243
|
+
parsed[field] = dataset[field];
|
|
244
|
+
}
|
|
275
245
|
}
|
|
276
246
|
}
|
|
247
|
+
return parsed;
|
|
277
248
|
}
|
|
278
249
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
} else {
|
|
307
|
-
formattedConfigObject[key] = override;
|
|
250
|
+
/**
|
|
251
|
+
* Config merging function
|
|
252
|
+
*
|
|
253
|
+
* Takes any number of objects and combines them together, with
|
|
254
|
+
* greatest priority on the LAST item passed in.
|
|
255
|
+
*
|
|
256
|
+
* @param {...{ [key: string]: unknown }} configObjects - Config objects to merge
|
|
257
|
+
* @returns {{ [key: string]: unknown }} A merged config object
|
|
258
|
+
*/
|
|
259
|
+
mergeConfigs(...configObjects) {
|
|
260
|
+
const formattedConfigObject = {};
|
|
261
|
+
|
|
262
|
+
// Loop through each of the passed objects
|
|
263
|
+
for (const configObject of configObjects) {
|
|
264
|
+
for (const key of Object.keys(configObject)) {
|
|
265
|
+
const option = formattedConfigObject[key];
|
|
266
|
+
const override = configObject[key];
|
|
267
|
+
|
|
268
|
+
// Push their keys one-by-one into formattedConfigObject. Any duplicate
|
|
269
|
+
// keys with object values will be merged, otherwise the new value will
|
|
270
|
+
// override the existing value.
|
|
271
|
+
if (typeof option === 'object' && typeof override === 'object') {
|
|
272
|
+
// @ts-expect-error Index signature for type 'string' is missing
|
|
273
|
+
formattedConfigObject[key] = this.mergeConfigs(option, override);
|
|
274
|
+
} else {
|
|
275
|
+
formattedConfigObject[key] = override;
|
|
276
|
+
}
|
|
308
277
|
}
|
|
309
278
|
}
|
|
279
|
+
return formattedConfigObject;
|
|
310
280
|
}
|
|
311
|
-
|
|
312
|
-
return formattedConfigObject
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Schema for component config
|
|
317
|
-
*
|
|
318
|
-
* @typedef {object} Schema
|
|
319
|
-
* @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
|
|
320
|
-
*/
|
|
281
|
+
}
|
|
321
282
|
|
|
322
283
|
/**
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
* @
|
|
326
|
-
* @property {
|
|
284
|
+
* @typedef {object} ButtonMenuConfig
|
|
285
|
+
* @property {string} [buttonText='Actions'] - Label for the toggle button
|
|
286
|
+
* @property {"left" | "right"} [alignMenu='left'] - the alignment of the menu
|
|
287
|
+
* @property {string} [buttonClasses='govuk-button--secondary'] - css classes applied to the toggle button
|
|
327
288
|
*/
|
|
328
289
|
|
|
329
290
|
export { ButtonMenu };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button-menu.mjs","sources":["../../../../src/moj/components/button-menu/button-menu.mjs"],"sourcesContent":["/**\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 * @param {HTMLElement} $module\n * @param {ButtonMenuConfig} config\n * @class\n */\nexport function ButtonMenu($module, config = {}) {\n if (!$module) {\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\nButtonMenu.prototype.init = function () {\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 })\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\nButtonMenu.prototype.initMenu = function () {\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\nButtonMenu.prototype.createMenu = function () {\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\nButtonMenu.prototype.setupMenuItems = function () {\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', (event) => {\n setTimeout(() => {\n this.closeMenu(false)\n }, 50)\n })\n })\n}\n\nButtonMenu.prototype.toggleTemplate = function () {\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 */\nButtonMenu.prototype.isOpen = function () {\n return this.$menuToggle.getAttribute('aria-expanded') === 'true'\n}\n\nButtonMenu.prototype.toggleMenu = function (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 */\nButtonMenu.prototype.openMenu = function (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 */\nButtonMenu.prototype.closeMenu = function (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 */\nButtonMenu.prototype.focusItem = function (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\nButtonMenu.prototype.currentFocusIndex = function () {\n const activeElement = document.activeElement\n const menuItems = Array.from(this.items)\n\n return menuItems.indexOf(activeElement)\n}\n\nButtonMenu.prototype.handleKeyDown = function (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 */\nButtonMenu.prototype.parseDataset = function (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 */\nButtonMenu.prototype.mergeConfigs = function (...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 * Schema for component config\n *\n * @typedef {object} Schema\n * @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties\n */\n\n/**\n * Schema property for component config\n *\n * @typedef {object} SchemaProperty\n * @property {'string' | 'boolean' | 'number' | 'object'} type - Property type\n */\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"button-menu.mjs","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":"AAAO,MAAMA,UAAU,CAAC;AACtB;AACF;AACA;AACA;AACEC,EAAAA,WAAWA,CAACC,OAAO,EAAEC,MAAM,GAAG,EAAE,EAAE;IAChC,IAAI,CAACD,OAAO,IAAI,EAAEA,OAAO,YAAYE,WAAW,CAAC,EAAE;AACjD,MAAA,OAAO,IAAI;AACb;AAEA,IAAA,MAAMC,MAAM,GAAGC,MAAM,CAACC,MAAM,CAAC;AAC3BC,MAAAA,UAAU,EAAE;AACVC,QAAAA,UAAU,EAAE;AAAEC,UAAAA,IAAI,EAAE;SAAU;AAC9BC,QAAAA,aAAa,EAAE;AAAED,UAAAA,IAAI,EAAE;SAAU;AACjCE,QAAAA,SAAS,EAAE;AAAEF,UAAAA,IAAI,EAAE;AAAS;AAC9B;AACF,KAAC,CAAC;AAEF,IAAA,MAAMG,QAAQ,GAAG;AACfJ,MAAAA,UAAU,EAAE,SAAS;AACrBG,MAAAA,SAAS,EAAE,MAAM;AACjBD,MAAAA,aAAa,EAAE;KAChB;AACD;IACA,IAAI,CAACR,MAAM,GAAG,IAAI,CAACW,YAAY,CAC7BD,QAAQ,EACRV,MAAM,EACN,IAAI,CAACY,YAAY,CAACV,MAAM,EAAEH,OAAO,CAACc,OAAO,CAC3C,CAAC;IAED,IAAI,CAACd,OAAO,GAAGA,OAAO;;AAEtB;AACA;IACA,IAAI,IAAI,CAACA,OAAO,CAACe,QAAQ,CAACC,MAAM,KAAK,CAAC,EAAE;MACtC,MAAMC,MAAM,GAAG,IAAI,CAACjB,OAAO,CAACe,QAAQ,CAAC,CAAC,CAAC;AACvCE,MAAAA,MAAM,CAACC,SAAS,CAACC,OAAO,CAAEC,SAAS,IAAK;AACtC,QAAA,IAAIA,SAAS,CAACC,UAAU,CAAC,eAAe,CAAC,EAAE;AACzCJ,UAAAA,MAAM,CAACC,SAAS,CAACI,MAAM,CAACF,SAAS,CAAC;AACpC;AACAH,QAAAA,MAAM,CAACC,SAAS,CAACI,MAAM,CAAC,uBAAuB,CAAC;AAChDL,QAAAA,MAAM,CAACC,SAAS,CAACK,GAAG,CAAC,gCAAgC,CAAC;AACxD,OAAC,CAAC;AACF,MAAA,IAAI,IAAI,CAACtB,MAAM,CAACQ,aAAa,EAAE;AAC7BQ,QAAAA,MAAM,CAACC,SAAS,CAACK,GAAG,CAAC,GAAG,IAAI,CAACtB,MAAM,CAACQ,aAAa,CAACe,KAAK,CAAC,GAAG,CAAC,CAAC;AAC/D;AACF;AACA;IACA,IAAI,IAAI,CAACxB,OAAO,CAACe,QAAQ,CAACC,MAAM,GAAG,CAAC,EAAE;MACpC,IAAI,CAACS,QAAQ,EAAE;AACjB;AACF;AAEAA,EAAAA,QAAQA,GAAG;AACT,IAAA,IAAI,CAACC,KAAK,GAAG,IAAI,CAACC,UAAU,EAAE;AAC9B,IAAA,IAAI,CAAC3B,OAAO,CAAC4B,kBAAkB,CAAC,YAAY,EAAE,IAAI,CAACC,cAAc,EAAE,CAAC;IACpE,IAAI,CAACC,cAAc,EAAE;IAErB,IAAI,CAACC,WAAW,GAAG,IAAI,CAAC/B,OAAO,CAACgC,aAAa,CAAC,iBAAiB,CAAC;IAChE,IAAI,CAACC,KAAK,GAAG,IAAI,CAACP,KAAK,CAACQ,gBAAgB,CAAC,WAAW,CAAC;IAErD,IAAI,CAACH,WAAW,CAACI,gBAAgB,CAAC,OAAO,EAAGC,KAAK,IAAK;AACpD,MAAA,IAAI,CAACC,UAAU,CAACD,KAAK,CAAC;AACxB,KAAC,CAAC;IAEF,IAAI,CAACpC,OAAO,CAACmC,gBAAgB,CAAC,SAAS,EAAGC,KAAK,IAAK;AAClD,MAAA,IAAI,CAACE,aAAa,CAACF,KAAK,CAAC;AAC3B,KAAC,CAAC;AAEFG,IAAAA,QAAQ,CAACJ,gBAAgB,CAAC,OAAO,EAAGC,KAAK,IAAK;MAC5C,IAAI,CAAC,IAAI,CAACpC,OAAO,CAACwC,QAAQ,CAACJ,KAAK,CAACK,MAAM,CAAC,EAAE;AACxC,QAAA,IAAI,CAACC,SAAS,CAAC,KAAK,CAAC;AACvB;AACF,KAAC,CAAC;AACJ;AAEAf,EAAAA,UAAUA,GAAG;AACX,IAAA,MAAMD,KAAK,GAAGa,QAAQ,CAACI,aAAa,CAAC,IAAI,CAAC;AAC1CjB,IAAAA,KAAK,CAACkB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC;IAClClB,KAAK,CAACmB,MAAM,GAAG,IAAI;AACnBnB,IAAAA,KAAK,CAACR,SAAS,CAACK,GAAG,CAAC,0BAA0B,CAAC;AAC/C,IAAA,IAAI,IAAI,CAACtB,MAAM,CAACS,SAAS,KAAK,OAAO,EAAE;AACrCgB,MAAAA,KAAK,CAACR,SAAS,CAACK,GAAG,CAAC,iCAAiC,CAAC;AACxD;AAEA,IAAA,IAAI,CAACvB,OAAO,CAAC8C,WAAW,CAACpB,KAAK,CAAC;AAC/B,IAAA,OAAO,IAAI,CAAC1B,OAAO,CAAC+C,UAAU,KAAKrB,KAAK,EAAE;MACxCA,KAAK,CAACoB,WAAW,CAAC,IAAI,CAAC9C,OAAO,CAAC+C,UAAU,CAAC;AAC5C;AAEA,IAAA,OAAOrB,KAAK;AACd;AAEAI,EAAAA,cAAcA,GAAG;AACfkB,IAAAA,KAAK,CAACC,IAAI,CAAC,IAAI,CAACvB,KAAK,CAACX,QAAQ,CAAC,CAACI,OAAO,CAAE+B,IAAI,IAAK;AAChD;AACA,MAAA,MAAMC,QAAQ,GAAGZ,QAAQ,CAACI,aAAa,CAAC,IAAI,CAAC;MAC7C,IAAI,CAACjB,KAAK,CAAC0B,YAAY,CAACD,QAAQ,EAAED,IAAI,CAAC;AACvCC,MAAAA,QAAQ,CAACL,WAAW,CAACI,IAAI,CAAC;AAE1BA,MAAAA,IAAI,CAACN,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC;AAEnC,MAAA,IAAIM,IAAI,CAACG,OAAO,KAAK,QAAQ,EAAE;AAC7BH,QAAAA,IAAI,CAACN,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC;AACrC;AAEAM,MAAAA,IAAI,CAAChC,SAAS,CAACC,OAAO,CAAEC,SAAS,IAAK;AACpC,QAAA,IAAIA,SAAS,CAACC,UAAU,CAAC,cAAc,CAAC,EAAE;AACxC6B,UAAAA,IAAI,CAAChC,SAAS,CAACI,MAAM,CAACF,SAAS,CAAC;AAClC;AACF,OAAC,CAAC;;AAEF;AACA8B,MAAAA,IAAI,CAACf,gBAAgB,CAAC,OAAO,EAAE,MAAM;AACnCmB,QAAAA,UAAU,CAAC,MAAM;AACf,UAAA,IAAI,CAACZ,SAAS,CAAC,KAAK,CAAC;SACtB,EAAE,EAAE,CAAC;AACR,OAAC,CAAC;AACJ,KAAC,CAAC;AACJ;AAEAb,EAAAA,cAAcA,GAAG;IACf,OAAO;AACX,6EAAA,EAA+E,IAAI,CAAC5B,MAAM,CAACQ,aAAa,IAAI,EAAE,CAAA;AAC9G;AACA,OAAA,EAAS,IAAI,CAACR,MAAM,CAACM,UAAU;AAC/B;AACA;AACA;AACA;AACA,aAAc,CAAA;AACZ;;AAEA;AACF;AACA;AACEgD,EAAAA,MAAMA,GAAG;IACP,OAAO,IAAI,CAACxB,WAAW,CAACyB,YAAY,CAAC,eAAe,CAAC,KAAK,MAAM;AAClE;EAEAnB,UAAUA,CAACD,KAAK,EAAE;IAChBA,KAAK,CAACqB,cAAc,EAAE;;AAEtB;AACA,IAAA,MAAMC,aAAa,GAAGtB,KAAK,CAACuB,MAAM,KAAK,CAAC;AACxC,IAAA,MAAMC,UAAU,GAAGF,aAAa,GAAG,CAAC,GAAG,EAAE;AAEzC,IAAA,IAAI,IAAI,CAACH,MAAM,EAAE,EAAE;MACjB,IAAI,CAACb,SAAS,EAAE;AAClB,KAAC,MAAM;AACL,MAAA,IAAI,CAACmB,QAAQ,CAACD,UAAU,CAAC;AAC3B;AACF;;AAEA;AACF;AACA;AACA;AACA;AACEC,EAAAA,QAAQA,CAACD,UAAU,GAAG,CAAC,EAAE;AACvB,IAAA,IAAI,CAAClC,KAAK,CAACmB,MAAM,GAAG,KAAK;IACzB,IAAI,CAACd,WAAW,CAACa,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC;AACtD,IAAA,IAAIgB,UAAU,KAAK,EAAE,EAAE;AACrB,MAAA,IAAI,CAACE,SAAS,CAACF,UAAU,CAAC;AAC5B;AACF;;AAEA;AACF;AACA;AACA;AACA;AACElB,EAAAA,SAASA,CAACqB,SAAS,GAAG,IAAI,EAAE;AAC1B,IAAA,IAAI,CAACrC,KAAK,CAACmB,MAAM,GAAG,IAAI;IACxB,IAAI,CAACd,WAAW,CAACa,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC;AACvD,IAAA,IAAImB,SAAS,EAAE;AACb,MAAA,IAAI,CAAChC,WAAW,CAACiC,KAAK,EAAE;AAC1B;AACF;;AAEA;AACF;AACA;AACA;AACA;EACEF,SAASA,CAACG,KAAK,EAAE;IACf,IAAIA,KAAK,IAAI,IAAI,CAAChC,KAAK,CAACjB,MAAM,EAAEiD,KAAK,GAAG,CAAC;AACzC,IAAA,IAAIA,KAAK,GAAG,CAAC,EAAEA,KAAK,GAAG,IAAI,CAAChC,KAAK,CAACjB,MAAM,GAAG,CAAC;IAE5C,MAAMkD,QAAQ,GAAG,IAAI,CAACjC,KAAK,CAACiB,IAAI,CAACe,KAAK,CAAC;AACvC,IAAA,IAAIC,QAAQ,EAAE;MACZA,QAAQ,CAACF,KAAK,EAAE;AAClB;AACF;AAEAG,EAAAA,iBAAiBA,GAAG;AAClB,IAAA,MAAMC,aAAa,GAAG7B,QAAQ,CAAC6B,aAAa;IAC5C,MAAMC,SAAS,GAAGrB,KAAK,CAACC,IAAI,CAAC,IAAI,CAAChB,KAAK,CAAC;AAExC,IAAA,OAAOoC,SAAS,CAACC,OAAO,CAACF,aAAa,CAAC;AACzC;EAEA9B,aAAaA,CAACF,KAAK,EAAE;AACnB,IAAA,IAAIA,KAAK,CAACK,MAAM,KAAK,IAAI,CAACV,WAAW,EAAE;MACrC,QAAQK,KAAK,CAACmC,GAAG;AACf,QAAA,KAAK,WAAW;UACdnC,KAAK,CAACqB,cAAc,EAAE;UACtB,IAAI,CAACI,QAAQ,EAAE;AACf,UAAA;AACF,QAAA,KAAK,SAAS;UACZzB,KAAK,CAACqB,cAAc,EAAE;UACtB,IAAI,CAACI,QAAQ,CAAC,IAAI,CAAC5B,KAAK,CAACjB,MAAM,GAAG,CAAC,CAAC;AACpC,UAAA;AACJ;AACF;AAEA,IAAA,IAAI,IAAI,CAACU,KAAK,CAACc,QAAQ,CAACJ,KAAK,CAACK,MAAM,CAAC,IAAI,IAAI,CAACc,MAAM,EAAE,EAAE;MACtD,QAAQnB,KAAK,CAACmC,GAAG;AACf,QAAA,KAAK,WAAW;UACdnC,KAAK,CAACqB,cAAc,EAAE;UACtB,IAAI,IAAI,CAACU,iBAAiB,EAAE,KAAK,EAAE,EAAE;YACnC,IAAI,CAACL,SAAS,CAAC,IAAI,CAACK,iBAAiB,EAAE,GAAG,CAAC,CAAC;AAC9C;AACA,UAAA;AACF,QAAA,KAAK,SAAS;UACZ/B,KAAK,CAACqB,cAAc,EAAE;UACtB,IAAI,IAAI,CAACU,iBAAiB,EAAE,KAAK,EAAE,EAAE;YACnC,IAAI,CAACL,SAAS,CAAC,IAAI,CAACK,iBAAiB,EAAE,GAAG,CAAC,CAAC;AAC9C;AACA,UAAA;AACF,QAAA,KAAK,MAAM;UACT/B,KAAK,CAACqB,cAAc,EAAE;AACtB,UAAA,IAAI,CAACK,SAAS,CAAC,CAAC,CAAC;AACjB,UAAA;AACF,QAAA,KAAK,KAAK;UACR1B,KAAK,CAACqB,cAAc,EAAE;UACtB,IAAI,CAACK,SAAS,CAAC,IAAI,CAAC7B,KAAK,CAACjB,MAAM,GAAG,CAAC,CAAC;AACrC,UAAA;AACJ;AACF;IAEA,IAAIoB,KAAK,CAACmC,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAChB,MAAM,EAAE,EAAE;MAC3C,IAAI,CAACb,SAAS,EAAE;AAClB;IACA,IAAIN,KAAK,CAACmC,GAAG,KAAK,KAAK,IAAI,IAAI,CAAChB,MAAM,EAAE,EAAE;AACxC,MAAA,IAAI,CAACb,SAAS,CAAC,KAAK,CAAC;AACvB;AACF;;AAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACE7B,EAAAA,YAAYA,CAACV,MAAM,EAAEW,OAAO,EAAE;IAC5B,MAAM0D,MAAM,GAAG,EAAE;AAEjB,IAAA,KAAK,MAAM,CAACC,KAAK,GAAI,IAAIrE,MAAM,CAACsE,OAAO,CAACvE,MAAM,CAACG,UAAU,CAAC,EAAE;MAC1D,IAAImE,KAAK,IAAI3D,OAAO,EAAE;AACpB,QAAA,IAAIA,OAAO,CAAC2D,KAAK,CAAC,EAAE;AAClBD,UAAAA,MAAM,CAACC,KAAK,CAAC,GAAG3D,OAAO,CAAC2D,KAAK,CAAC;AAChC;AACF;AACF;AAEA,IAAA,OAAOD,MAAM;AACf;;AAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACE5D,YAAYA,CAAC,GAAG+D,aAAa,EAAE;IAC7B,MAAMC,qBAAqB,GAAG,EAAE;;AAEhC;AACA,IAAA,KAAK,MAAMC,YAAY,IAAIF,aAAa,EAAE;MACxC,KAAK,MAAMJ,GAAG,IAAInE,MAAM,CAAC0E,IAAI,CAACD,YAAY,CAAC,EAAE;AAC3C,QAAA,MAAME,MAAM,GAAGH,qBAAqB,CAACL,GAAG,CAAC;AACzC,QAAA,MAAMS,QAAQ,GAAGH,YAAY,CAACN,GAAG,CAAC;;AAElC;AACA;AACA;QACA,IAAI,OAAOQ,MAAM,KAAK,QAAQ,IAAI,OAAOC,QAAQ,KAAK,QAAQ,EAAE;AAC9D;UACAJ,qBAAqB,CAACL,GAAG,CAAC,GAAG,IAAI,CAAC3D,YAAY,CAACmE,MAAM,EAAEC,QAAQ,CAAC;AAClE,SAAC,MAAM;AACLJ,UAAAA,qBAAqB,CAACL,GAAG,CAAC,GAAGS,QAAQ;AACvC;AACF;AACF;AAEA,IAAA,OAAOJ,qBAAqB;AAC9B;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;;;;"}
|