atmosphere explorer
at main 66 lines 2.1 kB view raw
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};