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;