Appearance
Example: To-Do List
examplejs
import { compose } from 'paintor'
import { ToDoList } from './components/ToDoList.js'
const initialTasks = [
{ title: 'Make to-do list', completed: true },
{ title: 'Hit the gym', completed: false }
]
compose(
ToDoList({ initialTasks })
).paint('#to-do-list')
js
import { state, template } from 'paintor'
import { ToDoHeader } from './ToDoHeader.js'
import { ToDoTasks } from './ToDoTasks.js'
const tasks = state([])
export function ToDoList({ initialTasks }) {
// Push the initial tasks into the state
for (const initialTask of initialTasks) {
tasks.push(initialTask)
}
return template(() => [
ToDoHeader({ tasks }),
ToDoTasks({ tasks }),
])
}
js
import { template } from 'paintor'
const newTask = { title: '', completed: false }
function addTask(tasks, newTask) {
if (!newTask.title) {
alert('You must write a title for your new task!')
}
else {
tasks.push({ ...newTask })
}
}
export function ToDoHeader(props) {
return template((x) => {
x.h3('Tasks ', x.span(() => `(${props.tasks.length})`)),
x.div({ class: 'header' },
x.input(
{
type: 'text',
placeholder: 'New Task...',
onKeyUp: (event) => {
newTask.title = event.target.value
}
}
),
x.button({
onClick: () => addTask(props.tasks, newTask) },
'Add'
)
)
})
}
js
import { template } from 'paintor'
import { ToDoTaskItem } from './ToDoTaskItem.js'
export function ToDoTasks(props) {
return template((x) => {
x.ul(
x.$each(props.tasks, ToDoTaskItem.bind(null, props.tasks))
)
})
}
js
import { template } from 'paintor'
function deleteTask(tasks, task) {
tasks.splice(getTaskIndex(tasks, task), 1)
}
function getTaskIndex(tasks, task) {
return tasks.indexOf(task)
}
function toggleCompleted(task) {
task.completed = !task.completed
}
export function ToDoTaskItem(tasks, task) {
return template((x) => {
x.li({
class: () => (task.completed ? 'completed' : ''),
onClick: () => toggleCompleted(task)
},
task.title,
x.button(
{
onClick: () => deleteTask(tasks, task)
},
'\u00D7'
)
)
})
}
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>