forked from
pds.ls/pdsls
atmosphere explorer
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 alignTop?: boolean;
9 contentClass?: string;
10}
11
12export const Modal = (props: ModalProps) => {
13 return (
14 <Show when={props.open}>
15 <div
16 data-modal
17 class="fixed inset-0 z-50 flex h-full max-h-none w-full max-w-none justify-center bg-transparent text-neutral-900 dark:text-neutral-200"
18 classList={{
19 "pointer-events-none": props.nonBlocking,
20 "items-start pt-18": props.alignTop,
21 "items-start pt-[20vh]": !props.alignTop,
22 }}
23 ref={(node) => {
24 const handleEscape = (e: KeyboardEvent) => {
25 if (e.key === "Escape") {
26 const modals = document.querySelectorAll("[data-modal]");
27 const lastModal = modals[modals.length - 1];
28 if (lastModal === node) {
29 e.preventDefault();
30 e.stopPropagation();
31 if (props.onClose) props.onClose();
32 }
33 }
34 };
35
36 createEffect(() => {
37 if (!props.nonBlocking) document.body.style.overflow = "hidden";
38 else document.body.style.overflow = "auto";
39 });
40
41 document.addEventListener("keydown", handleEscape);
42
43 onCleanup(() => {
44 document.body.style.overflow = "auto";
45 document.removeEventListener("keydown", handleEscape);
46 });
47 }}
48 onClick={(ev) => {
49 if (
50 (props.closeOnClick ?? true) &&
51 ev.target === ev.currentTarget &&
52 !props.nonBlocking
53 ) {
54 if (props.onClose) props.onClose();
55 }
56 }}
57 >
58 <div
59 class={`transition-all starting:scale-95 starting:opacity-0 ${props.contentClass ?? ""}`}
60 >
61 {props.children}
62 </div>
63 </div>
64 </Show>
65 );
66};