diff --git a/apps/admin/src/pages/HomePage/HomePage.tsx b/apps/admin/src/pages/HomePage/HomePage.tsx index 2f96d96c..e938bd22 100644 --- a/apps/admin/src/pages/HomePage/HomePage.tsx +++ b/apps/admin/src/pages/HomePage/HomePage.tsx @@ -1,4 +1,4 @@ -import { useToast, useDialog } from '@boolti/ui'; +import { useToast, useDialog, useConfirm } from '@boolti/ui'; import Header from '../../components/Header/Header'; import Layout from '../../components/Layout/Layout'; import { PATH } from '../../constants/routes'; @@ -11,6 +11,8 @@ const HomePage = () => { const dialog2 = useDialog(); const dialog3 = useDialog(); + const confirm = useConfirm(); + return ( { > Open Dialog 2 + + ); }; diff --git a/packages/ui/src/components/BooltiUIProvider/index.tsx b/packages/ui/src/components/BooltiUIProvider/index.tsx index 32712b42..66c13454 100644 --- a/packages/ui/src/components/BooltiUIProvider/index.tsx +++ b/packages/ui/src/components/BooltiUIProvider/index.tsx @@ -1,3 +1,4 @@ +import ConfirmProvider from '../ConfirmProvider'; import DialogProvider from '../DialogProvider'; import ThemeProvider from '../ThemeProvider'; @@ -8,7 +9,9 @@ interface BooltiUIProviderProps { const BooltiUIProvider = ({ children }: BooltiUIProviderProps) => { return ( - {children} + + {children} + ); }; diff --git a/packages/ui/src/components/Confirm/Confirm.styles.ts b/packages/ui/src/components/Confirm/Confirm.styles.ts new file mode 100644 index 00000000..f7fbbf86 --- /dev/null +++ b/packages/ui/src/components/Confirm/Confirm.styles.ts @@ -0,0 +1,66 @@ +import styled from '@emotion/styled'; + +const CONFIRM_WIDTH = '450px'; + +const DimmedArea = styled.div` + position: fixed; + inset: 0; + background-color: ${({ theme }) => theme.palette.dim.dialog}; + display: flex; + justify-content: center; + align-items: center; +`; + +const Confirm = styled.div` + width: ${CONFIRM_WIDTH}; + border-radius: 8px; + background-color: ${({ theme }) => theme.palette.grey.w}; + padding: 32px; +`; + +const ConfirmMessage = styled.p` + ${({ theme }) => theme.typo.b3}; + color: ${({ theme }) => theme.palette.grey.g90}; + margin-bottom: 32px; +`; + +const ConfirmButtonContainer = styled.div` + text-align: right; +`; + +const CancelButton = styled.button` + display: inline-flex; + justify-content: center; + align-items: center; + height: 48px; + padding: 0 20px; + border-radius: 4px; + border: 1px solid ${({ theme }) => theme.palette.grey.g90}; + background-color: ${({ theme }) => theme.palette.grey.w}; + color: ${({ theme }) => theme.palette.grey.g90}; + ${({ theme }) => theme.typo.sh1}; + margin-right: 8px; + cursor: pointer; +`; + +const ConfirmButton = styled.button` + display: inline-flex; + justify-content: center; + align-items: center; + height: 48px; + padding: 0 20px; + border-radius: 4px; + background-color: ${({ theme }) => theme.palette.primary.o1}; + color: ${({ theme }) => theme.palette.grey.w}; + ${({ theme }) => theme.typo.sh1}; + cursor: pointer; +`; + +export default { + DimmedArea, + Confirm, + ConfirmMessage, + ConfirmButtonContainer, + CancelButton, + ConfirmButton, +}; diff --git a/packages/ui/src/components/Confirm/index.tsx b/packages/ui/src/components/Confirm/index.tsx new file mode 100644 index 00000000..48f8fb58 --- /dev/null +++ b/packages/ui/src/components/Confirm/index.tsx @@ -0,0 +1,32 @@ +import Portal from '../Portal'; +import Styled from './Confirm.styles'; + +interface ConfirmProps { + children: React.ReactNode; + cancelText?: string; + confirmText?: string; + onCancel?: () => void; + onConfirm?: () => void; +} + +const Confirm = ({ children, cancelText, confirmText, onCancel, onConfirm }: ConfirmProps) => { + return ( + + + + {children} + + + {cancelText ?? '취소'} + + + {confirmText ?? '확인'} + + + + + + ); +}; + +export default Confirm; diff --git a/packages/ui/src/components/ConfirmProvider/index.tsx b/packages/ui/src/components/ConfirmProvider/index.tsx new file mode 100644 index 00000000..e8b4e468 --- /dev/null +++ b/packages/ui/src/components/ConfirmProvider/index.tsx @@ -0,0 +1,35 @@ +import { useState } from 'react'; +import confirmContext, { IConfirm } from '../../contexts/confirmContext'; +import Confirm from '../Confirm'; + +interface ConfirmProviderProps { + children: React.ReactNode; +} + +const ConfirmProvider = ({ children }: ConfirmProviderProps) => { + const [currentConfirm, setCurrentConfirm] = useState(null); + + return ( + + {children} + {currentConfirm && ( + { + currentConfirm.resolve(false); + setCurrentConfirm(null); + }} + onConfirm={() => { + currentConfirm.resolve(true); + setCurrentConfirm(null); + }} + > + {currentConfirm.message} + + )} + + ); +}; + +export default ConfirmProvider; diff --git a/packages/ui/src/contexts/confirmContext.ts b/packages/ui/src/contexts/confirmContext.ts new file mode 100644 index 00000000..7ed7b825 --- /dev/null +++ b/packages/ui/src/contexts/confirmContext.ts @@ -0,0 +1,21 @@ +import { createContext } from 'react'; + +export interface ConfirmButtonText { + cancel?: string; + confirm?: string; +} + +export interface IConfirm { + message: React.ReactNode; + buttonText?: ConfirmButtonText; + resolve: (value: boolean | PromiseLike) => void; +} + +interface ConfirmContext { + currentConfirm: IConfirm | null; + setCurrentConfirm: React.Dispatch>; +} + +const confirmContext = createContext(null); + +export default confirmContext; diff --git a/packages/ui/src/contexts/dialogContext.tsx b/packages/ui/src/contexts/dialogContext.ts similarity index 100% rename from packages/ui/src/contexts/dialogContext.tsx rename to packages/ui/src/contexts/dialogContext.ts diff --git a/packages/ui/src/hooks/index.ts b/packages/ui/src/hooks/index.ts index 1cd50e68..c2b91d23 100644 --- a/packages/ui/src/hooks/index.ts +++ b/packages/ui/src/hooks/index.ts @@ -1,4 +1,5 @@ import useToast from './useToast'; import useDialog from './useDialog'; +import useConfirm from './useConfirm'; -export { useToast, useDialog }; +export { useToast, useDialog, useConfirm }; diff --git a/packages/ui/src/hooks/useConfirm.ts b/packages/ui/src/hooks/useConfirm.ts new file mode 100644 index 00000000..948618f6 --- /dev/null +++ b/packages/ui/src/hooks/useConfirm.ts @@ -0,0 +1,17 @@ +import { useCallback, useContext } from 'react'; +import confirmContext, { ConfirmButtonText } from '../contexts/confirmContext'; + +const useConfirm = () => { + const context = useContext(confirmContext); + + return useCallback( + (message: React.ReactNode, buttonText?: ConfirmButtonText) => { + return new Promise((resolve) => { + context?.setCurrentConfirm({ message, buttonText, resolve }); + }); + }, + [context], + ); +}; + +export default useConfirm;