DocWeave / frontend /src /components /ui /Modal.jsx
shak3008's picture
feat: add durable workflows, proposal review, and frontend foundation
9308228
Raw
History Blame Contribute Delete
1.73 kB
import { useEffect } from "react";
import { createPortal } from "react-dom";
import "./Modal.css";
/**
* Shared foundation for both centered dialogs and side drawers.
* variant: "dialog" | "drawer"
*/
export function Modal({
open,
onClose,
title,
children,
footer,
variant = "dialog",
width = 480,
}) {
useEffect(() => {
if (!open) return;
const onKeyDown = (e) => {
if (e.key === "Escape") onClose?.();
};
window.addEventListener("keydown", onKeyDown);
return () => window.removeEventListener("keydown", onKeyDown);
}, [open, onClose]);
if (!open) return null;
return createPortal(
<div className="dw-modal-backdrop" onMouseDown={onClose}>
<div
className={`dw-modal dw-modal--${variant}`}
style={variant === "dialog" ? { width } : undefined}
onMouseDown={(e) => e.stopPropagation()}
role="dialog"
aria-modal="true"
>
{title && (
<div className="dw-modal__header">
<h3>{title}</h3>
<button className="dw-modal__close" onClick={onClose} aria-label="Close">
×
</button>
</div>
)}
<div className="dw-modal__body">{children}</div>
{footer && <div className="dw-modal__footer">{footer}</div>}
</div>
</div>,
document.body
);
}
/** Convenience wrapper — same foundation, right-side drawer defaults. */
export function Drawer(props) {
return <Modal variant="drawer" {...props} />;
}