From 4255eb2d465fe2a98396ca674123a4e8926e290d Mon Sep 17 00:00:00 2001 From: sikkzz Date: Sun, 2 Feb 2025 17:43:51 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=EB=AA=A8=EB=8B=AC=20=ED=8B=80,=20p?= =?UTF-8?q?ortal=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ui/Modal/Modal.module.scss | 16 +++++++++++ src/components/ui/Modal/Modal.tsx | 34 +++++++++++++++++++++++ src/components/ui/Modal/Portal.tsx | 15 ++++++++++ 3 files changed, 65 insertions(+) create mode 100644 src/components/ui/Modal/Modal.module.scss create mode 100644 src/components/ui/Modal/Modal.tsx create mode 100644 src/components/ui/Modal/Portal.tsx diff --git a/src/components/ui/Modal/Modal.module.scss b/src/components/ui/Modal/Modal.module.scss new file mode 100644 index 0000000..43e8bdb --- /dev/null +++ b/src/components/ui/Modal/Modal.module.scss @@ -0,0 +1,16 @@ +.ModalBackdrop { + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.3); + z-index: 99; +} + +.Modal { + position: fixed; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + z-index: 100; + background-color: var(--color-white); + border-radius: 1.25rem; +} diff --git a/src/components/ui/Modal/Modal.tsx b/src/components/ui/Modal/Modal.tsx new file mode 100644 index 0000000..aa59d56 --- /dev/null +++ b/src/components/ui/Modal/Modal.tsx @@ -0,0 +1,34 @@ +import type { PropsWithChildren } from "react"; +import { useEffect } from "react"; + +import Portal from "@/components/ui/Modal/Portal"; + +import styles from "@/components/ui/Modal/Modal.module.css"; + +interface ModalProps extends PropsWithChildren { + isOpen: boolean; +} + +const Modal = ({ isOpen, children }: ModalProps) => { + useEffect(() => { + document.body.style.overflow = "hidden"; + + return () => { + document.body.style.overflow = "auto"; + }; + }, []); + + return ( + <> + {isOpen && ( + +
+ +
{children}
+ + )} + + ); +}; + +export default Modal; diff --git a/src/components/ui/Modal/Portal.tsx b/src/components/ui/Modal/Portal.tsx new file mode 100644 index 0000000..5d31ca6 --- /dev/null +++ b/src/components/ui/Modal/Portal.tsx @@ -0,0 +1,15 @@ +import { useMemo } from "react"; +import type { PropsWithChildren } from "react"; +import { createPortal } from "react-dom"; + +interface PortalProps extends PropsWithChildren { + elementId: string; +} + +const Portal = ({ children, elementId }: PortalProps) => { + const rootElement = useMemo(() => document.getElementById(elementId), [elementId]); + + return createPortal(children, rootElement as HTMLElement); +}; + +export default Portal; From 927a6e05e5ddb68fa5b2533b27837f05701d4fe2 Mon Sep 17 00:00:00 2001 From: sikkzz Date: Sun, 2 Feb 2025 18:22:39 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=ED=99=88=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EC=95=88=EB=82=B4=20=ED=8C=9D=EC=97=85=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EC=A0=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 1 + src/components/Home/Home.tsx | 9 ++- .../HomeNavigateConfirmModal.module.scss | 36 +++++++++++ .../HomeNavigateConfirmModal.tsx | 61 +++++++++++++++++++ src/components/ui/Modal/Modal.tsx | 3 +- src/hooks/common/useOverlay.ts | 19 ++++++ 6 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss create mode 100644 src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.tsx create mode 100644 src/hooks/common/useOverlay.ts diff --git a/index.html b/index.html index be6dbad..6ccc736 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,7 @@
+ diff --git a/src/components/Home/Home.tsx b/src/components/Home/Home.tsx index b0eac9e..61811e8 100644 --- a/src/components/Home/Home.tsx +++ b/src/components/Home/Home.tsx @@ -1,8 +1,13 @@ import styles from "@/components/Home/Home.module.scss"; +import HomeNavigateConfirmModal from "@/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal"; import IconButton from "@/components/ui/IconButton/IconButton"; import Text from "@/components/ui/Text/Text"; +import { useOverlay } from "@/hooks/common/useOverlay"; + const Home = () => { + const { isOpen, handleClose, handleOpen } = useOverlay(); + return (
@@ -17,9 +22,11 @@ const Home = () => { mainLogo
- +
+ +
); }; diff --git a/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss new file mode 100644 index 0000000..49e31b0 --- /dev/null +++ b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss @@ -0,0 +1,36 @@ +.Modal { + padding: 1.875rem 1.25rem 1.25rem; + + & > span { + margin-top: 0.375rem; + } +} + +.ButtonWrapper { + display: flex; + align-items: center; + gap: 0.625rem; + margin-top: 1.5rem; + + & > button { + width: 8.75rem; + } +} + +.ShowButtonWrapper { + display: flex; + align-items: center; + justify-content: center; + gap: 0.375rem; + margin-top: 0.75rem; + + & > svg > path { + fill: rgba(0, 0, 0, 0.15); + } + + &.isChecked { + & > svg > path { + fill: rgb(54, 54, 66); + } + } +} diff --git a/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.tsx b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.tsx new file mode 100644 index 0000000..9948774 --- /dev/null +++ b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.tsx @@ -0,0 +1,61 @@ +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; + +import classNames from "classnames"; + +import styles from "@/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss"; +import Button from "@/components/ui/Button/Button"; +import Icon from "@/components/ui/Icon/Icon"; +import Modal from "@/components/ui/Modal/Modal"; +import Text from "@/components/ui/Text/Text"; + +interface HomeNavigateConfirmModalProps { + isOpen: boolean; + handleClose: () => void; +} + +const HomeNavigateConfirmModal = ({ isOpen, handleClose }: HomeNavigateConfirmModalProps) => { + const navigate = useNavigate(); + + // 이후 상태 초기값 재설정 + const [isShowButtonChecked, setIsShowButtonChecked] = useState(false); + + const handleShowButtonClick = () => { + setIsShowButtonChecked((prev) => !prev); + }; + + const handleNavigateHome = () => { + handleClose(); + navigate("/"); + }; + + return ( + +
+ + 홈으로 가시겠어요? + + + 복사하지 않은 리뷰는 삭제돼요. + +
+
+
+ + + 다시 안볼래요 + +
+
+
+ ); +}; + +export default HomeNavigateConfirmModal; diff --git a/src/components/ui/Modal/Modal.tsx b/src/components/ui/Modal/Modal.tsx index aa59d56..c0112fa 100644 --- a/src/components/ui/Modal/Modal.tsx +++ b/src/components/ui/Modal/Modal.tsx @@ -1,10 +1,9 @@ import type { PropsWithChildren } from "react"; import { useEffect } from "react"; +import styles from "@/components/ui/Modal/Modal.module.scss"; import Portal from "@/components/ui/Modal/Portal"; -import styles from "@/components/ui/Modal/Modal.module.css"; - interface ModalProps extends PropsWithChildren { isOpen: boolean; } diff --git a/src/hooks/common/useOverlay.ts b/src/hooks/common/useOverlay.ts new file mode 100644 index 0000000..4fdcb63 --- /dev/null +++ b/src/hooks/common/useOverlay.ts @@ -0,0 +1,19 @@ +import { useCallback, useState } from "react"; + +export const useOverlay = () => { + const [isOpen, setIsOpen] = useState(false); + + const handleOpen = useCallback(() => { + setIsOpen(true); + }, [setIsOpen]); + + const handleClose = useCallback(() => { + setIsOpen(false); + }, [setIsOpen]); + + const handleToggle = useCallback(() => { + setIsOpen((prev) => !prev); + }, []); + + return { isOpen, handleOpen, handleClose, handleToggle }; +}; From c49ed493d948f685e1d963412f9b93a3da3157aa Mon Sep 17 00:00:00 2001 From: sikkzz Date: Sun, 2 Feb 2025 18:48:09 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=EB=AA=A8=EB=8B=AC=20=EC=98=A4?= =?UTF-8?q?=ED=94=88=20=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ui/Modal/Modal.module.scss | 18 ++++++++++++++++++ src/components/ui/Modal/Modal.tsx | 16 ++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/components/ui/Modal/Modal.module.scss b/src/components/ui/Modal/Modal.module.scss index 43e8bdb..61d0d3d 100644 --- a/src/components/ui/Modal/Modal.module.scss +++ b/src/components/ui/Modal/Modal.module.scss @@ -3,6 +3,10 @@ inset: 0; background-color: rgba(0, 0, 0, 0.3); z-index: 99; + + &.Open { + animation: animation-show 300ms cubic-bezier(0.3, 0, 0, 1); + } } .Modal { @@ -13,4 +17,18 @@ z-index: 100; background-color: var(--color-white); border-radius: 1.25rem; + + &.Open { + animation: animation-show 300ms cubic-bezier(0.3, 0, 0, 1); + } +} + +@keyframes animation-show { + from { + opacity: 0; + } + + to { + opacity: 1; + } } diff --git a/src/components/ui/Modal/Modal.tsx b/src/components/ui/Modal/Modal.tsx index c0112fa..4c35293 100644 --- a/src/components/ui/Modal/Modal.tsx +++ b/src/components/ui/Modal/Modal.tsx @@ -1,6 +1,8 @@ import type { PropsWithChildren } from "react"; import { useEffect } from "react"; +import classNames from "classnames"; + import styles from "@/components/ui/Modal/Modal.module.scss"; import Portal from "@/components/ui/Modal/Portal"; @@ -21,9 +23,19 @@ const Modal = ({ isOpen, children }: ModalProps) => { <> {isOpen && ( -
+
-
{children}
+
+ {children} +
)} From 3cfccb53255578489b92136b98a30d2116e90f8c Mon Sep 17 00:00:00 2001 From: sikkzz Date: Sun, 2 Feb 2025 19:03:35 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=ED=99=88=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EC=95=88=EB=82=B4=20=EB=AA=A8=EB=8B=AC=20=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .storybook/preview-head.html | 1 + .../HomeNavigateConfirmModal.module.scss | 2 + .../HomeNavigateConfirmModal.stories.tsx | 81 +++++++++++++++++++ .../HomeNavigateConfirmModal.tsx | 4 +- 4 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 .storybook/preview-head.html create mode 100644 src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.stories.tsx diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html new file mode 100644 index 0000000..2e3ae13 --- /dev/null +++ b/.storybook/preview-head.html @@ -0,0 +1 @@ + diff --git a/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss index 49e31b0..c8a4771 100644 --- a/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss +++ b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss @@ -23,6 +23,8 @@ justify-content: center; gap: 0.375rem; margin-top: 0.75rem; + width: 100%; + cursor: pointer; & > svg > path { fill: rgba(0, 0, 0, 0.15); diff --git a/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.stories.tsx b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.stories.tsx new file mode 100644 index 0000000..da92eed --- /dev/null +++ b/src/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.stories.tsx @@ -0,0 +1,81 @@ +import { useState } from "react"; + +import classNames from "classnames"; + +import styles from "@/components/HomeNavigateConfirmModal/HomeNavigateConfirmModal.module.scss"; +import Button from "@/components/ui/Button/Button"; +import Icon from "@/components/ui/Icon/Icon"; +import Modal from "@/components/ui/Modal/Modal"; +import Text from "@/components/ui/Text/Text"; + +import { useOverlay } from "@/hooks/common/useOverlay"; + +import type { Meta, StoryObj, StoryFn } from "@storybook/react"; + +interface HomeNavigateConfirmModalProps { + isOpen: boolean; + handleClose: () => void; +} + +const HomeNavigateConfirmModalStory = ({ isOpen, handleClose }: HomeNavigateConfirmModalProps) => { + const [isShowButtonChecked, setIsShowButtonChecked] = useState(false); + + const handleShowButtonClick = () => { + setIsShowButtonChecked((prev) => !prev); + }; + return ( + +
+ + 홈으로 가시겠어요? + + + 복사하지 않은 리뷰는 삭제돼요. + +
+
+ +
+
+ ); +}; + +const meta: Meta = { + title: "Example/HomeNavigateConfirmModal", + component: HomeNavigateConfirmModalStory, + parameters: { + layout: "centered", + }, + tags: ["!autodocs"], +}; + +export default meta; + +const ModalTemplate = () => { + const { isOpen, handleOpen, handleClose } = useOverlay(); + + return ( + <> +
-
다시 안볼래요 -
+
);