diff options
author | Raimund Schlüßler <raimund.schluessler@mailbox.org> | 2020-01-23 23:25:08 +0300 |
---|---|---|
committer | Raimund Schlüßler <raimund.schluessler@mailbox.org> | 2020-02-05 00:49:18 +0300 |
commit | 3ae623cf08c90b76ef969a3c140eb6fd3a611144 (patch) | |
tree | c53e85d451166590178531f8d7b82bf52975ad9e /src/components | |
parent | 2ab7e6bd3b091413b9fff797a51ddb213d8d1bf9 (diff) |
Allow dropping tasks on collections and calendars
Signed-off-by: Raimund Schlüßler <raimund.schluessler@mailbox.org>
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/AppNavigation/List.vue | 93 | ||||
-rw-r--r-- | src/components/AppNavigation/ListItemCalendar.vue | 91 | ||||
-rw-r--r-- | src/components/TaskBody.vue | 14 | ||||
-rw-r--r-- | src/components/TaskDragContainer.vue | 7 |
4 files changed, 190 insertions, 15 deletions
diff --git a/src/components/AppNavigation/List.vue b/src/components/AppNavigation/List.vue index 26b3c836..a1ad5b67 100644 --- a/src/components/AppNavigation/List.vue +++ b/src/components/AppNavigation/List.vue @@ -31,7 +31,12 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. :to="{ name: 'collections', params: { collectionId: collection.id } }" :title="collection.displayName" class="collection reactive" - @add="dropTaskOnCollection(...arguments, collection)"> + draggable="false" + @dragstart.native="dragStart" + @drop.native="dropTaskOnCollection(...arguments, collection)" + @dragover.native="dragOver" + @dragenter.native="dragEnter(...arguments, collection)" + @dragleave.native="dragLeave"> <AppNavigationCounter slot="counter"> {{ collectionCount(collection.id) | counterFormatter }} </AppNavigationCounter> @@ -140,11 +145,87 @@ export default { 'setPercentComplete', 'setDate', ]), - dropTaskOnCollection: function($event, collection) { - let task - const taskAttribute = $event.item.attributes['task-id'] - if (taskAttribute) { - task = this.getTask(taskAttribute.value) + + /** + * Handle the drag start + * + * @param {Object} e The event object + * @returns {Boolean} + */ + dragStart(e) { + e.stopPropagation() + e.preventDefault() + return false + }, + /** + * Handle the drag over + * + * @param {Object} e The event object + * @returns {Boolean} + */ + dragOver(e) { + if (e.preventDefault) { + e.preventDefault() + } + return false + }, + /** + * Set the appropriate class on hovering + * + * @param {Object} e The event object + * @param {Object} collection The collection on which the task was dropped + */ + dragEnter(e, collection) { + // Check if dropping here is allowed + if (!['starred', 'completed', 'today', 'week'].includes(collection.id)) { + return + } + // Get the correct element, in case we hover a child. + if (e.target.closest) { + const target = e.target.closest('li.collection') + if (target) { + const collections = document.querySelectorAll('li.collection') + collections.forEach((f) => { f.classList.remove('dnd-hover') }) + target.classList.add('dnd-hover') + } + } + }, + /** + * Remove the hovering class after leaving + * + * @param {Object} e The event object + */ + dragLeave(e) { + // Don't do anything if we leave towards a child element. + if (e.target.contains(e.relatedTarget)) { + return + } + // Get the correct element, in case we leave directly from a child. + if (e.target.closest) { + const target = e.target.closest('li.collection') + if (!target || target.contains(e.relatedTarget)) { + return + } + target.classList.remove('dnd-hover') + } + }, + /** + * Drop a task on a collection + * + * @param {Object} e The event object + * @param {Object} collection The collection + */ + dropTaskOnCollection(e, collection) { + // Remove all hover classes + const collections = document.querySelectorAll('li.collection') + collections.forEach((f) => { f.classList.remove('dnd-hover') }) + // Check if dropping here is allowed + if (!['starred', 'completed', 'today', 'week'].includes(collection.id)) { + return + } + const taskUri = e.dataTransfer.getData('text/plain') + if (taskUri) { + const task = this.getTask(taskUri) switch (collection.id) { case 'starred': this.setPriority({ task: task, priority: 1 }) diff --git a/src/components/AppNavigation/ListItemCalendar.vue b/src/components/AppNavigation/ListItemCalendar.vue index cc31bfc6..05dc7c21 100644 --- a/src/components/AppNavigation/ListItemCalendar.vue +++ b/src/components/AppNavigation/ListItemCalendar.vue @@ -28,7 +28,12 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. :title="calendar.displayName" :class="{edit: editing, deleted: !!deleteTimeout}" class="list reactive" - @add="dropTaskOnCalendar(...arguments, calendar)"> + draggable="false" + @dragstart.native="dragStart" + @drop.native="dropTask" + @dragover.native="dragOver" + @dragenter.native="dragEnter" + @dragleave.native="dragLeave"> <AppNavigationIconBullet slot="icon" :color="calendar.color" /> <template v-if="!deleteTimeout" slot="counter"> @@ -235,13 +240,83 @@ export default { 'deleteCalendar', 'moveTask', ]), - dropTaskOnCalendar: function($event, calendar) { - let task - const taskAttribute = $event.item.attributes['task-id'] - if (taskAttribute) { - task = this.getTask(taskAttribute.value) - if (calendar !== task.calendar) { - this.moveTask({ task: task, calendar: calendar, parent: undefined }) + + /** + * Handle the drag start + * + * @param {Object} e The event object + * @returns {Boolean} + */ + dragStart(e) { + e.stopPropagation() + e.preventDefault() + return false + }, + /** + * Handle the drag over + * + * @param {Object} e The event object + * @returns {Boolean} + */ + dragOver(e) { + if (e.preventDefault) { + e.preventDefault() + } + return false + }, + /** + * Set the appropriate class on hovering + * + * @param {Object} e The event object + */ + dragEnter(e) { + // Check if dropping here is allowed + if (this.calendar.readOnly) { + return + } + // Get the correct element, in case we hover a child. + if (e.target.closest) { + const target = e.target.closest('li.list') + if (target) { + const calendars = document.querySelectorAll('li.list') + calendars.forEach((f) => { f.classList.remove('dnd-hover') }) + target.classList.add('dnd-hover') + } + } + }, + /** + * Remove the hovering class after leaving + * + * @param {Object} e The event object + */ + dragLeave(e) { + // Don't do anything if we leave towards a child element. + if (e.target.contains(e.relatedTarget)) { + return + } + // Get the correct element, in case we leave directly from a child. + if (e.target.closest) { + const target = e.target.closest('li.list') + if (!target || target.contains(e.relatedTarget)) { + return + } + target.classList.remove('dnd-hover') + } + }, + /** + * Drop a task on a calendar + * + * @param {Object} e The event object + */ + dropTask(e) { + // Remove all hover classes + const calendars = document.querySelectorAll('li.list') + calendars.forEach((f) => { f.classList.remove('dnd-hover') }) + const taskUri = e.dataTransfer.getData('text/plain') + if (taskUri) { + const task = this.getTask(taskUri) + if (this.calendar !== task.calendar) { + this.moveTask({ task: task, calendar: this.calendar, parent: undefined }) } } }, diff --git a/src/components/TaskBody.vue b/src/components/TaskBody.vue index 41101ba5..82e03bda 100644 --- a/src/components/TaskBody.vue +++ b/src/components/TaskBody.vue @@ -25,7 +25,8 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. :task-id="task.uri" :class="{done: task.completed, readOnly: task.calendar.readOnly}" :data-priority="[task.priority]" - class="task-item"> + class="task-item" + @dragstart="dragStart($event)"> <div :task-id="task.uri" :class="{active: isTaskOpen()}" class="task-body reactive" @@ -308,6 +309,17 @@ export default { overdue: overdue, /** + * Set task uri in the data transfer object + * so we can get it when dropped on an + * app-navigation-item + * + * @param {Object} e The drag event + */ + dragStart(e) { + e.dataTransfer.setData('text/plain', this.task.uri) + }, + + /** * Checks if one of the tasks sub(sub-...)tasks matches the search query * * @param {Task} task The task to search in diff --git a/src/components/TaskDragContainer.vue b/src/components/TaskDragContainer.vue index 56ab0c38..5b15994a 100644 --- a/src/components/TaskDragContainer.vue +++ b/src/components/TaskDragContainer.vue @@ -22,6 +22,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>. <template> <draggable tag="ol" :list="['']" + :set-data="setDragData" v-bind="{group: 'tasks', swapThreshold: 0.30, delay: 500, delayOnTouchOnly: true, touchStartThreshold: 3, disabled: disabled, filter: '.readOnly'}" @add="onAdd"> <slot /> @@ -56,6 +57,12 @@ export default { 'setDate', ]), + setDragData: (dataTransfer) => { + // We do nothing here, this just prevents + // vue.draggable from setting data on the + // dataTransfer object. + }, + /** * Called when a task is dropped. * |