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()
     ...