import { useEffect } from "react"; export function useEscapeKey(active: boolean, onEscape: () => void) { useEffect(() => { if (!active) return; const handler = (e: KeyboardEvent) => { if (e.key === "Escape") { e.stopPropagation(); onEscape(); } }; document.addEventListener("keydown", handler); return () => document.removeEventListener("keydown", handler); }, [active, onEscape]); } const FOCUSABLE = [ "a[href]", "button:not([disabled])", "input:not([disabled])", "select:not([disabled])", "textarea:not([disabled])", '[tabindex]:not([tabindex="-1"])', ].join(","); export function useFocusTrap(active: boolean, containerRef: React.RefObject) { useEffect(() => { if (!active) return; const node = containerRef.current; if (!node) return; const previouslyFocused = document.activeElement as HTMLElement | null; const focusables = () => Array.from(node.querySelectorAll(FOCUSABLE)); const first = focusables()[0]; first?.focus(); const handler = (e: KeyboardEvent) => { if (e.key !== "Tab") return; const list = focusables(); if (list.length === 0) { e.preventDefault(); return; } const head = list[0]; const tail = list[list.length - 1]; const current = document.activeElement as HTMLElement | null; if (e.shiftKey) { if (current === head || !node.contains(current)) { e.preventDefault(); tail.focus(); } } else { if (current === tail) { e.preventDefault(); head.focus(); } } }; document.addEventListener("keydown", handler); return () => { document.removeEventListener("keydown", handler); previouslyFocused?.focus(); }; }, [active, containerRef]); } export function useBodyScrollLock(active: boolean) { useEffect(() => { if (!active) return; const prev = document.body.style.overflow; document.body.style.overflow = "hidden"; return () => { document.body.style.overflow = prev; }; }, [active]); }