Skip to content

Commit 3ea8e26

Browse files
committed
chore: add playground demo todos
1 parent cfc874a commit 3ea8e26

File tree

4 files changed

+101
-22
lines changed

4 files changed

+101
-22
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<script lang="ts" setup>
2+
import type { Todo } from '../types'
3+
import { computed, nextTick, ref } from 'vue'
4+
5+
const props = defineProps<{
6+
todo: Todo & { id: string }
7+
}>()
8+
9+
const emit = defineEmits<{
10+
(event: 'update:todo', id: string, newTodo: Todo): void
11+
(event: 'remove', id: string): void
12+
}>()
13+
14+
const isEditing = ref(false)
15+
const textCopy = ref('')
16+
17+
function startEdit() {
18+
textCopy.value = props.todo.text
19+
isEditing.value = true
20+
}
21+
22+
function saveTodo() {
23+
if (!isEditing.value) return
24+
25+
emit('update:todo', props.todo.id, {
26+
...props.todo,
27+
text: textCopy.value,
28+
})
29+
30+
isEditing.value = false
31+
}
32+
33+
const finished = computed({
34+
get: () => props.todo.finished,
35+
set(finished) {
36+
emit('update:todo', props.todo.id, { ...props.todo, finished })
37+
},
38+
})
39+
40+
async function lostFocus() {
41+
await nextTick()
42+
saveTodo()
43+
}
44+
</script>
45+
46+
<template>
47+
<li>
48+
<form v-if="isEditing" @submit.prevent="saveTodo">
49+
<input
50+
v-model="textCopy"
51+
v-focus
52+
type="text"
53+
@keydown.esc="isEditing = false"
54+
@blur="lostFocus"
55+
/>
56+
<button>Save</button>
57+
<button type="button" @click="isEditing = false">Cancel</button>
58+
</form>
59+
<template v-else>
60+
<input v-model="finished" type="checkbox" />
61+
<span
62+
:class="todo.finished ? 'line-through' : ''"
63+
@dblclick="startEdit"
64+
>{{ todo.text }}</span
65+
>
66+
</template>
67+
<button @click="$emit('delete', todo.id)">Delete</button>
68+
</li>
69+
</template>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface Todo {
2+
created: Date
3+
finished: boolean
4+
text: string
5+
}

playground/src/main.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createApp } from 'vue'
1+
import { createApp, nextTick } from 'vue'
22
import { createPinia } from 'pinia'
33
import {
44
VueFire,
@@ -33,13 +33,19 @@ const store = createStore({
3333

3434
const app = createApp(App)
3535
app
36+
.directive('focus', {
37+
mounted: async (el) => {
38+
await nextTick()
39+
el.focus()
40+
},
41+
})
3642
.use(createPinia())
3743
.use(VueFire, {
3844
firebaseApp: createFirebaseApp(),
3945
modules: [
4046
VueFireAuth(),
4147
VueFireAppCheck({
42-
// debug: process.env.NODE_ENV !== 'production',
48+
debug: process.env.NODE_ENV !== 'production',
4349
isTokenAutoRefreshEnabled: true,
4450
provider: new ReCaptchaV3Provider(
4551
'6LfJ0vgiAAAAAHheQE7GQVdG_c9m8xipBESx_SKI'

playground/src/pages/todos.vue

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script setup lang="ts">
2+
import type { Todo } from '@/demos/TodoList/types'
3+
import TodoItem from '@/demos/TodoList/components/TodoItem.vue'
24
import {
35
addDoc,
46
collection,
@@ -12,20 +14,18 @@ import {
1214
import { ref } from 'vue'
1315
import { useCollection, useFirestore } from 'vuefire'
1416
15-
interface Todo {
16-
created: Date
17-
finished: boolean
18-
text: string
19-
}
20-
2117
// TODO: expose some kind of type to make this posssible
2218
// type TodoData = _VueFireQueryData<Todo>
2319
2420
const db = useFirestore()
2521
const todosRef = collection(db, 'todos')
2622
const todosWithConverterRef = collection(db, 'todos').withConverter<Todo>({
2723
toFirestore(todoModel) {
28-
const { id, ...todo } = todoModel
24+
const {
25+
// @ts-expect-error: it might not exist
26+
id,
27+
...todo
28+
} = todoModel
2929
return todo
3030
},
3131
fromFirestore(snapshot, options) {
@@ -55,14 +55,13 @@ function addTodo() {
5555
}
5656
}
5757
58-
function updateTodoText(todo: Todo, newText: string) {
59-
updateDoc(doc(db, 'todos', todo.id), {
60-
text: newText,
61-
})
58+
function updateTodo(id: string, newTodo: Todo) {
59+
// NOTE: the copy shouldn't be necessary...
60+
updateDoc(doc(todosRef, id), { ...newTodo })
6261
}
6362
64-
function removeTodo(todo: Todo) {
65-
deleteDoc(doc(db, 'todos', todo.id))
63+
function removeTodo(id: string) {
64+
deleteDoc(doc(db, 'todos', id))
6665
}
6766
6867
function toggleTodos() {
@@ -77,12 +76,12 @@ function toggleTodos() {
7776
<button>Add Todo</button>
7877
</form>
7978
<ul>
80-
<li v-for="todo in todos">
81-
<input
82-
:value="todo.text"
83-
@input="updateTodoText(todo, $event.target.value)"
84-
/>
85-
<button @click="removeTodo(todo)">X</button>
86-
</li>
79+
<TodoItem
80+
v-for="todo in todos"
81+
:key="todo.id"
82+
:todo="todo"
83+
@remove="removeTodo"
84+
@update:todo="updateTodo"
85+
/>
8786
</ul>
8887
</template>

0 commit comments

Comments
 (0)