Skip to content

To-Do List Example

example
js
import { ToDoList } from './components/ToDoList.js'

const initialTasks = [
  { title: 'Make to-do list', completed: true },
  { title: 'Hit the gym', completed: false }
]

ToDoList(initialTasks).paint('#to-do-list')
js
import { component, state, template } from 'paintor'

const tasks = state([])
const newTask = { title: '', completed: false }

function addTask() {
  if (!newTask.title) {
    alert('You must write a title for your new task!')
  }
  else {
    tasks.push({ ...newTask })
  }
}

function deleteTask(task) {
  tasks.splice(getTaskIndex(task), 1)
}

function getTaskIndex(task) {
  return tasks.indexOf(task)
}

function toggleCompleted(task) {
  task.completed = !task.completed
}

const todoHeader = template(({ h3, span, div, input, button }) => {
  h3('Tasks ', span(() => `(${tasks.length})`)),
    div({ class: 'header' },
      input(
        {
          type: 'text',
          placeholder: 'New Task...',
          onKeyUp: (event) => {
            newTask.title = event.target.value
          }
        }
      ),
      button({ onClick: addTask }, 'Add')
    )
})

const taskItem = (task) => template(({ li, button }) => {
  li({
      class: () => (task.completed ? 'completed' : ''),
      onClick: () => toggleCompleted(task)
    },
    task.title,
    button(
      {
        onClick: () => deleteTask(task)
      },
      '\u00D7'
    )
  )
})

const todoTasks = template(({ ul, forEach }) => {
  ul(
    forEach(tasks, taskItem)
  )
})

const ToDoList = (initialTasks) => {
  // Push the initial tasks into the state
  for (const initialTask of initialTasks) {
    tasks.push(initialTask)
  }

  // Create and return the component
  return component(
    todoHeader,
    todoTasks
  )
}

export { ToDoList }
css
#to-do-list {
  color: black;

  & h3 {
    margin: 0;
    color: deepskyblue;
    font-weight: bold;
  }

  & .header {
    padding: 1rem;
    background: deepskyblue;

    & input {
      float: left;
      width: 75%;
      padding: 0.3rem;
      background: white;
      color: black;

      &::placeholder {
        color: gray
      }
    }

    & button {
      width: 25%;
      padding: 0.3rem;
      background: #d9d9d9;
      text-align: center;
      color: #555;

      &:hover {
        background: #ccc;
      }
    }
  }

  & ul {
    list-style-type: none;
    margin: 0;
    padding: 0;

    & li {
      position: relative;
      user-select: none;
      margin: 0;
      padding: 0.3rem;
      background: #eee;
      cursor: pointer;

      &:nth-child(odd) {
        background: #f9f9f9;
      }

      &:hover {
        background: #ddd;
      }

      & button {
        position: absolute;
        right: 0;
        top: 0;
        padding: 0.3rem 0.6rem;
        background: transparent;
        border: none;
        font-size: larger;

        &:hover {
          background: deepskyblue;
        }
      }

      &.completed {
        background: #888;
        text-decoration: line-through;
      }
    }
  }
}
html
<div id="to-do-list"></div>