Repository navigation
Conversation
… that finger until it lifts (spec:AST-094 FR14) A recognition that claimed its touches and was then cancelled gave them back to the browser unless another recognition owned them, so a finger resting there could pinch-zoom the page. A gesture that cancels itself on a finger's move, a long press dragged past its tolerance after it began, gave back a finger that was dragging: the host stopped cancelling its moves and the page scrolled the rest of the drag, a hand change FR14 forbids and UIKit never makes (its scroll view never takes a touch another recognizer had). A cancel during a finger's move now keeps that finger claimed until it lifts; a resting finger is given back as before, and a waiting recognition that fails still gives its claim back. The new arena case fails on the previous code: the dragging finger reads unclaimed right after the cancel.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
There was a problem hiding this comment.
Recommending approval: keeping a finger claimed until it lifts when its gesture is cancelled during that finger's move follows spec:AST-094 FR14, and the resting-finger and waiting-recognition releases stay as they were. The new arena test fails without the change and passes with it, and the gestures suite, core typecheck, lint and changeset check pass. Optionally, the releaseClaim comment could describe the rule as a gesture cancelled during a finger's move: a cancel() made from a listener during the move keeps the moving finger too, while the comment still lists a gesture the app cancels among those that give their touches back.
[Automated review]
| * cancels) gives them back to the browser unless another recognition owns | ||
| * them, so a finger still resting there can pinch-zoom the page. | ||
| * them, so a finger still resting there can pinch-zoom the page. A gesture | ||
| * that cancels itself on a finger's move (a long press past its tolerance) |
There was a problem hiding this comment.
Optional wording: the keep follows the move being delivered, not who cancels, so a cancel() made from a changed listener during the move keeps the moving finger too, while line 550 still lists a gesture "the app cancels" among those that give their touches back. "A gesture cancelled during a finger's move" would match the code.
PR Analysis Report📚 Storybook Preview🧪 Sandbox PreviewNo new or modified components detected. Bundle Size SummaryGzip figures measure each package's entry file, not the code an import pulls in.
Accessibility AuditStatus: No accessibility violations detected. Visual Regression55 of 502 shot(s) changed. A change here is a question, not a failure: check whether the after is the
and 35 more. Generated by PR Enrichment workflow | Storybook | Sandbox | View full report |
…or, and Lab's route through core's exports Core's internal touch foundation on the gesture recognizers (meta#8061) and the gesture host (meta#8071 + meta#8072): UIControl press tracking with its press profiles, paint and hooks; the animator, which starts a motion from rest at its commit, holds the value on screen inline while a curve runs, and lets a call wait for a presented frame; and Lab reaching Core only through its exports, internal entries included. It absorbs meta#8073 (closed: the press tracking this rests on), meta#8562 (the internal entries) and meta#8340 (the animator's start and hold fixes), with the held dip's load fixes, and keeps meta#8641's rule: a gesture that cancels itself while its finger moves keeps that finger until it lifts. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…or, and Lab's route through core's exports Core's internal touch foundation on the gesture recognizers (meta#8061) and the gesture host (meta#8071 + meta#8072): UIControl press tracking with its press profiles and hooks; the animator, which starts a motion from rest at its commit, holds the value on screen inline while a curve runs, and lets a call wait for a presented frame; and Lab reaching Core only through its exports, internal entries included. It absorbs meta#8073 (closed: the press tracking this rests on), meta#8562 (the internal entries) and meta#8340 (the animator's start and hold fixes), with the held dip's load fixes, and keeps meta#8641's rule: a gesture that cancels itself while its finger moves keeps that finger until it lifts. Press tracking is built by createPressTracker (from meta#8763), and a gesture that ends on an unmount or a replaced node calls no listener from the effect. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…mator Core's internal touch foundation on the gesture recognizers (meta#8061) and the gesture host (meta#8071 + meta#8072): UIControl press tracking with its press profiles and hooks, built by createPressTracker; and the animator, which starts a motion from rest at its commit, holds the value on screen inline while a curve runs, and lets a call wait for a presented frame. It absorbs meta#8073 (closed: the press tracking this rests on) and meta#8340 (the animator's start and hold fixes), with the held dip's load fixes, and keeps meta#8641's rule: a gesture that cancels itself while its finger moves keeps that finger until it lifts. A gesture that ends on an unmount or a replaced node calls no listener from the effect. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…mator Core's internal touch foundation on the gesture recognizers (meta#8061) and the gesture host (meta#8071 + meta#8072): UIControl press tracking with its press profiles and hooks, built by createPressTracker; and the animator, which starts a motion from rest at its commit, holds the value on screen inline while a curve runs, and lets a call wait for a presented frame. It absorbs meta#8073 (closed: the press tracking this rests on) and meta#8340 (the animator's start and hold fixes), with the held dip's load fixes, and keeps meta#8641's rule: a gesture that cancels itself while its finger moves keeps that finger until it lifts. A gesture that ends on an unmount or a replaced node calls no listener from the effect. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…mator Core's internal touch foundation on the gesture recognizers (meta#8061) and the gesture host (meta#8071 + meta#8072): UIControl press tracking with its press profiles and hooks, built by createPressTracker; and the animator, which starts a motion from rest at its commit, holds the value on screen inline while a curve runs, and lets a call wait for a presented frame. It absorbs meta#8073 (closed: the press tracking this rests on) and meta#8340 (the animator's start and hold fixes), with the held dip's load fixes, and keeps meta#8641's rule: a gesture that cancels itself while its finger moves keeps that finger until it lifts. A gesture that ends on an unmount or a replaced node calls no listener from the effect. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…mator Core's internal touch foundation on the gesture recognizers (meta#8061) and the gesture host (meta#8071 + meta#8072): UIControl press tracking with its press profiles and hooks, built by createPressTracker; and the animator, which starts a motion from rest at its commit, holds the value on screen inline while a curve runs, and lets a call wait for a presented frame. It absorbs meta#8073 (closed: the press tracking this rests on) and meta#8340 (the animator's start and hold fixes), with the held dip's load fixes, and keeps meta#8641's rule: a gesture that cancels itself while its finger moves keeps that finger until it lifts. A gesture that ends on an unmount or a replaced node calls no listener from the effect. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…mator Core's internal touch foundation on the gesture recognizers (meta#8061) and the gesture host (meta#8071 + meta#8072): UIControl press tracking with its press profiles and hooks, built by createPressTracker; and the animator, which starts a motion from rest at its commit, holds the value on screen inline while a curve runs, and lets a call wait for a presented frame. It absorbs meta#8073 (closed: the press tracking this rests on) and meta#8340 (the animator's start and hold fixes), with the held dip's load fixes, and keeps meta#8641's rule: a gesture that cancels itself while its finger moves keeps that finger until it lifts. A gesture that ends on an unmount or a replaced node calls no listener from the effect. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…mator Core's internal touch foundation on the gesture recognizers (meta#8061) and the gesture host (meta#8071 + meta#8072): UIControl press tracking with its press profiles and hooks, built by createPressTracker; and the animator, which starts a motion from rest at its commit, holds the value on screen inline while a curve runs, and lets a call wait for a presented frame. It absorbs meta#8073 (closed: the press tracking this rests on) and meta#8340 (the animator's start and hold fixes), with the held dip's load fixes, and keeps meta#8641's rule: a gesture that cancels itself while its finger moves keeps that finger until it lifts. A gesture that ends on an unmount or a replaced node calls no listener from the effect. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…mator Core's internal touch foundation on the gesture recognizers (meta#8061) and the gesture host (meta#8071 + meta#8072): UIControl press tracking with its press profiles and hooks, built by createPressTracker; and the animator, which starts a motion from rest at its commit, holds the value on screen inline while a curve runs, and lets a call wait for a presented frame. It absorbs meta#8073 (closed: the press tracking this rests on) and meta#8340 (the animator's start and hold fixes), with the held dip's load fixes, and keeps meta#8641's rule: a gesture that cancels itself while its finger moves keeps that finger until it lifts. A gesture that ends on an unmount or a replaced node calls no listener from the effect. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…mator Core's internal touch foundation on the gesture recognizers (meta#8061) and the gesture host (meta#8071 + meta#8072): UIControl press tracking with its press profiles and hooks, built by createPressTracker; and the animator, which starts a motion from rest at its commit, holds the value on screen inline while a curve runs, and lets a call wait for a presented frame. It absorbs meta#8073 (closed: the press tracking this rests on) and meta#8340 (the animator's start and hold fixes), with the held dip's load fixes, and keeps meta#8641's rule: a gesture that cancels itself while its finger moves keeps that finger until it lifts. A gesture that ends on an unmount or a replaced node calls no listener from the effect. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…mator Core's internal touch foundation on the gesture recognizers (meta#8061) and the gesture host (meta#8071 + meta#8072): UIControl press tracking with its press profiles and hooks, built by createPressTracker; and the animator, which starts a motion from rest at its commit, holds the value on screen inline while a curve runs, and lets a call wait for a presented frame. It absorbs meta#8073 (closed: the press tracking this rests on) and meta#8340 (the animator's start and hold fixes), with the held dip's load fixes, and keeps meta#8641's rule: a gesture that cancels itself while its finger moves keeps that finger until it lifts. A gesture that ends on an unmount or a replaced node calls no listener from the effect. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…mator (#8076) Behavior-module PR: internal touch foundation, animator, press tracking Stacked on #8071, one commit on `main`, with no merge commits: review only this pull request's two commits, vjeux@0ddfa8937e and its test vjeux@91d49e1438. ## Why Touch components need Core's animator, press tracking and gesture host without making them public. ## What it absorbs, and why One solution, the internal touch foundation, so these close with a link here: - #8073 (closed): UIControl press tracking, its press profiles and the hooks. Everything else here rests on it. - #8340: the animator's start and hold. No frame shows where a motion is going, a call can wait for a presented frame, and in Safari a start from rest moves past a block of over two frames in the task that created it. It brings the held dip's load fixes from #8360 too. - #8641's rule (approved): a gesture that cancels itself while its finger moves keeps that finger until it lifts. ## Left out - Nothing here rests on the undecided mouse or pen drag selection (#8665), wheel ending (#8676) or an open owner ruling. - #8446's harness changes land with #8554. - The StyleX `defineConsts` build fix and the stylesheet's undefined-variable check stay in #8075. - `RotationGestureRecognizer` and `HoverGestureRecognizer` are not exported: nothing uses them, and rotation's threshold is not measured. - `paintPress` and its held state land with the action-group tracker, `useAnimator` with Lab's SearchField, and Core's internal entry points for Lab (#8562's change) with the first Lab component that uses them: each with its first user. ## What changes - Press tracking follows UIControl and is built by `createPressTracker`: the press clocks and profiles, `usePressTracker`, and release velocity on UIKit's filter (`spec:AST-100` FR1, FR2). - The animator holds the value on screen inline while a curve runs, starts a motion from rest when the browser commits it, offers `start: 'presented'` and `'commit'`, additive reversals and `handle.curve`. A browser proof counts no Layout or Paint while a spring plays, on the harness's rendering trace. - The motion modules and recognizers load in plain Node, with no StyleX module in their graph. ## How to check it 1. Run `pnpm exec vitest run scripts/motion-node-load.test.mjs`: the motion modules and recognizers load in a plain Node process. 2. In a component using `usePressTracker`, change `highlightDelay` while mounted: the next touch uses the new delay. 3. Run `pnpm test:interaction animator.interaction.browser.spec.ts` in Chromium and WebKit: a motion started right before a blocked main thread plays from its start after the block. ## Evidence - Local gates at 91d49e1: build, type check, `pnpm test:ui` (16,704), the node suites, lint, Storybook, and the motion and gesture interaction specs in Chromium (103) and WebKit (96); the held-dip, block and no-Layout-or-Paint proofs pass six times each at eight workers. ## Credit Co-authored-by: Cindy Zhang (her commits in #8073 and #8076). ## Scope - [x] Unrelated fixes, cleanup, and policy changes were removed or split. - [x] Public text and artifacts contain no internal Meta context. Behavior-module PR: LOC split waived per maintainer rule.
|
Closing: carried by #8076, merged. |
User impact
On a phone, a gesture that claims its finger and then cancels itself on one of that finger's moves hands the rest of the drag to the page. The visible case: a touch ContextMenu trigger held past its long press's commit, then dragged past the press's tolerance before the menu is due. The press cancels itself and the page scrolls with the finger, a drag that changes hands midway.
Problem and solution fit
spec:AST-094FR19), so the host stops cancelling that finger'stouchmove, and a finger that is dragging becomes the page's scroll.The fix keeps the release for a resting finger and stops it for a moving one.
Expected behavior and authority
spec:AST-094FR14 (current). The case was raised on #8058 as a question (issuecomment-6408527888): should a gesture that cancels itself on a finger's move keep that finger claimed until it lifts, with the release kept for a resting finger?Smallest restoration
GestureArena: a cancel during a finger's move keeps that finger claimed until it lifts. A resting finger is still given back when its gesture is cancelled (the pinch case), and a waiting recognition that fails still gives its claim back. One arena change, its test and a changeset.Evidence
a drag that cancels the press after the commit does not scroll the page either(a hold past the commit, then a 200 px drag) scrolls the page 190 px in Chromium on every run.arena.test.tscase fails on main (the dragging finger reads unclaimed right after the cancel) and passes with the fix.Scope
Testing
pnpm exec vitest run --project ui packages/core/src/gestures: 113 passed on main bb24260 with this commit; the new arena case fails without it.tsc --project packages/core/tsconfig.json --noEmit,ASTRYX_STRICT_LINT=1 eslint packages/core/src/gestures/GestureArena.ts packages/core/src/gestures/__tests__/arena.test.ts,pnpm check:changesets: clean.