Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ <h1>TODOS</h1>
</div>
</main>
</div>
<script src="./src/index.js" type="module"></script>
</body>
</html>
9 changes: 9 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import TodoInput from "./components/TodoInput.js";
import TodoList from "./components/TodoList.js";
import { $ } from "./utils/selectors.js";

export default function App(store) {
store.addObserver(new TodoInput(store, $(".new-todo")));
store.addObserver(new TodoList(store, $(".todo-list")));
}

28 changes: 28 additions & 0 deletions src/components/TodoInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { $ } from "../utils/selectors.js";

const CIPHER = 1000;

export default class TodoInput {
constructor(store, $app) {
this.store = store;
this.$app = $app;
this.mount();
}
mount() {
this.$app.addEventListener("keypress", this.handleInputValue.bind(this));
}
render() {}
handleInputValue(e) {
if (e.key === "Enter") {
const prevState = this.store.getState();
const newTodo = {
id: Math.floor(Math.random() * CIPHER),
content: e.target.value,
status: "false",
};
const newState = { ...prevState, todos: [...prevState.todos, newTodo] };
this.store.setState(newState);
e.target.value = "";
}
}
}
60 changes: 60 additions & 0 deletions src/components/TodoList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { buildNewState } from "../utils/helpers.js";
import { $ } from "../utils/selectors.js";

export default class TodoList {
constructor(store, $app) {
this.store = store;
this.$app = $app;
this.mount();
}
mount() {
this.$app.addEventListener("click", (e) => {
const isToggle = e.target.classList.contains("toggle");
const isDestroy = e.target.classList.contains("destroy");
if (isToggle) {
const newState = buildNewState("TOGGLE", this.store, e);
this.store.setState(newState);
}
if (isDestroy) {
const newState = buildNewState("DELETE", this.store, e);
this.store.setState(newState);
}
});
this.$app.addEventListener("dblclick", (e) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이벤트 콜백 함수를 따로 만들어서 관리하는 방법도 좋지 않을 까라는 생각을 해보았습니다.ㅎㅎ

const isList = e.target.closest("li");
if (isList) {
isList.classList.add("editing");
}
});
this.$app.addEventListener("keydown", (e) => {
const isEditing = e.target.classList.contains("edit");

if (isEditing && e.key === "Enter") {
const newState = buildNewState("EDIT", this.store, e);
this.store.setState(newState);
e.target.closest("li").classList.remove("editing");
}
if (isEditing && e.key === "Escape") {
const currentValue = $(".label").textContent;
e.target.value = currentValue;
e.target.closest("li").classList.remove("editing");
}
});
}
render() {
const newState = this.store.getState();
this.$app.innerHTML = newState.todos
.map(({ id, content, status, edit }) => {
const isChecked = status === "completed" ? "checked" : "false";
return `<li dataset-id=${id} class="${status} ${edit}">
<div class="view">
<input class="toggle" type="checkbox" ${isChecked}>
<label class="label">${content}</label>
<button class="destroy" ></button>
</div>
<input class="edit" value="${content}">
</li>`;
})
.join("");
}
}
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import App from "./App.js";
import Store from "./store/index.js";
new App(new Store());
22 changes: 22 additions & 0 deletions src/store/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export default function Store() {
//State
this.state = {
todos: [],
status: "all",
};
//Observer
this.observers = [];
this.addObserver = (observer) => this.observers.push(observer);
this.observing = () =>
this.observers.forEach((observer) => observer.render());

//GET
this.getState = () => {
return this.state;
};
//SET
this.setState = (newState) => {
this.state = { ...this.state, ...newState };
this.observing();
};
}
44 changes: 44 additions & 0 deletions src/utils/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//NEWSTATE
export function buildNewState(op, store, e) {
const OPERATIONS = {
TOGGLE: toggleTodoStatus,
DELETE: deleteTodo,
EDIT: editTodo,
};
const prevState = store.getState();
const targetId = Number(e.target.closest("li").getAttribute("dataset-id"));

const newTodos = OPERATIONS[op](prevState, targetId, e);

const newState = { ...prevState, todos: newTodos };
return newState;
}

//NEWTODOS
function toggleTodoStatus(prevState, targetId, e) {
const newStatus = e.target.checked ? "completed" : "false";
const newTodos = prevState.todos.map((todo) => {
if (todo.id === targetId) {
return { ...todo, status: newStatus };
}
return todo;
});
return newTodos;
}

function deleteTodo(prevState, targetId) {
const newTodos = prevState.todos.filter((todo) => {
return todo.id !== targetId;
});
return newTodos;
}

function editTodo(prevState, targetId, e) {
const newTodos = prevState.todos.map((todo) => {
if (todo.id === targetId) {
return { ...todo, content: e.target.value };
}
return todo;
});
return newTodos;
}
2 changes: 2 additions & 0 deletions src/utils/selectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const $ = (node) => document.querySelector(node);
export const $all = (node) => document.querySelectorAll(node)