-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[라빈] TodoList 상태관리 하기 미션 제출합니다. #10
base: master
Are you sure you want to change the base?
Changes from all commits
15b8b37
3a22116
2011f7c
d4c690b
9e99039
1a76d54
20429f3
a755f40
2d79be6
ff7991a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import { STATUS, EVENT_TYPE, KEY_TYPE, TAG_TYPE } from "../util/constants.js"; | ||
import { TodoInput } from './TodoInput.js'; | ||
import { TodoItem } from './TodoItem.js'; | ||
import { TodoList } from './TodoList.js'; | ||
import { TodoCount } from './TodoCount.js'; | ||
|
||
function TodoApp() { | ||
this.$todoList = document.querySelector("#todo-list"); | ||
this.$todoInput = document.querySelector("#new-todo-title"); | ||
this.$todoCount = document.querySelector("body > section > div.count-container > span > strong"); | ||
|
||
this.todoItems = []; | ||
this.todoList = new TodoList(this.$todoList); | ||
|
||
let todoItemIndex = this.todoItems.length; | ||
|
||
new TodoCount(event => { | ||
event.preventDefault(); | ||
const $target = event.target; | ||
const tag = $target.getAttribute("href"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if (tag === TAG_TYPE.ALL) { | ||
this.setState(this.todoItems); | ||
return; | ||
} | ||
if (tag === TAG_TYPE.ACTIVE) { | ||
const activeItems = this.todoItems.filter(item => item.status === STATUS.TODO); | ||
this.setState(activeItems); | ||
return; | ||
} | ||
if (tag === TAG_TYPE.COMPLETED) { | ||
const completedItems = this.todoItems.filter(item => item.status === STATUS.COMPLETED); | ||
this.setState(completedItems); | ||
} | ||
}); | ||
|
||
new TodoInput({ | ||
onAdd: contents => { | ||
const newTodoItem = new TodoItem(todoItemIndex++, contents, STATUS.TODO); | ||
this.todoItems.push(newTodoItem); | ||
this.setState(this.todoItems); | ||
} | ||
}, this.$todoInput); | ||
|
||
this.setState = updatedItems => { | ||
const showItems = updatedItems; | ||
this.todoList.setState(showItems); | ||
this.$todoCount.innerText = showItems.length; | ||
}; | ||
|
||
const completedTodoItem = event => { | ||
event.preventDefault(); | ||
const $target = event.target; | ||
const isCompletedButton = $target.classList.contains("toggle"); | ||
if (isCompletedButton) { | ||
const todoItemId = Number.parseInt($target.closest("div").dataset.itemId); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 서비스가 업데이트 되면서 |
||
const targetItem = this.todoItems.find(item => item.id === todoItemId); | ||
targetItem.status = targetItem.status === STATUS.TODO ? STATUS.COMPLETED : STATUS.TODO; | ||
this.todoList.setState(this.todoItems); | ||
} | ||
}; | ||
|
||
const removeTodoItem = event => { | ||
event.preventDefault(); | ||
const $target = event.target; | ||
const isDeleteButton = $target.classList.contains("destroy"); | ||
if (isDeleteButton) { | ||
removeTargetTodoItem($target); | ||
} | ||
} | ||
|
||
const removeTargetTodoItem = ($target) => { | ||
const todoItemId = Number.parseInt($target.closest("div").dataset.itemId); | ||
const targetItem = this.todoItems.find(item => item.id === todoItemId); | ||
const isConfirmDelete = confirm(`${targetItem.content}를 삭제하시겠습니까?`); | ||
if (isConfirmDelete) { | ||
const targetItemIndex = this.todoItems.indexOf(targetItem); | ||
this.todoItems.splice(targetItemIndex, 1) | ||
this.setState(this.todoItems); | ||
} | ||
} | ||
|
||
const setEditMode = event => { | ||
event.preventDefault(); | ||
const $target = event.target; | ||
const isDeleteButton = $target.classList.contains("destroy"); | ||
const isCompletedButton = $target.classList.contains("toggle"); | ||
if (!isDeleteButton && !isCompletedButton) { | ||
const todoItemId = Number.parseInt($target.closest("div").dataset.itemId); | ||
const targetItem = this.todoItems.find(item => item.id === todoItemId); | ||
targetItem.status = STATUS.EDIT; | ||
this.todoList.setState(this.todoItems); | ||
document.querySelector("#todo-list > li.editing > input").addEventListener(EVENT_TYPE.KEY_DOWN, editMode); | ||
} | ||
} | ||
|
||
const editMode = event => { | ||
const $target = event.target; | ||
const todoItemId = Number.parseInt($target.previousSibling.previousSibling.dataset.itemId); | ||
const targetItem = this.todoItems.find(item => item.id === todoItemId); | ||
if (event.target.value !== 0 && event.key === KEY_TYPE.ESC) { | ||
event.preventDefault(); | ||
targetItem.status = STATUS.TODO; | ||
this.todoList.setState(this.todoItems); | ||
return; | ||
} | ||
if ($target.value.trim() !== 0 && event.key === KEY_TYPE.ENTER) { | ||
const updateItem = new TodoItem(targetItem.id, $target.value, STATUS.TODO); | ||
const targetItemIndex = this.todoItems.indexOf(targetItem); | ||
this.todoItems.splice(targetItemIndex, 1, updateItem); | ||
this.todoList.setState(this.todoItems); | ||
} | ||
} | ||
|
||
const initEventListener = () => { | ||
this.$todoList.addEventListener(EVENT_TYPE.CLICK, completedTodoItem); | ||
this.$todoList.addEventListener(EVENT_TYPE.CLICK, removeTodoItem); | ||
this.$todoList.addEventListener(EVENT_TYPE.DOUBLE_CLICK, setEditMode); | ||
} | ||
|
||
this.init = () => { | ||
initEventListener(); | ||
} | ||
} | ||
|
||
const todoApp = new TodoApp(); | ||
todoApp.init(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import {EVENT_TYPE} from "../util/constants.js"; | ||
|
||
export function TodoCount(showItems) { | ||
this.number = document.querySelector("div.count-container > span > strong"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 |
||
this.allTodoItems = document.querySelector("div.count-container > ul > li:nth-child(1) > a"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. element를 찾기 위한 query가 길어진다는 생각이 들어요 😢 |
||
this.todoItems = document.querySelector("div.count-container > ul > li:nth-child(2) > a"); | ||
this.doneItems = document.querySelector("div.count-container > ul > li:nth-child(3) > a"); | ||
|
||
this.initEventListener = () => { | ||
this.allTodoItems.addEventListener(EVENT_TYPE.CLICK, showItems); | ||
this.todoItems.addEventListener(EVENT_TYPE.CLICK, showItems); | ||
this.doneItems.addEventListener(EVENT_TYPE.CLICK, showItems); | ||
} | ||
|
||
return this.initEventListener(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { EVENT_TYPE, KEY_TYPE } from "../util/constants.js"; | ||
|
||
export function TodoInput({ onAdd }, todoInput) { | ||
todoInput.addEventListener(EVENT_TYPE.KEY_DOWN, event => this.addTodoItem(event)); | ||
|
||
this.isValid = (event, value) => { | ||
return (event.key === KEY_TYPE.ENTER) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. enter키를 체크하는 로직이 반복되고 있는데요. util성 메서드를 모아놓는 파일을 따로 만들어서 |
||
&& value.trim().length !== 0; | ||
} | ||
|
||
this.addTodoItem = event => { | ||
const $newTodoTarget = event.target; | ||
if (this.isValid(event, $newTodoTarget.value)) { | ||
event.preventDefault(); | ||
onAdd($newTodoTarget.value); | ||
$newTodoTarget.value = ""; | ||
} | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export function TodoItem(id, content, status) { | ||
this.id = id; | ||
this.content = content; | ||
this.status = status; | ||
|
||
return this; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { todoItemTemplate } from '../util/templates.js' | ||
|
||
export function TodoList(todoList) { | ||
this.setState = updatedTodoItems => { | ||
this.todoItems = updatedTodoItems; | ||
this.render(this.todoItems); | ||
}; | ||
|
||
this.render = items => { | ||
const template = items.map(todoItemTemplate); | ||
todoList.innerHTML = template.join(""); | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
export const STATUS = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 디렉토리 경로가 |
||
TODO: "todo", | ||
COMPLETED: "completed", | ||
EDIT: "editing" | ||
}; | ||
|
||
export const EVENT_TYPE = { | ||
CLICK: "click", | ||
KEY_PRESS: "keypress", | ||
DOUBLE_CLICK: "dblclick", | ||
KEY_DOWN: "keydown" | ||
}; | ||
|
||
export const KEY_TYPE = { | ||
ESC: "Escape", | ||
ENTER: "Enter" | ||
}; | ||
|
||
export const TAG_TYPE = { | ||
ALL: "#/", | ||
ACTIVE: "#/active", | ||
COMPLETED: "#/completed" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import {STATUS} from "./constants.js"; | ||
|
||
export const todoItemTemplate = item => { | ||
const isChecked = item.status === STATUS.COMPLETED ? "checked" : ""; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
return `<li class="${item.status}"> | ||
<div class="view" data-item-id="${item.id}"> | ||
<input class="toggle" type="checkbox" ${isChecked}> | ||
<label class="label">${item.content}</label> | ||
<button class="destroy"></button> | ||
</div> | ||
<input class="edit" value="${item.content}"> | ||
</li>` | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TodoCount
에서TodoApp
에 종속적인this.setState
만 파라미터로 넘겨주고, 나머지 구체적인 구현부는TodoCount.js
안에서 구현해주면TodoApp
이 더욱 깔끔해질 것 같은데 라빈의 생각은 어떤가요? 🤔