Last active
April 29, 2019 15:11
-
-
Save hanjae-jea/453c9f8c9cd5f31d0e559fa008d22a1c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie-edge"> | |
<title>Document</title> | |
<style> | |
main .gnb{ | |
} | |
main .folder{ | |
float: left; | |
width: 200px; | |
border-right: 1px solid #000; | |
} | |
main .task{ | |
margin-left:210px; | |
} | |
</style> | |
</head> | |
<body> | |
<main> | |
<nav class="gnb"> | |
<button>load</button> | |
<button>save</button> | |
</nav> | |
<nav class="folder"> | |
<input type="text"> | |
<ul></ul> | |
</nav> | |
<section class="task"> | |
<header> | |
<h2></h2> | |
<input type="text"> | |
</header> | |
<ul></ul> | |
</section> | |
</main> | |
<script src="index.js"> | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const err = v =>{ | |
throw v; | |
} | |
const Task = class{ | |
static load(json){ | |
const task = new Task(json.title, json.isComplete); | |
return task; | |
} | |
toJSON(){ | |
return this.getInfo(); | |
} | |
static get(title){ | |
return new Task(title); | |
} | |
constructor(title, isComplete = false){ | |
this.title = title; | |
this.isComplete = false; | |
} | |
toggle(){this.isComplete = !this.isComplete;} | |
getInfo(){return {title:this.title, isComplete:this.isComplete};} | |
}; | |
const Folder = class extends Set{ | |
static load(json){ | |
const folder = new Folder(json.title); | |
json.tasks.forEach(task=>{ | |
this.addTask(Task.load(task)); | |
}); | |
return folder; | |
} | |
toJSON(){ | |
return {title: this.title, tasks: this.getTasks()}; | |
} | |
static get(title){ | |
return new Folder(title); | |
} | |
constructor(title){ | |
super(); | |
this.title = title; | |
} | |
moveTask(task, srcFolder){ | |
if( super.has(task) || !srcFolder.has(task) ) return err('...'); | |
srcFolder.removeTask(task); | |
this.addTask(task); | |
} | |
addTask(task){ | |
if( !(task instanceof Task) ) err('invalid task'); | |
super.add(task); | |
} | |
removeTask(task){ | |
if( !(task instanceof Task) ) err('invalid task'); | |
super.delete(task); | |
} | |
getTitle(){return this.title;} | |
getTasks(){return [...super.values()];} | |
add(){err('...');} | |
delete(){err('...');} | |
clear(){err('...');} | |
values(){err('...');} | |
}; | |
const App = class extends Set{ | |
static load(json){ | |
const app = new App(); | |
json.forEach(folder=>{ | |
app.addFolder(Folder.load(f)); | |
}); | |
return app; | |
} | |
toJSON(){ | |
return this.getFolders(); | |
} | |
constructor(renderer){ | |
super(); | |
} | |
addFolder(folder){ | |
if( !(folder instanceof Folder) ) err('invalid folder'); | |
super.add(folder); | |
} | |
removeFolder(Folder){ | |
if( !(folder instanceof Folder) ) err('invalid folder'); | |
super.delete(folder); | |
} | |
getFolders(){return [...super.values()];} | |
add(){err('...');} | |
delete(){err('...');} | |
clear(){err('...');} | |
values(){err('...');} | |
}; | |
const Renderer = class{ | |
constructor(app){this.app = app;} | |
render(){this._render();} | |
_render(){throw 1;} | |
}; | |
const el = tag => document.createElement(tag); | |
const DomRenderer = class extends Renderer{ | |
constructor(parent, app){ | |
super(app); | |
this.taskEl = []; | |
const [folder, task] = Array.from(parent.querySelectorAll('ul')); | |
const [load, save] = Array.from(parent.querySelectorAll('button')); | |
load.onclick=e=>{ | |
const v = localStorage['todo']; | |
if(v){ | |
this.app = App.load(JSON.parse(v)); | |
this.render(); | |
} | |
}; | |
save.onclick=e=>{ | |
localStorage['todo'] = JSON.stringify(this.app); | |
}; | |
this.folder = folder; | |
this.task = task; | |
this.currentFolder = null; | |
parent.querySelector('nav>input').addEventListener('keyup', e=>{ | |
if(e.keyCode != 13) return; | |
const v = e.target.value; | |
const folder = Folder.get(v); | |
this.app.addFolder(folder); | |
e.target.value = ''; | |
this.render(); | |
}); | |
parent.querySelector('header>input').addEventListener('keyup', e=>{ | |
if(e.keyCode != 13 || !this.currentFolder) return; | |
const v = e.target.value; | |
const task = Task.get(v); | |
this.currentFolder.addTask(task); | |
e.target.value = ''; | |
this.render(); | |
}); | |
} | |
_render(){ | |
const folders = this.app.getFolders(); | |
let moveTask, tasks; | |
if(!folders.length) return; | |
if(!this.currentFolder) this.currentFolder = folders[0]; | |
let oldEl = this.folder.firstElementChild, lastEl=oldEl; | |
folders.forEach(f=>{ | |
let li; | |
if( oldEl ){ | |
li = oldEl; | |
oldEl = oldEl.nextElementSibling; | |
} | |
else { | |
li = el('li'); | |
this.folder.appendChild(li); | |
oldEl = null; | |
} | |
lastEl = li; | |
li.innerHTML = f.getTitle(); | |
li.style.color = this.currentFolder == f ? '#000' : '#777'; | |
li.onclick=()=>{ | |
this.currentFolder = f; | |
this.render(); | |
}; | |
li.ondrop=e=>{ | |
e.preventDefault(); | |
f.moveTask(moveTask, this.currentFolder); | |
this.render(); | |
}; | |
li.ondragover=e=>{ | |
e.preventDefault(); | |
}; | |
}); | |
if(lastEl){ | |
while(oldEl = lastEl.nextElementSibling){ | |
this.folder.removeChild(oldEl); | |
} | |
} | |
if(!this.currentFolder) return; | |
tasks = this.currentFolder.getTasks(); | |
if( !tasks.length ){ | |
while( oldEl = this.task.firstElementChild ){ | |
this.task.removeChild(oldEl); | |
this.taskEl.push(oldEl); | |
} | |
} | |
oldEl = this.task.firstElementChild, lastEl = oldEl; | |
tasks.forEach(t=>{ | |
let li; | |
if( oldEl ){ | |
li = oldEl; | |
oldEl = oldEl.nextElementSibling; | |
}else{ | |
li = this.taskEl.length ? this.taskEl.pop() : el('li'); | |
this.task.appendChild(li); | |
oldEl = null; | |
} | |
lastEl = li; | |
const {title, isComplete} = t.getInfo(); | |
li.setAttribute('draggable', true); | |
li.innerHTML = (isComplete?'completed ':'process ') + title; | |
li.onclick=()=>{ | |
t.toggle(); | |
this.render(); | |
}; | |
li.ondragstart=()=>{ | |
moveTask = t; | |
}; | |
}); | |
if( lastEl ){ | |
while( oldEl = lastEl.nextElementSibling ){ | |
this.task.removeChild(oldEl); | |
this.taskEl.push(oldEl); | |
} | |
} | |
} | |
} | |
const todo = new DomRenderer(document.querySelector('main'), new App()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment