1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-09-15 18:57:04 +00:00

refactor(js): clean up modal_handler.js

Since there is a single modal in miniflux, namely the keyboard help, there is
no need to have a full-fledged focus stealer where an ad-hoc one would be
enough. This simplifies the code and makes it way more understandable.

We might want to move to the <dialog> tag at some point in the future.
This commit is contained in:
jvoisin 2025-08-26 18:12:39 +02:00
parent baf8e40152
commit 40c7fefdb6
2 changed files with 21 additions and 67 deletions

View file

@ -570,7 +570,7 @@ function initializeFormHandlers() {
*/ */
function showKeyboardShortcutsAction() { function showKeyboardShortcutsAction() {
const template = document.getElementById("keyboard-shortcuts"); const template = document.getElementById("keyboard-shortcuts");
ModalHandler.open(template.content, "dialog-title"); KeyboardModalHandler.open(template.content, "dialog-title");
} }
/** /**
@ -1215,7 +1215,7 @@ function initializeKeyboardShortcuts() {
// UI actions // UI actions
keyboardHandler.on("?", showKeyboardShortcutsAction); keyboardHandler.on("?", showKeyboardShortcutsAction);
keyboardHandler.on("Escape", () => ModalHandler.close()); keyboardHandler.on("Escape", () => KeyboardModalHandler.close());
keyboardHandler.on("a", () => { keyboardHandler.on("a", () => {
const enclosureElement = document.querySelector('.entry-enclosures'); const enclosureElement = document.querySelector('.entry-enclosures');
if (enclosureElement) { if (enclosureElement) {

View file

@ -1,57 +1,21 @@
class ModalHandler { class KeyboardModalHandler {
static exists() {
return document.getElementById("modal-container") !== null;
}
static getModalContainer() {
return document.getElementById("modal-container");
}
static getFocusableElements() {
const container = this.getModalContainer();
if (container === null) {
return null;
}
return container.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
}
static setupFocusTrap() { static setupFocusTrap() {
const focusableElements = this.getFocusableElements(); const container = document.getElementById("modal-container");
if (container !== null) {
if (focusableElements === null) { container.onkeydown = (e) => {
return; if (e.key === 'Tab') {
} // Since there is only one focusable button in the keyboard modal we always want to focus it 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
const firstFocusableElement = focusableElements[0]; // tab order.
const lastFocusableElement = focusableElements[focusableElements.length - 1]; container.querySelectorAll('button')[0].focus();
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(); e.preventDefault();
} }
}; };
} }
}
static open(fragment, initialFocusElementId) { static open(fragment, initialFocusElementId) {
if (ModalHandler.exists()) { if (document.getElementById("modal-container") !== null){
return; return;
} }
@ -67,20 +31,11 @@ class ModalHandler {
if (closeButton !== null) { if (closeButton !== null) {
closeButton.onclick = (event) => { closeButton.onclick = (event) => {
event.preventDefault(); event.preventDefault();
ModalHandler.close(); KeyboardModalHandler.close();
}; };
} }
let initialFocusElement; const initialFocusElement = document.getElementById(initialFocusElementId);
if (initialFocusElementId !== undefined) {
initialFocusElement = document.getElementById(initialFocusElementId);
} else {
let focusableElements = this.getFocusableElements();
if (focusableElements !== null) {
initialFocusElement = focusableElements[0];
}
}
if (initialFocusElement !== undefined) { if (initialFocusElement !== undefined) {
initialFocusElement.focus(); initialFocusElement.focus();
} }
@ -89,13 +44,12 @@ class ModalHandler {
} }
static close() { static close() {
const container = this.getModalContainer(); const container = document.getElementById("modal-container");
if (container !== null) { if (container !== null) {
container.parentNode.removeChild(container); container.parentNode.removeChild(container);
}
if (this.activeElement !== undefined && this.activeElement !== null) { if (this.activeElement !== undefined && this.activeElement !== null) {
this.activeElement.focus(); this.activeElement.focus();
} }
} }
}
} }