miniflux/ui/static/js/modal_handler.js
2023-07-07 15:44:44 -07:00

102 lines
3.1 KiB
JavaScript

class ModalHandler {
static exists() {
return document.getElementById("modal-container") !== null;
}
static getModalContainer() {
let container = document.getElementById("modal-container");
if (container === undefined) {
return;
}
return container;
}
static getFocusableElements() {
let container = this.getModalContainer();
if (container === undefined) {
return;
}
return container.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
}
static setupFocusTrap() {
let focusableElements = this.getFocusableElements();
if (focusableElements === undefined) {
return;
}
let firstFocusableElement = focusableElements[0];
let lastFocusableElement = focusableElements[focusableElements.length - 1];
this.getModalContainer().onkeydown = (e) => {
if (e.key !== 'Tab') {
return;
}
// If there is only one focusable element in the dialog we always want to focus that one with the tab key.
// This handles the special case of having just one focusable element in a dialog where keyboard focus is placed on an element that is not in the tab order.
if (focusableElements.length === 1) {
firstFocusableElement.focus();
e.preventDefault();
return;
}
if (e.shiftKey && document.activeElement === firstFocusableElement) {
lastFocusableElement.focus();
e.preventDefault();
} else if (!e.shiftKey && document.activeElement === lastFocusableElement) {
firstFocusableElement.focus();
e.preventDefault();
}
};
}
static open(fragment, initialFocusElementId) {
if (ModalHandler.exists()) {
return;
}
this.activeElement = document.activeElement;
let container = document.createElement("div");
container.id = "modal-container";
container.setAttribute("role", "dialog");
container.appendChild(document.importNode(fragment, true));
document.body.appendChild(container);
let closeButton = document.querySelector("button.btn-close-modal");
if (closeButton !== null) {
closeButton.onclick = (event) => {
event.preventDefault();
ModalHandler.close();
};
}
let initialFocusElement;
if (initialFocusElementId !== undefined) {
initialFocusElement = document.getElementById(initialFocusElementId);
} else {
initialFocusElement = this.getFocusableElements()[0];
}
initialFocusElement.focus();
this.setupFocusTrap();
}
static close() {
let container = this.getModalContainer();
if (container !== null) {
container.parentNode.removeChild(container);
}
if (this.activeElement !== undefined) {
this.activeElement.focus();
}
}
}