type FocusableElement = | HTMLButtonElement | HTMLAnchorElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement; const useFocusTrap = () => { let trapElement: HTMLElement | null = null; let firstFocusableElement: FocusableElement | null = null; let lastFocusableElement: FocusableElement | null = null; function initFocusTrap(element: HTMLElement) { trapElement = element; calculateFocusTrap(); window.addEventListener("keydown", focusFirstElement); return true; } function calculateFocusTrap() { const focusable = Array.from( trapElement?.querySelectorAll( 'button, a, input, select, textarea, [tabindex]:not([tabindex="-1"])' ) as NodeListOf ).filter((el: FocusableElement) => { const shouldFocus = el instanceof HTMLAnchorElement ? true : !el.disabled; return ( !el.classList.contains("ps__thumb-x") && !el.classList.contains("ps__thumb-y") && shouldFocus ); }); if (focusable.length === 0) return; firstFocusableElement = focusable[0]; lastFocusableElement = focusable[focusable.length - 1]; lastFocusableElement?.addEventListener("keydown", (event) => handleLastElementKeydown(event as KeyboardEvent) ); } function handleLastElementKeydown(e: KeyboardEvent) { if (e.key === "Tab") { e.preventDefault(); focusTrap(); } } function focusTrap() { if (!firstFocusableElement) return; firstFocusableElement?.focus(); } function focusFirstElement(e: KeyboardEvent, trap = false) { if (e.key === "Tab") { e.preventDefault(); focusTrap(); } if (trap) return; window.removeEventListener("keydown", focusFirstElement); } function removeFocusTrap() { lastFocusableElement?.removeEventListener("keydown", (event) => handleLastElementKeydown(event as KeyboardEvent) ); } return { initFocusTrap, removeFocusTrap, }; }; export { useFocusTrap };