72 lines
1.9 KiB
TypeScript
72 lines
1.9 KiB
TypeScript
import React, { DetailedHTMLProps, HTMLAttributes } from "react";
|
|
import { twMerge } from "tailwind-merge";
|
|
import { createRoot } from "react-dom/client";
|
|
import Paper from "./Paper";
|
|
|
|
type Props = DetailedHTMLProps<
|
|
HTMLAttributes<HTMLDivElement>,
|
|
HTMLDivElement
|
|
> & {
|
|
target: React.ReactNode;
|
|
};
|
|
|
|
/**
|
|
* # Modal Component
|
|
* @className_wrapper twui-modal-root
|
|
* @className_wrapper twui-modal
|
|
*/
|
|
export default function Modal({ target, ...props }: Props) {
|
|
const [wrapper, setWrapper] = React.useState<HTMLDivElement | null>(null);
|
|
|
|
React.useEffect(() => {
|
|
const wrapperEl = document.createElement("div");
|
|
wrapperEl.className = twMerge(
|
|
"fixed z-[200000] top-0 left-0 w-screen h-screen",
|
|
"flex flex-col items-center justify-center",
|
|
"twui-modal-root"
|
|
);
|
|
|
|
setWrapper(wrapperEl);
|
|
}, []);
|
|
|
|
const modalEl = (
|
|
<React.Fragment>
|
|
<div
|
|
className={twMerge(
|
|
"absolute top-0 left-0 bg-slate-900/80 z-0",
|
|
"w-screen h-screen"
|
|
)}
|
|
onClick={(e) => {
|
|
closeModal({ wrapperEl: wrapper });
|
|
}}
|
|
></div>
|
|
<Paper
|
|
{...props}
|
|
className={twMerge("z-10 max-w-[500px]", props.className)}
|
|
>
|
|
{props.children}
|
|
</Paper>
|
|
</React.Fragment>
|
|
);
|
|
|
|
const targetEl = (
|
|
<div
|
|
onClick={(e) => {
|
|
if (!wrapper) return;
|
|
document.body.appendChild(wrapper);
|
|
const root = createRoot(wrapper);
|
|
root.render(modalEl);
|
|
}}
|
|
>
|
|
{target}
|
|
</div>
|
|
);
|
|
|
|
return targetEl;
|
|
}
|
|
|
|
function closeModal({ wrapperEl }: { wrapperEl: HTMLDivElement | null }) {
|
|
if (!wrapperEl) return;
|
|
wrapperEl.parentElement?.removeChild(wrapperEl);
|
|
}
|