diff --git a/src/App.css b/src/App.css index 45c6062..4ffd20d 100644 --- a/src/App.css +++ b/src/App.css @@ -64,12 +64,60 @@ body { align-self: stretch; } +.todo-input-card { + display: flex; + flex-direction: row; + align-items: center; + gap: 12px; + width: 100%; +} + +.todo-input { + display: flex; + height: 46.6px; + padding: 12px 16px; + align-items: center; + flex: 1 0 0; + border-radius: 8px; + border: 0.8px solid #E5E7EB; +} + +.todo-input:focus { + outline: none; + border-radius: 8px; + border: 2px solid #3B82F6; + background: rgba(255, 255, 255, 0.00); + box-shadow: 0 0 0 4px #3B82F6; +} + +.add-button { + display: flex; + width: 72.2px; + height: 46.6px; + padding: 12.8px 23.2px 12.8px 24px; + justify-content: center; + align-items: center; + flex-shrink: 0; + border: none; + border-radius: 8px; + background: #3B82F6; + white-space: nowrap; + color: #FFF; + text-align: center; + font-family: Pretendard; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 21px; /* 150% */ +} + .todo-card { display: flex; padding: 16px; align-items: center; gap: 10px; align-self: stretch; + justify-content: space-between; border-radius: 12px; background: #FFF; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.10); @@ -115,6 +163,20 @@ body { text-decoration-line: line-through; } +.delete-button { + color: #0A0A0A; + text-align: center; + font-family: Pretendard; + font-size: 20px; + font-style: normal; + font-weight: 500; + line-height: 30px; /* 150% */ + border: none; + background: none; + margin-left: auto; + gap: 12px; +} + .todo-empty { display: flex; width: 640px; @@ -146,4 +208,5 @@ body { font-style: normal; font-weight: 400; line-height: 72px; /* 150% */ -} \ No newline at end of file +} + diff --git a/src/App.tsx b/src/App.tsx index b37b93b..e2fcfd8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,26 +1,67 @@ +import { useState } from "react"; import TodoHeader from "./components/TodoHeader"; import TodoList from "./components/TodoList"; import "./App.css"; -const headerIcon = "✅"; -const headerTitle = "오늘의 할 일"; +interface Todo { + id: number; + text: string; + done: boolean; +} -const todos = [ - {id: 1, text:"리액트 공식문서 읽기", done: true}, - {id: 2, text:"알고리즘 문제 풀기", done: true}, - {id: 3, text:"운동 30분 하기", done: false}, - {id: 4, text:"프로젝트 회의 준비", done: false}, -]; +export default function App() { + const [todos, setTodos] = useState([ + { id: 1, text: "리액트 공식문서 읽기", done: true }, + { id: 2, text: "알고리즘 문제 풀기", done: true }, + { id: 3, text: "운동 30분 하기", done: false }, + { id: 4, text: "프로젝트 회의 준비", done: false }, + { id: 5, text: "장보기", done: false }, + ]); + const [inputValue, setInputValue] = useState(""); -const weekLabel = todos.length === 0 ? "Week 2 — 빈 상태" : "Week 2 — 체크박스 토글"; + const [isFocused, setIsFocused] = useState(false); + + const weekLabel = isFocused + ? "week3 - 입력 중(focus)" + : "week3 - 기본 상태"; + + + const handleAdd = () => { + if (inputValue.trim() === "") return; + setTodos((prev) => [ + ...prev, + { id: Date.now(), text: inputValue.trim(), done: false }, + ]); + setInputValue(""); + }; + + const handleDelete = (id: number) => { + setTodos((prev) => prev.filter((todo) => todo.id !== id)); + }; + + const handleToggle = (id: number) => { + setTodos((prev) => + prev.map((todo) => + todo.id === id ? { ...todo, done: !todo.done } : todo + ) + ); + }; -export default function App() { return (

{weekLabel}

- - + + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + />
); diff --git a/src/components/TodoCard.tsx b/src/components/TodoCard.tsx index 57465b2..8b3b774 100644 --- a/src/components/TodoCard.tsx +++ b/src/components/TodoCard.tsx @@ -3,12 +3,17 @@ import checkIcon from "../assets/check.svg"; type TodoCardProps = { text: string; done: boolean; + onDelete: () => void; + onToggle: () => void; }; -export default function TodoCard({ text, done }: TodoCardProps) { +export default function TodoCard({ text, done, onDelete, onToggle }: TodoCardProps) { return (
-
+
{done && ( 완료 )} @@ -16,6 +21,9 @@ export default function TodoCard({ text, done }: TodoCardProps) { {text} +
); -} \ No newline at end of file +} diff --git a/src/components/TodoList.tsx b/src/components/TodoList.tsx index 79446d6..662325f 100644 --- a/src/components/TodoList.tsx +++ b/src/components/TodoList.tsx @@ -8,23 +8,59 @@ type Todo = { type TodoListProps = { items: Todo[]; + inputValue: string; + onInputChange: (value: string) => void; + onAdd: () => void; + onDelete: (id: number) => void; + onToggle: (id: number) => void; + onFocus: () => void; + onBlur: () => void; }; -export default function TodoList({ items }: TodoListProps) { - if (items.length === 0) { - return ( -
- {"📋"} -

{"아직 할 일이 없어요"}

-
- ); - } - +export default function TodoList({ + items, + inputValue, + onInputChange, + onAdd, + onDelete, + onToggle, + onFocus, + onBlur, +}: TodoListProps) { return (
- {items.map((item) => ( - - ))} +
+ onInputChange(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && onAdd()} + onFocus={onFocus} + onBlur={onBlur} + /> + +
+ + {items.length === 0 ? ( +
+ {"📋"} +

{"아직 할 일이 없어요"}

+
+ ) : ( + items.map((item) => ( + onDelete(item.id)} + onToggle={() => onToggle(item.id)} + /> + )) + )}
); } \ No newline at end of file