1import { ComponentProps, createEffect, onCleanup, Show } from "solid-js";
2
3export interface ModalProps extends Pick<ComponentProps<"svg">, "children"> {
4 open?: boolean;
5 onClose?: () => void;
6 closeOnClick?: boolean;
7 nonBlocking?: boolean;
8}
9
10export const Modal = (props: ModalProps) => {
11 return (
12 <Show when={props.open}>
13 <div
14 data-modal
15 class="fixed inset-0 z-50 h-full max-h-none w-full max-w-none bg-transparent text-neutral-900 dark:text-neutral-200"
16 classList={{
17 "pointer-events-none": props.nonBlocking,
18 }}
19 ref={(node) => {
20 const handleEscape = (e: KeyboardEvent) => {
21 if (e.key === "Escape") {
22 const modals = document.querySelectorAll("[data-modal]");
23 const lastModal = modals[modals.length - 1];
24 if (lastModal === node) {
25 e.preventDefault();
26 e.stopPropagation();
27 if (props.onClose) props.onClose();
28 }
29 }
30 };
31
32 createEffect(() => {
33 if (!props.nonBlocking) document.body.style.overflow = "hidden";
34 else document.body.style.overflow = "auto";
35 });
36
37 document.addEventListener("keydown", handleEscape);
38
39 onCleanup(() => {
40 document.body.style.overflow = "auto";
41 document.removeEventListener("keydown", handleEscape);
42 });
43 }}
44 onClick={(ev) => {
45 if (
46 (props.closeOnClick ?? true) &&
47 ev.target === ev.currentTarget &&
48 !props.nonBlocking
49 ) {
50 if (props.onClose) props.onClose();
51 }
52 }}
53 >
54 {props.children}
55 </div>
56 </Show>
57 );
58};