Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
2.0 kB · 73 lines
TypeScript
at commit 4df798fc
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374"use client";
import { useEffect, useRef } from "react";
const FOCUSABLE_SELECTOR = [ "a[href]", "button:not([disabled])", "input:not([disabled])", "select:not([disabled])", "textarea:not([disabled])", '[tabindex]:not([tabindex="-1"])',].join(",");
export default function useModalDialog(onClose: () => void, active = true) { const dialogRef = useRef<HTMLDivElement>(null); const onCloseRef = useRef(onClose);
useEffect(() => { onCloseRef.current = onClose; }, [onClose]);
useEffect(() => { if (!active) return;
const previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null; const dialog = dialogRef.current; const frame = requestAnimationFrame(() => { dialog ?.querySelector<HTMLElement>(FOCUSABLE_SELECTOR) ?.focus({ preventScroll: true }); });
function onKeyDown(event: KeyboardEvent) { if (event.key === "Escape") { event.preventDefault(); onCloseRef.current(); return; } if (event.key !== "Tab" || !dialog) return;
const focusable = Array.from( dialog.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR), ).filter((element) => !element.hasAttribute("hidden")); if (focusable.length === 0) { event.preventDefault(); dialog.focus(); return; }
const first = focusable[0]; const last = focusable[focusable.length - 1]; if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); } else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); } }
document.addEventListener("keydown", onKeyDown); return () => { cancelAnimationFrame(frame); document.removeEventListener("keydown", onKeyDown); previouslyFocused?.focus({ preventScroll: true }); }; }, [active]);
return dialogRef;}