feat: replace drag-and-drop with pointer events for item reordering
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m10s
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m10s
This commit is contained in:
@@ -28,13 +28,8 @@
|
|||||||
class="item-row"
|
class="item-row"
|
||||||
:data-idx="idx"
|
:data-idx="idx"
|
||||||
:class="{ 'drag-over': dragOverIdx === idx, dragging: draggingIdx === idx }"
|
:class="{ 'drag-over': dragOverIdx === idx, dragging: draggingIdx === idx }"
|
||||||
draggable="true"
|
|
||||||
@dragstart="(e) => onDragStart(e, idx)"
|
|
||||||
@dragover.prevent="(e) => onDragOver(e, idx)"
|
|
||||||
@drop.prevent="onDrop(idx)"
|
|
||||||
@dragend="onDragEnd"
|
|
||||||
>
|
>
|
||||||
<span class="drag-handle" title="Drag to reorder" @touchstart="(e) => onTouchStart(e, idx)">⠿</span>
|
<span class="drag-handle" title="Drag to reorder" @pointerdown.prevent="(e) => onPointerDown(e, idx)">⠿</span>
|
||||||
<div class="item-left">
|
<div class="item-left">
|
||||||
<img
|
<img
|
||||||
v-if="item.image_url"
|
v-if="item.image_url"
|
||||||
@@ -334,44 +329,13 @@ function cancelEditItem() {
|
|||||||
newItemLocalFile.value = null
|
newItemLocalFile.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDragStart(e: DragEvent, idx: number) {
|
function onPointerDown(e: PointerEvent, idx: number) {
|
||||||
if (e.dataTransfer) e.dataTransfer.effectAllowed = 'move'
|
|
||||||
cancelEditItem()
|
|
||||||
draggingIdx.value = idx
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDragOver(e: DragEvent, idx: number) {
|
|
||||||
if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'
|
|
||||||
if (draggingIdx.value === null || draggingIdx.value === idx) return
|
|
||||||
dragOverIdx.value = idx
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDrop(idx: number) {
|
|
||||||
if (draggingIdx.value === null || draggingIdx.value === idx) {
|
|
||||||
draggingIdx.value = null
|
|
||||||
dragOverIdx.value = null
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const moved = items.value.splice(draggingIdx.value, 1)[0]
|
|
||||||
items.value.splice(idx, 0, moved)
|
|
||||||
normalizeItemOrder()
|
|
||||||
draggingIdx.value = null
|
|
||||||
dragOverIdx.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
function onDragEnd() {
|
|
||||||
draggingIdx.value = null
|
|
||||||
dragOverIdx.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
function onTouchStart(e: TouchEvent, idx: number) {
|
|
||||||
draggingIdx.value = idx
|
draggingIdx.value = idx
|
||||||
cancelEditItem()
|
cancelEditItem()
|
||||||
|
|
||||||
const handleTouchMove = (moveEvt: TouchEvent) => {
|
const handlePointerMove = (moveEvt: PointerEvent) => {
|
||||||
moveEvt.preventDefault()
|
moveEvt.preventDefault()
|
||||||
const touch = moveEvt.touches[0]
|
const el = document.elementFromPoint(moveEvt.clientX, moveEvt.clientY) as Element | null
|
||||||
const el = document.elementFromPoint(touch.clientX, touch.clientY) as Element | null
|
|
||||||
if (!el) return
|
if (!el) return
|
||||||
const row = el.closest('[data-idx]') as HTMLElement | null
|
const row = el.closest('[data-idx]') as HTMLElement | null
|
||||||
if (!row) return
|
if (!row) return
|
||||||
@@ -381,29 +345,35 @@ function onTouchStart(e: TouchEvent, idx: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTouchEnd = (endEvt: TouchEvent) => {
|
const cleanup = (upEvt?: PointerEvent) => {
|
||||||
const touch = endEvt.changedTouches[0]
|
if (upEvt) {
|
||||||
const el = document.elementFromPoint(touch.clientX, touch.clientY) as Element | null
|
const el = document.elementFromPoint(upEvt.clientX, upEvt.clientY) as Element | null
|
||||||
const fromIdx = draggingIdx.value
|
const fromIdx = draggingIdx.value
|
||||||
if (el && fromIdx !== null) {
|
if (el && fromIdx !== null) {
|
||||||
const row = el.closest('[data-idx]') as HTMLElement | null
|
const row = el.closest('[data-idx]') as HTMLElement | null
|
||||||
if (row) {
|
if (row) {
|
||||||
const toIdx = parseInt(row.dataset.idx ?? '-1')
|
const toIdx = parseInt(row.dataset.idx ?? '-1')
|
||||||
if (toIdx !== -1 && toIdx !== fromIdx) {
|
if (toIdx !== -1 && toIdx !== fromIdx) {
|
||||||
const moved = items.value.splice(fromIdx, 1)[0]
|
const moved = items.value.splice(fromIdx, 1)[0]
|
||||||
items.value.splice(toIdx, 0, moved)
|
items.value.splice(toIdx, 0, moved)
|
||||||
normalizeItemOrder()
|
normalizeItemOrder()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
draggingIdx.value = null
|
draggingIdx.value = null
|
||||||
dragOverIdx.value = null
|
dragOverIdx.value = null
|
||||||
document.removeEventListener('touchmove', handleTouchMove)
|
document.removeEventListener('pointermove', handlePointerMove)
|
||||||
document.removeEventListener('touchend', handleTouchEnd)
|
document.removeEventListener('pointerup', handlePointerUp)
|
||||||
|
document.removeEventListener('pointercancel', handlePointerCancel)
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('touchmove', handleTouchMove, { passive: false })
|
const handlePointerUp = (upEvt: PointerEvent) => cleanup(upEvt)
|
||||||
document.addEventListener('touchend', handleTouchEnd)
|
const handlePointerCancel = () => cleanup()
|
||||||
|
|
||||||
|
document.addEventListener('pointermove', handlePointerMove, { passive: false })
|
||||||
|
document.addEventListener('pointerup', handlePointerUp)
|
||||||
|
document.addEventListener('pointercancel', handlePointerCancel)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadImage(file: File): Promise<string> {
|
async function uploadImage(file: File): Promise<string> {
|
||||||
@@ -575,8 +545,9 @@ function handleCancel() {
|
|||||||
cursor: grab;
|
cursor: grab;
|
||||||
color: var(--form-label, #aaa);
|
color: var(--form-label, #aaa);
|
||||||
font-size: 1.15rem;
|
font-size: 1.15rem;
|
||||||
padding-right: 0.45rem;
|
padding: 0.4rem 0.45rem 0.4rem 0;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
touch-action: none;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,13 +555,6 @@ function handleCancel() {
|
|||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (hover: none) {
|
|
||||||
.drag-handle {
|
|
||||||
padding: 0.5rem 0.6rem;
|
|
||||||
font-size: 1.4rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-left {
|
.item-left {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
Reference in New Issue
Block a user