diff --git a/README.md b/README.md index 3c0710d2..e8b27d7f 100644 --- a/README.md +++ b/README.md @@ -1 +1,26 @@ -# react-todo-list-precourse \ No newline at end of file +# react-todo-list-precourse + +## 할 일 목록 애플리케이션 + +- 이 프로그램은 사용자가 할 일을 입력하여 todo list를 관리할 수 있습니다. + +### 사용 방법 + +1. 입력 필드에 할 일을 입력하고 엔터를 눌러 추가합니다. +2. 체크박스를 클릭하여 할 일을 완료로 표시합니다. +3. "All", "Active", "Completed" 버튼을 클릭하여 할 일을 필터링합니다. +4. "Clear completed" 버튼을 클릭하여 완료된 할 일을 삭제합니다. +5. 입력 필드 왼쪽의 화살표 버튼을 클릭하여 모든 할 일을 완료로 표시합니다. + +### 구현된 기능 목록 + +- 사용자 인터페이스 구성 +- 새로운 할 일 추가 기능 +- 할 일 완료 표시 +- 완료된 할 일 삭제 버튼 + +### 구현할 기능 목록 + +- 완료되지 않은 할 일 개수 출력 +- 할 일을 상태별로 필터링(All, Active, Completed) +- 모든 할 일 완료로 표시 \ No newline at end of file diff --git a/index.html b/index.html index b021b5c8..d669e6c3 100644 --- a/index.html +++ b/index.html @@ -1,12 +1,12 @@ - + - + todo-list
- + diff --git a/src/App.css b/src/App.css new file mode 100644 index 00000000..d1ace32b --- /dev/null +++ b/src/App.css @@ -0,0 +1,63 @@ +body { + background-color: #fce4ec; +} + +.inner { + width: 600px; + margin: 0 auto; +} + +h1 { + font-size: 70px; + font-family: "Helvetica Neue", "Helvetica", "Arial", "sans-serif"; + font-weight: 300; + text-align: center; +} + +input#todoInput { + display: block; + width: 400px; + height: 60px; + padding: 0 50px; + margin: 0 auto; + font-size: 25px; +} + +ul { + width: 500px; + margin: 20px auto; + padding: 0; +} + +li { + display: flex; + align-items: center; + position: relative; + border-bottom: 1px solid black; + list-style: none; + padding: 15px 0px 15px 20px; +} + +li:last-child { + border: none; +} + +li .todo { + position: absolute; + width: 350px; + margin: auto 70px; + font-size: 25px; + font-weight: 200; + flex-grow: 1; +} + +li img { + width: 50px; + height: 50px; + margin-left: auto; +} + +.completed .todo { + text-decoration: line-through; + text-decoration-color: #f06292; +} diff --git a/src/App.js b/src/App.js new file mode 100644 index 00000000..62d9ea5c --- /dev/null +++ b/src/App.js @@ -0,0 +1,6 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import Main from './main'; + +const root = ReactDOM.createRoot(document.getElementById('app')); +root.render(React.createElement(Main)); \ No newline at end of file diff --git a/src/components/CheckBox.css b/src/components/CheckBox.css new file mode 100644 index 00000000..f9dd45f3 --- /dev/null +++ b/src/components/CheckBox.css @@ -0,0 +1,5 @@ +input#checkbox { + width: 50px; + height: 50px; + margin: 0; +} diff --git a/src/components/CheckBox.jsx b/src/components/CheckBox.jsx new file mode 100644 index 00000000..41ad5c1d --- /dev/null +++ b/src/components/CheckBox.jsx @@ -0,0 +1,14 @@ +import "./CheckBox.css"; + +const CheckBox = ({ isChecked, onChange }) => { + return ( + + ); +}; + +export default CheckBox; diff --git a/src/components/DeleteButton.jsx b/src/components/DeleteButton.jsx new file mode 100644 index 00000000..8888e317 --- /dev/null +++ b/src/components/DeleteButton.jsx @@ -0,0 +1,18 @@ +import deleteImg from "../img/deleteImg.png"; +import deleteTodo from "../utils/deleteTodo"; + +function DeleteButton({index, todos, setTodos}) { + const handleClick = () => { + deleteTodo(index, todos, setTodos); + }; + + return( + delete + ) +} + +export default DeleteButton; \ No newline at end of file diff --git a/src/components/Input.jsx b/src/components/Input.jsx new file mode 100644 index 00000000..10424e3f --- /dev/null +++ b/src/components/Input.jsx @@ -0,0 +1,14 @@ +function Input({ value, onChange, onKeyPress }) { + return ( + + ); +} + +export default Input; diff --git a/src/components/TodoList.jsx b/src/components/TodoList.jsx new file mode 100644 index 00000000..b85fe05b --- /dev/null +++ b/src/components/TodoList.jsx @@ -0,0 +1,25 @@ +import { useState } from "react"; +import CheckBox from "./CheckBox"; +import toggleTodo from "../utils/toggleTodo"; +import DeleteButton from "./DeleteButton"; +import deleteTodo from "../utils/deleteTodo"; + +const TodoList = ({ todos, setTodos}) => { + const [completedTodos, setCompletedTodos] = useState([]); + + return ( + + ); +}; + +export default TodoList; diff --git a/src/img/deleteImg.png b/src/img/deleteImg.png new file mode 100644 index 00000000..db96e100 Binary files /dev/null and b/src/img/deleteImg.png differ diff --git a/src/main.js b/src/main.js deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 00000000..7760e662 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,27 @@ +import React, { useState } from "react"; +import "./App.css"; +import Input from "./components/Input.jsx"; +import TodoList from "./components/TodoList.jsx"; +import handleInputChange from "./utils/handleInputChange.jsx"; +import handleKeyPress from "./utils/handleKeyPress.jsx"; +import addTodo from "./utils/addTodo.jsx"; + +function Main() { + const [todos, setTodos] = useState([]); + const [inputValue, setInputValue] = useState(""); + return ( +
+

🎀 todos

+ handleInputChange(e, setInputValue)} + onKeyPress={(e) => + handleKeyPress(e, todos, setTodos, inputValue, setInputValue) + } + /> + +
+ ); +} + +export default Main; diff --git a/src/utils/addTodo.jsx b/src/utils/addTodo.jsx new file mode 100644 index 00000000..53e75827 --- /dev/null +++ b/src/utils/addTodo.jsx @@ -0,0 +1,6 @@ +const addTodo = (todos, inputValue) => { + if (inputValue.trim() === "") return todos; + return [...todos, inputValue]; +}; + +export default addTodo; \ No newline at end of file diff --git a/src/utils/deleteTodo.jsx b/src/utils/deleteTodo.jsx new file mode 100644 index 00000000..b36a77f3 --- /dev/null +++ b/src/utils/deleteTodo.jsx @@ -0,0 +1,5 @@ +const deleteTodo = (index, todos, setTodos) => { + setTodos(todos.filter((_, i) => i !== index)); +}; + +export default deleteTodo; diff --git a/src/utils/handleInputChange.jsx b/src/utils/handleInputChange.jsx new file mode 100644 index 00000000..96ea24e9 --- /dev/null +++ b/src/utils/handleInputChange.jsx @@ -0,0 +1,5 @@ +const handleInputChange = (e, setInputValue) => { + setInputValue(e.target.value); +}; + +export default handleInputChange; diff --git a/src/utils/handleKeyPress.jsx b/src/utils/handleKeyPress.jsx new file mode 100644 index 00000000..6e217906 --- /dev/null +++ b/src/utils/handleKeyPress.jsx @@ -0,0 +1,12 @@ +const handleKeyPress = (e, todos, setTodos, inputValue, setInputValue) => { + if (e.key === "Enter") { + e.preventDefault(); + const newTodo = inputValue.trim(); // 입력된 값의 앞뒤 공백 제거 + if (newTodo !== "") { //입력된 값이 공백이 아닌 경우에만 + setTodos((prevTodos) => [...prevTodos, newTodo]); // 이전 할 일에 새로운 할 일 추가하여 리스트 업데이트 + setInputValue(""); // 입력값 초기화 + } + } +}; + +export default handleKeyPress; diff --git a/src/utils/toggleTodo.jsx b/src/utils/toggleTodo.jsx new file mode 100644 index 00000000..b79312d5 --- /dev/null +++ b/src/utils/toggleTodo.jsx @@ -0,0 +1,11 @@ +const toggleTodo = (index, completedTodos, setCompletedTodos) => { + //index의 할 일이 이미 완료된 할 일인지 확인 + const isCompleted = completedTodos.includes(index); + if (isCompleted) { + setCompletedTodos(completedTodos.filter((item) => item !== index)); + } else { + setCompletedTodos([...completedTodos, index]); + } +}; + +export default toggleTodo; diff --git a/vite.config.ts b/vite.config.ts index 5a33944a..d1fa1aef 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,6 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; -// https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], -}) + plugins: [react({})], +});