// ===================================================================== // Accessibility helpers for Shaarli Pro modals and overlays. // ===================================================================== (function (root) { 'use strict'; const FOCUSABLE_SELECTORS = [ 'a[href]', 'button:not([disabled])', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', '[tabindex]:not([tabindex="-1"])' ].join(', '); /** * Return a focusable element inside a container. */ function getFocusable(container, first) { if (!container) return null; const elements = Array.prototype.slice.call(container.querySelectorAll(FOCUSABLE_SELECTORS)); return first ? elements[0] : elements[elements.length - 1]; } /** * Move focus to the first focusable element inside a container. */ function focusFirst(container) { const el = getFocusable(container, true); if (el) el.focus(); } /** * Keep keyboard focus inside a container while it is open. * Returns a cleanup function that removes the listener. */ function trapFocus(container) { if (!container) return function () {}; function handleKey(e) { if (e.key !== 'Tab') return; const first = getFocusable(container, true); const last = getFocusable(container, false); if (!first || !last) return; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } } container.addEventListener('keydown', handleKey); return function () { container.removeEventListener('keydown', handleKey); }; } root.ShaarliProA11y = { focusFirst: focusFirst, trapFocus: trapFocus, getFocusable: getFocusable }; }(window));