Skip to content

Commit d5787c8

Browse files
committed
todomvc
1 parent 39a6ae1 commit d5787c8

File tree

13 files changed

+398
-26
lines changed

13 files changed

+398
-26
lines changed

src/.vitepress/config.js

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ const sidebar = {
350350
],
351351
'/examples/': [
352352
{
353-
text: 'Examples',
353+
text: 'Basic Examples',
354354
items: [
355355
{
356356
text: 'Markdown Editor',
@@ -415,28 +415,29 @@ const sidebar = {
415415
link: '/examples/#cells'
416416
}
417417
]
418-
},
419-
{
420-
text: 'API Usage',
421-
items: [
422-
{
423-
text: 'v-bind',
424-
link: '/examples/#v-bind'
425-
},
426-
{
427-
text: 'v-if',
428-
link: '/examples/#v-if'
429-
},
430-
{
431-
text: 'v-for',
432-
link: '/examples/#v-for'
433-
},
434-
{
435-
text: 'v-model',
436-
link: '/examples/#v-model'
437-
}
438-
]
439418
}
419+
// TODO
420+
// {
421+
// text: 'API Usage',
422+
// items: [
423+
// {
424+
// text: 'v-bind',
425+
// link: '/examples/#v-bind'
426+
// },
427+
// {
428+
// text: 'v-if',
429+
// link: '/examples/#v-if'
430+
// },
431+
// {
432+
// text: 'v-for',
433+
// link: '/examples/#v-for'
434+
// },
435+
// {
436+
// text: 'v-model',
437+
// link: '/examples/#v-model'
438+
// }
439+
// ]
440+
// }
440441
],
441442
'/tutorial/': [
442443
{

src/examples/ExampleRepl.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,12 @@ function toScriptSetup(src: string): string {
9999
.trim()
100100
)
101101
102-
const propsDeclIdnex = src.indexOf(`\n props:`)
103-
if (propsDeclIdnex > -1) {
104-
const setupStartIndex = src.indexOf(`\n setup(`)
102+
const propsStartIndex = src.indexOf(`\n props:`)
103+
if (propsStartIndex > -1) {
104+
const propsEndIndex = src.indexOf(`\n }`, propsStartIndex) + 4
105105
const propsDef = deindent(
106106
src
107-
.slice(propsDeclIdnex, setupStartIndex)
107+
.slice(propsStartIndex, propsEndIndex)
108108
.trim()
109109
.replace(/,$/, '')
110110
.replace(/^props: /, 'const props = defineProps(') + ')',
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { ref, watchEffect } from 'vue'
2+
3+
const API_URL = `https://api.github.com/repos/vuejs/vue-next/commits?per_page=3&sha=`
4+
const branches = ['master', 'v2-compat']
5+
6+
export default {
7+
setup() {
8+
const currentBranch = ref('master')
9+
const commits = ref(null)
10+
11+
watchEffect(async () => {
12+
commits.value = await (
13+
await fetch(`${API_URL}${currentBranch.value}`)
14+
).json()
15+
})
16+
17+
function truncate(v) {
18+
const newline = v.indexOf('\n')
19+
return newline > 0 ? v.slice(0, newline) : v
20+
}
21+
22+
function formatDate(v) {
23+
return v.replace(/T|Z/g, ' ')
24+
}
25+
26+
return {
27+
branches,
28+
currentBranch,
29+
commits,
30+
truncate,
31+
formatDate
32+
}
33+
}
34+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const API_URL = `https://api.github.com/repos/vuejs/vue-next/commits?per_page=3&sha=`
2+
3+
export default {
4+
data: () => ({
5+
branches: ['master', 'v2-compat'],
6+
currentBranch: 'master',
7+
commits: null
8+
}),
9+
10+
created() {
11+
this.fetchData()
12+
},
13+
14+
watch: {
15+
currentBranch: 'fetchData'
16+
},
17+
18+
methods: {
19+
async fetchData() {
20+
this.commits = await (
21+
await fetch(`${API_URL}${this.currentBranch}`)
22+
).json()
23+
},
24+
truncate(v) {
25+
const newline = v.indexOf('\n')
26+
return newline > 0 ? v.slice(0, newline) : v
27+
},
28+
formatDate(v) {
29+
return v.replace(/T|Z/g, ' ')
30+
}
31+
}
32+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
a {
2+
text-decoration: none;
3+
color: #42b883;
4+
}
5+
li {
6+
line-height: 1.5em;
7+
margin-bottom: 20px;
8+
}
9+
.author,
10+
.date {
11+
font-weight: bold;
12+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<h1>Latest Vue.js Commits</h1>
2+
<template v-for="branch in branches">
3+
<input type="radio"
4+
:id="branch"
5+
:value="branch"
6+
name="branch"
7+
v-model="currentBranch">
8+
<label :for="branch">{{ branch }}</label>
9+
</template>
10+
<p>vuejs/vue@{{ currentBranch }}</p>
11+
<ul>
12+
<li v-for="{ html_url, sha, author, commit } in commits">
13+
<a :href="html_url" target="_blank" class="commit">{{ sha.slice(0, 7) }}</a>
14+
- <span class="message">{{ truncate(commit.message) }}</span><br>
15+
by <span class="author">
16+
<a :href="author.html_url" target="_blank">{{ commit.author.name }}</a>
17+
</span>
18+
at <span class="date">{{ formatDate(commit.author.date) }}</span>
19+
</li>
20+
</ul>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This example fetches latest Vue.js commits data from
2+
GitHub’s API and displays them as a list. You can switch
3+
between the master and dev branches.

src/examples/src/grid/App/composition.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import DemoGrid from './Grid.vue'
22
import { ref } from 'vue'
33

44
export default {
5+
components: {
6+
DemoGrid
7+
},
58
setup() {
69
const searchQuery = ref('')
710
const gridColumns = ['name', 'power']
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { ref, computed, watchEffect } from 'vue'
2+
3+
const STORAGE_KEY = 'vue-todomvc'
4+
5+
const filters = {
6+
all: (todos) => todos,
7+
active: (todos) => todos.filter((todo) => !todo.completed),
8+
completed: (todos) => todos.filter((todo) => todo.completed)
9+
}
10+
11+
export default {
12+
setup() {
13+
// state
14+
const todos = ref(JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'))
15+
const visibility = ref('all')
16+
const editedTodo = ref()
17+
18+
// derived state
19+
const filteredTodos = computed(() => filters[visibility.value](todos.value))
20+
const remaining = computed(() => filters.active(todos.value).length)
21+
22+
// handle routing
23+
window.addEventListener('hashchange', onHashChange)
24+
onHashChange()
25+
26+
// persist state
27+
watchEffect(() => {
28+
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos.value))
29+
})
30+
31+
function toggleAll(e) {
32+
todos.value.forEach((todo) => (todo.completed = e.target.checked))
33+
}
34+
35+
function addTodo(e) {
36+
const value = e.target.value.trim()
37+
if (value) {
38+
todos.value.push({
39+
id: Date.now(),
40+
title: value,
41+
completed: false
42+
})
43+
e.target.value = ''
44+
}
45+
}
46+
47+
function removeTodo(todo) {
48+
todos.value.splice(todos.value.indexOf(todo), 1)
49+
}
50+
51+
let beforeEditCache = ''
52+
function editTodo(todo) {
53+
beforeEditCache = todo.title
54+
editedTodo.value = todo
55+
}
56+
57+
function cancelEdit(todo) {
58+
editedTodo.value = null
59+
todo.title = beforeEditCache
60+
}
61+
62+
function doneEdit(todo) {
63+
if (editedTodo.value) {
64+
editedTodo.value = null
65+
todo.title = todo.title.trim()
66+
if (!todo.title) removeTodo(todo)
67+
}
68+
}
69+
70+
function removeCompleted() {
71+
todos.value = filters.active(todos.value)
72+
}
73+
74+
function onHashChange() {
75+
const route = window.___location.hash.replace(/#\/?/, '')
76+
if (filters[route]) {
77+
visibility.value = route
78+
} else {
79+
window.___location.hash = ''
80+
visibility.value = 'all'
81+
}
82+
}
83+
84+
return {
85+
todos,
86+
visibility,
87+
editedTodo,
88+
filteredTodos,
89+
remaining,
90+
toggleAll,
91+
addTodo,
92+
removeTodo,
93+
editTodo,
94+
doneEdit,
95+
cancelEdit,
96+
removeCompleted,
97+
}
98+
}
99+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
const STORAGE_KEY = 'vue-todomvc'
2+
3+
const filters = {
4+
all: (todos) => todos,
5+
active: (todos) => todos.filter((todo) => !todo.completed),
6+
completed: (todos) => todos.filter((todo) => todo.completed)
7+
}
8+
9+
export default {
10+
// app initial state
11+
data: () => ({
12+
todos: JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'),
13+
editedTodo: null,
14+
visibility: 'all'
15+
}),
16+
17+
// watch todos change for localStorage persistence
18+
watch: {
19+
todos: {
20+
handler(todos) {
21+
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
22+
},
23+
deep: true
24+
}
25+
},
26+
27+
mounted() {
28+
window.addEventListener('hashchange', this.onHashChange)
29+
this.onHashChange()
30+
},
31+
32+
computed: {
33+
filteredTodos() {
34+
return filters[this.visibility](this.todos)
35+
},
36+
remaining() {
37+
return filters.active(this.todos).length
38+
}
39+
},
40+
41+
// methods that implement data logic.
42+
// note there's no DOM manipulation here at all.
43+
methods: {
44+
toggleAll(e) {
45+
this.todos.forEach((todo) => (todo.completed = e.target.checked))
46+
},
47+
48+
addTodo(e) {
49+
const value = e.target.value.trim()
50+
if (!value) {
51+
return
52+
}
53+
this.todos.push({
54+
id: Date.now(),
55+
title: value,
56+
completed: false
57+
})
58+
e.target.value = ''
59+
},
60+
61+
removeTodo(todo) {
62+
this.todos.splice(this.todos.indexOf(todo), 1)
63+
},
64+
65+
editTodo(todo) {
66+
this.beforeEditCache = todo.title
67+
this.editedTodo = todo
68+
},
69+
70+
doneEdit(todo) {
71+
if (!this.editedTodo) {
72+
return
73+
}
74+
this.editedTodo = null
75+
todo.title = todo.title.trim()
76+
if (!todo.title) {
77+
this.removeTodo(todo)
78+
}
79+
},
80+
81+
cancelEdit(todo) {
82+
this.editedTodo = null
83+
todo.title = this.beforeEditCache
84+
},
85+
86+
removeCompleted() {
87+
this.todos = filters.active(this.todos)
88+
},
89+
90+
onHashChange() {
91+
var visibility = window.___location.hash.replace(/#\/?/, '')
92+
if (filters[visibility]) {
93+
this.visibility = visibility
94+
} else {
95+
window.___location.hash = ''
96+
this.visibility = 'all'
97+
}
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)