diff --git a/src/components/common/modal/Modal.tsx b/src/components/common/modal/Modal.tsx index 929b5c1e..0cc35696 100644 --- a/src/components/common/modal/Modal.tsx +++ b/src/components/common/modal/Modal.tsx @@ -1,18 +1,20 @@ -import { useState } from 'react'; +import { PropsWithChildren, useState } from 'react'; import { createPortal } from 'react-dom'; import { CheckCircleIcon } from '@heroicons/react/24/outline'; -import * as S from './Modal.styled'; import ScrollPreventor from './ScrollPreventor'; -import ModalCloseButton from './ModalCloseButton'; import { useOutsideClick } from '../../../hooks/useOutsideClick'; +import { ModalWrapper } from './ModalWrapper'; interface ModalProps { - children: React.ReactNode; isOpen: boolean; onClose: () => void; } -const Modal = ({ children, isOpen, onClose }: ModalProps) => { +const Modal = ({ + children, + isOpen, + onClose, +}: PropsWithChildren) => { const modalRefs = useOutsideClick(() => handleClose()); const [isFadingOut, setIsFadingOut] = useState(false); @@ -31,18 +33,18 @@ const Modal = ({ children, isOpen, onClose }: ModalProps) => { return createPortal( - - - - - - - {children} - - + + + + + } /> + {children} + + + , document.body ); diff --git a/src/components/common/modal/ModalBody.tsx b/src/components/common/modal/ModalBody.tsx new file mode 100644 index 00000000..56144194 --- /dev/null +++ b/src/components/common/modal/ModalBody.tsx @@ -0,0 +1,8 @@ +import { forwardRef, PropsWithChildren } from 'react'; +import * as S from './Modal.styled'; + +export const ModalBody = forwardRef( + ({ children }, ref) => { + return {children}; + } +); diff --git a/src/components/common/modal/ModalContainer.tsx b/src/components/common/modal/ModalContainer.tsx new file mode 100644 index 00000000..7e4315a8 --- /dev/null +++ b/src/components/common/modal/ModalContainer.tsx @@ -0,0 +1,18 @@ +import { PropsWithChildren } from 'react'; +import * as S from './Modal.styled'; +interface ModalContainerProps { + $fadeOut: boolean; + onAnimationEnd: () => void; +} + +export const ModalContainer = ({ + $fadeOut, + onAnimationEnd, + children, +}: PropsWithChildren) => { + return ( + + {children} + + ); +}; diff --git a/src/components/common/modal/ModalContents.tsx b/src/components/common/modal/ModalContents.tsx new file mode 100644 index 00000000..907b0cd9 --- /dev/null +++ b/src/components/common/modal/ModalContents.tsx @@ -0,0 +1,6 @@ +import { PropsWithChildren } from 'react'; +import * as S from './Modal.styled'; + +export const ModalContents = ({ children }: PropsWithChildren) => { + return {children}; +}; diff --git a/src/components/common/modal/ModalIconWrapper.tsx b/src/components/common/modal/ModalIconWrapper.tsx new file mode 100644 index 00000000..293a1cb6 --- /dev/null +++ b/src/components/common/modal/ModalIconWrapper.tsx @@ -0,0 +1,9 @@ +import { ReactNode } from 'react'; +import * as S from './Modal.styled'; +interface ModalContentIconProps { + Icon: ReactNode; +} + +export const ModalContentIcon = ({ Icon }: ModalContentIconProps) => { + return {Icon}; +}; diff --git a/src/components/common/modal/ModalProvider.tsx b/src/components/common/modal/ModalProvider.tsx new file mode 100644 index 00000000..0cec13ea --- /dev/null +++ b/src/components/common/modal/ModalProvider.tsx @@ -0,0 +1,14 @@ +import { PropsWithChildren } from 'react'; +import { + ModalContext, + type ModalContextProps, +} from '../../../context/ModalContext'; + +export const ModalProvider = ({ + children, + value, +}: PropsWithChildren<{ value: ModalContextProps }>) => { + return ( + {children} + ); +}; diff --git a/src/components/common/modal/ModalWrapper.tsx b/src/components/common/modal/ModalWrapper.tsx new file mode 100644 index 00000000..62afdd79 --- /dev/null +++ b/src/components/common/modal/ModalWrapper.tsx @@ -0,0 +1,32 @@ +import { PropsWithChildren } from 'react'; +import { ModalContextProps } from '../../../context/ModalContext'; +import { ModalProvider } from './ModalProvider'; +import ModalCloseButton from './ModalCloseButton'; +import { ModalContents } from './ModalContents'; +import { ModalContainer } from './ModalContainer'; +import { ModalBody } from './ModalBody'; +import { ModalContentIcon } from './ModalIconWrapper'; + +export const ModalWrapper = ({ + isOpen = false, + onClose = (event?: React.SyntheticEvent) => { + if (event) { + event.preventDefault(); + event.stopPropagation(); + } + }, + children, +}: PropsWithChildren>) => { + const modalProps: ModalContextProps = { + isOpen, + onClose, + }; + + return {children}; +}; + +ModalWrapper.CloseButton = ModalCloseButton; +ModalWrapper.Contents = ModalContents; +ModalWrapper.Container = ModalContainer; +ModalWrapper.Body = ModalBody; +ModalWrapper.ContentIcon = ModalContentIcon; diff --git a/src/context/ModalContext.tsx b/src/context/ModalContext.tsx new file mode 100644 index 00000000..12df9830 --- /dev/null +++ b/src/context/ModalContext.tsx @@ -0,0 +1,14 @@ +import { createContext } from 'react'; + +export interface ModalContextProps { + isOpen: boolean; + onClose: (event?: React.SyntheticEvent) => void; +} + +const defaultContext: Partial = { + isOpen: false, + onClose: () => {}, +}; + +export const ModalContext = + createContext>(defaultContext); diff --git a/src/mock/mockProjectDetail.json b/src/mock/mockProjectDetail.json index d71fcedb..052edf9a 100644 --- a/src/mock/mockProjectDetail.json +++ b/src/mock/mockProjectDetail.json @@ -9,7 +9,7 @@ "authorId": 8, "views": 1, "isBeginner": true, - "isDone": true, + "isDone": false, "recruitmentEndDate": "2025-02-15T00:00:00.000Z", "recruitmentStartDate": "2025-01-06T00:00:00.000Z", "createdAt": "2025-01-06T07:05:01.000Z",