diff --git a/README.md b/README.md index 3c0710d2..ff82efd9 100644 --- a/README.md +++ b/README.md @@ -1 +1,33 @@ -# react-todo-list-precourse \ No newline at end of file +# react-todo-list-precourse + +## 할 일 목록 + +하루 또는 한 주의 할 일 목록을 업데이트하는 할 일 목록을 구현한다. + +### 기능 설명 + +- 사용자는 할 일을 입력한다. +- 추가 버튼 클릭, 혹은 엔터 키 클릭으로 할 일을 추가한다.(아무것도 입력하지 않았을 경우 추가 x) +- 사용자가 추가한 할 일의 목록을 볼 수 있다. +- 할 일의 완료 상태를 전활할 수 있다. + +### 선택 기능 설명 + +- 현재 진행 중인 할 일, 완료된 할 일, 모든 할 일을 필터링할 수 있다. +- 해야 할 일의 총개수를 확인할 수 있다. +- 새로고침을 하여도 이전에 작성한 데이터는 유지되어야 한다. + +### 에러 처리 + +사용자가 아무것도 입력하지 않았을 경우
+-> 추가 버튼 클릭 비활성화 및 엔터 키 비활성화 + +### 기술 스택 + +HTML, CSS, JavaScript, React + +### 컴포넌트 분리 + +- 할 일 입력 +- 할 일 목록 +- 필터링 버튼 \ No newline at end of file diff --git a/index.html b/index.html index b021b5c8..29923517 100644 --- a/index.html +++ b/index.html @@ -1,12 +1,15 @@ - - - - - - -
- - - + + + + + + + + +
+ + + + \ No newline at end of file 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/newTodo/ButtonSubmit.js b/src/components/newTodo/ButtonSubmit.js new file mode 100644 index 00000000..a90adbbc --- /dev/null +++ b/src/components/newTodo/ButtonSubmit.js @@ -0,0 +1,8 @@ +import { useCallback } from 'react'; + +export const onButtonSubmitHandler = (texting, setNewTodo, setTexting) => useCallback(() => { + if (texting) { + setNewTodo(texting); + setTexting(''); + } +}, [texting, setNewTodo]); \ No newline at end of file diff --git a/src/components/newTodo/ChangeTexting.js b/src/components/newTodo/ChangeTexting.js new file mode 100644 index 00000000..7e2199c1 --- /dev/null +++ b/src/components/newTodo/ChangeTexting.js @@ -0,0 +1,5 @@ +import { useCallback } from 'react'; + +export const onChangeText = (setTexting) => useCallback((e) => { + setTexting(e.target.value); +}, []); \ No newline at end of file diff --git a/src/components/newTodo/EnterSubmit.js b/src/components/newTodo/EnterSubmit.js new file mode 100644 index 00000000..736f35c2 --- /dev/null +++ b/src/components/newTodo/EnterSubmit.js @@ -0,0 +1,8 @@ +import { useCallback } from 'react'; + +export const onEnterSubmitHandler = (texting, setNewTodo, setTexting) => useCallback((e) => { + if (e.key === 'Enter' && texting) { + setNewTodo(texting); + setTexting(''); + } +}, [texting, setNewTodo]); \ No newline at end of file diff --git a/src/components/newTodo/NewTodo.jsx b/src/components/newTodo/NewTodo.jsx new file mode 100644 index 00000000..9adcc3e0 --- /dev/null +++ b/src/components/newTodo/NewTodo.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import './newTodo.styles.css'; +import { Texting } from './Texting'; + +const NewTodo = ({ setNewTodo }) => { + const { texting, onChangeText, onEnterSubmitHandler, onButtonSubmitHandler } = Texting(setNewTodo); + + return ( +
+ + {texting && } +
+ ); +} + +export default NewTodo; \ No newline at end of file diff --git a/src/components/newTodo/Texting.js b/src/components/newTodo/Texting.js new file mode 100644 index 00000000..b923c4a1 --- /dev/null +++ b/src/components/newTodo/Texting.js @@ -0,0 +1,14 @@ +import { useState } from 'react'; +import { onChangeText } from './ChangeTexting'; +import { onEnterSubmitHandler } from './EnterSubmit'; +import { onButtonSubmitHandler } from './ButtonSubmit'; + +export const Texting = (setNewTodo) => { + const [texting, setTexting] = useState(''); + + const changeText = onChangeText(setTexting); + const enterSubmitHandler = onEnterSubmitHandler(texting, setNewTodo, setTexting); + const buttonSubmitHandler = onButtonSubmitHandler(texting, setNewTodo, setTexting); + + return { texting, onChangeText: changeText, onEnterSubmitHandler: enterSubmitHandler, onButtonSubmitHandler: buttonSubmitHandler }; +}; \ No newline at end of file diff --git a/src/components/newTodo/newTodo.styles.css b/src/components/newTodo/newTodo.styles.css new file mode 100644 index 00000000..07a5b4e1 --- /dev/null +++ b/src/components/newTodo/newTodo.styles.css @@ -0,0 +1,17 @@ +.newtodo { + width: 100vw; + text-align: center; + height: 50px; +} + +.newtodo input { + padding: 10px 20px; + font-size: 15px; + border: 1.7px solid rgb(167, 44, 54); + border-radius: 10px; +} + +.newtodo button { + margin-left: 10px; + padding: 5px 10px; +} \ No newline at end of file diff --git a/src/components/todoList/DeleteButton.js b/src/components/todoList/DeleteButton.js new file mode 100644 index 00000000..33b4de4d --- /dev/null +++ b/src/components/todoList/DeleteButton.js @@ -0,0 +1,3 @@ +export const onDeleteButtonHandler = (todoList, setTodoList, idx) => { + setTodoList(todoList.filter((_, index) => index !== idx)); +}; \ No newline at end of file diff --git a/src/components/todoList/TodoList.jsx b/src/components/todoList/TodoList.jsx new file mode 100644 index 00000000..8934b850 --- /dev/null +++ b/src/components/todoList/TodoList.jsx @@ -0,0 +1,26 @@ +import React, { useState, useEffect } from "react"; +import './todoList.styles.css'; +import { toggleDone } from "./ToggleDone"; +import { onDeleteButtonHandler } from "./DeleteButton"; + +const TodoList = ({ todoList, setTodoList }) => { + const [doneStatus, setDoneStatus] = useState(Array(todoList.length).fill(false)); + + useEffect(() => { + setDoneStatus(Array(todoList.length).fill(false)); + }, [todoList]); + + return ( +
+ {todoList ? todoList.map((todo, idx) => ( +
+ {todo} + + +
+ )) : <>} +
+ ); +} + +export default TodoList; \ No newline at end of file diff --git a/src/components/todoList/ToggleDone.js b/src/components/todoList/ToggleDone.js new file mode 100644 index 00000000..08a6c84f --- /dev/null +++ b/src/components/todoList/ToggleDone.js @@ -0,0 +1,3 @@ +export const toggleDone = (doneStatus, setDoneStatus, idx) => { + setDoneStatus(doneStatus.map((status, index) => idx === index ? !status : status)); +}; \ No newline at end of file diff --git a/src/components/todoList/todoList.styles.css b/src/components/todoList/todoList.styles.css new file mode 100644 index 00000000..63ab0e03 --- /dev/null +++ b/src/components/todoList/todoList.styles.css @@ -0,0 +1,25 @@ +.todolist-boxs button { + margin-left: 10px; +} + +.todolist-boxs-done button { + margin-left: 10px; +} + +.todolist-boxs-done button:nth-child(1) { + color: #ccc; +} + +.todolist-boxs { + padding: 10px; + border: 1px solid #ccc; + margin: 5px 0; +} + +.todolist-boxs-done { + padding: 10px; + border: 1px solid #ccc; + margin: 5px 0; + text-decoration: line-through; + color: #ccc; +} \ No newline at end of file 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..f23e43d8 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,21 @@ +import React, { useState, useEffect } from "react"; +import NewTodo from "./components/newTodo/NewTodo"; +import TodoList from "./components/todoList/TodoList"; + +const Main = () => { + const [newTodo, setNewTodo] = useState(''); + const [todoList, setTodoList] = useState([]); + useEffect(() => { + newTodo ? setTodoList([...todoList, newTodo]) : ''; + setNewTodo(''); + }, [newTodo]) + return ( +
+

todos

+ + +
+ ); +} + +export default Main; \ No newline at end of file