mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 08:07:08 +00:00
Add <dougal-event-edit-dialog/> component.
Used to enter new events.
This commit is contained in:
391
lib/www/client/source/src/components/event-edit-dialog.vue
Normal file
391
lib/www/client/source/src/components/event-edit-dialog.vue
Normal file
@@ -0,0 +1,391 @@
|
||||
<template>
|
||||
|
||||
<v-dialog
|
||||
v-model="show"
|
||||
max-width="600px"
|
||||
>
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-btn
|
||||
class="mx-2"
|
||||
fab dark
|
||||
x-small
|
||||
color="primary"
|
||||
title="Add event"
|
||||
v-bind="attrs"
|
||||
v-on="on"
|
||||
>
|
||||
<v-icon dark>mdi-plus</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<span class="headline">{{ formTitle }}</span>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<v-container>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-textarea
|
||||
v-model="remarks"
|
||||
label="Description"
|
||||
rows="1"
|
||||
auto-grow
|
||||
clearable
|
||||
autofocus
|
||||
filled
|
||||
:hint="presetRemarks ? 'Enter your own comment or select a preset one from the menu on the left' : 'Enter a comment'"
|
||||
@keyup="handleKeys"
|
||||
>
|
||||
<template v-slot:prepend v-if="presetRemarks">
|
||||
<v-icon
|
||||
title="Select predefined comments"
|
||||
color="primary"
|
||||
@click="showRemarksMenu"
|
||||
>
|
||||
mdi-dots-vertical
|
||||
</v-icon>
|
||||
</template>
|
||||
|
||||
<template v-slot:prepend v-else>
|
||||
<v-icon
|
||||
color="disabled"
|
||||
>
|
||||
mdi-dots-vertical
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-textarea>
|
||||
|
||||
<dougal-context-menu
|
||||
:value="remarksMenu"
|
||||
@input="addRemark"
|
||||
:items="presetRemarks"
|
||||
absolute
|
||||
></dougal-context-menu>
|
||||
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row dense>
|
||||
<v-col>
|
||||
<v-autocomplete
|
||||
ref="labels"
|
||||
v-model="labels"
|
||||
:items="Object.keys(allowedLabels)"
|
||||
chips
|
||||
deletable-chips
|
||||
multiple
|
||||
label="Labels"
|
||||
@input="labelSearch=null; $refs.labels.isMenuActive=false"
|
||||
:search-input.sync="labelSearch"
|
||||
>
|
||||
<template v-slot:selection="data">
|
||||
<v-chip
|
||||
v-bind="data.attrs"
|
||||
:input-value="data.selected"
|
||||
close
|
||||
@click="data.select"
|
||||
@click:close="remove(data.item)"
|
||||
:color="allowedLabels[data.item].view.colour"
|
||||
:title="allowedLabels[data.item].view.description"
|
||||
>{{data.item}}</v-chip>
|
||||
</template>
|
||||
|
||||
<template v-slot:prepend v-if="presetLabels">
|
||||
<v-icon
|
||||
title="Select labels"
|
||||
color="primary"
|
||||
@click="showLabelsMenu"
|
||||
>
|
||||
mdi-dots-vertical
|
||||
</v-icon>
|
||||
</template>
|
||||
|
||||
<template v-slot:prepend v-else>
|
||||
<v-icon
|
||||
color="disabled"
|
||||
>
|
||||
mdi-dots-vertical
|
||||
</v-icon>
|
||||
</template>
|
||||
|
||||
</v-autocomplete>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row dense>
|
||||
<v-col>
|
||||
<v-switch label="Change time" v-model="timeInput" :disabled="shotInput"></v-switch>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-switch label="Enter shotpoint" v-model="shotInput" :disabled="timeInput"></v-switch>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row dense>
|
||||
<v-col :style="{visibility: timeInput ? 'visible' : 'hidden'}">
|
||||
<v-text-field v-model="tsTime" type="time" step="1" label="Time">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col :style="{visibility: timeInput ? 'visible' : 'hidden'}">
|
||||
<v-text-field v-model="tsDate" type="date" label="Date">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col :style="{visibility: shotInput ? 'visible' : 'hidden'}">
|
||||
<v-autocomplete
|
||||
:items="sequenceList"
|
||||
v-model="sequence"
|
||||
label="Sequence"
|
||||
></v-autocomplete>
|
||||
</v-col>
|
||||
<v-col :style="{visibility: shotInput ? 'visible' : 'hidden'}">
|
||||
<v-text-field v-model="point" type="number" label="Shot">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
|
||||
</v-container>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="blue darken-1" text @click="close">Cancel</v-btn>
|
||||
<v-btn color="blue darken-1" text @click="save" :disabled="!isValid">Save</v-btn>
|
||||
</v-card-actions>
|
||||
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
</template>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapActions } from 'vuex';
|
||||
import DougalContextMenu from '@/components/context-menu';
|
||||
|
||||
export default {
|
||||
name: 'DougalEventEditDialog',
|
||||
|
||||
components: {
|
||||
DougalContextMenu
|
||||
},
|
||||
|
||||
props: {
|
||||
value: Boolean,
|
||||
allowedLabels: { type: Object, default: () => {} },
|
||||
sequences: { type: Object, default: null },
|
||||
defaultTimestamp: { type: [ Date, String, Number, Function ], default: null },
|
||||
defaultSequence: { type: Number, default: null },
|
||||
defaultShotNumber: { type: Number, default: null },
|
||||
presetRemarks: { type: [ Object, Array ], default: null },
|
||||
presetLabels: { type: [ Object, Array ], default: null }
|
||||
},
|
||||
|
||||
data () {
|
||||
const tsNow = new Date;
|
||||
|
||||
return {
|
||||
show: false,
|
||||
tsDate: tsNow.toISOString().substring(0, 10),
|
||||
tsTime: tsNow.toISOString().substring(11, 19),
|
||||
sequenceData: null,
|
||||
sequence: null,
|
||||
point: null,
|
||||
remarks: "",
|
||||
labels: [],
|
||||
labelSearch: null,
|
||||
timer: null,
|
||||
timeInput: false,
|
||||
shotInput: false,
|
||||
|
||||
remarksMenu: false,
|
||||
menuX: 0,
|
||||
menuY: 0,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
||||
formTitle () {
|
||||
if (this.timeInput) {
|
||||
return "New event at time";
|
||||
} else if (this.shotInput) {
|
||||
return "New event at shotpoint";
|
||||
} else if (this.defaultTimestamp) {
|
||||
return "New event at " +
|
||||
this.defaultTimestampAsDate.toISOString().replace(/(.{10})T(.{8}).{4}Z$/, "$1 $2");
|
||||
} else if (this.defaultShotNumber) {
|
||||
return "New event on shotpoint " + this.defaultShotNumber;
|
||||
}
|
||||
return "New event";
|
||||
},
|
||||
|
||||
defaultTimestampAsDate () {
|
||||
if (this.defaultTimestamp instanceof Date) {
|
||||
return this.defaultTimestamp;
|
||||
} else if (typeof this.defaultTimestamp == 'string') {
|
||||
return new Date(this.defaultTimestamp);
|
||||
} else if (typeof this.defaultTimestamp == 'number') {
|
||||
return new Date(this.defaultTimestamp);
|
||||
} else if (typeof this.defaultTimestamp == 'function') {
|
||||
return new Date(this.defaultTimestamp());
|
||||
}
|
||||
},
|
||||
|
||||
tstamp () {
|
||||
return this.timeInput
|
||||
? new Date(this.tsDate+"T"+this.tsTime+"Z")
|
||||
: this.defaultTimestampAsDate;
|
||||
},
|
||||
|
||||
shot () {
|
||||
return this.shotInput
|
||||
? { sequence: this.sequence, point: Number(this.point) }
|
||||
: { sequence: this.defaultSequence, point: this.defaultShotNumber };
|
||||
},
|
||||
|
||||
isTimedEvent () {
|
||||
return Boolean((this.timeInput && this.tstamp) ||
|
||||
(this.defaultTimestampAsDate && !this.shotInput));
|
||||
},
|
||||
|
||||
isShotEvent () {
|
||||
return Boolean((this.shotInput && this.shot.sequence && this.shot.point) ||
|
||||
(this.defaultSequence && this.defaultShotNumber && !this.timeInput));
|
||||
},
|
||||
|
||||
isValid () {
|
||||
if (this.isTimedEvent) {
|
||||
return !isNaN(this.tstamp) &&
|
||||
((this.remarks && this.remarks.trim()) || this.labels.length);
|
||||
}
|
||||
|
||||
if (this.isShotEvent) {
|
||||
return Number(this.sequence) && Number(this.point) &&
|
||||
((this.remarks && this.remarks.trim()) || this.labels.length);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
sequenceList () {
|
||||
const seq = this.sequences || this.sequenceData || [];
|
||||
return seq.map(s => s.sequence).sort((a,b) => b-a);
|
||||
},
|
||||
|
||||
eventData () {
|
||||
if (!this.isValid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = {}
|
||||
|
||||
data.remarks = this.remarks.trim();
|
||||
if (this.labels) {
|
||||
data.labels = this.labels;
|
||||
}
|
||||
|
||||
if (this.isTimedEvent) {
|
||||
data.tstamp = this.tstamp;
|
||||
} else if (this.isShotEvent) {
|
||||
data.sequence = this.shot.sequence;
|
||||
data.point = this.shot.point;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
async show (value) {
|
||||
this.$emit('input', value);
|
||||
if (value) {
|
||||
this.updateTimeFields();
|
||||
await this.updateSequences();
|
||||
this.sequence = this.defaultSequence;
|
||||
this.point = this.defaultShotNumber;
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
value (v) {
|
||||
if (v != this.show) {
|
||||
this.show = v;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
clear () {
|
||||
this.timeInput = false;
|
||||
this.shotInput = false;
|
||||
this.remarks = "";
|
||||
this.labels = [];
|
||||
},
|
||||
|
||||
close () {
|
||||
this.show = false;
|
||||
this.clear();
|
||||
},
|
||||
|
||||
save () {
|
||||
this.$emit('save', this.eventData);
|
||||
this.close();
|
||||
},
|
||||
|
||||
remove (item) {
|
||||
this.labels.splice(this.labels.indexOf(item), 1);
|
||||
},
|
||||
|
||||
updateTimeFields () {
|
||||
const tsNow = new Date;
|
||||
this.tsDate = tsNow.toISOString().substring(0, 10);
|
||||
this.tsTime = tsNow.toISOString().substring(11, 19);
|
||||
},
|
||||
|
||||
async updateSequences () {
|
||||
if (this.sequences == null) {
|
||||
const url = `/project/${this.$route.params.project}/sequence`;
|
||||
this.sequenceData = await this.api([url]) || null
|
||||
}
|
||||
this.sequence = this.sequenceList.reduce( (a, b) => Math.max(a, b) );
|
||||
},
|
||||
|
||||
showRemarksMenu (e) {
|
||||
this.remarksMenu = e;
|
||||
},
|
||||
|
||||
addRemark ({text}) {
|
||||
if (text) {
|
||||
if (this.remarks === null) {
|
||||
this.remarks = "";
|
||||
}
|
||||
if (this.remarks.length && this.remarks[this.remarks.length-1] != "\n") {
|
||||
this.remarks += "\n";
|
||||
}
|
||||
this.remarks += text;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
handleKeys (e) {
|
||||
if (e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && e.keyCode == 13) {
|
||||
// Ctrl+Enter
|
||||
if (this.isValid) {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
...mapActions(["api"])
|
||||
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user