2019-11-24 14:16:24 +01:00
|
|
|
<template>
|
|
|
|
<multiselect
|
2020-09-05 22:35:52 +02:00
|
|
|
:clear-on-select="true"
|
|
|
|
:close-on-select="false"
|
|
|
|
:disabled="disabled"
|
|
|
|
:hide-selected="true"
|
|
|
|
:internal-search="true"
|
|
|
|
:loading="labelService.loading || labelTaskService.loading"
|
|
|
|
:multiple="true"
|
|
|
|
:options="foundLabels"
|
|
|
|
:options-limit="300"
|
|
|
|
:searchable="true"
|
|
|
|
:showNoOptions="false"
|
|
|
|
:taggable="true"
|
|
|
|
@search-change="findLabel"
|
|
|
|
@select="addLabel"
|
|
|
|
@tag="createAndAddLabel"
|
|
|
|
label="title"
|
|
|
|
placeholder="Type to add a new label..."
|
|
|
|
tag-placeholder="Add this as new label"
|
|
|
|
track-by="id"
|
|
|
|
v-model="labels"
|
2019-11-24 14:16:24 +01:00
|
|
|
>
|
2020-09-05 22:35:52 +02:00
|
|
|
<template
|
|
|
|
slot="tag"
|
|
|
|
slot-scope="{ option }">
|
|
|
|
<span
|
|
|
|
:style="{'background': option.hexColor, 'color': option.textColor}"
|
|
|
|
class="tag">
|
|
|
|
<span>{{ option.title }}</span>
|
|
|
|
<a @click="removeLabel(option)" class="delete is-small"></a>
|
|
|
|
</span>
|
2019-11-24 14:16:24 +01:00
|
|
|
</template>
|
|
|
|
<template slot="clear" slot-scope="props">
|
2020-09-05 22:35:52 +02:00
|
|
|
<div
|
|
|
|
@mousedown.prevent.stop="clearAllLabels(props.search)"
|
|
|
|
class="multiselect__clear"
|
|
|
|
v-if="labels.length"></div>
|
2019-11-24 14:16:24 +01:00
|
|
|
</template>
|
|
|
|
</multiselect>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2020-11-02 21:47:31 +01:00
|
|
|
import differenceWith from 'lodash/differenceWith'
|
2019-11-24 14:16:24 +01:00
|
|
|
|
2020-09-05 22:35:52 +02:00
|
|
|
import LabelService from '../../../services/label'
|
|
|
|
import LabelModel from '../../../models/label'
|
|
|
|
import LabelTaskService from '../../../services/labelTask'
|
|
|
|
import LoadingComponent from '../../misc/loading'
|
|
|
|
import ErrorComponent from '../../misc/error'
|
2019-11-24 14:16:24 +01:00
|
|
|
|
2020-09-05 22:35:52 +02:00
|
|
|
export default {
|
|
|
|
name: 'edit-labels',
|
|
|
|
props: {
|
|
|
|
value: {
|
|
|
|
default: () => [],
|
|
|
|
type: Array,
|
2019-11-24 14:16:24 +01:00
|
|
|
},
|
2020-09-05 22:35:52 +02:00
|
|
|
taskId: {
|
|
|
|
type: Number,
|
|
|
|
required: true,
|
2019-11-24 14:16:24 +01:00
|
|
|
},
|
2020-09-05 22:35:52 +02:00
|
|
|
disabled: {
|
|
|
|
default: false,
|
2019-11-24 14:16:24 +01:00
|
|
|
},
|
2020-09-05 22:35:52 +02:00
|
|
|
},
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
labelService: LabelService,
|
|
|
|
labelTaskService: LabelTaskService,
|
|
|
|
foundLabels: [],
|
|
|
|
labelTimeout: null,
|
|
|
|
labels: [],
|
|
|
|
searchQuery: '',
|
|
|
|
}
|
|
|
|
},
|
|
|
|
components: {
|
|
|
|
multiselect: () => ({
|
2020-11-02 21:47:31 +01:00
|
|
|
component: import(/* webpackChunkName: "multiselect" */ 'vue-multiselect'),
|
2020-09-05 22:35:52 +02:00
|
|
|
loading: LoadingComponent,
|
|
|
|
error: ErrorComponent,
|
|
|
|
timeout: 60000,
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
watch: {
|
|
|
|
value(newLabels) {
|
|
|
|
this.labels = newLabels
|
2019-11-24 14:16:24 +01:00
|
|
|
},
|
2020-09-05 22:35:52 +02:00
|
|
|
},
|
|
|
|
created() {
|
|
|
|
this.labelService = new LabelService()
|
|
|
|
this.labelTaskService = new LabelTaskService()
|
|
|
|
this.labels = this.value
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
findLabel(query) {
|
|
|
|
this.searchQuery = query
|
|
|
|
if (query === '') {
|
|
|
|
this.clearAllLabels()
|
|
|
|
return
|
|
|
|
}
|
2019-11-24 14:16:24 +01:00
|
|
|
|
2020-09-05 22:35:52 +02:00
|
|
|
if (this.labelTimeout !== null) {
|
|
|
|
clearTimeout(this.labelTimeout)
|
|
|
|
}
|
2019-11-24 14:16:24 +01:00
|
|
|
|
2020-09-05 22:35:52 +02:00
|
|
|
// Delay the search 300ms to not send a request on every keystroke
|
|
|
|
this.labelTimeout = setTimeout(() => {
|
|
|
|
this.labelService.getAll({}, {s: query})
|
|
|
|
.then(response => {
|
|
|
|
this.$set(this, 'foundLabels', differenceWith(response, this.labels, (first, second) => {
|
|
|
|
return first.id === second.id
|
|
|
|
}))
|
|
|
|
this.labelTimeout = null
|
2019-11-24 14:16:24 +01:00
|
|
|
})
|
|
|
|
.catch(e => {
|
2020-01-30 22:47:08 +01:00
|
|
|
this.error(e, this)
|
2019-11-24 14:16:24 +01:00
|
|
|
})
|
2020-09-05 22:35:52 +02:00
|
|
|
}, 300)
|
|
|
|
},
|
|
|
|
clearAllLabels() {
|
|
|
|
this.$set(this, 'foundLabels', [])
|
|
|
|
},
|
|
|
|
addLabel(label) {
|
|
|
|
this.$store.dispatch('tasks/addLabel', {label: label, taskId: this.taskId})
|
|
|
|
.then(() => {
|
|
|
|
this.$emit('input', this.labels)
|
|
|
|
})
|
|
|
|
.catch(e => {
|
|
|
|
this.error(e, this)
|
|
|
|
})
|
|
|
|
},
|
|
|
|
removeLabel(label) {
|
|
|
|
this.$store.dispatch('tasks/removeLabel', {label: label, taskId: this.taskId})
|
|
|
|
.then(() => {
|
|
|
|
// Remove the label from the list
|
|
|
|
for (const l in this.labels) {
|
|
|
|
if (this.labels[l].id === label.id) {
|
|
|
|
this.labels.splice(l, 1)
|
2019-11-24 14:16:24 +01:00
|
|
|
}
|
2020-09-05 22:35:52 +02:00
|
|
|
}
|
|
|
|
this.$emit('input', this.labels)
|
|
|
|
})
|
|
|
|
.catch(e => {
|
|
|
|
this.error(e, this)
|
|
|
|
})
|
2019-11-24 14:16:24 +01:00
|
|
|
},
|
2020-09-05 22:35:52 +02:00
|
|
|
createAndAddLabel(title) {
|
|
|
|
let newLabel = new LabelModel({title: title})
|
|
|
|
this.labelService.create(newLabel)
|
|
|
|
.then(r => {
|
|
|
|
this.addLabel(r)
|
|
|
|
this.labels.push(r)
|
|
|
|
})
|
|
|
|
.catch(e => {
|
|
|
|
this.error(e, this)
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
},
|
|
|
|
}
|
2019-11-24 14:16:24 +01:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
|
|
</style>
|