vue3-yuanma/packages/vue/examples/composition/todomvc.html

209 lines
5.3 KiB
HTML

<script src="../../dist/vue.global.js"></script>
<link rel="stylesheet" href="../../../../node_modules/todomvc-app-css/index.css">
<div id="app">
<section class="todoapp">
<header class="header">
<h1>todos</h1>
<input class="new-todo"
autofocus autocomplete="off"
placeholder="What needs to be done?"
v-model="state.newTodo"
@keyup.enter="addTodo">
</header>
<section class="main" v-show="state.todos.length">
<input id="toggle-all" class="toggle-all" type="checkbox" v-model="state.allDone">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<li v-for="todo in state.filteredTodos"
class="todo"
:key="todo.id"
:class="{ completed: todo.completed, editing: todo === state.editedTodo }">
<div class="view">
<input class="toggle" type="checkbox" v-model="todo.completed">
<label @dblclick="editTodo(todo)">{{ todo.title }}</label>
<button class="destroy" @click="removeTodo(todo)"></button>
</div>
<input class="edit" type="text"
v-model="todo.title"
v-todo-focus="todo === state.editedTodo"
@blur="doneEdit(todo)"
@keyup.enter="doneEdit(todo)"
@keyup.escape="cancelEdit(todo)"
>
</li>
</ul>
</section>
<footer class="footer" v-show="state.todos.length">
<span class="todo-count">
<strong>{{ state.remaining }}</strong>
<span>{{ state.remainingText }}</span>
</span>
<ul class="filters">
<li><a href="#/all" :class="{ selected: state.visibility === 'all' }">All</a></li>
<li><a href="#/active" :class="{ selected: state.visibility === 'active' }">Active</a></li>
<li><a href="#/completed" :class="{ selected: state.visibility === 'completed' }">Completed</a></li>
</ul>
<button class="clear-completed" @click="removeCompleted" v-show="state.todos.length > state.remaining">
Clear completed
</button>
</footer>
</section>
</div>
<script>
const { createApp, reactive, computed, watch, onMounted, onUnmounted } = Vue
const STORAGE_KEY = 'todos-vuejs-3.x'
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))
}
}
const filters = {
all (todos) {
return todos
},
active (todos) {
return todos.filter((todo) => {
return !todo.completed
})
},
completed (todos) {
return todos.filter(function (todo) {
return todo.completed
})
}
}
function pluralize (n) {
return n === 1 ? 'item' : 'items'
}
const App = {
setup () {
const state = reactive({
todos: todoStorage.fetch(),
editedTodo: null,
newTodo: '',
beforeEditCache: '',
visibility: 'all',
remaining: computed(() => {
return filters.active(state.todos).length
}),
remainingText: computed(() => {
return ` ${pluralize(state.remaining)} left`
}),
filteredTodos: computed(() => {
return filters[state.visibility](state.todos)
}),
allDone: computed({
get: function () {
return state.remaining === 0
},
set: function (value) {
state.todos.forEach((todo) => {
todo.completed = value
})
}
})
})
watch(() => {
todoStorage.save(state.todos)
})
onMounted(() => {
window.addEventListener('hashchange', onHashChange)
onHashChange()
})
onUnmounted(() => {
window.removeEventListener('hashchange', onHashChange)
})
function onHashChange () {
const visibility = window.location.hash.replace(/#\/?/, '')
if (filters[visibility]) {
state.visibility = visibility
} else {
window.location.hash = ''
state.visibility = 'all'
}
}
function addTodo () {
const value = state.newTodo && state.newTodo.trim()
if (!value) {
return
}
state.todos.push({
id: todoStorage.uid++,
title: value,
completed: false
})
state.newTodo = ''
}
function removeTodo (todo) {
state.todos.splice(state.todos.indexOf(todo), 1)
}
function editTodo (todo) {
state.beforeEditCache = todo.title
state.editedTodo = todo
}
function doneEdit (todo) {
if (!state.editedTodo) {
return
}
state.editedTodo = null
todo.title = todo.title.trim()
if (!todo.title) {
removeTodo(todo)
}
}
function cancelEdit (todo) {
state.editedTodo = null
todo.title = state.beforeEditCache
}
function removeCompleted () {
state.todos = filters.active(state.todos)
}
return {
state,
addTodo,
removeTodo,
editTodo,
doneEdit,
cancelEdit,
removeCompleted
}
},
directives: {
'todo-focus': (el, { value }) => {
if (value) {
el.focus()
}
}
}
}
createApp().mount(App, '#app')
</script>