From 83c8b6f01b50df5281625a563799147eb5f8ab44 Mon Sep 17 00:00:00 2001 From: jieunsse Date: Fri, 6 Feb 2026 18:29:46 +0900 Subject: [PATCH 01/15] =?UTF-8?q?feat:=20=ED=95=A0=EC=9D=BC=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=AA=A8=EB=8B=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modal/domain/AddTodoList.module.css | 113 ++++++++++++++++++ src/components/Modal/domain/AddTodoList.tsx | 52 ++++++++ src/components/Modal/types/types.ts | 1 + 3 files changed, 166 insertions(+) create mode 100644 src/components/Modal/domain/AddTodoList.module.css create mode 100644 src/components/Modal/domain/AddTodoList.tsx diff --git a/src/components/Modal/domain/AddTodoList.module.css b/src/components/Modal/domain/AddTodoList.module.css new file mode 100644 index 0000000..5d1d989 --- /dev/null +++ b/src/components/Modal/domain/AddTodoList.module.css @@ -0,0 +1,113 @@ +.container { + --dialog-max-width-mobile: 375px; + --dialog-padding: 16px 16px 32px; + --dialog-gap: 10px; + --control-height: 48px; + --close-button-size: 24px; + --input-max-width: 288px; + --button-max-width: 280px; + + display: flex; + padding: var(--dialog-padding); + flex-direction: column; + gap: var(--dialog-gap); + align-items: stretch; + box-sizing: border-box; +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.title { + color: var(--Text-Primary, #1e293b); + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 19px; + margin: 0; +} + +.form { + display: flex; + flex-direction: column; + gap: 24px; +} + +.input.input { + padding: 14px 16px; + width: 100%; + max-width: var(--input-max-width); + height: var(--control-height); + box-sizing: border-box; + align-self: center; + text-align: center; +} + +.input.input::placeholder { + color: var(--Text-Default, #64748b); + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 19px; +} + +.footer { + display: flex; + justify-content: center; +} + +.button { + display: flex; + width: 100%; + max-width: var(--button-max-width); + height: var(--control-height); + justify-content: center; + align-items: center; + gap: 10px; + flex-shrink: 0; + border-radius: 12px; + background: var(--Color-Brand-Primary, #5189fa); + border: none; + color: var(--color-text-inverse, #ffffff); + text-align: center; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 19px; +} + +.closeButton { + width: var(--close-button-size); + height: var(--close-button-size); + padding: 0; + border: none; + background: transparent; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; +} + +.modalContent.modalContent { + --dialog-radius-desktop: 24px; + --dialog-radius-mobile: 24px 24px 0 0; + border-radius: var(--dialog-radius-desktop); +} + +@media (max-width: 480px) { + .container { + width: 100%; + max-width: var(--dialog-max-width-mobile); + } + + .modalContent.modalContent { + border-radius: var(--dialog-radius-mobile); + } +} diff --git a/src/components/Modal/domain/AddTodoList.tsx b/src/components/Modal/domain/AddTodoList.tsx new file mode 100644 index 0000000..99ed38a --- /dev/null +++ b/src/components/Modal/domain/AddTodoList.tsx @@ -0,0 +1,52 @@ +'use client'; + +import Image from 'next/image'; +import type { FormEvent } from 'react'; +import { Input } from '@/components/input'; +import Modal from '../Modal'; +import styles from './AddTodoList.module.css'; +import xMarkBig from '@/assets/icons/xMark/xMarkBig.svg'; + +const TITLE_ID = 'add-todo-list-title'; + +export interface AddTodoListProps { + isOpen: boolean; + onClose: () => void; + onCreate: () => void; +} + +export default function AddTodoList({ isOpen, onClose, onCreate }: AddTodoListProps) { + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + onCreate(); + }; + + return ( + +
+
+

+ 할 일 목록 +

+ +
+ +
+ +
+ +
+
+
+
+ ); +} diff --git a/src/components/Modal/types/types.ts b/src/components/Modal/types/types.ts index 6c53d07..98b4c1c 100644 --- a/src/components/Modal/types/types.ts +++ b/src/components/Modal/types/types.ts @@ -6,6 +6,7 @@ interface BaseModalProps { children?: ReactNode; ariaDescribedby?: string; className?: string; + contentClassName?: string; closeOnOverlayClick?: boolean; closeOnEscape?: boolean; } From 35f8a93b6ddb280b2e57a5372e2b8940eab195a1 Mon Sep 17 00:00:00 2001 From: jieunsse Date: Fri, 6 Feb 2026 18:37:59 +0900 Subject: [PATCH 02/15] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=EC=B4=88?= =?UTF-8?q?=EB=8C=80=20=EB=AA=A8=EB=8B=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modal/domain/MemberInvite.module.css | 90 +++++++++++++++++++ src/components/Modal/domain/MemberInvite.tsx | 48 ++++++++++ 2 files changed, 138 insertions(+) create mode 100644 src/components/Modal/domain/MemberInvite.module.css create mode 100644 src/components/Modal/domain/MemberInvite.tsx diff --git a/src/components/Modal/domain/MemberInvite.module.css b/src/components/Modal/domain/MemberInvite.module.css new file mode 100644 index 0000000..a196476 --- /dev/null +++ b/src/components/Modal/domain/MemberInvite.module.css @@ -0,0 +1,90 @@ +@import '@shared/styles/color.css'; + +.container { + position: relative; + width: 384px; + height: 211px; + padding: 40px 24px 32px; + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + text-align: center; +} + +.modalContent { + border-radius: 24px; +} + +.closeButton { + position: absolute; + top: 16px; + right: 16px; + width: 24px; + height: 24px; + padding: 0; + display: inline-flex; + align-items: center; + justify-content: center; + border: none; + background: transparent; + cursor: pointer; +} + +.title { + margin: 8px 0 0; + color: var(--Text-Primary, #1e293b); + font-family: Pretendard, sans-serif; + font-size: 16px; + font-weight: 500; + line-height: 19px; +} + +.description { + margin: 4px 0 16px; + color: var(--Text-Secondary, #334155); + font-family: Pretendard, sans-serif; + font-size: 14px; + font-weight: 500; + line-height: 17px; +} + +.copyButton { + display: flex; + width: 280px; + height: 48px; + padding: 14px 24px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 16px; + border: none; + background: var(--color-brand-primary); + color: var(--Text-inverse, #fff); + font-family: Pretendard, sans-serif; + font-size: 16px; + font-weight: 600; + line-height: 19px; + cursor: pointer; +} + +.copyButton:disabled { + background: var(--color-interaction-inactive); + cursor: not-allowed; +} + +@media (max-width: 480px) { + .modalContent { + border-radius: 24px 24px 0 0; + } + + .container { + width: 100%; + max-width: 375px; + height: 195px; + flex-shrink: 0; + border-radius: 24px 24px 0 0; + background: var(--Background-Primary, #fff); + box-sizing: border-box; + } +} diff --git a/src/components/Modal/domain/MemberInvite.tsx b/src/components/Modal/domain/MemberInvite.tsx new file mode 100644 index 0000000..82f3e66 --- /dev/null +++ b/src/components/Modal/domain/MemberInvite.tsx @@ -0,0 +1,48 @@ +'use client'; + +import Image from 'next/image'; +import Modal from '../Modal'; +import styles from './MemberInvite.module.css'; +import xMarkBig from '@/assets/icons/xMark/xMarkBig.svg'; + +export type MemberInviteProps = { + isOpen: boolean; + onClose: () => void; + inviteLink: string; + onCopy?: (link: string) => void; +}; + +export default function MemberInvite({ isOpen, onClose, inviteLink, onCopy }: MemberInviteProps) { + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(inviteLink); + } finally { + onCopy?.(inviteLink); + } + }; + + return ( + +
+ +

+ 멤버 초대 +

+

+ 그룹에 참여할 수 있는 링크를 복사합니다. +

+ +
+
+ ); +} From 9837a09575a41ff639d4a4c0f7e6838c5f947c9e Mon Sep 17 00:00:00 2001 From: jieunsse Date: Fri, 6 Feb 2026 19:36:08 +0900 Subject: [PATCH 03/15] =?UTF-8?q?feat:=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EB=A6=AC=EC=85=8B=20=EB=AA=A8=EB=8B=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modal/domain/ResetPassword.module.css | 117 ++++++++++++++++++ src/components/Modal/domain/ResetPassword.tsx | 79 ++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 src/components/Modal/domain/ResetPassword.module.css create mode 100644 src/components/Modal/domain/ResetPassword.tsx diff --git a/src/components/Modal/domain/ResetPassword.module.css b/src/components/Modal/domain/ResetPassword.module.css new file mode 100644 index 0000000..8416fd3 --- /dev/null +++ b/src/components/Modal/domain/ResetPassword.module.css @@ -0,0 +1,117 @@ +.modalContent.modalContent { + display: inline-flex; + padding: 16px 16px 32px 16px; + flex-direction: column; + align-items: stretch; + gap: 10px; + border-radius: 24px; + background: var(--Background-Primary, #fff); + box-sizing: border-box; + width: 384px; + height: 260px; +} + +.container { + display: flex; + flex-direction: column; + gap: 16px; + width: 100%; + height: 100%; + box-sizing: border-box; + margin-top: 24px; +} + +.header { + display: flex; + flex-direction: column; + gap: 6px; + align-items: center; +} + +.title { + margin: 0; + color: var(--Text-Primary, #1e293b); + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 19px; + text-align: center; +} + +.description { + margin: 0; + color: var(--Text-Default, #64748b); + font-family: Pretendard; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 17px; + text-align: center; +} + +.form { + display: flex; + flex-direction: column; + gap: 16px; + flex: 1; +} + +.input.input { + display: flex; + width: 280px; + height: 48px; + padding: 16px; + align-items: center; + gap: 10px; + border-radius: 12px; + border: 1px solid var(--Border-Primary, #e2e8f0); + background: var(--Background-Primary, #fff); + box-sizing: border-box; + margin: 0 auto; +} + +.actions { + display: flex; + width: 280px; + gap: 8px; + margin: auto auto 0; +} + +.closeButton { + display: flex; + width: 136px; + height: 48px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 12px; + border: 1px solid var(--Color-Brand-Primary, #5189fa); + background: transparent; + color: var(--Color-Brand-Primary, #5189fa); + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 19px; + cursor: pointer; +} + +.sendButton { + display: flex; + width: 136px; + height: 48px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 12px; + border: none; + background: var(--Color-Brand-Primary, #5189fa); + color: #fff; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 19px; + cursor: pointer; +} diff --git a/src/components/Modal/domain/ResetPassword.tsx b/src/components/Modal/domain/ResetPassword.tsx new file mode 100644 index 0000000..94f8afa --- /dev/null +++ b/src/components/Modal/domain/ResetPassword.tsx @@ -0,0 +1,79 @@ +'use client'; + +import type { FormEvent } from 'react'; + +import Modal from '../Modal'; +import { Input } from '@/components/input'; +import type { InputProps } from '@/components/input/types/types'; +import styles from './ResetPassword.module.css'; + +const TITLE_ID = 'reset-password-title'; +const DESCRIPTION_ID = 'reset-password-description'; + +export interface ResetPasswordProps { + isOpen: boolean; + onClose: () => void; + onSendLink: () => void; + emailInputProps?: Omit; +} + +/** + * 비밀번호 재설정 링크를 보내기 위한 모달 UI 컴포넌트. + * + * @param props.isOpen 모달을 열지 여부 + * @param props.onClose 모달을 닫을 때 호출 (오버레이 클릭/Escape 포함) + * @param props.onSendLink "링크 보내기" 제출 시 호출되는 콜백 + * @param props.emailInputProps 이메일 Input에 그대로 전달할 props (예: `value`, `onChange`, `isError`, `errorMessage`) + */ +export default function ResetPassword({ + isOpen, + onClose, + onSendLink, + emailInputProps, +}: ResetPasswordProps) { + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + onSendLink(); + }; + + return ( + +
+
+

+ 비밀번호 재설정 +

+

+ 비밀번호 재설정 링크를 보내드립니다. +

+
+ +
+ + +
+ + +
+
+
+
+ ); +} From aa170fadb3877f92d404f1b47554bc2c3317ea11 Mon Sep 17 00:00:00 2001 From: jieunsse Date: Sat, 7 Feb 2026 15:22:19 +0900 Subject: [PATCH 04/15] =?UTF-8?q?feat:=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EB=B3=80=EA=B2=BD=20=EB=AA=A8=EB=8B=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modal/domain/ChangePassword.module.css | 132 ++++++++++++++++++ .../Modal/domain/ChangePassword.tsx | 102 ++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 src/components/Modal/domain/ChangePassword.module.css create mode 100644 src/components/Modal/domain/ChangePassword.tsx diff --git a/src/components/Modal/domain/ChangePassword.module.css b/src/components/Modal/domain/ChangePassword.module.css new file mode 100644 index 0000000..2b3195d --- /dev/null +++ b/src/components/Modal/domain/ChangePassword.module.css @@ -0,0 +1,132 @@ +.modalContent.modalContent { + display: flex; + width: 384px; + height: 353px; + padding: 16px 16px 32px; + flex-direction: column; + align-items: stretch; + gap: 10px; + border-radius: 12px; + background: var(--Background-Primary, #fff); + box-sizing: border-box; +} + +.container { + width: 100%; + height: 100%; + padding-top: 24px; + display: flex; + flex-direction: column; + gap: 24px; + box-sizing: border-box; +} + +.title { + margin: 0; + width: 100%; + color: var(--Text-Primary, #1e293b); + text-align: center; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 19px; +} + +.form { + display: flex; + flex-direction: column; + gap: 16px; + flex: 1; + min-height: 0; +} + +.field { + display: flex; + width: 280px; + margin: 0 auto; + flex-direction: column; + gap: 8px; +} + +.label { + color: var(--Text-Primary, #1e293b); + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 19px; +} + +.input.input { + display: flex; + width: 280px; + height: 48px; + padding: 16px; + align-items: center; + gap: 10px; + border-radius: 12px; + border: 1px solid var(--Border-Primary, #e2e8f0); + background: var(--Background-Primary, #fff); + box-sizing: border-box; +} + +.input.input::placeholder { + color: var(--Text-Default, #64748b); + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 19px; +} + +.actions { + display: flex; + width: 280px; + margin: auto auto 0; + gap: 8px; +} + +.closeButton { + display: flex; + width: 136px; + height: 48px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 12px; + border: 1px solid var(--Color-Brand-Primary, #5189fa); + background: var(--Background-Primary, #fff); + color: var(--Color-Brand-Primary, #5189fa); + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 19px; + cursor: pointer; +} + +.submitButton { + display: flex; + width: 136px; + height: 48px; + justify-content: center; + align-items: center; + gap: 10px; + border: none; + border-radius: 12px; + background: var(--Color-Brand-Primary, #5189fa); + color: var(--Text-Inverse, #fff); + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 19px; + cursor: pointer; +} + +@media (max-width: 480px) { + .modalContent.modalContent { + border-radius: 12px 12px 0 0; + } +} diff --git a/src/components/Modal/domain/ChangePassword.tsx b/src/components/Modal/domain/ChangePassword.tsx new file mode 100644 index 0000000..8481651 --- /dev/null +++ b/src/components/Modal/domain/ChangePassword.tsx @@ -0,0 +1,102 @@ +'use client'; + +import type { FormEvent } from 'react'; +import { useId } from 'react'; + +import Modal from '../Modal'; +import { Input } from '@/components/input'; +import type { InputProps } from '@/components/input/types/types'; +import styles from './ChangePassword.module.css'; + +const TITLE_ID = 'change-password-title'; +const NEW_PASSWORD_NAME = 'newPassword'; +const CONFIRM_PASSWORD_NAME = 'confirmPassword'; +const NEW_PASSWORD_PLACEHOLDER = '새 비밀번호를 입력해주세요.'; +const CONFIRM_PASSWORD_PLACEHOLDER = '새 비밀번호를 다시 한 번 입력해주세요.'; + +type PasswordInputFieldProps = Omit< + InputProps, + 'className' | 'type' | 'name' | 'autoComplete' | 'placeholder' +>; + +export interface ChangePasswordProps { + isOpen: boolean; + onClose: () => void; + onSubmit: () => void; + newPasswordInputProps?: PasswordInputFieldProps; + confirmPasswordInputProps?: PasswordInputFieldProps; +} + +export default function ChangePassword({ + isOpen, + onClose, + onSubmit, + newPasswordInputProps, + confirmPasswordInputProps, +}: ChangePasswordProps) { + const generatedNewPasswordId = useId(); + const generatedConfirmPasswordId = useId(); + const newPasswordId = newPasswordInputProps?.id ?? generatedNewPasswordId; + const confirmPasswordId = confirmPasswordInputProps?.id ?? generatedConfirmPasswordId; + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + onSubmit(); + }; + + return ( + +
+

+ 비밀번호 변경하기 +

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ ); +} From bdb0a90d91cf41f8ed1755ff433d7a361b3411c5 Mon Sep 17 00:00:00 2001 From: jieunsse Date: Sat, 7 Feb 2026 15:47:18 +0900 Subject: [PATCH 05/15] =?UTF-8?q?style:=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=8B=9C=ED=94=84=ED=8C=85=20=EB=B3=B4=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Modal/domain/ResetPassword.module.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/Modal/domain/ResetPassword.module.css b/src/components/Modal/domain/ResetPassword.module.css index 8416fd3..bdf6bc7 100644 --- a/src/components/Modal/domain/ResetPassword.module.css +++ b/src/components/Modal/domain/ResetPassword.module.css @@ -115,3 +115,9 @@ line-height: 19px; cursor: pointer; } + +@media (max-width: 480px) { + .modalContent.modalContent { + border-radius: 24px 24px 0 0; + } +} From 4e6a30610894079337bee3fc600e54ac99ee1c29 Mon Sep 17 00:00:00 2001 From: jieunsse Date: Sat, 7 Feb 2026 16:12:18 +0900 Subject: [PATCH 06/15] =?UTF-8?q?feat:=20=EA=B2=BD=EA=B3=A0=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modal/domain/WarningModal.module.css | 120 ++++++++++++++++++ src/components/Modal/domain/WarningModal.tsx | 79 ++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 src/components/Modal/domain/WarningModal.module.css create mode 100644 src/components/Modal/domain/WarningModal.tsx diff --git a/src/components/Modal/domain/WarningModal.module.css b/src/components/Modal/domain/WarningModal.module.css new file mode 100644 index 0000000..c2ea796 --- /dev/null +++ b/src/components/Modal/domain/WarningModal.module.css @@ -0,0 +1,120 @@ +.modalContent.modalContent { + box-sizing: border-box; + display: inline-flex; + width: 384px; + padding: 16px 16px 32px; + flex-direction: column; + align-items: flex-start; + gap: 10px; + border-radius: 24px; + background: var(--Background-Primary, #fff); +} + +.container { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + margin-top: 20px; +} + +.header { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; +} + +.icon { + flex-shrink: 0; +} + +.title { + margin: 0; + color: var(--Text-Primary, #1e293b); + text-align: center; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 19px; +} + +.description { + margin: 0; + width: 280px; + color: var(--Text-Secondary, #334155); + text-align: center; + font-family: Pretendard; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 17px; + white-space: pre-line; +} + +.actions { + display: flex; + width: 280px; + gap: 8px; + margin-top: 16px; +} + +.closeButton { + display: flex; + width: 136px; + height: 48px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 12px; + border: 1px solid var(--Border-Secondary, #cbd5e1); + background: var(--Background-Primary, #fff); + color: var(--Text-Default, #64748b); + text-align: center; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 19px; + cursor: pointer; +} + +.confirmButton { + display: flex; + width: 136px; + height: 48px; + justify-content: center; + align-items: center; + gap: 10px; + border: none; + border-radius: 12px; + background: var(--color-status-danger, #fc4b4b); + color: var(--Text-inverse, #fff); + text-align: center; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 19px; + cursor: pointer; +} + +.confirmButton:hover { + background: var(--color-status-danger-hover, #e53e3e); +} + +.confirmButton:active { + background: var(--color-status-danger-pressed, #c53030); +} + +@media (max-width: 480px) { + .modalContent.modalContent { + width: 100%; + max-width: 375px; + border-radius: 24px 24px 0 0; + background: var(--Background-Primary, #fff); + } +} diff --git a/src/components/Modal/domain/WarningModal.tsx b/src/components/Modal/domain/WarningModal.tsx new file mode 100644 index 0000000..b344e4e --- /dev/null +++ b/src/components/Modal/domain/WarningModal.tsx @@ -0,0 +1,79 @@ +'use client'; + +import Image from 'next/image'; + +import Modal from '../Modal'; +import styles from './WarningModal.module.css'; +import alertSmall from '@/assets/icons/alert/alertSmall.svg'; + +const TITLE_ID = 'warning-modal-title'; +const DESCRIPTION_ID = 'warning-modal-description'; +const DEFAULT_TITLE = '회원 탈퇴를 진행하시겠어요?'; +const DEFAULT_DESCRIPTION = '그룹장으로 있는 그룹은 자동으로 삭제되고,\n모든 그룹에서 나가집니다.'; +const DEFAULT_CLOSE_LABEL = '닫기'; +const DEFAULT_CONFIRM_LABEL = '회원 탈퇴'; + +export interface WarningModalProps { + isOpen: boolean; + onClose: () => void; + onConfirm: () => void; + title?: string; + description?: string; + closeLabel?: string; + confirmLabel?: string; + closeOnOverlayClick?: boolean; + closeOnEscape?: boolean; +} + +export default function WarningModal({ + isOpen, + onClose, + onConfirm, + title = DEFAULT_TITLE, + description = DEFAULT_DESCRIPTION, + closeLabel = DEFAULT_CLOSE_LABEL, + confirmLabel = DEFAULT_CONFIRM_LABEL, + closeOnOverlayClick = true, + closeOnEscape = true, +}: WarningModalProps) { + return ( + +
+
+ +

+ {title} +

+
+ +

+ {description} +

+ +
+ + +
+
+
+ ); +} From a086683406ba245280f5a52d59eb30347de678aa Mon Sep 17 00:00:00 2001 From: jieunsse Date: Sat, 7 Feb 2026 16:48:04 +0900 Subject: [PATCH 07/15] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EB=AA=A8=EB=8B=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modal/domain/LogoutModal.module.css | 99 +++++++++++++++++++ src/components/Modal/domain/LogoutModal.tsx | 57 +++++++++++ 2 files changed, 156 insertions(+) create mode 100644 src/components/Modal/domain/LogoutModal.module.css create mode 100644 src/components/Modal/domain/LogoutModal.tsx diff --git a/src/components/Modal/domain/LogoutModal.module.css b/src/components/Modal/domain/LogoutModal.module.css new file mode 100644 index 0000000..eb52a63 --- /dev/null +++ b/src/components/Modal/domain/LogoutModal.module.css @@ -0,0 +1,99 @@ +.modalContent.modalContent { + width: 384px; + height: 171px; + box-sizing: border-box; + display: inline-flex; + padding: 16px 16px 32px; + flex-direction: column; + align-items: flex-start; + gap: 10px; + border-radius: 24px; + background: var(--Background-Primary, #fff); +} + +.container { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + margin-top: 8px; + box-sizing: border-box; +} + +.title { + margin: 0; + color: var(--Text-Primary, #1e293b); + text-align: center; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 19px; + margin-top: 12px; +} + +.actions { + display: flex; + width: 280px; + gap: 8px; + margin-top: auto; +} + +.closeButton { + display: flex; + width: 136px; + height: 48px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 12px; + border: 1px solid var(--Border-Secondary, #cbd5e1); + background: var(--Background-Primary, #fff); + color: var(--Text-Default, #64748b); + text-align: center; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 19px; + cursor: pointer; +} + +.confirmButton { + display: flex; + width: 136px; + height: 48px; + justify-content: center; + align-items: center; + gap: 10px; + border: none; + border-radius: 12px; + background: var(--color-status-danger, #fc4b4b); + color: var(--Text-inverse, #fff); + text-align: center; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 19px; + cursor: pointer; +} + +.confirmButton:hover { + background: var(--color-status-danger-hover, #e53e3e); +} + +.confirmButton:active { + background: var(--color-status-danger-pressed, #c53030); +} + +@media (max-width: 480px) { + .modalContent.modalContent { + width: 100%; + max-width: 375px; + border-radius: 24px 24px 0 0; + background: var(--Background-Primary, #fff); + } +} diff --git a/src/components/Modal/domain/LogoutModal.tsx b/src/components/Modal/domain/LogoutModal.tsx new file mode 100644 index 0000000..bcc9368 --- /dev/null +++ b/src/components/Modal/domain/LogoutModal.tsx @@ -0,0 +1,57 @@ +'use client'; + +import Modal from '../Modal'; +import styles from './LogoutModal.module.css'; + +const TITLE_ID = 'logout-modal-title'; +const DEFAULT_TITLE = '로그아웃 하시겠어요?'; +const DEFAULT_CLOSE_LABEL = '닫기'; +const DEFAULT_CONFIRM_LABEL = '로그아웃'; + +export interface LogoutModalProps { + isOpen: boolean; + onClose: () => void; + onConfirm: () => void; + title?: string; + closeLabel?: string; + confirmLabel?: string; + closeOnOverlayClick?: boolean; + closeOnEscape?: boolean; +} + +export default function LogoutModal({ + isOpen, + onClose, + onConfirm, + title = DEFAULT_TITLE, + closeLabel = DEFAULT_CLOSE_LABEL, + confirmLabel = DEFAULT_CONFIRM_LABEL, + closeOnOverlayClick = true, + closeOnEscape = true, +}: LogoutModalProps) { + return ( + +
+

+ {title} +

+ +
+ + +
+
+
+ ); +} From c14f7452918e2979d6a7296991d591dfcb8be213 Mon Sep 17 00:00:00 2001 From: jieunsse Date: Sat, 7 Feb 2026 17:09:42 +0900 Subject: [PATCH 08/15] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modal/domain/ProfileModal.module.css | 118 ++++++++++++++++++ src/components/Modal/domain/ProfileModal.tsx | 84 +++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 src/components/Modal/domain/ProfileModal.module.css create mode 100644 src/components/Modal/domain/ProfileModal.tsx diff --git a/src/components/Modal/domain/ProfileModal.module.css b/src/components/Modal/domain/ProfileModal.module.css new file mode 100644 index 0000000..6e104f3 --- /dev/null +++ b/src/components/Modal/domain/ProfileModal.module.css @@ -0,0 +1,118 @@ +.modalContent.modalContent { + width: 344px; + height: 243px; + display: inline-flex; + padding: 16px 32px 32px; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 8px; + border-radius: 24px; + background: var(--Background-Primary, #fff); + box-sizing: border-box; +} + +.container { + position: relative; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 8px; + box-sizing: border-box; +} + +.container > :not(.closeButton) { + transform: translateY(12px); +} + +.closeButton { + position: absolute; + top: 0; + right: 0; + width: 24px; + height: 24px; + padding: 0; + border: none; + background: transparent; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; +} + +.profileImage { + display: flex; + width: 40px; + height: 40px; + justify-content: center; + align-items: center; + aspect-ratio: 1 / 1; + border-radius: 999px; + overflow: hidden; +} + +.profileImage :global(img) { + width: 100%; + height: 100%; + object-fit: cover; +} + +.title { + margin: 0; + color: var(--Text-Primary, #1e293b); + font-family: Pretendard; + font-size: 14px; + font-style: normal; + font-weight: 600; + line-height: 17px; +} + +.email { + margin: 0; + color: var(--Text-Secondary, #334155); + font-family: Pretendard; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 14px; +} + +.copyButton { + display: flex; + width: 280px; + height: 48px; + margin-top: 16px; + justify-content: center; + align-items: center; + gap: 10px; + border: none; + border-radius: 12px; + background: var(--Color-Brand-Primary, #5189fa); + color: var(--Text-inverse, #fff); + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 19px; + cursor: pointer; +} + +.copyButton:hover { + background: var(--color-interaction-hover, #416ec8); +} + +.copyButton:active { + background: var(--color-interaction-pressed, #3b63b5); +} + +@media (max-width: 480px) { + .modalContent.modalContent { + width: 100%; + max-width: 375px; + border-radius: 24px 24px 0 0; + background: var(--Background-Primary, #fff); + } +} diff --git a/src/components/Modal/domain/ProfileModal.tsx b/src/components/Modal/domain/ProfileModal.tsx new file mode 100644 index 0000000..518ef6d --- /dev/null +++ b/src/components/Modal/domain/ProfileModal.tsx @@ -0,0 +1,84 @@ +'use client'; + +import type { ImageProps } from 'next/image'; +import Image from 'next/image'; + +import Modal from '../Modal'; +import styles from './ProfileModal.module.css'; +import profileFallback from '@/assets/icons/img/img.svg'; +import xMarkBig from '@/assets/icons/xMark/xMarkBig.svg'; + +const TITLE_ID = 'profile-modal-title'; +const EMAIL_ID = 'profile-modal-email'; +const CLOSE_BUTTON_ARIA_LABEL = '닫기'; +const DEFAULT_COPY_LABEL = '이메일 복사하기'; +const DEFAULT_PROFILE_ALT = '프로필 이미지'; + +export interface ProfileModalProps { + isOpen: boolean; + onClose: () => void; + onCopyEmail: () => void; + title: string; + email: string; + profileImageSrc?: ImageProps['src']; + profileImageAlt?: string; + copyButtonLabel?: string; + closeOnOverlayClick?: boolean; + closeOnEscape?: boolean; +} + +export default function ProfileModal({ + isOpen, + onClose, + onCopyEmail, + title, + email, + profileImageSrc, + profileImageAlt = DEFAULT_PROFILE_ALT, + copyButtonLabel = DEFAULT_COPY_LABEL, + closeOnOverlayClick = true, + closeOnEscape = true, +}: ProfileModalProps) { + return ( + +
+ + +
+ {profileImageAlt} +
+ +

+ {title} +

+

+ {email} +

+ + +
+
+ ); +} From 6732f71a814067c4cfc6c5a32188978305d9d848 Mon Sep 17 00:00:00 2001 From: jieunsse Date: Sat, 7 Feb 2026 17:23:13 +0900 Subject: [PATCH 09/15] =?UTF-8?q?refactor:=20=ED=81=B4=EB=A6=B0=EC=BD=94?= =?UTF-8?q?=EB=93=9C=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=20=EC=A7=84=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Modal/domain/AddTodoList.tsx | 53 ++++++++++++--- .../Modal/domain/ChangePassword.tsx | 46 +++++++++---- src/components/Modal/domain/LogoutModal.tsx | 7 +- .../Modal/domain/MemberInvite.module.css | 4 +- src/components/Modal/domain/MemberInvite.tsx | 68 ++++++++++++------- .../Modal/domain/ProfileModal.module.css | 10 ++- src/components/Modal/domain/ProfileModal.tsx | 43 ++++++------ src/components/Modal/domain/ResetPassword.tsx | 51 ++++++++++---- src/components/Modal/domain/WarningModal.tsx | 7 +- src/components/Modal/domain/types.ts | 6 ++ 10 files changed, 198 insertions(+), 97 deletions(-) create mode 100644 src/components/Modal/domain/types.ts diff --git a/src/components/Modal/domain/AddTodoList.tsx b/src/components/Modal/domain/AddTodoList.tsx index 99ed38a..269e9f8 100644 --- a/src/components/Modal/domain/AddTodoList.tsx +++ b/src/components/Modal/domain/AddTodoList.tsx @@ -3,22 +3,42 @@ import Image from 'next/image'; import type { FormEvent } from 'react'; import { Input } from '@/components/input'; +import type { InputProps } from '@/components/input/types/types'; import Modal from '../Modal'; import styles from './AddTodoList.module.css'; import xMarkBig from '@/assets/icons/xMark/xMarkBig.svg'; +import type { BaseDomainModalProps } from './types'; const TITLE_ID = 'add-todo-list-title'; +const CLOSE_BUTTON_ARIA_LABEL = '닫기'; +const DEFAULT_TITLE = '할 일 목록'; +const DEFAULT_PLACEHOLDER = '할 일을 입력하세요'; +const DEFAULT_SUBMIT_LABEL = '만들기'; -export interface AddTodoListProps { - isOpen: boolean; - onClose: () => void; - onCreate: () => void; +type TodoInputProps = Omit; + +export interface AddTodoListProps extends BaseDomainModalProps { + onSubmit: () => void; + title?: string; + submitLabel?: string; + inputPlaceholder?: string; + inputProps?: TodoInputProps; } -export default function AddTodoList({ isOpen, onClose, onCreate }: AddTodoListProps) { +export default function AddTodoList({ + isOpen, + onClose, + onSubmit, + title = DEFAULT_TITLE, + submitLabel = DEFAULT_SUBMIT_LABEL, + inputPlaceholder = DEFAULT_PLACEHOLDER, + inputProps, + closeOnOverlayClick = true, + closeOnEscape = true, +}: AddTodoListProps) { const handleSubmit = (e: FormEvent) => { e.preventDefault(); - onCreate(); + onSubmit(); }; return ( @@ -27,22 +47,35 @@ export default function AddTodoList({ isOpen, onClose, onCreate }: AddTodoListPr onClose={onClose} ariaLabelledby={TITLE_ID} contentClassName={styles.modalContent} + closeOnOverlayClick={closeOnOverlayClick} + closeOnEscape={closeOnEscape} >

- 할 일 목록 + {title}

-
- +
diff --git a/src/components/Modal/domain/ChangePassword.tsx b/src/components/Modal/domain/ChangePassword.tsx index 8481651..e83d1e5 100644 --- a/src/components/Modal/domain/ChangePassword.tsx +++ b/src/components/Modal/domain/ChangePassword.tsx @@ -7,22 +7,33 @@ import Modal from '../Modal'; import { Input } from '@/components/input'; import type { InputProps } from '@/components/input/types/types'; import styles from './ChangePassword.module.css'; +import type { BaseDomainModalProps } from './types'; const TITLE_ID = 'change-password-title'; const NEW_PASSWORD_NAME = 'newPassword'; const CONFIRM_PASSWORD_NAME = 'confirmPassword'; -const NEW_PASSWORD_PLACEHOLDER = '새 비밀번호를 입력해주세요.'; -const CONFIRM_PASSWORD_PLACEHOLDER = '새 비밀번호를 다시 한 번 입력해주세요.'; +const DEFAULT_TITLE = '비밀번호 변경하기'; +const DEFAULT_NEW_PASSWORD_LABEL = '새 비밀번호'; +const DEFAULT_CONFIRM_PASSWORD_LABEL = '새 비밀번호 확인'; +const DEFAULT_NEW_PASSWORD_PLACEHOLDER = '새 비밀번호를 입력해주세요.'; +const DEFAULT_CONFIRM_PASSWORD_PLACEHOLDER = '새 비밀번호를 다시 한 번 입력해주세요.'; +const DEFAULT_CLOSE_LABEL = '닫기'; +const DEFAULT_SUBMIT_LABEL = '변경하기'; type PasswordInputFieldProps = Omit< InputProps, 'className' | 'type' | 'name' | 'autoComplete' | 'placeholder' >; -export interface ChangePasswordProps { - isOpen: boolean; - onClose: () => void; +export interface ChangePasswordProps extends BaseDomainModalProps { onSubmit: () => void; + title?: string; + newPasswordLabel?: string; + confirmPasswordLabel?: string; + newPasswordPlaceholder?: string; + confirmPasswordPlaceholder?: string; + closeLabel?: string; + submitLabel?: string; newPasswordInputProps?: PasswordInputFieldProps; confirmPasswordInputProps?: PasswordInputFieldProps; } @@ -31,8 +42,17 @@ export default function ChangePassword({ isOpen, onClose, onSubmit, + title = DEFAULT_TITLE, + newPasswordLabel = DEFAULT_NEW_PASSWORD_LABEL, + confirmPasswordLabel = DEFAULT_CONFIRM_PASSWORD_LABEL, + newPasswordPlaceholder = DEFAULT_NEW_PASSWORD_PLACEHOLDER, + confirmPasswordPlaceholder = DEFAULT_CONFIRM_PASSWORD_PLACEHOLDER, + closeLabel = DEFAULT_CLOSE_LABEL, + submitLabel = DEFAULT_SUBMIT_LABEL, newPasswordInputProps, confirmPasswordInputProps, + closeOnOverlayClick = true, + closeOnEscape = true, }: ChangePasswordProps) { const generatedNewPasswordId = useId(); const generatedConfirmPasswordId = useId(); @@ -50,16 +70,18 @@ export default function ChangePassword({ onClose={onClose} ariaLabelledby={TITLE_ID} contentClassName={styles.modalContent} + closeOnOverlayClick={closeOnOverlayClick} + closeOnEscape={closeOnEscape} >

- 비밀번호 변경하기 + {title}

diff --git a/src/components/Modal/domain/LogoutModal.tsx b/src/components/Modal/domain/LogoutModal.tsx index bcc9368..db1e326 100644 --- a/src/components/Modal/domain/LogoutModal.tsx +++ b/src/components/Modal/domain/LogoutModal.tsx @@ -2,21 +2,18 @@ import Modal from '../Modal'; import styles from './LogoutModal.module.css'; +import type { BaseDomainModalProps } from './types'; const TITLE_ID = 'logout-modal-title'; const DEFAULT_TITLE = '로그아웃 하시겠어요?'; const DEFAULT_CLOSE_LABEL = '닫기'; const DEFAULT_CONFIRM_LABEL = '로그아웃'; -export interface LogoutModalProps { - isOpen: boolean; - onClose: () => void; +export interface LogoutModalProps extends BaseDomainModalProps { onConfirm: () => void; title?: string; closeLabel?: string; confirmLabel?: string; - closeOnOverlayClick?: boolean; - closeOnEscape?: boolean; } export default function LogoutModal({ diff --git a/src/components/Modal/domain/MemberInvite.module.css b/src/components/Modal/domain/MemberInvite.module.css index a196476..362a49a 100644 --- a/src/components/Modal/domain/MemberInvite.module.css +++ b/src/components/Modal/domain/MemberInvite.module.css @@ -12,7 +12,7 @@ text-align: center; } -.modalContent { +.modalContent.modalContent { border-radius: 24px; } @@ -74,7 +74,7 @@ } @media (max-width: 480px) { - .modalContent { + .modalContent.modalContent { border-radius: 24px 24px 0 0; } diff --git a/src/components/Modal/domain/MemberInvite.tsx b/src/components/Modal/domain/MemberInvite.tsx index 82f3e66..51332e2 100644 --- a/src/components/Modal/domain/MemberInvite.tsx +++ b/src/components/Modal/domain/MemberInvite.tsx @@ -4,45 +4,65 @@ import Image from 'next/image'; import Modal from '../Modal'; import styles from './MemberInvite.module.css'; import xMarkBig from '@/assets/icons/xMark/xMarkBig.svg'; +import type { BaseDomainModalProps } from './types'; -export type MemberInviteProps = { - isOpen: boolean; - onClose: () => void; +const TITLE_ID = 'member-invite-title'; +const DESCRIPTION_ID = 'member-invite-description'; +const CLOSE_BUTTON_ARIA_LABEL = '닫기'; +const DEFAULT_TITLE = '멤버 초대'; +const DEFAULT_DESCRIPTION = '그룹에 참여할 수 있는 링크를 복사합니다.'; +const DEFAULT_COPY_LABEL = '링크 복사하기'; + +export interface MemberInviteProps extends BaseDomainModalProps { inviteLink: string; - onCopy?: (link: string) => void; -}; + onCopyLink?: (link: string) => void; + title?: string; + description?: string; + copyButtonLabel?: string; +} -export default function MemberInvite({ isOpen, onClose, inviteLink, onCopy }: MemberInviteProps) { - const handleCopy = async () => { - try { - await navigator.clipboard.writeText(inviteLink); - } finally { - onCopy?.(inviteLink); - } - }; +export default function MemberInvite({ + isOpen, + onClose, + inviteLink, + onCopyLink, + title = DEFAULT_TITLE, + description = DEFAULT_DESCRIPTION, + copyButtonLabel = DEFAULT_COPY_LABEL, + closeOnOverlayClick = true, + closeOnEscape = true, +}: MemberInviteProps) { + const handleCopy = () => onCopyLink?.(inviteLink); return ( -
- -

- 멤버 초대 +

+ {title}

-

- 그룹에 참여할 수 있는 링크를 복사합니다. +

+ {description}

-
+
); } diff --git a/src/components/Modal/domain/ProfileModal.module.css b/src/components/Modal/domain/ProfileModal.module.css index 6e104f3..4452dfb 100644 --- a/src/components/Modal/domain/ProfileModal.module.css +++ b/src/components/Modal/domain/ProfileModal.module.css @@ -17,14 +17,18 @@ width: 100%; height: 100%; display: flex; - flex-direction: column; justify-content: center; align-items: center; - gap: 8px; box-sizing: border-box; } -.container > :not(.closeButton) { +.content { + display: flex; + width: 100%; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 8px; transform: translateY(12px); } diff --git a/src/components/Modal/domain/ProfileModal.tsx b/src/components/Modal/domain/ProfileModal.tsx index 518ef6d..9dba11f 100644 --- a/src/components/Modal/domain/ProfileModal.tsx +++ b/src/components/Modal/domain/ProfileModal.tsx @@ -7,6 +7,7 @@ import Modal from '../Modal'; import styles from './ProfileModal.module.css'; import profileFallback from '@/assets/icons/img/img.svg'; import xMarkBig from '@/assets/icons/xMark/xMarkBig.svg'; +import type { BaseDomainModalProps } from './types'; const TITLE_ID = 'profile-modal-title'; const EMAIL_ID = 'profile-modal-email'; @@ -14,17 +15,13 @@ const CLOSE_BUTTON_ARIA_LABEL = '닫기'; const DEFAULT_COPY_LABEL = '이메일 복사하기'; const DEFAULT_PROFILE_ALT = '프로필 이미지'; -export interface ProfileModalProps { - isOpen: boolean; - onClose: () => void; +export interface ProfileModalProps extends BaseDomainModalProps { onCopyEmail: () => void; title: string; email: string; profileImageSrc?: ImageProps['src']; profileImageAlt?: string; copyButtonLabel?: string; - closeOnOverlayClick?: boolean; - closeOnEscape?: boolean; } export default function ProfileModal({ @@ -59,25 +56,27 @@ export default function ProfileModal({ -
- {profileImageAlt} -
+
+
+ {profileImageAlt} +
-

- {title} -

-

- {email} -

+

+ {title} +

+

+ {email} +

- + +
); diff --git a/src/components/Modal/domain/ResetPassword.tsx b/src/components/Modal/domain/ResetPassword.tsx index 94f8afa..e21d1cb 100644 --- a/src/components/Modal/domain/ResetPassword.tsx +++ b/src/components/Modal/domain/ResetPassword.tsx @@ -6,15 +6,29 @@ import Modal from '../Modal'; import { Input } from '@/components/input'; import type { InputProps } from '@/components/input/types/types'; import styles from './ResetPassword.module.css'; +import type { BaseDomainModalProps } from './types'; const TITLE_ID = 'reset-password-title'; const DESCRIPTION_ID = 'reset-password-description'; +const DEFAULT_TITLE = '비밀번호 재설정'; +const DEFAULT_DESCRIPTION = '비밀번호 재설정 링크를 보내드립니다.'; +const DEFAULT_CLOSE_LABEL = '닫기'; +const DEFAULT_SUBMIT_LABEL = '링크 보내기'; +const DEFAULT_EMAIL_PLACEHOLDER = '이메일을 입력하세요'; -export interface ResetPasswordProps { - isOpen: boolean; - onClose: () => void; - onSendLink: () => void; - emailInputProps?: Omit; +type EmailInputFieldProps = Omit< + InputProps, + 'className' | 'type' | 'name' | 'autoComplete' | 'placeholder' +>; + +export interface ResetPasswordProps extends BaseDomainModalProps { + onSubmit: () => void; + title?: string; + description?: string; + closeLabel?: string; + submitLabel?: string; + emailPlaceholder?: string; + emailInputProps?: EmailInputFieldProps; } /** @@ -22,18 +36,25 @@ export interface ResetPasswordProps { * * @param props.isOpen 모달을 열지 여부 * @param props.onClose 모달을 닫을 때 호출 (오버레이 클릭/Escape 포함) - * @param props.onSendLink "링크 보내기" 제출 시 호출되는 콜백 + * @param props.onSubmit "링크 보내기" 제출 시 호출되는 콜백 * @param props.emailInputProps 이메일 Input에 그대로 전달할 props (예: `value`, `onChange`, `isError`, `errorMessage`) */ export default function ResetPassword({ isOpen, onClose, - onSendLink, + onSubmit, + title = DEFAULT_TITLE, + description = DEFAULT_DESCRIPTION, + closeLabel = DEFAULT_CLOSE_LABEL, + submitLabel = DEFAULT_SUBMIT_LABEL, + emailPlaceholder = DEFAULT_EMAIL_PLACEHOLDER, emailInputProps, + closeOnOverlayClick = true, + closeOnEscape = true, }: ResetPasswordProps) { const handleSubmit = (e: FormEvent) => { e.preventDefault(); - onSendLink(); + onSubmit(); }; return ( @@ -43,33 +64,35 @@ export default function ResetPassword({ ariaLabelledby={TITLE_ID} ariaDescribedby={DESCRIPTION_ID} contentClassName={styles.modalContent} + closeOnOverlayClick={closeOnOverlayClick} + closeOnEscape={closeOnEscape} >

- 비밀번호 재설정 + {title}

- 비밀번호 재설정 링크를 보내드립니다. + {description}

diff --git a/src/components/Modal/domain/WarningModal.tsx b/src/components/Modal/domain/WarningModal.tsx index b344e4e..69bb041 100644 --- a/src/components/Modal/domain/WarningModal.tsx +++ b/src/components/Modal/domain/WarningModal.tsx @@ -5,6 +5,7 @@ import Image from 'next/image'; import Modal from '../Modal'; import styles from './WarningModal.module.css'; import alertSmall from '@/assets/icons/alert/alertSmall.svg'; +import type { BaseDomainModalProps } from './types'; const TITLE_ID = 'warning-modal-title'; const DESCRIPTION_ID = 'warning-modal-description'; @@ -13,16 +14,12 @@ const DEFAULT_DESCRIPTION = '그룹장으로 있는 그룹은 자동으로 삭 const DEFAULT_CLOSE_LABEL = '닫기'; const DEFAULT_CONFIRM_LABEL = '회원 탈퇴'; -export interface WarningModalProps { - isOpen: boolean; - onClose: () => void; +export interface WarningModalProps extends BaseDomainModalProps { onConfirm: () => void; title?: string; description?: string; closeLabel?: string; confirmLabel?: string; - closeOnOverlayClick?: boolean; - closeOnEscape?: boolean; } export default function WarningModal({ diff --git a/src/components/Modal/domain/types.ts b/src/components/Modal/domain/types.ts new file mode 100644 index 0000000..6648209 --- /dev/null +++ b/src/components/Modal/domain/types.ts @@ -0,0 +1,6 @@ +export interface BaseDomainModalProps { + isOpen: boolean; + onClose: () => void; + closeOnOverlayClick?: boolean; + closeOnEscape?: boolean; +} From 4c1c7de3a12ca55d974de5ac6f389ba2b299d9fd Mon Sep 17 00:00:00 2001 From: jieunsse Date: Sat, 7 Feb 2026 19:27:40 +0900 Subject: [PATCH 10/15] =?UTF-8?q?style:=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=8B=9C=ED=94=84=ED=8A=B8=20=ED=98=84=EC=83=81=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modal/domain/AddTodoList.module.css | 103 ++++++++++-------- src/components/Modal/domain/AddTodoList.tsx | 10 +- 2 files changed, 62 insertions(+), 51 deletions(-) diff --git a/src/components/Modal/domain/AddTodoList.module.css b/src/components/Modal/domain/AddTodoList.module.css index 5d1d989..d2afc36 100644 --- a/src/components/Modal/domain/AddTodoList.module.css +++ b/src/components/Modal/domain/AddTodoList.module.css @@ -1,51 +1,83 @@ -.container { - --dialog-max-width-mobile: 375px; - --dialog-padding: 16px 16px 32px; - --dialog-gap: 10px; - --control-height: 48px; - --close-button-size: 24px; - --input-max-width: 288px; - --button-max-width: 280px; +.modalContent.modalContent { + width: 384px; + height: 235px; + display: inline-flex; + padding: 16px 16px 32px; + flex-direction: column; + align-items: flex-start; + gap: 10px; + border-radius: 24px; + background: var(--Background-Primary, #fff); + box-sizing: border-box; +} +.container { + width: 100%; + height: 100%; display: flex; - padding: var(--dialog-padding); flex-direction: column; - gap: var(--dialog-gap); align-items: stretch; + gap: 10px; box-sizing: border-box; } +.buttonContainer { + display: flex; + align-items: center; + justify-content: flex-end; + margin-right: 8px; +} + .header { display: flex; + width: 100%; align-items: center; - justify-content: space-between; + justify-content: center; gap: 12px; } .title { + margin: 0; color: var(--Text-Primary, #1e293b); font-family: Pretendard; font-size: 16px; font-style: normal; font-weight: 500; line-height: 19px; - margin: 0; +} + +.closeButton { + width: 24px; + height: 24px; + padding: 0; + border: none; + background: transparent; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; } .form { display: flex; + flex: 1; + min-height: 0; flex-direction: column; gap: 24px; } .input.input { - padding: 14px 16px; - width: 100%; - max-width: var(--input-max-width); - height: var(--control-height); + display: flex; + width: 280px; + height: 48px; + padding: 16px; + align-items: center; + gap: 10px; + border-radius: 12px; + border: 1px solid var(--Border-Primary, #e2e8f0); + background: var(--Background-Primary, #fff); box-sizing: border-box; - align-self: center; - text-align: center; + margin: 0 auto; } .input.input::placeholder { @@ -60,21 +92,21 @@ .footer { display: flex; justify-content: center; + margin-top: auto; } .button { display: flex; - width: 100%; - max-width: var(--button-max-width); - height: var(--control-height); + width: 280px; + height: 48px; justify-content: center; align-items: center; gap: 10px; flex-shrink: 0; + border: none; border-radius: 12px; background: var(--Color-Brand-Primary, #5189fa); - border: none; - color: var(--color-text-inverse, #ffffff); + color: var(--Text-inverse, #fff); text-align: center; font-family: Pretendard; font-size: 16px; @@ -83,31 +115,8 @@ line-height: 19px; } -.closeButton { - width: var(--close-button-size); - height: var(--close-button-size); - padding: 0; - border: none; - background: transparent; - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; -} - -.modalContent.modalContent { - --dialog-radius-desktop: 24px; - --dialog-radius-mobile: 24px 24px 0 0; - border-radius: var(--dialog-radius-desktop); -} - @media (max-width: 480px) { - .container { - width: 100%; - max-width: var(--dialog-max-width-mobile); - } - .modalContent.modalContent { - border-radius: var(--dialog-radius-mobile); + border-radius: 24px 24px 0 0; } } diff --git a/src/components/Modal/domain/AddTodoList.tsx b/src/components/Modal/domain/AddTodoList.tsx index 269e9f8..c3cd1cc 100644 --- a/src/components/Modal/domain/AddTodoList.tsx +++ b/src/components/Modal/domain/AddTodoList.tsx @@ -51,10 +51,7 @@ export default function AddTodoList({ closeOnEscape={closeOnEscape} >
-
-

- {title} -

+
+
+
+

+ {title} +

From 72631e01ec967efeb12a51a8d832a3839ad64293 Mon Sep 17 00:00:00 2001 From: jieunsse Date: Sat, 7 Feb 2026 19:31:01 +0900 Subject: [PATCH 11/15] =?UTF-8?q?chore:=20=EB=88=84=EB=9D=BD=EB=90=9C=20?= =?UTF-8?q?=ED=94=84=EB=A1=AD=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Modal/Modal.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index b31070a..8c9c2b3 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -24,6 +24,7 @@ export default function Modal({ ariaLabelledby, ariaDescribedby, className, + contentClassName, closeOnOverlayClick = true, closeOnEscape = true, }: ModalProps) { @@ -55,7 +56,7 @@ export default function Modal({ role="presentation" >
Date: Sat, 7 Feb 2026 19:40:42 +0900 Subject: [PATCH 12/15] =?UTF-8?q?refactor:=20=EA=B3=B5=EC=9A=A9=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modal/domain/AddTodoList.module.css | 8 ++++++++ src/components/Modal/domain/AddTodoList.tsx | 9 +++++---- .../Modal/domain/ChangePassword.tsx | 14 ++++++++++---- .../Modal/domain/LogoutModal.module.css | 8 ++++++++ src/components/Modal/domain/LogoutModal.tsx | 19 +++++++++++++++---- .../Modal/domain/MemberInvite.module.css | 8 ++++++++ src/components/Modal/domain/MemberInvite.tsx | 14 ++++++++++---- .../Modal/domain/ProfileModal.module.css | 8 ++++++++ src/components/Modal/domain/ProfileModal.tsx | 14 ++++++++++---- src/components/Modal/domain/ResetPassword.tsx | 14 ++++++++++---- .../Modal/domain/WarningModal.module.css | 8 ++++++++ src/components/Modal/domain/WarningModal.tsx | 19 +++++++++++++++---- 12 files changed, 115 insertions(+), 28 deletions(-) diff --git a/src/components/Modal/domain/AddTodoList.module.css b/src/components/Modal/domain/AddTodoList.module.css index d2afc36..5d76e96 100644 --- a/src/components/Modal/domain/AddTodoList.module.css +++ b/src/components/Modal/domain/AddTodoList.module.css @@ -58,6 +58,14 @@ cursor: pointer; } +.closeButton.closeButton, +.closeButton.closeButton:hover:not(:disabled), +.closeButton.closeButton:active:not(:disabled) { + border: none; + background: transparent; + color: inherit; +} + .form { display: flex; flex: 1; diff --git a/src/components/Modal/domain/AddTodoList.tsx b/src/components/Modal/domain/AddTodoList.tsx index c3cd1cc..456a813 100644 --- a/src/components/Modal/domain/AddTodoList.tsx +++ b/src/components/Modal/domain/AddTodoList.tsx @@ -2,6 +2,7 @@ import Image from 'next/image'; import type { FormEvent } from 'react'; +import BaseButton from '@/components/Button/base/BaseButton'; import { Input } from '@/components/input'; import type { InputProps } from '@/components/input/types/types'; import Modal from '../Modal'; @@ -52,14 +53,14 @@ export default function AddTodoList({ >
- +

@@ -76,9 +77,9 @@ export default function AddTodoList({ placeholder={inputPlaceholder} />
- +

diff --git a/src/components/Modal/domain/ChangePassword.tsx b/src/components/Modal/domain/ChangePassword.tsx index e83d1e5..2e6fae5 100644 --- a/src/components/Modal/domain/ChangePassword.tsx +++ b/src/components/Modal/domain/ChangePassword.tsx @@ -4,6 +4,7 @@ import type { FormEvent } from 'react'; import { useId } from 'react'; import Modal from '../Modal'; +import BaseButton from '@/components/Button/base/BaseButton'; import { Input } from '@/components/input'; import type { InputProps } from '@/components/input/types/types'; import styles from './ChangePassword.module.css'; @@ -110,12 +111,17 @@ export default function ChangePassword({
- - +
diff --git a/src/components/Modal/domain/LogoutModal.module.css b/src/components/Modal/domain/LogoutModal.module.css index eb52a63..32d3376 100644 --- a/src/components/Modal/domain/LogoutModal.module.css +++ b/src/components/Modal/domain/LogoutModal.module.css @@ -61,6 +61,14 @@ cursor: pointer; } +.closeButton.closeButton, +.closeButton.closeButton:hover:not(:disabled), +.closeButton.closeButton:active:not(:disabled) { + border: 1px solid var(--Border-Secondary, #cbd5e1); + background: var(--Background-Primary, #fff); + color: var(--Text-Default, #64748b); +} + .confirmButton { display: flex; width: 136px; diff --git a/src/components/Modal/domain/LogoutModal.tsx b/src/components/Modal/domain/LogoutModal.tsx index db1e326..9572b45 100644 --- a/src/components/Modal/domain/LogoutModal.tsx +++ b/src/components/Modal/domain/LogoutModal.tsx @@ -1,6 +1,7 @@ 'use client'; import Modal from '../Modal'; +import BaseButton from '@/components/Button/base/BaseButton'; import styles from './LogoutModal.module.css'; import type { BaseDomainModalProps } from './types'; @@ -41,12 +42,22 @@ export default function LogoutModal({
- - +
diff --git a/src/components/Modal/domain/MemberInvite.module.css b/src/components/Modal/domain/MemberInvite.module.css index 362a49a..998f9de 100644 --- a/src/components/Modal/domain/MemberInvite.module.css +++ b/src/components/Modal/domain/MemberInvite.module.css @@ -31,6 +31,14 @@ cursor: pointer; } +.closeButton.closeButton, +.closeButton.closeButton:hover:not(:disabled), +.closeButton.closeButton:active:not(:disabled) { + border: none; + background: transparent; + color: inherit; +} + .title { margin: 8px 0 0; color: var(--Text-Primary, #1e293b); diff --git a/src/components/Modal/domain/MemberInvite.tsx b/src/components/Modal/domain/MemberInvite.tsx index 51332e2..a494cbd 100644 --- a/src/components/Modal/domain/MemberInvite.tsx +++ b/src/components/Modal/domain/MemberInvite.tsx @@ -3,6 +3,7 @@ import Image from 'next/image'; import Modal from '../Modal'; import styles from './MemberInvite.module.css'; +import BaseButton from '@/components/Button/base/BaseButton'; import xMarkBig from '@/assets/icons/xMark/xMarkBig.svg'; import type { BaseDomainModalProps } from './types'; @@ -45,23 +46,28 @@ export default function MemberInvite({ closeOnEscape={closeOnEscape} >
- +

{title}

{description}

- +
); diff --git a/src/components/Modal/domain/ProfileModal.module.css b/src/components/Modal/domain/ProfileModal.module.css index 4452dfb..1e14e15 100644 --- a/src/components/Modal/domain/ProfileModal.module.css +++ b/src/components/Modal/domain/ProfileModal.module.css @@ -47,6 +47,14 @@ cursor: pointer; } +.closeButton.closeButton, +.closeButton.closeButton:hover:not(:disabled), +.closeButton.closeButton:active:not(:disabled) { + border: none; + background: transparent; + color: inherit; +} + .profileImage { display: flex; width: 40px; diff --git a/src/components/Modal/domain/ProfileModal.tsx b/src/components/Modal/domain/ProfileModal.tsx index 9dba11f..e6b762f 100644 --- a/src/components/Modal/domain/ProfileModal.tsx +++ b/src/components/Modal/domain/ProfileModal.tsx @@ -5,6 +5,7 @@ import Image from 'next/image'; import Modal from '../Modal'; import styles from './ProfileModal.module.css'; +import BaseButton from '@/components/Button/base/BaseButton'; import profileFallback from '@/assets/icons/img/img.svg'; import xMarkBig from '@/assets/icons/xMark/xMarkBig.svg'; import type { BaseDomainModalProps } from './types'; @@ -47,14 +48,14 @@ export default function ProfileModal({ closeOnEscape={closeOnEscape} >
- +
@@ -73,9 +74,14 @@ export default function ProfileModal({ {email}

- +
diff --git a/src/components/Modal/domain/ResetPassword.tsx b/src/components/Modal/domain/ResetPassword.tsx index e21d1cb..ab59b87 100644 --- a/src/components/Modal/domain/ResetPassword.tsx +++ b/src/components/Modal/domain/ResetPassword.tsx @@ -3,6 +3,7 @@ import type { FormEvent } from 'react'; import Modal from '../Modal'; +import BaseButton from '@/components/Button/base/BaseButton'; import { Input } from '@/components/input'; import type { InputProps } from '@/components/input/types/types'; import styles from './ResetPassword.module.css'; @@ -88,12 +89,17 @@ export default function ResetPassword({ />
- - +
diff --git a/src/components/Modal/domain/WarningModal.module.css b/src/components/Modal/domain/WarningModal.module.css index c2ea796..ba6c291 100644 --- a/src/components/Modal/domain/WarningModal.module.css +++ b/src/components/Modal/domain/WarningModal.module.css @@ -82,6 +82,14 @@ cursor: pointer; } +.closeButton.closeButton, +.closeButton.closeButton:hover:not(:disabled), +.closeButton.closeButton:active:not(:disabled) { + border: 1px solid var(--Border-Secondary, #cbd5e1); + background: var(--Background-Primary, #fff); + color: var(--Text-Default, #64748b); +} + .confirmButton { display: flex; width: 136px; diff --git a/src/components/Modal/domain/WarningModal.tsx b/src/components/Modal/domain/WarningModal.tsx index 69bb041..788ebfd 100644 --- a/src/components/Modal/domain/WarningModal.tsx +++ b/src/components/Modal/domain/WarningModal.tsx @@ -3,6 +3,7 @@ import Image from 'next/image'; import Modal from '../Modal'; +import BaseButton from '@/components/Button/base/BaseButton'; import styles from './WarningModal.module.css'; import alertSmall from '@/assets/icons/alert/alertSmall.svg'; import type { BaseDomainModalProps } from './types'; @@ -63,12 +64,22 @@ export default function WarningModal({

- - +
From e10caa2a829e13853b7c902b5f60e1f64a10eeb6 Mon Sep 17 00:00:00 2001 From: jieunsse Date: Sat, 7 Feb 2026 19:54:15 +0900 Subject: [PATCH 13/15] =?UTF-8?q?chore:=20jsDoc=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=20=EA=B2=BD=EB=A1=9C=20=EC=84=B8=EB=B6=84?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AddTodoList}/AddTodoList.module.css | 0 .../AddTodoList}/AddTodoList.tsx | 43 +++++++++---- .../ChangePassword}/ChangePassword.module.css | 0 .../ChangePassword}/ChangePassword.tsx | 61 ++++++++++++------- .../LogoutModal}/LogoutModal.module.css | 0 .../LogoutModal}/LogoutModal.tsx | 32 +++++++--- .../MemberInvite}/MemberInvite.module.css | 0 .../MemberInvite}/MemberInvite.tsx | 43 +++++++++---- .../ProfileModal}/ProfileModal.module.css | 0 .../ProfileModal}/ProfileModal.tsx | 43 ++++++++----- .../ResetPassword}/ResetPassword.module.css | 0 .../ResetPassword}/ResetPassword.tsx | 51 ++++++++++------ .../WarningModal}/WarningModal.module.css | 0 .../WarningModal}/WarningModal.tsx | 34 ++++++++--- src/components/Modal/domain/types.ts | 6 -- src/components/Modal/domain/types/types.ts | 10 +++ 16 files changed, 216 insertions(+), 107 deletions(-) rename src/components/Modal/domain/{ => components/AddTodoList}/AddTodoList.module.css (100%) rename src/components/Modal/domain/{ => components/AddTodoList}/AddTodoList.tsx (65%) rename src/components/Modal/domain/{ => components/ChangePassword}/ChangePassword.module.css (100%) rename src/components/Modal/domain/{ => components/ChangePassword}/ChangePassword.tsx (64%) rename src/components/Modal/domain/{ => components/LogoutModal}/LogoutModal.module.css (100%) rename src/components/Modal/domain/{ => components/LogoutModal}/LogoutModal.tsx (60%) rename src/components/Modal/domain/{ => components/MemberInvite}/MemberInvite.module.css (100%) rename src/components/Modal/domain/{ => components/MemberInvite}/MemberInvite.tsx (61%) rename src/components/Modal/domain/{ => components/ProfileModal}/ProfileModal.module.css (100%) rename src/components/Modal/domain/{ => components/ProfileModal}/ProfileModal.tsx (64%) rename src/components/Modal/domain/{ => components/ResetPassword}/ResetPassword.module.css (100%) rename src/components/Modal/domain/{ => components/ResetPassword}/ResetPassword.tsx (65%) rename src/components/Modal/domain/{ => components/WarningModal}/WarningModal.module.css (100%) rename src/components/Modal/domain/{ => components/WarningModal}/WarningModal.tsx (67%) delete mode 100644 src/components/Modal/domain/types.ts create mode 100644 src/components/Modal/domain/types/types.ts diff --git a/src/components/Modal/domain/AddTodoList.module.css b/src/components/Modal/domain/components/AddTodoList/AddTodoList.module.css similarity index 100% rename from src/components/Modal/domain/AddTodoList.module.css rename to src/components/Modal/domain/components/AddTodoList/AddTodoList.module.css diff --git a/src/components/Modal/domain/AddTodoList.tsx b/src/components/Modal/domain/components/AddTodoList/AddTodoList.tsx similarity index 65% rename from src/components/Modal/domain/AddTodoList.tsx rename to src/components/Modal/domain/components/AddTodoList/AddTodoList.tsx index 456a813..9bf8035 100644 --- a/src/components/Modal/domain/AddTodoList.tsx +++ b/src/components/Modal/domain/components/AddTodoList/AddTodoList.tsx @@ -5,10 +5,10 @@ import type { FormEvent } from 'react'; import BaseButton from '@/components/Button/base/BaseButton'; import { Input } from '@/components/input'; import type { InputProps } from '@/components/input/types/types'; -import Modal from '../Modal'; +import Modal from '../../../Modal'; import styles from './AddTodoList.module.css'; import xMarkBig from '@/assets/icons/xMark/xMarkBig.svg'; -import type { BaseDomainModalProps } from './types'; +import type { BaseDomainModalProps } from '../../types/types'; const TITLE_ID = 'add-todo-list-title'; const CLOSE_BUTTON_ARIA_LABEL = '닫기'; @@ -18,25 +18,44 @@ const DEFAULT_SUBMIT_LABEL = '만들기'; type TodoInputProps = Omit; -export interface AddTodoListProps extends BaseDomainModalProps { - onSubmit: () => void; +interface AddTodoListTextOptions { title?: string; submitLabel?: string; inputPlaceholder?: string; - inputProps?: TodoInputProps; } +interface AddTodoListInputOptions { + props?: TodoInputProps; +} + +export interface AddTodoListProps extends BaseDomainModalProps { + onSubmit: () => void; + text?: AddTodoListTextOptions; + input?: AddTodoListInputOptions; +} + +/** + * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. + * @param props.onClose 모달을 닫을 때 실행할 함수를 전달합니다. + * @param props.onSubmit 할 일 생성 버튼 클릭 시 실행할 함수를 전달합니다. + * @param props.text 모달 제목과 버튼 문구 같은 텍스트 옵션을 객체로 전달합니다. + * @param props.input 할 일 입력창에 적용할 옵션을 객체로 전달합니다. + * @param props.closeOptions 오버레이 클릭과 Escape 닫힘 옵션을 객체로 전달합니다. + */ export default function AddTodoList({ isOpen, onClose, onSubmit, - title = DEFAULT_TITLE, - submitLabel = DEFAULT_SUBMIT_LABEL, - inputPlaceholder = DEFAULT_PLACEHOLDER, - inputProps, - closeOnOverlayClick = true, - closeOnEscape = true, + text, + input, + closeOptions, }: AddTodoListProps) { + const title = text?.title ?? DEFAULT_TITLE; + const submitLabel = text?.submitLabel ?? DEFAULT_SUBMIT_LABEL; + const inputPlaceholder = text?.inputPlaceholder ?? DEFAULT_PLACEHOLDER; + const closeOnOverlayClick = closeOptions?.overlayClick ?? true; + const closeOnEscape = closeOptions?.escape ?? true; + const handleSubmit = (e: FormEvent) => { e.preventDefault(); onSubmit(); @@ -70,7 +89,7 @@ export default function AddTodoList({
; -export interface ChangePasswordProps extends BaseDomainModalProps { - onSubmit: () => void; +interface ChangePasswordTextOptions { title?: string; newPasswordLabel?: string; confirmPasswordLabel?: string; @@ -35,30 +34,50 @@ export interface ChangePasswordProps extends BaseDomainModalProps { confirmPasswordPlaceholder?: string; closeLabel?: string; submitLabel?: string; - newPasswordInputProps?: PasswordInputFieldProps; - confirmPasswordInputProps?: PasswordInputFieldProps; } +interface ChangePasswordInputOptions { + newPassword?: PasswordInputFieldProps; + confirmPassword?: PasswordInputFieldProps; +} + +export interface ChangePasswordProps extends BaseDomainModalProps { + onSubmit: () => void; + text?: ChangePasswordTextOptions; + input?: ChangePasswordInputOptions; +} + +/** + * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. + * @param props.onClose 모달을 닫을 때 실행할 함수를 전달합니다. + * @param props.onSubmit 비밀번호 변경 제출 시 실행할 함수를 전달합니다. + * @param props.text 제목과 버튼 문구와 라벨 같은 텍스트 옵션을 객체로 전달합니다. + * @param props.input 비밀번호 입력창들에 적용할 옵션을 객체로 전달합니다. + * @param props.closeOptions 오버레이 클릭과 Escape 닫힘 옵션을 객체로 전달합니다. + */ export default function ChangePassword({ isOpen, onClose, onSubmit, - title = DEFAULT_TITLE, - newPasswordLabel = DEFAULT_NEW_PASSWORD_LABEL, - confirmPasswordLabel = DEFAULT_CONFIRM_PASSWORD_LABEL, - newPasswordPlaceholder = DEFAULT_NEW_PASSWORD_PLACEHOLDER, - confirmPasswordPlaceholder = DEFAULT_CONFIRM_PASSWORD_PLACEHOLDER, - closeLabel = DEFAULT_CLOSE_LABEL, - submitLabel = DEFAULT_SUBMIT_LABEL, - newPasswordInputProps, - confirmPasswordInputProps, - closeOnOverlayClick = true, - closeOnEscape = true, + text, + input, + closeOptions, }: ChangePasswordProps) { + const title = text?.title ?? DEFAULT_TITLE; + const newPasswordLabel = text?.newPasswordLabel ?? DEFAULT_NEW_PASSWORD_LABEL; + const confirmPasswordLabel = text?.confirmPasswordLabel ?? DEFAULT_CONFIRM_PASSWORD_LABEL; + const newPasswordPlaceholder = text?.newPasswordPlaceholder ?? DEFAULT_NEW_PASSWORD_PLACEHOLDER; + const confirmPasswordPlaceholder = + text?.confirmPasswordPlaceholder ?? DEFAULT_CONFIRM_PASSWORD_PLACEHOLDER; + const closeLabel = text?.closeLabel ?? DEFAULT_CLOSE_LABEL; + const submitLabel = text?.submitLabel ?? DEFAULT_SUBMIT_LABEL; + const closeOnOverlayClick = closeOptions?.overlayClick ?? true; + const closeOnEscape = closeOptions?.escape ?? true; + const generatedNewPasswordId = useId(); const generatedConfirmPasswordId = useId(); - const newPasswordId = newPasswordInputProps?.id ?? generatedNewPasswordId; - const confirmPasswordId = confirmPasswordInputProps?.id ?? generatedConfirmPasswordId; + const newPasswordId = input?.newPassword?.id ?? generatedNewPasswordId; + const confirmPasswordId = input?.confirmPassword?.id ?? generatedConfirmPasswordId; const handleSubmit = (e: FormEvent) => { e.preventDefault(); @@ -85,7 +104,7 @@ export default function ChangePassword({ {newPasswordLabel} void; +interface LogoutModalTextOptions { title?: string; closeLabel?: string; confirmLabel?: string; } +export interface LogoutModalProps extends BaseDomainModalProps { + onConfirm: () => void; + text?: LogoutModalTextOptions; +} + +/** + * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. + * @param props.onClose 모달을 닫을 때 실행할 함수를 전달합니다. + * @param props.onConfirm 로그아웃 버튼 클릭 시 실행할 함수를 전달합니다. + * @param props.text 모달 제목과 버튼 문구 같은 텍스트 옵션을 객체로 전달합니다. + * @param props.closeOptions 오버레이 클릭과 Escape 닫힘 옵션을 객체로 전달합니다. + */ export default function LogoutModal({ isOpen, onClose, onConfirm, - title = DEFAULT_TITLE, - closeLabel = DEFAULT_CLOSE_LABEL, - confirmLabel = DEFAULT_CONFIRM_LABEL, - closeOnOverlayClick = true, - closeOnEscape = true, + text, + closeOptions, }: LogoutModalProps) { + const title = text?.title ?? DEFAULT_TITLE; + const closeLabel = text?.closeLabel ?? DEFAULT_CLOSE_LABEL; + const confirmLabel = text?.confirmLabel ?? DEFAULT_CONFIRM_LABEL; + const closeOnOverlayClick = closeOptions?.overlayClick ?? true; + const closeOnEscape = closeOptions?.escape ?? true; + return ( void; +interface MemberInviteTextOptions { title?: string; description?: string; copyButtonLabel?: string; } +interface MemberInviteInviteOptions { + link: string; + onCopyLink?: (link: string) => void; +} + +export interface MemberInviteProps extends BaseDomainModalProps { + invite: MemberInviteInviteOptions; + text?: MemberInviteTextOptions; +} + +/** + * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. + * @param props.onClose 모달을 닫을 때 실행할 함수를 전달합니다. + * @param props.invite 초대 링크와 복사 핸들러를 객체로 전달합니다. + * @param props.text 모달 제목과 설명과 버튼 문구를 객체로 전달합니다. + * @param props.closeOptions 오버레이 클릭과 Escape 닫힘 옵션을 객체로 전달합니다. + */ export default function MemberInvite({ isOpen, onClose, - inviteLink, - onCopyLink, - title = DEFAULT_TITLE, - description = DEFAULT_DESCRIPTION, - copyButtonLabel = DEFAULT_COPY_LABEL, - closeOnOverlayClick = true, - closeOnEscape = true, + invite, + text, + closeOptions, }: MemberInviteProps) { - const handleCopy = () => onCopyLink?.(inviteLink); + const title = text?.title ?? DEFAULT_TITLE; + const description = text?.description ?? DEFAULT_DESCRIPTION; + const copyButtonLabel = text?.copyButtonLabel ?? DEFAULT_COPY_LABEL; + const closeOnOverlayClick = closeOptions?.overlayClick ?? true; + const closeOnEscape = closeOptions?.escape ?? true; + + const handleCopy = () => invite.onCopyLink?.(invite.link); return ( void; +interface ProfileModalProfileOptions { title: string; email: string; - profileImageSrc?: ImageProps['src']; - profileImageAlt?: string; + imageSrc?: ImageProps['src']; + imageAlt?: string; copyButtonLabel?: string; } +export interface ProfileModalProps extends BaseDomainModalProps { + onCopyEmail: () => void; + profile: ProfileModalProfileOptions; +} + +/** + * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. + * @param props.onClose 모달을 닫을 때 실행할 함수를 전달합니다. + * @param props.onCopyEmail 이메일 복사 버튼 클릭 시 실행할 함수를 전달합니다. + * @param props.profile 프로필 정보와 표시 텍스트를 객체로 전달합니다. + * @param props.closeOptions 오버레이 클릭과 Escape 닫힘 옵션을 객체로 전달합니다. + */ export default function ProfileModal({ isOpen, onClose, onCopyEmail, - title, - email, - profileImageSrc, - profileImageAlt = DEFAULT_PROFILE_ALT, - copyButtonLabel = DEFAULT_COPY_LABEL, - closeOnOverlayClick = true, - closeOnEscape = true, + profile, + closeOptions, }: ProfileModalProps) { + const profileImageAlt = profile.imageAlt ?? DEFAULT_PROFILE_ALT; + const copyButtonLabel = profile.copyButtonLabel ?? DEFAULT_COPY_LABEL; + const closeOnOverlayClick = closeOptions?.overlayClick ?? true; + const closeOnEscape = closeOptions?.escape ?? true; + return (
{profileImageAlt}

- {title} + {profile.title}

- {email} + {profile.email}

; -export interface ResetPasswordProps extends BaseDomainModalProps { - onSubmit: () => void; +interface ResetPasswordTextOptions { title?: string; description?: string; closeLabel?: string; submitLabel?: string; emailPlaceholder?: string; - emailInputProps?: EmailInputFieldProps; +} + +interface ResetPasswordInputOptions { + email?: EmailInputFieldProps; +} + +export interface ResetPasswordProps extends BaseDomainModalProps { + onSubmit: () => void; + text?: ResetPasswordTextOptions; + input?: ResetPasswordInputOptions; } /** - * 비밀번호 재설정 링크를 보내기 위한 모달 UI 컴포넌트. - * - * @param props.isOpen 모달을 열지 여부 - * @param props.onClose 모달을 닫을 때 호출 (오버레이 클릭/Escape 포함) - * @param props.onSubmit "링크 보내기" 제출 시 호출되는 콜백 - * @param props.emailInputProps 이메일 Input에 그대로 전달할 props (예: `value`, `onChange`, `isError`, `errorMessage`) + * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. + * @param props.onClose 모달을 닫을 때 실행할 함수를 전달합니다. + * @param props.onSubmit 링크 보내기 제출 시 실행할 함수를 전달합니다. + * @param props.text 제목과 버튼 문구와 안내 문구 같은 텍스트 옵션을 객체로 전달합니다. + * @param props.input 이메일 입력창에 적용할 옵션을 객체로 전달합니다. + * @param props.closeOptions 오버레이 클릭과 Escape 닫힘 옵션을 객체로 전달합니다. */ export default function ResetPassword({ isOpen, onClose, onSubmit, - title = DEFAULT_TITLE, - description = DEFAULT_DESCRIPTION, - closeLabel = DEFAULT_CLOSE_LABEL, - submitLabel = DEFAULT_SUBMIT_LABEL, - emailPlaceholder = DEFAULT_EMAIL_PLACEHOLDER, - emailInputProps, - closeOnOverlayClick = true, - closeOnEscape = true, + text, + input, + closeOptions, }: ResetPasswordProps) { + const title = text?.title ?? DEFAULT_TITLE; + const description = text?.description ?? DEFAULT_DESCRIPTION; + const closeLabel = text?.closeLabel ?? DEFAULT_CLOSE_LABEL; + const submitLabel = text?.submitLabel ?? DEFAULT_SUBMIT_LABEL; + const emailPlaceholder = text?.emailPlaceholder ?? DEFAULT_EMAIL_PLACEHOLDER; + const closeOnOverlayClick = closeOptions?.overlayClick ?? true; + const closeOnEscape = closeOptions?.escape ?? true; + const handleSubmit = (e: FormEvent) => { e.preventDefault(); onSubmit(); @@ -80,7 +91,7 @@ export default function ResetPassword({ void; +interface WarningModalTextOptions { title?: string; description?: string; closeLabel?: string; confirmLabel?: string; } +export interface WarningModalProps extends BaseDomainModalProps { + onConfirm: () => void; + text?: WarningModalTextOptions; +} + +/** + * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. + * @param props.onClose 모달을 닫을 때 실행할 함수를 전달합니다. + * @param props.onConfirm 회원 탈퇴 확인 버튼 클릭 시 실행할 함수를 전달합니다. + * @param props.text 경고 모달 제목과 설명과 버튼 문구를 객체로 전달합니다. + * @param props.closeOptions 오버레이 클릭과 Escape 닫힘 옵션을 객체로 전달합니다. + */ export default function WarningModal({ isOpen, onClose, onConfirm, - title = DEFAULT_TITLE, - description = DEFAULT_DESCRIPTION, - closeLabel = DEFAULT_CLOSE_LABEL, - confirmLabel = DEFAULT_CONFIRM_LABEL, - closeOnOverlayClick = true, - closeOnEscape = true, + text, + closeOptions, }: WarningModalProps) { + const title = text?.title ?? DEFAULT_TITLE; + const description = text?.description ?? DEFAULT_DESCRIPTION; + const closeLabel = text?.closeLabel ?? DEFAULT_CLOSE_LABEL; + const confirmLabel = text?.confirmLabel ?? DEFAULT_CONFIRM_LABEL; + const closeOnOverlayClick = closeOptions?.overlayClick ?? true; + const closeOnEscape = closeOptions?.escape ?? true; + return ( void; - closeOnOverlayClick?: boolean; - closeOnEscape?: boolean; -} diff --git a/src/components/Modal/domain/types/types.ts b/src/components/Modal/domain/types/types.ts new file mode 100644 index 0000000..c79b7c8 --- /dev/null +++ b/src/components/Modal/domain/types/types.ts @@ -0,0 +1,10 @@ +export interface DomainModalCloseOptions { + overlayClick?: boolean; + escape?: boolean; +} + +export interface BaseDomainModalProps { + isOpen: boolean; + onClose: () => void; + closeOptions?: DomainModalCloseOptions; +} From 307907b41f38da7ca900852082053ba63cbc6819 Mon Sep 17 00:00:00 2001 From: jieunsse Date: Sat, 7 Feb 2026 20:12:26 +0900 Subject: [PATCH 14/15] =?UTF-8?q?refactor:=20=EB=8B=A8=EC=9D=BC=EC=B1=85?= =?UTF-8?q?=EC=9E=84=EC=9B=90=EC=B9=99=EC=97=90=20=EC=9D=98=ED=95=9C=20?= =?UTF-8?q?=EC=86=8C=EC=8A=A4=EC=BD=94=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AddTodoList/AddTodoList.constants.ts | 5 ++ .../components/AddTodoList/AddTodoList.tsx | 35 ++++-------- .../AddTodoList/AddTodoList.types.ts | 20 +++++++ .../ChangePassword.constants.ts | 10 ++++ .../ChangePassword/ChangePassword.tsx | 53 +++++-------------- .../ChangePassword/ChangePassword.types.ts | 28 ++++++++++ .../LogoutModal/LogoutModal.constants.ts | 4 ++ .../components/LogoutModal/LogoutModal.tsx | 25 +++------ .../LogoutModal/LogoutModal.types.ts | 12 +++++ .../MemberInvite/MemberInvite.constants.ts | 6 +++ .../components/MemberInvite/MemberInvite.tsx | 34 ++++-------- .../MemberInvite/MemberInvite.types.ts | 17 ++++++ .../ProfileModal/ProfileModal.constants.ts | 5 ++ .../components/ProfileModal/ProfileModal.tsx | 30 ++++------- .../ProfileModal/ProfileModal.types.ts | 15 ++++++ .../ResetPassword/ResetPassword.constants.ts | 7 +++ .../ResetPassword/ResetPassword.tsx | 44 ++++----------- .../ResetPassword/ResetPassword.types.ts | 25 +++++++++ .../WarningModal/WarningModal.constants.ts | 7 +++ .../components/WarningModal/WarningModal.tsx | 30 ++++------- .../WarningModal/WarningModal.types.ts | 13 +++++ 21 files changed, 245 insertions(+), 180 deletions(-) create mode 100644 src/components/Modal/domain/components/AddTodoList/AddTodoList.constants.ts create mode 100644 src/components/Modal/domain/components/AddTodoList/AddTodoList.types.ts create mode 100644 src/components/Modal/domain/components/ChangePassword/ChangePassword.constants.ts create mode 100644 src/components/Modal/domain/components/ChangePassword/ChangePassword.types.ts create mode 100644 src/components/Modal/domain/components/LogoutModal/LogoutModal.constants.ts create mode 100644 src/components/Modal/domain/components/LogoutModal/LogoutModal.types.ts create mode 100644 src/components/Modal/domain/components/MemberInvite/MemberInvite.constants.ts create mode 100644 src/components/Modal/domain/components/MemberInvite/MemberInvite.types.ts create mode 100644 src/components/Modal/domain/components/ProfileModal/ProfileModal.constants.ts create mode 100644 src/components/Modal/domain/components/ProfileModal/ProfileModal.types.ts create mode 100644 src/components/Modal/domain/components/ResetPassword/ResetPassword.constants.ts create mode 100644 src/components/Modal/domain/components/ResetPassword/ResetPassword.types.ts create mode 100644 src/components/Modal/domain/components/WarningModal/WarningModal.constants.ts create mode 100644 src/components/Modal/domain/components/WarningModal/WarningModal.types.ts diff --git a/src/components/Modal/domain/components/AddTodoList/AddTodoList.constants.ts b/src/components/Modal/domain/components/AddTodoList/AddTodoList.constants.ts new file mode 100644 index 0000000..fe2914f --- /dev/null +++ b/src/components/Modal/domain/components/AddTodoList/AddTodoList.constants.ts @@ -0,0 +1,5 @@ +export const TITLE_ID = 'add-todo-list-title'; +export const CLOSE_BUTTON_ARIA_LABEL = '닫기'; +export const DEFAULT_TITLE = '할 일 목록'; +export const DEFAULT_PLACEHOLDER = '할 일을 입력하세요'; +export const DEFAULT_SUBMIT_LABEL = '만들기'; diff --git a/src/components/Modal/domain/components/AddTodoList/AddTodoList.tsx b/src/components/Modal/domain/components/AddTodoList/AddTodoList.tsx index 9bf8035..934aa92 100644 --- a/src/components/Modal/domain/components/AddTodoList/AddTodoList.tsx +++ b/src/components/Modal/domain/components/AddTodoList/AddTodoList.tsx @@ -4,35 +4,18 @@ import Image from 'next/image'; import type { FormEvent } from 'react'; import BaseButton from '@/components/Button/base/BaseButton'; import { Input } from '@/components/input'; -import type { InputProps } from '@/components/input/types/types'; import Modal from '../../../Modal'; import styles from './AddTodoList.module.css'; import xMarkBig from '@/assets/icons/xMark/xMarkBig.svg'; -import type { BaseDomainModalProps } from '../../types/types'; - -const TITLE_ID = 'add-todo-list-title'; -const CLOSE_BUTTON_ARIA_LABEL = '닫기'; -const DEFAULT_TITLE = '할 일 목록'; -const DEFAULT_PLACEHOLDER = '할 일을 입력하세요'; -const DEFAULT_SUBMIT_LABEL = '만들기'; - -type TodoInputProps = Omit; - -interface AddTodoListTextOptions { - title?: string; - submitLabel?: string; - inputPlaceholder?: string; -} - -interface AddTodoListInputOptions { - props?: TodoInputProps; -} - -export interface AddTodoListProps extends BaseDomainModalProps { - onSubmit: () => void; - text?: AddTodoListTextOptions; - input?: AddTodoListInputOptions; -} +import { + CLOSE_BUTTON_ARIA_LABEL, + DEFAULT_PLACEHOLDER, + DEFAULT_SUBMIT_LABEL, + DEFAULT_TITLE, + TITLE_ID, +} from './AddTodoList.constants'; +import type { AddTodoListProps } from './AddTodoList.types'; +export type { AddTodoListProps } from './AddTodoList.types'; /** * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. diff --git a/src/components/Modal/domain/components/AddTodoList/AddTodoList.types.ts b/src/components/Modal/domain/components/AddTodoList/AddTodoList.types.ts new file mode 100644 index 0000000..2833191 --- /dev/null +++ b/src/components/Modal/domain/components/AddTodoList/AddTodoList.types.ts @@ -0,0 +1,20 @@ +import type { InputProps } from '@/components/input/types/types'; +import type { BaseDomainModalProps } from '../../types/types'; + +export type TodoInputProps = Omit; + +export interface AddTodoListTextOptions { + title?: string; + submitLabel?: string; + inputPlaceholder?: string; +} + +export interface AddTodoListInputOptions { + props?: TodoInputProps; +} + +export interface AddTodoListProps extends BaseDomainModalProps { + onSubmit: () => void; + text?: AddTodoListTextOptions; + input?: AddTodoListInputOptions; +} diff --git a/src/components/Modal/domain/components/ChangePassword/ChangePassword.constants.ts b/src/components/Modal/domain/components/ChangePassword/ChangePassword.constants.ts new file mode 100644 index 0000000..752fa1b --- /dev/null +++ b/src/components/Modal/domain/components/ChangePassword/ChangePassword.constants.ts @@ -0,0 +1,10 @@ +export const TITLE_ID = 'change-password-title'; +export const NEW_PASSWORD_NAME = 'newPassword'; +export const CONFIRM_PASSWORD_NAME = 'confirmPassword'; +export const DEFAULT_TITLE = '비밀번호 변경하기'; +export const DEFAULT_NEW_PASSWORD_LABEL = '새 비밀번호'; +export const DEFAULT_CONFIRM_PASSWORD_LABEL = '새 비밀번호 확인'; +export const DEFAULT_NEW_PASSWORD_PLACEHOLDER = '새 비밀번호를 입력해주세요.'; +export const DEFAULT_CONFIRM_PASSWORD_PLACEHOLDER = '새 비밀번호를 다시 한 번 입력해주세요.'; +export const DEFAULT_CLOSE_LABEL = '닫기'; +export const DEFAULT_SUBMIT_LABEL = '변경하기'; diff --git a/src/components/Modal/domain/components/ChangePassword/ChangePassword.tsx b/src/components/Modal/domain/components/ChangePassword/ChangePassword.tsx index d814100..a80077a 100644 --- a/src/components/Modal/domain/components/ChangePassword/ChangePassword.tsx +++ b/src/components/Modal/domain/components/ChangePassword/ChangePassword.tsx @@ -6,46 +6,21 @@ import { useId } from 'react'; import Modal from '../../../Modal'; import BaseButton from '@/components/Button/base/BaseButton'; import { Input } from '@/components/input'; -import type { InputProps } from '@/components/input/types/types'; import styles from './ChangePassword.module.css'; -import type { BaseDomainModalProps } from '../../types/types'; - -const TITLE_ID = 'change-password-title'; -const NEW_PASSWORD_NAME = 'newPassword'; -const CONFIRM_PASSWORD_NAME = 'confirmPassword'; -const DEFAULT_TITLE = '비밀번호 변경하기'; -const DEFAULT_NEW_PASSWORD_LABEL = '새 비밀번호'; -const DEFAULT_CONFIRM_PASSWORD_LABEL = '새 비밀번호 확인'; -const DEFAULT_NEW_PASSWORD_PLACEHOLDER = '새 비밀번호를 입력해주세요.'; -const DEFAULT_CONFIRM_PASSWORD_PLACEHOLDER = '새 비밀번호를 다시 한 번 입력해주세요.'; -const DEFAULT_CLOSE_LABEL = '닫기'; -const DEFAULT_SUBMIT_LABEL = '변경하기'; - -type PasswordInputFieldProps = Omit< - InputProps, - 'className' | 'type' | 'name' | 'autoComplete' | 'placeholder' ->; - -interface ChangePasswordTextOptions { - title?: string; - newPasswordLabel?: string; - confirmPasswordLabel?: string; - newPasswordPlaceholder?: string; - confirmPasswordPlaceholder?: string; - closeLabel?: string; - submitLabel?: string; -} - -interface ChangePasswordInputOptions { - newPassword?: PasswordInputFieldProps; - confirmPassword?: PasswordInputFieldProps; -} - -export interface ChangePasswordProps extends BaseDomainModalProps { - onSubmit: () => void; - text?: ChangePasswordTextOptions; - input?: ChangePasswordInputOptions; -} +import { + CONFIRM_PASSWORD_NAME, + DEFAULT_CLOSE_LABEL, + DEFAULT_CONFIRM_PASSWORD_LABEL, + DEFAULT_CONFIRM_PASSWORD_PLACEHOLDER, + DEFAULT_NEW_PASSWORD_LABEL, + DEFAULT_NEW_PASSWORD_PLACEHOLDER, + DEFAULT_SUBMIT_LABEL, + DEFAULT_TITLE, + NEW_PASSWORD_NAME, + TITLE_ID, +} from './ChangePassword.constants'; +import type { ChangePasswordProps } from './ChangePassword.types'; +export type { ChangePasswordProps } from './ChangePassword.types'; /** * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. diff --git a/src/components/Modal/domain/components/ChangePassword/ChangePassword.types.ts b/src/components/Modal/domain/components/ChangePassword/ChangePassword.types.ts new file mode 100644 index 0000000..1ef44b1 --- /dev/null +++ b/src/components/Modal/domain/components/ChangePassword/ChangePassword.types.ts @@ -0,0 +1,28 @@ +import type { InputProps } from '@/components/input/types/types'; +import type { BaseDomainModalProps } from '../../types/types'; + +export type PasswordInputFieldProps = Omit< + InputProps, + 'className' | 'type' | 'name' | 'autoComplete' | 'placeholder' +>; + +export interface ChangePasswordTextOptions { + title?: string; + newPasswordLabel?: string; + confirmPasswordLabel?: string; + newPasswordPlaceholder?: string; + confirmPasswordPlaceholder?: string; + closeLabel?: string; + submitLabel?: string; +} + +export interface ChangePasswordInputOptions { + newPassword?: PasswordInputFieldProps; + confirmPassword?: PasswordInputFieldProps; +} + +export interface ChangePasswordProps extends BaseDomainModalProps { + onSubmit: () => void; + text?: ChangePasswordTextOptions; + input?: ChangePasswordInputOptions; +} diff --git a/src/components/Modal/domain/components/LogoutModal/LogoutModal.constants.ts b/src/components/Modal/domain/components/LogoutModal/LogoutModal.constants.ts new file mode 100644 index 0000000..4cc086c --- /dev/null +++ b/src/components/Modal/domain/components/LogoutModal/LogoutModal.constants.ts @@ -0,0 +1,4 @@ +export const TITLE_ID = 'logout-modal-title'; +export const DEFAULT_TITLE = '로그아웃 하시겠어요?'; +export const DEFAULT_CLOSE_LABEL = '닫기'; +export const DEFAULT_CONFIRM_LABEL = '로그아웃'; diff --git a/src/components/Modal/domain/components/LogoutModal/LogoutModal.tsx b/src/components/Modal/domain/components/LogoutModal/LogoutModal.tsx index 29ecd03..59fa57e 100644 --- a/src/components/Modal/domain/components/LogoutModal/LogoutModal.tsx +++ b/src/components/Modal/domain/components/LogoutModal/LogoutModal.tsx @@ -3,23 +3,14 @@ import Modal from '../../../Modal'; import BaseButton from '@/components/Button/base/BaseButton'; import styles from './LogoutModal.module.css'; -import type { BaseDomainModalProps } from '../../types/types'; - -const TITLE_ID = 'logout-modal-title'; -const DEFAULT_TITLE = '로그아웃 하시겠어요?'; -const DEFAULT_CLOSE_LABEL = '닫기'; -const DEFAULT_CONFIRM_LABEL = '로그아웃'; - -interface LogoutModalTextOptions { - title?: string; - closeLabel?: string; - confirmLabel?: string; -} - -export interface LogoutModalProps extends BaseDomainModalProps { - onConfirm: () => void; - text?: LogoutModalTextOptions; -} +import { + DEFAULT_CLOSE_LABEL, + DEFAULT_CONFIRM_LABEL, + DEFAULT_TITLE, + TITLE_ID, +} from './LogoutModal.constants'; +import type { LogoutModalProps } from './LogoutModal.types'; +export type { LogoutModalProps } from './LogoutModal.types'; /** * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. diff --git a/src/components/Modal/domain/components/LogoutModal/LogoutModal.types.ts b/src/components/Modal/domain/components/LogoutModal/LogoutModal.types.ts new file mode 100644 index 0000000..f87adca --- /dev/null +++ b/src/components/Modal/domain/components/LogoutModal/LogoutModal.types.ts @@ -0,0 +1,12 @@ +import type { BaseDomainModalProps } from '../../types/types'; + +export interface LogoutModalTextOptions { + title?: string; + closeLabel?: string; + confirmLabel?: string; +} + +export interface LogoutModalProps extends BaseDomainModalProps { + onConfirm: () => void; + text?: LogoutModalTextOptions; +} diff --git a/src/components/Modal/domain/components/MemberInvite/MemberInvite.constants.ts b/src/components/Modal/domain/components/MemberInvite/MemberInvite.constants.ts new file mode 100644 index 0000000..ab0e5b5 --- /dev/null +++ b/src/components/Modal/domain/components/MemberInvite/MemberInvite.constants.ts @@ -0,0 +1,6 @@ +export const TITLE_ID = 'member-invite-title'; +export const DESCRIPTION_ID = 'member-invite-description'; +export const CLOSE_BUTTON_ARIA_LABEL = '닫기'; +export const DEFAULT_TITLE = '멤버 초대'; +export const DEFAULT_DESCRIPTION = '그룹에 참여할 수 있는 링크를 복사합니다.'; +export const DEFAULT_COPY_LABEL = '링크 복사하기'; diff --git a/src/components/Modal/domain/components/MemberInvite/MemberInvite.tsx b/src/components/Modal/domain/components/MemberInvite/MemberInvite.tsx index bcad177..f50f2d2 100644 --- a/src/components/Modal/domain/components/MemberInvite/MemberInvite.tsx +++ b/src/components/Modal/domain/components/MemberInvite/MemberInvite.tsx @@ -5,30 +5,16 @@ import Modal from '../../../Modal'; import styles from './MemberInvite.module.css'; import BaseButton from '@/components/Button/base/BaseButton'; import xMarkBig from '@/assets/icons/xMark/xMarkBig.svg'; -import type { BaseDomainModalProps } from '../../types/types'; - -const TITLE_ID = 'member-invite-title'; -const DESCRIPTION_ID = 'member-invite-description'; -const CLOSE_BUTTON_ARIA_LABEL = '닫기'; -const DEFAULT_TITLE = '멤버 초대'; -const DEFAULT_DESCRIPTION = '그룹에 참여할 수 있는 링크를 복사합니다.'; -const DEFAULT_COPY_LABEL = '링크 복사하기'; - -interface MemberInviteTextOptions { - title?: string; - description?: string; - copyButtonLabel?: string; -} - -interface MemberInviteInviteOptions { - link: string; - onCopyLink?: (link: string) => void; -} - -export interface MemberInviteProps extends BaseDomainModalProps { - invite: MemberInviteInviteOptions; - text?: MemberInviteTextOptions; -} +import { + CLOSE_BUTTON_ARIA_LABEL, + DEFAULT_COPY_LABEL, + DEFAULT_DESCRIPTION, + DEFAULT_TITLE, + DESCRIPTION_ID, + TITLE_ID, +} from './MemberInvite.constants'; +import type { MemberInviteProps } from './MemberInvite.types'; +export type { MemberInviteProps } from './MemberInvite.types'; /** * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. diff --git a/src/components/Modal/domain/components/MemberInvite/MemberInvite.types.ts b/src/components/Modal/domain/components/MemberInvite/MemberInvite.types.ts new file mode 100644 index 0000000..7f487b8 --- /dev/null +++ b/src/components/Modal/domain/components/MemberInvite/MemberInvite.types.ts @@ -0,0 +1,17 @@ +import type { BaseDomainModalProps } from '../../types/types'; + +export interface MemberInviteTextOptions { + title?: string; + description?: string; + copyButtonLabel?: string; +} + +export interface MemberInviteInviteOptions { + link: string; + onCopyLink?: (link: string) => void; +} + +export interface MemberInviteProps extends BaseDomainModalProps { + invite: MemberInviteInviteOptions; + text?: MemberInviteTextOptions; +} diff --git a/src/components/Modal/domain/components/ProfileModal/ProfileModal.constants.ts b/src/components/Modal/domain/components/ProfileModal/ProfileModal.constants.ts new file mode 100644 index 0000000..0d10a46 --- /dev/null +++ b/src/components/Modal/domain/components/ProfileModal/ProfileModal.constants.ts @@ -0,0 +1,5 @@ +export const TITLE_ID = 'profile-modal-title'; +export const EMAIL_ID = 'profile-modal-email'; +export const CLOSE_BUTTON_ARIA_LABEL = '닫기'; +export const DEFAULT_COPY_LABEL = '이메일 복사하기'; +export const DEFAULT_PROFILE_ALT = '프로필 이미지'; diff --git a/src/components/Modal/domain/components/ProfileModal/ProfileModal.tsx b/src/components/Modal/domain/components/ProfileModal/ProfileModal.tsx index 50a2665..1cca58a 100644 --- a/src/components/Modal/domain/components/ProfileModal/ProfileModal.tsx +++ b/src/components/Modal/domain/components/ProfileModal/ProfileModal.tsx @@ -1,6 +1,5 @@ 'use client'; -import type { ImageProps } from 'next/image'; import Image from 'next/image'; import Modal from '../../../Modal'; @@ -8,26 +7,15 @@ import styles from './ProfileModal.module.css'; import BaseButton from '@/components/Button/base/BaseButton'; import profileFallback from '@/assets/icons/img/img.svg'; import xMarkBig from '@/assets/icons/xMark/xMarkBig.svg'; -import type { BaseDomainModalProps } from '../../types/types'; - -const TITLE_ID = 'profile-modal-title'; -const EMAIL_ID = 'profile-modal-email'; -const CLOSE_BUTTON_ARIA_LABEL = '닫기'; -const DEFAULT_COPY_LABEL = '이메일 복사하기'; -const DEFAULT_PROFILE_ALT = '프로필 이미지'; - -interface ProfileModalProfileOptions { - title: string; - email: string; - imageSrc?: ImageProps['src']; - imageAlt?: string; - copyButtonLabel?: string; -} - -export interface ProfileModalProps extends BaseDomainModalProps { - onCopyEmail: () => void; - profile: ProfileModalProfileOptions; -} +import { + CLOSE_BUTTON_ARIA_LABEL, + DEFAULT_COPY_LABEL, + DEFAULT_PROFILE_ALT, + EMAIL_ID, + TITLE_ID, +} from './ProfileModal.constants'; +import type { ProfileModalProps } from './ProfileModal.types'; +export type { ProfileModalProps } from './ProfileModal.types'; /** * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. diff --git a/src/components/Modal/domain/components/ProfileModal/ProfileModal.types.ts b/src/components/Modal/domain/components/ProfileModal/ProfileModal.types.ts new file mode 100644 index 0000000..33ebe1a --- /dev/null +++ b/src/components/Modal/domain/components/ProfileModal/ProfileModal.types.ts @@ -0,0 +1,15 @@ +import type { ImageProps } from 'next/image'; +import type { BaseDomainModalProps } from '../../types/types'; + +export interface ProfileModalProfileOptions { + title: string; + email: string; + imageSrc?: ImageProps['src']; + imageAlt?: string; + copyButtonLabel?: string; +} + +export interface ProfileModalProps extends BaseDomainModalProps { + onCopyEmail: () => void; + profile: ProfileModalProfileOptions; +} diff --git a/src/components/Modal/domain/components/ResetPassword/ResetPassword.constants.ts b/src/components/Modal/domain/components/ResetPassword/ResetPassword.constants.ts new file mode 100644 index 0000000..0938364 --- /dev/null +++ b/src/components/Modal/domain/components/ResetPassword/ResetPassword.constants.ts @@ -0,0 +1,7 @@ +export const TITLE_ID = 'reset-password-title'; +export const DESCRIPTION_ID = 'reset-password-description'; +export const DEFAULT_TITLE = '비밀번호 재설정'; +export const DEFAULT_DESCRIPTION = '비밀번호 재설정 링크를 보내드립니다.'; +export const DEFAULT_CLOSE_LABEL = '닫기'; +export const DEFAULT_SUBMIT_LABEL = '링크 보내기'; +export const DEFAULT_EMAIL_PLACEHOLDER = '이메일을 입력하세요'; diff --git a/src/components/Modal/domain/components/ResetPassword/ResetPassword.tsx b/src/components/Modal/domain/components/ResetPassword/ResetPassword.tsx index 02978cd..0e3a2d2 100644 --- a/src/components/Modal/domain/components/ResetPassword/ResetPassword.tsx +++ b/src/components/Modal/domain/components/ResetPassword/ResetPassword.tsx @@ -5,40 +5,18 @@ import type { FormEvent } from 'react'; import Modal from '../../../Modal'; import BaseButton from '@/components/Button/base/BaseButton'; import { Input } from '@/components/input'; -import type { InputProps } from '@/components/input/types/types'; import styles from './ResetPassword.module.css'; -import type { BaseDomainModalProps } from '../../types/types'; - -const TITLE_ID = 'reset-password-title'; -const DESCRIPTION_ID = 'reset-password-description'; -const DEFAULT_TITLE = '비밀번호 재설정'; -const DEFAULT_DESCRIPTION = '비밀번호 재설정 링크를 보내드립니다.'; -const DEFAULT_CLOSE_LABEL = '닫기'; -const DEFAULT_SUBMIT_LABEL = '링크 보내기'; -const DEFAULT_EMAIL_PLACEHOLDER = '이메일을 입력하세요'; - -type EmailInputFieldProps = Omit< - InputProps, - 'className' | 'type' | 'name' | 'autoComplete' | 'placeholder' ->; - -interface ResetPasswordTextOptions { - title?: string; - description?: string; - closeLabel?: string; - submitLabel?: string; - emailPlaceholder?: string; -} - -interface ResetPasswordInputOptions { - email?: EmailInputFieldProps; -} - -export interface ResetPasswordProps extends BaseDomainModalProps { - onSubmit: () => void; - text?: ResetPasswordTextOptions; - input?: ResetPasswordInputOptions; -} +import { + DEFAULT_CLOSE_LABEL, + DEFAULT_DESCRIPTION, + DEFAULT_EMAIL_PLACEHOLDER, + DEFAULT_SUBMIT_LABEL, + DEFAULT_TITLE, + DESCRIPTION_ID, + TITLE_ID, +} from './ResetPassword.constants'; +import type { ResetPasswordProps } from './ResetPassword.types'; +export type { ResetPasswordProps } from './ResetPassword.types'; /** * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. diff --git a/src/components/Modal/domain/components/ResetPassword/ResetPassword.types.ts b/src/components/Modal/domain/components/ResetPassword/ResetPassword.types.ts new file mode 100644 index 0000000..2f7f0a3 --- /dev/null +++ b/src/components/Modal/domain/components/ResetPassword/ResetPassword.types.ts @@ -0,0 +1,25 @@ +import type { InputProps } from '@/components/input/types/types'; +import type { BaseDomainModalProps } from '../../types/types'; + +export type EmailInputFieldProps = Omit< + InputProps, + 'className' | 'type' | 'name' | 'autoComplete' | 'placeholder' +>; + +export interface ResetPasswordTextOptions { + title?: string; + description?: string; + closeLabel?: string; + submitLabel?: string; + emailPlaceholder?: string; +} + +export interface ResetPasswordInputOptions { + email?: EmailInputFieldProps; +} + +export interface ResetPasswordProps extends BaseDomainModalProps { + onSubmit: () => void; + text?: ResetPasswordTextOptions; + input?: ResetPasswordInputOptions; +} diff --git a/src/components/Modal/domain/components/WarningModal/WarningModal.constants.ts b/src/components/Modal/domain/components/WarningModal/WarningModal.constants.ts new file mode 100644 index 0000000..d7d4e56 --- /dev/null +++ b/src/components/Modal/domain/components/WarningModal/WarningModal.constants.ts @@ -0,0 +1,7 @@ +export const TITLE_ID = 'warning-modal-title'; +export const DESCRIPTION_ID = 'warning-modal-description'; +export const DEFAULT_TITLE = '회원 탈퇴를 진행하시겠어요?'; +export const DEFAULT_DESCRIPTION = + '그룹장으로 있는 그룹은 자동으로 삭제되고,\n모든 그룹에서 나가집니다.'; +export const DEFAULT_CLOSE_LABEL = '닫기'; +export const DEFAULT_CONFIRM_LABEL = '회원 탈퇴'; diff --git a/src/components/Modal/domain/components/WarningModal/WarningModal.tsx b/src/components/Modal/domain/components/WarningModal/WarningModal.tsx index 915681c..89aaf44 100644 --- a/src/components/Modal/domain/components/WarningModal/WarningModal.tsx +++ b/src/components/Modal/domain/components/WarningModal/WarningModal.tsx @@ -6,26 +6,16 @@ import Modal from '../../../Modal'; import BaseButton from '@/components/Button/base/BaseButton'; import styles from './WarningModal.module.css'; import alertSmall from '@/assets/icons/alert/alertSmall.svg'; -import type { BaseDomainModalProps } from '../../types/types'; - -const TITLE_ID = 'warning-modal-title'; -const DESCRIPTION_ID = 'warning-modal-description'; -const DEFAULT_TITLE = '회원 탈퇴를 진행하시겠어요?'; -const DEFAULT_DESCRIPTION = '그룹장으로 있는 그룹은 자동으로 삭제되고,\n모든 그룹에서 나가집니다.'; -const DEFAULT_CLOSE_LABEL = '닫기'; -const DEFAULT_CONFIRM_LABEL = '회원 탈퇴'; - -interface WarningModalTextOptions { - title?: string; - description?: string; - closeLabel?: string; - confirmLabel?: string; -} - -export interface WarningModalProps extends BaseDomainModalProps { - onConfirm: () => void; - text?: WarningModalTextOptions; -} +import { + DEFAULT_CLOSE_LABEL, + DEFAULT_CONFIRM_LABEL, + DEFAULT_DESCRIPTION, + DEFAULT_TITLE, + DESCRIPTION_ID, + TITLE_ID, +} from './WarningModal.constants'; +import type { WarningModalProps } from './WarningModal.types'; +export type { WarningModalProps } from './WarningModal.types'; /** * @param props.isOpen 모달 표시 여부를 boolean으로 전달합니다. diff --git a/src/components/Modal/domain/components/WarningModal/WarningModal.types.ts b/src/components/Modal/domain/components/WarningModal/WarningModal.types.ts new file mode 100644 index 0000000..4f9343e --- /dev/null +++ b/src/components/Modal/domain/components/WarningModal/WarningModal.types.ts @@ -0,0 +1,13 @@ +import type { BaseDomainModalProps } from '../../types/types'; + +export interface WarningModalTextOptions { + title?: string; + description?: string; + closeLabel?: string; + confirmLabel?: string; +} + +export interface WarningModalProps extends BaseDomainModalProps { + onConfirm: () => void; + text?: WarningModalTextOptions; +} From 8dfd13617f2de303b3e82702496114d8d0990574 Mon Sep 17 00:00:00 2001 From: jieunsse Date: Sat, 7 Feb 2026 20:34:36 +0900 Subject: [PATCH 15/15] =?UTF-8?q?style:=20css=20=EC=97=B0=EC=87=84?= =?UTF-8?q?=EC=84=A0=ED=83=9D=EC=9E=90=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AddTodoList/AddTodoList.module.css | 14 ++--- .../ChangePassword/ChangePassword.module.css | 8 +-- .../ChangePassword/ChangePassword.tsx | 53 ++++++++----------- .../ChangePassword/ChangePassword.utils.ts | 50 +++++++++++++++++ .../LogoutModal/LogoutModal.module.css | 10 ++-- .../MemberInvite/MemberInvite.module.css | 10 ++-- .../ProfileModal/ProfileModal.module.css | 10 ++-- .../ResetPassword/ResetPassword.module.css | 6 +-- .../WarningModal/WarningModal.module.css | 10 ++-- 9 files changed, 107 insertions(+), 64 deletions(-) create mode 100644 src/components/Modal/domain/components/ChangePassword/ChangePassword.utils.ts diff --git a/src/components/Modal/domain/components/AddTodoList/AddTodoList.module.css b/src/components/Modal/domain/components/AddTodoList/AddTodoList.module.css index 5d76e96..95583b2 100644 --- a/src/components/Modal/domain/components/AddTodoList/AddTodoList.module.css +++ b/src/components/Modal/domain/components/AddTodoList/AddTodoList.module.css @@ -1,4 +1,4 @@ -.modalContent.modalContent { +section > .modalContent { width: 384px; height: 235px; display: inline-flex; @@ -58,9 +58,9 @@ cursor: pointer; } -.closeButton.closeButton, -.closeButton.closeButton:hover:not(:disabled), -.closeButton.closeButton:active:not(:disabled) { +.buttonContainer .closeButton, +.buttonContainer .closeButton:hover:not(:disabled), +.buttonContainer .closeButton:active:not(:disabled) { border: none; background: transparent; color: inherit; @@ -74,7 +74,7 @@ gap: 24px; } -.input.input { +.form .input { display: flex; width: 280px; height: 48px; @@ -88,7 +88,7 @@ margin: 0 auto; } -.input.input::placeholder { +.form .input::placeholder { color: var(--Text-Default, #64748b); font-family: Pretendard; font-size: 16px; @@ -124,7 +124,7 @@ } @media (max-width: 480px) { - .modalContent.modalContent { + section > .modalContent { border-radius: 24px 24px 0 0; } } diff --git a/src/components/Modal/domain/components/ChangePassword/ChangePassword.module.css b/src/components/Modal/domain/components/ChangePassword/ChangePassword.module.css index 2b3195d..05f3488 100644 --- a/src/components/Modal/domain/components/ChangePassword/ChangePassword.module.css +++ b/src/components/Modal/domain/components/ChangePassword/ChangePassword.module.css @@ -1,4 +1,4 @@ -.modalContent.modalContent { +section > .modalContent { display: flex; width: 384px; height: 353px; @@ -58,7 +58,7 @@ line-height: 19px; } -.input.input { +.field .input { display: flex; width: 280px; height: 48px; @@ -71,7 +71,7 @@ box-sizing: border-box; } -.input.input::placeholder { +.field .input::placeholder { color: var(--Text-Default, #64748b); font-family: Pretendard; font-size: 16px; @@ -126,7 +126,7 @@ } @media (max-width: 480px) { - .modalContent.modalContent { + section > .modalContent { border-radius: 12px 12px 0 0; } } diff --git a/src/components/Modal/domain/components/ChangePassword/ChangePassword.tsx b/src/components/Modal/domain/components/ChangePassword/ChangePassword.tsx index a80077a..8aacd0b 100644 --- a/src/components/Modal/domain/components/ChangePassword/ChangePassword.tsx +++ b/src/components/Modal/domain/components/ChangePassword/ChangePassword.tsx @@ -1,25 +1,19 @@ 'use client'; -import type { FormEvent } from 'react'; import { useId } from 'react'; import Modal from '../../../Modal'; import BaseButton from '@/components/Button/base/BaseButton'; import { Input } from '@/components/input'; import styles from './ChangePassword.module.css'; -import { - CONFIRM_PASSWORD_NAME, - DEFAULT_CLOSE_LABEL, - DEFAULT_CONFIRM_PASSWORD_LABEL, - DEFAULT_CONFIRM_PASSWORD_PLACEHOLDER, - DEFAULT_NEW_PASSWORD_LABEL, - DEFAULT_NEW_PASSWORD_PLACEHOLDER, - DEFAULT_SUBMIT_LABEL, - DEFAULT_TITLE, - NEW_PASSWORD_NAME, - TITLE_ID, -} from './ChangePassword.constants'; +import { CONFIRM_PASSWORD_NAME, NEW_PASSWORD_NAME, TITLE_ID } from './ChangePassword.constants'; import type { ChangePasswordProps } from './ChangePassword.types'; +import { + createSubmitHandler, + resolveChangePasswordText, + resolveCloseOptions, + resolvePasswordInputIds, +} from './ChangePassword.utils'; export type { ChangePasswordProps } from './ChangePassword.types'; /** @@ -38,26 +32,25 @@ export default function ChangePassword({ input, closeOptions, }: ChangePasswordProps) { - const title = text?.title ?? DEFAULT_TITLE; - const newPasswordLabel = text?.newPasswordLabel ?? DEFAULT_NEW_PASSWORD_LABEL; - const confirmPasswordLabel = text?.confirmPasswordLabel ?? DEFAULT_CONFIRM_PASSWORD_LABEL; - const newPasswordPlaceholder = text?.newPasswordPlaceholder ?? DEFAULT_NEW_PASSWORD_PLACEHOLDER; - const confirmPasswordPlaceholder = - text?.confirmPasswordPlaceholder ?? DEFAULT_CONFIRM_PASSWORD_PLACEHOLDER; - const closeLabel = text?.closeLabel ?? DEFAULT_CLOSE_LABEL; - const submitLabel = text?.submitLabel ?? DEFAULT_SUBMIT_LABEL; - const closeOnOverlayClick = closeOptions?.overlayClick ?? true; - const closeOnEscape = closeOptions?.escape ?? true; + const { + title, + newPasswordLabel, + confirmPasswordLabel, + newPasswordPlaceholder, + confirmPasswordPlaceholder, + closeLabel, + submitLabel, + } = resolveChangePasswordText(text); + const { closeOnOverlayClick, closeOnEscape } = resolveCloseOptions(closeOptions); const generatedNewPasswordId = useId(); const generatedConfirmPasswordId = useId(); - const newPasswordId = input?.newPassword?.id ?? generatedNewPasswordId; - const confirmPasswordId = input?.confirmPassword?.id ?? generatedConfirmPasswordId; - - const handleSubmit = (e: FormEvent) => { - e.preventDefault(); - onSubmit(); - }; + const { newPasswordId, confirmPasswordId } = resolvePasswordInputIds( + input, + generatedNewPasswordId, + generatedConfirmPasswordId, + ); + const handleSubmit = createSubmitHandler(onSubmit); return ( void) { + return (e: FormEvent) => { + e.preventDefault(); + onSubmit(); + }; +} diff --git a/src/components/Modal/domain/components/LogoutModal/LogoutModal.module.css b/src/components/Modal/domain/components/LogoutModal/LogoutModal.module.css index 32d3376..63b0d3c 100644 --- a/src/components/Modal/domain/components/LogoutModal/LogoutModal.module.css +++ b/src/components/Modal/domain/components/LogoutModal/LogoutModal.module.css @@ -1,4 +1,4 @@ -.modalContent.modalContent { +section > .modalContent { width: 384px; height: 171px; box-sizing: border-box; @@ -61,9 +61,9 @@ cursor: pointer; } -.closeButton.closeButton, -.closeButton.closeButton:hover:not(:disabled), -.closeButton.closeButton:active:not(:disabled) { +.actions .closeButton, +.actions .closeButton:hover:not(:disabled), +.actions .closeButton:active:not(:disabled) { border: 1px solid var(--Border-Secondary, #cbd5e1); background: var(--Background-Primary, #fff); color: var(--Text-Default, #64748b); @@ -98,7 +98,7 @@ } @media (max-width: 480px) { - .modalContent.modalContent { + section > .modalContent { width: 100%; max-width: 375px; border-radius: 24px 24px 0 0; diff --git a/src/components/Modal/domain/components/MemberInvite/MemberInvite.module.css b/src/components/Modal/domain/components/MemberInvite/MemberInvite.module.css index 998f9de..412ffc7 100644 --- a/src/components/Modal/domain/components/MemberInvite/MemberInvite.module.css +++ b/src/components/Modal/domain/components/MemberInvite/MemberInvite.module.css @@ -12,7 +12,7 @@ text-align: center; } -.modalContent.modalContent { +section > .modalContent { border-radius: 24px; } @@ -31,9 +31,9 @@ cursor: pointer; } -.closeButton.closeButton, -.closeButton.closeButton:hover:not(:disabled), -.closeButton.closeButton:active:not(:disabled) { +.container .closeButton, +.container .closeButton:hover:not(:disabled), +.container .closeButton:active:not(:disabled) { border: none; background: transparent; color: inherit; @@ -82,7 +82,7 @@ } @media (max-width: 480px) { - .modalContent.modalContent { + section > .modalContent { border-radius: 24px 24px 0 0; } diff --git a/src/components/Modal/domain/components/ProfileModal/ProfileModal.module.css b/src/components/Modal/domain/components/ProfileModal/ProfileModal.module.css index 1e14e15..4378c62 100644 --- a/src/components/Modal/domain/components/ProfileModal/ProfileModal.module.css +++ b/src/components/Modal/domain/components/ProfileModal/ProfileModal.module.css @@ -1,4 +1,4 @@ -.modalContent.modalContent { +section > .modalContent { width: 344px; height: 243px; display: inline-flex; @@ -47,9 +47,9 @@ cursor: pointer; } -.closeButton.closeButton, -.closeButton.closeButton:hover:not(:disabled), -.closeButton.closeButton:active:not(:disabled) { +.container .closeButton, +.container .closeButton:hover:not(:disabled), +.container .closeButton:active:not(:disabled) { border: none; background: transparent; color: inherit; @@ -121,7 +121,7 @@ } @media (max-width: 480px) { - .modalContent.modalContent { + section > .modalContent { width: 100%; max-width: 375px; border-radius: 24px 24px 0 0; diff --git a/src/components/Modal/domain/components/ResetPassword/ResetPassword.module.css b/src/components/Modal/domain/components/ResetPassword/ResetPassword.module.css index bdf6bc7..15b2064 100644 --- a/src/components/Modal/domain/components/ResetPassword/ResetPassword.module.css +++ b/src/components/Modal/domain/components/ResetPassword/ResetPassword.module.css @@ -1,4 +1,4 @@ -.modalContent.modalContent { +section > .modalContent { display: inline-flex; padding: 16px 16px 32px 16px; flex-direction: column; @@ -57,7 +57,7 @@ flex: 1; } -.input.input { +.form .input { display: flex; width: 280px; height: 48px; @@ -117,7 +117,7 @@ } @media (max-width: 480px) { - .modalContent.modalContent { + section > .modalContent { border-radius: 24px 24px 0 0; } } diff --git a/src/components/Modal/domain/components/WarningModal/WarningModal.module.css b/src/components/Modal/domain/components/WarningModal/WarningModal.module.css index ba6c291..cd3718a 100644 --- a/src/components/Modal/domain/components/WarningModal/WarningModal.module.css +++ b/src/components/Modal/domain/components/WarningModal/WarningModal.module.css @@ -1,4 +1,4 @@ -.modalContent.modalContent { +section > .modalContent { box-sizing: border-box; display: inline-flex; width: 384px; @@ -82,9 +82,9 @@ cursor: pointer; } -.closeButton.closeButton, -.closeButton.closeButton:hover:not(:disabled), -.closeButton.closeButton:active:not(:disabled) { +.actions .closeButton, +.actions .closeButton:hover:not(:disabled), +.actions .closeButton:active:not(:disabled) { border: 1px solid var(--Border-Secondary, #cbd5e1); background: var(--Background-Primary, #fff); color: var(--Text-Default, #64748b); @@ -119,7 +119,7 @@ } @media (max-width: 480px) { - .modalContent.modalContent { + section > .modalContent { width: 100%; max-width: 375px; border-radius: 24px 24px 0 0;