Add dialogue to edit event labels.

This assumes that adding or removing labels is a relatively
common action to do on an event and provides a quicker
and simpler mechanism than bringing up the full event
dialogue.

This is meant to be invoked from a context menu action or
similar.
This commit is contained in:
D. Berge
2022-02-27 19:36:28 +01:00
parent 48505dbaeb
commit 8f4bda011b
2 changed files with 219 additions and 0 deletions

View File

@@ -0,0 +1,208 @@
<template>
<v-dialog
:value="value"
@input="(e) => $emit('input', e)"
max-width="600"
>
<v-card>
<v-toolbar
flat
color="transparent"
>
<v-toolbar-title>Event labels</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn
icon
@click="$refs.search.focus()"
>
<v-icon>mdi-magnify</v-icon>
</v-btn>
</v-toolbar>
<v-container class="py-0">
<v-row
align="center"
justify="start"
>
<v-col
v-for="(item, i) in selection"
:key="item.text"
class="shrink"
>
<v-chip
:disabled="loading"
small
:color="item.colour"
:title="item.title"
close
@click:close="selection.splice(i, 1)"
>
<v-icon
left
v-text="item.icon"
></v-icon>
{{ item.text }}
</v-chip>
</v-col>
<v-col v-if="!allSelected"
cols="12"
>
<v-text-field
ref="search"
v-model="search"
full-width
hide-details
label="Search"
single-line
></v-text-field>
</v-col>
</v-row>
</v-container>
<v-divider v-if="!allSelected"></v-divider>
<v-list dense style="max-height:600px;overflow-y:auto;">
<template v-for="item in categories">
<v-list-item v-if="!selection.find(i => i.text == item.text)"
dense
:key="item.text"
:disabled="loading"
@click="selection.push(item)"
>
<v-list-item-avatar
class="my-0"
width="12ex"
>
<v-chip
x-small
:color="item.colour"
:title="item.title"
>{{item.text}}</v-chip>
</v-list-item-avatar>
<v-list-item-title v-text="item.title"></v-list-item-title>
</v-list-item>
</template>
</v-list>
<v-divider></v-divider>
<v-card-actions>
<v-btn
:loading="loading"
color="warning"
text
@click="close"
>
Cancel
</v-btn>
<v-spacer></v-spacer>
<v-btn
:disabled="!dirty"
:loading="loading"
color="primary"
text
@click="save"
>
Save
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
function stringSort (a, b) {
return a == b
? 0
: a < b
? -1
: +1;
}
export default {
name: 'DougalEventEditLabels',
props: {
value: { default: false },
labels: { type: Object },
selected: {type: Array },
loading: { type: Boolean, default: false }
},
data: () => ({
dialog: false,
search: '',
selection: [],
}),
computed: {
allSelected () {
return this.selection.length === this.items.length
},
dirty () {
// Checks if the arrays have the same elements
return !this.selection.every(i => this.selected.includes(i.text)) ||
!this.selected.every(i => this.selection.find(j => j.text==i));
},
categories () {
const search = this.search.toLowerCase()
if (!search) return this.items
return this.items.filter(item => {
const text = item.text.toLowerCase();
const title = item.title.toLowerCase();
return text.includes(search) || title.includes(search);
}).sort( (a, b) => stringSort(a.text, b.text) )
},
items () {
return Object.keys(this.labels).map(this.labelToItem);
}
},
watch: {
value () {
this.dialog = this.value;
if (this.dialog) {
this.$nextTick(() => this.$refs.search?.focus());
}
},
selected () {
this.selection = this.selected.map(this.labelToItem)
},
selection () {
this.search = '';
this.$refs.search?.focus();
},
},
methods: {
labelToItem (k) {
return {
text: k,
icon: this.labels[k].view?.icon,
colour: this.labels[k].view?.colour,
title: this.labels[k].view?.description
};
},
close () {
this.selection = this.selected.map(this.labelToItem)
this.$emit("input", false);
},
save () {
this.$emit("selectionChanged", {labels: this.selection.map(i => i.text)});
this.$emit("input", false);
},
},
}
</script>

View File

@@ -44,6 +44,14 @@
> >
</dougal-event-edit> </dougal-event-edit>
<dougal-event-edit-labels v-if="writeaccess"
v-model="eventLabelsDialog"
:labels="userLabels"
:selected="contextMenuItem ? contextMenuItem.labels||[] : []"
@selectionChanged="(data) => patchEvent(contextMenuItem.id, data)"
>
</dougal-event-edit-labels>
<v-menu v-if="$route.params.sequence"> <v-menu v-if="$route.params.sequence">
<template v-slot:activator="{on, attrs}"> <template v-slot:activator="{on, attrs}">
<v-btn class="ml-5" small v-on="on" v-bind="attrs"> <v-btn class="ml-5" small v-on="on" v-bind="attrs">
@@ -179,6 +187,7 @@ tr.align-top td:not(.align-default) {
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import DougalContextMenu from '@/components/context-menu'; import DougalContextMenu from '@/components/context-menu';
import DougalEventEdit from '@/components/event-edit' import DougalEventEdit from '@/components/event-edit'
import DougalEventEditLabels from '@/components/event-edit-labels'
import DougalEventEditHistory from '@/components/event-edit-history' import DougalEventEditHistory from '@/components/event-edit-history'
function ArraysEqual (a, b) { function ArraysEqual (a, b) {
@@ -198,6 +207,7 @@ export default {
components: { components: {
DougalEventEdit, DougalEventEdit,
DougalEventEditLabels,
DougalEventEditHistory, DougalEventEditHistory,
DougalContextMenu DougalContextMenu
}, },
@@ -235,6 +245,7 @@ export default {
filter: "", filter: "",
eventCount: null, eventCount: null,
eventDialog: false, eventDialog: false,
eventLabelsDialog: false,
defaultEventTimestamp: null, defaultEventTimestamp: null,
presetRemarks: null, presetRemarks: null,
remarksMenu: null, remarksMenu: null,