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;
|
transition: all 0.2s;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-item.selected {
|
.task-item.selected {
|
||||||
@ -235,6 +238,53 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['api'])) {
|
|||||||
background: #E8E8E8;
|
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 {
|
.task-name {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@ -442,18 +492,29 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['api'])) {
|
|||||||
v-for="(task, index) in tasks"
|
v-for="(task, index) in tasks"
|
||||||
:key="task.id"
|
:key="task.id"
|
||||||
:class="['task-item', task.status, { selected: task.selected, dragging: draggedTask === task }]"
|
: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)"
|
@dragover="onDragOver(task, $event)"
|
||||||
@drop="onDrop(task, $event)"
|
@drop="onDrop(task, $event)"
|
||||||
@dragend="onDragEnd"
|
|
||||||
>
|
>
|
||||||
<div v-if="task.isSeparator" class="separator"></div>
|
<div v-if="task.isSeparator" class="separator"></div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="task-name">{{ task.name }}</div>
|
<div
|
||||||
<div class="task-notes" v-if="task.firstNoteLine">{{ task.firstNoteLine }}</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>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -523,6 +584,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['api'])) {
|
|||||||
currentFileName: 'Inbox',
|
currentFileName: 'Inbox',
|
||||||
fileMtime: null,
|
fileMtime: null,
|
||||||
draggedTask: null,
|
draggedTask: null,
|
||||||
|
touchDraggedTask: null,
|
||||||
|
touchStartY: 0,
|
||||||
showFileModal: false,
|
showFileModal: false,
|
||||||
fileModalTitle: '',
|
fileModalTitle: '',
|
||||||
fileModalMode: '',
|
fileModalMode: '',
|
||||||
@ -753,6 +816,61 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['api'])) {
|
|||||||
this.draggedTask = null;
|
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
|
// Actions
|
||||||
addTask() {
|
addTask() {
|
||||||
this.editorTitle = 'New Task';
|
this.editorTitle = 'New Task';
|
||||||
|
|||||||
Reference in New Issue
Block a user