diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..aef84430 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/README.md b/README.md index 24e05bed..fb198a8c 100644 --- a/README.md +++ b/README.md @@ -39,15 +39,15 @@ ## ๐ŸŽฏ ์š”๊ตฌ์‚ฌํ•ญ -- [ ] todo list์— todoItem์„ ํ‚ค๋ณด๋“œ๋กœ ์ž…๋ ฅํ•˜์—ฌ ์ถ”๊ฐ€ํ•˜๊ธฐ -- [ ] todo list์˜ ์ฒดํฌ๋ฐ•์Šค๋ฅผ ํด๋ฆญํ•˜์—ฌ complete ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ (li tag ์— completed class ์ถ”๊ฐ€, input ํƒœ๊ทธ์— checked ์†์„ฑ ์ถ”๊ฐ€) -- [ ] todo list์˜ x๋ฒ„ํŠผ์„ ์ด์šฉํ•ด์„œ ํ•ด๋‹น ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์‚ญ์ œ -- [ ] todo list๋ฅผ ๋”๋ธ”ํด๋ฆญํ–ˆ์„ ๋•Œ input ๋ชจ๋“œ๋กœ ๋ณ€๊ฒฝ (li tag ์— editing class ์ถ”๊ฐ€) ๋‹จ ์ด๋•Œ ์ˆ˜์ •์„ ์™„๋ฃŒํ•˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ escํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ์ˆ˜์ •๋˜์ง€ ์•Š์€ ์ฑ„๋กœ ๋‹ค์‹œ view ๋ชจ๋“œ๋กœ ๋ณต๊ท€ -- [ ] todo list์˜ item๊ฐฏ์ˆ˜๋ฅผ countํ•œ ๊ฐฏ์ˆ˜๋ฅผ ๋ฆฌ์ŠคํŠธ์˜ ํ•˜๋‹จ์— ๋ณด์—ฌ์ฃผ๊ธฐ -- [ ] todo list์˜ ์ƒํƒœ๊ฐ’์„ ํ™•์ธํ•˜์—ฌ, ํ•ด์•ผํ•  ์ผ๊ณผ, ์™„๋ฃŒํ•œ ์ผ์„ ํด๋ฆญํ•˜๋ฉด ํ•ด๋‹น ์ƒํƒœ์˜ ์•„์ดํ…œ๋งŒ ๋ณด์—ฌ์ฃผ๊ธฐ +- [x] todo list์— todoItem์„ ํ‚ค๋ณด๋“œ๋กœ ์ž…๋ ฅํ•˜์—ฌ ์ถ”๊ฐ€ํ•˜๊ธฐ +- [x] todo list์˜ ์ฒดํฌ๋ฐ•์Šค๋ฅผ ํด๋ฆญํ•˜์—ฌ complete ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ (li tag ์— completed class ์ถ”๊ฐ€, input ํƒœ๊ทธ์— checked ์†์„ฑ ์ถ”๊ฐ€) +- [x] todo list์˜ x๋ฒ„ํŠผ์„ ์ด์šฉํ•ด์„œ ํ•ด๋‹น ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์‚ญ์ œ +- [x] todo list๋ฅผ ๋”๋ธ”ํด๋ฆญํ–ˆ์„ ๋•Œ input ๋ชจ๋“œ๋กœ ๋ณ€๊ฒฝ (li tag ์— editing class ์ถ”๊ฐ€) ๋‹จ ์ด๋•Œ ์ˆ˜์ •์„ ์™„๋ฃŒํ•˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ escํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ์ˆ˜์ •๋˜์ง€ ์•Š์€ ์ฑ„๋กœ ๋‹ค์‹œ view ๋ชจ๋“œ๋กœ ๋ณต๊ท€ +- [x] todo list์˜ item๊ฐฏ์ˆ˜๋ฅผ countํ•œ ๊ฐฏ์ˆ˜๋ฅผ ๋ฆฌ์ŠคํŠธ์˜ ํ•˜๋‹จ์— ๋ณด์—ฌ์ฃผ๊ธฐ +- [x] todo list์˜ ์ƒํƒœ๊ฐ’์„ ํ™•์ธํ•˜์—ฌ, ํ•ด์•ผํ•  ์ผ๊ณผ, ์™„๋ฃŒํ•œ ์ผ์„ ํด๋ฆญํ•˜๋ฉด ํ•ด๋‹น ์ƒํƒœ์˜ ์•„์ดํ…œ๋งŒ ๋ณด์—ฌ์ฃผ๊ธฐ ## ๐ŸŽฏ๐ŸŽฏ ์‹ฌํ™” ์š”๊ตฌ์‚ฌํ•ญ -- [ ] localStorage์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜์—ฌ, TodoItem์˜ CRUD๋ฅผ ๋ฐ˜์˜ํ•˜๊ธฐ. ๋”ฐ๋ผ์„œ ์ƒˆ๋กœ๊ณ ์นจํ•˜์—ฌ๋„ ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•จ +- [x] localStorage์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜์—ฌ, TodoItem์˜ CRUD๋ฅผ ๋ฐ˜์˜ํ•˜๊ธฐ. ๋”ฐ๋ผ์„œ ์ƒˆ๋กœ๊ณ ์นจํ•˜์—ฌ๋„ ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•จ
@@ -109,27 +109,4 @@ live-server ํด๋”๋ช…
-## ๐Ÿ’ฌ 1์ฃผ์ฐจ ๋ฏธ์…˜ ํ›„๊ธฐ ๋ธ”๋กœ๊ทธ -์•„๋ž˜ ๋งํฌ๋Š” 1์ฃผ์ฐจ ๋ฏธ์…˜์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋ธ”๋กœ๊ทธ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ์‹  ๋ถ„๋“ค์˜ ๊ธ€์ž…๋‹ˆ๋‹ค. ๋ฏธ์…˜์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ, ๋‹ค๋ฅธ ๋ถ„๋“ค์˜ ๋ฌธ์ œ ํ•ด๊ฒฐ ๊ณผ์ •์ด ๊ถ๊ธˆํ•˜๋‹ค๋ฉด ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š” ๐Ÿ˜„ -- [1์ฃผ์ฐจ ๋ฏธ์…˜ํ›„๊ธฐ](https://www.notion.so/1-2-8b624729fbce4174b8b583efb10c3200) -- [๋ธ”๋ž™์ปคํ”ผ ํ”„๋ก ํŠธ์—”๋“œ ์Šคํ„ฐ๋”” ๋ ˆ๋ฒจ1 ํ›„๊ธฐ](https://yujo11.github.io/%EB%B8%94%EB%9E%99%EC%BB%A4%ED%94%BC/%EB%B8%94%EB%9E%99%EC%BB%A4%ED%94%BC-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%8A%A4%ED%84%B0%EB%94%94-%EB%A0%88%EB%B2%A81-%ED%9B%84%EA%B8%B0/) -- [๋ธ”๋ž™์ปคํ”ผ ํ”„๋ก ํŠธ์—”๋“œ ์Šคํ„ฐ๋”” ํšŒ๊ณ ](https://www.notion.so/bffb14daea984293a954ac7cdb4f7c1e) - -
- -## ๐Ÿ‘๐Ÿผ Contributing - -๋งŒ์•ฝ ๋ฏธ์…˜ ์ˆ˜ํ–‰ ์ค‘์— ๊ฐœ์„ ์‚ฌํ•ญ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด, ์–ธ์ œ๋“  ์ž์œ ๋กญ๊ฒŒ PR์„ ๋ณด๋‚ด์ฃผ์„ธ์š”. - -
- -## ๐Ÿž Bug Report - -๋ฒ„๊ทธ๋ฅผ ๋ฐœ๊ฒฌํ•œ๋‹ค๋ฉด, [Issues](https://github.com/next-step/js-todo-list-step1/issues)์— ๋“ฑ๋กํ•ด์ฃผ์„ธ์š”. - -
- -## ๐Ÿ“ License - -This project is [MIT](https://github.com/next-step/js-todo-list-step1/blob/main/LICENSE) licensed. diff --git a/index.html b/index.html index 13a02fdb..275c69a6 100644 --- a/index.html +++ b/index.html @@ -3,12 +3,12 @@ - ์ด๋ฒคํŠธ - TODOS + TODOS
-

TODOS

+
+ + diff --git a/src/app.js b/src/app.js new file mode 100644 index 00000000..ce78aebe --- /dev/null +++ b/src/app.js @@ -0,0 +1,102 @@ +import Component from "./core/component.js"; +import Filter from "./components/Filter.js" +import Input from "./components/Input.js" +import TodoList from "./components/TodoList.js" +import {$, setLocalStorageItem, getLocalStorageItem} from './utils/util.js'; + + + +class App extends Component{ + setup(){ + const todo = getLocalStorageItem("todo")? + getLocalStorageItem("todo") : setLocalStorageItem("todo", {"count": 0,"Filtermode" : 0, "List" : []}); + this.$state= todo; + } + + template(){ + return ` +

TODOS

+ +
+ + +
+
+ ` + } + + mounted(){ + const {$state ,onAddTodo, onToggleTodo, onDeleteTodo, onUpdateTodo, onFilterTodo} = this; + const _Input = $('#new-todo-title'); + const _TodoList = $('#todo-list'); + const _Filter = $('#todo-filter'); + + new Input(_Input, onAddTodo.bind(this)); + new TodoList(_TodoList, { + $state, + onToggleTodo: onToggleTodo.bind(this), + onDeleteTodo : onDeleteTodo.bind(this), + onUpdateTodo : onUpdateTodo.bind(this) + }); + new Filter(_Filter,{ + $state, + onFilterTodo : onFilterTodo.bind(this) + }); + } + + onAddTodo(content){ + const id = String(this.$state.count*1+1); + const Filtermode = this.$state.Filtermode; + const List = [...this.$state.List, {id ,content:content,activate:false}]; + setLocalStorageItem("todo",{List,count : id*1,Filtermode}); + this.setState(getLocalStorageItem("todo")); + } + onToggleTodo(id){ + const List = []; + this.$state.List.map(todo => { + if(todo.id==id){ + List.push({id:todo.id, content:todo.content, activate:!todo.activate}) + }else{ + List.push({id:todo.id,content:todo.content, activate:todo.activate}) + } + }); + const count = String(this.$state.count*1+1); + const Filtermode = this.$state.Filtermode; + setLocalStorageItem("todo",{List,count :count*1, Filtermode}) + this.setState(getLocalStorageItem("todo")); + + } + onDeleteTodo(id){ + const List = this.$state.List.filter(todo => todo.id!==id); + const count = String(this.$state.count*1); + const Filtermode = this.$state.Filtermode; + setLocalStorageItem("todo",{List,count :count*1, Filtermode}) + this.setState(getLocalStorageItem("todo")); + } + onUpdateTodo(id, new_content){ + const List = []; + this.$state.List.map(todo => { + if(todo.id==id){ + List.push({id:todo.id, content:new_content, activate:todo.activate}) + }else{ + List.push({id:todo.id,content:todo.content, activate:todo.activate}) + } + }); + const count = String(this.$state.count*1); + const Filtermode = this.$state.Filtermode; + setLocalStorageItem("todo",{List,count :count*1, Filtermode}) + this.setState(getLocalStorageItem("todo")); + } + onFilterTodo(mode){ + const List = this.$state.List; + const count = String(this.$state.count*1); + const Filtermode = mode; + setLocalStorageItem("todo",{List,count :count*1, Filtermode}) + this.setState(getLocalStorageItem("todo")); + } +} + + +export default App; \ No newline at end of file diff --git a/src/components/Filter.js b/src/components/Filter.js new file mode 100644 index 00000000..a0be78fc --- /dev/null +++ b/src/components/Filter.js @@ -0,0 +1,45 @@ +import Component from "../core/component.js"; +import {$$} from '../utils/util.js'; + +class Filter extends Component{ + setup(){ + this.$state = this.$props.$state; + } + + template(){ + const mode = this.$state.Filtermode; + const totalNum = ((mode)=>{ + if(mode==0){ + return this.$state.List.length; + }else if(mode==1){ + return this.$state.List.filter(item => item.activate!=true).length; + }else{ + return this.$state.List.filter(item => item.activate!=false).length; + } + })(mode) + return ` + ์ด ${totalNum} ๊ฐœ + + ` + } + mounted(){ + const filterBtn = $$('.filters > li > a'); + filterBtn.forEach(element =>{ + element.addEventListener('click',(e)=>{ + this.$props.onFilterTodo(e.target.id); + }) + }); + } +} + +export default Filter; \ No newline at end of file diff --git a/src/components/Input.js b/src/components/Input.js new file mode 100644 index 00000000..3d89e204 --- /dev/null +++ b/src/components/Input.js @@ -0,0 +1,15 @@ +import Component from "../core/component.js"; + +class Input extends Component{ + setEvent(){ + const onAddTodo = this.$props; + this.$target.addEventListener('keyup',(e)=>{ + if(e.key=="Enter"){ + const content = this.$target.value; + onAddTodo(content); + } + }); + } +} + +export default Input; \ No newline at end of file diff --git a/src/components/TodoList.js b/src/components/TodoList.js new file mode 100644 index 00000000..ec835f9f --- /dev/null +++ b/src/components/TodoList.js @@ -0,0 +1,64 @@ +import Component from "../core/component.js"; +import {$$} from "../utils/util.js"; + +class TodoList extends Component{ + setup(){ + this.$state = this.$props.$state; + } + + template(){ + const todoList = this.$state; + return ` + ${todoList.List.map(item =>` +
  • +
    + + + +
    + +
  • + `).join('')} + ` + } + mounted(){ + const deleteBtns = $$('.destroy'); + deleteBtns.forEach(element => { + element.addEventListener('click',(e)=>{ + this.$props.onDeleteTodo(e.target.id); + }) + }); + + const toggleBtn = $$('.toggle'); + toggleBtn.forEach(element => { + element.addEventListener('click',(e)=>{ + this.$props.onToggleTodo(e.target.id); + }) + }); + + const editBtn = $$('.label'); + editBtn.forEach(element =>{ + element.addEventListener('dblclick', (e)=>{ + this.editTodo(e.target); + }) + }); + } + editTodo(targetDom){ + const edit_li = targetDom.parentNode.parentNode; + const edit_input = targetDom.parentNode.nextElementSibling; + edit_li.classList.add('editing'); + + if(edit_li.classList.contains("editing")){ + edit_li.addEventListener('keyup',(e)=>{ + if(e.key=="Enter"){ + this.$props.onUpdateTodo(targetDom.id, edit_input.value); + } + if(e.key=="Escape"){ + edit_li.classList.remove('editing'); + } + }) + } + } +} + +export default TodoList; \ No newline at end of file diff --git a/src/core/component.js b/src/core/component.js new file mode 100644 index 00000000..a055b7f8 --- /dev/null +++ b/src/core/component.js @@ -0,0 +1,24 @@ +export default class Component { + $target; + $props; + $state; + constructor ($target, $props) { + this.$target = $target; + this.$props = $props; + this.setup(); + this.setEvent(); + this.render(); + } + setup () {}; + template () { return ''; } + render () { + this.$target.innerHTML = this.template(); + this.mounted(); + } + setEvent () {} + mounted(){} + setState (List) { + this.$state = List; + this.render(); + } + } \ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100644 index 00000000..057ca9a5 --- /dev/null +++ b/src/main.js @@ -0,0 +1,3 @@ +import App from './App.js'; + +new App(document.querySelector('.todoapp')); \ No newline at end of file diff --git a/src/utils/util.js b/src/utils/util.js new file mode 100644 index 00000000..875502d1 --- /dev/null +++ b/src/utils/util.js @@ -0,0 +1,9 @@ +const $ = (selector) => document.querySelector(selector); +const $$ = (selector) => document.querySelectorAll(selector); +const setLocalStorageItem = (key, value) => localStorage.setItem(key, JSON.stringify(value)); +const getLocalStorageItem = (key) => { + const todoList = localStorage.getItem(key); + return JSON.parse(todoList); +}; + +export {$, $$, setLocalStorageItem, getLocalStorageItem}; \ No newline at end of file