From 4080af895e14ee509f252977be5f2f86bddc4dfb Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Fri, 7 Feb 2020 14:21:03 +0900 Subject: [PATCH 01/24] Init mission02 Copy mission01 files to here. --- mission002/hsna7024/css/style.css | 334 ++++++++++++++++++++++ mission002/hsna7024/index.html | 61 ++++ mission002/hsna7024/js/App.js | 82 ++++++ mission002/hsna7024/js/TodoCount.js | 19 ++ mission002/hsna7024/js/TodoFilter.js | 25 ++ mission002/hsna7024/js/TodoInput.js | 16 ++ mission002/hsna7024/js/TodoList.js | 61 ++++ mission002/hsna7024/js/main.js | 33 +++ mission002/hsna7024/js/utils/constants.js | 21 ++ mission002/hsna7024/js/utils/templates.js | 33 +++ 10 files changed, 685 insertions(+) create mode 100644 mission002/hsna7024/css/style.css create mode 100644 mission002/hsna7024/index.html create mode 100644 mission002/hsna7024/js/App.js create mode 100644 mission002/hsna7024/js/TodoCount.js create mode 100644 mission002/hsna7024/js/TodoFilter.js create mode 100644 mission002/hsna7024/js/TodoInput.js create mode 100644 mission002/hsna7024/js/TodoList.js create mode 100644 mission002/hsna7024/js/main.js create mode 100644 mission002/hsna7024/js/utils/constants.js create mode 100644 mission002/hsna7024/js/utils/templates.js diff --git a/mission002/hsna7024/css/style.css b/mission002/hsna7024/css/style.css new file mode 100644 index 0000000..14a905e --- /dev/null +++ b/mission002/hsna7024/css/style.css @@ -0,0 +1,334 @@ +html, +body { + margin: 0; + padding: 10px; +} + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + font-weight: inherit; + color: inherit; + -webkit-appearance: none; + appearance: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #f5f5f5; + color: #4d4d4d; + min-width: 230px; + max-width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-weight: 300; +} + +:focus { + outline: 0; +} + +.hidden { + display: none; +} + +.todoapp { + background: #fff; + margin: 130px 0 40px 0; + position: relative; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.1); +} + +.todoapp input::-webkit-input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +.todoapp h1 { + position: absolute; + top: -125px; + width: 100%; + font-size: 60px; + text-align: center; + color: dimgray; + font-weight: 100; + font-family: Helvetica Neue, Helvetica, Arial, sans-serif; +} + +.new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + font-weight: inherit; + line-height: 1.4em; + border: 0; + color: inherit; + padding: 6px; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.003); + box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); +} + +.main { + position: relative; + z-index: 2; + border-top: 1px solid #e6e6e6; +} + +.toggle-all { + width: 1px; + height: 1px; + border: none; + opacity: 0; + position: absolute; + right: 100%; + bottom: 100%; +} + +.toggle-all + label { + width: 60px; + height: 34px; + font-size: 0; + position: absolute; + top: -52px; + left: -13px; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); +} + +.toggle-all + label:before { + content: '❯'; + font-size: 22px; + color: #e6e6e6; + padding: 10px 27px 10px 27px; +} + +.toggle-all:checked + label:before { + color: #737373; +} + +.todo-list { + margin: 0; + padding: 0; + list-style: none; +} + +.todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px solid #ededed; +} + +.todo-list li:last-child { + border-bottom: none; +} + +.todo-list li.editing { + border-bottom: none; + padding: 0; +} + +.todo-list li.editing .edit { + display: block; + width: calc(100% - 43px); + padding: 12px 16px; + margin: 0 0 0 43px; +} + +.todo-list li.editing .view { + display: none; +} + +.todo-list li .toggle { + text-align: center; + width: 40px; + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + border: none; + -webkit-appearance: none; + appearance: none; +} + +.todo-list li .toggle { + opacity: 0; +} + +.todo-list li .toggle + label { + background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E'); + background-repeat: no-repeat; + background-position: center left; +} + +.todo-list li .toggle:checked + label { + background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E'); +} + +.todo-list li label { + word-break: break-all; + padding: 15px 15px 15px 60px; + display: block; + line-height: 1.2; + transition: color 0.4s; +} + +.todo-list li.completed label { + color: #d9d9d9; + text-decoration: line-through; +} + +.todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 30px; + color: #cc9a9a; + margin-bottom: 11px; + transition: color 0.2s ease-out; + cursor: pointer; +} + +.todo-list li .destroy:hover { + color: #af5b5e; +} + +.todo-list li .destroy:after { + content: '×'; +} + +.todo-list li:hover .destroy { + display: block; +} + +.todo-list li .edit { + display: none; +} + +.todo-list li.editing:last-child { + margin-bottom: -1px; +} + +.count-container { + color: #777; + padding: 10px 15px; + height: 20px; + text-align: center; + border-top: 1px solid #e6e6e6; +} + +.count-container:before { + content: ''; + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 50px; + overflow: hidden; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), + 0 8px 0 -3px #f6f6f6, + 0 9px 1px -3px rgba(0, 0, 0, 0.2), + 0 16px 0 -6px #f6f6f6, + 0 17px 2px -6px rgba(0, 0, 0, 0.2); +} + +.todo-count { + float: left; + text-align: left; +} + +.todo-count strong { + font-weight: 300; +} + +.filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +.filters li { + display: inline; +} + +.filters li a { + color: inherit; + margin: 3px; + padding: 3px 7px; + text-decoration: none; + border: 1px solid transparent; + border-radius: 3px; +} + +.filters li a:hover { + border-color: rgba(175, 47, 47, 0.1); +} + +.filters li a.selected { + border-color: rgba(175, 47, 47, 0.2); +} + +.clear-completed, html .clear-completed:active { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + cursor: pointer; +} + +.clear-completed:hover { + text-decoration: underline; +} + +.info { + margin: 65px auto 0; + color: #bfbfbf; + font-size: 10px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-align: center; +} + +.info p { + line-height: 1; +} + +.info a { + color: inherit; + text-decoration: none; + font-weight: 400; +} + +.info a:hover { + text-decoration: underline; +} diff --git a/mission002/hsna7024/index.html b/mission002/hsna7024/index.html new file mode 100644 index 0000000..b2a621a --- /dev/null +++ b/mission002/hsna7024/index.html @@ -0,0 +1,61 @@ + + + + + + 이벤트 - TODOS + + + +
+
+

TODOS

+ +
+
+ +
    +
  • +
    + + + +
    + +
  • +
  • +
    + + + +
    + +
  • +
  • +
    + + + +
    + +
  • +
+
+
+ 0 + +
+
+ + + diff --git a/mission002/hsna7024/js/App.js b/mission002/hsna7024/js/App.js new file mode 100644 index 0000000..91386e7 --- /dev/null +++ b/mission002/hsna7024/js/App.js @@ -0,0 +1,82 @@ +import TodoList from "./TodoList.js"; +import TodoInput from "./TodoInput.js"; +import TodoCount from "./TodoCount.js"; +import TodoFilter from "./TodoFilter.js"; +import { filterMap } from "./utils/constants.js"; + +export default function App(params) { + const { + $targetTodoList, + $targetTodoInput, + $targetTodoCount, + $targetTodoFilter + } = params; + let data = params.data || []; + let filter = params.filter || filterMap.ALL; + + const filterTodos = (todos, filter) => { + switch (filter) { + case filterMap.ACTIVE: + return todos.filter(todo => !todo.isCompleted); + case filterMap.COMPLETED: + return todos.filter(todo => todo.isCompleted); + default: + return todos; + } + }; + + const todoList = new TodoList({ + $target: $targetTodoList, + data, + toggleTodo: id => { + data[id].isCompleted = !data[id].isCompleted; + this.render(); + }, + removeTodo: id => { + data.splice(id, 1); + this.render(); + }, + filter, + filterTodos + }); + + const todoInput = new TodoInput({ + $target: $targetTodoInput, + onKeyEnter: content => { + data.push({ + content + }); + this.render(); + } + }); + + const todoCount = new TodoCount({ + $target: $targetTodoCount, + count : data.length + }); + + const todoFilter = new TodoFilter({ + $target: $targetTodoFilter, + changeFilter: nextFilter => { + this.setState(data, nextFilter); + }, + filter + }); + + this.setState = (nextData, nextFilter) => { + data = nextData; + filter = nextFilter; + todoList.setState(data, filter); + todoCount.setState(filterTodos(data, filter).length); + todoFilter.setState(filter); + this.render(); + }; + + this.render = () => { + todoList.render(); + todoCount.render(); + todoFilter.render(); + }; + + this.render(); +} diff --git a/mission002/hsna7024/js/TodoCount.js b/mission002/hsna7024/js/TodoCount.js new file mode 100644 index 0000000..eaf403e --- /dev/null +++ b/mission002/hsna7024/js/TodoCount.js @@ -0,0 +1,19 @@ +import { todoCountTemplate } from "./utils/templates.js"; + +export default function TodoCount(params) { + const { $target } = params; + let count = params.count || 0; + + if($target === null) { + throw new Error(errorMessageMap.IS_NO_TARGET); + } + + this.setState = nextCount => { + count = nextCount; + this.render(); + }; + + this.render = () => { + $target.innerHTML = todoCountTemplate(count); + }; +} diff --git a/mission002/hsna7024/js/TodoFilter.js b/mission002/hsna7024/js/TodoFilter.js new file mode 100644 index 0000000..7ad735a --- /dev/null +++ b/mission002/hsna7024/js/TodoFilter.js @@ -0,0 +1,25 @@ +import { todoFilterTemplate } from "./utils/templates.js"; + +export default function TodoFilter(params) { + const { $target, changeFilter } = params; + let filter = params.filter || ""; + + if($target === null) { + throw new Error(errorMessageMap.IS_NO_TARGET); + } + + $target.addEventListener("click", e => { + changeFilter(e.target.className); + }); + + this.setState = nextFilter => { + filter = nextFilter; + this.render(); + }; + + this.render = () => { + $target.innerHTML = todoFilterTemplate(filter); + }; + + this.render(); +} diff --git a/mission002/hsna7024/js/TodoInput.js b/mission002/hsna7024/js/TodoInput.js new file mode 100644 index 0000000..560785d --- /dev/null +++ b/mission002/hsna7024/js/TodoInput.js @@ -0,0 +1,16 @@ +import { keyMap } from "./utils/constants.js"; + +export default function TodoInput(params) { + const { $target, onKeyEnter } = params; + + if($target === null) { + throw new Error(errorMessageMap.IS_NO_TARGET); + } + + $target.addEventListener("keydown", e => { + if (e.key === keyMap.ENTER && $target.value) { + onKeyEnter($target.value); + $target.value = ""; + } + }); +} diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js new file mode 100644 index 0000000..f0d6468 --- /dev/null +++ b/mission002/hsna7024/js/TodoList.js @@ -0,0 +1,61 @@ +import { classNameMap, keyMap, errorMessageMap } from "./utils/constants.js"; +import { todoListTemplate } from "./utils/templates.js"; + +export default function TodoList(params) { + const { $target, toggleTodo, removeTodo, filterTodos } = params; + let data = params.data || []; + let filter = params.filter || ""; + let filteredData = []; + + if($target === null) { + throw new Error(errorMessageMap.IS_NO_TARGET); + } + + $target.addEventListener("click", e => { + const { id } = e.toElement.parentElement.parentElement.dataset; + if (e.target.className === classNameMap.TOGGLE) { + toggleTodo(id); + } + if (e.target.className === classNameMap.REMOVE) { + removeTodo(id); + } + }); + + $target.addEventListener("dblclick", e => { + if (e.target.className === classNameMap.LABEL) { + const { id } = e.toElement.parentElement.parentElement.dataset; + data[id].onEdit = true; + this.render(); + } + }); + + $target.addEventListener("keydown", e => { + if (e.target.className === classNameMap.EDIT) { + const { id } = e.target.parentElement.dataset; + if (e.key === keyMap.ENTER && e.target.value) { + data[id].content = e.target.value; + data[id].onEdit = false; + this.render(); + } + if (e.key === keyMap.ESC) { + data[id].onEdit = false; + this.render(); + } + } + }); + + filteredData = filterTodos(data, filter); + + this.setState = (nextData, nextFilter) => { + data = nextData || data; + filter = nextFilter || filter; + filteredData = filterTodos(data, filter); + this.render(); + }; + + this.render = () => { + $target.innerHTML = filteredData.map(todoListTemplate).join(""); + }; + + this.render(); +} diff --git a/mission002/hsna7024/js/main.js b/mission002/hsna7024/js/main.js new file mode 100644 index 0000000..98417a9 --- /dev/null +++ b/mission002/hsna7024/js/main.js @@ -0,0 +1,33 @@ +import App from "./App.js"; +import { filterMap } from "./utils/constants.js"; + +const data = [ + { + content: "새로운 타이틀", + isCompleted: false, + onEdit: false + }, + { + content: "완료된 타이틀", + isCompleted: true, + onEdit: false + }, + { + content: "완료된 타이틀", + isCompleted: true, + onEdit: false + } +]; + +const init = () => { + const app = new App({ + $targetTodoList: document.querySelector("#todo-list"), + $targetTodoInput: document.querySelector("#new-todo-title"), + $targetTodoCount: document.querySelector(".todo-count"), + $targetTodoFilter: document.querySelector(".filters"), + filter: filterMap.ALL, + data + }); +}; + +init(); diff --git a/mission002/hsna7024/js/utils/constants.js b/mission002/hsna7024/js/utils/constants.js new file mode 100644 index 0000000..72d7a1d --- /dev/null +++ b/mission002/hsna7024/js/utils/constants.js @@ -0,0 +1,21 @@ +export const filterMap = { + ALL: "all", + ACTIVE: "active", + COMPLETED: "completed" +}; + +export const classNameMap = { + TOGGLE: "toggle", + REMOVE: "destroy", + LABEL: "label", + EDIT: "edit" +}; + +export const keyMap = { + ENTER: "Enter", + ESC: "Escape" +} + +export const errorMessageMap = { + IS_NO_TARGET: 'target element가 없습니다.', +} diff --git a/mission002/hsna7024/js/utils/templates.js b/mission002/hsna7024/js/utils/templates.js new file mode 100644 index 0000000..7a12351 --- /dev/null +++ b/mission002/hsna7024/js/utils/templates.js @@ -0,0 +1,33 @@ +import { filterMap } from "./constants.js"; + +export const todoListTemplate = (todo, index) => { + const contentHtmlString = `
+ + +
+ `; + const completedClassName = todo.isCompleted ? `class = "completed"` : ""; + const editingClassName = todo.onEdit ? `class = "editing"` : ""; + + return `
  • ${contentHtmlString}
  • `; +}; + +export const todoFilterTemplate = filter => { + const allSelected = filter === filterMap.ALL ? " selected" : ""; + const activeSelected = filter === filterMap.ACTIVE ? " selected" : ""; + const completedSelected = filter === filterMap.COMPLETED ? " selected" : ""; + + return `
  • + 전체보기 +
  • +
  • + 해야할 일 +
  • +
  • + 완료한 일 +
  • `; +}; + +export const todoCountTemplate = length => { + return `총 ${length} 개`; +}; From bb3a60cf73799e3a1ce5a2530b116c5c2bfed7bd Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Fri, 7 Feb 2020 15:25:48 +0900 Subject: [PATCH 02/24] feat: Create api.js --- mission002/hsna7024/js/utils/api.js | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 mission002/hsna7024/js/utils/api.js diff --git a/mission002/hsna7024/js/utils/api.js b/mission002/hsna7024/js/utils/api.js new file mode 100644 index 0000000..0980043 --- /dev/null +++ b/mission002/hsna7024/js/utils/api.js @@ -0,0 +1,33 @@ +const API_URL = "http://todo-api.roto.codes"; + +const request = async (uri, method) => { + try { + const res = await fetch(uri, method); + return await res.json(); + } catch (error) { + console.log(error); + } +}; + +export const api = { + getTodos: async username => request(`${API_URL}/${username}`), + postTodo: async (username, todoText) => { + return request(`${API_URL}/${username}`, { + method: 'POST', + headers: { + 'Content-type': 'application/json', + }, + body: JSON.stringify({ content: todoText }), + }) + }, + removeTodo: async (username, id) => { + return request(`${API_URL}/${username}/${id}`, { + method: 'DELETE', + }) + }, + toggleTodo: async (username, id) => { + return request(`${API_URL}/${username}/${id}/toggle`, { + method: 'PUT', + }) + }, +} From f3d84f4fb771b53190ec59b3a27912649ea1f01d Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Fri, 7 Feb 2020 15:33:59 +0900 Subject: [PATCH 03/24] refactor: Use getTodos, postTodo --- mission002/hsna7024/js/App.js | 12 ++++++------ mission002/hsna7024/js/main.js | 23 ++++------------------- mission002/hsna7024/js/utils/constants.js | 2 ++ 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/mission002/hsna7024/js/App.js b/mission002/hsna7024/js/App.js index 91386e7..9223437 100644 --- a/mission002/hsna7024/js/App.js +++ b/mission002/hsna7024/js/App.js @@ -2,7 +2,8 @@ import TodoList from "./TodoList.js"; import TodoInput from "./TodoInput.js"; import TodoCount from "./TodoCount.js"; import TodoFilter from "./TodoFilter.js"; -import { filterMap } from "./utils/constants.js"; +import { filterMap, USERNAME } from "./utils/constants.js"; +import { api } from "./utils/api.js"; export default function App(params) { const { @@ -42,11 +43,10 @@ export default function App(params) { const todoInput = new TodoInput({ $target: $targetTodoInput, - onKeyEnter: content => { - data.push({ - content - }); - this.render(); + onKeyEnter: async content => { + const res = await api.postTodo(USERNAME, content); + const nextData = await api.getTodos(USERNAME); + this.setState(nextData, filter); } }); diff --git a/mission002/hsna7024/js/main.js b/mission002/hsna7024/js/main.js index 98417a9..ed9e5d1 100644 --- a/mission002/hsna7024/js/main.js +++ b/mission002/hsna7024/js/main.js @@ -1,25 +1,10 @@ import App from "./App.js"; -import { filterMap } from "./utils/constants.js"; +import { filterMap, USERNAME } from "./utils/constants.js"; +import { api } from "./utils/api.js"; -const data = [ - { - content: "새로운 타이틀", - isCompleted: false, - onEdit: false - }, - { - content: "완료된 타이틀", - isCompleted: true, - onEdit: false - }, - { - content: "완료된 타이틀", - isCompleted: true, - onEdit: false - } -]; +const init = async () => { + const data = await api.getTodos(USERNAME); -const init = () => { const app = new App({ $targetTodoList: document.querySelector("#todo-list"), $targetTodoInput: document.querySelector("#new-todo-title"), diff --git a/mission002/hsna7024/js/utils/constants.js b/mission002/hsna7024/js/utils/constants.js index 72d7a1d..c066e2b 100644 --- a/mission002/hsna7024/js/utils/constants.js +++ b/mission002/hsna7024/js/utils/constants.js @@ -19,3 +19,5 @@ export const keyMap = { export const errorMessageMap = { IS_NO_TARGET: 'target element가 없습니다.', } + +export const USERNAME = "hsna7024"; From 272530c679fbc52f912041033a2e5b169fc42ed7 Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Fri, 7 Feb 2020 15:39:57 +0900 Subject: [PATCH 04/24] refactor: Modify variables name Modify data to todos. --- mission002/hsna7024/js/App.js | 24 ++++++++++++------------ mission002/hsna7024/js/TodoList.js | 22 +++++++++++----------- mission002/hsna7024/js/main.js | 4 ++-- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/mission002/hsna7024/js/App.js b/mission002/hsna7024/js/App.js index 9223437..3d5d6ad 100644 --- a/mission002/hsna7024/js/App.js +++ b/mission002/hsna7024/js/App.js @@ -12,7 +12,7 @@ export default function App(params) { $targetTodoCount, $targetTodoFilter } = params; - let data = params.data || []; + let todos = params.todos || []; let filter = params.filter || filterMap.ALL; const filterTodos = (todos, filter) => { @@ -28,13 +28,13 @@ export default function App(params) { const todoList = new TodoList({ $target: $targetTodoList, - data, + todos, toggleTodo: id => { - data[id].isCompleted = !data[id].isCompleted; + todos[id].isCompleted = !todos[id].isCompleted; this.render(); }, removeTodo: id => { - data.splice(id, 1); + todos.splice(id, 1); this.render(); }, filter, @@ -45,29 +45,29 @@ export default function App(params) { $target: $targetTodoInput, onKeyEnter: async content => { const res = await api.postTodo(USERNAME, content); - const nextData = await api.getTodos(USERNAME); - this.setState(nextData, filter); + const nextTodos = await api.getTodos(USERNAME); + this.setState(nextTodos, filter); } }); const todoCount = new TodoCount({ $target: $targetTodoCount, - count : data.length + count : todos.length }); const todoFilter = new TodoFilter({ $target: $targetTodoFilter, changeFilter: nextFilter => { - this.setState(data, nextFilter); + this.setState(todos, nextFilter); }, filter }); - this.setState = (nextData, nextFilter) => { - data = nextData; + this.setState = (nextTodos, nextFilter) => { + todos = nextTodos; filter = nextFilter; - todoList.setState(data, filter); - todoCount.setState(filterTodos(data, filter).length); + todoList.setState(todos, filter); + todoCount.setState(filterTodos(todos, filter).length); todoFilter.setState(filter); this.render(); }; diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index f0d6468..af92000 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -3,9 +3,9 @@ import { todoListTemplate } from "./utils/templates.js"; export default function TodoList(params) { const { $target, toggleTodo, removeTodo, filterTodos } = params; - let data = params.data || []; + let todos = params.todos || []; let filter = params.filter || ""; - let filteredData = []; + let filteredTodos = []; if($target === null) { throw new Error(errorMessageMap.IS_NO_TARGET); @@ -24,7 +24,7 @@ export default function TodoList(params) { $target.addEventListener("dblclick", e => { if (e.target.className === classNameMap.LABEL) { const { id } = e.toElement.parentElement.parentElement.dataset; - data[id].onEdit = true; + todos[id].onEdit = true; this.render(); } }); @@ -33,28 +33,28 @@ export default function TodoList(params) { if (e.target.className === classNameMap.EDIT) { const { id } = e.target.parentElement.dataset; if (e.key === keyMap.ENTER && e.target.value) { - data[id].content = e.target.value; - data[id].onEdit = false; + todos[id].content = e.target.value; + todos[id].onEdit = false; this.render(); } if (e.key === keyMap.ESC) { - data[id].onEdit = false; + todos[id].onEdit = false; this.render(); } } }); - filteredData = filterTodos(data, filter); + filteredTodos = filterTodos(todos, filter); - this.setState = (nextData, nextFilter) => { - data = nextData || data; + this.setState = (nextTodos, nextFilter) => { + todos = nextTodos || todos; filter = nextFilter || filter; - filteredData = filterTodos(data, filter); + filteredTodos = filterTodos(todos, filter); this.render(); }; this.render = () => { - $target.innerHTML = filteredData.map(todoListTemplate).join(""); + $target.innerHTML = filteredTodos.map(todoListTemplate).join(""); }; this.render(); diff --git a/mission002/hsna7024/js/main.js b/mission002/hsna7024/js/main.js index ed9e5d1..85ac053 100644 --- a/mission002/hsna7024/js/main.js +++ b/mission002/hsna7024/js/main.js @@ -3,7 +3,7 @@ import { filterMap, USERNAME } from "./utils/constants.js"; import { api } from "./utils/api.js"; const init = async () => { - const data = await api.getTodos(USERNAME); + const todos = await api.getTodos(USERNAME); const app = new App({ $targetTodoList: document.querySelector("#todo-list"), @@ -11,7 +11,7 @@ const init = async () => { $targetTodoCount: document.querySelector(".todo-count"), $targetTodoFilter: document.querySelector(".filters"), filter: filterMap.ALL, - data + todos }); }; From 247dfe546d5bbd37749abb0e33ed84db7e602bd5 Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Fri, 7 Feb 2020 15:55:40 +0900 Subject: [PATCH 05/24] refactor: Modify todo id in template --- mission002/hsna7024/js/utils/templates.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mission002/hsna7024/js/utils/templates.js b/mission002/hsna7024/js/utils/templates.js index 7a12351..6d0434c 100644 --- a/mission002/hsna7024/js/utils/templates.js +++ b/mission002/hsna7024/js/utils/templates.js @@ -9,7 +9,7 @@ export const todoListTemplate = (todo, index) => { const completedClassName = todo.isCompleted ? `class = "completed"` : ""; const editingClassName = todo.onEdit ? `class = "editing"` : ""; - return `
  • ${contentHtmlString}
  • `; + return `
  • ${contentHtmlString}
  • `; }; export const todoFilterTemplate = filter => { From cecf4e2ce7b88b8293870a7654a119049476ad8d Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Fri, 7 Feb 2020 15:58:53 +0900 Subject: [PATCH 06/24] refactor: Use api.toggleTodo --- mission002/hsna7024/js/App.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mission002/hsna7024/js/App.js b/mission002/hsna7024/js/App.js index 3d5d6ad..0c4147e 100644 --- a/mission002/hsna7024/js/App.js +++ b/mission002/hsna7024/js/App.js @@ -29,9 +29,10 @@ export default function App(params) { const todoList = new TodoList({ $target: $targetTodoList, todos, - toggleTodo: id => { - todos[id].isCompleted = !todos[id].isCompleted; - this.render(); + toggleTodo: async id => { + await api.toggleTodo(USERNAME, id); + const nextTodos = await api.getTodos(USERNAME); + this.setState(nextTodos, filter); }, removeTodo: id => { todos.splice(id, 1); From 305a0623b75935d026f539575ea00a490541f5a0 Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Fri, 7 Feb 2020 16:01:02 +0900 Subject: [PATCH 07/24] refactor : Use api in removing todo --- mission002/hsna7024/js/App.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mission002/hsna7024/js/App.js b/mission002/hsna7024/js/App.js index 0c4147e..7132575 100644 --- a/mission002/hsna7024/js/App.js +++ b/mission002/hsna7024/js/App.js @@ -34,9 +34,10 @@ export default function App(params) { const nextTodos = await api.getTodos(USERNAME); this.setState(nextTodos, filter); }, - removeTodo: id => { - todos.splice(id, 1); - this.render(); + removeTodo: async id => { + await api.removeTodo(USERNAME, id); + const nextTodos = await api.getTodos(USERNAME); + this.setState(nextTodos, filter); }, filter, filterTodos From 69356b17c75ac1fd426a820b6507d3179f5f088e Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Fri, 7 Feb 2020 16:54:18 +0900 Subject: [PATCH 08/24] refactor: Modify editing todo on dbclick --- mission002/hsna7024/js/App.js | 8 +++++++- mission002/hsna7024/js/TodoList.js | 17 +++++++++-------- mission002/hsna7024/js/utils/templates.js | 3 +-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/mission002/hsna7024/js/App.js b/mission002/hsna7024/js/App.js index 7132575..17dec91 100644 --- a/mission002/hsna7024/js/App.js +++ b/mission002/hsna7024/js/App.js @@ -39,13 +39,19 @@ export default function App(params) { const nextTodos = await api.getTodos(USERNAME); this.setState(nextTodos, filter); }, + onKeyEnter: async (todo) => { + // TODO : 변경 된 todo로 갱신하기 + // api.updateTodo(USERNAME, todo); + // const nextTodos = await api.getTodos(USERNAME); + // this.setState(nextTodos, filter); + }, filter, filterTodos }); const todoInput = new TodoInput({ $target: $targetTodoInput, - onKeyEnter: async content => { + onKeyEnter : async content => { const res = await api.postTodo(USERNAME, content); const nextTodos = await api.getTodos(USERNAME); this.setState(nextTodos, filter); diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index af92000..dfb75b8 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -2,7 +2,7 @@ import { classNameMap, keyMap, errorMessageMap } from "./utils/constants.js"; import { todoListTemplate } from "./utils/templates.js"; export default function TodoList(params) { - const { $target, toggleTodo, removeTodo, filterTodos } = params; + const { $target, toggleTodo, removeTodo, filterTodos, onKeyEnter } = params; let todos = params.todos || []; let filter = params.filter || ""; let filteredTodos = []; @@ -23,9 +23,7 @@ export default function TodoList(params) { $target.addEventListener("dblclick", e => { if (e.target.className === classNameMap.LABEL) { - const { id } = e.toElement.parentElement.parentElement.dataset; - todos[id].onEdit = true; - this.render(); + e.target.closest('li').classList.toggle('editing'); } }); @@ -33,13 +31,16 @@ export default function TodoList(params) { if (e.target.className === classNameMap.EDIT) { const { id } = e.target.parentElement.dataset; if (e.key === keyMap.ENTER && e.target.value) { - todos[id].content = e.target.value; - todos[id].onEdit = false; + const index = todos.findIndex(todo => { + return todo._id === id; + }); + todos[index].content = e.target.value; + onKeyEnter(todos[index]); + e.target.closest('li').classList.toggle('editing'); this.render(); } if (e.key === keyMap.ESC) { - todos[id].onEdit = false; - this.render(); + e.target.closest('li').classList.toggle('editing'); } } }); diff --git a/mission002/hsna7024/js/utils/templates.js b/mission002/hsna7024/js/utils/templates.js index 6d0434c..c8ae391 100644 --- a/mission002/hsna7024/js/utils/templates.js +++ b/mission002/hsna7024/js/utils/templates.js @@ -7,9 +7,8 @@ export const todoListTemplate = (todo, index) => { `; const completedClassName = todo.isCompleted ? `class = "completed"` : ""; - const editingClassName = todo.onEdit ? `class = "editing"` : ""; - return `
  • ${contentHtmlString}
  • `; + return `
  • ${contentHtmlString}
  • `; }; export const todoFilterTemplate = filter => { From af435e1e5f391670527b99adf6a6577921941876 Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Fri, 7 Feb 2020 18:04:41 +0900 Subject: [PATCH 09/24] feat: Create local stroage --- mission002/hsna7024/js/App.js | 23 +++++++++++++------- mission002/hsna7024/js/main.js | 3 ++- mission002/hsna7024/js/utils/localStorage.js | 10 +++++++++ 3 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 mission002/hsna7024/js/utils/localStorage.js diff --git a/mission002/hsna7024/js/App.js b/mission002/hsna7024/js/App.js index 17dec91..591196c 100644 --- a/mission002/hsna7024/js/App.js +++ b/mission002/hsna7024/js/App.js @@ -4,6 +4,7 @@ import TodoCount from "./TodoCount.js"; import TodoFilter from "./TodoFilter.js"; import { filterMap, USERNAME } from "./utils/constants.js"; import { api } from "./utils/api.js"; +import { loadTodos, saveTodos } from "./utils/localStorage.js"; export default function App(params) { const { @@ -15,6 +16,16 @@ export default function App(params) { let todos = params.todos || []; let filter = params.filter || filterMap.ALL; + const getTodos = async () => { + return nextTodos; + } + + const refreshTodos = async () => { + const nextTodos = await api.getTodos(USERNAME); + saveTodos(nextTodos); + this.setState(nextTodos, filter); + } + const filterTodos = (todos, filter) => { switch (filter) { case filterMap.ACTIVE: @@ -31,19 +42,16 @@ export default function App(params) { todos, toggleTodo: async id => { await api.toggleTodo(USERNAME, id); - const nextTodos = await api.getTodos(USERNAME); - this.setState(nextTodos, filter); + refreshTodos(); }, removeTodo: async id => { await api.removeTodo(USERNAME, id); - const nextTodos = await api.getTodos(USERNAME); - this.setState(nextTodos, filter); + refreshTodos(); }, onKeyEnter: async (todo) => { // TODO : 변경 된 todo로 갱신하기 // api.updateTodo(USERNAME, todo); - // const nextTodos = await api.getTodos(USERNAME); - // this.setState(nextTodos, filter); + // refreshTodos(); }, filter, filterTodos @@ -53,8 +61,7 @@ export default function App(params) { $target: $targetTodoInput, onKeyEnter : async content => { const res = await api.postTodo(USERNAME, content); - const nextTodos = await api.getTodos(USERNAME); - this.setState(nextTodos, filter); + refreshTodos(); } }); diff --git a/mission002/hsna7024/js/main.js b/mission002/hsna7024/js/main.js index 85ac053..77b3207 100644 --- a/mission002/hsna7024/js/main.js +++ b/mission002/hsna7024/js/main.js @@ -1,9 +1,10 @@ import App from "./App.js"; import { filterMap, USERNAME } from "./utils/constants.js"; import { api } from "./utils/api.js"; +import { loadTodos } from "./utils/localStorage.js"; const init = async () => { - const todos = await api.getTodos(USERNAME); + const todos = loadTodos() || await api.getTodos(USERNAME); const app = new App({ $targetTodoList: document.querySelector("#todo-list"), diff --git a/mission002/hsna7024/js/utils/localStorage.js b/mission002/hsna7024/js/utils/localStorage.js new file mode 100644 index 0000000..8a0916e --- /dev/null +++ b/mission002/hsna7024/js/utils/localStorage.js @@ -0,0 +1,10 @@ +const TODOS_LS = "todos"; + +export const saveTodos = todos => { + localStorage.setItem(TODOS_LS, JSON.stringify(todos)); +}; + +export const loadTodos = () => { + const loadedToDos = localStorage.getItem(TODOS_LS); + return JSON.parse(loadedToDos); +}; From 1d8774fcd83f1386aca6f321d21e1742b75cbd61 Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Tue, 11 Feb 2020 23:58:59 +0900 Subject: [PATCH 10/24] refactor: Use eslint Adjust eslint and delete unused code. --- mission002/hsna7024/.eslintrc.js | 17 +++++++++++++++++ mission002/hsna7024/.gitignore | 1 + mission002/hsna7024/js/App.js | 16 ++++++---------- mission002/hsna7024/js/TodoCount.js | 3 ++- mission002/hsna7024/js/TodoFilter.js | 3 ++- mission002/hsna7024/js/TodoInput.js | 6 +++--- mission002/hsna7024/js/TodoList.js | 8 ++++---- mission002/hsna7024/js/main.js | 2 +- mission002/hsna7024/js/utils/api.js | 20 ++++++++++---------- mission002/hsna7024/js/utils/constants.js | 6 +++--- mission002/hsna7024/js/utils/templates.js | 2 +- mission002/hsna7024/package.json | 17 +++++++++++++++++ 12 files changed, 67 insertions(+), 34 deletions(-) create mode 100644 mission002/hsna7024/.eslintrc.js create mode 100644 mission002/hsna7024/.gitignore create mode 100644 mission002/hsna7024/package.json diff --git a/mission002/hsna7024/.eslintrc.js b/mission002/hsna7024/.eslintrc.js new file mode 100644 index 0000000..edadc67 --- /dev/null +++ b/mission002/hsna7024/.eslintrc.js @@ -0,0 +1,17 @@ +module.exports = { + "env": { + "browser": true, + "es6": true + }, + "extends": "eslint:recommended", + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module" + }, + "rules": { + } +}; \ No newline at end of file diff --git a/mission002/hsna7024/.gitignore b/mission002/hsna7024/.gitignore new file mode 100644 index 0000000..d8b83df --- /dev/null +++ b/mission002/hsna7024/.gitignore @@ -0,0 +1 @@ +package-lock.json diff --git a/mission002/hsna7024/js/App.js b/mission002/hsna7024/js/App.js index 591196c..11652c3 100644 --- a/mission002/hsna7024/js/App.js +++ b/mission002/hsna7024/js/App.js @@ -4,7 +4,7 @@ import TodoCount from "./TodoCount.js"; import TodoFilter from "./TodoFilter.js"; import { filterMap, USERNAME } from "./utils/constants.js"; import { api } from "./utils/api.js"; -import { loadTodos, saveTodos } from "./utils/localStorage.js"; +import { saveTodos } from "./utils/localStorage.js"; export default function App(params) { const { @@ -16,15 +16,11 @@ export default function App(params) { let todos = params.todos || []; let filter = params.filter || filterMap.ALL; - const getTodos = async () => { - return nextTodos; - } - const refreshTodos = async () => { const nextTodos = await api.getTodos(USERNAME); saveTodos(nextTodos); this.setState(nextTodos, filter); - } + }; const filterTodos = (todos, filter) => { switch (filter) { @@ -48,7 +44,7 @@ export default function App(params) { await api.removeTodo(USERNAME, id); refreshTodos(); }, - onKeyEnter: async (todo) => { + onKeyEnter: async todo => { // TODO : 변경 된 todo로 갱신하기 // api.updateTodo(USERNAME, todo); // refreshTodos(); @@ -59,15 +55,15 @@ export default function App(params) { const todoInput = new TodoInput({ $target: $targetTodoInput, - onKeyEnter : async content => { - const res = await api.postTodo(USERNAME, content); + onKeyEnter: async content => { + await api.postTodo(USERNAME, content); refreshTodos(); } }); const todoCount = new TodoCount({ $target: $targetTodoCount, - count : todos.length + count: todos.length }); const todoFilter = new TodoFilter({ diff --git a/mission002/hsna7024/js/TodoCount.js b/mission002/hsna7024/js/TodoCount.js index eaf403e..bfe3c35 100644 --- a/mission002/hsna7024/js/TodoCount.js +++ b/mission002/hsna7024/js/TodoCount.js @@ -1,10 +1,11 @@ import { todoCountTemplate } from "./utils/templates.js"; +import {errorMessageMap} from "./utils/constants.js"; export default function TodoCount(params) { const { $target } = params; let count = params.count || 0; - if($target === null) { + if ($target === null) { throw new Error(errorMessageMap.IS_NO_TARGET); } diff --git a/mission002/hsna7024/js/TodoFilter.js b/mission002/hsna7024/js/TodoFilter.js index 7ad735a..d142513 100644 --- a/mission002/hsna7024/js/TodoFilter.js +++ b/mission002/hsna7024/js/TodoFilter.js @@ -1,10 +1,11 @@ import { todoFilterTemplate } from "./utils/templates.js"; +import { errorMessageMap } from "./utils/constants.js"; export default function TodoFilter(params) { const { $target, changeFilter } = params; let filter = params.filter || ""; - if($target === null) { + if ($target === null) { throw new Error(errorMessageMap.IS_NO_TARGET); } diff --git a/mission002/hsna7024/js/TodoInput.js b/mission002/hsna7024/js/TodoInput.js index 560785d..a31da88 100644 --- a/mission002/hsna7024/js/TodoInput.js +++ b/mission002/hsna7024/js/TodoInput.js @@ -1,14 +1,14 @@ -import { keyMap } from "./utils/constants.js"; +import { keyMap, errorMessageMap } from "./utils/constants.js"; export default function TodoInput(params) { const { $target, onKeyEnter } = params; - if($target === null) { + if ($target === null) { throw new Error(errorMessageMap.IS_NO_TARGET); } $target.addEventListener("keydown", e => { - if (e.key === keyMap.ENTER && $target.value) { + if (e.key === keyMap.ENTER && $target.value) { onKeyEnter($target.value); $target.value = ""; } diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index dfb75b8..8ce68e2 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -7,7 +7,7 @@ export default function TodoList(params) { let filter = params.filter || ""; let filteredTodos = []; - if($target === null) { + if ($target === null) { throw new Error(errorMessageMap.IS_NO_TARGET); } @@ -23,7 +23,7 @@ export default function TodoList(params) { $target.addEventListener("dblclick", e => { if (e.target.className === classNameMap.LABEL) { - e.target.closest('li').classList.toggle('editing'); + e.target.closest("li").classList.toggle("editing"); } }); @@ -36,11 +36,11 @@ export default function TodoList(params) { }); todos[index].content = e.target.value; onKeyEnter(todos[index]); - e.target.closest('li').classList.toggle('editing'); + e.target.closest("li").classList.toggle("editing"); this.render(); } if (e.key === keyMap.ESC) { - e.target.closest('li').classList.toggle('editing'); + e.target.closest("li").classList.toggle("editing"); } } }); diff --git a/mission002/hsna7024/js/main.js b/mission002/hsna7024/js/main.js index 77b3207..bb4ec12 100644 --- a/mission002/hsna7024/js/main.js +++ b/mission002/hsna7024/js/main.js @@ -4,7 +4,7 @@ import { api } from "./utils/api.js"; import { loadTodos } from "./utils/localStorage.js"; const init = async () => { - const todos = loadTodos() || await api.getTodos(USERNAME); + const todos = loadTodos() || (await api.getTodos(USERNAME)); const app = new App({ $targetTodoList: document.querySelector("#todo-list"), diff --git a/mission002/hsna7024/js/utils/api.js b/mission002/hsna7024/js/utils/api.js index 0980043..f2834d1 100644 --- a/mission002/hsna7024/js/utils/api.js +++ b/mission002/hsna7024/js/utils/api.js @@ -13,21 +13,21 @@ export const api = { getTodos: async username => request(`${API_URL}/${username}`), postTodo: async (username, todoText) => { return request(`${API_URL}/${username}`, { - method: 'POST', + method: "POST", headers: { - 'Content-type': 'application/json', + "Content-type": "application/json" }, - body: JSON.stringify({ content: todoText }), - }) + body: JSON.stringify({ content: todoText }) + }); }, removeTodo: async (username, id) => { return request(`${API_URL}/${username}/${id}`, { - method: 'DELETE', - }) + method: "DELETE" + }); }, toggleTodo: async (username, id) => { return request(`${API_URL}/${username}/${id}/toggle`, { - method: 'PUT', - }) - }, -} + method: "PUT" + }); + } +}; diff --git a/mission002/hsna7024/js/utils/constants.js b/mission002/hsna7024/js/utils/constants.js index c066e2b..bc2b9ee 100644 --- a/mission002/hsna7024/js/utils/constants.js +++ b/mission002/hsna7024/js/utils/constants.js @@ -14,10 +14,10 @@ export const classNameMap = { export const keyMap = { ENTER: "Enter", ESC: "Escape" -} +}; export const errorMessageMap = { - IS_NO_TARGET: 'target element가 없습니다.', -} + IS_NO_TARGET: "target element가 없습니다." +}; export const USERNAME = "hsna7024"; diff --git a/mission002/hsna7024/js/utils/templates.js b/mission002/hsna7024/js/utils/templates.js index c8ae391..cd4d146 100644 --- a/mission002/hsna7024/js/utils/templates.js +++ b/mission002/hsna7024/js/utils/templates.js @@ -1,6 +1,6 @@ import { filterMap } from "./constants.js"; -export const todoListTemplate = (todo, index) => { +export const todoListTemplate = todo => { const contentHtmlString = `
    diff --git a/mission002/hsna7024/package.json b/mission002/hsna7024/package.json new file mode 100644 index 0000000..2ba258a --- /dev/null +++ b/mission002/hsna7024/package.json @@ -0,0 +1,17 @@ +{ + "name": "hsna7024", + "version": "1.0.0", + "description": "mission02", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "eslint-config-prettier": "^6.10.0" + }, + "devDependencies": { + "eslint": "^6.8.0" + } +} From 7babea389c5b51fd5a9c3683e1c8e755da32d875 Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Wed, 12 Feb 2020 10:41:25 +0900 Subject: [PATCH 11/24] refactor: Use closest --- mission002/hsna7024/js/TodoList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index 8ce68e2..2554188 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -12,7 +12,7 @@ export default function TodoList(params) { } $target.addEventListener("click", e => { - const { id } = e.toElement.parentElement.parentElement.dataset; + const { id } = e.target.closest("li").dataset; if (e.target.className === classNameMap.TOGGLE) { toggleTodo(id); } @@ -29,7 +29,7 @@ export default function TodoList(params) { $target.addEventListener("keydown", e => { if (e.target.className === classNameMap.EDIT) { - const { id } = e.target.parentElement.dataset; + const { id } = e.target.closest("li").dataset; if (e.key === keyMap.ENTER && e.target.value) { const index = todos.findIndex(todo => { return todo._id === id; From 0fa3009169ca39b442dcee1f4a5c3916401a3f2b Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Wed, 12 Feb 2020 10:42:55 +0900 Subject: [PATCH 12/24] refactor: Use else if --- mission002/hsna7024/js/TodoList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index 2554188..d8e22e9 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -16,7 +16,7 @@ export default function TodoList(params) { if (e.target.className === classNameMap.TOGGLE) { toggleTodo(id); } - if (e.target.className === classNameMap.REMOVE) { + else if (e.target.className === classNameMap.REMOVE) { removeTodo(id); } }); @@ -39,7 +39,7 @@ export default function TodoList(params) { e.target.closest("li").classList.toggle("editing"); this.render(); } - if (e.key === keyMap.ESC) { + else if (e.key === keyMap.ESC) { e.target.closest("li").classList.toggle("editing"); } } From 998f4a5b615600df901ad23baa7e156cc05d8a9d Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Wed, 12 Feb 2020 10:45:51 +0900 Subject: [PATCH 13/24] refactor: Use classList --- mission002/hsna7024/js/TodoList.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index d8e22e9..46ff45d 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -13,22 +13,22 @@ export default function TodoList(params) { $target.addEventListener("click", e => { const { id } = e.target.closest("li").dataset; - if (e.target.className === classNameMap.TOGGLE) { + if (e.target.classList.contains(classNameMap.TOGGLE)) { toggleTodo(id); } - else if (e.target.className === classNameMap.REMOVE) { + else if (e.target.classList.contains(classNameMap.REMOVE)) { removeTodo(id); } }); $target.addEventListener("dblclick", e => { - if (e.target.className === classNameMap.LABEL) { + if (e.target.classList.contains(classNameMap.LABEL)) { e.target.closest("li").classList.toggle("editing"); } }); $target.addEventListener("keydown", e => { - if (e.target.className === classNameMap.EDIT) { + if (e.target.classList.contains(classNameMap.EDIT)) { const { id } = e.target.closest("li").dataset; if (e.key === keyMap.ENTER && e.target.value) { const index = todos.findIndex(todo => { From ba1ad9cbd1766f6ec32ae4f27888dd2662b28d0a Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Wed, 12 Feb 2020 10:47:26 +0900 Subject: [PATCH 14/24] refactor: Modify template indent --- mission002/hsna7024/js/utils/templates.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/mission002/hsna7024/js/utils/templates.js b/mission002/hsna7024/js/utils/templates.js index cd4d146..0b687e4 100644 --- a/mission002/hsna7024/js/utils/templates.js +++ b/mission002/hsna7024/js/utils/templates.js @@ -1,11 +1,13 @@ import { filterMap } from "./constants.js"; export const todoListTemplate = todo => { - const contentHtmlString = `
    - - -
    - `; + const contentHtmlString = ` +
    + + + +
    + `; const completedClassName = todo.isCompleted ? `class = "completed"` : ""; return `
  • ${contentHtmlString}
  • `; @@ -16,7 +18,8 @@ export const todoFilterTemplate = filter => { const activeSelected = filter === filterMap.ACTIVE ? " selected" : ""; const completedSelected = filter === filterMap.COMPLETED ? " selected" : ""; - return `
  • + return ` +
  • 전체보기
  • From db7d82c1d8f7caa224375634f65b2db2829f9cee Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Thu, 13 Feb 2020 22:10:06 +0900 Subject: [PATCH 15/24] refactor: Add updateTodoByIndex --- mission002/hsna7024/js/App.js | 5 +++++ mission002/hsna7024/js/TodoList.js | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mission002/hsna7024/js/App.js b/mission002/hsna7024/js/App.js index 11652c3..3f8f182 100644 --- a/mission002/hsna7024/js/App.js +++ b/mission002/hsna7024/js/App.js @@ -49,6 +49,11 @@ export default function App(params) { // api.updateTodo(USERNAME, todo); // refreshTodos(); }, + updateTodoByIndex: async (index, content) => { + todos[index].content = content; + // TODO : 변경 된 todo로 갱신하기 + // api.updateTodo(USERNAME, todos[index]); + }, filter, filterTodos }); diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index 46ff45d..321d685 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -2,7 +2,7 @@ import { classNameMap, keyMap, errorMessageMap } from "./utils/constants.js"; import { todoListTemplate } from "./utils/templates.js"; export default function TodoList(params) { - const { $target, toggleTodo, removeTodo, filterTodos, onKeyEnter } = params; + const { $target, toggleTodo, removeTodo, filterTodos, onKeyEnter, updateTodoByIndex } = params; let todos = params.todos || []; let filter = params.filter || ""; let filteredTodos = []; @@ -34,8 +34,7 @@ export default function TodoList(params) { const index = todos.findIndex(todo => { return todo._id === id; }); - todos[index].content = e.target.value; - onKeyEnter(todos[index]); + updateTodoByIndex(index, e.target.value); e.target.closest("li").classList.toggle("editing"); this.render(); } From d7132647d4bce4bdf33cf8759716599dfab4a4cf Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Thu, 13 Feb 2020 22:21:02 +0900 Subject: [PATCH 16/24] refactor: Modify edit mode Add onEdit, offEdit and refactoring code. --- mission002/hsna7024/js/TodoList.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index 321d685..eceb60a 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -7,6 +7,9 @@ export default function TodoList(params) { let filter = params.filter || ""; let filteredTodos = []; + const onEditMode = ($element) => $element.classList.add("editing"); + const offEditMode = ($element) => $element.classList.remove("editing") + if ($target === null) { throw new Error(errorMessageMap.IS_NO_TARGET); } @@ -23,23 +26,22 @@ export default function TodoList(params) { $target.addEventListener("dblclick", e => { if (e.target.classList.contains(classNameMap.LABEL)) { - e.target.closest("li").classList.toggle("editing"); + onEditMode(e.target.closest("li")); } }); $target.addEventListener("keydown", e => { if (e.target.classList.contains(classNameMap.EDIT)) { - const { id } = e.target.closest("li").dataset; if (e.key === keyMap.ENTER && e.target.value) { - const index = todos.findIndex(todo => { - return todo._id === id; - }); - updateTodoByIndex(index, e.target.value); - e.target.closest("li").classList.toggle("editing"); + const { id } = e.target.closest("li").dataset; + const index = todos.findIndex(todo => todo._id === id); + const content = e.target.value + updateTodoByIndex(index, content); + offEditMode(e.target.closest("li")); this.render(); } else if (e.key === keyMap.ESC) { - e.target.closest("li").classList.toggle("editing"); + offEditMode(e.target.closest("li")); } } }); From 1e76d77930b750e70681cd4ddae83530db3abec9 Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Thu, 13 Feb 2020 22:21:29 +0900 Subject: [PATCH 17/24] reafactor: Delete unused method --- mission002/hsna7024/js/App.js | 5 ----- mission002/hsna7024/js/TodoList.js | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/mission002/hsna7024/js/App.js b/mission002/hsna7024/js/App.js index 3f8f182..5fc9855 100644 --- a/mission002/hsna7024/js/App.js +++ b/mission002/hsna7024/js/App.js @@ -44,11 +44,6 @@ export default function App(params) { await api.removeTodo(USERNAME, id); refreshTodos(); }, - onKeyEnter: async todo => { - // TODO : 변경 된 todo로 갱신하기 - // api.updateTodo(USERNAME, todo); - // refreshTodos(); - }, updateTodoByIndex: async (index, content) => { todos[index].content = content; // TODO : 변경 된 todo로 갱신하기 diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index eceb60a..048dff0 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -2,7 +2,7 @@ import { classNameMap, keyMap, errorMessageMap } from "./utils/constants.js"; import { todoListTemplate } from "./utils/templates.js"; export default function TodoList(params) { - const { $target, toggleTodo, removeTodo, filterTodos, onKeyEnter, updateTodoByIndex } = params; + const { $target, toggleTodo, removeTodo, filterTodos, updateTodoByIndex } = params; let todos = params.todos || []; let filter = params.filter || ""; let filteredTodos = []; From 77982c699093d4b129cce92ea8f6e7145f3b1c49 Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Thu, 13 Feb 2020 22:23:55 +0900 Subject: [PATCH 18/24] refactor: Add "EDIT_MODE" constant --- mission002/hsna7024/js/TodoList.js | 4 ++-- mission002/hsna7024/js/utils/constants.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index 048dff0..f0e0a27 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -7,8 +7,8 @@ export default function TodoList(params) { let filter = params.filter || ""; let filteredTodos = []; - const onEditMode = ($element) => $element.classList.add("editing"); - const offEditMode = ($element) => $element.classList.remove("editing") + const onEditMode = ($element) => $element.classList.add(classNameMap.EDIT_MODE); + const offEditMode = ($element) => $element.classList.remove(classNameMap.EDIT_MODE) if ($target === null) { throw new Error(errorMessageMap.IS_NO_TARGET); diff --git a/mission002/hsna7024/js/utils/constants.js b/mission002/hsna7024/js/utils/constants.js index bc2b9ee..94f4c54 100644 --- a/mission002/hsna7024/js/utils/constants.js +++ b/mission002/hsna7024/js/utils/constants.js @@ -8,7 +8,8 @@ export const classNameMap = { TOGGLE: "toggle", REMOVE: "destroy", LABEL: "label", - EDIT: "edit" + EDIT: "edit", + EDIT_MODE: "editing" }; export const keyMap = { From eab3571ab84a62fbffbd63c93ae7b141217cf9c1 Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Thu, 13 Feb 2020 22:27:54 +0900 Subject: [PATCH 19/24] refactor: Add onEnterInEditMode method --- mission002/hsna7024/js/TodoList.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index f0e0a27..0dfed7b 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -9,6 +9,13 @@ export default function TodoList(params) { const onEditMode = ($element) => $element.classList.add(classNameMap.EDIT_MODE); const offEditMode = ($element) => $element.classList.remove(classNameMap.EDIT_MODE) + const onEnterInEditMode = ($element) => { + const { id } = $element.closest("li").dataset; + const index = todos.findIndex(todo => todo._id === id); + const content = $element.value + updateTodoByIndex(index, content); + offEditMode($element.closest("li")); + } if ($target === null) { throw new Error(errorMessageMap.IS_NO_TARGET); @@ -33,11 +40,7 @@ export default function TodoList(params) { $target.addEventListener("keydown", e => { if (e.target.classList.contains(classNameMap.EDIT)) { if (e.key === keyMap.ENTER && e.target.value) { - const { id } = e.target.closest("li").dataset; - const index = todos.findIndex(todo => todo._id === id); - const content = e.target.value - updateTodoByIndex(index, content); - offEditMode(e.target.closest("li")); + onEnterInEditMode(e.target); this.render(); } else if (e.key === keyMap.ESC) { From 9aaed0e7dd369736f0581310ca562b2d401fea4a Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Thu, 13 Feb 2020 22:35:00 +0900 Subject: [PATCH 20/24] refactor: Modify keydown validation in edit mode --- mission002/hsna7024/js/TodoList.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index 0dfed7b..f71b158 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -38,7 +38,10 @@ export default function TodoList(params) { }); $target.addEventListener("keydown", e => { - if (e.target.classList.contains(classNameMap.EDIT)) { + if (!(e.target.classList.contains(classNameMap.EDIT))){ + return; + } + else { if (e.key === keyMap.ENTER && e.target.value) { onEnterInEditMode(e.target); this.render(); From 7024079061d9efbeb8ba26070a6ca740fb9be5dd Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Thu, 13 Feb 2020 22:36:57 +0900 Subject: [PATCH 21/24] style: Update code style --- mission002/hsna7024/js/TodoList.js | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index f71b158..bd6b640 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -2,20 +2,27 @@ import { classNameMap, keyMap, errorMessageMap } from "./utils/constants.js"; import { todoListTemplate } from "./utils/templates.js"; export default function TodoList(params) { - const { $target, toggleTodo, removeTodo, filterTodos, updateTodoByIndex } = params; + const { + $target, + toggleTodo, + removeTodo, + filterTodos, + updateTodoByIndex + } = params; let todos = params.todos || []; let filter = params.filter || ""; let filteredTodos = []; - const onEditMode = ($element) => $element.classList.add(classNameMap.EDIT_MODE); - const offEditMode = ($element) => $element.classList.remove(classNameMap.EDIT_MODE) - const onEnterInEditMode = ($element) => { + const onEditMode = $element => $element.classList.add(classNameMap.EDIT_MODE); + const offEditMode = $element => + $element.classList.remove(classNameMap.EDIT_MODE); + const onEnterInEditMode = $element => { const { id } = $element.closest("li").dataset; const index = todos.findIndex(todo => todo._id === id); - const content = $element.value + const content = $element.value; updateTodoByIndex(index, content); offEditMode($element.closest("li")); - } + }; if ($target === null) { throw new Error(errorMessageMap.IS_NO_TARGET); @@ -25,8 +32,7 @@ export default function TodoList(params) { const { id } = e.target.closest("li").dataset; if (e.target.classList.contains(classNameMap.TOGGLE)) { toggleTodo(id); - } - else if (e.target.classList.contains(classNameMap.REMOVE)) { + } else if (e.target.classList.contains(classNameMap.REMOVE)) { removeTodo(id); } }); @@ -38,15 +44,13 @@ export default function TodoList(params) { }); $target.addEventListener("keydown", e => { - if (!(e.target.classList.contains(classNameMap.EDIT))){ + if (!e.target.classList.contains(classNameMap.EDIT)) { return; - } - else { + } else { if (e.key === keyMap.ENTER && e.target.value) { onEnterInEditMode(e.target); this.render(); - } - else if (e.key === keyMap.ESC) { + } else if (e.key === keyMap.ESC) { offEditMode(e.target.closest("li")); } } From 49bf38938ff5a10c15c44909f541714af904b426 Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Thu, 13 Feb 2020 22:37:39 +0900 Subject: [PATCH 22/24] refactor: Delete unused variables --- mission002/hsna7024/js/App.js | 2 +- mission002/hsna7024/js/main.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mission002/hsna7024/js/App.js b/mission002/hsna7024/js/App.js index 5fc9855..f493992 100644 --- a/mission002/hsna7024/js/App.js +++ b/mission002/hsna7024/js/App.js @@ -53,7 +53,7 @@ export default function App(params) { filterTodos }); - const todoInput = new TodoInput({ + new TodoInput({ $target: $targetTodoInput, onKeyEnter: async content => { await api.postTodo(USERNAME, content); diff --git a/mission002/hsna7024/js/main.js b/mission002/hsna7024/js/main.js index bb4ec12..6ea66f4 100644 --- a/mission002/hsna7024/js/main.js +++ b/mission002/hsna7024/js/main.js @@ -6,7 +6,7 @@ import { loadTodos } from "./utils/localStorage.js"; const init = async () => { const todos = loadTodos() || (await api.getTodos(USERNAME)); - const app = new App({ + new App({ $targetTodoList: document.querySelector("#todo-list"), $targetTodoInput: document.querySelector("#new-todo-title"), $targetTodoCount: document.querySelector(".todo-count"), From 0b916f54e1b78272a11f23dfc2774970ca0e3d63 Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Thu, 13 Feb 2020 22:51:12 +0900 Subject: [PATCH 23/24] refactor: Add onKeydownInEditMode --- mission002/hsna7024/js/TodoList.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index bd6b640..c41f5b6 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -23,6 +23,14 @@ export default function TodoList(params) { updateTodoByIndex(index, content); offEditMode($element.closest("li")); }; + const onKeydownInEditMode = e => { + if (e.key === keyMap.ENTER && e.target.value) { + onEnterInEditMode(event.target); + this.render(); + } else if (e.key === keyMap.ESC) { + offEditMode(e.target.closest("li")); + } + }; if ($target === null) { throw new Error(errorMessageMap.IS_NO_TARGET); @@ -47,12 +55,7 @@ export default function TodoList(params) { if (!e.target.classList.contains(classNameMap.EDIT)) { return; } else { - if (e.key === keyMap.ENTER && e.target.value) { - onEnterInEditMode(e.target); - this.render(); - } else if (e.key === keyMap.ESC) { - offEditMode(e.target.closest("li")); - } + onKeydownInEditMode(e); } }); From 224058483c1832fab2ccbb14fdf14119c9aca40e Mon Sep 17 00:00:00 2001 From: Hosek Na Date: Thu, 13 Feb 2020 22:56:41 +0900 Subject: [PATCH 24/24] style: Add newline --- mission002/hsna7024/js/TodoList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mission002/hsna7024/js/TodoList.js b/mission002/hsna7024/js/TodoList.js index c41f5b6..a3097ae 100644 --- a/mission002/hsna7024/js/TodoList.js +++ b/mission002/hsna7024/js/TodoList.js @@ -20,6 +20,7 @@ export default function TodoList(params) { const { id } = $element.closest("li").dataset; const index = todos.findIndex(todo => todo._id === id); const content = $element.value; + updateTodoByIndex(index, content); offEditMode($element.closest("li")); };