diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..6b665aaa --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} diff --git a/index.html b/index.html index 13a02fdb..6b98146c 100644 --- a/index.html +++ b/index.html @@ -3,36 +3,30 @@ - 이벤트 - TODOS + Tami's Todo + + -
-

TODOS

- -
- - -
- 0 - -
-
+
+
+

+ TODOS +

+
+ +
+
+
diff --git a/index.js b/index.js new file mode 100644 index 00000000..fbb77074 --- /dev/null +++ b/index.js @@ -0,0 +1,2 @@ +import TodoApp from './src/js/TodoApp.js'; +new TodoApp(document.querySelector('.App')); diff --git a/src/css/style.css b/src/css/style.css index 7dcbc6db..c7ac568d 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -1,334 +1,378 @@ html, body { - margin: 0; - padding: 10px; + 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; + 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; + 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; + outline: 0; } .hidden { - display: none; + display: none; +} +.todoWrap { + display: flex; } .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); + min-width: 500px; + 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; + 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; + 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; + 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); + 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; + 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%; + 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); + 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; + content: '❯'; + font-size: 22px; + color: #e6e6e6; + padding: 10px 27px 10px 27px; } .toggle-all:checked + label:before { - color: #737373; + color: #737373; } .todo-list { - margin: 0; - padding: 0; - list-style: none; + margin: 0; + padding: 0; + list-style: none; } .todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px solid #ededed; + position: relative; + font-size: 24px; + border-bottom: 1px solid #ededed; } .todo-list li:last-child { - border-bottom: none; + border-bottom: none; } .todo-list li.editing { - border-bottom: none; - padding: 0; + border-bottom: none; + padding: 0; } .todo-list li.editing .edit { - display: block; - width: calc(100% - 43px); - padding: 12px 16px; - margin: 0 0 0 43px; + display: block; + width: calc(100% - 43px); + padding: 12px 16px; + margin: 0 0 0 43px; } .todo-list li.editing .view { - display: none; + 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; + text-align: center; + width: 40px; + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + border: none; + -webkit-appearance: none; + appearance: none; + cursor: pointer; } .todo-list li .toggle { - opacity: 0; + 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; + 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'); + 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; + 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; + 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; + 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; + color: #af5b5e; } .todo-list li .destroy:after { - content: '×'; + content: '×'; } .todo-list li:hover .destroy { - display: block; + display: block; } .todo-list li .edit { - display: none; + display: none; } .todo-list li.editing:last-child { - margin-bottom: -1px; + margin-bottom: -1px; } .count-container { - color: #777; - padding: 10px 15px; - height: 20px; - text-align: center; - border-top: 1px solid #e6e6e6; + 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, + 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; + float: left; + text-align: left; } .todo-count strong { - font-weight: 300; + font-weight: 300; } .filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; } .filters li { - display: inline; + display: inline; } .filters li a { - color: inherit; - margin: 3px; - padding: 3px 7px; - text-decoration: none; - border: 1px solid transparent; - border-radius: 3px; + 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); + border-color: rgba(175, 47, 47, 0.1); } .filters li a.selected { - border-color: rgba(175, 47, 47, 0.2); + 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, +html .clear-completed:active { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + cursor: pointer; } .clear-completed:hover { - text-decoration: underline; + 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; + 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; + line-height: 1; } .info a { - color: inherit; - text-decoration: none; - font-weight: 400; + color: inherit; + text-decoration: none; + font-weight: 400; } .info a:hover { - text-decoration: underline; + text-decoration: underline; +} + +.userApp { + /* margin-left: 50px; */ + min-width: 200px; + font-size: 20px; +} + +.user-drop { + margin-left: 40px; + font-size: 20px; + cursor: pointer; + color: dimgray; + padding: 3px 7px; + border: 1px solid transparent; + border-radius: 3px; +} + +.user-list { + list-style: none; +} +.user-list li { + float: left; + margin-left: 20px; + margin-bottom: 10px; + + border: 1px solid transparent; + + border-radius: 3px; + + outline: 1px solid #c9c9c9; + padding: 3px; + cursor: pointer; +} +.user-list li:hover { + color: white; + background-color: #c1c1c1; +} + +.drop { + display: none; } diff --git a/src/images/tamicon.ico b/src/images/tamicon.ico new file mode 100644 index 00000000..e528ec9d Binary files /dev/null and b/src/images/tamicon.ico differ diff --git a/src/js/TodoApp.js b/src/js/TodoApp.js new file mode 100644 index 00000000..0dfbb376 --- /dev/null +++ b/src/js/TodoApp.js @@ -0,0 +1,179 @@ +import TodoFilter from './components/TodoFilter.js'; +import TodoInput from './components/TodoInput.js'; +import TodoList from './components/TodoList.js'; +import { FILTER_TYPES } from '../utils/const.js'; +import getUserList from './core/getUserList.js'; +import UserList from './components/user/UserList.js'; +import getUserData from './core/getUserData.js'; + +export default function TodoApp($app) { + const initialData = { + todoes: [ + { + idx: 0, + content: 'hiEvery One', + state: '', + }, + { + idx: 1, + content: 'Im Tami', + state: '', + }, + ], + todoesFiltered: [], + filterState: FILTER_TYPES.ALL, + todoesCount: '0', + users: [], + }; + const localData = JSON.parse(localStorage.getItem('state')); + const viewData = localData ? localData : initialData; + this.state = viewData; + + this.setState = (nextState) => { + this.state = nextState; + todoList.setState(nextState); + todoInput.setState(nextState); + todoFilter.setState(nextState); + userList.setState(nextState); + }; + + const todoInput = new TodoInput({ + $app, + initialState: this.state, + onAdd: (contents) => addTodo(contents), + }); + + const todoList = new TodoList({ + $app, + initialState: this.state, + onToggle: (idx) => toggleTodo(idx), + onDelete: (idx) => deleteTodo(idx), + onEdit: (idx, isEdit, newContent) => editTodo(idx, isEdit, newContent), + }); + const todoFilter = new TodoFilter({ + $app, + + onFilter: (filterType) => filterTodo(filterType), + }); + + const userList = new UserList({ + initialState: this.state, + onUser: (userId) => updateTodo(userId), + }); + + const updateTodo = (userId) => { + getUserData(userId); + }; + + const addTodo = (addContent) => { + const { todoes } = this.state; + const nextIdx = Math.max(0, ...todoes.map((todo) => todo.idx)) + 1; + const newTodo = { + idx: nextIdx, + content: addContent, + state: '', + edit: '', + }; + todoes.push(newTodo); + localStorage.clear(); + localStorage.setItem('state', JSON.stringify({ ...this.state })); + this.state = JSON.parse(localStorage.getItem('state')); + this.setState({ + ...this.state, + }); + }; + + const toggleTodo = (idx) => { + const { todoes } = this.state; + + todoes.map((todo) => { + if (todo.idx === idx) { + todo.state = todo.state === '' ? FILTER_TYPES.COMPLETE : ''; + } + }); + localStorage.clear(); + localStorage.setItem('state', JSON.stringify({ ...this.state })); + this.state = JSON.parse(localStorage.getItem('state')); + this.setState({ + ...this.state, + }); + }; + + const deleteTodo = (idx) => { + const { todoes } = this.state; + + const resetTodoes = todoes.filter((todo) => { + return todo.idx !== idx; + }); + this.state['todoes'] = resetTodoes; + localStorage.clear(); + localStorage.setItem('state', JSON.stringify({ ...this.state })); + this.state = JSON.parse(localStorage.getItem('state')); + this.setState({ + ...this.state, + }); + }; + + const editTodo = (idx, isEdit, newContent) => { + const { todoes } = this.state; + + todoes.map((todo) => { + if (todo.idx === idx) { + todo.state = todo.state === '' ? 'editing' : ''; + if (isEdit) { + todo.content = newContent; + } + } + }); + localStorage.clear(); + localStorage.setItem('state', JSON.stringify({ ...this.state })); + this.state = JSON.parse(localStorage.getItem('state')); + this.setState({ + ...this.state, + }); + this.setState({ + ...this.state, + }); + }; + + const filterTodo = (filterType) => { + const { todoes } = this.state; + if (filterType === FILTER_TYPES.ALL) { + this.setState({ + ...this.state, + filterState: FILTER_TYPES.ALL, + todoesCount: todoes.length, + }); + } else if (filterType === FILTER_TYPES.COMPLETE) { + const completedTodoes = todoes.filter( + (todo) => todo.state === FILTER_TYPES.COMPLETE + ); + + this.setState({ + ...this.state, + todoesFiltered: completedTodoes, + filterState: FILTER_TYPES.COMPLETE, + todoesCount: completedTodoes.length, + }); + } else if (filterType === FILTER_TYPES.ACTIVE) { + const activeTodoes = todoes.filter( + (todo) => todo.state !== FILTER_TYPES.COMPLETE + ); + this.setState({ + ...this.state, + todoesFiltered: activeTodoes, + filterState: FILTER_TYPES.ACTIVE, + todoesCount: activeTodoes.length, + }); + } + }; + + const init = async () => { + const userData = await getUserList(); + this.state['users'] = userData; + this.setState({ + ...this.state, + }); + }; + init(); +} diff --git a/src/js/components/TodoFilter.js b/src/js/components/TodoFilter.js new file mode 100644 index 00000000..2ee42498 --- /dev/null +++ b/src/js/components/TodoFilter.js @@ -0,0 +1,55 @@ +import TodoLocalStore from '../core/TodoLocalStore.js'; +import { FILTER_TYPES, TODO_FILTER_MENU } from '../../utils/const.js'; +export default function TodoFilter({ $app, onFilter }) { + // const todoLocalStore = new TodoLocalStore(); + // this.state = todoLocalStore.getItems(); + this.state = localStorage.getItem('state'); + + this.$target = document.createElement('div'); + this.$target.className = 'count-container'; + $app.appendChild(this.$target); + const $nodeTodoFilter = this.$target; + + this.setState = (nextState) => { + this.state = nextState; + this.render(); + }; + + $nodeTodoFilter.addEventListener('click', (e) => { + const $node = e.target; + const nodeClass = $node.className.trim(); + if (nodeClass === FILTER_TYPES.COMPLETE) { + onFilter(FILTER_TYPES.COMPLETE); + } else if (nodeClass === FILTER_TYPES.ACTIVE) { + onFilter(FILTER_TYPES.ACTIVE); + } else { + onFilter(FILTER_TYPES.ALL); + } + }); + + this.render = () => { + const { todoes, todoesFiltered, filterState } = this.state; + const todoTotalCount = + filterState === FILTER_TYPES.ALL ? todoes.length : todoesFiltered.length; + const todoFilterTemplate = ` + ${todoTotalCount} + `; + $nodeTodoFilter.innerHTML = todoFilterTemplate; + }; +} diff --git a/src/js/components/TodoInput.js b/src/js/components/TodoInput.js new file mode 100644 index 00000000..d388f13b --- /dev/null +++ b/src/js/components/TodoInput.js @@ -0,0 +1,26 @@ +export default function TodoInput({ $app, initialState, onAdd }) { + this.state = initialState; + this.$target = document.createElement('input'); + this.$target.id = 'new-todo-title'; + this.$target.className = 'new-todo'; + this.$target.placeholder = '할일 추가하기'; + this.$target.autofocus; + $app.appendChild(this.$target); + + this.setState = (nextState) => { + this.state = nextState; + }; + + this.$target.addEventListener('keydown', (e) => { + this.addTodoItem(e); + }); + + this.addTodoItem = (e) => { + const $newTodoTarget = e.target; + + if (e.keyCode === 13) { + onAdd($newTodoTarget.value); + $newTodoTarget.value = ''; + } + }; +} diff --git a/src/js/components/TodoList.js b/src/js/components/TodoList.js new file mode 100644 index 00000000..11a3c807 --- /dev/null +++ b/src/js/components/TodoList.js @@ -0,0 +1,100 @@ +import TodoLocalStore from '../core/TodoLocalStore.js'; +import { FILTER_TYPES, TODO_ITEM_CLASS } from '../../utils/const.js'; + +export default function TodoList({ + $app, + initialState, + onToggle, + onDelete, + onEdit, +}) { + this.state = JSON.parse(localStorage.getItem('state')); + this.$target = document.createElement('ul'); + this.$target.className = 'todo-list'; + this.$target.id = 'todo-list'; + $app.appendChild(this.$target); + const $nodeTodoList = this.$target; + + this.setState = (nextState) => { + this.state = nextState; + this.render(); + }; + + $nodeTodoList.addEventListener('click', (e) => { + const $node = e.target; + + if ($node.className === TODO_ITEM_CLASS.TOGGLE) { + this.toggleTodoItem($node); + } + if ($node.className === TODO_ITEM_CLASS.DESTROY) { + this.deleteTodoItem($node); + } + + if ($node.className === TODO_ITEM_CLASS.EDIT) { + this.editTodoItem($node); + } + }); + + $nodeTodoList.addEventListener('dblclick', (e) => { + const $node = e.target; + this.editTodoItem($node); + }); + + this.toggleTodoItem = ($node) => { + const { nodeId } = $node.closest(`.${TODO_ITEM_CLASS.VIEW}`).parentNode + .dataset; + onToggle(parseInt(nodeId)); + }; + + this.deleteTodoItem = ($node) => { + const { nodeId } = $node.closest(`.${TODO_ITEM_CLASS.VIEW}`).parentNode + .dataset; + onDelete(parseInt(nodeId)); + }; + + this.editTodoItem = ($node) => { + if ($node.className === TODO_ITEM_CLASS.EDIT) { + const { nodeId } = $node.parentNode.dataset; + $node.addEventListener('keydown', (e) => { + //Enter key 입력 + if (e.keyCode === 13) { + onEdit(parseInt(nodeId), true, $node.value); + } //esc key 입력 + else if (e.keyCode === 27) { + onEdit(parseInt(nodeId), false, ''); + } + }); + } else { + const { nodeId } = $node.closest(`.${TODO_ITEM_CLASS.VIEW}`).parentNode + .dataset; + if ($node.className === TODO_ITEM_CLASS.LABEL) { + onEdit(parseInt(nodeId), false, ''); + } + } + }; + + this.render = () => { + const { todoes, filterState, todoesFiltered } = this.state; + const viewTodoes = + filterState === FILTER_TYPES.ALL ? todoes : todoesFiltered; + const todoTemplate = `${viewTodoes + .map( + (todo, idx) => + `
  • +
    + + + +
    + +
  • ` + ) + .join('')}`; + $nodeTodoList.innerHTML = todoTemplate; + }; +} diff --git a/src/js/components/user/UserList.js b/src/js/components/user/UserList.js new file mode 100644 index 00000000..130052e8 --- /dev/null +++ b/src/js/components/user/UserList.js @@ -0,0 +1,41 @@ +export default function UserList({ initialState, onUser }) { + this.state = initialState; + + this.$targetBtn = document.createElement('input'); + this.$targetBtn.type = 'button'; + this.$targetBtn.value = '📄 USER'; + this.$targetBtn.className = 'user-drop'; + + this.$target = document.createElement('ul'); + this.$target.className = 'user-list'; + this.$target.id = 'user-list'; + const $userApp = document.querySelector('.userApp'); + $userApp.append(this.$targetBtn, this.$target); + + const { users } = this.state; + + this.setState = (newState) => { + this.state = newState; + this.render(); + }; + + this.$targetBtn.addEventListener('click', (e) => { + if (e.target.className === 'user-drop') { + let $userList = document.getElementById('user-list'); + $userList.classList.toggle('drop'); + } + }); + this.$target.addEventListener('click', (e) => { + const { userId } = e.target.dataset; + console.log(userId); + onUser(userId); + }); + + this.render = () => { + const userListTemplate = `${users + .map((user) => `
  • ${user.name}
  • `) + .join('')}`; + + this.$target.innerHTML = userListTemplate; + }; +} diff --git a/src/js/core/TodoLocalStore.js b/src/js/core/TodoLocalStore.js new file mode 100644 index 00000000..2e557e46 --- /dev/null +++ b/src/js/core/TodoLocalStore.js @@ -0,0 +1,34 @@ +//아직 쓰이지 않음 +import { FILTER_TYPES } from '../../utils/const.js'; +export default function TodoLocalStore() { + this.setState = (nextState) => { + this.state = nextState; + }; + this.setItems = (newState) => { + this.state = { ...this.state, ...newState }; + // localStorage.clear(); + // localStorage.removeItem('state'); + localStorage.setItem('state', JSON.stringify(this.state)); + }; + this.getItems = () => { + const initialDtate = { + todoes: [ + { + idx: 0, + content: 'Hi Every One', + state: '', + }, + { + idx: 1, + content: 'Im Tami', + state: '', + }, + ], + todoesFiltered: [], + filterState: FILTER_TYPES.ALL, + todoesCount: '0', + }; + const localData = JSON.parse(localStorage.getItem('state')); + return localData ? localData : initialDtate; + }; +} diff --git a/src/js/core/getUserData.js b/src/js/core/getUserData.js new file mode 100644 index 00000000..6aeb2bbc --- /dev/null +++ b/src/js/core/getUserData.js @@ -0,0 +1,15 @@ +const getUserData = async (userId) => { + const BASE_URL = 'https://js-todo-list-9ca3a.df.r.appspot.com'; + try { + const res = await fetch(`${BASE_URL}/api/users/:userId`); + if (res.ok) { + console.log('유저만의데이터', res); + return await res.json(); + } else { + return null; + } + } catch (e) { + throw new Error(`오류가 생겼습니다 ${e.message}`); + } +}; +export default getUserData; diff --git a/src/js/core/getUserList.js b/src/js/core/getUserList.js new file mode 100644 index 00000000..4c7f600c --- /dev/null +++ b/src/js/core/getUserList.js @@ -0,0 +1,14 @@ +const getUserList = async () => { + const BASE_URL = 'https://js-todo-list-9ca3a.df.r.appspot.com'; + try { + const res = await fetch(`${BASE_URL}/api/users`); + if (res.ok) { + return await res.json(); + } else { + return null; + } + } catch (e) { + throw new Error(`오류가 생겼습니다 ${e.message}`); + } +}; +export default getUserList; diff --git a/src/utils/const.js b/src/utils/const.js new file mode 100644 index 00000000..4df252da --- /dev/null +++ b/src/utils/const.js @@ -0,0 +1,19 @@ +export const FILTER_TYPES = { + ALL: 'all', + COMPLETE: 'completed', + ACTIVE: 'active', +}; + +export const TODO_ITEM_CLASS = { + TOGGLE: 'toggle', + DESTROY: 'destroy', + EDIT: 'edit', + LABEL: 'label', + VIEW: 'view', +}; + +export const TODO_FILTER_MENU = { + ALL_MENU: '전체보기', + ACTIVE_MENU: '해야할 일', + COMPLETE_MENU: '완료한 일', +};