Files
photo-renamer/src/renderer/modal.js
T

95 lines
2.2 KiB
JavaScript

export function initModalControls() {
document.addEventListener('click', e => {
const closeBtn = e.target.closest('.modal-close');
if (!closeBtn) return;
const modal = closeBtn.closest('.modal-overlay');
if (!modal) return;
closeModal(modal);
});
document.addEventListener('keydown', e => {
const modal = getActiveModal();
if (!modal) return;
if (e.code === 'Escape') {
const closeBtn = modal.querySelector('.modal-close');
if (!closeBtn) return;
e.preventDefault();
closeBtn.click();
return;
}
if (e.code === 'Enter') {
const actionBtn = modal.querySelector('.modal-action');
if (!actionBtn) return;
e.preventDefault();
actionBtn.click();
return;
}
if (e.code === 'Tab') {
trapFocus(e, modal);
}
});
}
export function openModal(modal) {
modal.style.display = 'flex';
const primaryBtn = modal.querySelector('.modal-action');
const closeBtn = modal.querySelector('.modal-close');
(primaryBtn || closeBtn)?.focus();
}
export function closeModal(modal) {
modal.style.display = 'none';
modal.dispatchEvent(new CustomEvent('modal:closed'));
}
function getActiveModal() {
const modals = [...document.querySelectorAll('.modal-overlay')];
return modals.find(modal => modal.style.display === 'flex') || null;
}
function trapFocus(e, modal) {
const focusable = getFocusableElements(modal);
if (!focusable.length) {
e.preventDefault();
modal.focus();
return;
}
const first = focusable[0];
const last = focusable[focusable.length - 1];
const active = document.activeElement;
if (!modal.contains(active)) {
e.preventDefault();
first.focus();
return;
}
if (e.shiftKey && active === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && active === last) {
e.preventDefault();
first.focus();
}
}
function getFocusableElements(container) {
const selector = [
'button:not([disabled])',
'input:not([disabled])',
'select:not([disabled])',
'textarea:not([disabled])',
'a[href]',
'[tabindex]:not([tabindex="-1"])',
].join(',');
return [...container.querySelectorAll(selector)]
.filter(el => !el.hidden && el.offsetParent !== null);
}