From 85d9d1f1b3a01aabe9e67db3dc5a5518157ef016 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Jun 2024 07:13:58 +0900 Subject: [PATCH 01/12] docs: add README --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c0710d2..88709043 100644 --- a/README.md +++ b/README.md @@ -1 +1,9 @@ -# react-todo-list-precourse \ No newline at end of file +# react-todo-list-precourse + +1. 기본구조 작성 및 스타일 적용 +2. 할 일 추가하기 기능 구현 +3. 할 일 삭제하기 기능 구현 +4. 할 일 완료 상태 전환하기 구현 +5. 필터링 기능 구현 +6. 남은 할 일 개수 표시 구현 +7. 새로고침 시 작성한 데이터 유지 \ No newline at end of file From cb0b8c0440702e56cef7f6d57364361d6ce91f34 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Jun 2024 07:19:30 +0900 Subject: [PATCH 02/12] style: add basic format & style --- index.html | 4 +- src/App.jsx | 19 +++++ src/components/footer.jsx | 16 +++++ src/components/header.jsx | 14 ++++ src/components/todoelement.jsx | 13 ++++ src/components/todolist.jsx | 13 ++++ src/index.css | 124 +++++++++++++++++++++++++++++++++ src/main.js | 0 src/main.jsx | 11 +++ 9 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 src/App.jsx create mode 100644 src/components/footer.jsx create mode 100644 src/components/header.jsx create mode 100644 src/components/todoelement.jsx create mode 100644 src/components/todolist.jsx create mode 100644 src/index.css delete mode 100644 src/main.js create mode 100644 src/main.jsx diff --git a/index.html b/index.html index b021b5c8..82b6987d 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,7 @@ -
- +
+ diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 00000000..69376cdf --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,19 @@ +import React from 'react'; +import Header from './components/header.jsx'; +import TodoList from './components/todolist.jsx'; +import Footer from './components/footer.jsx'; + +function App() { + return ( +
+

TODO

+
+
+ +
+
+
+ ); +} + +export default App; \ No newline at end of file diff --git a/src/components/footer.jsx b/src/components/footer.jsx new file mode 100644 index 00000000..49ae942d --- /dev/null +++ b/src/components/footer.jsx @@ -0,0 +1,16 @@ +import React from 'react'; + +function Footer() { + return ( +
+
X items left
+
+ + + +
+
+ ); +} + +export default Footer; \ No newline at end of file diff --git a/src/components/header.jsx b/src/components/header.jsx new file mode 100644 index 00000000..a52149f2 --- /dev/null +++ b/src/components/header.jsx @@ -0,0 +1,14 @@ +import React from 'react'; + +function Header() { + return ( +
+
+ + +
+
+ ); +} + +export default Header; \ No newline at end of file diff --git a/src/components/todoelement.jsx b/src/components/todoelement.jsx new file mode 100644 index 00000000..f85c2704 --- /dev/null +++ b/src/components/todoelement.jsx @@ -0,0 +1,13 @@ +import React from 'react'; + +function TodoElement(props) { + return ( +
+ +

내용

+ +
+ ); +} + +export default TodoElement; \ No newline at end of file diff --git a/src/components/todolist.jsx b/src/components/todolist.jsx new file mode 100644 index 00000000..dd66bf5b --- /dev/null +++ b/src/components/todolist.jsx @@ -0,0 +1,13 @@ +import React from 'react'; +import TodoElement from './todoelement.jsx'; + +function TodoList() { + return ( +
+ + +
+ ); +} + +export default TodoList; \ No newline at end of file diff --git a/src/index.css b/src/index.css new file mode 100644 index 00000000..5f273293 --- /dev/null +++ b/src/index.css @@ -0,0 +1,124 @@ +body { + background-color: aliceblue; + display: flex; + justify-content: center; + color: #707070; +} +h1 { + text-align: center; + color:midnightblue; +} + +button { + background-color: transparent; + border: 0; + color: #707070;; +} + +.app { + width: 700px; + font-size: 25px; +} +@media screen and (max-width: 710px) { + .app { + width: 90vw; + font-size: 25px; + } +} + +.app .mainframe { + background-color: white; + width: 100%; + border: 1px solid; + border-color: lightslategray; +} + +.app .mainframe .header { + width: 100%; + border-bottom: 1px solid; + border-color: lightslategray; +} +.app .mainframe .header .inputform { + width: 100%; + display: flex; +} +.app .mainframe .header .inputtext { + font-size: 25px; + flex: 1 0 auto; + outline: none; + border-width: 0; + margin-left: 10px; +} +.app .mainframe .header .inputbtn { + font-size: 35px; + width: 45px; + height: auto; + border: 0; + color: #707070; +} +.app .mainframe .header .inputbtn:hover { + background-color: dodgerblue; + color:white; +} + +.app .mainframe .todolist { + +} +.app .mainframe .todolist .todoelement { + width: 100%; + height: auto; + display: flex; + align-items: center; +} +.app .mainframe .todolist .todoelement:hover { + background-color: azure; + color: black; +} +.app .mainframe .todolist .todoelement input[type="checkbox"] { + margin: 15px; + display: inline-block; + width: 30px; + height: 30px; + border:3px solid #707070; + position: relative; +} +.app .mainframe .todolist .todoelement p { + +} +.app .mainframe .todolist .todoelement button { + font-size: 20px; + width: 50px; + height: 60px; + float: right; + margin-left: auto; + margin-right: 15px; + color: transparent; +} +.app .mainframe .todolist .todoelement button:hover { + color:dodgerblue; +} + +.app .mainframe .footer { + display: flex; + font-size: 15px; + width: 100%; + border-top: 1px solid; + border-color: lightslategray; +} +.app .mainframe .footer .itemleft { + margin-left: 15px; + height: 40px; + display: flex; + align-items: center; +} +.app .mainframe .footer .filter { + margin-left: auto; +} +.app .mainframe .footer .filter button { + width: 100px; + height: 100%; +} +.app .mainframe .footer .filter button:hover { + color:dodgerblue; + font-weight: bold; +} \ 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..0f664110 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App.jsx'; +import './index.css'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); \ No newline at end of file From af0487a799b0b2d99fb58990adf3fcede9b9cf0b Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Jun 2024 07:21:43 +0900 Subject: [PATCH 03/12] feat: add Todo --- src/App.jsx | 9 ++++++--- src/components/header.jsx | 23 ++++++++++++++++++----- src/components/todoelement.jsx | 20 ++++++++++++++++++-- src/components/todolist.jsx | 13 ++++++++++--- src/hooks/useTodo.jsx | 13 +++++++++++++ src/index.css | 3 +++ 6 files changed, 68 insertions(+), 13 deletions(-) create mode 100644 src/hooks/useTodo.jsx diff --git a/src/App.jsx b/src/App.jsx index 69376cdf..02fff503 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,15 +1,18 @@ -import React from 'react'; +import React, { useState } from 'react'; import Header from './components/header.jsx'; import TodoList from './components/todolist.jsx'; import Footer from './components/footer.jsx'; +import UseTodoState from './hooks/useTodo.jsx'; function App() { + const {todos, addTodo} = UseTodoState(); + return (

TODO

-
- +
+
diff --git a/src/components/header.jsx b/src/components/header.jsx index a52149f2..6ade9eb6 100644 --- a/src/components/header.jsx +++ b/src/components/header.jsx @@ -1,12 +1,25 @@ -import React from 'react'; +import React, { useState } from 'react'; -function Header() { +function makeForm(content, setContent) { return ( -
-
- + + setContent(e.target.value)} required/>
+ ) +} + +function Header({addTodo}) { + const [content, setContent] = useState(""); + const handleSubmit = e => { + e.preventDefault(); + addTodo(content); + setContent(""); + }; + + return ( +
+ { makeForm(content, setContent) }
); } diff --git a/src/components/todoelement.jsx b/src/components/todoelement.jsx index f85c2704..6c6c9d0c 100644 --- a/src/components/todoelement.jsx +++ b/src/components/todoelement.jsx @@ -1,10 +1,26 @@ import React from 'react'; +function makeCheckbox(todo) { + if (todo.isCompleted) { + return () + } else { + return () + } +} + +function makeContent(todo) { + if (todo.isCompleted) { + return (

{ todo.content }

) + } else { + return (

{ todo.content }

) + } +} + function TodoElement(props) { return (
- -

내용

+ { makeCheckbox(props.todo) } + { makeContent(props.todo) }
); diff --git a/src/components/todolist.jsx b/src/components/todolist.jsx index dd66bf5b..a50ac910 100644 --- a/src/components/todolist.jsx +++ b/src/components/todolist.jsx @@ -1,11 +1,18 @@ import React from 'react'; import TodoElement from './todoelement.jsx'; -function TodoList() { +function makelist(todos) { + return ( + todos.map(function(element, index) { + return ( ) + }) + ) +} + +function TodoList(props) { return (
- - + { makelist(props.todos) }
); } diff --git a/src/hooks/useTodo.jsx b/src/hooks/useTodo.jsx new file mode 100644 index 00000000..c0ea2d34 --- /dev/null +++ b/src/hooks/useTodo.jsx @@ -0,0 +1,13 @@ +import { useState } from 'react'; + +const useTodoState = () => { + const [todos, setTodos] = useState([{'content':'test1', 'isCompleted':true}, {'content':'test2', 'isCompleted':false}]); + + const addTodo = (todo) => { + setTodos((prev) => [...prev, {'content':todo, 'isCompleted':false}]); + }; + + return { todos, addTodo }; +} + +export default useTodoState; \ No newline at end of file diff --git a/src/index.css b/src/index.css index 5f273293..c23bd751 100644 --- a/src/index.css +++ b/src/index.css @@ -84,6 +84,9 @@ button { } .app .mainframe .todolist .todoelement p { +} +.app .mainframe .todolist .todoelement .completed { + text-decoration: line-through; } .app .mainframe .todolist .todoelement button { font-size: 20px; From 6b646b21d7314dd81e81090cdf86fb5f63a00dfb Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Jun 2024 07:32:00 +0900 Subject: [PATCH 04/12] feat: delete Todo --- src/App.jsx | 4 ++-- src/components/todoelement.jsx | 6 +++++- src/components/todolist.jsx | 6 +++--- src/hooks/useTodo.jsx | 8 +++++++- src/index.css | 1 - 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 02fff503..ae3a784d 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -5,14 +5,14 @@ import Footer from './components/footer.jsx'; import UseTodoState from './hooks/useTodo.jsx'; function App() { - const {todos, addTodo} = UseTodoState(); + const { todos, addTodo, deleteTodo } = UseTodoState(); return (

TODO

- +
diff --git a/src/components/todoelement.jsx b/src/components/todoelement.jsx index 6c6c9d0c..3653d0a0 100644 --- a/src/components/todoelement.jsx +++ b/src/components/todoelement.jsx @@ -17,11 +17,15 @@ function makeContent(todo) { } function TodoElement(props) { + const handleClick = e => { + props.deleteTodo(props.key); + }; + return (
{ makeCheckbox(props.todo) } { makeContent(props.todo) } - +
); } diff --git a/src/components/todolist.jsx b/src/components/todolist.jsx index a50ac910..0af5d02f 100644 --- a/src/components/todolist.jsx +++ b/src/components/todolist.jsx @@ -1,10 +1,10 @@ import React from 'react'; import TodoElement from './todoelement.jsx'; -function makelist(todos) { +function makelist(todos, deleteTodo) { return ( todos.map(function(element, index) { - return ( ) + return ( ) }) ) } @@ -12,7 +12,7 @@ function makelist(todos) { function TodoList(props) { return (
- { makelist(props.todos) } + { makelist(props.todos, props.deleteTodo) }
); } diff --git a/src/hooks/useTodo.jsx b/src/hooks/useTodo.jsx index c0ea2d34..f6142f40 100644 --- a/src/hooks/useTodo.jsx +++ b/src/hooks/useTodo.jsx @@ -7,7 +7,13 @@ const useTodoState = () => { setTodos((prev) => [...prev, {'content':todo, 'isCompleted':false}]); }; - return { todos, addTodo }; + const deleteTodo = (index) => { + var prev = [...todos]; + prev.splice(index, 1); + setTodos(prev); + }; + + return { todos, addTodo, deleteTodo }; } export default useTodoState; \ No newline at end of file diff --git a/src/index.css b/src/index.css index c23bd751..5a5345b8 100644 --- a/src/index.css +++ b/src/index.css @@ -95,7 +95,6 @@ button { float: right; margin-left: auto; margin-right: 15px; - color: transparent; } .app .mainframe .todolist .todoelement button:hover { color:dodgerblue; From 0d33f3a87a5b084d9664b414a7064956b0a4f179 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Jun 2024 07:58:02 +0900 Subject: [PATCH 05/12] fix: delete Todo --- src/App.jsx | 4 ++-- src/components/todoelement.jsx | 14 +++++++++----- src/components/todolist.jsx | 8 ++++---- src/hooks/useTodo.jsx | 18 +++++++++++------- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index ae3a784d..705b4c6c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -5,14 +5,14 @@ import Footer from './components/footer.jsx'; import UseTodoState from './hooks/useTodo.jsx'; function App() { - const { todos, addTodo, deleteTodo } = UseTodoState(); + const { todos, addTodo, deleteTodo, changeCompleted } = UseTodoState(); return (

TODO

- +
diff --git a/src/components/todoelement.jsx b/src/components/todoelement.jsx index 3653d0a0..1bb6fe54 100644 --- a/src/components/todoelement.jsx +++ b/src/components/todoelement.jsx @@ -1,10 +1,14 @@ import React from 'react'; -function makeCheckbox(todo) { +function makeCheckbox(todo, changeCompleted) { + const handleClick = e => { + // changeCompleted(); + }; + if (todo.isCompleted) { - return () + return () } else { - return () + return () } } @@ -18,12 +22,12 @@ function makeContent(todo) { function TodoElement(props) { const handleClick = e => { - props.deleteTodo(props.key); + props.deleteTodo(props.todo.id); }; return (
- { makeCheckbox(props.todo) } + { makeCheckbox(props.todo, props.changeCompleted) } { makeContent(props.todo) }
diff --git a/src/components/todolist.jsx b/src/components/todolist.jsx index 0af5d02f..ed208620 100644 --- a/src/components/todolist.jsx +++ b/src/components/todolist.jsx @@ -1,10 +1,10 @@ import React from 'react'; import TodoElement from './todoelement.jsx'; -function makelist(todos, deleteTodo) { +function makelist(elementProps) { return ( - todos.map(function(element, index) { - return ( ) + elementProps.todos.map(function(element) { + return ( ) }) ) } @@ -12,7 +12,7 @@ function makelist(todos, deleteTodo) { function TodoList(props) { return (
- { makelist(props.todos, props.deleteTodo) } + { makelist(props) }
); } diff --git a/src/hooks/useTodo.jsx b/src/hooks/useTodo.jsx index f6142f40..6f522a60 100644 --- a/src/hooks/useTodo.jsx +++ b/src/hooks/useTodo.jsx @@ -1,19 +1,23 @@ import { useState } from 'react'; const useTodoState = () => { - const [todos, setTodos] = useState([{'content':'test1', 'isCompleted':true}, {'content':'test2', 'isCompleted':false}]); + const [todos, setTodos] = useState([]); + const [next, setNext] = useState(0); const addTodo = (todo) => { - setTodos((prev) => [...prev, {'content':todo, 'isCompleted':false}]); + setNext(next+1); + setTodos((prev) => [...prev, {'content':todo, 'isCompleted':false, 'id':next}]); }; - const deleteTodo = (index) => { - var prev = [...todos]; - prev.splice(index, 1); - setTodos(prev); + const deleteTodo = (target) => { + setTodos((prev) => prev.filter((todo) => todo.id !== target)); }; - return { todos, addTodo, deleteTodo }; + const changeCompleted = (target) => { + + } + + return { todos, addTodo, deleteTodo, changeCompleted }; } export default useTodoState; \ No newline at end of file From 1a29b69496b3428f4f496015e2abbcdfd89e54a4 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Jun 2024 08:05:50 +0900 Subject: [PATCH 06/12] feat: change Todo status --- src/components/todoelement.jsx | 2 +- src/hooks/useTodo.jsx | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/todoelement.jsx b/src/components/todoelement.jsx index 1bb6fe54..1fe834ca 100644 --- a/src/components/todoelement.jsx +++ b/src/components/todoelement.jsx @@ -2,7 +2,7 @@ import React from 'react'; function makeCheckbox(todo, changeCompleted) { const handleClick = e => { - // changeCompleted(); + changeCompleted(todo.id); }; if (todo.isCompleted) { diff --git a/src/hooks/useTodo.jsx b/src/hooks/useTodo.jsx index 6f522a60..9e048820 100644 --- a/src/hooks/useTodo.jsx +++ b/src/hooks/useTodo.jsx @@ -6,7 +6,7 @@ const useTodoState = () => { const addTodo = (todo) => { setNext(next+1); - setTodos((prev) => [...prev, {'content':todo, 'isCompleted':false, 'id':next}]); + setTodos((prev) => [...prev, {content:todo, isCompleted:false, id:next}]); }; const deleteTodo = (target) => { @@ -14,7 +14,9 @@ const useTodoState = () => { }; const changeCompleted = (target) => { - + setTodos((prev) => prev.map((todo) => + todo.id === target ? {...todo, isCompleted:!todo.isCompleted} : todo + )); } return { todos, addTodo, deleteTodo, changeCompleted }; From 56b8409c95b16d08ad107ed5e1fa00af031de9ba Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Jun 2024 08:27:26 +0900 Subject: [PATCH 07/12] feat: add filter --- src/App.jsx | 5 +++-- src/components/footer.jsx | 11 +++++++---- src/components/todolist.jsx | 10 ++++++++-- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 705b4c6c..924de451 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -6,14 +6,15 @@ import UseTodoState from './hooks/useTodo.jsx'; function App() { const { todos, addTodo, deleteTodo, changeCompleted } = UseTodoState(); + const [filter, useFilter] = useState('All'); return (

TODO

- -
+ +
); diff --git a/src/components/footer.jsx b/src/components/footer.jsx index 49ae942d..dab9f119 100644 --- a/src/components/footer.jsx +++ b/src/components/footer.jsx @@ -1,13 +1,16 @@ import React from 'react'; -function Footer() { +function Footer(props) { + const handleClick = e => { + props.changeFilter(e.target.textContent); + }; return (
X items left
- - - + + +
); diff --git a/src/components/todolist.jsx b/src/components/todolist.jsx index ed208620..b5443767 100644 --- a/src/components/todolist.jsx +++ b/src/components/todolist.jsx @@ -2,11 +2,17 @@ import React from 'react'; import TodoElement from './todoelement.jsx'; function makelist(elementProps) { + var list = [...elementProps.todos]; + + if (elementProps.filter !== "All") { + const target = (elementProps.filter === "Active" ? true : false); + list = list.filter((todo) => todo.isCompleted !== target); + } return ( - elementProps.todos.map(function(element) { + list.map(function(element) { return ( ) }) - ) + ); } function TodoList(props) { From a05530156f7ba414d35242d69663ac972b31dc49 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Jun 2024 09:13:02 +0900 Subject: [PATCH 08/12] fix: filter & checkbox --- src/App.jsx | 2 +- src/components/footer.jsx | 17 ++++++++++++----- src/components/todoelement.jsx | 7 ++----- src/components/todolist.jsx | 2 +- src/index.css | 3 +++ 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 924de451..96274ca0 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -14,7 +14,7 @@ function App() {
-
+
); diff --git a/src/components/footer.jsx b/src/components/footer.jsx index dab9f119..4038a970 100644 --- a/src/components/footer.jsx +++ b/src/components/footer.jsx @@ -1,16 +1,23 @@ import React from 'react'; -function Footer(props) { +function makeFilter(filter, changeFilter) { + const filterName = ['All', 'Active', 'Completed']; const handleClick = e => { - props.changeFilter(e.target.textContent); + changeFilter(e.target.textContent); }; + return ( + filterName.map(function(element) { + return (element === filter ? : ) + }) + ); +} + +function Footer(props) { return (
X items left
- - - + { makeFilter(props.filter, props.changeFilter) }
); diff --git a/src/components/todoelement.jsx b/src/components/todoelement.jsx index 1fe834ca..526e9395 100644 --- a/src/components/todoelement.jsx +++ b/src/components/todoelement.jsx @@ -5,11 +5,8 @@ function makeCheckbox(todo, changeCompleted) { changeCompleted(todo.id); }; - if (todo.isCompleted) { - return () - } else { - return () - } + + return (); } function makeContent(todo) { diff --git a/src/components/todolist.jsx b/src/components/todolist.jsx index b5443767..2a566704 100644 --- a/src/components/todolist.jsx +++ b/src/components/todolist.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import TodoElement from './todoelement.jsx'; function makelist(elementProps) { diff --git a/src/index.css b/src/index.css index 5a5345b8..84d6acf2 100644 --- a/src/index.css +++ b/src/index.css @@ -120,6 +120,9 @@ button { width: 100px; height: 100%; } +.app .mainframe .footer .filter .choosen { + font-weight: bold; +} .app .mainframe .footer .filter button:hover { color:dodgerblue; font-weight: bold; From 36e50d6393fea3cd9dd57f2f84369527ddd2041f Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Jun 2024 09:19:36 +0900 Subject: [PATCH 09/12] feat: print active Todo number --- src/App.jsx | 7 ++++++- src/components/footer.jsx | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 96274ca0..21f97fc8 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -4,6 +4,11 @@ import TodoList from './components/todolist.jsx'; import Footer from './components/footer.jsx'; import UseTodoState from './hooks/useTodo.jsx'; +function activeLen(todos) { + const activeTodos = todos.filter((todo) => todo.isCompleted == false); + return activeTodos.length; +} + function App() { const { todos, addTodo, deleteTodo, changeCompleted } = UseTodoState(); const [filter, useFilter] = useState('All'); @@ -14,7 +19,7 @@ function App() {
-
+
); diff --git a/src/components/footer.jsx b/src/components/footer.jsx index 4038a970..2cbfc1c0 100644 --- a/src/components/footer.jsx +++ b/src/components/footer.jsx @@ -15,7 +15,7 @@ function makeFilter(filter, changeFilter) { function Footer(props) { return (
-
X items left
+
{props.todoLeft} items left
{ makeFilter(props.filter, props.changeFilter) }
From 8a35d846860ad41e011e6c9b8405d497fc0cc3c6 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Jun 2024 09:58:50 +0900 Subject: [PATCH 10/12] feat: save at localstorage --- src/App.jsx | 8 ++++++-- src/hooks/useTodo.jsx | 7 ++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 21f97fc8..022eebbf 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import Header from './components/header.jsx'; import TodoList from './components/todolist.jsx'; import Footer from './components/footer.jsx'; @@ -10,9 +10,13 @@ function activeLen(todos) { } function App() { - const { todos, addTodo, deleteTodo, changeCompleted } = UseTodoState(); + const { todos, setTodos, addTodo, deleteTodo, changeCompleted } = UseTodoState(); const [filter, useFilter] = useState('All'); + useEffect(() => { + localStorage.setItem("todoItem", JSON.stringify(todos)); + }, [todos]); + return (

TODO

diff --git a/src/hooks/useTodo.jsx b/src/hooks/useTodo.jsx index 9e048820..c2877b2b 100644 --- a/src/hooks/useTodo.jsx +++ b/src/hooks/useTodo.jsx @@ -1,12 +1,13 @@ import { useState } from 'react'; const useTodoState = () => { - const [todos, setTodos] = useState([]); - const [next, setNext] = useState(0); + const [todos, setTodos] = useState(JSON.parse(localStorage.getItem("todoItem"))); + const [next, setNext] = useState(1+parseInt(JSON.parse(localStorage.getItem("todoID")))); const addTodo = (todo) => { setNext(next+1); setTodos((prev) => [...prev, {content:todo, isCompleted:false, id:next}]); + localStorage.setItem("todoID", JSON.stringify(next)); }; const deleteTodo = (target) => { @@ -19,7 +20,7 @@ const useTodoState = () => { )); } - return { todos, addTodo, deleteTodo, changeCompleted }; + return { todos, setTodos, addTodo, deleteTodo, changeCompleted }; } export default useTodoState; \ No newline at end of file From f2020b518538c119cabfb507cac175e06bf48538 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Jun 2024 10:31:45 +0900 Subject: [PATCH 11/12] refactor: check requirements & add exit --- src/App.jsx | 22 +++++++++++++++++----- src/components/header.jsx | 6 +++--- src/components/todoelement.jsx | 1 - src/hooks/useTodo.jsx | 24 ++++++++++++++---------- src/index.css | 21 +++++++++++++++------ 5 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 022eebbf..a61da914 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -4,11 +4,26 @@ import TodoList from './components/todolist.jsx'; import Footer from './components/footer.jsx'; import UseTodoState from './hooks/useTodo.jsx'; +function exitProgram() { + console.log("exit"); + window.close(); +} + function activeLen(todos) { const activeTodos = todos.filter((todo) => todo.isCompleted == false); return activeTodos.length; } +function mainFrame(todos, addTodo, deleteTodo, changeCompleted, filter, useFilter) { + return ( +
+
+ +
+
+ ); +} + function App() { const { todos, setTodos, addTodo, deleteTodo, changeCompleted } = UseTodoState(); const [filter, useFilter] = useState('All'); @@ -20,11 +35,8 @@ function App() { return (

TODO

-
-
- -
-
+ { mainFrame (todos, addTodo, deleteTodo, changeCompleted, filter, useFilter) } +
); } diff --git a/src/components/header.jsx b/src/components/header.jsx index 6ade9eb6..4fb6eabf 100644 --- a/src/components/header.jsx +++ b/src/components/header.jsx @@ -3,9 +3,9 @@ import React, { useState } from 'react'; function makeForm(content, setContent) { return (
- setContent(e.target.value)} required/> - -
+ setContent(e.target.value)} required/> + + ) } diff --git a/src/components/todoelement.jsx b/src/components/todoelement.jsx index 526e9395..075dff67 100644 --- a/src/components/todoelement.jsx +++ b/src/components/todoelement.jsx @@ -5,7 +5,6 @@ function makeCheckbox(todo, changeCompleted) { changeCompleted(todo.id); }; - return (); } diff --git a/src/hooks/useTodo.jsx b/src/hooks/useTodo.jsx index c2877b2b..42212c82 100644 --- a/src/hooks/useTodo.jsx +++ b/src/hooks/useTodo.jsx @@ -1,25 +1,29 @@ import { useState } from 'react'; +function add(next, setNext, setTodos, element) { + setNext((prev) => prev+1); + setTodos((prev) => [...prev, {content:element, isCompleted:false, id:next}]); + localStorage.setItem("todoID", JSON.stringify(next)); +} + +function change(setTodos, target) { + setTodos((prev) => prev.map((todo) => + todo.id === target ? {...todo, isCompleted:!todo.isCompleted} : todo + )); +} + const useTodoState = () => { const [todos, setTodos] = useState(JSON.parse(localStorage.getItem("todoItem"))); const [next, setNext] = useState(1+parseInt(JSON.parse(localStorage.getItem("todoID")))); - const addTodo = (todo) => { - setNext(next+1); - setTodos((prev) => [...prev, {content:todo, isCompleted:false, id:next}]); - localStorage.setItem("todoID", JSON.stringify(next)); + add(next, setNext, setTodos, todo); }; - const deleteTodo = (target) => { setTodos((prev) => prev.filter((todo) => todo.id !== target)); }; - const changeCompleted = (target) => { - setTodos((prev) => prev.map((todo) => - todo.id === target ? {...todo, isCompleted:!todo.isCompleted} : todo - )); + change(setTodos, target) } - return { todos, setTodos, addTodo, deleteTodo, changeCompleted }; } diff --git a/src/index.css b/src/index.css index 84d6acf2..660be0bd 100644 --- a/src/index.css +++ b/src/index.css @@ -7,12 +7,13 @@ body { h1 { text-align: center; color:midnightblue; + margin-bottom: 50px; } button { background-color: transparent; border: 0; - color: #707070;; + color: #707070; } .app { @@ -26,6 +27,19 @@ button { } } +.app .exitbtn { + margin-top: 50px; + float: right; + height: 30px; + width: 120px; + background-color: white; + border: 1px solid; + border-color: lightslategray; +} +.app .exitbtn:hover { + color: dodgerblue; + border-color: dodgerblue; +} .app .mainframe { background-color: white; width: 100%; @@ -61,9 +75,6 @@ button { color:white; } -.app .mainframe .todolist { - -} .app .mainframe .todolist .todoelement { width: 100%; height: auto; @@ -82,9 +93,7 @@ button { border:3px solid #707070; position: relative; } -.app .mainframe .todolist .todoelement p { -} .app .mainframe .todolist .todoelement .completed { text-decoration: line-through; } From 755cea4e1248216fe6bbfa66b23563ee0047fcf5 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Jun 2024 15:11:27 +0900 Subject: [PATCH 12/12] fix: when local storage is empty --- src/hooks/useTodo.jsx | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/hooks/useTodo.jsx b/src/hooks/useTodo.jsx index 42212c82..bab11613 100644 --- a/src/hooks/useTodo.jsx +++ b/src/hooks/useTodo.jsx @@ -12,9 +12,28 @@ function change(setTodos, target) { )); } +function initTodo() { + const data = localStorage.getItem("todoItem"); + + if(data) + return JSON.parse(data); + else + return []; +} + +function initNext() { + const data = localStorage.getItem("todoID"); + + if(data) + return 1+parseInt(JSON.parse(data)); + else + return 0; +} + const useTodoState = () => { - const [todos, setTodos] = useState(JSON.parse(localStorage.getItem("todoItem"))); - const [next, setNext] = useState(1+parseInt(JSON.parse(localStorage.getItem("todoID")))); + const [todos, setTodos] = useState(initTodo()); + const [next, setNext] = useState(initNext()); + const addTodo = (todo) => { add(next, setNext, setTodos, todo); };