diff --git a/src/app/(content-layout)/[groupId]/(groupId)/@modal/(.)create/page.tsx b/src/app/(content-layout)/[groupId]/(groupId)/@modal/(.)create/page.tsx deleted file mode 100644 index d26ae3f0..00000000 --- a/src/app/(content-layout)/[groupId]/(groupId)/@modal/(.)create/page.tsx +++ /dev/null @@ -1,30 +0,0 @@ -'use client'; -import { useEffect } from 'react'; -import { - ModalCloseButton, - ModalContainer, - ModalFooter, - ModalHeading, - ModalOverlay, -} from '@/components/common/modal'; -import { useModal, ModalPortal } from '@/contexts/ModalContext'; -export default function Page() { - const { openModal } = useModal(); - useEffect(() => { - openModal('modalId'); - }, []); - - return ( - - - - -
- 할 일 목록 추가 -
- -
-
-
- ); -} diff --git a/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistCreateModal.tsx b/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistCreateModal.tsx deleted file mode 100644 index f2521707..00000000 --- a/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistCreateModal.tsx +++ /dev/null @@ -1,74 +0,0 @@ -'use client'; -import { useState } from 'react'; -import Button from '@/components/common/Button'; -import FormField from '@/components/common/formField'; -import { - ModalCloseButton, - ModalContainer, - ModalFooter, - ModalHeading, - ModalOverlay, -} from '@/components/common/modal'; -import { useModal, ModalPortal } from '@/contexts/ModalContext'; -import BouncingDots from '@/components/common/loading/BouncingDots'; -import { validateEmptyValue } from '@/utils/validators'; - -interface TasklistCreateModalProps { - modalId: string; - isLoading: boolean; - createTasklist: (name: string) => void; -} - -export default function TasklistCreateModal({ - modalId, - isLoading, - createTasklist, -}: TasklistCreateModalProps) { - const [name, setName] = useState(''); - const { closeModal } = useModal(); - - const handleChangeName = (e: React.ChangeEvent) => { - setName(e.target.value); - }; - - const clearName = () => { - setName(''); - }; - - const handleClickAddButton = async () => { - if (validateEmptyValue(name)) return; - createTasklist(name); - clearName(); - closeModal(modalId); - }; - - return ( - <> - - - - -
- 할 일 목록 추가 - -
- - - -
-
-
- - ); -} diff --git a/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/index.tsx b/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/index.tsx index 68755d88..183ac8f4 100644 --- a/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/index.tsx +++ b/src/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/index.tsx @@ -1,10 +1,8 @@ 'use client'; import useTasklists from '@/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/useTasklists'; import TasklistItem from '@/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistItem'; -import TasklistCreateModal from '@/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistCreateModal'; import TasklistUpdateModal from '@/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistUpdateModal'; import TasklistDeleteModal from '@/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/TasklistDeleteModal'; -import { ModalTrigger } from '@/components/common/modal'; import { Group } from '@/types/group'; import { Tasklist } from '@/types/tasklist'; import Link from 'next/link'; @@ -20,15 +18,12 @@ export default function Tasklists({ groupId, tasklists }: TasklistsProps) { optimisticTasklists, selectedTasklist, setSelectedTasklist, - isCreateLoading, isUpdateLoading, isDeleteLoading, - createTasklist, updateTasklist, deleteTasklist, } = useTasklists(groupId, tasklists); - const tasklistCreateModalId = `tasklistCreate-${groupId}`; const tasklistUpdateModalId = selectedTasklist ? `tasklistUpdate-${selectedTasklist.id}` : ''; const tasklistDeleteModalId = selectedTasklist ? `tasklistDelete-${selectedTasklist.id}` : ''; const totalTasklistCount = optimisticTasklists.length; @@ -41,10 +36,7 @@ export default function Tasklists({ groupId, tasklists }: TasklistsProps) {

할 일 목록 ({totalTasklistCount}개)

- {/* - + 새로운 목록 추가하기 - */} - + + 새로운 목록 추가하기 @@ -60,12 +52,6 @@ export default function Tasklists({ groupId, tasklists }: TasklistsProps) { - - {selectedTasklist && ( zz; -} diff --git a/src/app/(content-layout)/[groupId]/(groupId)/layout.tsx b/src/app/(content-layout)/[groupId]/(groupId)/layout.tsx deleted file mode 100644 index 73e93591..00000000 --- a/src/app/(content-layout)/[groupId]/(groupId)/layout.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export default function Layout({ - children, - modal, -}: { - children: React.ReactNode; - modal: React.ReactNode; -}) { - return ( - <> -
{children}
-
{modal}
- - ); -} diff --git a/src/app/@modal/(...)[...modal]/create-tasklist/page.tsx b/src/app/@modal/(...)[...modal]/create-tasklist/page.tsx new file mode 100644 index 00000000..49be2bb1 --- /dev/null +++ b/src/app/@modal/(...)[...modal]/create-tasklist/page.tsx @@ -0,0 +1,58 @@ +'use client'; +import { useState } from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import Button from '@/components/common/Button'; +import FormField from '@/components/common/formField'; +import Modal from '@/components/common/modal/newModal'; +import BouncingDots from '@/components/common/loading/BouncingDots'; +import { validateEmptyValue } from '@/utils/validators'; +import { createTasklistAction } from '@/app/(content-layout)/[groupId]/(groupId)/_[groupId]/Tasklists/actions'; + +export default function Page() { + const { groupId } = useParams<{ groupId: string }>(); + const router = useRouter(); + const [name, setName] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + const handleChangeName = (e: React.ChangeEvent) => { + setName(e.target.value); + }; + + const handleClickAddButton = async () => { + setIsLoading(true); + if (validateEmptyValue(name)) { + setIsLoading(false); + return; + } + createTasklistAction(Number(groupId), name).then(() => { + setIsLoading(false); + router.back(); + }); + }; + + return ( + + + +
+ 할 일 목록 추가 + +
+ + + +
+
+ ); +} diff --git a/src/app/(content-layout)/[groupId]/(groupId)/@modal/default.tsx b/src/app/@modal/default.tsx similarity index 100% rename from src/app/(content-layout)/[groupId]/(groupId)/@modal/default.tsx rename to src/app/@modal/default.tsx diff --git a/src/app/[...modal]/page.tsx b/src/app/[...modal]/page.tsx new file mode 100644 index 00000000..e7c23934 --- /dev/null +++ b/src/app/[...modal]/page.tsx @@ -0,0 +1,5 @@ +'use client'; + +import notFound from '@/app/not-found'; + +export default notFound; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index de8e1aee..0871d184 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -11,8 +11,10 @@ export const metadata: Metadata = { export default function RootLayout({ children, + modal, }: Readonly<{ children: React.ReactNode; + modal: React.ReactNode; }>) { return ( @@ -24,6 +26,7 @@ export default function RootLayout({
{children}
+
{modal}
diff --git a/src/components/common/modal/newModal/CloseButton.tsx b/src/components/common/modal/newModal/CloseButton.tsx new file mode 100644 index 00000000..634aa37a --- /dev/null +++ b/src/components/common/modal/newModal/CloseButton.tsx @@ -0,0 +1,23 @@ +'use client'; +import Image from 'next/image'; +import { useRouter } from 'next/navigation'; +import clsx from 'clsx'; + +export default function ModalCloseButton({ + className, + onClick, + ...props +}: React.ComponentProps<'button'>) { + const router = useRouter(); + + const handleClick = (e: React.MouseEvent) => { + onClick?.(e); + router.back(); + }; + + return ( + + ); +} diff --git a/src/components/common/modal/newModal/Container.tsx b/src/components/common/modal/newModal/Container.tsx new file mode 100644 index 00000000..e95b4a6b --- /dev/null +++ b/src/components/common/modal/newModal/Container.tsx @@ -0,0 +1,15 @@ +import clsx from 'clsx'; + +export default function Container({ className, children, ...props }: React.ComponentProps<'div'>) { + return ( +
+ {children} +
+ ); +} diff --git a/src/components/common/modal/newModal/Description.tsx b/src/components/common/modal/newModal/Description.tsx new file mode 100644 index 00000000..75ffbb04 --- /dev/null +++ b/src/components/common/modal/newModal/Description.tsx @@ -0,0 +1,15 @@ +import clsx from 'clsx'; + +export default function Description({ className, children, ...props }: React.ComponentProps<'p'>) { + return ( +

+ {children} +

+ ); +} diff --git a/src/components/common/modal/newModal/Footer.tsx b/src/components/common/modal/newModal/Footer.tsx new file mode 100644 index 00000000..1ff370c8 --- /dev/null +++ b/src/components/common/modal/newModal/Footer.tsx @@ -0,0 +1,9 @@ +import clsx from 'clsx'; + +export default function Footer({ className, children, ...props }: React.ComponentProps<'div'>) { + return ( +
+ {children} +
+ ); +} diff --git a/src/components/common/modal/newModal/Heading.tsx b/src/components/common/modal/newModal/Heading.tsx new file mode 100644 index 00000000..59aa69a0 --- /dev/null +++ b/src/components/common/modal/newModal/Heading.tsx @@ -0,0 +1,12 @@ +import clsx from 'clsx'; + +export default function Heading({ className, children, ...props }: React.ComponentProps<'h2'>) { + return ( +

+ {children} +

+ ); +} diff --git a/src/components/common/modal/newModal/Overlay.tsx b/src/components/common/modal/newModal/Overlay.tsx new file mode 100644 index 00000000..4b823283 --- /dev/null +++ b/src/components/common/modal/newModal/Overlay.tsx @@ -0,0 +1,47 @@ +'use client'; +import { useEffect } from 'react'; +import { useRouter } from 'next/navigation'; +import clsx from 'clsx'; + +interface OverlayProps extends React.ComponentProps<'div'> { + disableOverlayClose?: boolean; +} + +export default function Overlay({ + disableOverlayClose = false, + onClick, + className, + children, + ...props +}: OverlayProps) { + const router = useRouter(); + + const handleClick = (e: React.MouseEvent) => { + if (e.target === e.currentTarget && !disableOverlayClose) { + onClick?.(e); + router.back(); + } + }; + + useEffect(function lockBodyScroll() { + const originalStyle = window.getComputedStyle(document.body).overflow; + document.body.style.overflow = 'hidden'; + + return () => { + document.body.style.overflow = originalStyle; + }; + }, []); + + return ( +
+ {children} +
+ ); +} diff --git a/src/components/common/modal/newModal/index.ts b/src/components/common/modal/newModal/index.ts new file mode 100644 index 00000000..d0138340 --- /dev/null +++ b/src/components/common/modal/newModal/index.ts @@ -0,0 +1,17 @@ +import CloseButton from './CloseButton'; +import Container from './Container'; +import Description from './Description'; +import Footer from './Footer'; +import Heading from './Heading'; +import Overlay from './Overlay'; + +const Modal = { + CloseButton, + Container, + Description, + Footer, + Heading, + Overlay, +}; + +export default Modal;