JSFiddle - React, Tailwind, and code Playground
HTML
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<h2>Todo:</h2>
<ol class="tasks">
<task-element
v-for="(n, i) in todos"
:key="n.id"
:task="n"
@update="Object.assign(n, $event)"
@delete="todos.splice(i, 1)"
></task-element>
</ol>
<h3>Add new task:</h3>
<input placeholder="New Todo" v-model="newTodo" @keypress.enter="addTodo">
<button type="button" @click="addTodo">Add task</button>
</div>
CSS
body {
background: #20262E;
padding: 20px;
font-family: monospace;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
width: 250px;
border-bottom: 1px solid #ccc;
margin-bottom: 20px;
}
label.islong span::before {
content: "⌛";
}
div.loaded ol.tasks li span {
opacity: 1;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
.created {
color: #999;
}
JavaScript
/*
const callbacks = new Set;
const intervalId = setInterval(() => {
const now = Date.now();
callbacks.forEach(n => n(now));
}, 1000);
*/
const timer = {
timeoutId: null,
timeoutCallback() {
const now = Date.now();
timer.callbacks.forEach(n => n(now));
timer.timeoutId = setTimeout(timer.timeoutCallback, 1000);
},
callbacks: new Set,
add(callback) {
this.callbacks.add(callback);
if (!this.timeoutId) {
this.timeoutCallback();
}
},
delete(callback) {
this.callbacks.delete(callback);
if (!this.callbacks.size) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
},
};
Vue.component('task-element', {
props: [ 'task' ],
data: () => ({
created: null,
}),
template : `
<li>
<label :class="{ islong: task.text.length >= 15 }">
<input type="checkbox" v-model="done">
<span>{{ task.text }}</span>
</label>
<button v-if="task.done" @click="$emit('delete')">delete</button>
<div v-if="created !== null" class="created">
created {{ created }} seconds ago
</div>
</li>`,
computed: {
done: {
get() {
return this.task.done;
},
set(done) {
this.$emit('update', { done });
},
},
},
created() {
const { createdAt } = this.task;
if (createdAt) {
const updateCreated = now => this.created = (now - createdAt) / 1e3 | 0;
/*callbacks*/timer.add(updateCreated);
this.$on('hook:beforeDestroy', () => /*callbacks*/timer.delete(updateCreated));
}
},
});
new Vue({
el: '#app',
data: () => ({
newTodo: '',
todos: [
{ id: 1, text: 'Task 1', done: false },
{ id: 2, text: 'Task 2', done: false, createdAt: Date.now() - 15000 },
{ id: 3, text: 'Task 3', done: true },
],
}),
methods: {
addTodo() {
const text = this.newTodo;
if (text.length) {
this.todos.push({
id: -~Math.max(0, ...this.todos.map(n => n.id)),
text,
done: false,
...