mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 10:27:09 +00:00
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:
208
lib/www/client/source/src/components/event-edit-labels.vue
Normal file
208
lib/www/client/source/src/components/event-edit-labels.vue
Normal 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>
|
||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user