/** @jsxImportSource @emotion/react */ 'use client'; import { colors } from '@/libs/themes'; import { CSSObject, Interpolation, Theme } from '@emotion/react'; import { ReactNode, createContext, useCallback, useContext, useEffect, useRef, useState } from 'react'; // ========== BlurLayer Component ========== const BlurLayer = ({ zIndex }: { zIndex?: number }) => { return (
); }; // ========== useModalStatic Hook ========== function useModalStatic({ ref, open, onCancel, clickOutSideClose = true, windowScreenScroll = false, }: { ref: React.RefObject; open: boolean; onCancel: () => void; clickOutSideClose?: boolean; windowScreenScroll?: boolean; }) { const initialOverflowY = useRef(null); const initialScrollY = useRef(0); const clickModalOutside = useCallback( (event: MouseEvent) => { if (clickOutSideClose && open && ref.current && !ref.current.contains(event.target as Node)) { onCancel(); } }, [open, onCancel, clickOutSideClose, ref] ); useEffect(() => { if (!windowScreenScroll) { if (open) { initialOverflowY.current = document.body.style.overflowY; initialScrollY.current = window.scrollY; if (initialOverflowY.current !== 'hidden') { document.body.style.top = `-${initialScrollY.current}px`; document.body.style.overflowY = 'hidden'; } } else { if (initialOverflowY.current !== 'hidden') { document.body.style.top = ''; document.body.style.overflowY = 'auto'; window.scrollTo(0, initialScrollY.current); } } } }, [open, windowScreenScroll]); useEffect(() => { document.addEventListener('mousedown', clickModalOutside); return () => { document.removeEventListener('mousedown', clickModalOutside); }; }, [clickModalOutside]); return null; } // ========== Dialog Component ========== interface DialogProps { open: boolean; title: string; message: string; tabName?: string; onConfirm: () => void; onClose: () => void; } type TabProps = { name: string; buttonColor?: string; txtColor?: string; onClick?: () => void; disabled?: boolean; }; const Dialog: React.FC = ({ open, title, message, tabName, onConfirm, onClose }) => { const tabs: TabProps[] = [ { name: '취소', txtColor: '#888', buttonColor: '#e2e2e2', onClick: onClose, }, { name: tabName ?? '확인', txtColor: '#fff', buttonColor: colors.keyColor, onClick: onConfirm, }, ]; const ref = useRef(null); const [delayedOpen, setDelayedOpen] = useState(false); const handleCancel = () => { setDelayedOpen(false); const timeout = setTimeout(() => onClose(), 100); return () => clearTimeout(timeout); }; useEffect(() => { if (open) { const timeout = setTimeout(() => setDelayedOpen(true), 50); return () => clearTimeout(timeout); } else { setDelayedOpen(false); } }, [open]); useModalStatic({ ref, open: delayedOpen, onCancel: handleCancel, clickOutSideClose: true, windowScreenScroll: false, }); return ( <> {open && ( <>
{title}

{message}

{tabs?.length !== 0 && !!tabs && (
{tabs?.map((item: TabProps, i: number) => ( ))}
)}
)} ); }; // Common style const flexT: Interpolation = { position: 'relative', display: 'flex', flexDirection: 'column', alignItems: 'center', width: '100%', height: '100%', transition: '0.2s ease-in-out', }; // ========== DialogProvider Context ========== interface DialogContextProps { openDialog: (props: DialogProviderProps) => void; closeDialog: () => void; onBack: (onConfirm: () => void) => void; } interface DialogProviderProps { open?: boolean; title: string; message: string; tabName: string; onConfirm: () => void; } const DialogContext = createContext(undefined); export function DialogProvider({ children }: { children: ReactNode }) { const [dialogProps, setDialogProps] = useState(null); const openDialog = (props: DialogProviderProps) => { setDialogProps({ ...props, open: true }); }; const closeDialog = () => { setDialogProps(prev => (prev ? { ...prev, open: false } : prev)); }; const onBack = (onConfirm: () => void) => { setDialogProps({ open: true, title: '이전으로 이동하시겠어요?', message: '이전 페이지로 이동하면\n입력 또는 저장된 정보는 초기화돼요', tabName: '뒤로가기', onConfirm, }); }; return ( {children} {dialogProps && ( { dialogProps.onConfirm(); closeDialog(); }} onClose={closeDialog} /> )} ); } // Custom hook for using dialog context export function useDialog() { const context = useContext(DialogContext); if (context === undefined) { throw new Error('useDialog must be used within a DialogProvider'); } return context; }