Add color picker to change task color to task detail view

This commit is contained in:
kolaente 2020-06-21 20:27:39 +02:00
parent 8d94bdb081
commit 736fe03b39
No known key found for this signature in database
GPG key ID: F40E70337AB24C9B
4 changed files with 54 additions and 12 deletions

View file

@ -2,7 +2,7 @@
<div class="color-picker-container"> <div class="color-picker-container">
<verte <verte
v-model="color" v-model="color"
menuPosition="top" :menuPosition="menuPosition"
picker="square" picker="square"
model="hex" model="hex"
:enableAlpha="false" :enableAlpha="false"
@ -22,6 +22,7 @@
data() { data() {
return { return {
color: '', color: '',
lastChangeTimeout: null,
} }
}, },
components: { components: {
@ -31,6 +32,10 @@
value: { value: {
required: true, required: true,
}, },
menuPosition: {
type: String,
default: 'top',
},
}, },
watch: { watch: {
value(newVal) { value(newVal) {
@ -38,15 +43,22 @@
}, },
color() { color() {
this.update() this.update()
} },
}, },
mounted() { mounted() {
this.color = this.value this.color = this.value
}, },
methods: { methods: {
update() { update() {
this.$emit('input', this.color)
this.$emit('change') if(this.lastChangeTimeout !== null) {
clearTimeout(this.lastChangeTimeout)
}
this.lastChangeTimeout = setTimeout(() => {
this.$emit('input', this.color)
this.$emit('change')
}, 500)
}, },
reset() { reset() {
// FIXME: I havn't found a way to make it clear to the user the color war reset. // FIXME: I havn't found a way to make it clear to the user the color war reset.

View file

@ -67,6 +67,7 @@ import { faSortUp } from '@fortawesome/free-solid-svg-icons'
import { faList } from '@fortawesome/free-solid-svg-icons' import { faList } from '@fortawesome/free-solid-svg-icons'
import { faEllipsisV } from '@fortawesome/free-solid-svg-icons' import { faEllipsisV } from '@fortawesome/free-solid-svg-icons'
import { faFilter } from '@fortawesome/free-solid-svg-icons' import { faFilter } from '@fortawesome/free-solid-svg-icons'
import { faFillDrip } from '@fortawesome/free-solid-svg-icons'
import { faComments } from '@fortawesome/free-regular-svg-icons' import { faComments } from '@fortawesome/free-regular-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
@ -113,6 +114,7 @@ library.add(faSortUp)
library.add(faList) library.add(faList)
library.add(faEllipsisV) library.add(faEllipsisV)
library.add(faFilter) library.add(faFilter)
library.add(faFillDrip)
Vue.component('icon', FontAwesomeIcon) Vue.component('icon', FontAwesomeIcon)

View file

@ -15,7 +15,7 @@ export default class TaskService extends AbstractService {
delete: '/tasks/{id}', delete: '/tasks/{id}',
}); });
} }
modelFactory(data) { modelFactory(data) {
return new TaskModel(data) return new TaskModel(data)
} }
@ -34,9 +34,9 @@ export default class TaskService extends AbstractService {
model.listId = Number(model.listId) model.listId = Number(model.listId)
// Convert dates into an iso string // Convert dates into an iso string
model.dueDate = model.dueDate === null ? null : formatISO(new Date(model.dueDate)) model.dueDate = model.dueDate ? null : formatISO(new Date(model.dueDate))
model.startDate = model.startDate === null ? null : formatISO(new Date(model.startDate)) model.startDate = model.startDate ? null : formatISO(new Date(model.startDate))
model.endDate = model.endDate === null ? null : formatISO(new Date(model.endDate)) model.endDate = model.endDate ? null : formatISO(new Date(model.endDate))
model.created = formatISO(model.created) model.created = formatISO(model.created)
model.updated = formatISO(model.updated) model.updated = formatISO(model.updated)
@ -48,7 +48,7 @@ export default class TaskService extends AbstractService {
} }
// Make normal timestamps from js dates // Make normal timestamps from js dates
if(model.reminderDates.length > 0) { if (model.reminderDates.length > 0) {
model.reminderDates = model.reminderDates.map(r => { model.reminderDates = model.reminderDates.map(r => {
return formatISO(new Date(r)) return formatISO(new Date(r))
}) })
@ -82,14 +82,14 @@ export default class TaskService extends AbstractService {
} }
// Do the same for all related tasks // Do the same for all related tasks
Object.keys(model.relatedTasks).forEach(relationKind => { Object.keys(model.relatedTasks).forEach(relationKind => {
model.relatedTasks[relationKind] = model.relatedTasks[relationKind].map(t => { model.relatedTasks[relationKind] = model.relatedTasks[relationKind].map(t => {
return this.processModel(t) return this.processModel(t)
}) })
}) })
// Process all attachments to preven parsing errors // Process all attachments to preven parsing errors
if(model.attachments.length > 0) { if (model.attachments.length > 0) {
const attachmentService = new AttachmentService() const attachmentService = new AttachmentService()
model.attachments.map(a => { model.attachments.map(a => {
return attachmentService.processModel(a) return attachmentService.processModel(a)
@ -97,7 +97,7 @@ export default class TaskService extends AbstractService {
} }
// Preprocess all labels // Preprocess all labels
if(model.labels.length > 0) { if (model.labels.length > 0) {
const labelService = new LabelService() const labelService = new LabelService()
model.labels = model.labels.map(l => labelService.processModel(l)) model.labels = model.labels.map(l => labelService.processModel(l))
} }

View file

@ -147,6 +147,18 @@
@change="saveTask" @change="saveTask"
ref="repeatAfter"/> ref="repeatAfter"/>
</div> </div>
<div class="column" v-if="activeFields.color">
<!-- Color -->
<div class="detail-title">
<icon icon="fill-drip"/>
Color
</div>
<color-picker
v-model="taskColor"
menu-position="bottom"
@change="saveTask"
ref="color"/>
</div>
</div> </div>
<!-- Labels --> <!-- Labels -->
@ -286,6 +298,10 @@
<span class="icon is-small"><icon icon="list"/></span> <span class="icon is-small"><icon icon="list"/></span>
Move task Move task
</a> </a>
<a class="button" @click="setFieldActive('color')">
<span class="icon is-small"><icon icon="fill-drip"/></span>
Set task color
</a>
<a class="button is-danger is-outlined noshadow has-no-border" @click="showDeleteModal = true"> <a class="button is-danger is-outlined noshadow has-no-border" @click="showDeleteModal = true">
<span class="icon is-small"><icon icon="trash-alt"/></span> <span class="icon is-small"><icon icon="trash-alt"/></span>
Delete task Delete task
@ -330,10 +346,12 @@
import Comments from '../../components/tasks/partials/comments' import Comments from '../../components/tasks/partials/comments'
import router from '../../router' import router from '../../router'
import ListSearch from '../../components/tasks/partials/listSearch' import ListSearch from '../../components/tasks/partials/listSearch'
import ColorPicker from "../../components/input/colorPicker";
export default { export default {
name: 'TaskDetailView', name: 'TaskDetailView',
components: { components: {
ColorPicker,
ListSearch, ListSearch,
Reminders, Reminders,
RepeatAfter, RepeatAfter,
@ -355,6 +373,13 @@
// The due date is a seperate property in the task to prevent flatpickr from modifying the task model // The due date is a seperate property in the task to prevent flatpickr from modifying the task model
// in store right after updating it from the api resulting in the wrong due date format being saved in the task. // in store right after updating it from the api resulting in the wrong due date format being saved in the task.
dueDate: null, dueDate: null,
// We doubled the task color property here because verte does not have a real change property, leading
// to the color property change being triggered when the # is removed from it, leading to an update,
// which leads in turn to a change... This creates an infinite loop in which the task is updated, changed,
// updated, changed, updated and so on.
// To prevent this, we put the task color property in a seperate value which is set to the task color
// when it is saved and loaded.
taskColor: '',
showDeleteModal: false, showDeleteModal: false,
taskTitle: '', taskTitle: '',
@ -382,6 +407,7 @@
attachments: false, attachments: false,
relatedTasks: false, relatedTasks: false,
moveList: false, moveList: false,
color: false,
}, },
} }
}, },
@ -425,6 +451,7 @@
.then(r => { .then(r => {
this.$set(this, 'task', r) this.$set(this, 'task', r)
this.taskTitle = this.task.title this.taskTitle = this.task.title
this.taskColor = this.task.hexColor
this.setActiveFields() this.setActiveFields()
}) })
.catch(e => { .catch(e => {
@ -472,6 +499,7 @@
saveTask(undoCallback = null) { saveTask(undoCallback = null) {
this.task.dueDate = this.dueDate this.task.dueDate = this.dueDate
this.task.hexColor = this.taskColor
// If no end date is being set, but a start date and due date, // If no end date is being set, but a start date and due date,
// use the due date as the end date // use the due date as the end date