"use client"; import type { RefObject } from "react"; import { useEffect, useRef } from "react"; /** * Escape-to-close, body scroll lock, focus move-in and tab trap for Create-shell modals. */ export function useCreateModalA11y( isOpen: boolean, onClose: () => void, dialogRef: RefObject, ): void { const previousActiveElementRef = useRef(null); useEffect(() => { if (!isOpen) return; const handleEscape = (e: KeyboardEvent) => { if (e.key === "Escape") { onClose(); } }; document.addEventListener("keydown", handleEscape); return () => { document.removeEventListener("keydown", handleEscape); }; }, [isOpen, onClose]); useEffect(() => { if (!isOpen) return; previousActiveElementRef.current = document.activeElement as HTMLElement; document.body.style.overflow = "hidden"; if (dialogRef.current) { const focusableElements = dialogRef.current.querySelectorAll( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])', ); const firstElement = focusableElements[0] as HTMLElement; if (firstElement) { firstElement.focus(); } else { dialogRef.current.setAttribute("tabindex", "-1"); dialogRef.current.focus(); } } const handleTab = (e: KeyboardEvent) => { if (e.key !== "Tab" || !dialogRef.current) return; const focusableElements = dialogRef.current.querySelectorAll( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])', ); const firstElement = focusableElements[0] as HTMLElement; const lastElement = focusableElements[ focusableElements.length - 1 ] as HTMLElement; if (e.shiftKey) { if (document.activeElement === firstElement) { e.preventDefault(); lastElement?.focus(); } } else { if (document.activeElement === lastElement) { e.preventDefault(); firstElement?.focus(); } } }; document.addEventListener("keydown", handleTab); return () => { document.body.style.overflow = ""; document.removeEventListener("keydown", handleTab); previousActiveElementRef.current?.focus(); }; }, [isOpen]); }