Powered by Courvux — reactivity, computed, watchers, and localStorage persistence.
Tasks are saved to localStorage automatically. Reload the page — they persist.
The full implementation — reactivity, computed, deep watcher, inline editing, and localStorage in one file.
const STORAGE_KEY = 'courvux-demo-todos';
export default {
data: {
todos: JSON.parse(localStorage.getItem(STORAGE_KEY)) || [],
newTodo: '',
filter: 'all', // 'all' | 'active' | 'completed'
editingId: null,
editText: '',
_nextId: Date.now(),
},
computed: {
filteredTodos() {
if (this.filter === 'active') return this.todos.filter(t => !t.done);
if (this.filter === 'completed') return this.todos.filter(t => t.done);
return this.todos;
},
remaining() { return this.todos.filter(t => !t.done).length; },
allDone() { return this.todos.length > 0 && this.todos.every(t => t.done); },
},
watch: {
todos: { deep: true, handler(val) { localStorage.setItem(STORAGE_KEY, JSON.stringify(val)); } }
},
methods: {
add() {
const text = this.newTodo.trim();
if (!text) return;
this.todos = [...this.todos, { id: this._nextId++, text, done: false }];
this.newTodo = '';
},
toggle(id) { this.todos = this.todos.map(t => t.id === id ? { ...t, done: !t.done } : t); },
remove(id) { this.todos = this.todos.filter(t => t.id !== id); },
toggleAll() { const d = this.allDone; this.todos = this.todos.map(t => ({ ...t, done: !d })); },
clearCompleted() { this.todos = this.todos.filter(t => !t.done); },
startEdit(todo) {
this.editingId = todo.id;
this.editText = todo.text;
this.$nextTick(() => this.$refs['edit_' + todo.id]?.focus());
},
commitEdit(id) {
const text = this.editText.trim();
if (text) this.todos = this.todos.map(t => t.id === id ? { ...t, text } : t);
else this.remove(id);
this.editingId = null;
this.editText = '';
},
cancelEdit() { this.editingId = null; this.editText = ''; },
setFilter(f) { this.filter = f; },
},
template: `...`, // see HTML tab
};