Fix mobile drag and drop
This commit is contained in:
@ -208,6 +208,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['api'])) {
|
||||
transition: all 0.2s;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.task-item.selected {
|
||||
@ -235,6 +238,53 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['api'])) {
|
||||
background: #E8E8E8;
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
cursor: grab;
|
||||
padding: 4px;
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.drag-handle::before,
|
||||
.drag-handle::after {
|
||||
content: '';
|
||||
width: 18px;
|
||||
height: 2px;
|
||||
background: #999;
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.drag-handle span {
|
||||
width: 18px;
|
||||
height: 2px;
|
||||
background: #999;
|
||||
border-radius: 1px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.drag-handle:active::before,
|
||||
.drag-handle:active::after,
|
||||
.drag-handle:active span {
|
||||
background: #4A90E2;
|
||||
}
|
||||
|
||||
.drag-handle:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.task-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.task-name {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
@ -442,18 +492,29 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['api'])) {
|
||||
v-for="(task, index) in tasks"
|
||||
:key="task.id"
|
||||
:class="['task-item', task.status, { selected: task.selected, dragging: draggedTask === task }]"
|
||||
:draggable="!task.isSeparator"
|
||||
@click="!task.isSeparator && toggleTaskSelection(task)"
|
||||
@dblclick="!task.isSeparator && editSingleTask(task)"
|
||||
@dragstart="!task.isSeparator && onDragStart(task, $event)"
|
||||
@dragover="onDragOver(task, $event)"
|
||||
@drop="onDrop(task, $event)"
|
||||
@dragend="onDragEnd"
|
||||
>
|
||||
<div v-if="task.isSeparator" class="separator"></div>
|
||||
<template v-else>
|
||||
<div
|
||||
class="drag-handle"
|
||||
draggable="true"
|
||||
@dragstart="onDragStart(task, $event)"
|
||||
@dragend="onDragEnd"
|
||||
@touchstart="onTouchStart(task, $event)"
|
||||
@touchmove="onTouchMove(task, $event)"
|
||||
@touchend="onTouchEnd($event)"
|
||||
@touchcancel="onTouchCancel($event)"
|
||||
><span></span></div>
|
||||
<div
|
||||
class="task-content"
|
||||
@click="toggleTaskSelection(task)"
|
||||
@dblclick="editSingleTask(task)"
|
||||
>
|
||||
<div class="task-name">{{ task.name }}</div>
|
||||
<div class="task-notes" v-if="task.firstNoteLine">{{ task.firstNoteLine }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@ -523,6 +584,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['api'])) {
|
||||
currentFileName: 'Inbox',
|
||||
fileMtime: null,
|
||||
draggedTask: null,
|
||||
touchDraggedTask: null,
|
||||
touchStartY: 0,
|
||||
showFileModal: false,
|
||||
fileModalTitle: '',
|
||||
fileModalMode: '',
|
||||
@ -753,6 +816,61 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['api'])) {
|
||||
this.draggedTask = null;
|
||||
},
|
||||
|
||||
// Touch drag for mobile
|
||||
onTouchStart(task, event) {
|
||||
this.touchDraggedTask = task;
|
||||
this.touchStartY = event.touches[0].clientY;
|
||||
// Prevent default to stop scrolling while dragging from handle
|
||||
event.preventDefault();
|
||||
},
|
||||
|
||||
onTouchMove(task, event) {
|
||||
if (!this.touchDraggedTask) return;
|
||||
|
||||
event.preventDefault();
|
||||
const touchY = event.touches[0].clientY;
|
||||
|
||||
// Find which task element we're over
|
||||
const elements = document.elementsFromPoint(event.touches[0].clientX, touchY);
|
||||
|
||||
for (const el of elements) {
|
||||
if (el.classList && el.classList.contains('task-item')) {
|
||||
// Find the task index
|
||||
const taskItems = Array.from(document.querySelectorAll('.task-item'));
|
||||
const targetIndex = taskItems.indexOf(el);
|
||||
|
||||
if (targetIndex !== -1) {
|
||||
const targetTask = this.tasks[targetIndex];
|
||||
|
||||
if (targetTask && targetTask !== this.touchDraggedTask && !targetTask.isSeparator) {
|
||||
const draggedIndex = this.tasks.indexOf(this.touchDraggedTask);
|
||||
|
||||
if (draggedIndex !== -1 && draggedIndex !== targetIndex) {
|
||||
// Remove from old position
|
||||
this.tasks.splice(draggedIndex, 1);
|
||||
|
||||
// Insert at new position
|
||||
const newIndex = draggedIndex < targetIndex ? targetIndex : targetIndex;
|
||||
this.tasks.splice(newIndex, 0, this.touchDraggedTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onTouchEnd(event) {
|
||||
if (this.touchDraggedTask) {
|
||||
this.saveFile();
|
||||
this.touchDraggedTask = null;
|
||||
}
|
||||
},
|
||||
|
||||
onTouchCancel(event) {
|
||||
this.touchDraggedTask = null;
|
||||
},
|
||||
|
||||
// Actions
|
||||
addTask() {
|
||||
this.editorTitle = 'New Task';
|
||||
|
||||
Reference in New Issue
Block a user