vue-todo/src/components/Lists.vue

205 lines
5.0 KiB
Vue

<!--
Component for editing task lists.
Part of the basebox sample Todo app.
https://basebox.io
-->
<template>
<main>
<h1>
Lists
</h1>
<div class="toolbar">
<button type="button" @click="addList()" class="btn btn-primary btn-large">New List</button>
<div class="ms-auto">
<small>{{ gStore.lists.length }} lists found.</small>
</div>
</div>
<div id="list-container">
<transition-group name="list" tag="div">
<div class="list-item" v-for="list in gStore.lists" :key="list.id" :id="`list-${list.id}`">
<div class="item-v-container">
<input class="item-title" type="text" @blur="saveList(list)" v-model="list.title">
</div>
<div class="item-meta">
<div class="dropdown">
<button class="btn btn-sm btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-trash"></i>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#"
@click="deleteList(list)">Delete this list</a></li>
</ul>
</div>
<span class="item-counter">
{{ listItemCount(list) }}
<i class="bi bi-card-checklist"></i>
</span>
</div>
</div>
<div class="alert alert-info" v-if="gStore.lists.length === 0">
<p>There are no lists, yet.</p>
<button type="button" @click="addList()" class="btn btn-primary btn-large">Create your first list</button>
</div>
</transition-group>
</div>
</main>
</template>
<script>
import {gqlQuery} from "../util/net";
import {showError, store} from "../store";
import {showModal} from "../util/misc";
const NEW_LIST_ID = "new-list-id";
export default {
name: "Lists",
methods: {
/**
* Save a list to the database.
* @param list - the list object to save.
*/
saveList(list)
{
/* Prepare request; it is different if the list is new */
let gql = "";
if (list.id === NEW_LIST_ID) {
/* create new list */
gql = `mutation {
createList(
title: "${list.title}"
user: {
username: "${store.session.username}"
}
) {
id
}
}`;
} else {
/* save existing list */
gql = `mutation {
updateList(
id: "${list.id}",
title: "${list.title}"
) {
id
}
}`;
}
/* send query */
gqlQuery(
gql
).then(data => {
/* Save the list's id in case it was just created */
if (!list.id) {
list.id = data.createList.id;
}
}).catch(e => {
const errMsg = `Failed to save list: ${e}`;
console.error(errMsg);
showError(errMsg);
});
},
/**
* Delete a list.
* @param list - the list object to delete
*/
deleteList(list) {
if (list.id !== NEW_LIST_ID) {
/* Make sure the list is not in use */
if (store.tasks.findIndex(task => task.list.id === list.id) !== -1) {
showModal("Delete List", "The list cannot be deleted as it is in use.");
return;
}
/* List must be also deleted from the server */
gqlQuery(`mutation {
deleteList(id: "${list.id}") {
id
}
}`);
}
/* delete list from the store. */
const idx = store.lists.findIndex(item => item.id === list.id);
if (idx !== -1) {
store.lists.splice(idx, 1);
}
},
/**
* Add a new list.
*/
addList()
{
/* push it to the array of lists in the store; it will be saved
* when the user leaves the title field.
*/
store.lists.push({
title: "Enter list title",
id: NEW_LIST_ID
});
/* wait a moment, then scroll list to the bottom */
setTimeout(function() {
document.getElementById("list-container").scrollTo({
top: 100000,
behavior: "smooth"
});
/* select text in new item's title field */
const titleInput = document.querySelector(`#list-${NEW_LIST_ID} .item-title`);
titleInput.setSelectionRange(0, 1000);
titleInput.focus();
}, 100);
},
/**
* Return number of todo items in a list.
*/
listItemCount(list) {
return store.tasks.filter((item) => item.list.id === list.id).length;
},
},
computed: {
/**
* Add global store to the context, so we can refer to it in the template.
*/
gStore() {
return store;
},
}
}
</script>
<style lang="scss" scoped>
#list-container {
margin-top: 20px;
}
.item-counter {
font-size: .8em;
border: 1px solid var(--bs-border-color);
padding: 3px 5px;
text-align: right;
border-radius: 3px;
margin-top: 5px;
display: block;
}
</style>