Fix touch gestures cancelling, backward flicks, and late onCommit

- Only the host's own lostpointercapture cancels a gesture. Taking
  capture makes the touched child lose its implicit touch capture, and
  that bubbling event was cancelling every touch gesture immediately.
- A flick at least commitVelocity fast now decides by its direction:
  backward cancels even past the distance threshold, forward commits.
  Slower releases still decide by distance.
- Pointer-up finishes an already-begun operation without awaiting, so
  onCommit runs inside the pointer-up event, where iOS still allows
  haptics, audio, and clipboard writes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Faarh2Ts6StJPcz2mRiHiR
This commit is contained in:
Claude
2026-10-31 05:41:51 +00:00
parent 7033814550
commit d4d7feb3b7
5 changed files with 112 additions and 22 deletions
+26
View File
@@ -247,6 +247,32 @@ describe(".onCommit()", () => {
}
});
it("runs synchronously inside the pointer-up event", async () => {
let calledDuringDispatch = false;
let dispatching = false;
const { host } = await mount(
gesture.to
.left()
.navigate(() => forward(originView(Target)))
.onCommit(() => {
calledDuringDispatch = dispatching;
})
.animate(motion),
);
host.dispatchEvent(pointer("pointerdown", 190, 0));
host.dispatchEvent(pointer("pointermove", 180, 500));
await flush();
host.dispatchEvent(pointer("pointermove", 40, 1000));
await flush();
// No awaiting between the event and the handler: iOS only allows
// haptics, audio, and clipboard writes during the user's gesture.
dispatching = true;
host.dispatchEvent(pointer("pointerup", 40, 1000));
dispatching = false;
expect(calledDuringDispatch).toBe(true);
});
it("fires at release, before the settling animation finishes", async () => {
const frames: FrameRequestCallback[] = [];
vi.stubGlobal("requestAnimationFrame", (cb: FrameRequestCallback) =>