diff --git a/assets/js/theme/design-system/modal.js b/assets/js/theme/design-system/modal.js index 53d6f67cc38553e0351a6064d4bb40701f927270..c025b8907a392038d75961bf64580e89e5790d57 100644 --- a/assets/js/theme/design-system/modal.js +++ b/assets/js/theme/design-system/modal.js @@ -1,3 +1,4 @@ +import { focusTrap } from '../utils/focus-trap'; const CLASSES = { modalOpened: 'has-modal-opened' @@ -37,7 +38,7 @@ class Modal { if (event.keyCode === 27 || event.key === 'Escape') { this.toggle(false); } else if (event.key === "Tab" && this.state.isOpened) { - this.innerFocus(event); + focusTrap(event, this.element, this.state.isOpened); event.preventDefault(); } }); @@ -49,27 +50,6 @@ class Modal { }); } - innerFocus(event) { - const focusables = 'a, button, input, textarea, select, details, [tabindex], [contenteditable="true"]'; - const elements = this.element.querySelectorAll(focusables); - - const focusableInDialog = Array.from(elements).filter(element => element.tabIndex >= 0); - const firstFocusable = focusableInDialog[0]; - const lastFocusable = focusableInDialog.at(-1); - - if (!this.state.isOpened) { - return; - } - - if (!this.element.contains(event.target) && event.shiftKey) { - lastFocusable.focus(); - } - else if (!this.element.contains(event.target)) { - firstFocusable.focus(); - } - firstFocusable.focus(); - } - toggle(open = !this.state.isOpened) { this.state.isOpened = open; const classAction = this.state.isOpened ? 'add' : 'remove'; diff --git a/assets/js/theme/design-system/search.js b/assets/js/theme/design-system/search.js index b35b2d618338b69dd4bc261dc047ddf5aca79dd3..49d41d855d606cafc03942cc839d230ef3b42a85 100644 --- a/assets/js/theme/design-system/search.js +++ b/assets/js/theme/design-system/search.js @@ -1,3 +1,5 @@ +import { focusTrap } from '../utils/focus-trap'; + class Search { constructor(button, pageFind) { this.state = { @@ -39,7 +41,7 @@ class Search { this.button.focus(); } } else if (event.key === "Tab" && this.state.isOpened) { - this.innerFocus(event); + focusTrap(event, this.element, this.state.isOpened); this.buttonMore = this.element.querySelector('.pagefind-ui__results + button'); @@ -68,35 +70,15 @@ class Search { button.parentElement.removeChild(button); } } - innerFocus(event) { - const focusables = 'a, button, input, textarea, select, details, [tabindex], [contenteditable="true"]'; - const elements = this.element.querySelectorAll(focusables); - - const focusableInDialog = Array.from(elements).filter(element => element.tabIndex >= 0); - const firstFocusable = focusableInDialog[0]; - const lastFocusable = focusableInDialog.at(-1); - - if (!this.state.isOpened) { - return; - } - if (!this.element.contains(event.target) && event.shiftKey) { - lastFocusable.focus(); - event.preventDefault(); - } - else if (!this.element.contains(event.target)) { - firstFocusable.focus(); - event.preventDefault(); - } - } buttonMoreFocus() { const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { - mutation.addedNodes.forEach(addedNode => { + mutation.addedNodes.forEach(addedNode => { if (addedNode instanceof HTMLAnchorElement) { - addedNode.focus(); + addedNode.focus(); } - }); + }); }); }); const observerConfig = { childList: true, subtree: true }; diff --git a/assets/js/theme/utils/focus-trap.js b/assets/js/theme/utils/focus-trap.js new file mode 100644 index 0000000000000000000000000000000000000000..ba6c8fe74e0926e184b41273d07934800a58339a --- /dev/null +++ b/assets/js/theme/utils/focus-trap.js @@ -0,0 +1,20 @@ +export function focusTrap(event, element, isOpened) { + const focusables = 'a, button, input, textarea, select, details, [tabindex], [contenteditable="true"]'; + const elements = element.querySelectorAll(focusables); + + const focusableInDialog = Array.from(elements).filter(element => element.tabIndex >= 0); + const firstFocusable = focusableInDialog[0]; + const lastFocusable = focusableInDialog.at(-1); + + if (!isOpened) { + return; + } + if (!element.contains(event.target) && event.shiftKey) { + lastFocusable.focus(); + event.preventDefault(); + } + else if (!element.contains(event.target)) { + firstFocusable.focus(); + event.preventDefault(); + } +}