diff --git a/README.md b/README.md index 3c0710d2..ffca5a25 100644 --- a/README.md +++ b/README.md @@ -1 +1,11 @@ -# react-todo-list-precourse \ No newline at end of file +# react-todo-list-precourse + +1. 할 일 추가 및 삭제 (Enter키나 추가 버튼) +2. todos 관리를 위한 util 함수 작성(추가, 삭제, 체크, 필터링) +3. todoList 구성 +4. 할 일 완료 checkbox 구현 +5. 현재 진행 중인 할 일, 완료된 할 일, 모든 할 일 필터링 기능 +6. footer 구현 +7. 디자인 변경 +8. 새로고침 시 작성한 데이터 유지 +9. 컴포넌트 리팩토링 및 코드 스타일 수정 (하나의 기능, 15줄 넘지 않게 작성) diff --git a/index.html b/index.html index b021b5c8..76aa23f7 100644 --- a/index.html +++ b/index.html @@ -1,12 +1,16 @@ - + - + + Hyoeun TodoList -
- +
+ diff --git a/src/App.js b/src/App.js new file mode 100644 index 00000000..c5f7e962 --- /dev/null +++ b/src/App.js @@ -0,0 +1,6 @@ +import React from "react"; +import { createRoot } from "react-dom/client"; +import Main from "./main"; + +const root = createRoot(document.getElementById("root")); +root.render(React.createElement(Main)); \ No newline at end of file diff --git a/src/components/form/CheckBox.jsx b/src/components/form/CheckBox.jsx new file mode 100644 index 00000000..c8da64e3 --- /dev/null +++ b/src/components/form/CheckBox.jsx @@ -0,0 +1,18 @@ +import { checkTodo } from "../../util/todoUtilFunc"; +import "../../style/CheckBox.css"; +const CheckBox = ({ className, checked, handleCheck }) => { + return ( +
+ + +
+ ); +}; +export default CheckBox; diff --git a/src/components/form/Filter.jsx b/src/components/form/Filter.jsx new file mode 100644 index 00000000..e6f2988c --- /dev/null +++ b/src/components/form/Filter.jsx @@ -0,0 +1,13 @@ +const Filter = ({ text, filter, setFilter }) => { + return ( +
  • setFilter(text)} + style={{ + borderColor: filter === text ? "rgb(56, 56, 180)" : null, + }} + > + {text} +
  • + ); +}; +export default Filter; diff --git a/src/components/form/FilterContainer.jsx b/src/components/form/FilterContainer.jsx new file mode 100644 index 00000000..a100b058 --- /dev/null +++ b/src/components/form/FilterContainer.jsx @@ -0,0 +1,13 @@ +import Filter from "./Filter"; +import "../../style/Filter.css"; + +const FilterContainer = ({ filter, setFilter }) => { + return ( + + ); +}; +export default FilterContainer; diff --git a/src/components/form/ListBottom.jsx b/src/components/form/ListBottom.jsx new file mode 100644 index 00000000..6fe85b88 --- /dev/null +++ b/src/components/form/ListBottom.jsx @@ -0,0 +1,15 @@ +import FilterContainer from "./FilterContainer"; +import { activeNum, handleClear } from "../../util/todoUtilFunc"; +import "../../style/ListBottom.css"; +const ListBottom = ({ todos, setTodos, filter, setFilter }) => { + return ( +
    + {activeNum(todos)}개 남음! + + +
    + ); +}; +export default ListBottom; diff --git a/src/components/form/ListItem.jsx b/src/components/form/ListItem.jsx new file mode 100644 index 00000000..83a1436f --- /dev/null +++ b/src/components/form/ListItem.jsx @@ -0,0 +1,19 @@ +import React from "react"; +import CheckBox from "./CheckBox"; +import { checkTodo, deleteTodo } from "../../util/todoUtilFunc"; +import "../../style/ListItem.css"; + +const ListItem = ({ todos, setTodos, todo }) => { + return ( +
  • + checkTodo(todos, setTodos, todo.id)} + /> + {todo.text} + +
  • + ); +}; +export default ListItem; diff --git a/src/components/main/Footer.jsx b/src/components/main/Footer.jsx new file mode 100644 index 00000000..2cf4abe7 --- /dev/null +++ b/src/components/main/Footer.jsx @@ -0,0 +1,13 @@ +import "../../style/Footer.css"; + +const Footer = () => { + return ( +
    +

    Double-click edit a todo

    +

    Creted by Hyoeun Kookie

    +

    Part of KakaoTechCampus

    +
    + ); +}; + +export default Footer; diff --git a/src/components/main/Input.jsx b/src/components/main/Input.jsx new file mode 100644 index 00000000..4db9c91f --- /dev/null +++ b/src/components/main/Input.jsx @@ -0,0 +1,22 @@ +import React, { useRef, useState } from "react"; +import { useInputTodo } from "../../hooks/useInputTodo"; +import "../../style/Input.css"; + +const Input = ({ todos, setTodos }) => { + const { handleText, text, onChange } = useInputTodo({ todos, setTodos }); + const inputRef = useRef(); + return ( +
    + { + if (e.key === "Enter") handleText(inputRef.current.value); + }} + /> + +
    + ); +}; + +export default Input; diff --git a/src/components/main/Todo.jsx b/src/components/main/Todo.jsx new file mode 100644 index 00000000..d24ebb8c --- /dev/null +++ b/src/components/main/Todo.jsx @@ -0,0 +1,24 @@ +import { useState } from "react"; +import { allCheckTodo, filterTodos } from "../../util/todoUtilFunc"; +import CheckBox from "../form/CheckBox"; +import ListItem from "../form/ListItem"; +import ListBottom from "../form/ListBottom"; +import "../../style/Todo.css"; + +export default function Todo({ todos, setTodos }) { + const [filter, setFilter] = useState("All"); + const filtered = filterTodos(todos, filter); + if (todos.length === 0) return null; + return ( +
    + todo.done)} + handleCheck={(e) => allCheckTodo(todos, setTodos, e.target.checked)} + /> + + +
    + ); +} diff --git a/src/hooks/useInputTodo.jsx b/src/hooks/useInputTodo.jsx new file mode 100644 index 00000000..06e92650 --- /dev/null +++ b/src/hooks/useInputTodo.jsx @@ -0,0 +1,20 @@ +import { useState, useCallback, useEffect } from "react"; +import { addTodo } from "../util/todoUtilFunc"; + +export const useInputTodo = ({ todos, setTodos }) => { + const [text, setText] = useState(""); + + const handleText = useCallback( + (text) => { + if (text === "") return; + addTodo(todos, setTodos, text); + setText(""); + }, + [todos, setTodos] + ); + const onChange = (e) => { + setText(e.target.value); + }; + + return { handleText, onChange, text }; +}; 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..8a80a1a3 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,22 @@ +import { useEffect, useState } from "react"; +import Input from "./components/main/Input"; +import Todo from "./components/main/Todo"; +import Footer from "./components/main/Footer"; +import "./style/Global.css"; + +export default function Main() { + const [todos, setTodos] = useState(JSON.parse(localStorage.getItem("todos"))); + useEffect(() => { + localStorage.setItem("todos", JSON.stringify(todos)); + }, [todos]); + return ( + <> +
    +

    Hyo's Todo

    + + +
    +