fix(client): handle pointercancel and make drag-start idempotent
Add onCancel handler for browser/OS gesture takeover (scroll, palm rejection) that previously leaked window listeners and left drag stuck. Extract detach() helper called by onUp, onCancel, and start() for idempotency. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,12 @@ function makeDrag() {
|
||||
let suppressClickOn: Square | null = null;
|
||||
const THRESHOLD = 6;
|
||||
|
||||
function detach() {
|
||||
window.removeEventListener('pointermove', onMove);
|
||||
window.removeEventListener('pointerup', onUp);
|
||||
window.removeEventListener('pointercancel', onCancel);
|
||||
}
|
||||
|
||||
function onMove(e: PointerEvent) {
|
||||
if (!state.active) return;
|
||||
state.x = e.clientX;
|
||||
@@ -36,15 +42,23 @@ function makeDrag() {
|
||||
}
|
||||
}
|
||||
|
||||
// pointercancel fires instead of pointerup when the browser/OS takes over
|
||||
// the gesture (common on touch). Abort the drag: clean up, drop nothing.
|
||||
function onCancel() {
|
||||
detach();
|
||||
state.active = null;
|
||||
state.moved = false;
|
||||
}
|
||||
|
||||
function onUp(e: PointerEvent) {
|
||||
window.removeEventListener('pointermove', onMove);
|
||||
window.removeEventListener('pointerup', onUp);
|
||||
detach();
|
||||
const src = state.active;
|
||||
const wasDrag = state.moved;
|
||||
state.active = null;
|
||||
state.moved = false;
|
||||
if (!src || !wasDrag) return; // a tap — the board click handler deals with it
|
||||
|
||||
// elementFromPoint returns null off-viewport — treated as an off-board drop.
|
||||
const el = document.elementFromPoint(e.clientX, e.clientY);
|
||||
const sqEl = el?.closest('[data-square]') as HTMLElement | null;
|
||||
const target = sqEl?.dataset.square as Square | undefined;
|
||||
@@ -61,6 +75,7 @@ function makeDrag() {
|
||||
}
|
||||
|
||||
function start(src: DragSource, e: PointerEvent) {
|
||||
detach(); // idempotency — drop any listeners from an unfinished prior drag
|
||||
suppressClickOn = null;
|
||||
state.active = src;
|
||||
state.x = startX = e.clientX;
|
||||
@@ -68,6 +83,7 @@ function makeDrag() {
|
||||
state.moved = false;
|
||||
window.addEventListener('pointermove', onMove);
|
||||
window.addEventListener('pointerup', onUp);
|
||||
window.addEventListener('pointercancel', onCancel);
|
||||
}
|
||||
|
||||
/** The board calls this first in its square-click handler. */
|
||||
|
||||
Reference in New Issue
Block a user