1- import { onBeforeUnmount , onMounted , ref , type Ref } from 'vue'
1+ import { onBeforeUnmount , onMounted , ref , watch , type Ref } from 'vue'
22
33export interface TouchReorderCallbacks {
44 onDragStart : ( _id : number ) => void
@@ -7,7 +7,7 @@ export interface TouchReorderCallbacks {
77 onCancel : ( ) => void
88}
99
10- const LONG_PRESS_MS = 300
10+ const LONG_PRESS_MS = 400
1111const MOVE_THRESHOLD = 8
1212const SCROLL_EDGE = 40
1313const SCROLL_SPEED = 8
@@ -120,6 +120,13 @@ export function useTouchReorder(
120120 dragId = null
121121 }
122122
123+ // Suppress the browser context menu while a long-press drag is pending or active.
124+ function onContextMenu ( e : Event ) {
125+ if ( longPressTimer || isTouchDragging . value ) {
126+ e . preventDefault ( )
127+ }
128+ }
129+
123130 function onTouchStart ( e : TouchEvent ) {
124131 if ( enabled && ! enabled . value ) return
125132 const touch = e . touches [ 0 ]
@@ -161,8 +168,9 @@ export function useTouchReorder(
161168 return
162169 }
163170
164- // We're dragging — prevent scroll
171+ // We're dragging — prevent scroll and default behaviour
165172 e . preventDefault ( )
173+ e . stopPropagation ( )
166174
167175 moveGhost ( touch . clientX , touch . clientY )
168176 autoScroll ( touch . clientY )
@@ -176,35 +184,55 @@ export function useTouchReorder(
176184 }
177185 }
178186
179- function onTouchEnd ( ) {
187+ function onTouchEnd ( e : TouchEvent ) {
180188 clearLongPress ( )
181189 stopAutoScroll ( )
182190 removeGhost ( )
183191
184192 if ( isTouchDragging . value ) {
193+ // Prevent the browser from synthesising a click / mousedown after the drag.
194+ e . preventDefault ( )
185195 isTouchDragging . value = false
186196 callbacks . onDrop ( )
187197 }
188198 dragId = null
189199 }
190200
191- onMounted ( ( ) => {
192- const el = containerRef . value
193- if ( ! el ) return
201+ function bind ( el : HTMLElement ) {
194202 el . addEventListener ( 'touchstart' , onTouchStart , { passive : false } )
195203 el . addEventListener ( 'touchmove' , onTouchMove , { passive : false } )
196- el . addEventListener ( 'touchend' , onTouchEnd )
204+ el . addEventListener ( 'touchend' , onTouchEnd , { passive : false } )
197205 el . addEventListener ( 'touchcancel' , cancelDrag )
198- } )
206+ el . addEventListener ( 'contextmenu' , onContextMenu )
207+ }
199208
200- onBeforeUnmount ( ( ) => {
201- cancelDrag ( )
202- const el = containerRef . value
203- if ( ! el ) return
209+ function unbind ( el : HTMLElement ) {
204210 el . removeEventListener ( 'touchstart' , onTouchStart )
205211 el . removeEventListener ( 'touchmove' , onTouchMove )
206212 el . removeEventListener ( 'touchend' , onTouchEnd )
207213 el . removeEventListener ( 'touchcancel' , cancelDrag )
214+ el . removeEventListener ( 'contextmenu' , onContextMenu )
215+ }
216+
217+ // Re-bind when the container ref changes (e.g. v-if toggling the element).
218+ let boundEl : HTMLElement | null = null
219+ function syncBinding ( ) {
220+ const el = containerRef . value
221+ if ( el === boundEl ) return
222+ if ( boundEl ) unbind ( boundEl )
223+ boundEl = el
224+ if ( el ) bind ( el )
225+ }
226+
227+ onMounted ( syncBinding )
228+ watch ( containerRef , syncBinding )
229+
230+ onBeforeUnmount ( ( ) => {
231+ cancelDrag ( )
232+ if ( boundEl ) {
233+ unbind ( boundEl )
234+ boundEl = null
235+ }
208236 } )
209237
210238 return { isTouchDragging }
0 commit comments