From a0b64fab2bc701398ec92c5583dea13a92d58984 Mon Sep 17 00:00:00 2001 From: Shim MunSeong Date: Sat, 27 Jan 2024 23:47:42 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20Dialog=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/admin/src/pages/HomePage/HomePage.tsx | 18 +++++++ .../ui/src/components/Dialog/CloseIcon.tsx | 27 ++++++++++ .../ui/src/components/Dialog/Dialog.styles.ts | 54 +++++++++++++++++++ packages/ui/src/components/Dialog/index.tsx | 32 +++++++++++ packages/ui/src/components/Portal/index.tsx | 10 ++++ packages/ui/src/components/index.ts | 3 +- packages/ui/src/hooks/index.ts | 3 +- packages/ui/src/hooks/useDialogState.ts | 25 +++++++++ 8 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 packages/ui/src/components/Dialog/CloseIcon.tsx create mode 100644 packages/ui/src/components/Dialog/Dialog.styles.ts create mode 100644 packages/ui/src/components/Dialog/index.tsx create mode 100644 packages/ui/src/components/Portal/index.tsx create mode 100644 packages/ui/src/hooks/useDialogState.ts diff --git a/apps/admin/src/pages/HomePage/HomePage.tsx b/apps/admin/src/pages/HomePage/HomePage.tsx index 7be8e9e5..0deeaf67 100644 --- a/apps/admin/src/pages/HomePage/HomePage.tsx +++ b/apps/admin/src/pages/HomePage/HomePage.tsx @@ -3,9 +3,11 @@ import Header from '../../components/Header/Header'; import Layout from '../../components/Layout/Layout'; import { PATH } from '../../constants/routes'; import Styled from './HomePage.styles'; +import { Dialog, useDialogState } from '@boolti/ui'; const HomePage = () => { const toast = useToast(); + const { open, openDialog, closeDialog } = useDialogState(); return ( { 정보 제공 토스트 띄우기 + + { + closeDialog(); + }} + > +

Dialog Content

+
); }; diff --git a/packages/ui/src/components/Dialog/CloseIcon.tsx b/packages/ui/src/components/Dialog/CloseIcon.tsx new file mode 100644 index 00000000..710ea599 --- /dev/null +++ b/packages/ui/src/components/Dialog/CloseIcon.tsx @@ -0,0 +1,27 @@ +const CloseIcon = () => ( + + + + + + + + + + + +); + +export default CloseIcon; diff --git a/packages/ui/src/components/Dialog/Dialog.styles.ts b/packages/ui/src/components/Dialog/Dialog.styles.ts new file mode 100644 index 00000000..4a778e86 --- /dev/null +++ b/packages/ui/src/components/Dialog/Dialog.styles.ts @@ -0,0 +1,54 @@ +import styled from '@emotion/styled'; + +const DIALOG_WIDTH = '450px'; + +const DimmedArea = styled.div` + position: fixed; + inset: 0; + background-color: rgba(86, 86, 86, 0.25); + display: flex; + justify-content: center; + align-items: center; +`; + +const Dialog = styled.div` + width: ${DIALOG_WIDTH}; + border-radius: 8px; + background-color: ${({ theme }) => theme.palette.grey.w}; +`; + +const DialogHeader = styled.div` + padding: 16px 32px; + border-bottom: 1px solid ${({ theme }) => theme.palette.grey.g30}; + position: relative; +`; + +const DialogTitle = styled.h2` + ${({ theme }) => theme.typo.sh2}; + color: ${({ theme }) => theme.palette.grey.g70}; +`; + +const DialogCloseButton = styled.button` + position: absolute; + top: 17px; + right: 32px; + width: 24px; + height: 24px; + display: inline-flex; + justify-content: center; + align-items: center; + cursor: pointer; +`; + +const DialogContent = styled.div` + padding: 32px; +`; + +export default { + DimmedArea, + Dialog, + DialogHeader, + DialogTitle, + DialogCloseButton, + DialogContent, +}; diff --git a/packages/ui/src/components/Dialog/index.tsx b/packages/ui/src/components/Dialog/index.tsx new file mode 100644 index 00000000..3c506377 --- /dev/null +++ b/packages/ui/src/components/Dialog/index.tsx @@ -0,0 +1,32 @@ +import Portal from '../Portal'; +import CloseIcon from './CloseIcon'; +import Styled from './Dialog.styles'; + +interface DialogProps { + open: boolean; + children: React.ReactNode; + title?: string; + onClose?: () => void; +} + +const Dialog = ({ open, children, title, onClose }: DialogProps) => { + if (!open) return null; + + return ( + + + + + {title} + + + + + {children} + + + + ); +}; + +export default Dialog; diff --git a/packages/ui/src/components/Portal/index.tsx b/packages/ui/src/components/Portal/index.tsx new file mode 100644 index 00000000..e33d0d45 --- /dev/null +++ b/packages/ui/src/components/Portal/index.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +interface PortalProps { + children: React.ReactNode; +} + +const Portal = ({ children }: PortalProps) => ReactDOM.createPortal(children, document.body); + +export default Portal; diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 644c62ed..dca2bd9e 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -2,5 +2,6 @@ import ThemeProvider from './ThemeProvider'; import Button from './Button'; import TextButton from './TextButton'; import Badge from './Badge'; +import Dialog from './Dialog'; -export { ThemeProvider, Button, TextButton, Badge }; +export { ThemeProvider, Button, TextButton, Badge, Dialog }; diff --git a/packages/ui/src/hooks/index.ts b/packages/ui/src/hooks/index.ts index bacab629..77cb1424 100644 --- a/packages/ui/src/hooks/index.ts +++ b/packages/ui/src/hooks/index.ts @@ -1,3 +1,4 @@ import useToast from './useToast'; +import useDialogState from './useDialogState'; -export { useToast }; +export { useToast, useDialogState }; diff --git a/packages/ui/src/hooks/useDialogState.ts b/packages/ui/src/hooks/useDialogState.ts new file mode 100644 index 00000000..3d2cbe82 --- /dev/null +++ b/packages/ui/src/hooks/useDialogState.ts @@ -0,0 +1,25 @@ +import { useCallback, useState } from 'react'; + +interface UseDialogParams { + defaultOpen?: boolean; +} + +const useDialogState = (params?: UseDialogParams) => { + const [open, setOpen] = useState(params?.defaultOpen ?? false); + + const openDialog = useCallback(() => { + setOpen(true); + }, []); + + const closeDialog = useCallback(() => { + setOpen(false); + }, []); + + return { + open, + openDialog, + closeDialog, + }; +}; + +export default useDialogState; From 6eaedd55c94a8015b11bd495ac2a951b69a3043f Mon Sep 17 00:00:00 2001 From: Shim MunSeong Date: Sun, 28 Jan 2024 13:57:50 +0900 Subject: [PATCH 2/4] =?UTF-8?q?refactor:=20theme.palette.dim.dialog=20?= =?UTF-8?q?=EC=83=89=EC=83=81=20=EC=83=81=EC=88=98=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ui/src/components/Dialog/Dialog.styles.ts | 2 +- packages/ui/src/systems/palette.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/components/Dialog/Dialog.styles.ts b/packages/ui/src/components/Dialog/Dialog.styles.ts index 4a778e86..d3b225b0 100644 --- a/packages/ui/src/components/Dialog/Dialog.styles.ts +++ b/packages/ui/src/components/Dialog/Dialog.styles.ts @@ -5,7 +5,7 @@ const DIALOG_WIDTH = '450px'; const DimmedArea = styled.div` position: fixed; inset: 0; - background-color: rgba(86, 86, 86, 0.25); + background-color: ${({ theme }) => theme.palette.dim.dialog}; display: flex; justify-content: center; align-items: center; diff --git a/packages/ui/src/systems/palette.ts b/packages/ui/src/systems/palette.ts index 0e2a7136..3f13b8f8 100644 --- a/packages/ui/src/systems/palette.ts +++ b/packages/ui/src/systems/palette.ts @@ -43,6 +43,9 @@ const palette = { g100: '#222529', b: '#000000', }, + dim: { + dialog: 'rgba(86, 86, 86, 0.25)', + }, } as const; export default palette; From 5f9547ff250f0aa8d17edb732e0b24edf61e6e9a Mon Sep 17 00:00:00 2001 From: Shim MunSeong Date: Sun, 28 Jan 2024 17:56:43 +0900 Subject: [PATCH 3/4] =?UTF-8?q?refactor:=20Dialog=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=82=AC=EC=9A=A9=20=EB=B0=A9=EB=B2=95=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=9D=84=20=EC=9C=84=ED=95=9C=20useDialog=20?= =?UTF-8?q?=EB=B0=8F=20DialogProvider=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BooltiUIProvider로 Provider 통합 - useDialogState 삭제 --- .pnp.cjs | 8 +++ apps/admin/src/App.tsx | 6 +- apps/admin/src/pages/HomePage/HomePage.tsx | 65 +++++++++++++++---- packages/ui/package.json | 1 + .../src/components/BooltiUIProvider/index.tsx | 16 +++++ .../src/components/DialogProvider/index.tsx | 32 +++++++++ packages/ui/src/components/index.ts | 4 +- packages/ui/src/contexts/dialogContext.tsx | 17 +++++ packages/ui/src/hooks/index.ts | 4 +- packages/ui/src/hooks/useDialog.ts | 43 ++++++++++++ packages/ui/src/hooks/useDialogState.ts | 25 ------- yarn.lock | 10 +++ 12 files changed, 187 insertions(+), 44 deletions(-) create mode 100644 packages/ui/src/components/BooltiUIProvider/index.tsx create mode 100644 packages/ui/src/components/DialogProvider/index.tsx create mode 100644 packages/ui/src/contexts/dialogContext.tsx create mode 100644 packages/ui/src/hooks/useDialog.ts delete mode 100644 packages/ui/src/hooks/useDialogState.ts diff --git a/.pnp.cjs b/.pnp.cjs index 9fdb8a01..8192cebe 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -482,6 +482,7 @@ const RAW_RUNTIME_STATE = ["@emotion/styled", "virtual:9ef42ff9c873460955cc48cd9b15127324f3d1f83a4bea8e6327df0101bb993bef095b175f8d10a3f0d23ee47f702ca3ef7272cba815f708e8609d03d84b96a2#npm:11.11.0"],\ ["@types/react", "npm:18.2.48"],\ ["@types/react-dom", "npm:18.2.18"],\ + ["nanoid", "npm:5.0.4"],\ ["react", "npm:18.2.0"],\ ["react-dom", "virtual:de80dc576383b2386358abc0e9fe49c00e3397fe355a0337462b73ab3115c2e557eb85784ee0fe776394cc11dd020b4e84dbbd75acf72ee6d54415d82d21f5c5#npm:18.2.0"],\ ["react-hot-toast", "virtual:9ef42ff9c873460955cc48cd9b15127324f3d1f83a4bea8e6327df0101bb993bef095b175f8d10a3f0d23ee47f702ca3ef7272cba815f708e8609d03d84b96a2#npm:2.4.1"],\ @@ -3533,6 +3534,13 @@ const RAW_RUNTIME_STATE = ["nanoid", "npm:3.3.7"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:5.0.4", {\ + "packageLocation": "../../.yarn/berry/cache/nanoid-npm-5.0.4-0c697f9311-10c0.zip/node_modules/nanoid/",\ + "packageDependencies": [\ + ["nanoid", "npm:5.0.4"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["natural-compare", [\ diff --git a/apps/admin/src/App.tsx b/apps/admin/src/App.tsx index a3079ccd..e0afce27 100644 --- a/apps/admin/src/App.tsx +++ b/apps/admin/src/App.tsx @@ -1,6 +1,6 @@ import { QueryClientProvider } from '@boolti/api'; import { createBrowserRouter, RouterProvider, Navigate } from 'react-router-dom'; -import { ThemeProvider } from '@boolti/ui'; +import { BooltiUIProvider } from '@boolti/ui'; import LoginPage from './pages/Login/LoginPage'; import SignUpCompletePage from './pages/SignUpComplete/SignUpCompletePage'; import 'the-new-css-reset/css/reset.css'; @@ -33,9 +33,9 @@ const App = () => { // console.log(data?.hello) return ( - + - + ); }; diff --git a/apps/admin/src/pages/HomePage/HomePage.tsx b/apps/admin/src/pages/HomePage/HomePage.tsx index 0deeaf67..2f96d96c 100644 --- a/apps/admin/src/pages/HomePage/HomePage.tsx +++ b/apps/admin/src/pages/HomePage/HomePage.tsx @@ -1,13 +1,15 @@ -import { useToast } from '@boolti/ui'; +import { useToast, useDialog } from '@boolti/ui'; import Header from '../../components/Header/Header'; import Layout from '../../components/Layout/Layout'; import { PATH } from '../../constants/routes'; import Styled from './HomePage.styles'; -import { Dialog, useDialogState } from '@boolti/ui'; const HomePage = () => { const toast = useToast(); - const { open, openDialog, closeDialog } = useDialogState(); + + const dialog1 = useDialog(); + const dialog2 = useDialog(); + const dialog3 = useDialog(); return ( { + ), + onClose: () => { + console.log('다이얼로그 1 닫힘'); + }, + }); }} > - Open Dialog + Open Dialog 1 - { - closeDialog(); + + + Open Dialog 2 + ); }; diff --git a/packages/ui/package.json b/packages/ui/package.json index abc22e70..59066031 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -11,6 +11,7 @@ "dependencies": { "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", + "nanoid": "^5.0.4", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1" diff --git a/packages/ui/src/components/BooltiUIProvider/index.tsx b/packages/ui/src/components/BooltiUIProvider/index.tsx new file mode 100644 index 00000000..32712b42 --- /dev/null +++ b/packages/ui/src/components/BooltiUIProvider/index.tsx @@ -0,0 +1,16 @@ +import DialogProvider from '../DialogProvider'; +import ThemeProvider from '../ThemeProvider'; + +interface BooltiUIProviderProps { + children: React.ReactNode; +} + +const BooltiUIProvider = ({ children }: BooltiUIProviderProps) => { + return ( + + {children} + + ); +}; + +export default BooltiUIProvider; diff --git a/packages/ui/src/components/DialogProvider/index.tsx b/packages/ui/src/components/DialogProvider/index.tsx new file mode 100644 index 00000000..a7a11101 --- /dev/null +++ b/packages/ui/src/components/DialogProvider/index.tsx @@ -0,0 +1,32 @@ +import { useState } from 'react'; +import dialogContext, { IDialog } from '../../contexts/dialogContext'; +import Dialog from '../Dialog'; + +interface DialogProviderProps { + children: React.ReactNode; +} + +const DialogProvider = ({ children }: DialogProviderProps) => { + const [dialogList, setDialogList] = useState([]); + + return ( + + {children} + {dialogList.map((dialog) => ( + dialog.id === id)} + title={dialog.title} + onClose={() => { + dialog.onClose?.(); + setDialogList((prev) => prev.filter(({ id }) => dialog.id !== id)); + }} + > + {dialog.content} + + ))} + + ); +}; + +export default DialogProvider; diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index dca2bd9e..07122f1c 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -1,7 +1,7 @@ -import ThemeProvider from './ThemeProvider'; +import BooltiUIProvider from './BooltiUIProvider'; import Button from './Button'; import TextButton from './TextButton'; import Badge from './Badge'; import Dialog from './Dialog'; -export { ThemeProvider, Button, TextButton, Badge, Dialog }; +export { BooltiUIProvider, Button, TextButton, Badge, Dialog }; diff --git a/packages/ui/src/contexts/dialogContext.tsx b/packages/ui/src/contexts/dialogContext.tsx new file mode 100644 index 00000000..d5795205 --- /dev/null +++ b/packages/ui/src/contexts/dialogContext.tsx @@ -0,0 +1,17 @@ +import { createContext } from 'react'; + +export interface IDialog { + id: string; + content: React.ReactNode; + title?: string; + onClose?: () => void; +} + +interface DialogContext { + dialogList: IDialog[]; + setDialogList: React.Dispatch>; +} + +const dialogContext = createContext(null); + +export default dialogContext; diff --git a/packages/ui/src/hooks/index.ts b/packages/ui/src/hooks/index.ts index 77cb1424..1cd50e68 100644 --- a/packages/ui/src/hooks/index.ts +++ b/packages/ui/src/hooks/index.ts @@ -1,4 +1,4 @@ import useToast from './useToast'; -import useDialogState from './useDialogState'; +import useDialog from './useDialog'; -export { useToast, useDialogState }; +export { useToast, useDialog }; diff --git a/packages/ui/src/hooks/useDialog.ts b/packages/ui/src/hooks/useDialog.ts new file mode 100644 index 00000000..ad6fa4e2 --- /dev/null +++ b/packages/ui/src/hooks/useDialog.ts @@ -0,0 +1,43 @@ +import { useCallback, useContext, useRef } from 'react'; +import { nanoid } from 'nanoid'; +import dialogContext, { IDialog } from '../contexts/dialogContext'; + +const useDialog = () => { + const id = useRef(nanoid(6)); + + const context = useContext(dialogContext); + + const open = useCallback( + ({ + content, + title, + onClose, + }: { + content: React.ReactNode; + title?: string; + onClose?: () => void; + }) => { + const newDialog: IDialog = { + id: id.current, + content, + title, + onClose, + }; + + context?.setDialogList((prev) => [...prev, newDialog]); + }, + [context, id], + ); + + const close = useCallback(() => { + context?.setDialogList((prev) => prev.filter((dialog) => dialog.id !== id.current)); + }, [context, id]); + + return { + id: id.current, + open, + close, + }; +}; + +export default useDialog; diff --git a/packages/ui/src/hooks/useDialogState.ts b/packages/ui/src/hooks/useDialogState.ts deleted file mode 100644 index 3d2cbe82..00000000 --- a/packages/ui/src/hooks/useDialogState.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { useCallback, useState } from 'react'; - -interface UseDialogParams { - defaultOpen?: boolean; -} - -const useDialogState = (params?: UseDialogParams) => { - const [open, setOpen] = useState(params?.defaultOpen ?? false); - - const openDialog = useCallback(() => { - setOpen(true); - }, []); - - const closeDialog = useCallback(() => { - setOpen(false); - }, []); - - return { - open, - openDialog, - closeDialog, - }; -}; - -export default useDialogState; diff --git a/yarn.lock b/yarn.lock index ad38b53d..defb6bc5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -336,6 +336,7 @@ __metadata: "@emotion/styled": "npm:^11.11.0" "@types/react": "npm:^18.2.43" "@types/react-dom": "npm:^18.2.17" + nanoid: "npm:^5.0.4" react: "npm:^18.2.0" react-dom: "npm:^18.2.0" react-hot-toast: "npm:^2.4.1" @@ -2776,6 +2777,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^5.0.4": + version: 5.0.4 + resolution: "nanoid@npm:5.0.4" + bin: + nanoid: bin/nanoid.js + checksum: 28c22d3ff96ea9e6abc059813b8483890c1f52de0f185d8347b0ea205c5966530205c374f1f77492bb53dc5d649de5d36d6eade16645055eb9220dc62080c1b1 + languageName: node + linkType: hard + "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" From f013caa5fd985f44d96359488bebead4f7927d35 Mon Sep 17 00:00:00 2001 From: Shim MunSeong Date: Sun, 28 Jan 2024 17:58:38 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20ThemeProvider=20->=20BooltiUIProvide?= =?UTF-8?q?r=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 --- apps/preview/src/App.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/preview/src/App.tsx b/apps/preview/src/App.tsx index 9a8475c4..e6332d9a 100644 --- a/apps/preview/src/App.tsx +++ b/apps/preview/src/App.tsx @@ -1,4 +1,4 @@ -import { Badge, Button, TextButton, ThemeProvider } from '@boolti/ui'; +import { Badge, Button, TextButton, BooltiUIProvider } from '@boolti/ui'; const Icon = () => ( @@ -13,7 +13,7 @@ const Icon = () => ( const App = () => { return ( - +

Preview
{ 공연 종료

- + ); };