import { ComponentProps, createEffect, createSignal, onCleanup, Show } from "solid-js"; export interface ModalProps extends Pick, "children"> { open?: boolean; onClose?: () => void; onClosed?: () => void; closeOnClick?: boolean; nonBlocking?: boolean; alignTop?: boolean; contentClass?: string; } export const CLOSE_DURATION = 200; export const Modal = (props: ModalProps) => { const [mounted, setMounted] = createSignal(props.open ?? false); const [closing, setClosing] = createSignal(false); createEffect(() => { if (props.open) { setMounted(true); setClosing(false); } else if (mounted()) { setClosing(true); const t = setTimeout(() => { setMounted(false); setClosing(false); props.onClosed?.(); }, CLOSE_DURATION); onCleanup(() => clearTimeout(t)); } }); return (
{ const handleEscape = (e: KeyboardEvent) => { if (e.key === "Escape") { const modals = document.querySelectorAll("[data-modal]"); const lastModal = modals[modals.length - 1]; if (lastModal === node) { e.preventDefault(); e.stopPropagation(); if (props.onClose) props.onClose(); } } }; createEffect(() => { if (!props.nonBlocking) document.body.style.overflow = "hidden"; else document.body.style.overflow = "auto"; }); document.addEventListener("keydown", handleEscape); onCleanup(() => { document.body.style.overflow = "auto"; document.removeEventListener("keydown", handleEscape); }); }} onClick={(ev) => { if ( (props.closeOnClick ?? true) && ev.target === ev.currentTarget && !props.nonBlocking ) { if (props.onClose) props.onClose(); } }} >
{props.children}
); };