Files
regex-tools/src/components/ModalDialog.tsx

72 lines
1.6 KiB
TypeScript

import { useEffect, useRef, type ReactNode } from "react";
export function ModalDialog({
id,
open,
labelledBy,
onClose,
children,
className = "",
}: {
readonly id: string;
readonly open: boolean;
readonly labelledBy: string;
readonly onClose: () => void;
readonly children: ReactNode;
readonly className?: string;
}) {
const dialog = useRef<HTMLDialogElement>(null);
useEffect(() => {
const element = dialog.current;
if (!element) return;
if (open && !element.open) {
if (typeof element.showModal === "function") {
element.showModal();
} else {
// jsdom and older browsers can still expose the content for a usable
// non-modal fallback.
element.setAttribute("open", "");
}
(
element.querySelector<HTMLElement>("[data-dialog-initial-focus]") ??
element
).focus();
return;
}
if (!open && element.open) {
if (typeof element.close === "function") {
element.close();
} else {
element.removeAttribute("open");
}
}
}, [open]);
return (
<dialog
ref={dialog}
id={id}
className={`app-dialog auxiliary-dialog ${className}`.trim()}
aria-labelledby={labelledBy}
aria-modal="true"
tabIndex={-1}
onClose={onClose}
onCancel={(event) => {
event.preventDefault();
onClose();
}}
onKeyDownCapture={(event) => {
if (event.key !== "Escape") return;
event.preventDefault();
event.stopPropagation();
onClose();
}}
>
{children}
</dialog>
);
}