diff --git a/README.md b/README.md index 3c0710d2..6daae186 100644 --- a/README.md +++ b/README.md @@ -1 +1,16 @@ -# react-todo-list-precourse \ No newline at end of file +# 미니 과제 - 할 일 목록 (React) + +카카오 테크 캠퍼스 2기 + +### [STEP1] 2회차 미니과제 - 할 일 목록 (React) + +- 하루 또는 한 주의 할 일 목록을 업데이트하는 할 일 목록을 구현한다. +- `React` 라이브러리를 사용하여 웹 앱으로 구현한다. + +## 기능 목록 + +- 인터페이스 구현 +- 할 일 추가 기능 +- 할 일 삭제 기능 +- 할 일 완료 상태 전환 기능 +- 새로고침 시 목록 유지 기능 diff --git a/index.html b/index.html index b021b5c8..2eb4781a 100644 --- a/index.html +++ b/index.html @@ -1,12 +1,12 @@ - + - + Todos
- + diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 00000000..03b67d76 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,40 @@ +import React, { useState, useEffect } from "react"; +import "./styles/App.css"; +import ReactDOM from "react-dom/client"; +import Header from "./components/Header"; +import TodoList from "./components/TodoList"; +import { + loadTodos, + saveTodos, + createAddTodo, + createDelTodo, + createToggleTodo, +} from "./utils/utils.js"; + +function App() { + const [todos, setTodos] = useState(loadTodos); + + useEffect(() => { + saveTodos(todos); + }, [todos]); + + const addTodo = createAddTodo(setTodos); + const delTodo = createDelTodo(setTodos); + const toggleTodo = createToggleTodo(setTodos); + + return ( + <> +
+ + + ); +} + +const root = ReactDOM.createRoot(document.getElementById("app")); +root.render( + + + +); + +export default App; diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 00000000..071775f8 --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,36 @@ +import React, { useState } from "react"; + +function Header({ addTodo }) { + const [newTodo, setNewTodo] = useState(""); + + const handleKeyDown = (e) => { + if (e.key === "Enter") { + handleAddTodo(); + } + }; + + const handleAddTodo = () => { + if (newTodo.trim() !== "") { + addTodo(newTodo); + setNewTodo(""); + } + }; + + return ( +
+

todos

+
+ setNewTodo(e.target.value)} + onKeyDown={handleKeyDown} + /> +
+
+ ); +} + +export default Header; diff --git a/src/components/TodoItem.jsx b/src/components/TodoItem.jsx new file mode 100644 index 00000000..02849362 --- /dev/null +++ b/src/components/TodoItem.jsx @@ -0,0 +1,20 @@ +import React from "react"; + +function TodoItem({ todo, onDelete, onToggle }) { + return ( +
  • + onToggle(todo.id)} + > + + +
  • + ); +} + +export default TodoItem; diff --git a/src/components/TodoList.jsx b/src/components/TodoList.jsx new file mode 100644 index 00000000..f66c8bce --- /dev/null +++ b/src/components/TodoList.jsx @@ -0,0 +1,19 @@ +import React from "react"; +import TodoItem from "./TodoItem"; + +function TodoList({ todos, delTodo, toggleTodo }) { + return ( + + ); +} + +export default TodoList; diff --git a/src/main.js b/src/main.js deleted file mode 100644 index e69de29b..00000000 diff --git a/src/styles/App.css b/src/styles/App.css new file mode 100644 index 00000000..80b25169 --- /dev/null +++ b/src/styles/App.css @@ -0,0 +1,66 @@ +body { + margin: 0px; + padding: 0px; + width: 100vw; + height: 100vh; + overflow: hidden; + background-color: whitesmoke; + box-sizing: border-box; +} + +/*HEADER*/ +.header { + text-align: center; +} + +.header h1 { + color: pink; + font-size: 60px; +} + +.header .todo-input { + width: 400px; + font-size: 20px; + height: 50px; +} + +/*TodoItem*/ +.todo-list { + list-style-type: none; + width: 406px; + padding: 0px; + font-size: 20px; + background-color: white; + margin: 0 auto; +} + +.todo-list .todo-item { + display: flex; + align-items: center; + height: 50px; + margin: 0 auto; + padding: 0 20px 0 20px; +} +.toggle { + accent-color: pink; +} + +.todo-item input[type="checkbox"] { + margin-right: 1rem; +} +.todo-item-label { + width: 300px; +} +.todo-item .delete { + all: unset; + color: grey; +} +.todo-item .delete:hover { + color: pink; + cursor: pointer; +} +.completed .todo-item-label { + text-decoration: line-through; + text-decoration-color: pink; + color: grey; +} diff --git a/src/utils/utils.js b/src/utils/utils.js new file mode 100644 index 00000000..5820744a --- /dev/null +++ b/src/utils/utils.js @@ -0,0 +1,30 @@ +export const loadTodos = () => { + const savedTodos = JSON.parse(localStorage.getItem("todos-app")); + return savedTodos ? savedTodos : []; +}; + +export const saveTodos = (todos) => { + localStorage.setItem("todos-app", JSON.stringify(todos)); +}; +export const createAddTodo = (setTodos) => (todo) => { + setTodos((prevTodos) => [ + ...prevTodos, + { id: Date.now(), text: todo, completed: false }, + ]); +}; + +export const createDelTodo = (setTodos) => (id) => { + setTodos((prevTodos) => prevTodos.filter((todo) => todo.id !== id)); +}; + +export const createToggleTodo = (setTodos) => (id) => { + setTodos((prevTodos) => + prevTodos.map((todo) => { + if (todo.id === id) { + return { ...todo, completed: !todo.completed }; + } else { + return todo; + } + }) + ); +};