Skip to content

Instantly share code, notes, and snippets.

@FrankFan
Created January 28, 2021 10:14
Show Gist options
  • Save FrankFan/e8337874ec756ea51efb9e9a9716acbc to your computer and use it in GitHub Desktop.
Save FrankFan/e8337874ec756ea51efb9e9a9716acbc to your computer and use it in GitHub Desktop.
A SFC TodoApp with vue2
const STORAGE_KEY = `todo-app-vue2`;
export const todoStorage = {
fetch() {
const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
todos.forEach((todo, index) => {
todo.id = index;
});
todoStorage.uid = todos.length;
return todos;
},
save(todos) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
},
};
// visibility filters
export const filters = {
all: function (todos) {
return todos;
},
active: function (todos) {
return todos.filter(function (todo) {
return !todo.completed;
});
},
completed: function (todos) {
return todos.filter(function (todo) {
return todo.completed;
});
},
};
<template>
<div id="app">
<section class="todoapp">
<header class="header">
<h1>Todos</h1>
<input
class="new-todo"
type="text"
autofocus
autocomplete="off"
placeholder="What needs to be done?"
v-model="newTodo"
@keyup.enter="addTodo"
/>
</header>
<section class="main">
<input type="checkbox" id="toggle-all" class="toggle-all" v-model="allDone" />
<label for="toggle-all"></label>
<ul class="todo-list">
<li
class="todo"
:class="{ completed: todo.completed, editing: todo === editedTodo }"
v-for="todo in filteredTodos"
:key="todo.id"
>
<div class="view">
<input type="checkbox" class="toggle" v-model="todo.completed" />
<label @dblclick="editTodo(todo)">{{ todo.title }}</label>
<button class="destroy" @click="removeTodo(todo)"></button>
</div>
<input
type="text"
class="edit"
v-model="todo.title"
v-todo-focus="todo === editedTodo"
@keyup.enter="doneEdit(todo)"
@keyup.esc="cancelEdit(todo)"
@blur="doneEdit(todo)"
/>
</li>
</ul>
</section>
<footer class="footer">
<span class="todo-count">
<strong>{{ remaining }}</strong>
{{ remaining | pluralize }} left
</span>
<ul class="filters">
<li><a href="#/all">All</a></li>
<li><a href="#/active">Active</a></li>
<li><a href="#/completed">Completed</a></li>
</ul>
<button class="clear-completed" @click="removeCompleted" v-show="todos.length > remaining">Clear completed</button>
</footer>
</section>
<footer class="info">
<p>Double-click to edit a todo</p>
<p>Written by <a href="void:javascript(0)">Frank Fan</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
</div>
</template>
<script>
import { todoStorage, filters } from './helper';
export default {
data() {
return {
newTodo: '',
todos: todoStorage.fetch(),
editedTodo: null,
visibility: this.getHash() || 'all',
};
},
watch: {
todos: {
handler: function (todos) {
todoStorage.save(todos);
},
deep: true,
},
},
computed: {
filteredTodos() {
return filters[this.visibility](this.todos);
},
remaining() {
return filters.active(this.todos).length;
},
allDone: {
get() {
console.log('get');
return this.remaining === 0;
},
set(value) {
console.log('set ', value);
this.todos.forEach(todo => todo.completed = value);
},
},
},
mounted() {
window.addEventListener('hashchange', () => {
const visibility = this.getHash();
console.log('hashchange ', visibility);
if (filters[visibility]) {
this.visibility = visibility;
} else {
window.location.hash = '';
this.visibility = 'all';
}
});
},
filters: {
pluralize(n) {
return n === 1 ? 'item' : 'items';
},
},
methods: {
getHash() {
return location.hash.replace(/#\/?/, '');
},
addTodo() {
const todo = this.newTodo.trim();
if (!todo) return;
this.todos.push({
id: todoStorage.uid++,
title: todo,
completed: false,
});
this.newTodo = '';
},
removeTodo(todo) {
this.todos.splice(this.todos.indexOf(todo), 1);
},
editTodo(todo) {
this.beforeEditCache = todo.title;
this.editedTodo = todo;
},
doneEdit(todo) {
if (!this.editedTodo) return;
this.editedTodo = null;
todo.title = todo.title.trim();
if (!todo.title) {
this.removeTodo(todo);
}
},
cancelEdit(todo) {
this.editedTodo = null;
todo.title = this.beforeEditCache;
},
removeCompleted() {
this.todos = filters.active(this.todos);
},
},
directives: {
'todo-focus': function (el, binding) {
if (binding.value) {
el.focus();
}
},
},
};
</script>
<style>
@import 'https://cdn.jsdelivr.net/npm/todomvc-app-css@2.3.0/index.css';
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment