fix(stammbaum): node tap stopped selecting — defer pointer capture to drag start (#692)
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 2m34s
CI / OCR Service Tests (pull_request) Successful in 21s
CI / Backend Unit Tests (pull_request) Successful in 3m41s
CI / fail2ban Regex (pull_request) Successful in 44s
CI / Semgrep Security Scan (pull_request) Successful in 21s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m7s

Capturing the pointer on pointerdown made the browser dispatch the trailing
click at the SVG instead of the node under the finger, so node taps silently
stopped opening the person panel. Capture only once a drag crosses the
threshold; a tap now reaches the node's onclick. Verified live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-29 18:54:48 +02:00
parent d5a7974f3a
commit b170085311

View File

@@ -104,11 +104,11 @@ export const panZoomGestures: Action<SVGSVGElement, PanZoomGesturesParams> = (no
const onPointerDown = (e: PointerEvent) => {
cancelInertia();
try {
node.setPointerCapture(e.pointerId);
} catch {
/* pointer not capturable (e.g. synthetic event) — drag still works */
}
// NB: do NOT capture the pointer here — capturing on pointerdown makes the
// browser dispatch the trailing `click` at this element instead of the
// node under the pointer, which silently breaks node selection (a tap must
// still reach the node's onclick). Capture is deferred to the first move
// that crosses the drag threshold (see onPointerMove).
pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
p.onGestureStart?.();
@@ -146,7 +146,17 @@ export const panZoomGestures: Action<SVGSVGElement, PanZoomGesturesParams> = (no
if (!dragging) return;
const dxPx = e.clientX - lastX;
const dyPx = e.clientY - lastY;
if (!moved && Math.hypot(dxPx, dyPx) >= DRAG_THRESHOLD_PX) moved = true;
if (!moved && Math.hypot(dxPx, dyPx) >= DRAG_THRESHOLD_PX) {
// A real drag has started — now capture so we keep receiving move/up
// even if the pointer leaves the canvas. (Deferred from pointerdown so
// taps still select nodes.)
moved = true;
try {
node.setPointerCapture(e.pointerId);
} catch {
/* pointer not capturable (e.g. synthetic event) — drag still works */
}
}
const { dx, dy } = screenDeltaToSvg(
dxPx,