36 lines
1.7 KiB
TypeScript
36 lines
1.7 KiB
TypeScript
import { useEffect, useRef, type ReactNode } from "react";
|
||
import { createPortal } from "react-dom";
|
||
|
||
export function InspectorModal({ title, onClose, children }: { title: string; onClose: () => void; children: ReactNode }) {
|
||
const dialogRef = useRef<HTMLDialogElement>(null);
|
||
|
||
useEffect(() => {
|
||
const dialog = dialogRef.current!;
|
||
const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
||
const overflow = document.body.style.overflow;
|
||
document.body.style.overflow = "hidden";
|
||
dialog.showModal();
|
||
return () => {
|
||
dialog.close();
|
||
document.body.style.overflow = overflow;
|
||
opener?.focus({ preventScroll: true });
|
||
};
|
||
}, []);
|
||
|
||
return createPortal(
|
||
<dialog ref={dialogRef} className="inspector-modal" aria-labelledby="inspector-modal-title"
|
||
onCancel={event => { event.preventDefault(); onClose(); }}
|
||
onClick={event => {
|
||
if (event.target !== event.currentTarget) return;
|
||
const rect = event.currentTarget.getBoundingClientRect();
|
||
if (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom) onClose();
|
||
}}>
|
||
<header className="inspector-modal-header">
|
||
<div><p className="eyebrow">Questionnaire inspector</p><h2 id="inspector-modal-title">{title}</h2></div>
|
||
<button type="button" className="secondary" onClick={onClose} autoFocus aria-label="Close expanded inspector">Close <span aria-hidden="true">×</span></button>
|
||
</header>
|
||
<div className={`inspector-modal-content${title === "JSON" ? " inspector-modal-json" : ""}`}>{children}</div>
|
||
</dialog>, document.body
|
||
);
|
||
}
|