fix(dashboard): make app drag-to-reorder work on touch (iOS)

On iOS/WebKit the pager's `touch-action: pan-x` made horizontal touchmove
non-cancelable, so the moment a long-press drag moved the finger the
browser started a native page-pan and fired pointercancel, aborting the
reorder before it visibly began. Preventing pointermove doesn't stop
native scrolling on iOS, and the reordering `touch-action: none` rule was
applied only after the drag began — too late, since touch-action is
latched at gesture start.

Drop the explicit pan-x (back to auto) so horizontal touchmove is
cancelable, and install a non-passive touchmove listener that
preventDefaults only once reordering is armed (post long-press). Before
arming it's a no-op, so a quick swipe still flips pages natively.
This commit is contained in:
jelveh
2026-07-17 08:47:58 -07:00
parent 14900514b9
commit af0dc06f49
2 changed files with 19 additions and 1 deletions
+13
View File
@@ -603,6 +603,18 @@ const TabApps = {
document.addEventListener('keydown', d.onKey);
window.addEventListener('blur', d.onBlur);
// iOS/WebKit latches touch-action at gesture start and treats an
// in-progress pan as non-cancelable, so preventing pointermove can't
// stop the pager from scrolling out from under a drag (it just fires
// pointercancel and kills the reorder). A non-passive touchmove that
// preventDefaults *once reordering is armed* is what actually holds the
// scroller still. Before arming (readyToDrag false) it's a no-op, so a
// quick pre-long-press swipe still flips pages natively.
if ( pointerType === 'touch' ) {
d.onTouchMove = ev => { if ( d.readyToDrag ) ev.preventDefault(); };
document.addEventListener('touchmove', d.onTouchMove, { passive: false });
}
// Touch: a long-press *arms* reordering (it doesn't grab the tile yet).
// Moving after that begins the drag; holding still instead lets the
// native long-press context menu (Uninstall) fire. A finger that moves
@@ -831,6 +843,7 @@ const TabApps = {
document.removeEventListener('pointercancel', d.onCancel);
document.removeEventListener('keydown', d.onKey);
window.removeEventListener('blur', d.onBlur);
if ( d.onTouchMove ) document.removeEventListener('touchmove', d.onTouchMove, { passive: false });
clearTimeout(d.longPressTimer);
clearTimeout(d.edgeTimer);
clearTimeout(d.flipClearTimer);
+6 -1
View File
@@ -776,7 +776,12 @@ input.myapps-search::-webkit-search-decoration {
padding-top: 8px;
scroll-snap-type: x mandatory;
overscroll-behavior-x: contain;
touch-action: pan-x;
/* Deliberately not pan-x: an explicit pan value makes horizontal touchmove
non-cancelable on iOS/WebKit, so the pager pans out from under a
long-press drag and cancels it. With the default (auto) the non-passive
touchmove listener installed during a reorder can preventDefault the
native pan; a quick pre-long-press swipe still flips pages natively. */
touch-action: auto;
scrollbar-width: none;
}