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