Skip to content

Commit

Permalink
Use reactiveStruct to store todos
Browse files Browse the repository at this point in the history
  • Loading branch information
mbeckem committed Apr 15, 2024
1 parent c27bcce commit 82645b1
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 17 deletions.
13 changes: 2 additions & 11 deletions playground/Playground.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { ref } from "vue";
import { TodosModel } from "./TodosModel";
import { useReactiveSnapshot } from "./integration";
import TodoItem from "./TodoItem.vue";
const model = new TodosModel();
const snapshot = useReactiveSnapshot(() => {
Expand Down Expand Up @@ -47,17 +48,7 @@ function createTodo() {
<v-list v-if="snapshot.todos.length">
<template v-for="(todo, index) in snapshot.todos" :key="todo.id">
<v-divider v-if="index !== 0" />
<v-list-item>
<v-list-item-title>{{ todo.title }}</v-list-item-title>

<template #append>
<v-btn
icon="mdi-trash-can"
variant="text"
@click.stop.prevent="model.removeTodo(todo.id)"
/>
</template>
</v-list-item>
<todo-item :todo="todo" @remove="model.removeTodo(todo.id)" />
</template>
</v-list>
</v-card>
Expand Down
100 changes: 100 additions & 0 deletions playground/TodoItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<script setup lang="ts">
import { computed, reactive } from "vue";
import { Todo } from "./TodosModel";
const props = defineProps<{
todo: Todo;
}>();
defineEmits<{
remove: [];
}>();
interface EditDialogState {
open: boolean;
title: string;
}
// Temp storage for editable properties (not applied directly to the model).
const editState = reactive<EditDialogState>({
open: false,
title: ""
});
function startEdit() {
console.debug("start edit", props.todo.id);
const todo = props.todo;
editState.title = todo.title;
editState.open = true;
}
function stopEdit(saveChanges = false) {
console.debug("stop edit", props.todo.id);
if (!editState.open) {
return;
}
if (saveChanges) {
const todo = props.todo;
todo.title = editState.title;
}
// Also hides the dialog
editState.open = false;
editState.title = "";
}
// The dialog's v-model is derived from the edit state.
const dialogModel = computed({
get: () => editState.open,
set: (open) => {
if (!open) {
stopEdit();
}
}
});
</script>

<template>
<v-list-item>
<v-list-item-title>{{ todo.title }}</v-list-item-title>

<template #append>
<v-dialog v-model="dialogModel" max-width="500">
<template #activator="{ props: activatorProps }">
<v-btn
v-bind="activatorProps"
icon="mdi-pencil"
aria-label="Edit todo"
variant="text"
@click.stop.prevent="startEdit"
/>
</template>

<template #default="{ isActive }">
<v-card title="Dialog">
<v-card-text>
<v-form @submit.prevent="stopEdit(true)">
<v-text-field v-model="editState.title" label="Todo Title" />
</v-form>
</v-card-text>

<v-card-actions>
<v-spacer />
<v-btn text="Save Changes" @click="stopEdit(true)" />
<v-btn text="Close Dialog" @click="isActive.value = false" />
</v-card-actions>
</v-card>
</template>
</v-dialog>

<v-btn
icon="mdi-trash-can"
aria-label="Remove todo"
variant="text"
@click.stop.prevent="$emit('remove')"
/>
</template>
</v-list-item>
</template>
16 changes: 10 additions & 6 deletions playground/TodosModel.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { computed, reactiveMap } from "@conterra/reactivity-core";
import { computed, reactiveMap, reactiveStruct } from "@conterra/reactivity-core";
import { ReadonlyReactiveMap } from "@conterra/reactivity-core";

export interface Todo {
id: string;
readonly id: string;
title: string;
}

const Todo = reactiveStruct<Todo>().define({
id: {
writable: false
},
title: {}
});

let nextId = 1;

export class TodosModel {
Expand All @@ -14,10 +21,7 @@ export class TodosModel {

addTodo(title: string): string {
const id = String(nextId++);
this.#todos.set(id, {
id,
title
});
this.#todos.set(id, new Todo({ id, title }));
return id;
}

Expand Down

0 comments on commit 82645b1

Please sign in to comment.