// Generated with util/create-component.js import { AnimatePresence, motion, Variants } from "framer-motion"; import React, { MouseEvent, useEffect, useMemo, useRef } from "react"; import { Caret, Close } from "../Icon"; import "./Modal.scss"; import { DrawerDirectionType, ModalElementType, ModalProps, } from "./Modal.types"; const ModalVariants: Variants = { hide: { opacity: 0, transition: { type: "ease", duration: 0.2, }, }, show: { opacity: 1, transition: { type: "ease", duration: 0.2, }, }, }; const modalAnim = { initial: { opacity: 0, scale: 0.9 }, animate: { opacity: 1, scale: 1 }, exit: { opacity: 0, scale: 0 }, }; const drawerAnim = { bottom: { initial: { opacity: 0, y: 50 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: 50 }, }, top: { initial: { opacity: 0, y: -50 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -50 }, }, left: { initial: { opacity: 0, x: -50 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: -50 }, }, right: { initial: { opacity: 0, x: 50 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: 50 }, }, }; const wrapperClassMap: { [key in DrawerDirectionType]: string } = { top: "items-start", left: "justify-start", right: "justify-end", bottom: "items-end", }; const Modal = ({ children, className = "", modalBodyClassName = "", drawer, drawerDirection = "bottom", radius = "rounded-lg", modalWidth = "max-w-[80rem]", modalHeight = "max-h-[80vh]", visible, onClose, hasCloseButton = false, showScroll, }: ModalProps) => { const prevVisibleRef = useRef(); const ModalComponent = useMemo(() => { const result: { [key in ModalElementType]: React.ReactElement | null; } = { ModalHeader: null, ModalBody: null, ModalFooter: null, }; if (children) { React.Children.forEach( children as React.ReactElement[], (child: React.ReactElement<{ __TYPE: ModalElementType }>) => { if (allowedTypeList.includes(child?.props?.__TYPE)) { result[child?.props?.__TYPE] = child; } } ); } return result; }, [children]); const anim = drawer ? drawerAnim[drawerDirection] : modalAnim; const wrapperCss = drawer ? `top-0 left-0 w-full h-full ${wrapperClassMap[drawerDirection]}` : "justify-center items-center"; const childCss = drawer ? `${["bottom", "top"].indexOf(drawerDirection) > -1 ? "w-full" : "h-full"}` : `${modalWidth} ${modalHeight}`; const { ModalHeader, ModalBody, ModalFooter } = ModalComponent; // TODO: modal close될 때 뒤로가기 이슈로 인해 주석 처리 // close modal when go back button is clicked // useEffect(() => { // const preventGoBack = () => { // if (visible && onClose) { // onClose(); // if (history.state.prevUrl !== location.href) { // history.pushState({ prevUrl: location.href }, "", location.href); // } // } // }; // if (visible) { // if (history.state.prevUrl !== location.href) { // history.pushState({ prevUrl: location.href }, "", location.href); // } // } // if (prevVisibleRef.current && !visible) { // history.back(); // } // window.addEventListener("popstate", preventGoBack); // prevVisibleRef.current = visible; // return () => window.removeEventListener("popstate", preventGoBack); // }, [visible]); // disable html div scroll when visible useEffect(() => { if (visible) { document.documentElement.classList.add("disable-scroll"); } else { document.documentElement.classList.remove("disable-scroll"); } }, [visible]); return ( {visible && (
{hasCloseButton && (
{drawer ? ( ) : ( // #gray10_text )}
)} {ModalHeader && (
{ModalHeader}
)}
{ModalBody}
{ModalFooter &&
{ModalFooter}
}
)}
); }; const MODAL_HEADER = "ModalHeader"; const MODAL_BODY = "ModalBody"; const MODAL_FOOTER = "ModalFooter"; export const ModalHeader = (props: { children?: React.ReactNode; __TYPE: ModalElementType; }) => { return <>{props.children}; }; ModalHeader.defaultProps = { __TYPE: MODAL_HEADER, }; export const ModalBody = (props: { children?: React.ReactNode; __TYPE: ModalElementType; }) => { return <>{props.children}; }; ModalBody.defaultProps = { __TYPE: MODAL_BODY, }; export const ModalFooter = (props: { children?: React.ReactNode; __TYPE: ModalElementType; }) => { return <>{props.children}; }; ModalFooter.defaultProps = { __TYPE: MODAL_FOOTER, }; const allowedTypeList: ModalElementType[] = [ MODAL_HEADER, MODAL_BODY, MODAL_FOOTER, ]; const ModalOverlay: React.FC<{ onClose?: (e: MouseEvent) => void; }> = ({ onClose }) => { return ( ); }; const ModalOverlayMemo = React.memo(ModalOverlay); export default Modal;