MeltJS Todo App
by Alex Ou
HTML
<body>
<div id="app"></div>
<script src="https://rawgit.com/alex-ou/opaljs/master/dist/melt.min.js"></script>
</body>
Babel + JSX
let nextId = 0
function addTodo ({model}, text) {
const todos = [
...model.todos,
{
id: nextId++,
text: text,
completed: false
}]
return Object.assign({}, model, {todos})
}
function toggleTodo ({model}, id) {
const todos = model.todos.map(todo => {
if (todo.id !== id) {
return todo
}
return {
id: todo.id,
text: todo.text,
completed: !todo.completed
}
})
return Object.assign({}, model, {todos})
}
function setVisibilityFilter ({model}, visibilityFilter) {
return Object.assign({}, model, {visibilityFilter})
}
Melt.component('todo', {
props: ['text', 'completed', 'onClick'],
template: `<li
on-click="{onClick()}"
style.*="{{'text-decoration': completed ? 'line-through' : 'none'}}">{text}</li>`
})
Melt.component('todo-list', {
props: ['todos', 'onTodoClick'],
template:
`<ul>
<todo each="item in todos"
key="{item.id}"
completed="{item.completed}"
text="{item.text}"
on-click="{onTodoClick(item.id)}">
</todo>
</ul>`
})
Melt.container('visible-todo-list', {
template: '<todo-list todos="{getVisibleTodos()}" on-todo-click="{toggleTodo}"></todo-list>',
class: class VisibleTodoListComponent {
getVisibleTodos () {
const {visibilityFilter, todos} = this.model
switch (visibilityFilter) {
case 'SHOW_ALL':
return todos
case 'SHOW_ACTIVE':
return todos.filter(todo => !todo.completed)
case 'SHOW_COMPLETED':
return todos.filter(todo => todo.completed)
default:
throw new Error('unsupported filter:' + visibilityFilter)
}
}
}
})
Melt.component('todo-link', {
props: ['active', 'onClick'],
template: `<span>
<span if="{active}">{children}</span>
<a if="{!active}" href="#" on-click="handleClick">{children}</a>'
</span>`,
class: class TodoLinkComponent {
handleClick (e) {
e.preventDefault()
...