diff --git a/src/App.tsx b/src/App.tsx index 3913385..930189c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,8 @@ import { RouterProvider } from "react-router-dom"; +import Modal from "./components/Modal/Modal"; import { router } from "./Router"; + import ToastContainer from "@/components/Toast/ToastContainer"; function App() { @@ -8,6 +10,7 @@ function App() { <> + ); } diff --git a/src/components/Modal/AlertModalLayout.tsx b/src/components/Modal/AlertModalLayout.tsx new file mode 100644 index 0000000..f4a3d11 --- /dev/null +++ b/src/components/Modal/AlertModalLayout.tsx @@ -0,0 +1,70 @@ +import Button from "../Button"; + +import { Check, Notice } from "@/assets/icon"; +import { useModalStore } from "@/store/useModalStore"; + +interface ModalButton { + label: string; + style: "primary" | "white"; + onClick: () => void; +} + +interface Props { + type: "alert" | "message"; + iconType?: "check" | "warning" | "none"; + message: string; + button: ModalButton; +} + +const ICONS = { + check: Check, + warning: Notice, +}; + +export default function AlertModalLayout({ + type, + iconType = "none", + message, + button, +}: Props) { + const Icon = iconType !== "none" ? ICONS[iconType] : null; + const { closeModal } = useModalStore(); + return ( +
e.stopPropagation()} + > + {Icon && }{" "} +

{message}

+
+
+ +
+
+
+ ); +} diff --git a/src/components/Modal/ConfirmModalLayout.tsx b/src/components/Modal/ConfirmModalLayout.tsx new file mode 100644 index 0000000..cbe9ae6 --- /dev/null +++ b/src/components/Modal/ConfirmModalLayout.tsx @@ -0,0 +1,49 @@ +import { Check, Notice } from "@/assets/icon"; + +const ICONS = { + check: Check, + warning: Notice, +}; + +interface ConfirmModalLayoutProps { + iconType?: "check" | "warning" | "none"; + message: string; + onClose: () => void; + onConfirm: () => void; + confirmText?: string; + cancelText?: string; +} + +export default function ConfirmModalLayout({ + iconType = "none", + message, + onClose, + onConfirm, + confirmText = "예", + cancelText = "아니오", +}: ConfirmModalLayoutProps) { + const Icon = iconType !== "none" ? ICONS[iconType] : null; + + return ( +
+ {Icon && } +

+ {message} +

+
+ + +
+
+ ); +} diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx new file mode 100644 index 0000000..07b3c68 --- /dev/null +++ b/src/components/Modal/Modal.tsx @@ -0,0 +1,45 @@ +import AlertModalLayout from "./AlertModalLayout"; +import ConfirmModalLayout from "./ConfirmModalLayout"; + +import { useModalStore } from "@/store/useModalStore"; + +export default function Modal() { + const { isOpen, options, closeModal } = useModalStore(); + + if (!isOpen || !options) return null; + + const handleClose = () => { + options.onClose?.(); + closeModal(); + }; + + return ( +
+ {options.type === "confirm" && ( + { + options.onConfirm?.(); + closeModal(); + }} + confirmText={options.confirmText ?? "예"} + cancelText={options.cancelText ?? "아니오"} + /> + )} + {(options.type === "alert" || options.type === "message") && ( + + )} +
+ ); +} diff --git a/src/components/index.ts b/src/components/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/store/useModalStore.ts b/src/store/useModalStore.ts new file mode 100644 index 0000000..b0e6d04 --- /dev/null +++ b/src/store/useModalStore.ts @@ -0,0 +1,34 @@ +import { create } from "zustand"; + +export type ModalType = "alert" | "confirm" | "message"; + +export interface ModalButton { + label: string; + style: "primary" | "white"; + onClick: () => void; +} + +export interface ModalOptions { + type: "alert" | "confirm" | "message"; + message: string; + iconType?: "check" | "warning" | "none"; + buttons?: ModalButton[]; + onClose?: () => void; + onConfirm?: () => void; + confirmText?: string; + cancelText?: string; +} + +interface ModalState { + isOpen: boolean; + options: ModalOptions | null; + openModal: (options: ModalOptions) => void; + closeModal: () => void; +} + +export const useModalStore = create((set) => ({ + isOpen: false, + options: null, + openModal: (options) => set({ isOpen: true, options }), + closeModal: () => set({ isOpen: false, options: null }), +})); diff --git a/src/types/svg.d.ts b/src/types/svg.d.ts new file mode 100644 index 0000000..ed25de5 --- /dev/null +++ b/src/types/svg.d.ts @@ -0,0 +1,10 @@ +declare module "*.svg?react" { + import * as React from "react"; + const ReactComponent: React.FC>; + export default ReactComponent; +} + +declare module "*.svg" { + const content: string; + export default content; +} diff --git a/tsconfig.json b/tsconfig.json index 1ffef60..25ebec3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,5 +3,9 @@ "references": [ { "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" } - ] + ], + "compilerOptions": { + "typeRoots": ["./src/types", "./node_modules/@types"] + }, + "includes": ["src", "src/types"] }