Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions src/components/Modal/domain/AddTodoList.module.css
Original file line number Diff line number Diff line change
@@ -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);
}
}
52 changes: 52 additions & 0 deletions src/components/Modal/domain/AddTodoList.tsx
Original file line number Diff line number Diff line change
@@ -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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

onCreate prop이 생성할 할 일의 내용을 인자로 받을 수 있도록 onCreate: (todo: string) => void; 와 같이 시그니처를 변경해야 합니다. 현재 구현에서는 입력된 값이 onCreate 함수로 전달되지 않습니다.

Suggested change
onCreate: () => void;
onCreate: (todo: string) => void;

}

export default function AddTodoList({ isOpen, onClose, onCreate }: AddTodoListProps) {
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
onCreate();
};
Comment on lines +19 to +22

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

handleSubmit 함수가 폼 데이터를 읽어서 onCreate 콜백에 전달해야 합니다. FormData API를 사용하면 Input의 값을 쉽게 가져올 수 있습니다. 이를 위해 Input 컴포넌트에 name 속성을 추가하는 것을 잊지 마세요 (예: <Input name="todo" ... />).

  const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    const todo = formData.get('todo') as string;
    onCreate(todo);
  };


return (
<Modal
isOpen={isOpen}
onClose={onClose}
ariaLabelledby={TITLE_ID}
contentClassName={styles.modalContent}
>
<article className={styles.container}>
<header className={styles.header}>
<h2 id={TITLE_ID} className={styles.title}>
할 일 목록
</h2>
<button type="button" className={styles.closeButton} aria-label="close" onClick={onClose}>
<Image src={xMarkBig} alt="" width={24} height={24} />
</button>
</header>

<form className={styles.form} onSubmit={handleSubmit}>
<Input className={styles.input} placeholder="할 일을 입력하세요" />
<footer className={styles.footer}>
<button type="submit" className={styles.button}>
만들기
</button>
</footer>
</form>
</article>
</Modal>
);
}
90 changes: 90 additions & 0 deletions src/components/Modal/domain/MemberInvite.module.css
Original file line number Diff line number Diff line change
@@ -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;
}
}
48 changes: 48 additions & 0 deletions src/components/Modal/domain/MemberInvite.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Modal
isOpen={isOpen}
onClose={onClose}
ariaLabelledby="member-invite-title"
ariaDescribedby="member-invite-desc"
contentClassName={styles.modalContent}
>
<div className={styles.container}>
<button type="button" className={styles.closeButton} onClick={onClose} aria-label="close">
<Image src={xMarkBig} alt="" width={24} height={24} />
</button>
<h2 id="member-invite-title" className={styles.title}>
멤버 초대
</h2>
<p id="member-invite-desc" className={styles.description}>
그룹에 참여할 수 있는 링크를 복사합니다.
</p>
<button type="button" className={styles.copyButton} onClick={handleCopy}>
링크 복사하기
</button>
</div>
</Modal>
);
}
Loading
Loading