From 22330c5dfd7679d8dc38d9e1c09fdaf733a6b2e3 Mon Sep 17 00:00:00 2001 From: sean Date: Sat, 6 Jul 2024 14:42:18 +0900 Subject: [PATCH 001/168] feat:#11 Add Button Global Css --- src/style/global.css | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/style/global.css b/src/style/global.css index 03afaff8..23dd3ef5 100644 --- a/src/style/global.css +++ b/src/style/global.css @@ -1,4 +1,10 @@ body { - margin: 0; - padding: 0; -} \ No newline at end of file + margin: 0; + padding: 0; +} + +button { + border: none; + outline: none; + user-select: none; +} From 586fc3e189c9945e3027534ebb2302d35aec011c Mon Sep 17 00:00:00 2001 From: donghunee Date: Sat, 6 Jul 2024 15:27:57 +0900 Subject: [PATCH 002/168] =?UTF-8?q?feat:=20#2=20useModal=20hook=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/MainPage.tsx | 16 ++++++++++++---- src/hooks/useModal.ts | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 src/hooks/useModal.ts diff --git a/src/app/MainPage.tsx b/src/app/MainPage.tsx index 4df87f27..02909451 100644 --- a/src/app/MainPage.tsx +++ b/src/app/MainPage.tsx @@ -2,14 +2,22 @@ import { useAtom } from "jotai"; import { messageAtom } from "@/store/messageAtom.tsx"; +import Modal from "@/component/common/modal/Modal"; +import useModal from "@/hooks/useModal"; function MainPage() { const [message] = useAtom(messageAtom); + const { open, close, isOpen } = useModal(); return ( -
- welcome to layer ๐ŸŽ‡ -
{message}
-
+ <> +
+ welcome to layer ๐ŸŽ‡ +
{message}
+
+ +
๋ƒ ๋ƒ 
+
+ ); } diff --git a/src/hooks/useModal.ts b/src/hooks/useModal.ts new file mode 100644 index 00000000..6c56e6b8 --- /dev/null +++ b/src/hooks/useModal.ts @@ -0,0 +1,21 @@ +import { useCallback, useState } from "react"; + +const useModal = () => { + const [isOpen, setIsOpen] = useState(false); + + const open = useCallback(() => { + setIsOpen(true); + }, []); + + const close = useCallback(() => { + setIsOpen(false); + }, []); + + return { + open, + close, + isOpen, + }; +}; + +export default useModal; From 1e00004537b0a153d36420aaf7ad3e011d3eaf07 Mon Sep 17 00:00:00 2001 From: donghunee Date: Sat, 6 Jul 2024 16:21:56 +0900 Subject: [PATCH 003/168] =?UTF-8?q?feat:=20Modal=20Componet=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/modal/Modal.tsx | 82 ++++++++++++++++++++++ src/component/common/modal/ModalPortal.tsx | 13 ++++ 2 files changed, 95 insertions(+) create mode 100644 src/component/common/modal/Modal.tsx create mode 100644 src/component/common/modal/ModalPortal.tsx diff --git a/src/component/common/modal/Modal.tsx b/src/component/common/modal/Modal.tsx new file mode 100644 index 00000000..f959b8dc --- /dev/null +++ b/src/component/common/modal/Modal.tsx @@ -0,0 +1,82 @@ +import ModalPortal from "@/component/common/modal/ModalPortal"; +import { css } from "@emotion/react"; +import { memo, useEffect, useRef } from "react"; + +interface Props { + isModalOpen: boolean; + onClose: () => void; + children?: React.ReactNode; +} + +function Modal({ onClose, isModalOpen, children }: Props) { + const modalRef = useRef(null); + + // ๋ชจ๋‹ฌ ์˜คํ”ˆ ์‹œ ์Šคํฌ๋กค ๋ฐฉ์ง€ + useEffect(() => { + document.body.style.overflow = isModalOpen ? "hidden" : "auto"; + }, [isModalOpen]); + + // ๋ชจ๋‹ฌ ์˜คํ”ˆ ์‹œ ๋’ค๋กœ๊ฐ€๊ธฐ ๋ฐฉ์ง€ (๊ณ ๋ฏผ์ค‘) + useEffect(() => { + const preventGoBack = () => { + history.go(1); + onClose(); + }; + + history.pushState(null, "", location.href); + window.addEventListener("popstate", preventGoBack); + + return () => window.removeEventListener("popstate", preventGoBack); + }, [onClose]); + + // ๋ชจ๋‹ฌ ์˜์—ญ ์ด์™ธ ํด๋ฆญ ์‹œ ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ + useEffect(() => { + const listener = (e: MouseEvent) => { + if (modalRef.current && !modalRef.current.contains(e.target as Node)) { + onClose(); + } + }; + window.addEventListener("mousedown", listener); + return () => { + window.removeEventListener("mousedown", listener); + }; + }, [onClose, modalRef]); + + if (!isModalOpen) return null; + + return ( + +
+
+ {children} +
+
+
+ ); +} + +export default memo(Modal); diff --git a/src/component/common/modal/ModalPortal.tsx b/src/component/common/modal/ModalPortal.tsx new file mode 100644 index 00000000..8c77e135 --- /dev/null +++ b/src/component/common/modal/ModalPortal.tsx @@ -0,0 +1,13 @@ +import { ReactNode } from "react"; +import ReactDom from "react-dom"; + +interface Props { + children: ReactNode; +} + +const ModalPortal = ({ children }: Props) => { + const el = document.getElementById("modal-root") as HTMLElement; + return ReactDom.createPortal(children, el); +}; + +export default ModalPortal; From 1695c33d1d018c05bd3d94407bb0e47572e6c5f2 Mon Sep 17 00:00:00 2001 From: klmhyeonwoo Date: Sat, 6 Jul 2024 21:48:46 +0900 Subject: [PATCH 004/168] chore: #1 Add vercel.json --- src/app/test/Staging.tsx | 0 src/component/ProgressBar/ProgressBar.tsx | 28 +++++++++++++++++++++++ vercel.json | 6 +++++ 3 files changed, 34 insertions(+) create mode 100644 src/app/test/Staging.tsx create mode 100644 src/component/ProgressBar/ProgressBar.tsx create mode 100644 vercel.json diff --git a/src/app/test/Staging.tsx b/src/app/test/Staging.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/component/ProgressBar/ProgressBar.tsx b/src/component/ProgressBar/ProgressBar.tsx new file mode 100644 index 00000000..b8198a68 --- /dev/null +++ b/src/component/ProgressBar/ProgressBar.tsx @@ -0,0 +1,28 @@ +import {css} from "@emotion/react"; + +interface ProgressBarProps { + curPage: number, + lastPage: number, +} + +export default function ProgressBar({ curPage, lastPage }: ProgressBarProps) { + return ( + // FIXME: ์ถ”ํ›„ ๋””์ž์ธ ํ† ํฐ ์—ฐ๋™ ํ›„ ์ปฌ๋Ÿฌ ๊ฐ’ ๋ณ€๊ฒฝ +
+
+
+ ) +} \ No newline at end of file diff --git a/vercel.json b/vercel.json new file mode 100644 index 00000000..f90a92e1 --- /dev/null +++ b/vercel.json @@ -0,0 +1,6 @@ +{ + "routes": [ + { "handle": "filesystem" }, + { "src": "/.*", "dest": "/index.html" } + ] +} \ No newline at end of file From 4582a72e6d11ede8b544d53b8871b7c90a772007 Mon Sep 17 00:00:00 2001 From: klmhyeonwoo Date: Sat, 6 Jul 2024 21:49:20 +0900 Subject: [PATCH 005/168] feat: #11 Add Progress Bar --- src/app/test/Staging.tsx | 19 +++++++++++++++++++ src/component/ProgressBar/ProgressBar.tsx | 1 + src/router/index.tsx | 5 +++++ 3 files changed, 25 insertions(+) diff --git a/src/app/test/Staging.tsx b/src/app/test/Staging.tsx index e69de29b..1c2d47bc 100644 --- a/src/app/test/Staging.tsx +++ b/src/app/test/Staging.tsx @@ -0,0 +1,19 @@ +import ProgressBar from "@/component/ProgressBar/ProgressBar.tsx"; +import {useState} from "react"; +import {css} from "@emotion/react"; + +export default function Staging() { + + const [page, setPage] = useState(0); + + return ( +
+ +
ํ˜„์žฌ ํŽ˜์ด์ง€ ๋„˜๋ฒ„ {page}
+ + +
+ ) +} \ No newline at end of file diff --git a/src/component/ProgressBar/ProgressBar.tsx b/src/component/ProgressBar/ProgressBar.tsx index b8198a68..78aa4661 100644 --- a/src/component/ProgressBar/ProgressBar.tsx +++ b/src/component/ProgressBar/ProgressBar.tsx @@ -6,6 +6,7 @@ interface ProgressBarProps { } export default function ProgressBar({ curPage, lastPage }: ProgressBarProps) { + if (curPage > lastPage) curPage = lastPage; return ( // FIXME: ์ถ”ํ›„ ๋””์ž์ธ ํ† ํฐ ์—ฐ๋™ ํ›„ ์ปฌ๋Ÿฌ ๊ฐ’ ๋ณ€๊ฒฝ
, }, + { + path: '/staging', + element: + } ]; const router = createBrowserRouter([ From 05bbe78f12a481eb5512ad6ae071724de71ac929 Mon Sep 17 00:00:00 2001 From: klm hyeon woo Date: Sat, 6 Jul 2024 22:14:56 +0900 Subject: [PATCH 006/168] Update src/component/ProgressBar/ProgressBar.tsx --- src/component/ProgressBar/ProgressBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/component/ProgressBar/ProgressBar.tsx b/src/component/ProgressBar/ProgressBar.tsx index 78aa4661..164c33e7 100644 --- a/src/component/ProgressBar/ProgressBar.tsx +++ b/src/component/ProgressBar/ProgressBar.tsx @@ -18,7 +18,7 @@ export default function ProgressBar({ curPage, lastPage }: ProgressBarProps) { `}>
Date: Sat, 6 Jul 2024 23:25:30 +0900 Subject: [PATCH 007/168] chore: #1 Add rem css unit --- src/style/global.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/style/global.css b/src/style/global.css index 03afaff8..8f951830 100644 --- a/src/style/global.css +++ b/src/style/global.css @@ -1,4 +1,9 @@ +html { + font-size: 62.5%; +} + body { + font-size: 1.5rem; margin: 0; padding: 0; } \ No newline at end of file From fa383d597908b76f35fed4761c32ebd943487dae Mon Sep 17 00:00:00 2001 From: klmhyeonwoo Date: Sat, 6 Jul 2024 23:26:44 +0900 Subject: [PATCH 008/168] fix: #17 Change px to rem --- src/component/ProgressBar/ProgressBar.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/component/ProgressBar/ProgressBar.tsx b/src/component/ProgressBar/ProgressBar.tsx index 164c33e7..f55dbc8f 100644 --- a/src/component/ProgressBar/ProgressBar.tsx +++ b/src/component/ProgressBar/ProgressBar.tsx @@ -7,23 +7,24 @@ interface ProgressBarProps { export default function ProgressBar({ curPage, lastPage }: ProgressBarProps) { if (curPage > lastPage) curPage = lastPage; + return ( // FIXME: ์ถ”ํ›„ ๋””์ž์ธ ํ† ํฐ ์—ฐ๋™ ํ›„ ์ปฌ๋Ÿฌ ๊ฐ’ ๋ณ€๊ฒฝ
+ `}/>
) } \ No newline at end of file From 33a2a7fb1f74d5d2c4d43eec362e31d008bd351b Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 7 Jul 2024 01:14:03 +0900 Subject: [PATCH 009/168] feat:#11 Add Sprite Login Logo --- src/assets/imgs/loginLogo.svg | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/assets/imgs/loginLogo.svg diff --git a/src/assets/imgs/loginLogo.svg b/src/assets/imgs/loginLogo.svg new file mode 100644 index 00000000..3e5cf3aa --- /dev/null +++ b/src/assets/imgs/loginLogo.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + From 3e72f9669232c772e44c14e67d859fc1622b7ea9 Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 7 Jul 2024 01:14:28 +0900 Subject: [PATCH 010/168] feat:#11 Add Sprite Login Component --- src/component/LoginSpriteSvg.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/component/LoginSpriteSvg.tsx diff --git a/src/component/LoginSpriteSvg.tsx b/src/component/LoginSpriteSvg.tsx new file mode 100644 index 00000000..5e5dda79 --- /dev/null +++ b/src/component/LoginSpriteSvg.tsx @@ -0,0 +1,10 @@ +import loginLogo from "@/assets/imgs/loginLogo.svg"; +import { loginType } from "@/types/loginType"; + +const LoginSpriteSvg = ({ type }: loginType) => ( + + + +); + +export default LoginSpriteSvg; From c7d6d4fbfa2c37d6d575c962ef0f7975e1ffd20b Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 7 Jul 2024 01:14:48 +0900 Subject: [PATCH 011/168] feat:#11 Add Login Type --- src/types/loginType/index.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/types/loginType/index.tsx diff --git a/src/types/loginType/index.tsx b/src/types/loginType/index.tsx new file mode 100644 index 00000000..b0029548 --- /dev/null +++ b/src/types/loginType/index.tsx @@ -0,0 +1,8 @@ +export type loginType = { + type: "google" | "kakao"; +}; + +export type loginBtnType = { + type: "google" | "kakao"; + handler: () => void; +}; From 5e2da709c790e6830003e05230319a94d73bfc53 Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 7 Jul 2024 01:15:12 +0900 Subject: [PATCH 012/168] feat:#11 Add SocialLoginButton --- src/component/SocialLoginButton.tsx | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/component/SocialLoginButton.tsx diff --git a/src/component/SocialLoginButton.tsx b/src/component/SocialLoginButton.tsx new file mode 100644 index 00000000..9677ed91 --- /dev/null +++ b/src/component/SocialLoginButton.tsx @@ -0,0 +1,42 @@ +import { css } from "@emotion/react"; +import { loginBtnType } from "@/types/loginType"; +import LoginSpriteSvg from "./LoginSpriteSvg"; + +// FIXME : ๋ฒ„ํŠผ ์ƒ‰ ์ˆ˜์ • ํ•„์š” +const backgroundColors = { + kakao: "#ffe400", + google: "#FFFFFF", +}; + +const SocialLoginButton = ({ type, handler }: loginBtnType) => { + return ( + + ); +}; + +export default SocialLoginButton; From 8b05a4c2a57f0cd0c348b1226f6f928a5450dac0 Mon Sep 17 00:00:00 2001 From: sean Date: Sat, 6 Jul 2024 14:42:18 +0900 Subject: [PATCH 013/168] feat:#11 Add Button Global Css --- src/style/global.css | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/style/global.css b/src/style/global.css index 8f951830..343f77f8 100644 --- a/src/style/global.css +++ b/src/style/global.css @@ -1,9 +1,15 @@ html { - font-size: 62.5%; + font-size: 62.5%; } body { - font-size: 1.5rem; - margin: 0; - padding: 0; -} \ No newline at end of file + font-size: 1.5rem; + margin: 0; + padding: 0; +} + +button { + border: none; + outline: none; + user-select: none; +} From e07237895fc4cf6e3a35c33bf47eb2b8db369c05 Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 7 Jul 2024 01:14:03 +0900 Subject: [PATCH 014/168] feat:#11 Add Sprite Login Logo --- src/assets/imgs/loginLogo.svg | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/assets/imgs/loginLogo.svg diff --git a/src/assets/imgs/loginLogo.svg b/src/assets/imgs/loginLogo.svg new file mode 100644 index 00000000..3e5cf3aa --- /dev/null +++ b/src/assets/imgs/loginLogo.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + From 4cec40a3e8fecdcf79eeaf24c0730e97b7f6024a Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 7 Jul 2024 01:14:28 +0900 Subject: [PATCH 015/168] feat:#11 Add Sprite Login Component --- src/component/LoginSpriteSvg.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/component/LoginSpriteSvg.tsx diff --git a/src/component/LoginSpriteSvg.tsx b/src/component/LoginSpriteSvg.tsx new file mode 100644 index 00000000..5e5dda79 --- /dev/null +++ b/src/component/LoginSpriteSvg.tsx @@ -0,0 +1,10 @@ +import loginLogo from "@/assets/imgs/loginLogo.svg"; +import { loginType } from "@/types/loginType"; + +const LoginSpriteSvg = ({ type }: loginType) => ( + + + +); + +export default LoginSpriteSvg; From c5dce14f12d7af3f06e3676ca69171e3e9e0fa4b Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 7 Jul 2024 01:14:48 +0900 Subject: [PATCH 016/168] feat:#11 Add Login Type --- src/types/loginType/index.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/types/loginType/index.tsx diff --git a/src/types/loginType/index.tsx b/src/types/loginType/index.tsx new file mode 100644 index 00000000..b0029548 --- /dev/null +++ b/src/types/loginType/index.tsx @@ -0,0 +1,8 @@ +export type loginType = { + type: "google" | "kakao"; +}; + +export type loginBtnType = { + type: "google" | "kakao"; + handler: () => void; +}; From 8446926f213f682d2de6ae0b4fdc873dac911f05 Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 7 Jul 2024 01:15:12 +0900 Subject: [PATCH 017/168] feat:#11 Add SocialLoginButton --- src/component/SocialLoginButton.tsx | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/component/SocialLoginButton.tsx diff --git a/src/component/SocialLoginButton.tsx b/src/component/SocialLoginButton.tsx new file mode 100644 index 00000000..9677ed91 --- /dev/null +++ b/src/component/SocialLoginButton.tsx @@ -0,0 +1,42 @@ +import { css } from "@emotion/react"; +import { loginBtnType } from "@/types/loginType"; +import LoginSpriteSvg from "./LoginSpriteSvg"; + +// FIXME : ๋ฒ„ํŠผ ์ƒ‰ ์ˆ˜์ • ํ•„์š” +const backgroundColors = { + kakao: "#ffe400", + google: "#FFFFFF", +}; + +const SocialLoginButton = ({ type, handler }: loginBtnType) => { + return ( + + ); +}; + +export default SocialLoginButton; From 6d9fe7b05cc664b3f00588f6f6abdf99ba3fb2d4 Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 7 Jul 2024 01:24:20 +0900 Subject: [PATCH 018/168] refactor: #11 Modify px to rem --- src/component/LoginSpriteSvg.tsx | 2 +- src/component/SocialLoginButton.tsx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/component/LoginSpriteSvg.tsx b/src/component/LoginSpriteSvg.tsx index 5e5dda79..7e01c6c9 100644 --- a/src/component/LoginSpriteSvg.tsx +++ b/src/component/LoginSpriteSvg.tsx @@ -2,7 +2,7 @@ import loginLogo from "@/assets/imgs/loginLogo.svg"; import { loginType } from "@/types/loginType"; const LoginSpriteSvg = ({ type }: loginType) => ( - + ); diff --git a/src/component/SocialLoginButton.tsx b/src/component/SocialLoginButton.tsx index 9677ed91..8b531724 100644 --- a/src/component/SocialLoginButton.tsx +++ b/src/component/SocialLoginButton.tsx @@ -13,12 +13,12 @@ const SocialLoginButton = ({ type, handler }: loginBtnType) => {
Date: Sun, 7 Jul 2024 01:44:39 +0900 Subject: [PATCH 019/168] refactor: #11 Organize Components File Folder --- src/component/{ => Img}/LoginSpriteSvg.tsx | 0 src/component/{ => button}/SocialLoginButton.tsx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/component/{ => Img}/LoginSpriteSvg.tsx (100%) rename src/component/{ => button}/SocialLoginButton.tsx (94%) diff --git a/src/component/LoginSpriteSvg.tsx b/src/component/Img/LoginSpriteSvg.tsx similarity index 100% rename from src/component/LoginSpriteSvg.tsx rename to src/component/Img/LoginSpriteSvg.tsx diff --git a/src/component/SocialLoginButton.tsx b/src/component/button/SocialLoginButton.tsx similarity index 94% rename from src/component/SocialLoginButton.tsx rename to src/component/button/SocialLoginButton.tsx index 8b531724..ca7c211a 100644 --- a/src/component/SocialLoginButton.tsx +++ b/src/component/button/SocialLoginButton.tsx @@ -1,6 +1,6 @@ import { css } from "@emotion/react"; import { loginBtnType } from "@/types/loginType"; -import LoginSpriteSvg from "./LoginSpriteSvg"; +import LoginSpriteSvg from "../Img/LoginSpriteSvg"; // FIXME : ๋ฒ„ํŠผ ์ƒ‰ ์ˆ˜์ • ํ•„์š” const backgroundColors = { From 225d4bd96e300fde66d6f381f1f8b75aab3255c6 Mon Sep 17 00:00:00 2001 From: donghunee Date: Sun, 7 Jul 2024 06:25:44 +0900 Subject: [PATCH 020/168] =?UTF-8?q?chore:=20#11=20modal-root=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/index.html b/index.html index 9206f6cf..684f610a 100644 --- a/index.html +++ b/index.html @@ -8,6 +8,7 @@
+ From bf9619c10b2d25fc19100ef42cd56720c0355793 Mon Sep 17 00:00:00 2001 From: donghunee Date: Sun, 7 Jul 2024 11:42:57 +0900 Subject: [PATCH 021/168] =?UTF-8?q?fix:=20#11=20document=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95,=20=ED=95=84=EC=9A=94=EC=97=86=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/modal/Modal.tsx | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/component/common/modal/Modal.tsx b/src/component/common/modal/Modal.tsx index f959b8dc..983363f5 100644 --- a/src/component/common/modal/Modal.tsx +++ b/src/component/common/modal/Modal.tsx @@ -16,19 +16,6 @@ function Modal({ onClose, isModalOpen, children }: Props) { document.body.style.overflow = isModalOpen ? "hidden" : "auto"; }, [isModalOpen]); - // ๋ชจ๋‹ฌ ์˜คํ”ˆ ์‹œ ๋’ค๋กœ๊ฐ€๊ธฐ ๋ฐฉ์ง€ (๊ณ ๋ฏผ์ค‘) - useEffect(() => { - const preventGoBack = () => { - history.go(1); - onClose(); - }; - - history.pushState(null, "", location.href); - window.addEventListener("popstate", preventGoBack); - - return () => window.removeEventListener("popstate", preventGoBack); - }, [onClose]); - // ๋ชจ๋‹ฌ ์˜์—ญ ์ด์™ธ ํด๋ฆญ ์‹œ ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ useEffect(() => { const listener = (e: MouseEvent) => { @@ -36,9 +23,9 @@ function Modal({ onClose, isModalOpen, children }: Props) { onClose(); } }; - window.addEventListener("mousedown", listener); + document.addEventListener("mousedown", listener); return () => { - window.removeEventListener("mousedown", listener); + document.removeEventListener("mousedown", listener); }; }, [onClose, modalRef]); From 2f3eecc4172f3371b0d9b350245f6e3cb7a1c841 Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 7 Jul 2024 21:55:25 +0900 Subject: [PATCH 022/168] refactor: #11 Remove SocialLoginButtonDependency --- src/component/button/SocialLoginButton.tsx | 7 ++++--- src/types/loginType/index.tsx | 12 ++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/component/button/SocialLoginButton.tsx b/src/component/button/SocialLoginButton.tsx index ca7c211a..d437184f 100644 --- a/src/component/button/SocialLoginButton.tsx +++ b/src/component/button/SocialLoginButton.tsx @@ -1,11 +1,12 @@ import { css } from "@emotion/react"; -import { loginBtnType } from "@/types/loginType"; +import { loginTypeProvider, loginBtnType } from "@/types/loginType"; import LoginSpriteSvg from "../Img/LoginSpriteSvg"; // FIXME : ๋ฒ„ํŠผ ์ƒ‰ ์ˆ˜์ • ํ•„์š” -const backgroundColors = { +const backgroundColors: Record = { kakao: "#ffe400", google: "#FFFFFF", + apple: "red", }; const SocialLoginButton = ({ type, handler }: loginBtnType) => { @@ -34,7 +35,7 @@ const SocialLoginButton = ({ type, handler }: loginBtnType) => { >
- {type === "kakao" ? "์นด์นด์˜ค๋กœ" : "๊ตฌ๊ธ€"} ๋กœ๊ทธ์ธ + {loginTypeProvider[type]}๋กœ ๋กœ๊ทธ์ธ ); }; diff --git a/src/types/loginType/index.tsx b/src/types/loginType/index.tsx index b0029548..056cd487 100644 --- a/src/types/loginType/index.tsx +++ b/src/types/loginType/index.tsx @@ -1,8 +1,16 @@ +export const loginTypeProvider = { + apple: "์• ํ”Œ", + google: "๊ตฌ๊ธ€", + kakao: "์นด์นด์˜ค", +}; + +type loginProvider = keyof typeof loginTypeProvider; + export type loginType = { - type: "google" | "kakao"; + type: loginProvider; }; export type loginBtnType = { - type: "google" | "kakao"; + type: loginProvider; handler: () => void; }; From 987a3b7e6de96f5eb5758419c3c1bd7d71caf020 Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 7 Jul 2024 22:23:59 +0900 Subject: [PATCH 023/168] refactor/#11 Refactor SocialLoginButton --- src/component/button/SocialLoginButton.tsx | 11 ++--------- src/types/loginType/index.tsx | 9 ++++++++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/component/button/SocialLoginButton.tsx b/src/component/button/SocialLoginButton.tsx index d437184f..5dbd9c4d 100644 --- a/src/component/button/SocialLoginButton.tsx +++ b/src/component/button/SocialLoginButton.tsx @@ -1,15 +1,8 @@ import { css } from "@emotion/react"; -import { loginTypeProvider, loginBtnType } from "@/types/loginType"; +import { loginTypeProvider, loginBtnProps, backgroundColors } from "@/types/loginType"; import LoginSpriteSvg from "../Img/LoginSpriteSvg"; -// FIXME : ๋ฒ„ํŠผ ์ƒ‰ ์ˆ˜์ • ํ•„์š” -const backgroundColors: Record = { - kakao: "#ffe400", - google: "#FFFFFF", - apple: "red", -}; - -const SocialLoginButton = ({ type, handler }: loginBtnType) => { +const SocialLoginButton = ({ type, handler }: loginBtnProps) => { return ( + + + - const [page, setPage] = useState(0); - - return ( -
- -
ํ˜„์žฌ ํŽ˜์ด์ง€ ๋„˜๋ฒ„ {page}
- - -
- ) -} \ No newline at end of file + + ๊ธฐ๋ณธ ๋ฒ„ํŠผ + ํ•˜๋Š˜์ƒ‰ ๋ฒ„ํŠผ + ํšŒ์ƒ‰ ๋ฒ„ํŠผ + ๋น„ํ™œ์„ฑํ™” ๋ฒ„ํŠผ + + + ); +} diff --git a/src/component/Button/Button.tsx b/src/component/Button/Button.tsx index 37bce80e..36c3c623 100644 --- a/src/component/Button/Button.tsx +++ b/src/component/Button/Button.tsx @@ -5,6 +5,7 @@ export type ButtonProps = { colorSchema?: string; disabled?: boolean; } & Omit, "type">; + export default function Button({ children, colorSchema = "primary", disabled = false, ...props }: PropsWithChildren) { return ( + ); +} diff --git a/src/component/Button/ButtonProvider.tsx b/src/component/Button/ButtonProvider.tsx new file mode 100644 index 00000000..c4d2e02d --- /dev/null +++ b/src/component/Button/ButtonProvider.tsx @@ -0,0 +1,45 @@ +import { css } from "@emotion/react"; +import { Children, cloneElement, isValidElement, PropsWithChildren } from "react"; + +import Button, { ButtonProps } from "@/component/Button/Button.tsx"; + +const Primary = ({ ...props }) => { + return + + + - const [page, setPage] = useState(0); - - return ( -
- -
ํ˜„์žฌ ํŽ˜์ด์ง€ ๋„˜๋ฒ„ {page}
- - -
- ) -} \ No newline at end of file + + ๊ธฐ๋ณธ ๋ฒ„ํŠผ + ํ•˜๋Š˜์ƒ‰ ๋ฒ„ํŠผ + ํšŒ์ƒ‰ ๋ฒ„ํŠผ + ๋น„ํ™œ์„ฑํ™” ๋ฒ„ํŠผ + + + ); +} diff --git a/src/component/Button/Button.tsx b/src/component/Button/Button.tsx index 37bce80e..36c3c623 100644 --- a/src/component/Button/Button.tsx +++ b/src/component/Button/Button.tsx @@ -5,6 +5,7 @@ export type ButtonProps = { colorSchema?: string; disabled?: boolean; } & Omit, "type">; + export default function Button({ children, colorSchema = "primary", disabled = false, ...props }: PropsWithChildren) { return ( -
- ); -}; -export default Login; diff --git a/src/app/login/KakaoLoginRedirection.tsx b/src/app/login/KakaoLoginRedirection.tsx deleted file mode 100644 index 07390ffc..00000000 --- a/src/app/login/KakaoLoginRedirection.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useEffect } from "react"; -import { useNavigate } from "react-router-dom"; -import { api } from "@/api"; - -//FIXME: ์‘๋‹ต ๋ฐ์ดํ„ฐ ํ˜•์‹์— ๋”ฐ๋ผ ์ˆ˜์ • -type Response = {}; - -const KaKaoRedirection = () => { - const code = window.location.search; - const navigate = useNavigate(); - - useEffect(() => { - // FIXME: ๋ฐฑ์—”๋“œ API์— ๋”ฐ๋ผ ์ฃผ์†Œ ์ˆ˜์ • ํ•„์š” - api - .post(`kakaoLogin${code}`) - .then((_: Response) => { - // FIXME: ๋ฐ›์€๊ฑธ ์–ด๋””์— ์ €์žฅํ• ์ง€ ๋…ผ์˜ ํ•„์š” (๋กœ๊ทธ์ธ ์ €์žฅ ๋ฐฉ์‹์— ๋”ฐ๋ผ ๋‹ฌ๋ฆฌ์ง) - // => ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ ๋กœ์ง ์ถ”๊ฐ€ - // FIXME: ์™„๋ฃŒ ํ›„ ์ด๋™ (ํ”„๋กœ์„ธ์Šค์— ๋”ฐ๋ผ ํŽ˜์ด์ง€ URL ๋ณ€๊ฒฝ) - navigate("/test"); - }) - .catch((err) => { - console.log(err); - }); - }, []); - - // FIXME: ๋กœ๊ทธ์ธ ๋กœ๋”ฉ ๋””์ž์ธ ํ•„์š” ๋ฐ ์ด์— ๋”ฐ๋ฅธ ์ฝ”๋“œ ์ถ”๊ฐ€ ๊ฐœ๋ฐœ ํ•„์š” - return
๋กœ๊ทธ์ธ ์ค‘์ž…๋‹ˆ๋‹ค.
; -}; - -export default KaKaoRedirection; diff --git a/src/router/index.tsx b/src/router/index.tsx index a8ff8d58..c01cbd62 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -2,6 +2,7 @@ import { Fragment } from "react"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; import MainPage from "@/app/MainPage.tsx"; /* FIXME - ์‹ค์ œ ๋ฉ”์ธ ํŽ˜์ด์ง€ ์ž‘์„ฑ ํ›„ ๋Œ€์ฒดํ•ด์ฃผ์„ธ์š”. */ +import Login from "@/app/login/Login"; import Staging from "@/app/test/Staging.tsx"; import GlobalLayout from "@/layout/GlobalLayout.tsx"; @@ -14,6 +15,10 @@ const routerChildren = [ path: "/staging", element: , }, + { + path: "/login", + element: , + }, ]; const router = createBrowserRouter([ From 37750a902b69e944f57b88c8c1bd5e6d893252c9 Mon Sep 17 00:00:00 2001 From: sean Date: Tue, 9 Jul 2024 15:03:08 +0900 Subject: [PATCH 085/168] fix:#11 Rename import URL --- src/app/login/Login.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/login/Login.tsx b/src/app/login/Login.tsx index 8f57a41a..f6a7d95d 100644 --- a/src/app/login/Login.tsx +++ b/src/app/login/Login.tsx @@ -1,6 +1,6 @@ import { DefaultLayout } from "@/layout/DefaultLayout"; import SocialLoginButton from "@/component/button/SocialLoginButton"; -import { kakaoLogin } from "./kakao/KakaoLogin"; +import { kakaoLogin } from "./kakao/kakaoLogin"; import { googleLogin } from "./google/googleLogin"; const Login = () => { From 6df349a6a3c4f32024cc29087c3652ec95778d5b Mon Sep 17 00:00:00 2001 From: donghunee Date: Tue, 9 Jul 2024 23:33:18 +0900 Subject: [PATCH 086/168] =?UTF-8?q?refacort:=20#11=20svg=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/svgs/{ => common}/ic_back.svg | 0 src/assets/svgs/common/ic_check.svg | 6 ++++++ src/assets/svgs/common/index.ts | 2 ++ src/assets/svgs/index.ts | 5 ++--- src/assets/svgs/toast/ic_success.svg | 3 +++ src/assets/svgs/toast/index.ts | 1 + 6 files changed, 14 insertions(+), 3 deletions(-) rename src/assets/svgs/{ => common}/ic_back.svg (100%) create mode 100644 src/assets/svgs/common/ic_check.svg create mode 100644 src/assets/svgs/common/index.ts create mode 100644 src/assets/svgs/toast/ic_success.svg create mode 100644 src/assets/svgs/toast/index.ts diff --git a/src/assets/svgs/ic_back.svg b/src/assets/svgs/common/ic_back.svg similarity index 100% rename from src/assets/svgs/ic_back.svg rename to src/assets/svgs/common/ic_back.svg diff --git a/src/assets/svgs/common/ic_check.svg b/src/assets/svgs/common/ic_check.svg new file mode 100644 index 00000000..a67205b4 --- /dev/null +++ b/src/assets/svgs/common/ic_check.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/svgs/common/index.ts b/src/assets/svgs/common/index.ts new file mode 100644 index 00000000..89cc2442 --- /dev/null +++ b/src/assets/svgs/common/index.ts @@ -0,0 +1,2 @@ +export { default as ic_back } from "./ic_back.svg?react"; +export { default as ic_check } from "./ic_check.svg?react"; diff --git a/src/assets/svgs/index.ts b/src/assets/svgs/index.ts index aa59602b..c92002b3 100644 --- a/src/assets/svgs/index.ts +++ b/src/assets/svgs/index.ts @@ -1,3 +1,2 @@ -import ic_back from "./ic_back.svg?react"; - -export { ic_back }; +export * from "./common"; +export * from "./toast"; diff --git a/src/assets/svgs/toast/ic_success.svg b/src/assets/svgs/toast/ic_success.svg new file mode 100644 index 00000000..08351fe0 --- /dev/null +++ b/src/assets/svgs/toast/ic_success.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/toast/index.ts b/src/assets/svgs/toast/index.ts new file mode 100644 index 00000000..70380b1b --- /dev/null +++ b/src/assets/svgs/toast/index.ts @@ -0,0 +1 @@ +export { default as ic_success } from "./ic_success.svg?react"; From 1205d41acbc92413bdf3a319f482f873bd9ff9f0 Mon Sep 17 00:00:00 2001 From: donghunee Date: Tue, 9 Jul 2024 23:34:02 +0900 Subject: [PATCH 087/168] =?UTF-8?q?chore:=20#11=20Sample=20Icon=20Componen?= =?UTF-8?q?t=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/MainPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/MainPage.tsx b/src/app/MainPage.tsx index cada2a0c..81bf5b6b 100644 --- a/src/app/MainPage.tsx +++ b/src/app/MainPage.tsx @@ -20,6 +20,7 @@ function MainPage() { console.log("ํด๋ฆญ")} /> + From d50f24bc5d8384cde31761fea343dfaced53f266 Mon Sep 17 00:00:00 2001 From: donghunee Date: Tue, 9 Jul 2024 23:38:44 +0900 Subject: [PATCH 088/168] =?UTF-8?q?chore:=20#11=20react.svg=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/react.svg | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/assets/react.svg diff --git a/src/assets/react.svg b/src/assets/react.svg deleted file mode 100644 index 6c87de9b..00000000 --- a/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 83ec37b6f770148ed697a4f35aeb74179d535193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=86=E1=85=B5=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=B4?= Date: Wed, 10 Jul 2024 02:12:41 +0900 Subject: [PATCH 089/168] =?UTF-8?q?chore:=20#11=20=EB=9D=BC=EB=94=94?= =?UTF-8?q?=EC=98=A4=EB=B2=84=ED=8A=BC=20=EC=82=AC=EC=9A=A9=20=EC=98=88?= =?UTF-8?q?=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/test/Staging.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/app/test/Staging.tsx b/src/app/test/Staging.tsx index d19a1ea0..eb218748 100644 --- a/src/app/test/Staging.tsx +++ b/src/app/test/Staging.tsx @@ -1,8 +1,13 @@ +import { useState } from "react"; + import Button from "@/component/Button/Button.tsx"; import { ButtonProvider } from "@/component/Button/ButtonProvider.tsx"; +import Radio from "@/component/common/RadioButton/Radio"; +import RadioButtonGroup from "@/component/common/RadioButton/RadioButtonGroup"; import { DefaultLayout } from "@/layout/DefaultLayout.tsx"; export default function Staging() { + const [selectedValue, setSelectedValue] = useState(); return ( @@ -10,6 +15,13 @@ export default function Staging() { + + ์ฃผ 1ํšŒ + ์›” 1ํšŒ + ๋ถ„๊ธฐ๋ณ„ + ํ”„๋กœ์ ํŠธ ๋๋‚œ ํ›„ + + ๊ธฐ๋ณธ ๋ฒ„ํŠผ ํ•˜๋Š˜์ƒ‰ ๋ฒ„ํŠผ From 6124c7acf6e659a878c6d7da6c08642d33d56c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=86=E1=85=B5=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=B4?= Date: Wed, 10 Jul 2024 02:13:41 +0900 Subject: [PATCH 090/168] =?UTF-8?q?style:=20#11=20=EB=9D=BC=EB=94=94?= =?UTF-8?q?=EC=98=A4=20=EB=B2=84=ED=8A=BC=20cursor=20pointer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/RadioButton/Radio.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/component/common/RadioButton/Radio.tsx b/src/component/common/RadioButton/Radio.tsx index ddaef151..7f5372c0 100644 --- a/src/component/common/RadioButton/Radio.tsx +++ b/src/component/common/RadioButton/Radio.tsx @@ -22,6 +22,7 @@ const Radio = ({ value, children }: RadioProps) => { align-items: center; height: 100%; width: 100%; + cursor: pointer; `} > {children} From 4691b4c998d1bef3e46053917093cbbdf661332b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=86=E1=85=B5=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=B4?= Date: Wed, 10 Jul 2024 19:15:11 +0900 Subject: [PATCH 091/168] =?UTF-8?q?feat:=20#11=20=EB=8B=A4=EC=A4=91=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EB=B2=84=ED=8A=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/test/Staging.tsx | 22 ++++++++- src/component/common/CheckBox/CheckBox.tsx | 45 +++++++++++++++++++ .../common/CheckBox/CheckBoxGroup.tsx | 25 +++++++++++ src/hooks/useCheckBox.ts | 9 ++++ src/layout/GlobalLayout.tsx | 2 +- src/store/context/CheckBoxContext.ts | 8 ++++ 6 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 src/component/common/CheckBox/CheckBox.tsx create mode 100644 src/component/common/CheckBox/CheckBoxGroup.tsx create mode 100644 src/hooks/useCheckBox.ts create mode 100644 src/store/context/CheckBoxContext.ts diff --git a/src/app/test/Staging.tsx b/src/app/test/Staging.tsx index eb218748..0e96836d 100644 --- a/src/app/test/Staging.tsx +++ b/src/app/test/Staging.tsx @@ -1,13 +1,22 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import Button from "@/component/Button/Button.tsx"; import { ButtonProvider } from "@/component/Button/ButtonProvider.tsx"; +import CheckBox from "@/component/common/CheckBox/CheckBox"; +import CheckBoxGroup from "@/component/common/CheckBox/CheckBoxGroup"; import Radio from "@/component/common/RadioButton/Radio"; import RadioButtonGroup from "@/component/common/RadioButton/RadioButtonGroup"; +import { useCheckBox } from "@/hooks/useCheckBox"; import { DefaultLayout } from "@/layout/DefaultLayout.tsx"; export default function Staging() { const [selectedValue, setSelectedValue] = useState(); + const { isChecked, toggle, selectedValues } = useCheckBox(); + + useEffect(() => { + console.log("selectedValues", selectedValues); + }, [selectedValues]); + return ( @@ -15,6 +24,8 @@ export default function Staging() { +
+

๋ผ๋””์˜ค๋ฒ„ํŠผ

์ฃผ 1ํšŒ ์›” 1ํšŒ @@ -22,6 +33,15 @@ export default function Staging() { ํ”„๋กœ์ ํŠธ ๋๋‚œ ํ›„ +
+

์ฒดํฌ๋ฐ•์Šค

+ + ์ฃผ 1ํšŒ + ์›” 1ํšŒ + ๋ถ„๊ธฐ๋ณ„ + ํ”„๋กœ์ ํŠธ ๋๋‚œ ํ›„ + + ๊ธฐ๋ณธ ๋ฒ„ํŠผ ํ•˜๋Š˜์ƒ‰ ๋ฒ„ํŠผ diff --git a/src/component/common/CheckBox/CheckBox.tsx b/src/component/common/CheckBox/CheckBox.tsx new file mode 100644 index 00000000..11072c85 --- /dev/null +++ b/src/component/common/CheckBox/CheckBox.tsx @@ -0,0 +1,45 @@ +import { css } from "@emotion/react"; +import { useContext } from "react"; + +import ListItemCard from "@/component/common/Card/ListItemCard"; +import { CheckBoxContext } from "@/store/context/CheckBoxContext"; + +type CheckBoxProps = { + value: string; + children: React.ReactNode; +}; + +const CheckBox = ({ value, children }: CheckBoxProps) => { + const checkboxContext = useContext(CheckBoxContext); + return ( + + + { + checkboxContext?.onChange && checkboxContext.onChange(e.target.value); + }} + css={css` + display: none; + `} + /> + + ); +}; + +export default CheckBox; diff --git a/src/component/common/CheckBox/CheckBoxGroup.tsx b/src/component/common/CheckBox/CheckBoxGroup.tsx new file mode 100644 index 00000000..5b8078c8 --- /dev/null +++ b/src/component/common/CheckBox/CheckBoxGroup.tsx @@ -0,0 +1,25 @@ +import { css } from "@emotion/react"; + +import { CheckBoxContext } from "@/store/context/CheckBoxContext"; + +type CheckBoxGroupProps = { + children: React.ReactNode; + isChecked: (value: string) => boolean; + onChange: (value: string) => void; +}; + +const CheckBoxGroup = ({ children, ...rest }: CheckBoxGroupProps) => { + return ( +
+ {children} +
+ ); +}; + +export default CheckBoxGroup; diff --git a/src/hooks/useCheckBox.ts b/src/hooks/useCheckBox.ts new file mode 100644 index 00000000..e919baae --- /dev/null +++ b/src/hooks/useCheckBox.ts @@ -0,0 +1,9 @@ +import { useState } from "react"; + +export const useCheckBox = () => { + const [checkedStates, setCheckedStates] = useState>({}); + const isChecked = (value: string) => checkedStates[value]; + const toggle = (value: string) => setCheckedStates((prev) => ({ ...prev, [value]: !prev[value] })); + const selectedValues = Object.keys(checkedStates).filter((key) => checkedStates[key]); + return { isChecked, toggle, selectedValues }; +}; diff --git a/src/layout/GlobalLayout.tsx b/src/layout/GlobalLayout.tsx index 4c5184a6..db422bf4 100644 --- a/src/layout/GlobalLayout.tsx +++ b/src/layout/GlobalLayout.tsx @@ -17,7 +17,7 @@ export default function GlobalLayout() { display: flex; flex-direction: column; - background-color: #f1f3f5; + background-color: #ffffff; `} > diff --git a/src/store/context/CheckBoxContext.ts b/src/store/context/CheckBoxContext.ts new file mode 100644 index 00000000..68291f37 --- /dev/null +++ b/src/store/context/CheckBoxContext.ts @@ -0,0 +1,8 @@ +import { createContext } from "react"; + +type CheckBoxContext = { + isChecked: (value: string) => boolean; + onChange: (value: string) => void; +}; + +export const CheckBoxContext = createContext(undefined); From 71ef0c0057216279d9439148c1c029e02e3ea43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=86=E1=85=B5=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=B4?= Date: Wed, 10 Jul 2024 19:29:40 +0900 Subject: [PATCH 092/168] =?UTF-8?q?refactor:=20#11=20=EB=9D=BC=EB=94=94?= =?UTF-8?q?=EC=98=A4=EB=B2=84=ED=8A=BC=20=ED=9B=85=20=EC=9E=91=EC=84=B1,?= =?UTF-8?q?=20=EC=B2=B4=ED=81=AC=EB=B0=95=EC=8A=A4=EC=99=80=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=20=EB=B0=A9=EC=8B=9D=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/test/Staging.tsx | 15 ++++++++++----- src/component/common/RadioButton/Radio.tsx | 2 +- .../common/RadioButton/RadioButtonGroup.tsx | 4 ++-- src/hooks/useCheckBox.ts | 6 ++++-- src/hooks/useRadioButton.ts | 11 +++++++++++ src/store/context/RadioContext.ts | 4 ++-- 6 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 src/hooks/useRadioButton.ts diff --git a/src/app/test/Staging.tsx b/src/app/test/Staging.tsx index 0e96836d..61723872 100644 --- a/src/app/test/Staging.tsx +++ b/src/app/test/Staging.tsx @@ -7,14 +7,19 @@ import CheckBoxGroup from "@/component/common/CheckBox/CheckBoxGroup"; import Radio from "@/component/common/RadioButton/Radio"; import RadioButtonGroup from "@/component/common/RadioButton/RadioButtonGroup"; import { useCheckBox } from "@/hooks/useCheckBox"; +import { useRadioButton } from "@/hooks/useRadioButton"; import { DefaultLayout } from "@/layout/DefaultLayout.tsx"; export default function Staging() { - const [selectedValue, setSelectedValue] = useState(); - const { isChecked, toggle, selectedValues } = useCheckBox(); + const [isRadioChecked, onChange, selectedValue] = useRadioButton(); + const [isCheckBoxChecked, toggle, selectedValues] = useCheckBox(); useEffect(() => { - console.log("selectedValues", selectedValues); + console.log("๋ผ๋””์˜ค ๋ฒ„ํŠผ ์„ ํƒ value:", selectedValue); + }, [selectedValue]); + + useEffect(() => { + console.log("์ฒดํฌ๋ฐ•์Šค ์„ ํƒ values:", selectedValues); }, [selectedValues]); return ( @@ -26,7 +31,7 @@ export default function Staging() {

๋ผ๋””์˜ค๋ฒ„ํŠผ

- + ์ฃผ 1ํšŒ ์›” 1ํšŒ ๋ถ„๊ธฐ๋ณ„ @@ -35,7 +40,7 @@ export default function Staging() {

์ฒดํฌ๋ฐ•์Šค

- + ์ฃผ 1ํšŒ ์›” 1ํšŒ ๋ถ„๊ธฐ๋ณ„ diff --git a/src/component/common/RadioButton/Radio.tsx b/src/component/common/RadioButton/Radio.tsx index 7f5372c0..1dda467c 100644 --- a/src/component/common/RadioButton/Radio.tsx +++ b/src/component/common/RadioButton/Radio.tsx @@ -12,7 +12,7 @@ type RadioProps = { const Radio = ({ value, children }: RadioProps) => { const radioContext = useContext(RadioContext); return ( - +
+ +

์™„์ „ ์ด๊ฑฐ ๋Ÿญํ‚ค๋น„ํ‚ค์ž๋ƒ?

+ + } + handler={true} + />
); } diff --git a/src/assets/svgs/bottom-sheet/ic_quit.svg b/src/assets/svgs/bottom-sheet/ic_quit.svg new file mode 100644 index 00000000..9d67cb28 --- /dev/null +++ b/src/assets/svgs/bottom-sheet/ic_quit.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/svgs/bottom-sheet/index.ts b/src/assets/svgs/bottom-sheet/index.ts new file mode 100644 index 00000000..0b4bd1bc --- /dev/null +++ b/src/assets/svgs/bottom-sheet/index.ts @@ -0,0 +1 @@ +export { default as ic_quit } from "./ic_quit.svg?react"; diff --git a/src/assets/svgs/index.ts b/src/assets/svgs/index.ts index c92002b3..0ef0236a 100644 --- a/src/assets/svgs/index.ts +++ b/src/assets/svgs/index.ts @@ -1,2 +1,3 @@ export * from "./common"; export * from "./toast"; +export * from "./bottom-sheet"; diff --git a/src/component/BottomSheet/BottomSheet.tsx b/src/component/BottomSheet/BottomSheet.tsx new file mode 100644 index 00000000..254fb359 --- /dev/null +++ b/src/component/BottomSheet/BottomSheet.tsx @@ -0,0 +1,73 @@ +import { css } from "@emotion/react"; +import { Fragment, ReactElement, useEffect, useState } from "react"; + +import { MIN_Y } from "@/component/BottomSheet/BottomSheetOption.ts"; +import { BottomSheetContent, BottomSheetHeader } from "@/component/BottomSheet/component"; +import { useBottomSheet, useSetBottomSheet } from "@/hooks/useBottomSheet.ts"; + +export type BottomSheetType = { + title?: string; + contents: ReactElement; + handler: boolean; + sheetHeight?: number; +}; + +export function BottomSheet({ title, contents, handler = false, sheetHeight = 349 }: BottomSheetType) { + const { sheet, content } = useSetBottomSheet({ handler, sheetHeight }); + const { bottomSheetState } = useBottomSheet(); + const [height, _] = useState(sheetHeight); + + useEffect(() => { + sheet.current!.style.setProperty("transform", ``); + }, [bottomSheetState]); + + return ( + +
+
+ + {contents} +
+ + ); +} diff --git a/src/component/BottomSheet/BottomSheetOption.ts b/src/component/BottomSheet/BottomSheetOption.ts new file mode 100644 index 00000000..53750444 --- /dev/null +++ b/src/component/BottomSheet/BottomSheetOption.ts @@ -0,0 +1,4 @@ +export const MIN_Y = 0; // ๋ฐ”ํ…€์‹œํŠธ๊ฐ€ ์ตœ๋Œ€๋กœ ๋†’์ด ์˜ฌ๋ผ๊ฐ”์„ ๋•Œ์˜ y ๊ฐ’ +export const MAX_Y = window.innerHeight - 80; // ๋ฐ”ํ…€์‹œํŠธ๊ฐ€ ์ตœ์†Œ๋กœ ๋‚ด๋ ค๊ฐ”์„ ๋•Œ์˜ y ๊ฐ’ +export const BOTTOM_SHEET_MAX_HEIGHT = 349; // ์ผ๋ฐ˜ ๋ฐ”ํ…€ ์‹œํŠธ๊ฐ€ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ตœ๋Œ€๋กœ ๋ณด์—ฌ์ง€๋Š” ์˜์—ญ +export const CONTROLLABLE_BOTTOM_SHEET_MAX_HEIGHT = 482; // ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์กด์žฌํ•˜๋Š” ๋ฐ”ํ…€ ์‹œํŠธ๊ฐ€ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ตœ๋Œ€๋กœ ๋ณด์—ฌ์ง€๋Š” ์˜์—ญ diff --git a/src/component/BottomSheet/component/BottomSheetContent.tsx b/src/component/BottomSheet/component/BottomSheetContent.tsx new file mode 100644 index 00000000..ca9c7538 --- /dev/null +++ b/src/component/BottomSheet/component/BottomSheetContent.tsx @@ -0,0 +1,33 @@ +import { css } from "@emotion/react"; +import { forwardRef, PropsWithChildren } from "react"; + +export const BottomSheetContent = forwardRef(function Content({ children }, ref) { + return ( +
+ {children} +
+ ); +}); diff --git a/src/component/BottomSheet/component/BottomSheetHeader.tsx b/src/component/BottomSheet/component/BottomSheetHeader.tsx new file mode 100644 index 00000000..62c62f6b --- /dev/null +++ b/src/component/BottomSheet/component/BottomSheetHeader.tsx @@ -0,0 +1,65 @@ +import { css } from "@emotion/react"; + +import { BottomSheetType } from "@/component/BottomSheet/BottomSheet.tsx"; +import Icon from "@/component/common/Icon/Icon.tsx"; +import { useBottomSheet } from "@/hooks/useBottomSheet.ts"; + +export function BottomSheetHeader({ title, handler }: Pick) { + const { closeBottomSheet } = useBottomSheet(); + + if (!title && !handler) return; + return ( +
+ {handler && ( +
+ )} + {title && ( +
+ + {title} + + {/* FIXME: ์•„์ด์ฝ˜ ์˜์—ญ */} + +
+ )} +
+ ); +} diff --git a/src/component/BottomSheet/component/index.ts b/src/component/BottomSheet/component/index.ts new file mode 100644 index 00000000..42f12fb9 --- /dev/null +++ b/src/component/BottomSheet/component/index.ts @@ -0,0 +1,2 @@ +export { BottomSheetContent } from "./BottomSheetContent.tsx"; +export { BottomSheetHeader } from "./BottomSheetHeader.tsx"; diff --git a/src/component/BottomSheet/index.ts b/src/component/BottomSheet/index.ts new file mode 100644 index 00000000..1bb75a0e --- /dev/null +++ b/src/component/BottomSheet/index.ts @@ -0,0 +1 @@ +export { BottomSheet } from "./BottomSheet.tsx"; diff --git a/src/hooks/useBottomSheet.ts b/src/hooks/useBottomSheet.ts new file mode 100644 index 00000000..30f99dd5 --- /dev/null +++ b/src/hooks/useBottomSheet.ts @@ -0,0 +1,171 @@ +import { useRef, useEffect } from "react"; + +import { BottomSheetType } from "@/component/BottomSheet/BottomSheet.tsx"; +import { MIN_Y, MAX_Y } from "@/component/BottomSheet/BottomSheetOption.ts"; +import { useStore } from "@/utils/externalStore/create.ts"; +import { externalStore } from "@/utils/externalStore/hook.ts"; + +type BottomSheetMetrics = { + touchStart: { + sheetY: number; + touchY: number; + }; + touchMove: { + prevTouchY?: number; + movingDirection: "none" | "down" | "up"; + }; + isContentAreaTouched: boolean; +}; + +const bottomSheetStore = externalStore(false); + +export const useBottomSheet = () => { + const [bottomSheetState, setBottomSheetState] = useStore(bottomSheetStore); + const openBottomSheet = () => { + setBottomSheetState(true); + }; + + const closeBottomSheet = () => { + setBottomSheetState(false); + }; + + return { openBottomSheet, closeBottomSheet, bottomSheetState } as const; +}; + +export const useSetBottomSheet = ({ handler, sheetHeight }: Pick) => { + const { bottomSheetState, closeBottomSheet } = useBottomSheet(); + const sheet = useRef(null); + const content = useRef(null); + const metrics = useRef({ + touchStart: { + sheetY: 0, + touchY: 0, + }, + touchMove: { + prevTouchY: 0, + movingDirection: "none", + }, + isContentAreaTouched: false, + }); + + useEffect(() => { + // Even if there is no handler bar, it doesn't fire the event. + if (!handler) return; + const canUserMoveBottomSheet = () => { + const { touchMove, isContentAreaTouched } = metrics.current; + + if (!isContentAreaTouched) { + return true; + } + + if (touchMove.movingDirection === "down") { + return content.current!.scrollTop <= 0; + } + return false; + }; + + /** Bottom Sheet Move Start Event Handler */ + const handleStart = (e: TouchEvent | MouseEvent, method: string = "touch") => { + const { touchStart } = metrics.current; + + touchStart.sheetY = sheet.current!.getBoundingClientRect().y; + touchStart.touchY = method === "touch" ? (e as TouchEvent).touches[0].clientY : (e as MouseEvent).clientY; + + if (method === "mouse") { + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + } + }; + + /** Bottom Sheet Move Event Handler */ + const handleMove = (e: TouchEvent | MouseEvent, method: string = "touch") => { + const { touchStart, touchMove } = metrics.current; + const currentTouch = method === "touch" ? (e as TouchEvent).touches[0].clientY : (e as MouseEvent).clientY; + + if (currentTouch === touchStart.touchY) return; + if (touchMove.prevTouchY === undefined) touchMove.prevTouchY = touchStart.touchY; + if (touchMove.prevTouchY === 0) touchMove.prevTouchY = touchStart.touchY; + if (touchMove.prevTouchY < currentTouch) touchMove.movingDirection = "down"; + if (touchMove.prevTouchY > currentTouch) touchMove.movingDirection = "up"; + + if (canUserMoveBottomSheet()) { + e.preventDefault(); + const touchOffset = currentTouch - touchStart.touchY; + let nextSheetY = touchStart.sheetY + touchOffset; + if (nextSheetY <= MIN_Y) nextSheetY = MIN_Y; + if (nextSheetY >= MAX_Y) nextSheetY = MAX_Y; + if (Math.abs(nextSheetY - MAX_Y) > (sheetHeight as number)) return; + + sheet.current!.style.setProperty("transform", `translate3d(-50%, ${nextSheetY - MAX_Y}px, 0)`); + } else { + document.body.style.overflowY = "hidden"; + } + }; + + /** Bottom Sheet Move End Event Handler */ + const handleEnd = (e: TouchEvent | MouseEvent, method = "touch") => { + document.body.style.overflowY = "auto"; + const { touchMove } = metrics.current; + const currentSheetY = sheet.current!.getBoundingClientRect().y; + + if (currentSheetY !== MIN_Y) { + if (touchMove.movingDirection === "down") { + if (method === "touch") { + if (content.current!.scrollTop !== 0 && content.current!.contains(e.target)) return; + } + sheet.current!.style.setProperty("transform", "translate3d(-50%, 0, 0)"); + closeBottomSheet(); + } + + if (touchMove.movingDirection === "up") { + sheet.current!.style.setProperty("transform", `translate3d(-50%, -${sheetHeight}px, 0)`); + } + } + metrics.current = { + touchStart: { + sheetY: 0, + touchY: 0, + }, + touchMove: { + prevTouchY: 0, + movingDirection: "none", + }, + isContentAreaTouched: false, + }; + + if (method === "mouse") { + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + } + }; + + const handleMouseDown = (e: MouseEvent) => handleStart(e, "mouse"); + const handleMouseMove = (e: MouseEvent) => handleMove(e, "mouse"); + const handleMouseUp = (e: MouseEvent) => handleEnd(e, "mouse"); + const handleTouchStart = (e: TouchEvent) => handleStart(e, "touch"); + const handleTouchMove = (e: TouchEvent) => handleMove(e, "touch"); + const handleTouchEnd = (e: TouchEvent) => handleEnd(e, "touch"); + + sheet.current!.addEventListener("mousedown", handleMouseDown); + sheet.current!.addEventListener("touchstart", handleTouchStart); + sheet.current!.addEventListener("touchmove", handleTouchMove); + sheet.current!.addEventListener("touchend", handleTouchEnd); + }, []); + + useEffect(() => { + const handleTouchStart = () => { + metrics.current.isContentAreaTouched = true; + }; + content.current!.addEventListener("touchstart", handleTouchStart); + }, []); + + useEffect(() => { + if (!bottomSheetState) return; + document.body.style.overflow = "hidden"; + return () => { + document.body.style.overflow = "unset"; + }; + }, [bottomSheetState]); + + return { sheet, content }; +}; diff --git a/src/util/.gitkeep b/src/utils/.gitkeep similarity index 100% rename from src/util/.gitkeep rename to src/utils/.gitkeep diff --git a/src/utils/externalStore/create.ts b/src/utils/externalStore/create.ts new file mode 100644 index 00000000..25fa7c98 --- /dev/null +++ b/src/utils/externalStore/create.ts @@ -0,0 +1,9 @@ +import { useSyncExternalStore } from "react"; + +import { State } from "@/utils/externalStore/hook.ts"; + +export function useStore(state: State) { + const store = useSyncExternalStore(state.subscribe, state.getState, state.getState); + + return [store, state.setState] as const; +} diff --git a/src/utils/externalStore/hook.ts b/src/utils/externalStore/hook.ts new file mode 100644 index 00000000..bb75e607 --- /dev/null +++ b/src/utils/externalStore/hook.ts @@ -0,0 +1,37 @@ +type Fn = () => void; +type UpdateFn = (state: T) => T; + +export type State = { + getState: () => T; + setState: (update: UpdateFn | T) => void; + subscribe: (callback: Fn) => Fn; +}; + +function isUpdateFn(value: UpdateFn | T): value is UpdateFn { + return typeof value === "function"; +} + +export function externalStore(initialState: T): State { + let state = initialState; + const callbacks = new Set(); + + function subscribe(callback: Fn): Fn { + callbacks.add(callback); + return () => callbacks.delete(callback); + } + + function getState() { + return state; + } + + function setState(update: UpdateFn | T) { + state = isUpdateFn(update) ? update(state) : update; + callbacks.forEach((cb) => cb()); + } + + return { + getState, + setState, + subscribe, + }; +} From 8a37a8716f40e1eff2a60936458f0d6e29011c63 Mon Sep 17 00:00:00 2001 From: klmhyeonwoo Date: Sun, 14 Jul 2024 22:32:17 +0900 Subject: [PATCH 111/168] fix: #33 Remove unused variables --- src/component/BottomSheet/BottomSheetOption.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/component/BottomSheet/BottomSheetOption.ts b/src/component/BottomSheet/BottomSheetOption.ts index 53750444..7131736d 100644 --- a/src/component/BottomSheet/BottomSheetOption.ts +++ b/src/component/BottomSheet/BottomSheetOption.ts @@ -1,4 +1,2 @@ export const MIN_Y = 0; // ๋ฐ”ํ…€์‹œํŠธ๊ฐ€ ์ตœ๋Œ€๋กœ ๋†’์ด ์˜ฌ๋ผ๊ฐ”์„ ๋•Œ์˜ y ๊ฐ’ export const MAX_Y = window.innerHeight - 80; // ๋ฐ”ํ…€์‹œํŠธ๊ฐ€ ์ตœ์†Œ๋กœ ๋‚ด๋ ค๊ฐ”์„ ๋•Œ์˜ y ๊ฐ’ -export const BOTTOM_SHEET_MAX_HEIGHT = 349; // ์ผ๋ฐ˜ ๋ฐ”ํ…€ ์‹œํŠธ๊ฐ€ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ตœ๋Œ€๋กœ ๋ณด์—ฌ์ง€๋Š” ์˜์—ญ -export const CONTROLLABLE_BOTTOM_SHEET_MAX_HEIGHT = 482; // ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์กด์žฌํ•˜๋Š” ๋ฐ”ํ…€ ์‹œํŠธ๊ฐ€ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ตœ๋Œ€๋กœ ๋ณด์—ฌ์ง€๋Š” ์˜์—ญ From 9c5dc22518d8b3e20e84e7832dafc43209f676d1 Mon Sep 17 00:00:00 2001 From: donghunee Date: Mon, 15 Jul 2024 00:36:00 +0900 Subject: [PATCH 112/168] feat: #11 toast utils --- src/util/etc.ts | 1 + src/util/toast/collapseToast.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/util/etc.ts create mode 100644 src/util/toast/collapseToast.ts diff --git a/src/util/etc.ts b/src/util/etc.ts new file mode 100644 index 00000000..dcd998f9 --- /dev/null +++ b/src/util/etc.ts @@ -0,0 +1 @@ +export const getRandomID = () => String(new Date().getTime()); diff --git a/src/util/toast/collapseToast.ts b/src/util/toast/collapseToast.ts new file mode 100644 index 00000000..c8b5c815 --- /dev/null +++ b/src/util/toast/collapseToast.ts @@ -0,0 +1,15 @@ +export function collapseToast(node: HTMLDivElement, done: () => void, duration = 3000) { + if (node) { + const { scrollHeight, style } = node; + + requestAnimationFrame(() => { + style.height = scrollHeight + "px"; + style.transition = `all ${duration}ms`; + + requestAnimationFrame(() => { + style.height = "0"; + setTimeout(done, duration); + }); + }); + } +} From 1d5d44f42d7d362d8eee260ed9497e778e9e2343 Mon Sep 17 00:00:00 2001 From: donghunee Date: Mon, 15 Jul 2024 00:36:33 +0900 Subject: [PATCH 113/168] =?UTF-8?q?feat:=20#11=20toast=20type=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/types/toast.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/types/toast.ts diff --git a/src/types/toast.ts b/src/types/toast.ts new file mode 100644 index 00000000..ad04209c --- /dev/null +++ b/src/types/toast.ts @@ -0,0 +1,11 @@ +import { toastMap } from "@/style/common/toast"; + +export type ToastMessageType = keyof typeof toastMap; + +export type ToastType = { + type: ToastMessageType; + id: string; + content: string; + duration?: number; + bottom?: number; +}; From 8a47283d72d751048e24f223eaa3ebdd18f3f7c3 Mon Sep 17 00:00:00 2001 From: donghunee Date: Mon, 15 Jul 2024 00:37:02 +0900 Subject: [PATCH 114/168] =?UTF-8?q?feat:=20#11=20toast=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20style=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/style/common/animation.ts | 30 ++++++++++++++++++++++++++++++ src/style/common/toast.ts | 14 ++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/style/common/animation.ts create mode 100644 src/style/common/toast.ts diff --git a/src/style/common/animation.ts b/src/style/common/animation.ts new file mode 100644 index 00000000..2d69c4a4 --- /dev/null +++ b/src/style/common/animation.ts @@ -0,0 +1,30 @@ +import { keyframes } from "@emotion/react"; + +export const ANIMATION = { + FADE_IN: keyframes` + from { + opacity: 0; + } + to { + opacity: 1; + } + `, + FADE_OUT: keyframes` + from { + opacity: 1; + transform: translateY(0); + } + to { + opacity: 0; + transform: translateY(-1.5rem); + } + `, + TRIGGER: keyframes` + from { + opacity: 1; + } + to { + opacity: 1; + } + `, +}; diff --git a/src/style/common/toast.ts b/src/style/common/toast.ts new file mode 100644 index 00000000..14356f94 --- /dev/null +++ b/src/style/common/toast.ts @@ -0,0 +1,14 @@ +import { css } from "@emotion/react"; + +export const toastMap = { + success: css` + background-color: rgba(33, 37, 41, 0.8); + color: #ffffff; + `, + error: css` + background-color: rgba(225, 74, 74, 0.8); + color: #ffffff; + `, +}; + +export type ToastMessageType = keyof typeof toastMap; From 16accd9680bbe0a80ab2d10e89927fd703a4d2ec Mon Sep 17 00:00:00 2001 From: donghunee Date: Mon, 15 Jul 2024 00:37:48 +0900 Subject: [PATCH 115/168] =?UTF-8?q?feat:=20#11=20toast=20atom=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/toast/toastAtom.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/store/toast/toastAtom.ts diff --git a/src/store/toast/toastAtom.ts b/src/store/toast/toastAtom.ts new file mode 100644 index 00000000..dc1af4d0 --- /dev/null +++ b/src/store/toast/toastAtom.ts @@ -0,0 +1,5 @@ +import { atom } from "jotai"; + +import { ToastType } from "@/types/toast"; + +export const toastState = atom([]); From af093d6dff0f87ab0589a79efb47ada38a50bca9 Mon Sep 17 00:00:00 2001 From: donghunee Date: Mon, 15 Jul 2024 00:38:25 +0900 Subject: [PATCH 116/168] =?UTF-8?q?feat:=20#11=20useToast=20hook=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/hooks/useToast.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/hooks/useToast.ts diff --git a/src/hooks/useToast.ts b/src/hooks/useToast.ts new file mode 100644 index 00000000..d6feb6b9 --- /dev/null +++ b/src/hooks/useToast.ts @@ -0,0 +1,29 @@ +import { useAtom } from "jotai"; +import { useCallback } from "react"; + +import { toastState } from "@/store/toast/toastAtom"; +import { ToastType } from "@/types/toast"; +import { getRandomID } from "@/util/etc"; + +export const useToast = () => { + const [toastDataState, setToastDataState] = useAtom(toastState); + + const removeToast = useCallback( + (toastID: ToastType["id"]) => setToastDataState((prev) => prev.filter((toast) => toast.id !== toastID)), + [setToastDataState], + ); + + const emitToast = useCallback( + (toast: Omit) => { + setToastDataState((prev) => [...prev, { ...toast, id: getRandomID() }]); + }, + [setToastDataState], + ); + + const toast = { + success: (content: string) => emitToast({ type: "success", content }), + error: (content: string) => emitToast({ type: "error", content }), + }; + + return { toast, toastDataState, emitToast, removeToast }; +}; From c2f662330c39b13ff098cf493c70d125642dba9d Mon Sep 17 00:00:00 2001 From: donghunee Date: Mon, 15 Jul 2024 00:39:41 +0900 Subject: [PATCH 117/168] =?UTF-8?q?refactor:=20#11=20Icon=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20style=20props=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EC=BD=94=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/Icon/Icon.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/component/common/Icon/Icon.tsx b/src/component/common/Icon/Icon.tsx index 7ee240b1..2c52a221 100644 --- a/src/component/common/Icon/Icon.tsx +++ b/src/component/common/Icon/Icon.tsx @@ -1,7 +1,7 @@ -import { memo } from "react"; +import { css } from "@emotion/react"; +import { CSSProperties, memo } from "react"; import * as icons from "@/assets/svgs"; -import { css } from "@emotion/react"; type IconType = keyof typeof icons; @@ -10,11 +10,13 @@ type Props = { color?: string; size?: string | number; onClick?: () => void; + style?: CSSProperties; }; const DEFAULT_ICON_COLOR = "#000000"; -function Icon({ icon, color = DEFAULT_ICON_COLOR, size = "0.2rem", onClick }: Props) { +export const Icon = memo(function Icon({ icon, color = DEFAULT_ICON_COLOR, size = "0.2rem", onClick, style }: Props) { + // eslint-disable-next-line import/namespace const SVGIcon = icons[icon]; const widthRem = typeof size === "number" ? `${size}rem` : size; @@ -27,8 +29,7 @@ function Icon({ icon, color = DEFAULT_ICON_COLOR, size = "0.2rem", onClick }: Pr width: ${widthRem}; height: auto; `} + style={style} /> ); -} - -export default memo(Icon); +}); From 5f4b67098d899e14134e494b2a041941b070aa59 Mon Sep 17 00:00:00 2001 From: donghunee Date: Mon, 15 Jul 2024 00:40:26 +0900 Subject: [PATCH 118/168] =?UTF-8?q?feat:=20#11=20Portal=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/Portal/Portal.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/component/common/Portal/Portal.tsx diff --git a/src/component/common/Portal/Portal.tsx b/src/component/common/Portal/Portal.tsx new file mode 100644 index 00000000..702f9d6f --- /dev/null +++ b/src/component/common/Portal/Portal.tsx @@ -0,0 +1,12 @@ +import { ReactNode } from "react"; +import { createPortal } from "react-dom"; + +type Props = { + children: ReactNode; + id: string; +}; + +export const Portal = ({ id, children }: Props) => { + const el = document.getElementById(`${id}`) as HTMLElement; + return createPortal(children, el); +}; From 830a59205c6f8a255dd4b7956189c06dfea8e12e Mon Sep 17 00:00:00 2001 From: donghunee Date: Mon, 15 Jul 2024 00:41:03 +0900 Subject: [PATCH 119/168] =?UTF-8?q?feat:=20#11=20Toast=20Component=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/component/common/Toast/ToastItem.tsx | 86 ++++++++++++++++++++++++ src/component/common/Toast/ToastList.tsx | 29 ++++++++ 2 files changed, 115 insertions(+) create mode 100644 src/component/common/Toast/ToastItem.tsx create mode 100644 src/component/common/Toast/ToastList.tsx diff --git a/src/component/common/Toast/ToastItem.tsx b/src/component/common/Toast/ToastItem.tsx new file mode 100644 index 00000000..1db1146a --- /dev/null +++ b/src/component/common/Toast/ToastItem.tsx @@ -0,0 +1,86 @@ +import { css } from "@emotion/react"; +import { useRef, useState } from "react"; + +import { Icon } from "@/component/common/Icon/Icon"; +import { useToast } from "@/hooks/useToast"; +import { ANIMATION } from "@/style/common/animation"; +import { toastMap } from "@/style/common/toast"; +import { ToastType } from "@/types/toast"; +import { collapseToast } from "@/util/toast/collapseToast"; + +export function ToastItem({ type, content, id, duration = 3000 }: ToastType) { + const { removeToast } = useToast(); + const toastRef = useRef(null); + const [isClosing, setIsClosing] = useState(false); + const [isPaused, setIsPaused] = useState(false); + + const handleExitingAnimationEnd = () => { + setIsClosing(true); + collapseToast(toastRef.current!, () => { + removeToast(id); + }); + }; + + const handleTriggerAnimationEnd = () => { + duration && setIsClosing(true); + }; + + const handleClick = () => { + handleTriggerAnimationEnd(); + }; + + const handleMouseEnter = () => { + duration && setIsPaused(true); + }; + + const handleMouseLeave = () => { + if (duration) { + setIsPaused(false); + } + }; + + const getIcon = () => { + switch (type) { + case "success": + return ; + case "error": + return ; + } + }; + + return ( +
+
+ {getIcon()} + {content} +
+
+
+ ); +} diff --git a/src/component/common/Toast/ToastList.tsx b/src/component/common/Toast/ToastList.tsx new file mode 100644 index 00000000..3c7fd712 --- /dev/null +++ b/src/component/common/Toast/ToastList.tsx @@ -0,0 +1,29 @@ +import { css } from "@emotion/react"; + +import { Portal } from "@/component/common/Portal/Portal"; +import { ToastItem } from "@/component/common/Toast/ToastItem"; +import { useToast } from "@/hooks/useToast"; + +export function ToastList() { + const { toastDataState } = useToast(); + + return ( + +
+ {toastDataState.map((toast) => ( + + ))} +
+
+ ); +} From 4ebdd8915614405fa247a773008e194e3e75a2a5 Mon Sep 17 00:00:00 2001 From: donghunee Date: Mon, 15 Jul 2024 00:42:27 +0900 Subject: [PATCH 120/168] =?UTF-8?q?refactor:=20#11=20Toast=20=EC=98=88?= =?UTF-8?q?=EC=8B=9C=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20convention=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/MainPage.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/app/MainPage.tsx b/src/app/MainPage.tsx index 81bf5b6b..3ceca3df 100644 --- a/src/app/MainPage.tsx +++ b/src/app/MainPage.tsx @@ -1,14 +1,18 @@ /*NOTE - ํ•ด๋‹น ํŒŒ์ผ์€ ๋ฃจํŠธ router๋ฅผ ์œ„ํ•œ ์ž„์‹œ ํŽ˜์ด์ง€์ž…๋‹ˆ๋‹ค. ์‹ค์ œ ํŽ˜์ด์ง€ ์ž‘์„ฑ ํ›„ ์ง€์›Œ์ฃผ์„ธ์š”! */ import { useAtom } from "jotai"; +import Button from "@/component/Button/Button"; +import { Icon } from "@/component/common/Icon/Icon"; +import { Modal } from "@/component/common/Modal/Modal"; +import { ToastList } from "@/component/common/Toast/ToastList"; +import { useModal } from "@/hooks/useModal"; +import { useToast } from "@/hooks/useToast"; import { messageAtom } from "@/store/messageAtom.tsx"; -import Modal from "@/component/common/modal/Modal"; -import useModal from "@/hooks/useModal"; -import Icon from "@/component/common/Icon/Icon"; function MainPage() { const [message] = useAtom(messageAtom); const { open } = useModal(); + const { toast } = useToast(); return ( <> @@ -22,7 +26,13 @@ function MainPage() { + + + + ); } From 32797b605470f4d8d347aac0028011a7f1a839c1 Mon Sep 17 00:00:00 2001 From: donghunee Date: Mon, 15 Jul 2024 00:45:28 +0900 Subject: [PATCH 121/168] =?UTF-8?q?refactor:=20#28=20convention=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/modal/Modal.tsx | 15 +++++++-------- src/component/common/modal/ModalPortal.tsx | 13 ------------- src/hooks/useModal.ts | 11 +++++------ src/store/modal/modalAtom.ts | 3 ++- 4 files changed, 14 insertions(+), 28 deletions(-) delete mode 100644 src/component/common/modal/ModalPortal.tsx diff --git a/src/component/common/modal/Modal.tsx b/src/component/common/modal/Modal.tsx index 50189c8c..8b51664c 100644 --- a/src/component/common/modal/Modal.tsx +++ b/src/component/common/modal/Modal.tsx @@ -1,9 +1,10 @@ -import ModalPortal from "@/component/common/modal/ModalPortal"; -import useModal from "@/hooks/useModal"; import { css } from "@emotion/react"; import { useEffect, useRef } from "react"; -function Modal() { +import { Portal } from "@/component/common/Portal/Portal"; +import { useModal } from "@/hooks/useModal"; + +export function Modal() { const { modalDataState, close } = useModal(); const modalRef = useRef(null); @@ -18,7 +19,7 @@ function Modal() { return () => { document.removeEventListener("mousedown", listener); }; - }, []); + }, [close]); // ๋ชจ๋‹ฌ ์˜คํ”ˆ ์‹œ ์Šคํฌ๋กค ๊ธˆ์ง€ useEffect(() => { @@ -30,7 +31,7 @@ function Modal() { return ( // FIXME: ์ถ”ํ›„ ๋””์ž์ธ ํ† ํฐ ์—ฐ๋™ ํ›„ ์ปฌ๋Ÿฌ ๊ฐ’ ๋ณ€๊ฒฝ // FIXME: ์ถ”ํ›„ Modal ๋””์ž์ธ ํ™•์ • ์‹œ Body ํ˜•ํƒœ ๋ณ€๊ฒฝ - +
ํ™•์ธ
- + ); } - -export default Modal; diff --git a/src/component/common/modal/ModalPortal.tsx b/src/component/common/modal/ModalPortal.tsx deleted file mode 100644 index 5a2de12e..00000000 --- a/src/component/common/modal/ModalPortal.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { ReactNode } from "react"; -import ReactDom from "react-dom"; - -type Props = { - children: ReactNode; -}; - -const ModalPortal = ({ children }: Props) => { - const el = document.getElementById("modal-root") as HTMLElement; - return ReactDom.createPortal(children, el); -}; - -export default ModalPortal; diff --git a/src/hooks/useModal.ts b/src/hooks/useModal.ts index 341e074b..dd68fb42 100644 --- a/src/hooks/useModal.ts +++ b/src/hooks/useModal.ts @@ -1,14 +1,15 @@ -import { modalState } from "@/store/modal/modalAtom"; -import { ModalType } from "@/types/modal"; import { useAtom } from "jotai"; import { useCallback } from "react"; -const useModal = () => { +import { modalState } from "@/store/modal/modalAtom"; +import { ModalType } from "@/types/modal"; + +export const useModal = () => { const [modalDataState, setModalDataState] = useAtom(modalState); const close = useCallback(() => { setModalDataState({ ...modalDataState, isOpen: false }); - }, [setModalDataState]); + }, [modalDataState, setModalDataState]); const open = useCallback( ({ content, title, callBack }: Omit) => { @@ -28,5 +29,3 @@ const useModal = () => { modalDataState, }; }; - -export default useModal; diff --git a/src/store/modal/modalAtom.ts b/src/store/modal/modalAtom.ts index 97c210f6..de534011 100644 --- a/src/store/modal/modalAtom.ts +++ b/src/store/modal/modalAtom.ts @@ -1,6 +1,7 @@ -import { ModalType } from "@/types/modal"; import { atom } from "jotai"; +import { ModalType } from "@/types/modal"; + export const modalState = atom({ isOpen: false, title: "", From d3e557c907c22a72fdb56c11c60250640a4c04db Mon Sep 17 00:00:00 2001 From: donghunee Date: Mon, 15 Jul 2024 01:06:33 +0900 Subject: [PATCH 122/168] =?UTF-8?q?fix:=20#11=20Modal=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.cjs | 110 +++++++++--------- index.html | 1 + .../{button => Button}/SocialLoginButton.tsx | 0 .../common/{modal => Modal}/Modal.tsx | 0 4 files changed, 53 insertions(+), 58 deletions(-) rename src/component/{button => Button}/SocialLoginButton.tsx (100%) rename src/component/common/{modal => Modal}/Modal.tsx (100%) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 29a917b3..57fbf569 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,63 +1,57 @@ /* eslint-env node */ module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-type-checked", - "plugin:react-hooks/recommended", - "plugin:react/recommended", - "plugin:import/recommended", - "prettier", + root: true, + env: { browser: true, es2020: true }, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-type-checked", + "plugin:react-hooks/recommended", + "plugin:react/recommended", + "plugin:import/recommended", + "prettier", + ], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + project: true, + tsconfigRootDir: __dirname, + }, + ignorePatterns: ["postcss.config.cjs", "dist", ".eslintrc.cjs", "**/*.cjs"], + plugins: ["react-refresh", "react", "import"], + rules: { + "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }], + "react/no-unknown-property": ["error", { ignore: ["css"] }], + "@typescript-eslint/no-misused-promises": "off", + "react-refresh/only-export-components": ["off", { allowConstantExport: true }], + "react/react-in-jsx-scope": "off", + "no-restricted-imports": ["error", { patterns: ["../*", "../**/*"] }], + "import/newline-after-import": "warn", + "import/no-default-export": "warn", + "import/order": [ + "warn", + { + "newlines-between": "always", + alphabetize: { + order: "asc", + caseInsensitive: true, + }, + }, ], - parser: "@typescript-eslint/parser", - parserOptions: { - ecmaVersion: "latest", - sourceType: "module", - project: true, - tsconfigRootDir: __dirname, - }, - ignorePatterns: ["postcss.config.cjs", "dist", ".eslintrc.cjs", "**/*.cjs"], - plugins: ["react-refresh", "react", "import"], - rules: { - "@typescript-eslint/no-unused-vars": [ - "warn", - { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, - ], - "react/no-unknown-property": ["error", { "ignore": ["css"] }], - "@typescript-eslint/no-misused-promises": "off", - "react-refresh/only-export-components": [ - "off", - { allowConstantExport: true }, - ], - "react/react-in-jsx-scope": "off", - "no-restricted-imports": ["error", { patterns: ["../*", "../**/*"] }], - "import/newline-after-import": "warn", - "import/no-default-export": "warn", - "import/order": [ - "warn", - { - "newlines-between": "always", - alphabetize: { - order: "asc", - caseInsensitive: true, - }, - }, - ], + }, + settings: { + "import/parsers": { + "@typescript-eslint/parser": [".ts", ".tsx"], }, - settings: { - "import/parsers": { - "@typescript-eslint/parser": [".ts", ".tsx"], - }, - "import/resolver": { - typescript: { - alwaysTryTypes: true, - project: "src/", - }, - node: { - "extensions": [".js", ".jsx", ".ts", ".tsx"] - } - }, + "import/resolver": { + typescript: { + alwaysTryTypes: true, + project: "src/", + }, + node: { + extensions: [".js", ".jsx", ".ts", ".tsx"], + }, }, -}; \ No newline at end of file + }, +}; diff --git a/index.html b/index.html index 684f610a..46004780 100644 --- a/index.html +++ b/index.html @@ -9,6 +9,7 @@
+
diff --git a/src/component/button/SocialLoginButton.tsx b/src/component/Button/SocialLoginButton.tsx similarity index 100% rename from src/component/button/SocialLoginButton.tsx rename to src/component/Button/SocialLoginButton.tsx diff --git a/src/component/common/modal/Modal.tsx b/src/component/common/Modal/Modal.tsx similarity index 100% rename from src/component/common/modal/Modal.tsx rename to src/component/common/Modal/Modal.tsx From 522836200e1948dd7adce9f6729879f8c44c124d Mon Sep 17 00:00:00 2001 From: sean Date: Mon, 15 Jul 2024 04:51:29 +0900 Subject: [PATCH 123/168] =?UTF-8?q?chore:=20#11=20Props=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20AppBar=20=EC=BB=A8?= =?UTF-8?q?=EB=B2=A4=EC=85=98=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{AppBar/AppBar.tsx => common/appBar/index.tsx} | 9 ++------- src/layout/DefaultLayout.tsx | 12 +++++++----- 2 files changed, 9 insertions(+), 12 deletions(-) rename src/component/{AppBar/AppBar.tsx => common/appBar/index.tsx} (85%) diff --git a/src/component/AppBar/AppBar.tsx b/src/component/common/appBar/index.tsx similarity index 85% rename from src/component/AppBar/AppBar.tsx rename to src/component/common/appBar/index.tsx index fbc83a7f..282a9539 100644 --- a/src/component/AppBar/AppBar.tsx +++ b/src/component/common/appBar/index.tsx @@ -1,10 +1,9 @@ import { css } from "@emotion/react"; import { useNavigate } from "react-router-dom"; -import Icon from "../common/Icon/Icon"; +import Icon from "../Icon/Icon"; export type AppBarProps = { title?: string; - appBarVisible?: boolean; LeftComp?: React.ReactNode; RightComp?: React.ReactNode; }; @@ -24,11 +23,7 @@ const Back = () => { }; //FIXME : ๋””์ž์ธ ํ† ํฐ์— ๋”ฐ๋ผ ์ƒ‰๊น” ๋ณ€๊ฒฝ, ํฐํŠธ ์ˆ˜์ • -const AppBar = ({ title, appBarVisible = true, LeftComp = , RightComp =
}: AppBarProps) => { - if (!appBarVisible) { - return null; - } - +const AppBar = ({ title, LeftComp = , RightComp =
}: AppBarProps) => { return ( <>
; +type DefaultLayoutProps = PropsWithChildren & { + appBarVisible?: boolean; +}; -export function DefaultLayout({ children, title, appBarVisible, LeftComp, RightComp }: DefaultLayoutProps) { +export function DefaultLayout({ children, title, appBarVisible = true, LeftComp, RightComp }: DefaultLayoutProps) { return ( - + {appBarVisible && }
Date: Mon, 15 Jul 2024 13:00:22 +0900 Subject: [PATCH 124/168] =?UTF-8?q?refactor:=20#11=20ToastList=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=AA=85=20=EB=B3=80=EA=B2=BD=20->=20Toast?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/MainPage.tsx | 4 ++-- src/component/common/Toast/{ToastList.tsx => Toast.tsx} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/component/common/Toast/{ToastList.tsx => Toast.tsx} (95%) diff --git a/src/app/MainPage.tsx b/src/app/MainPage.tsx index 3ceca3df..78e539ba 100644 --- a/src/app/MainPage.tsx +++ b/src/app/MainPage.tsx @@ -4,7 +4,7 @@ import { useAtom } from "jotai"; import Button from "@/component/Button/Button"; import { Icon } from "@/component/common/Icon/Icon"; import { Modal } from "@/component/common/Modal/Modal"; -import { ToastList } from "@/component/common/Toast/ToastList"; +import { Toast } from "@/component/common/Toast/Toast"; import { useModal } from "@/hooks/useModal"; import { useToast } from "@/hooks/useToast"; import { messageAtom } from "@/store/messageAtom.tsx"; @@ -32,7 +32,7 @@ function MainPage() { - + ); } diff --git a/src/component/common/Toast/ToastList.tsx b/src/component/common/Toast/Toast.tsx similarity index 95% rename from src/component/common/Toast/ToastList.tsx rename to src/component/common/Toast/Toast.tsx index 3c7fd712..db997039 100644 --- a/src/component/common/Toast/ToastList.tsx +++ b/src/component/common/Toast/Toast.tsx @@ -4,7 +4,7 @@ import { Portal } from "@/component/common/Portal/Portal"; import { ToastItem } from "@/component/common/Toast/ToastItem"; import { useToast } from "@/hooks/useToast"; -export function ToastList() { +export function Toast() { const { toastDataState } = useToast(); return ( From 2bbdb06fd1c0aa18d7a0e9b315831b487ebeb15b Mon Sep 17 00:00:00 2001 From: donghunee Date: Mon, 15 Jul 2024 13:04:51 +0900 Subject: [PATCH 125/168] =?UTF-8?q?refactor:=20#11=20&&=20=EC=97=B0?= =?UTF-8?q?=EC=82=B0=EC=9E=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/Toast/ToastItem.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/component/common/Toast/ToastItem.tsx b/src/component/common/Toast/ToastItem.tsx index 1db1146a..d7e8814d 100644 --- a/src/component/common/Toast/ToastItem.tsx +++ b/src/component/common/Toast/ToastItem.tsx @@ -34,9 +34,7 @@ export function ToastItem({ type, content, id, duration = 3000 }: ToastType) { }; const handleMouseLeave = () => { - if (duration) { - setIsPaused(false); - } + duration && setIsPaused(false); }; const getIcon = () => { From 9848ed1ff781883b0aa3fd3bc244b9a2a83ca1a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=86=E1=85=B5=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=B4?= Date: Sat, 13 Jul 2024 23:28:35 +0900 Subject: [PATCH 126/168] =?UTF-8?q?feat:=20#11=20input,=20label=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/test/Staging.tsx | 9 +++ src/component/common/Input/Input.tsx | 34 ++++++++++ .../common/Input/InputLabelContainer.tsx | 27 ++++++++ src/component/common/Input/Label.tsx | 63 +++++++++++++++++++ src/component/common/Input/index.ts | 3 + src/hooks/useInput.ts | 13 ++++ src/style/global.css | 10 +++ 7 files changed, 159 insertions(+) create mode 100644 src/component/common/Input/Input.tsx create mode 100644 src/component/common/Input/InputLabelContainer.tsx create mode 100644 src/component/common/Input/Label.tsx create mode 100644 src/component/common/Input/index.ts create mode 100644 src/hooks/useInput.ts diff --git a/src/app/test/Staging.tsx b/src/app/test/Staging.tsx index 65dfecf3..7b5f5dec 100644 --- a/src/app/test/Staging.tsx +++ b/src/app/test/Staging.tsx @@ -4,15 +4,18 @@ import Button from "@/component/Button/Button.tsx"; import { ButtonProvider } from "@/component/Button/ButtonProvider.tsx"; import CheckBox from "@/component/common/CheckBox/CheckBox"; import CheckBoxGroup from "@/component/common/CheckBox/CheckBoxGroup"; +import { Input, InputLabelContainer, Label } from "@/component/common/Input"; import Radio from "@/component/common/RadioButton/Radio"; import RadioButtonGroup from "@/component/common/RadioButton/RadioButtonGroup"; import { useCheckBox } from "@/hooks/useCheckBox"; +import { useInput } from "@/hooks/useInput"; import { useRadioButton } from "@/hooks/useRadioButton"; import { DefaultLayout } from "@/layout/DefaultLayout.tsx"; export default function Staging() { const [isRadioChecked, onChange, selectedValue] = useRadioButton(); const [isCheckBoxChecked, toggle, selectedValues] = useCheckBox(); + const [layerName, handleChangeName] = useInput(); useEffect(() => { console.log("๋ผ๋””์˜ค ๋ฒ„ํŠผ ์„ ํƒ value:", selectedValue); @@ -47,6 +50,12 @@ export default function Staging() { ํ”„๋กœ์ ํŠธ ๋๋‚œ ํ›„ +
+ + + + + ๊ธฐ๋ณธ ๋ฒ„ํŠผ ํ•˜๋Š˜์ƒ‰ ๋ฒ„ํŠผ diff --git a/src/component/common/Input/Input.tsx b/src/component/common/Input/Input.tsx new file mode 100644 index 00000000..7fc19dcd --- /dev/null +++ b/src/component/common/Input/Input.tsx @@ -0,0 +1,34 @@ +import { css } from "@emotion/react"; +import { forwardRef, useContext } from "react"; + +import { InputContext } from "./InputLabelContainer"; + +type InputProps = { + width?: string; +} & React.InputHTMLAttributes; + +export const Input = forwardRef(function ({ id, width = "100%", ...props }: InputProps) { + const inputContext = useContext(InputContext); + return ( +
+
+ +
+
+ ); +}); + +Input.displayName = "Input"; diff --git a/src/component/common/Input/InputLabelContainer.tsx b/src/component/common/Input/InputLabelContainer.tsx new file mode 100644 index 00000000..c868b195 --- /dev/null +++ b/src/component/common/Input/InputLabelContainer.tsx @@ -0,0 +1,27 @@ +import { css } from "@emotion/react"; +import { createContext } from "react"; + +type InputLabelContainerProps = { + id: string; + children: React.ReactNode; +}; + +export const InputContext = createContext<{ id: string } | undefined>(undefined); + +export function InputLabelContainer({ id, children }: InputLabelContainerProps) { + return ( + +
+ {children} +
+
+ ); +} diff --git a/src/component/common/Input/Label.tsx b/src/component/common/Input/Label.tsx new file mode 100644 index 00000000..d218a7a2 --- /dev/null +++ b/src/component/common/Input/Label.tsx @@ -0,0 +1,63 @@ +import { css, Interpolation, Theme } from "@emotion/react"; +import { useContext } from "react"; + +import { InputContext } from "./InputLabelContainer"; + +type LabelProps = { + order?: number; + styles?: Interpolation; +} & React.LabelHTMLAttributes; + +export const Label = ({ id, children, order, styles }: LabelProps) => { + const inputContext = useContext(InputContext); + + return ( + + ); +}; diff --git a/src/component/common/Input/index.ts b/src/component/common/Input/index.ts new file mode 100644 index 00000000..836ad51f --- /dev/null +++ b/src/component/common/Input/index.ts @@ -0,0 +1,3 @@ +export { Input } from "./Input"; +export { Label } from "./Label"; +export { InputLabelContainer } from "./InputLabelContainer"; diff --git a/src/hooks/useInput.ts b/src/hooks/useInput.ts new file mode 100644 index 00000000..4a315892 --- /dev/null +++ b/src/hooks/useInput.ts @@ -0,0 +1,13 @@ +import { useCallback, useState } from "react"; + +type UseInputReturn = [string, (e: React.ChangeEvent) => void]; + +export const useInput = (defaultValue?: string): UseInputReturn => { + const [value, setValue] = useState(defaultValue || ""); + + const handleInputChange = useCallback((e: React.ChangeEvent) => { + setValue(e.target.value); + }, []); + + return [value, handleInputChange]; +}; diff --git a/src/style/global.css b/src/style/global.css index 343f77f8..f87e3541 100644 --- a/src/style/global.css +++ b/src/style/global.css @@ -1,3 +1,7 @@ +* { + box-sizing: border-box; +} + html { font-size: 62.5%; } @@ -13,3 +17,9 @@ button { outline: none; user-select: none; } + +input { + border: none; + background-color: inherit; + outline: none; +} From 043b547d905118bb1a1b8fbc6259f6799009b123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=86=E1=85=B5=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=B4?= Date: Mon, 15 Jul 2024 15:45:08 +0900 Subject: [PATCH 127/168] =?UTF-8?q?chore:=20#11=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20type=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useInput.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/hooks/useInput.ts b/src/hooks/useInput.ts index 4a315892..95794218 100644 --- a/src/hooks/useInput.ts +++ b/src/hooks/useInput.ts @@ -1,13 +1,11 @@ import { useCallback, useState } from "react"; -type UseInputReturn = [string, (e: React.ChangeEvent) => void]; - -export const useInput = (defaultValue?: string): UseInputReturn => { +export const useInput = (defaultValue?: string) => { const [value, setValue] = useState(defaultValue || ""); const handleInputChange = useCallback((e: React.ChangeEvent) => { setValue(e.target.value); }, []); - return [value, handleInputChange]; + return [value, handleInputChange] as const; }; From 3b70ba119c7aa714f4e107ba7c400305fd296b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=86=E1=85=B5=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=B4?= Date: Mon, 15 Jul 2024 15:45:43 +0900 Subject: [PATCH 128/168] =?UTF-8?q?chore:=20#11=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=BB=A8=EB=B2=A4=EC=85=98=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/Input/Label.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/component/common/Input/Label.tsx b/src/component/common/Input/Label.tsx index d218a7a2..1c7486a5 100644 --- a/src/component/common/Input/Label.tsx +++ b/src/component/common/Input/Label.tsx @@ -8,7 +8,7 @@ type LabelProps = { styles?: Interpolation; } & React.LabelHTMLAttributes; -export const Label = ({ id, children, order, styles }: LabelProps) => { +export function Label({ id, children, order, styles }: LabelProps) { const inputContext = useContext(InputContext); return ( @@ -60,4 +60,4 @@ export const Label = ({ id, children, order, styles }: LabelProps) => { ); -}; +} From 2d54ac00750d68e4ea35a6552d87769065f5548b Mon Sep 17 00:00:00 2001 From: sean Date: Mon, 15 Jul 2024 21:53:15 +0900 Subject: [PATCH 129/168] fix/#28/SpriteSvg error fix --- src/assets/{imgs => svgs/spriteSvgs}/loginLogo.svg | 0 src/component/Img/LoginSpriteSvg.tsx | 10 ---------- src/component/login/loginSpriteSvg/index.tsx | 10 ++++++++++ src/svg.d.ts | 5 +++++ 4 files changed, 15 insertions(+), 10 deletions(-) rename src/assets/{imgs => svgs/spriteSvgs}/loginLogo.svg (100%) delete mode 100644 src/component/Img/LoginSpriteSvg.tsx create mode 100644 src/component/login/loginSpriteSvg/index.tsx diff --git a/src/assets/imgs/loginLogo.svg b/src/assets/svgs/spriteSvgs/loginLogo.svg similarity index 100% rename from src/assets/imgs/loginLogo.svg rename to src/assets/svgs/spriteSvgs/loginLogo.svg diff --git a/src/component/Img/LoginSpriteSvg.tsx b/src/component/Img/LoginSpriteSvg.tsx deleted file mode 100644 index 7e01c6c9..00000000 --- a/src/component/Img/LoginSpriteSvg.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import loginLogo from "@/assets/imgs/loginLogo.svg"; -import { loginType } from "@/types/loginType"; - -const LoginSpriteSvg = ({ type }: loginType) => ( - - - -); - -export default LoginSpriteSvg; diff --git a/src/component/login/loginSpriteSvg/index.tsx b/src/component/login/loginSpriteSvg/index.tsx new file mode 100644 index 00000000..d0cfde10 --- /dev/null +++ b/src/component/login/loginSpriteSvg/index.tsx @@ -0,0 +1,10 @@ +import loginLogo from "@/assets/svgs/spriteSvgs/loginLogo.svg"; +import { loginType } from "@/types/loginType"; + +export function LoginSpriteSvg({ type }: loginType) { + return ( + + + + ); +} diff --git a/src/svg.d.ts b/src/svg.d.ts index 3ade18fd..39a57bfb 100644 --- a/src/svg.d.ts +++ b/src/svg.d.ts @@ -2,3 +2,8 @@ declare module "*.svg" { const value: React.FunctionComponent>; export default value; } + +declare module "@/assets/svgs/spriteSvgs/loginLogo.svg" { + const content: string; + export default content; +} From 735c11c15f7a89cc11a1d91a278fe20a0a001441 Mon Sep 17 00:00:00 2001 From: sean Date: Mon, 15 Jul 2024 21:54:13 +0900 Subject: [PATCH 130/168] refactor/#28/Social Login Convention Refactor --- .../socialLoginButton/index.tsx} | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) rename src/component/{button/SocialLoginButton.tsx => login/socialLoginButton/index.tsx} (83%) diff --git a/src/component/button/SocialLoginButton.tsx b/src/component/login/socialLoginButton/index.tsx similarity index 83% rename from src/component/button/SocialLoginButton.tsx rename to src/component/login/socialLoginButton/index.tsx index 5dbd9c4d..36058a80 100644 --- a/src/component/button/SocialLoginButton.tsx +++ b/src/component/login/socialLoginButton/index.tsx @@ -1,8 +1,9 @@ import { css } from "@emotion/react"; + +import { LoginSpriteSvg } from "@/component/login/loginSpriteSvg"; import { loginTypeProvider, loginBtnProps, backgroundColors } from "@/types/loginType"; -import LoginSpriteSvg from "../Img/LoginSpriteSvg"; -const SocialLoginButton = ({ type, handler }: loginBtnProps) => { +export function SocialLoginButton({ type, handler }: loginBtnProps) { return ( ); -}; - -export default SocialLoginButton; +} From 56de6418a2c600cf1ee09da5ca2b04b6d41e0a06 Mon Sep 17 00:00:00 2001 From: sean Date: Mon, 15 Jul 2024 21:54:46 +0900 Subject: [PATCH 131/168] refactor/#28/Login Page Convention Refactor --- src/app/login/{Login.tsx => LoginPage.tsx} | 9 ++++----- src/app/login/kakao/KakaoLoginRedirection.tsx | 9 ++++----- src/router/index.tsx | 6 +++--- 3 files changed, 11 insertions(+), 13 deletions(-) rename src/app/login/{Login.tsx => LoginPage.tsx} (75%) diff --git a/src/app/login/Login.tsx b/src/app/login/LoginPage.tsx similarity index 75% rename from src/app/login/Login.tsx rename to src/app/login/LoginPage.tsx index f6a7d95d..713ce508 100644 --- a/src/app/login/Login.tsx +++ b/src/app/login/LoginPage.tsx @@ -1,14 +1,13 @@ -import { DefaultLayout } from "@/layout/DefaultLayout"; -import SocialLoginButton from "@/component/button/SocialLoginButton"; import { kakaoLogin } from "./kakao/kakaoLogin"; import { googleLogin } from "./google/googleLogin"; +import { SocialLoginButton } from "@/component/login/socialLoginButton"; +import { DefaultLayout } from "@/layout/DefaultLayout"; -const Login = () => { +export function LoginPage() { return ( ); -}; -export default Login; +} diff --git a/src/app/login/kakao/KakaoLoginRedirection.tsx b/src/app/login/kakao/KakaoLoginRedirection.tsx index 07390ffc..ff81ba9d 100644 --- a/src/app/login/kakao/KakaoLoginRedirection.tsx +++ b/src/app/login/kakao/KakaoLoginRedirection.tsx @@ -3,9 +3,9 @@ import { useNavigate } from "react-router-dom"; import { api } from "@/api"; //FIXME: ์‘๋‹ต ๋ฐ์ดํ„ฐ ํ˜•์‹์— ๋”ฐ๋ผ ์ˆ˜์ • -type Response = {}; +type Response = { data: string }; -const KaKaoRedirection = () => { +export const KaKaoRedirection = () => { const code = window.location.search; const navigate = useNavigate(); @@ -13,10 +13,11 @@ const KaKaoRedirection = () => { // FIXME: ๋ฐฑ์—”๋“œ API์— ๋”ฐ๋ผ ์ฃผ์†Œ ์ˆ˜์ • ํ•„์š” api .post(`kakaoLogin${code}`) - .then((_: Response) => { + .then((data: Response) => { // FIXME: ๋ฐ›์€๊ฑธ ์–ด๋””์— ์ €์žฅํ• ์ง€ ๋…ผ์˜ ํ•„์š” (๋กœ๊ทธ์ธ ์ €์žฅ ๋ฐฉ์‹์— ๋”ฐ๋ผ ๋‹ฌ๋ฆฌ์ง) // => ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ ๋กœ์ง ์ถ”๊ฐ€ // FIXME: ์™„๋ฃŒ ํ›„ ์ด๋™ (ํ”„๋กœ์„ธ์Šค์— ๋”ฐ๋ผ ํŽ˜์ด์ง€ URL ๋ณ€๊ฒฝ) + console.log(data); navigate("/test"); }) .catch((err) => { @@ -27,5 +28,3 @@ const KaKaoRedirection = () => { // FIXME: ๋กœ๊ทธ์ธ ๋กœ๋”ฉ ๋””์ž์ธ ํ•„์š” ๋ฐ ์ด์— ๋”ฐ๋ฅธ ์ฝ”๋“œ ์ถ”๊ฐ€ ๊ฐœ๋ฐœ ํ•„์š” return
๋กœ๊ทธ์ธ ์ค‘์ž…๋‹ˆ๋‹ค.
; }; - -export default KaKaoRedirection; diff --git a/src/router/index.tsx b/src/router/index.tsx index 17f61648..e3225f0f 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -1,8 +1,8 @@ import { Fragment } from "react"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import { LoginPage } from "@/app/login/LoginPage"; import MainPage from "@/app/MainPage.tsx"; /* FIXME - ์‹ค์ œ ๋ฉ”์ธ ํŽ˜์ด์ง€ ์ž‘์„ฑ ํ›„ ๋Œ€์ฒดํ•ด์ฃผ์„ธ์š”. */ -import Login from "@/app/login/Login"; import Staging from "@/app/test/Staging.tsx"; import GlobalLayout from "@/layout/GlobalLayout.tsx"; @@ -17,7 +17,7 @@ const routerChildren = [ }, { path: "/login", - element: , + element: , }, ]; @@ -31,4 +31,4 @@ const router = createBrowserRouter([ ]); export const Routers = () => { return ; -}; \ No newline at end of file +}; From e2285c587ef359cad5bb3cc7fe13598ac848ace9 Mon Sep 17 00:00:00 2001 From: sean Date: Mon, 15 Jul 2024 21:55:13 +0900 Subject: [PATCH 132/168] refactor/#28/AppBar Convention Refactor --- src/component/common/appBar/index.tsx | 14 ++++++-------- src/layout/DefaultLayout.tsx | 3 +-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/component/common/appBar/index.tsx b/src/component/common/appBar/index.tsx index 282a9539..1d788e23 100644 --- a/src/component/common/appBar/index.tsx +++ b/src/component/common/appBar/index.tsx @@ -1,6 +1,7 @@ import { css } from "@emotion/react"; import { useNavigate } from "react-router-dom"; -import Icon from "../Icon/Icon"; + +import Icon from "@/component/common/Icon/Icon"; export type AppBarProps = { title?: string; @@ -9,9 +10,8 @@ export type AppBarProps = { }; //FIXME: ์ƒ‰๊น” ๋””์ž์ธ ํ† ํฐ์— ๋”ฐ๋ผ ๋ณ€๊ฒฝ -const Back = () => { +function Back() { const navigate = useNavigate(); - return ( { }} /> ); -}; +} //FIXME : ๋””์ž์ธ ํ† ํฐ์— ๋”ฐ๋ผ ์ƒ‰๊น” ๋ณ€๊ฒฝ, ํฐํŠธ ์ˆ˜์ • -const AppBar = ({ title, LeftComp = , RightComp =
}: AppBarProps) => { +export function AppBar({ title, LeftComp = , RightComp =
}: AppBarProps) { return ( <>
, RightComp =
}: AppBarP /> ); -}; - -export default AppBar; +} diff --git a/src/layout/DefaultLayout.tsx b/src/layout/DefaultLayout.tsx index edc936a7..70f16906 100644 --- a/src/layout/DefaultLayout.tsx +++ b/src/layout/DefaultLayout.tsx @@ -1,7 +1,6 @@ import { css } from "@emotion/react"; import { Fragment, PropsWithChildren } from "react"; -import AppBar from "@/component/common/appBar"; -import { AppBarProps } from "@/component/common/appBar"; +import { AppBar, AppBarProps } from "@/component/common/appBar"; type DefaultLayoutProps = PropsWithChildren & { appBarVisible?: boolean; From f9d7935876c34ba1253f9f960b2dba2d03e38f6a Mon Sep 17 00:00:00 2001 From: sean Date: Mon, 15 Jul 2024 21:55:34 +0900 Subject: [PATCH 133/168] refactor/#28/Typhography Convention Refactor --- src/component/common/typography/index.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/component/common/typography/index.tsx b/src/component/common/typography/index.tsx index b0873529..07305baf 100644 --- a/src/component/common/typography/index.tsx +++ b/src/component/common/typography/index.tsx @@ -1,7 +1,6 @@ -import React from "react"; import { css } from "@emotion/react"; -import { DESIGN_SYSTEM_TEXT } from "@/style/variable"; -import { DESIGN_SYSTEM_COLOR } from "@/style/variable"; + +import { DESIGN_SYSTEM_TEXT, DESIGN_SYSTEM_COLOR } from "@/style/variable"; type TextTags = "span" | "p" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "strong" | "em" | "small" | "q" | "u"; @@ -13,7 +12,7 @@ type TypographyProps = { }; // FIXME: ๋””์ž์ธ ํ† ํฐ์— ๋”ฐ๋ฅธ default ๊ฐ’ ์ˆ˜์ • -function Typography({ as: Component = "span", variant = "B1", color = "black", children }: TypographyProps) { +export function Typography({ as: Component = "span", variant = "B1", color = "black", children }: TypographyProps) { return ( ); } - -export { Typography }; From 88600aa605b3ecaec8543ea981ab93f046995e45 Mon Sep 17 00:00:00 2001 From: sean Date: Mon, 15 Jul 2024 21:56:06 +0900 Subject: [PATCH 134/168] =?UTF-8?q?fix/#28/error=20=EC=A0=95=EC=9D=98?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=84=A4=EC=A0=95=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/vite-env.d.ts | 9 +++++++++ tsconfig.json | 2 +- tsconfig.node.json | 4 ++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 11f02fe2..86be81ca 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1 +1,10 @@ /// + +type ImportMetaEnv = { + readonly VITE_REST_API_KEY: string; + readonly VITE_REDIRECT_URI: string; +}; + +type ImportMeta = { + readonly env: ImportMetaEnv; +}; diff --git a/tsconfig.json b/tsconfig.json index ffd3ebba..b165dbc9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,6 +35,6 @@ /* Matter-js library file */ "allowJs": true }, - "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js"], + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js", "vite-env.d.ts"], "references": [{ "path": "./tsconfig.node.json" }] } diff --git a/tsconfig.node.json b/tsconfig.node.json index f443d8c3..42872c59 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -4,7 +4,7 @@ "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true, + "allowSyntheticDefaultImports": true }, "include": ["vite.config.ts"] -} \ No newline at end of file +} From b705f6ad90d625cda90407288584633241ba9079 Mon Sep 17 00:00:00 2001 From: sean Date: Mon, 15 Jul 2024 22:11:29 +0900 Subject: [PATCH 135/168] refactor/#28/Login Convention Refactor --- src/app/login/{kakao => }/KakaoLoginRedirection.tsx | 4 ++-- src/app/login/LoginPage.tsx | 13 +++++++++++-- src/app/login/google/googleLogin.tsx | 3 --- src/app/login/kakao/kakaoLogin.tsx | 6 ------ 4 files changed, 13 insertions(+), 13 deletions(-) rename src/app/login/{kakao => }/KakaoLoginRedirection.tsx (91%) delete mode 100644 src/app/login/google/googleLogin.tsx delete mode 100644 src/app/login/kakao/kakaoLogin.tsx diff --git a/src/app/login/kakao/KakaoLoginRedirection.tsx b/src/app/login/KakaoLoginRedirection.tsx similarity index 91% rename from src/app/login/kakao/KakaoLoginRedirection.tsx rename to src/app/login/KakaoLoginRedirection.tsx index ff81ba9d..8138bbca 100644 --- a/src/app/login/kakao/KakaoLoginRedirection.tsx +++ b/src/app/login/KakaoLoginRedirection.tsx @@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom"; import { api } from "@/api"; //FIXME: ์‘๋‹ต ๋ฐ์ดํ„ฐ ํ˜•์‹์— ๋”ฐ๋ผ ์ˆ˜์ • -type Response = { data: string }; +type KakaoLoginResponse = { data: string }; export const KaKaoRedirection = () => { const code = window.location.search; @@ -13,7 +13,7 @@ export const KaKaoRedirection = () => { // FIXME: ๋ฐฑ์—”๋“œ API์— ๋”ฐ๋ผ ์ฃผ์†Œ ์ˆ˜์ • ํ•„์š” api .post(`kakaoLogin${code}`) - .then((data: Response) => { + .then((data: KakaoLoginResponse) => { // FIXME: ๋ฐ›์€๊ฑธ ์–ด๋””์— ์ €์žฅํ• ์ง€ ๋…ผ์˜ ํ•„์š” (๋กœ๊ทธ์ธ ์ €์žฅ ๋ฐฉ์‹์— ๋”ฐ๋ผ ๋‹ฌ๋ฆฌ์ง) // => ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ ๋กœ์ง ์ถ”๊ฐ€ // FIXME: ์™„๋ฃŒ ํ›„ ์ด๋™ (ํ”„๋กœ์„ธ์Šค์— ๋”ฐ๋ผ ํŽ˜์ด์ง€ URL ๋ณ€๊ฒฝ) diff --git a/src/app/login/LoginPage.tsx b/src/app/login/LoginPage.tsx index 713ce508..62b24ee4 100644 --- a/src/app/login/LoginPage.tsx +++ b/src/app/login/LoginPage.tsx @@ -1,5 +1,3 @@ -import { kakaoLogin } from "./kakao/kakaoLogin"; -import { googleLogin } from "./google/googleLogin"; import { SocialLoginButton } from "@/component/login/socialLoginButton"; import { DefaultLayout } from "@/layout/DefaultLayout"; @@ -11,3 +9,14 @@ export function LoginPage() { ); } + +function kakaoLogin() { + const REST_API_KEY = import.meta.env.VITE_REST_API_KEY as string; + const REDIRECT_URI = import.meta.env.VITE_REDIRECT_URI as string; + const link = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`; + window.location.href = link; +} + +function googleLogin() { + console.log("๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ์‹œ๋„"); +} diff --git a/src/app/login/google/googleLogin.tsx b/src/app/login/google/googleLogin.tsx deleted file mode 100644 index 27ca13ac..00000000 --- a/src/app/login/google/googleLogin.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export function googleLogin() { - console.log("๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ์‹œ๋„"); -} diff --git a/src/app/login/kakao/kakaoLogin.tsx b/src/app/login/kakao/kakaoLogin.tsx deleted file mode 100644 index 6e9de5c5..00000000 --- a/src/app/login/kakao/kakaoLogin.tsx +++ /dev/null @@ -1,6 +0,0 @@ -export function kakaoLogin() { - const REST_API_KEY: string = import.meta.env.VITE_REST_API_KEY; - const REDIRECT_URI: string = import.meta.env.VITE_REDIRECT_URI; - const link = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`; - window.location.href = link; -} From bae766706a7e24535b2359e2f2783f091fb4d130 Mon Sep 17 00:00:00 2001 From: sean Date: Mon, 15 Jul 2024 22:12:46 +0900 Subject: [PATCH 136/168] refactor/#28/Login Convention Refactor --- src/app/login/KakaoLoginRedirection.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/login/KakaoLoginRedirection.tsx b/src/app/login/KakaoLoginRedirection.tsx index 8138bbca..ec466e14 100644 --- a/src/app/login/KakaoLoginRedirection.tsx +++ b/src/app/login/KakaoLoginRedirection.tsx @@ -1,5 +1,6 @@ import { useEffect } from "react"; import { useNavigate } from "react-router-dom"; + import { api } from "@/api"; //FIXME: ์‘๋‹ต ๋ฐ์ดํ„ฐ ํ˜•์‹์— ๋”ฐ๋ผ ์ˆ˜์ • @@ -23,7 +24,7 @@ export const KaKaoRedirection = () => { .catch((err) => { console.log(err); }); - }, []); + }); // FIXME: ๋กœ๊ทธ์ธ ๋กœ๋”ฉ ๋””์ž์ธ ํ•„์š” ๋ฐ ์ด์— ๋”ฐ๋ฅธ ์ฝ”๋“œ ์ถ”๊ฐ€ ๊ฐœ๋ฐœ ํ•„์š” return
๋กœ๊ทธ์ธ ์ค‘์ž…๋‹ˆ๋‹ค.
; From cc3c157b7a094994cf903747aaadd57a0f9d147e Mon Sep 17 00:00:00 2001 From: sean Date: Mon, 15 Jul 2024 22:18:33 +0900 Subject: [PATCH 137/168] refactor/#28/Login Convention Refactor --- src/app/login/KakaoLoginRedirection.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/login/KakaoLoginRedirection.tsx b/src/app/login/KakaoLoginRedirection.tsx index ec466e14..82f7fe43 100644 --- a/src/app/login/KakaoLoginRedirection.tsx +++ b/src/app/login/KakaoLoginRedirection.tsx @@ -6,7 +6,7 @@ import { api } from "@/api"; //FIXME: ์‘๋‹ต ๋ฐ์ดํ„ฐ ํ˜•์‹์— ๋”ฐ๋ผ ์ˆ˜์ • type KakaoLoginResponse = { data: string }; -export const KaKaoRedirection = () => { +export function KaKaoRedirection() { const code = window.location.search; const navigate = useNavigate(); @@ -28,4 +28,4 @@ export const KaKaoRedirection = () => { // FIXME: ๋กœ๊ทธ์ธ ๋กœ๋”ฉ ๋””์ž์ธ ํ•„์š” ๋ฐ ์ด์— ๋”ฐ๋ฅธ ์ฝ”๋“œ ์ถ”๊ฐ€ ๊ฐœ๋ฐœ ํ•„์š” return
๋กœ๊ทธ์ธ ์ค‘์ž…๋‹ˆ๋‹ค.
; -}; +} From c339e7809001aed15ad42364a765eb973856a397 Mon Sep 17 00:00:00 2001 From: donghunee Date: Mon, 15 Jul 2024 23:25:36 +0900 Subject: [PATCH 138/168] =?UTF-8?q?refactor:=20#11=20Icon=20props=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/component/common/Icon/Icon.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/component/common/Icon/Icon.tsx b/src/component/common/Icon/Icon.tsx index 2c52a221..80fde153 100644 --- a/src/component/common/Icon/Icon.tsx +++ b/src/component/common/Icon/Icon.tsx @@ -1,5 +1,5 @@ import { css } from "@emotion/react"; -import { CSSProperties, memo } from "react"; +import { memo } from "react"; import * as icons from "@/assets/svgs"; @@ -10,12 +10,11 @@ type Props = { color?: string; size?: string | number; onClick?: () => void; - style?: CSSProperties; -}; +} & React.SVGProps; const DEFAULT_ICON_COLOR = "#000000"; -export const Icon = memo(function Icon({ icon, color = DEFAULT_ICON_COLOR, size = "0.2rem", onClick, style }: Props) { +export const Icon = memo(function Icon({ icon, color = DEFAULT_ICON_COLOR, size = "0.2rem", onClick, ...props }: Props) { // eslint-disable-next-line import/namespace const SVGIcon = icons[icon]; const widthRem = typeof size === "number" ? `${size}rem` : size; @@ -29,7 +28,7 @@ export const Icon = memo(function Icon({ icon, color = DEFAULT_ICON_COLOR, size width: ${widthRem}; height: auto; `} - style={style} + {...props} /> ); }); From b77f043e30a09eb9edfd78594c50ba6f3ad66027 Mon Sep 17 00:00:00 2001 From: klmhyeonwoo Date: Mon, 15 Jul 2024 23:54:22 +0900 Subject: [PATCH 139/168] refactor: #28 change button folder conventions --- public/layer.png | Bin 0 -> 106686 bytes .../{Button => common/button}/Button.tsx | 2 +- .../button}/ButtonProvider.tsx | 2 +- src/component/common/button/index.ts | 2 ++ 4 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 public/layer.png rename src/component/{Button => common/button}/Button.tsx (88%) rename src/component/{Button => common/button}/ButtonProvider.tsx (92%) create mode 100644 src/component/common/button/index.ts diff --git a/public/layer.png b/public/layer.png new file mode 100644 index 0000000000000000000000000000000000000000..8b9f032d2bbff92854fbdab9079715396ae743ea GIT binary patch literal 106686 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 U9df!S)&Kwi07*qoM6N<$f^6o4QUCw| literal 0 HcmV?d00001 diff --git a/src/component/Button/Button.tsx b/src/component/common/button/Button.tsx similarity index 88% rename from src/component/Button/Button.tsx rename to src/component/common/button/Button.tsx index 36c3c623..0bf273ac 100644 --- a/src/component/Button/Button.tsx +++ b/src/component/common/button/Button.tsx @@ -6,7 +6,7 @@ export type ButtonProps = { disabled?: boolean; } & Omit, "type">; -export default function Button({ children, colorSchema = "primary", disabled = false, ...props }: PropsWithChildren) { +export function Button({ children, colorSchema = "primary", disabled = false, ...props }: PropsWithChildren) { return (

- console.log("ํด๋ฆญ")} /> - - - - diff --git a/src/component/common/Icon/Icon.tsx b/src/component/common/Icon/Icon.tsx index 536b161b..b1080b73 100644 --- a/src/component/common/Icon/Icon.tsx +++ b/src/component/common/Icon/Icon.tsx @@ -1,6 +1,7 @@ import { css } from "@emotion/react"; import * as icons from "@/assets/svgs"; +import { memo } from "react"; type IconType = keyof typeof icons; @@ -13,8 +14,7 @@ type Props = { const DEFAULT_ICON_COLOR = "#000000"; -export function Icon({ icon, color = DEFAULT_ICON_COLOR, size = "2rem", onClick, ...props }: Props) { - // eslint-disable-next-line import/namespace +function Icon({ icon, color = DEFAULT_ICON_COLOR, size = "2rem", onClick, ...props }: Props) { const SVGIcon = icons[icon]; const widthRem = typeof size === "number" ? `${size}rem` : size; @@ -31,3 +31,5 @@ export function Icon({ icon, color = DEFAULT_ICON_COLOR, size = "2rem", onClick, /> ); } + +export default memo(Icon); diff --git a/src/component/common/Toast/ToastItem.tsx b/src/component/common/Toast/ToastItem.tsx index d7e8814d..86876a63 100644 --- a/src/component/common/Toast/ToastItem.tsx +++ b/src/component/common/Toast/ToastItem.tsx @@ -1,12 +1,12 @@ import { css } from "@emotion/react"; import { useRef, useState } from "react"; -import { Icon } from "@/component/common/Icon/Icon"; import { useToast } from "@/hooks/useToast"; import { ANIMATION } from "@/style/common/animation"; import { toastMap } from "@/style/common/toast"; import { ToastType } from "@/types/toast"; import { collapseToast } from "@/util/toast/collapseToast"; +import Icon from "@/component/common/Icon/Icon"; export function ToastItem({ type, content, id, duration = 3000 }: ToastType) { const { removeToast } = useToast(); From 56c985cfc840d4cf832c264c484b6430b8b1ea17 Mon Sep 17 00:00:00 2001 From: donghunee Date: Tue, 16 Jul 2024 00:28:45 +0900 Subject: [PATCH 146/168] =?UTF-8?q?fix:=20#11=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/button/SocialLoginButton.tsx | 35 ---------------------- 1 file changed, 35 deletions(-) delete mode 100644 src/component/button/SocialLoginButton.tsx diff --git a/src/component/button/SocialLoginButton.tsx b/src/component/button/SocialLoginButton.tsx deleted file mode 100644 index 36058a80..00000000 --- a/src/component/button/SocialLoginButton.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { css } from "@emotion/react"; - -import { LoginSpriteSvg } from "@/component/login/loginSpriteSvg"; -import { loginTypeProvider, loginBtnProps, backgroundColors } from "@/types/loginType"; - -export function SocialLoginButton({ type, handler }: loginBtnProps) { - return ( - - ); -} From 74cf5c2ea9f8739f628f6eed215bbcb9158c9121 Mon Sep 17 00:00:00 2001 From: donghunee Date: Tue, 16 Jul 2024 00:47:12 +0900 Subject: [PATCH 147/168] =?UTF-8?q?refactor:=20#28=20Icon=20convention=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/Icon/Icon.tsx | 6 ++---- src/component/common/Icon/index.ts | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 src/component/common/Icon/index.ts diff --git a/src/component/common/Icon/Icon.tsx b/src/component/common/Icon/Icon.tsx index 6c088e28..a3e56df3 100644 --- a/src/component/common/Icon/Icon.tsx +++ b/src/component/common/Icon/Icon.tsx @@ -14,7 +14,7 @@ type Props = { const DEFAULT_ICON_COLOR = "#000000"; -function Icon({ icon, color = DEFAULT_ICON_COLOR, size = "2rem", onClick, ...props }: Props) { +export const Icon = memo(function Icon({ icon, color = DEFAULT_ICON_COLOR, size = "2rem", onClick, ...props }: Props) { // eslint-disable-next-line import/namespace const SVGIcon = icons[icon]; const widthRem = typeof size === "number" ? `${size}rem` : size; @@ -31,6 +31,4 @@ function Icon({ icon, color = DEFAULT_ICON_COLOR, size = "2rem", onClick, ...pro {...props} /> ); -} - -export default memo(Icon); +}); diff --git a/src/component/common/Icon/index.ts b/src/component/common/Icon/index.ts new file mode 100644 index 00000000..87604f19 --- /dev/null +++ b/src/component/common/Icon/index.ts @@ -0,0 +1 @@ +export { Icon } from "@/component/common/Icon/Icon"; From 383dc90f96b8a62c410ac3e6d7d67226369e15bb Mon Sep 17 00:00:00 2001 From: donghunee Date: Tue, 16 Jul 2024 00:47:35 +0900 Subject: [PATCH 148/168] =?UTF-8?q?refactor:=20#28=20Modal=20convention=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/Modal/Modal.tsx | 2 +- src/component/common/Modal/index.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 src/component/common/Modal/index.ts diff --git a/src/component/common/Modal/Modal.tsx b/src/component/common/Modal/Modal.tsx index 8b51664c..9f76f0af 100644 --- a/src/component/common/Modal/Modal.tsx +++ b/src/component/common/Modal/Modal.tsx @@ -1,7 +1,7 @@ import { css } from "@emotion/react"; import { useEffect, useRef } from "react"; -import { Portal } from "@/component/common/Portal/Portal"; +import { Portal } from "@/component/common/Portal"; import { useModal } from "@/hooks/useModal"; export function Modal() { diff --git a/src/component/common/Modal/index.ts b/src/component/common/Modal/index.ts new file mode 100644 index 00000000..0920f43c --- /dev/null +++ b/src/component/common/Modal/index.ts @@ -0,0 +1 @@ +export { Modal } from "@/component/common/Modal/Modal"; From 2cd54e3723ad03c42b1f037266b888e3d6e27b5e Mon Sep 17 00:00:00 2001 From: donghunee Date: Tue, 16 Jul 2024 00:48:01 +0900 Subject: [PATCH 149/168] =?UTF-8?q?refactor:=20#28=20Portal=20convention?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/Portal/index.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/component/common/Portal/index.ts diff --git a/src/component/common/Portal/index.ts b/src/component/common/Portal/index.ts new file mode 100644 index 00000000..4723b974 --- /dev/null +++ b/src/component/common/Portal/index.ts @@ -0,0 +1 @@ +export { Portal } from "@/component/common/Portal/Portal"; From c050f04b89baac86c68ec90d090095c576801554 Mon Sep 17 00:00:00 2001 From: donghunee Date: Tue, 16 Jul 2024 00:48:26 +0900 Subject: [PATCH 150/168] =?UTF-8?q?refactor:=20#28=20Toast=20convention=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/Toast/Toast.tsx | 4 ++-- src/component/common/Toast/ToastItem.tsx | 2 +- src/component/common/Toast/index.ts | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 src/component/common/Toast/index.ts diff --git a/src/component/common/Toast/Toast.tsx b/src/component/common/Toast/Toast.tsx index db997039..a0e1819e 100644 --- a/src/component/common/Toast/Toast.tsx +++ b/src/component/common/Toast/Toast.tsx @@ -1,7 +1,7 @@ import { css } from "@emotion/react"; -import { Portal } from "@/component/common/Portal/Portal"; -import { ToastItem } from "@/component/common/Toast/ToastItem"; +import { Portal } from "@/component/common/Portal"; +import { ToastItem } from "@/component/common/Toast"; import { useToast } from "@/hooks/useToast"; export function Toast() { diff --git a/src/component/common/Toast/ToastItem.tsx b/src/component/common/Toast/ToastItem.tsx index 86876a63..b0f06e7e 100644 --- a/src/component/common/Toast/ToastItem.tsx +++ b/src/component/common/Toast/ToastItem.tsx @@ -1,12 +1,12 @@ import { css } from "@emotion/react"; import { useRef, useState } from "react"; +import { Icon } from "@/component/common/Icon"; import { useToast } from "@/hooks/useToast"; import { ANIMATION } from "@/style/common/animation"; import { toastMap } from "@/style/common/toast"; import { ToastType } from "@/types/toast"; import { collapseToast } from "@/util/toast/collapseToast"; -import Icon from "@/component/common/Icon/Icon"; export function ToastItem({ type, content, id, duration = 3000 }: ToastType) { const { removeToast } = useToast(); diff --git a/src/component/common/Toast/index.ts b/src/component/common/Toast/index.ts new file mode 100644 index 00000000..52f67247 --- /dev/null +++ b/src/component/common/Toast/index.ts @@ -0,0 +1,2 @@ +export { Toast } from "./Toast"; +export { ToastItem } from "./ToastItem"; From da43904f41d75fd63a40f95448b3bb3537a748b7 Mon Sep 17 00:00:00 2001 From: donghunee Date: Tue, 16 Jul 2024 00:49:01 +0900 Subject: [PATCH 151/168] =?UTF-8?q?refactor:=20#28=20MainPage=20import=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/MainPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/MainPage.tsx b/src/app/MainPage.tsx index b7fb19ac..46452926 100644 --- a/src/app/MainPage.tsx +++ b/src/app/MainPage.tsx @@ -2,8 +2,8 @@ import { useAtom } from "jotai"; import Button from "@/component/Button/Button"; -import { Modal } from "@/component/common/Modal/Modal"; -import { Toast } from "@/component/common/Toast/Toast"; +import { Modal } from "@/component/common/Modal"; +import { Toast } from "@/component/common/Toast"; import { useModal } from "@/hooks/useModal"; import { useToast } from "@/hooks/useToast"; import { messageAtom } from "@/store/messageAtom.tsx"; From ad5e248fadaace01ef46292708e624ab5265639c Mon Sep 17 00:00:00 2001 From: donghunee Date: Tue, 16 Jul 2024 00:53:41 +0900 Subject: [PATCH 152/168] =?UTF-8?q?refactor:=20#28=20appBar=20Icon=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/appBar/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/component/common/appBar/index.tsx b/src/component/common/appBar/index.tsx index 1d788e23..69284478 100644 --- a/src/component/common/appBar/index.tsx +++ b/src/component/common/appBar/index.tsx @@ -1,7 +1,7 @@ import { css } from "@emotion/react"; import { useNavigate } from "react-router-dom"; -import Icon from "@/component/common/Icon/Icon"; +import { Icon } from "@/component/common/Icon"; export type AppBarProps = { title?: string; From a8508734871231bb2b8e3be8660cfef2e72d8f22 Mon Sep 17 00:00:00 2001 From: klmhyeonwoo Date: Tue, 16 Jul 2024 20:09:19 +0900 Subject: [PATCH 153/168] fix: #33 Fix BottomSheet Function Clean Up --- src/hooks/useBottomSheet.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/hooks/useBottomSheet.ts b/src/hooks/useBottomSheet.ts index 30f99dd5..dd7c4e04 100644 --- a/src/hooks/useBottomSheet.ts +++ b/src/hooks/useBottomSheet.ts @@ -150,6 +150,13 @@ export const useSetBottomSheet = ({ handler, sheetHeight }: Pick { + sheet.current!.removeEventListener("mousedown", handleMouseDown); + sheet.current!.removeEventListener("touchstart", handleTouchStart); + sheet.current!.removeEventListener("touchmove", handleTouchMove); + sheet.current!.removeEventListener("touchend", handleTouchEnd); + }; }, []); useEffect(() => { @@ -157,6 +164,8 @@ export const useSetBottomSheet = ({ handler, sheetHeight }: Pick content.current!.removeEventListener("touchstart", handleTouchStart); }, []); useEffect(() => { From 3c673bb3ab19cf6b0c53059643c0b0417082eda5 Mon Sep 17 00:00:00 2001 From: klmhyeonwoo Date: Tue, 16 Jul 2024 20:10:29 +0900 Subject: [PATCH 154/168] refactor: #33 Change to React Portal --- index.html | 1 + src/component/BottomSheet/BottomSheet.tsx | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 46004780..bb153191 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@
+
diff --git a/src/component/BottomSheet/BottomSheet.tsx b/src/component/BottomSheet/BottomSheet.tsx index 254fb359..ea09fb71 100644 --- a/src/component/BottomSheet/BottomSheet.tsx +++ b/src/component/BottomSheet/BottomSheet.tsx @@ -1,8 +1,9 @@ import { css } from "@emotion/react"; -import { Fragment, ReactElement, useEffect, useState } from "react"; +import { ReactElement, useEffect, useState } from "react"; import { MIN_Y } from "@/component/BottomSheet/BottomSheetOption.ts"; import { BottomSheetContent, BottomSheetHeader } from "@/component/BottomSheet/component"; +import { Portal } from "@/component/common/Portal/Portal.tsx"; import { useBottomSheet, useSetBottomSheet } from "@/hooks/useBottomSheet.ts"; export type BottomSheetType = { @@ -22,7 +23,7 @@ export function BottomSheet({ title, contents, handler = false, sheetHeight = 34 }, [bottomSheetState]); return ( - +
{contents}
-
+ ); } From 5619a859e012a1e969870723207e9fe647221373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=86=E1=85=B5=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=B4?= Date: Tue, 16 Jul 2024 20:52:08 +0900 Subject: [PATCH 155/168] refactor: #28 remove Pascal folders --- src/component/common/CheckBox/CheckBox.tsx | 46 -------------- .../common/CheckBox/CheckBoxGroup.tsx | 29 --------- src/component/common/Input/Input.tsx | 34 ---------- .../common/Input/InputLabelContainer.tsx | 27 -------- src/component/common/Input/Label.tsx | 63 ------------------- src/component/common/Input/index.ts | 3 - src/component/common/RadioButton/Radio.tsx | 47 -------------- .../common/RadioButton/RadioButtonGroup.tsx | 30 --------- 8 files changed, 279 deletions(-) delete mode 100644 src/component/common/CheckBox/CheckBox.tsx delete mode 100644 src/component/common/CheckBox/CheckBoxGroup.tsx delete mode 100644 src/component/common/Input/Input.tsx delete mode 100644 src/component/common/Input/InputLabelContainer.tsx delete mode 100644 src/component/common/Input/Label.tsx delete mode 100644 src/component/common/Input/index.ts delete mode 100644 src/component/common/RadioButton/Radio.tsx delete mode 100644 src/component/common/RadioButton/RadioButtonGroup.tsx diff --git a/src/component/common/CheckBox/CheckBox.tsx b/src/component/common/CheckBox/CheckBox.tsx deleted file mode 100644 index 8118a9c8..00000000 --- a/src/component/common/CheckBox/CheckBox.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { css } from "@emotion/react"; -import { useContext } from "react"; - -import { CheckBoxContext } from "./CheckBoxGroup"; - -import ListItemCard from "@/component/common/Card/ListItemCard"; - -type CheckBoxProps = { - value: string; - children: React.ReactNode; -}; - -const CheckBox = ({ value, children }: CheckBoxProps) => { - const checkboxContext = useContext(CheckBoxContext); - return ( - - - { - checkboxContext?.onChange && checkboxContext.onChange(e.target.value); - }} - css={css` - display: none; - `} - /> - - ); -}; - -export default CheckBox; diff --git a/src/component/common/CheckBox/CheckBoxGroup.tsx b/src/component/common/CheckBox/CheckBoxGroup.tsx deleted file mode 100644 index 0bb6e58c..00000000 --- a/src/component/common/CheckBox/CheckBoxGroup.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { css } from "@emotion/react"; -import { createContext } from "react"; - -export type CheckBoxContextState = { - isChecked: (value: string) => boolean; - onChange: (value: string) => void; -}; - -export const CheckBoxContext = createContext(undefined); - -type CheckBoxGroupProps = { - children: React.ReactNode; -} & CheckBoxContextState; - -const CheckBoxGroup = ({ children, ...props }: CheckBoxGroupProps) => { - return ( -
- {children} -
- ); -}; - -export default CheckBoxGroup; diff --git a/src/component/common/Input/Input.tsx b/src/component/common/Input/Input.tsx deleted file mode 100644 index 7fc19dcd..00000000 --- a/src/component/common/Input/Input.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { css } from "@emotion/react"; -import { forwardRef, useContext } from "react"; - -import { InputContext } from "./InputLabelContainer"; - -type InputProps = { - width?: string; -} & React.InputHTMLAttributes; - -export const Input = forwardRef(function ({ id, width = "100%", ...props }: InputProps) { - const inputContext = useContext(InputContext); - return ( -
-
- -
-
- ); -}); - -Input.displayName = "Input"; diff --git a/src/component/common/Input/InputLabelContainer.tsx b/src/component/common/Input/InputLabelContainer.tsx deleted file mode 100644 index c868b195..00000000 --- a/src/component/common/Input/InputLabelContainer.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { css } from "@emotion/react"; -import { createContext } from "react"; - -type InputLabelContainerProps = { - id: string; - children: React.ReactNode; -}; - -export const InputContext = createContext<{ id: string } | undefined>(undefined); - -export function InputLabelContainer({ id, children }: InputLabelContainerProps) { - return ( - -
- {children} -
-
- ); -} diff --git a/src/component/common/Input/Label.tsx b/src/component/common/Input/Label.tsx deleted file mode 100644 index 1c7486a5..00000000 --- a/src/component/common/Input/Label.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { css, Interpolation, Theme } from "@emotion/react"; -import { useContext } from "react"; - -import { InputContext } from "./InputLabelContainer"; - -type LabelProps = { - order?: number; - styles?: Interpolation; -} & React.LabelHTMLAttributes; - -export function Label({ id, children, order, styles }: LabelProps) { - const inputContext = useContext(InputContext); - - return ( - - ); -} diff --git a/src/component/common/Input/index.ts b/src/component/common/Input/index.ts deleted file mode 100644 index 836ad51f..00000000 --- a/src/component/common/Input/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { Input } from "./Input"; -export { Label } from "./Label"; -export { InputLabelContainer } from "./InputLabelContainer"; diff --git a/src/component/common/RadioButton/Radio.tsx b/src/component/common/RadioButton/Radio.tsx deleted file mode 100644 index 1b129636..00000000 --- a/src/component/common/RadioButton/Radio.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { css } from "@emotion/react"; -import { useContext } from "react"; - -import { RadioContext } from "./RadioButtonGroup"; - -import ListItemCard from "@/component/common/Card/ListItemCard"; - -type RadioProps = { - value: string; - children: React.ReactNode; -}; - -const Radio = ({ value, children }: RadioProps) => { - const radioContext = useContext(RadioContext); - return ( - - - { - radioContext?.onChange && radioContext.onChange(e.target.value); - }} - css={css` - display: none; - `} - /> - - ); -}; - -export default Radio; diff --git a/src/component/common/RadioButton/RadioButtonGroup.tsx b/src/component/common/RadioButton/RadioButtonGroup.tsx deleted file mode 100644 index a009bea0..00000000 --- a/src/component/common/RadioButton/RadioButtonGroup.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { css } from "@emotion/react"; -import { createContext } from "react"; - -export type RadioContextState = { - radioName: string; - isChecked: (value: string) => boolean; - onChange: (value: string) => void; -}; - -export const RadioContext = createContext(undefined); - -type RadioButtonGroupProps = { - children: React.ReactNode; -} & RadioContextState; - -const RadioButtonGroup = ({ children, ...props }: RadioButtonGroupProps) => { - return ( -
- {children} -
- ); -}; - -export default RadioButtonGroup; From 9545fd68a25d569a94a4472220a3e8aebdd83d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=86=E1=85=B5=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=B4?= Date: Tue, 16 Jul 2024 20:55:08 +0900 Subject: [PATCH 156/168] refactor: #28 folder camel, add index --- src/app/test/Staging.tsx | 8 +-- src/component/common/checkBox/CheckBox.tsx | 44 +++++++++++++ .../common/checkBox/CheckBoxGroup.tsx | 27 ++++++++ src/component/common/checkBox/index.ts | 2 + src/component/common/input/Input.tsx | 34 ++++++++++ .../common/input/InputLabelContainer.tsx | 27 ++++++++ src/component/common/input/Label.tsx | 63 +++++++++++++++++++ src/component/common/input/index.ts | 3 + src/component/common/radioButton/Radio.tsx | 45 +++++++++++++ .../common/radioButton/RadioButtonGroup.tsx | 28 +++++++++ src/component/common/radioButton/index.ts | 2 + 11 files changed, 278 insertions(+), 5 deletions(-) create mode 100644 src/component/common/checkBox/CheckBox.tsx create mode 100644 src/component/common/checkBox/CheckBoxGroup.tsx create mode 100644 src/component/common/checkBox/index.ts create mode 100644 src/component/common/input/Input.tsx create mode 100644 src/component/common/input/InputLabelContainer.tsx create mode 100644 src/component/common/input/Label.tsx create mode 100644 src/component/common/input/index.ts create mode 100644 src/component/common/radioButton/Radio.tsx create mode 100644 src/component/common/radioButton/RadioButtonGroup.tsx create mode 100644 src/component/common/radioButton/index.ts diff --git a/src/app/test/Staging.tsx b/src/app/test/Staging.tsx index 7b5f5dec..18bd04ea 100644 --- a/src/app/test/Staging.tsx +++ b/src/app/test/Staging.tsx @@ -2,11 +2,9 @@ import { useEffect } from "react"; import Button from "@/component/Button/Button.tsx"; import { ButtonProvider } from "@/component/Button/ButtonProvider.tsx"; -import CheckBox from "@/component/common/CheckBox/CheckBox"; -import CheckBoxGroup from "@/component/common/CheckBox/CheckBoxGroup"; -import { Input, InputLabelContainer, Label } from "@/component/common/Input"; -import Radio from "@/component/common/RadioButton/Radio"; -import RadioButtonGroup from "@/component/common/RadioButton/RadioButtonGroup"; +import { CheckBox, CheckBoxGroup } from "@/component/common/checkBox"; +import { Input, InputLabelContainer, Label } from "@/component/common/input"; +import { Radio, RadioButtonGroup } from "@/component/common/radioButton"; import { useCheckBox } from "@/hooks/useCheckBox"; import { useInput } from "@/hooks/useInput"; import { useRadioButton } from "@/hooks/useRadioButton"; diff --git a/src/component/common/checkBox/CheckBox.tsx b/src/component/common/checkBox/CheckBox.tsx new file mode 100644 index 00000000..3b982d4c --- /dev/null +++ b/src/component/common/checkBox/CheckBox.tsx @@ -0,0 +1,44 @@ +import { css } from "@emotion/react"; +import { useContext } from "react"; + +import { CheckBoxContext } from "./CheckBoxGroup"; + +import ListItemCard from "@/component/common/Card/ListItemCard"; + +type CheckBoxProps = { + value: string; + children: React.ReactNode; +}; + +export function CheckBox({ value, children }: CheckBoxProps) { + const checkboxContext = useContext(CheckBoxContext); + return ( + + + { + checkboxContext?.onChange && checkboxContext.onChange(e.target.value); + }} + css={css` + display: none; + `} + /> + + ); +} diff --git a/src/component/common/checkBox/CheckBoxGroup.tsx b/src/component/common/checkBox/CheckBoxGroup.tsx new file mode 100644 index 00000000..32e0fbf9 --- /dev/null +++ b/src/component/common/checkBox/CheckBoxGroup.tsx @@ -0,0 +1,27 @@ +import { css } from "@emotion/react"; +import { createContext } from "react"; + +export type CheckBoxContextState = { + isChecked: (value: string) => boolean; + onChange: (value: string) => void; +}; + +export const CheckBoxContext = createContext(undefined); + +type CheckBoxGroupProps = { + children: React.ReactNode; +} & CheckBoxContextState; + +export function CheckBoxGroup({ children, ...props }: CheckBoxGroupProps) { + return ( +
+ {children} +
+ ); +} diff --git a/src/component/common/checkBox/index.ts b/src/component/common/checkBox/index.ts new file mode 100644 index 00000000..1c96a44e --- /dev/null +++ b/src/component/common/checkBox/index.ts @@ -0,0 +1,2 @@ +export { CheckBox } from "./CheckBox"; +export { CheckBoxGroup } from "./CheckBoxGroup"; diff --git a/src/component/common/input/Input.tsx b/src/component/common/input/Input.tsx new file mode 100644 index 00000000..7fc19dcd --- /dev/null +++ b/src/component/common/input/Input.tsx @@ -0,0 +1,34 @@ +import { css } from "@emotion/react"; +import { forwardRef, useContext } from "react"; + +import { InputContext } from "./InputLabelContainer"; + +type InputProps = { + width?: string; +} & React.InputHTMLAttributes; + +export const Input = forwardRef(function ({ id, width = "100%", ...props }: InputProps) { + const inputContext = useContext(InputContext); + return ( +
+
+ +
+
+ ); +}); + +Input.displayName = "Input"; diff --git a/src/component/common/input/InputLabelContainer.tsx b/src/component/common/input/InputLabelContainer.tsx new file mode 100644 index 00000000..c868b195 --- /dev/null +++ b/src/component/common/input/InputLabelContainer.tsx @@ -0,0 +1,27 @@ +import { css } from "@emotion/react"; +import { createContext } from "react"; + +type InputLabelContainerProps = { + id: string; + children: React.ReactNode; +}; + +export const InputContext = createContext<{ id: string } | undefined>(undefined); + +export function InputLabelContainer({ id, children }: InputLabelContainerProps) { + return ( + +
+ {children} +
+
+ ); +} diff --git a/src/component/common/input/Label.tsx b/src/component/common/input/Label.tsx new file mode 100644 index 00000000..1c7486a5 --- /dev/null +++ b/src/component/common/input/Label.tsx @@ -0,0 +1,63 @@ +import { css, Interpolation, Theme } from "@emotion/react"; +import { useContext } from "react"; + +import { InputContext } from "./InputLabelContainer"; + +type LabelProps = { + order?: number; + styles?: Interpolation; +} & React.LabelHTMLAttributes; + +export function Label({ id, children, order, styles }: LabelProps) { + const inputContext = useContext(InputContext); + + return ( + + ); +} diff --git a/src/component/common/input/index.ts b/src/component/common/input/index.ts new file mode 100644 index 00000000..836ad51f --- /dev/null +++ b/src/component/common/input/index.ts @@ -0,0 +1,3 @@ +export { Input } from "./Input"; +export { Label } from "./Label"; +export { InputLabelContainer } from "./InputLabelContainer"; diff --git a/src/component/common/radioButton/Radio.tsx b/src/component/common/radioButton/Radio.tsx new file mode 100644 index 00000000..6c668d62 --- /dev/null +++ b/src/component/common/radioButton/Radio.tsx @@ -0,0 +1,45 @@ +import { css } from "@emotion/react"; +import { useContext } from "react"; + +import { RadioContext } from "./RadioButtonGroup"; + +import ListItemCard from "@/component/common/Card/ListItemCard"; + +type RadioProps = { + value: string; + children: React.ReactNode; +}; + +export function Radio({ value, children }: RadioProps) { + const radioContext = useContext(RadioContext); + return ( + + + { + radioContext?.onChange && radioContext.onChange(e.target.value); + }} + css={css` + display: none; + `} + /> + + ); +} diff --git a/src/component/common/radioButton/RadioButtonGroup.tsx b/src/component/common/radioButton/RadioButtonGroup.tsx new file mode 100644 index 00000000..1093bc5d --- /dev/null +++ b/src/component/common/radioButton/RadioButtonGroup.tsx @@ -0,0 +1,28 @@ +import { css } from "@emotion/react"; +import { createContext } from "react"; + +export type RadioContextState = { + radioName: string; + isChecked: (value: string) => boolean; + onChange: (value: string) => void; +}; + +export const RadioContext = createContext(undefined); + +type RadioButtonGroupProps = { + children: React.ReactNode; +} & RadioContextState; + +export function RadioButtonGroup({ children, ...props }: RadioButtonGroupProps) { + return ( +
+ {children} +
+ ); +} diff --git a/src/component/common/radioButton/index.ts b/src/component/common/radioButton/index.ts new file mode 100644 index 00000000..054777c4 --- /dev/null +++ b/src/component/common/radioButton/index.ts @@ -0,0 +1,2 @@ +export { Radio } from "./Radio"; +export { RadioButtonGroup } from "./RadioButtonGroup"; From 4597094382f65b234362e070a3bb25f798a263bf Mon Sep 17 00:00:00 2001 From: klmhyeonwoo Date: Tue, 16 Jul 2024 22:26:46 +0900 Subject: [PATCH 157/168] fix: #38 Fix wrong import --- src/app/MainPage.tsx | 2 +- src/app/test/Staging.tsx | 11 ++--------- .../BottomSheet/component/BottomSheetHeader.tsx | 2 +- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/app/MainPage.tsx b/src/app/MainPage.tsx index 46452926..e40061f7 100644 --- a/src/app/MainPage.tsx +++ b/src/app/MainPage.tsx @@ -1,7 +1,7 @@ /*NOTE - ํ•ด๋‹น ํŒŒ์ผ์€ ๋ฃจํŠธ router๋ฅผ ์œ„ํ•œ ์ž„์‹œ ํŽ˜์ด์ง€์ž…๋‹ˆ๋‹ค. ์‹ค์ œ ํŽ˜์ด์ง€ ์ž‘์„ฑ ํ›„ ์ง€์›Œ์ฃผ์„ธ์š”! */ import { useAtom } from "jotai"; -import Button from "@/component/Button/Button"; +import { Button } from "@/component/common/button"; import { Modal } from "@/component/common/Modal"; import { Toast } from "@/component/common/Toast"; import { useModal } from "@/hooks/useModal"; diff --git a/src/app/test/Staging.tsx b/src/app/test/Staging.tsx index 105f3ad2..a8d4810f 100644 --- a/src/app/test/Staging.tsx +++ b/src/app/test/Staging.tsx @@ -1,18 +1,11 @@ import { Fragment, useEffect } from "react"; -import { Button, ButtonProvider } from "@/component/common/button"; import { BottomSheet } from "@/component/BottomSheet"; -import Button from "@/component/Button/Button.tsx"; -import { ButtonProvider } from "@/component/Button/ButtonProvider.tsx"; -import CheckBox from "@/component/common/CheckBox/CheckBox"; -import CheckBoxGroup from "@/component/common/CheckBox/CheckBoxGroup"; -import { Input, InputLabelContainer, Label } from "@/component/common/Input"; -import Radio from "@/component/common/RadioButton/Radio"; -import RadioButtonGroup from "@/component/common/RadioButton/RadioButtonGroup"; -import { useBottomSheet } from "@/hooks/useBottomSheet.ts"; +import { Button, ButtonProvider } from "@/component/common/button"; import { CheckBox, CheckBoxGroup } from "@/component/common/checkBox"; import { Input, InputLabelContainer, Label } from "@/component/common/input"; import { Radio, RadioButtonGroup } from "@/component/common/radioButton"; +import { useBottomSheet } from "@/hooks/useBottomSheet.ts"; import { useCheckBox } from "@/hooks/useCheckBox"; import { useInput } from "@/hooks/useInput"; import { useRadioButton } from "@/hooks/useRadioButton"; diff --git a/src/component/BottomSheet/component/BottomSheetHeader.tsx b/src/component/BottomSheet/component/BottomSheetHeader.tsx index 62c62f6b..4b2be758 100644 --- a/src/component/BottomSheet/component/BottomSheetHeader.tsx +++ b/src/component/BottomSheet/component/BottomSheetHeader.tsx @@ -1,7 +1,7 @@ import { css } from "@emotion/react"; import { BottomSheetType } from "@/component/BottomSheet/BottomSheet.tsx"; -import Icon from "@/component/common/Icon/Icon.tsx"; +import { Icon } from "@/component/common/Icon"; import { useBottomSheet } from "@/hooks/useBottomSheet.ts"; export function BottomSheetHeader({ title, handler }: Pick) { From 2b389d908be0312e5d921bd633ddfe058176f56d Mon Sep 17 00:00:00 2001 From: klmhyeonwoo Date: Wed, 17 Jul 2024 01:15:35 +0900 Subject: [PATCH 158/168] refactor: #11 Change ProgressBar --- src/app/test/Staging.tsx | 14 +++++- src/component/ProgressBar/ProgressBar.tsx | 30 ----------- .../common/ProgressBar/ProgressBar.tsx | 50 +++++++++++++++++++ src/component/common/ProgressBar/index.ts | 1 + 4 files changed, 64 insertions(+), 31 deletions(-) delete mode 100644 src/component/ProgressBar/ProgressBar.tsx create mode 100644 src/component/common/ProgressBar/ProgressBar.tsx create mode 100644 src/component/common/ProgressBar/index.ts diff --git a/src/app/test/Staging.tsx b/src/app/test/Staging.tsx index a8d4810f..4fae2a19 100644 --- a/src/app/test/Staging.tsx +++ b/src/app/test/Staging.tsx @@ -1,9 +1,11 @@ -import { Fragment, useEffect } from "react"; +import { css } from "@emotion/react"; +import { Fragment, useEffect, useState } from "react"; import { BottomSheet } from "@/component/BottomSheet"; import { Button, ButtonProvider } from "@/component/common/button"; import { CheckBox, CheckBoxGroup } from "@/component/common/checkBox"; import { Input, InputLabelContainer, Label } from "@/component/common/input"; +import { ProgressBar } from "@/component/common/ProgressBar"; import { Radio, RadioButtonGroup } from "@/component/common/radioButton"; import { useBottomSheet } from "@/hooks/useBottomSheet.ts"; import { useCheckBox } from "@/hooks/useCheckBox"; @@ -15,6 +17,7 @@ export default function Staging() { const [isRadioChecked, onChange, selectedValue] = useRadioButton(); const [isCheckBoxChecked, toggle, selectedValues] = useCheckBox(); const { openBottomSheet, bottomSheetState } = useBottomSheet(); + const [number, setNumber] = useState(0); const [layerName, handleChangeName] = useInput(); useEffect(() => { @@ -31,6 +34,15 @@ export default function Staging() { return ( + + + diff --git a/src/component/ProgressBar/ProgressBar.tsx b/src/component/ProgressBar/ProgressBar.tsx deleted file mode 100644 index f55dbc8f..00000000 --- a/src/component/ProgressBar/ProgressBar.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import {css} from "@emotion/react"; - -interface ProgressBarProps { - curPage: number, - lastPage: number, -} - -export default function ProgressBar({ curPage, lastPage }: ProgressBarProps) { - if (curPage > lastPage) curPage = lastPage; - - return ( - // FIXME: ์ถ”ํ›„ ๋””์ž์ธ ํ† ํฐ ์—ฐ๋™ ํ›„ ์ปฌ๋Ÿฌ ๊ฐ’ ๋ณ€๊ฒฝ -
-
-
- ) -} \ No newline at end of file diff --git a/src/component/common/ProgressBar/ProgressBar.tsx b/src/component/common/ProgressBar/ProgressBar.tsx new file mode 100644 index 00000000..7d34c3c3 --- /dev/null +++ b/src/component/common/ProgressBar/ProgressBar.tsx @@ -0,0 +1,50 @@ +import { css } from "@emotion/react"; +import { Fragment } from "react"; + +type ProgressBarProps = { + curPage: number; + lastPage: number; +} & Omit, "type">; + +export function ProgressBar({ curPage, lastPage, ...props }: ProgressBarProps) { + if (curPage > lastPage) curPage = lastPage; + + const segments = Array.from({ length: lastPage }, (_, i) => i < curPage); + + return ( +
+ {segments.map((isActive, index) => ( + +
+
+
+ + ))} +
+ ); +} diff --git a/src/component/common/ProgressBar/index.ts b/src/component/common/ProgressBar/index.ts new file mode 100644 index 00000000..4d83fe2b --- /dev/null +++ b/src/component/common/ProgressBar/index.ts @@ -0,0 +1 @@ +export { ProgressBar } from "./ProgressBar.tsx"; From 76489ce5f0fb7a82080134f7177737650b648378 Mon Sep 17 00:00:00 2001 From: klmhyeonwoo Date: Thu, 18 Jul 2024 01:28:48 +0900 Subject: [PATCH 159/168] feat: #11 Add Header --- src/component/common/header/Header.tsx | 83 +++++++++++++++++++ src/component/common/header/index.ts | 1 + .../common/typography/Typography.tsx | 2 +- 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/component/common/header/Header.tsx create mode 100644 src/component/common/header/index.ts diff --git a/src/component/common/header/Header.tsx b/src/component/common/header/Header.tsx new file mode 100644 index 00000000..96962823 --- /dev/null +++ b/src/component/common/header/Header.tsx @@ -0,0 +1,83 @@ +import { css } from "@emotion/react"; +import { Fragment } from "react"; + +import { Typography } from "@/component/common/typography"; +import { DESIGN_SYSTEM_COLOR } from "@/style/variable.ts"; + +type HeaderProps = { + theme?: ThemeSet; + title: string; + contents?: string; +}; + +type ThemeSet = "white" | "primary"; + +export function Header({ theme = "primary", title, contents }: HeaderProps) { + const themeSet: Record = { + primary: { + titleColor: "black", + contentsColor: "grey500", + }, + white: { + titleColor: "white", + contentsColor: "grey300", + }, + }; + + const parseTextToJSX = (text: string) => { + const regex = /\{(.*?)\}|([^{}]+)/g; + const matches = text.matchAll(regex); + + return Array.from(matches).map((match, index) => { + if (match[1]) { + return ( + + {match[1]} + + ); + } else { + return {match[2]}; + } + }); + }; + + return ( + +
+
+ {title.split("\n").map((item) => ( +
+ {parseTextToJSX(item).map((title, index) => ( + + {title} + + ))} +
+ ))} +
+ {contents && + contents.split("\n").map((item) => ( +
+ {parseTextToJSX(item).map((contents, index) => ( + + {contents} + + ))} +
+ ))} +
+
+ ); +} diff --git a/src/component/common/header/index.ts b/src/component/common/header/index.ts new file mode 100644 index 00000000..76db0637 --- /dev/null +++ b/src/component/common/header/index.ts @@ -0,0 +1 @@ +export { Header } from "./Header.tsx"; diff --git a/src/component/common/typography/Typography.tsx b/src/component/common/typography/Typography.tsx index 07305baf..afdfb705 100644 --- a/src/component/common/typography/Typography.tsx +++ b/src/component/common/typography/Typography.tsx @@ -8,7 +8,7 @@ type TypographyProps = { as?: Extract; variant?: keyof typeof DESIGN_SYSTEM_TEXT; color?: keyof typeof DESIGN_SYSTEM_COLOR; - children: string; + children: React.ReactElement | string; }; // FIXME: ๋””์ž์ธ ํ† ํฐ์— ๋”ฐ๋ฅธ default ๊ฐ’ ์ˆ˜์ • From b8a063a4559060430c309df11e01336c6a844b00 Mon Sep 17 00:00:00 2001 From: klmhyeonwoo Date: Thu, 18 Jul 2024 01:54:03 +0900 Subject: [PATCH 160/168] feat: #11 Add Button Provider Sort --- .../common/button/ButtonProvider.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/component/common/button/ButtonProvider.tsx b/src/component/common/button/ButtonProvider.tsx index 98ca9824..f051e4f6 100644 --- a/src/component/common/button/ButtonProvider.tsx +++ b/src/component/common/button/ButtonProvider.tsx @@ -3,6 +3,12 @@ import { Children, cloneElement, isValidElement, PropsWithChildren } from "react import { Button, ButtonProps } from "@/component/common/button/Button.tsx"; +type SortSet = "vertical" | "horizontal"; + +type ButtonProviderProps = { + sort?: SortSet; +} & ButtonProps; + const Primary = ({ ...props }) => { return