Files
freno-dev/src/components/ui/Modal.tsx
Michael Freno 133800f2e3 comment fixing
2026-01-06 13:40:32 -05:00

90 lines
2.3 KiB
TypeScript

import { Show, onMount, onCleanup, type JSX } from "solid-js";
import { Portal } from "solid-js/web";
import Xmark from "~/components/icons/Xmark";
export interface ModalProps {
/** Controls modal visibility */
open: boolean;
/** Callback when modal should close */
onClose: () => void;
/** Modal title (optional) */
title?: string | JSX.Element;
/** Modal content */
children: JSX.Element;
/** Action buttons (optional) */
actions?: JSX.Element;
/** Additional CSS classes for modal container */
class?: string;
}
export default function Modal(props: ModalProps) {
const handleBackdropClick = (e: MouseEvent) => {
if (e.target === e.currentTarget) {
props.onClose();
}
};
const handleEscapeKey = (e: KeyboardEvent) => {
if (e.key === "Escape") {
props.onClose();
}
};
onMount(() => {
if (props.open && typeof document !== "undefined") {
document.addEventListener("keydown", handleEscapeKey);
}
});
onCleanup(() => {
if (typeof document !== "undefined") {
document.removeEventListener("keydown", handleEscapeKey);
}
});
return (
<Show when={props.open}>
<Portal>
<div
class="fixed inset-0 z-500 flex items-center justify-center bg-black/50"
onClick={handleBackdropClick}
role="dialog"
aria-modal="true"
>
<div
class={`bg-base fade-in relative mx-4 max-w-md rounded-md px-8 py-4 shadow-lg ${props.class || ""}`}
onClick={(e) => e.stopPropagation()}
>
<button
class="absolute top-4 right-4"
onClick={props.onClose}
aria-label="Close modal"
>
<Xmark
strokeWidth={0.5}
color="var(--color-text)"
height={50}
width={50}
/>
</button>
<Show when={props.title}>
<div class="py-4 text-center text-3xl tracking-wide">
{props.title}
</div>
</Show>
<div class="modal-content">{props.children}</div>
<Show when={props.actions}>
<div class="modal-actions">{props.actions}</div>
</Show>
</div>
</div>
</Portal>
</Show>
);
}
export { Modal };