Repository navigation
refactor(motion): one release policy, resolveSnapPoint, on selectSnapPoint - #8663
Conversation
…Point Where a release lands is decided in one place: motion/resolveSnapPoint.ts. A slow release lands on the snap point nearest where it would rest; a flick (flickThreshold or faster) goes at least to the next snap point in its direction; with start, a release moving back toward it cancels to it (spec:AST-094 FR37); with fromIndex and maxSteps it moves at most that many points. selectSnapPoint in projection.ts stays the math primitive, and paging moves to selectPage.ts on top of resolveSnapPoint. Internal: not exported from the package (a test locks that). No behavior change. Split from #8565 so the public drag-to-snap-points API can build on it; #8565 keeps the bottom sheet's adoption.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
57a240e to
c85d092
Compare
PR Analysis Report
View Storybook for this PR · View Sandbox for this PR Generated by PR Enrichment workflow | View current CI run |
…se policy says The factory now calls resolveSnapPoint (meta#8663) with the snap points, the release position and velocity, flickThreshold and the drag's start, instead of a release rule of its own: the nearest snap point, the next one past the flick speed, and back to where the drag started when the release moves back toward it (spec:AST-094 FR37). Its own snapPointAfterRelease and nearestIndex are gone; the starting snap point comes from the same policy. The pure-rule tests now check FR2's and FR37's cases through that call, and the touch tests are unchanged.
There was a problem hiding this comment.
This looks ready to approve: resolveSnapPoint stays internal, its projection and moving-back cancel follow AST-094 FR3 and FR37 with thresholds left to callers (AST-100 FR3), and selectPage keeps its exact behavior after the move. Nothing is required; one optional hardening before a public caller such as #8658 passes thresholds through: a still release with flickThreshold: 0 is treated as a downward flick, which a velocity !== 0 guard on the flick branch (as the cancel branch already has) would prevent.
[Automated review]
| maxSteps, | ||
| }); | ||
| let index = choice.index; | ||
| if (Math.abs(velocity) >= flickThreshold) { |
There was a problem hiding this comment.
Optional hardening: with flickThreshold: 0, a still release (velocity === 0) enters this branch and the else arm walks down the axis, so resolveSnapPoint({points: [0, 300, 640], position: 300, velocity: 0, flickThreshold: 0}) returns index 0 instead of 1. A velocity !== 0 guard here, as the cancel branch below has, keeps a still release from flicking if a caller-supplied threshold ever reaches 0.
…xperimental touch exports The motion README's Files table is the union: main's rows, with meta#8663's resolveSnapPoint.ts and selectPage.ts, plus this branch's, in main's order. # Conflicts: # packages/core/src/motion/README.md
The motion README's Files table is the union: main's rows, with meta#8663's resolveSnapPoint.ts and selectPage.ts, plus this branch's, in main's order. # Conflicts: # packages/core/src/motion/README.md
…; this pull request's presentation.ts rows kept beside them) into meta#8293
…tor's browser proofs The motion README's Files table is the union: main's rows, with meta#8663's resolveSnapPoint.ts and selectPage.ts, plus this branch's, in main's order. # Conflicts: # packages/core/src/motion/README.md
…policy Main now has one release policy, `motion/resolveSnapPoint.ts` (meta#8663): "the one 'where does a release land' helper ... a component's release rule calls it with its own points and thresholds". `paging.ts`'s own `selectPage` was a second one, so it goes: the pager (meta#8146) picks the page with these measured thresholds through `resolveSnapPoint`. The thresholds stay in the paging table (`paging.release`), and their measured page-choice tests move with the rule. The scroll table keeps its `scroll.paging` estimates: main's `motion/selectPage.ts` reads `scroll.paging.projectionRate`.
`pageForRelease` is the pager's release rule on main's release policy, `motion/resolveSnapPoint.ts` (meta#8663), with UIKit's measured paging thresholds from `paging.release`: a flick from the advance speed on, the nearest page below it, a release moving back toward the start page settling there (spec:AST-094 FR37) through `start`, and at most one page from the start page through `maxSteps`. A looping pager passes only the pages within reach of its start page, so the target may leave the range and the owner wraps it. The measured page-choice tests move here from paging.test.ts. One test released at exactly half a page: past the pan's 10 pt claim its "90 px" drag moved the content 80 px. The policy settles an exact tie on the earlier page; UIKit's measured rule is "past half a page, the next one", so the finger now moves 100 px and the content travels the 90 px the test describes.
User impact
None directly: no behavior changes. Where a drag's release lands (a sheet's detent, a pager's page, a snap point) is now decided by one internal helper, so every gesture that settles on a set of positions, including the upcoming public drag-to-snap-points API, follows the same rules instead of each keeping its own copy.
Change
motion/resolveSnapPoint.ts(new, internal) is the release policy, on the existingselectSnapPointprimitive:flickThresholdor faster) goes at least to the next snap point in its direction.start, a release moving back toward where the drag began cancels to it (AST-094 FR37).fromIndexandmaxSteps, it moves at most that many points (a pager's one page per release).motion/projection.tskeepsselectSnapPointas the math primitive. Paging moves out tomotion/selectPage.ts, which is built onresolveSnapPoint.[chore]).Nothing on main imports paging from
projection.ts, andresolveSnapPointis not exported from the package (a test locks that).Relationship
This is the motion half of #8565, split out so it can land on main ahead of the bottom-sheet stack. #8565 keeps only the bottom sheet's adoption. The public
createDragToSnapPoints(#8658) can then callresolveSnapPointinstead of carrying its own copy of the same rules.Testing
tsc: clean.resolveSnapPoint.test.ts(flick, cancel, step limit, projection, tolerance, ties) andselectPage.test.ts.check:changesetsis clean, and ESLint is clean on the changed files.