Skip to content
Merged
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
1 change: 0 additions & 1 deletion public/favicon.svg

This file was deleted.

24 changes: 0 additions & 24 deletions public/icons.svg

This file was deleted.

27 changes: 20 additions & 7 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
import './App.css';
import EmptyList from './components/EmptyList';
import TodoHeader from './components/TodoHeader';
import TodoList from './components/TodoList';
import { useState } from 'react';


function App() {
const todos = [
'리액트 공식문서 읽기',
'알고리즘 문제 풀기',
'운동 30분 하기',
'프로젝트 회의 준비',
];
const [todos, setTodos] = useState([
{id:1, text:'리액트 공식문서 읽기', isChecked:false},
{id:2, text:'알고리즘 문제 풀기', isChecked:false},
{id:3, text:'운동 30분 하기', isChecked:false},
{id:4, text:'프로젝트 회의 준비', isChecked:false},
]);
Comment on lines +9 to +14
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

todo 데이터가 컴포넌트 안에 직접 들어가 있는데, App.tsx가 상태 관리와 화면 조합에 더 집중하도록 하려면 todo.data.ts 같은 파일로 분리해두는 것도 좋은 방향입니다.


const onCheck = (id: number) => {
setTodos((prev) =>
prev.map((todo) =>
todo.id === id ? { ...todo, isChecked: !todo.isChecked } : todo
)
);
};

return (
<div className="todo">
<TodoHeader title="오늘의 할 일" />
<TodoList todos={todos} />
<TodoList todos={todos} onCheck={onCheck} />

<TodoHeader title="오늘의 할 일" />
<EmptyList />
Comment on lines 26 to +30
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

다음주에는 이 부분을 리스트가 비어있을 경우에만 EmptyList를 보여주는 방식으로 바꾸시면 될 것 같습니다.

</div>
);
}
Expand Down
10 changes: 10 additions & 0 deletions src/assets/checkIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/components/EmptyList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import '../css/EmptyList.css';

export default function EmptyList() {
return(
<div className='empty-list'>
<span className='empty-icon'>📋</span>
<p className="empty-list-text">아직 할 일이 없어요</p>

</div>
)

}
25 changes: 23 additions & 2 deletions src/components/TodoCard.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
import '../css/TodoCard.css';
import checkIcon from '../assets/checkIcon.svg';

type Props = {
id: number;
text: string;
isChecked: boolean;
onCheck: (id: number) => void;
};

export default function TodoCard({text}: Props){
return <div className="card">{text}</div>
export default function TodoCard({ id, text, isChecked, onCheck }: Props) {
return (
<li className="card">
<label className="card-label">
<input
className="card-input"
type="checkbox"
checked={isChecked}
onChange={() => onCheck(id)}
/>
<span className="card-checkbox-ui">
{isChecked ? (
<img src={checkIcon} alt="" className="card-checkbox-icon" />
) : null}
</span>
<span className="card-text">{text}</span>
</label>
</li>
);
}
23 changes: 18 additions & 5 deletions src/components/TodoList.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
import TodoCard from './TodoCard';
import '../css/TodoList.css';

export type TodoItem = {
id: number;
text: string;
isChecked: boolean;
};

Comment on lines +4 to +9
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

현재 TodoItem 타입은 TodoList.tsx에 있고 실제 상태 데이터는 App.tsx에서 관리되고 있어서, 타입의 소유 위치가 조금 애매하게 느껴집니다. 이후에도 해당 타입을 사용 할 예정이라면src/types/todo.types.ts 처럼 공용 타입 파일로 분리해두면 구조가 더 명확해질 것 같습니다.

type Props = {
todos: string[];
todos: TodoItem[];
onCheck: (id: number) => void;
};

export default function TodoList({ todos }: Props) {
export default function TodoList({ todos, onCheck }: Props) {
return (
<div className="card-list">
<ul className="card-list">
{todos.map((todo) => (
<TodoCard key={todo} text={todo} />
<TodoCard
key={todo.id}
id={todo.id}
text={todo.text}
isChecked={todo.isChecked}
onCheck={onCheck}
/>
))}
</div>
</ul>
);
}
32 changes: 32 additions & 0 deletions src/css/EmptyList.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.empty-list{
display: flex;
width: 640px;
height: 233px;
flex-direction: column;
gap: 12px;
justify-content: center;
align-items: center;
border-radius: 12px;
background: #FFF;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.10);

}

.empty-icon{
text-align: center;
font-family: Pretendard;
font-size: 48px;
font-style: normal;
font-weight: 400;
line-height: 72px; /* 150% */
}

.empty-list-text{
color: #6B7280;
text-align: center;
font-family: Pretendard;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 21px; /* 150% */
}
55 changes: 53 additions & 2 deletions src/css/TodoCard.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.card {
width: 100%;
width: 640px;
height: 56px;
padding: 16px;
box-sizing: border-box;
display: flex;
Expand All @@ -16,4 +17,54 @@
font-style: normal;
font-weight: 400;
line-height: 21px;
}
}

.card:has(.card-input:checked) {
text-decoration: line-through;
opacity: 0.5;

}

.card-label:has(.card-input:checked) .card-checkbox-ui {
display: flex;
width: 24px;
height: 24px;
justify-content: center;
align-items: center;

border-radius: 13421800px;
border: 2px solid #3B82F6;
background: #3B82F6;
}

.card-label:has(.card-input:checked) .card-checkbox-icon {
width: 14px;
height: 10px;
flex-shrink: 0;
display: block;

}



.card-label {
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
flex: 1;
min-width: 0;
}

.card-input {
display: none;
}

.card-checkbox-ui {
width: 24px;
height: 24px;

border-radius: 13421800px;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

이부분은 제가 figma에 잘못 기입했는데 border-radius: 50%나 9999px정도만 해도 동그라미가 잘 되고 의미 전달도 잘 됩니다.

border: 2px solid #E5E7EB;
}