| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- <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="newTodo"
- @keyup.enter="addTodo"
- />
- </header>
- <section class="main" v-show="todos.length">
- <input
- id="toggle-all"
- class="toggle-all"
- type="checkbox"
- v-model="allDone"
- />
- <label for="toggle-all">Mark all as complete</label>
- <ul class="todo-list">
- <li
- v-for="todo in filteredTodos"
- class="todo"
- :key="todo.id"
- :class="{ completed: todo.completed, editing: todo === 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 === editedTodo"
- @blur="doneEdit(todo)"
- @keyup.enter="doneEdit(todo)"
- @keyup.escape="cancelEdit(todo)"
- />
- </li>
- </ul>
- </section>
- <footer class="footer" v-show="todos.length">
- <span class="todo-count">
- <strong>{{ remaining }}</strong>
- <span>{{ pluralize(remaining) }} left</span>
- </span>
- <ul class="filters">
- <li>
- <a href="#/all" :class="{ selected: visibility === 'all' }">All</a>
- </li>
- <li>
- <a href="#/active" :class="{ selected: visibility === 'active' }"
- >Active</a
- >
- </li>
- <li>
- <a
- href="#/completed"
- :class="{ selected: visibility === 'completed' }"
- >Completed</a
- >
- </li>
- </ul>
- <button
- class="clear-completed"
- @click="removeCompleted"
- v-show="todos.length > remaining"
- >
- Clear completed
- </button>
- </footer>
- </section>
- </div>
- <script>
- 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
- })
- },
- }
- Vue.createApp({
- // app initial state
- data: () => ({
- todos: todoStorage.fetch(),
- newTodo: '',
- editedTodo: null,
- visibility: 'all',
- }),
- // watch todos change for localStorage persistence
- watch: {
- todos: {
- handler(todos) {
- todoStorage.save(todos)
- },
- deep: true,
- },
- },
- mounted() {
- window.addEventListener('hashchange', this.onHashChange)
- this.onHashChange()
- },
- computed: {
- filteredTodos() {
- return filters[this.visibility](this.todos)
- },
- remaining() {
- return filters.active(this.todos).length
- },
- allDone: {
- get() {
- return this.remaining === 0
- },
- set(value) {
- this.todos.forEach(function (todo) {
- todo.completed = value
- })
- },
- },
- },
- // methods that implement data logic.
- // note there's no DOM manipulation here at all.
- methods: {
- addTodo() {
- var value = this.newTodo && this.newTodo.trim()
- if (!value) {
- return
- }
- this.todos.push({
- id: todoStorage.uid++,
- title: value,
- 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)
- },
- onHashChange() {
- var visibility = window.location.hash.replace(/#\/?/, '')
- if (filters[visibility]) {
- this.visibility = visibility
- } else {
- window.location.hash = ''
- this.visibility = 'all'
- }
- },
- pluralize(n) {
- return n === 1 ? 'item' : 'items'
- },
- },
- directives: {
- 'todo-focus'(el, binding) {
- if (binding.value) {
- el.focus()
- }
- },
- },
- }).mount('#app')
- </script>
|