Repository navigation
Conversation
… useAnimator's browser proofs
…fine pointer); the changeset says the same animator.test.ts keeps this layer's header (no compositor browser spec in this layer). The changeset now says decay sends a spring back from past an edge through the Reduce Motion policy: a cross-fade on a coarse pointer, at once elsewhere.
…side a scroll view The press profiles' README said a cell's highlight always waits the content-touch delay. PressTracker waits it inside a scroll view and lights at once outside one (spec:AST-094 FR11), as its own header says; the README now says so too.
…angeset wording) as a dependency
What one call becomes under Reduce Motion (an opacity-only cross-fade whose travel lands while the element is transparent, or everything at once; a dip on a borrowed stylesheet opacity) moves from createAnimator's closure to reducedMotionPlan.ts, a pure planner that reads the animator through a view and returns the plan; the animator turns the plan into its segments and runs the landings. No behavior change: the animator's tests pass unchanged, and the planner has its own. Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
…ed pull requests This pull request keeps the animator's Reduce Motion plan (now its own planner module), the browser-set start of a call from rest, promotion, atRest, rebase and animatorFor. Two features it carried move up the stack, each with its own tests and changeset: - play(curves), baking any closed-form curve like a spring; - driving a pseudo-element (a dialog's ::backdrop). The changeset is split the same way. Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
play(curves) bakes any curve given as value and velocity functions of time (a spring chasing a moving target from solveSpringTowardRamp, a fitted UIKit curve) exactly like a spring: presentation values, velocities, stop, finished and the shared clock work the same, a later animate starts from the curve's value and velocity, and under Reduce Motion a curve that moves is travel the policy replaces. Tests and a changeset of its own. Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
createAnimator(element, {pseudoElement: '::backdrop'}) animates a
pseudo-element: with no inline style, a static filling animation holds
its value after a curve ends and after set; a custom property on its host
promotes it; a Reduce Motion dip borrows and hands back the
pseudo-element's own opacity, and its holds follow a node replacement.
animatorFor keeps one animator per element and pseudo-element. Tests and
a changeset of its own.
Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
…ot the page's frames around it "a harness read inside a trace is not the page's long task…" also asserted that no long task started anywhere in the read's window. On CI's runner each frame of the fixture's 1,200 running rows took 51-54 ms, the page's own long tasks, and failed it; at 4x CPU locally they take 126-220 ms. The read itself was set apart correctly. The test now asserts the read's own classification, and removes the rows before the timer, click and direct-call windows, so each holds only its own work. It passes at 4x CPU, where the previous assertion failed.
…hed curve waitForAnimation's finished branch read every recorded animation, so a scroll- or view-timeline animation that reached its end (a scroller scrolled to its end) came back as finished with a NaN duration and interval. The recorded set now keeps document-timeline animations only. The scroll-timeline self-test scrolls its scroller to the end first; it returned the NaN entry before (Chromium, 3x repeat pass). The README, JSDoc and file header name watchAnimations where they list what moves the wait window and what the file exports. Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
…rves), pseudo-element support) as a dependency # Conflicts: # packages/core/src/motion/README.md # packages/core/src/motion/animator.ts
… motion-role animator # Conflicts: # packages/core/src/motion/animator.ts
The travel list moved to the planner with the Reduce Motion plan; nothing in the animator reads it any more (no-unused-vars).
…deselect clock; the animator changeset drops its no-component clause The press profiles' README gave a control UIButton's 0.25 s release. PRESS_PROFILES releases every surface on the list's deselect clock until a control runs its own release, as its comment says; the README now says so. Its test list still credited VelocityTracker's tests with checking each strategy against the true velocity, though one filter remains: it now names what they check. The animator's changeset said no component uses it, which goes false in the release that carries the components that do.
…removed at its end is still reported - Arming the recorder now records first and stamps strictly after the newest entry: a watch taken right after a step that changed style no longer reports that step's transition (created by the arm's own style read) as started after it. - Each entry keeps its target's ancestors as recorded, so an exit whose element the page removes at its end is still reported for the container it left, instead of a wait rejecting with nothing recorded. A self-test for each; both failed before (Chromium, 3x repeat pass). Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
solveSpringTowardRamp went with play(curves)'s tests, and a coast test destructured an element it never reads (no-unused-vars).
play(curves)'s tests use solveSpringTowardRamp, so this layer keeps its import.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
PR Analysis Report
View Storybook for this PR · View Sandbox for this PR Generated by PR Enrichment workflow | View current CI run |
There was a problem hiding this comment.
This pull request is on hold for spec decisions: making a finger on a fine-pointer touchscreen follow the menus' finger rules contradicts the current spec:AST-094 FR19, its menu-scope paragraph and FR24, and module:DropdownMenu/useMenuPress FR1, FR2, FR7 and FR9. The FR43 and DEC-21 amendment it implements is still in open #8646, so that amendment, plus a decision on OQ10 for the stacked in-place sub-menu that FR34 currently rules out, has to land on main first. The stacked record changes (DropdownMenu's FR20 and new FR23, DropdownMenuSubMenu's Sheet state, and architecture:layer-runtime's new INV13) need an engineering owner to approve them by commenting /approve-spec 1784c3023b2bc5a3c73e754a6ff0f8f34a4fc284 on this pull request; the author may do this as an engineering owner. Independent of those decisions, please address the following:
- Rebase once the twelve stacked pull requests land, so this PR carries only its own change rather than 26,203 authored lines.
- Sub-menu flyouts still take
touchModesas their touch model (DropdownMenu.tsx:1908), so on a fine-pointer touchscreen a cancelled touch leaves a flyout row lit while the root menu clears it. scripts/touch-support-bundle.test.mjsfails at this head becauseuseMenuPressnow reachesgestures/nativeScroll.ts; carry #8671's change or land it first..changeset/menu-press-public-default.md,.changeset/menu-press-handoff.mdand.changeset/dropdownmenu-submenu-in-place.mddescribe behavior this head no longer has, so they need rewording.
[Automated review]
| host: surfaceHost, | ||
| ownerId: menuId, | ||
| menuMaxHeight: resolvedMaxHeight ?? undefined, | ||
| touchModel: touchModes ? 'uikit' : 'pointer', |
There was a problem hiding this comment.
On a fine primary pointer touchModes is false while the root's press runs pressTouchModel ('uikit'), so every sub-menu flyout (DropdownMenuSubMenu.tsx:1085) keeps the pointer model: a cancelled touch leaves its row lit there but clears it in the root. Deriving this from pressTouchModel, with the memo dependencies to match, keeps one model per menu; a fine-pointer flyout cancel case beside #8508's would pin it.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [feature] `useMenuPress` with no `longPressDelayMs` now opens the menu as a finger presses on a touch screen too (a coarse primary pointer), with DropdownMenu's timing (`spec:AST-094` FR25, FR29); pass `longPressDelayMs: 500` to keep the previous 500 ms hold. Where the primary pointer is fine it keeps the 500 ms hold. |
There was a problem hiding this comment.
At this head a fine primary pointer no longer keeps the 500 ms hold: useMenuPress.ts:409–410 runs the 'uikit' model whenever longPressDelayMs is unset, as .changeset/menus-every-pointer.md says. Shipped together, these two notes contradict each other.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [feature] Under a finger, the finger that opened a DropdownMenu or MoreMenu now picks a row only after it has moved 10 px (`spec:AST-094` FR29), so a menu that appears under a resting finger never picks the row beneath it, however long the finger rests. To keep the previous behavior, where that finger's lift picked once the press had lasted 300 ms, pass `presentation="popover"`. A mouse and the public `useMenuPress` hook are unchanged. |
There was a problem hiding this comment.
The public useMenuPress hook now runs the same 'uikit' model, handoff included, whenever longPressDelayMs is unset, so "the public useMenuPress hook [is] unchanged" no longer holds at this head.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [feature] On a phone, a DropdownMenu sub-menu opens in place as UIMenu's does on iOS 26. The sub-menu's card grows down from the row it opened from, and the menu's box grows with it from the edge it is anchored by. The parent's rows stay under the card, faded to half and scaled to 97%. The sub-menu's header keeps the row's label, now semibold, with its chevron turned down; it is still named "Back to <parent>". Going back reverses it. The box plays UIKit's sub-menu springs. Under Reduce Motion the views cross-fade. |
There was a problem hiding this comment.
The touch header row's accessible name is now <label>, Back to <parent> (DropdownMenuSubMenu.tsx:1148–1155, @astryx.dropdownMenu.subMenuHeader; the test expects "Move to, Back to Actions"), not Back to <parent>.
|
Posted by Cindy's agent on her behalf: closed and reopened only to restart this PR's CI on the current workflow after tonight's queue-wide cancel; nothing in the PR changed. |
Depends on #8220, #8508, #8531. Review only this PR's change: vjeux/astryx@da77fee...1784c30
User need
On a touchscreen laptop (a fine primary pointer), a finger in a DropdownMenu or MoreMenu still gets the released finger behavior: its press on the trigger opens nothing, the finger that opened the menu picks with no handoff, and a cancelled touch leaves its row lit.
spec:AST-094FR43 (approved in #8646) has a finger follow the menus' finger rules on every device.Problem and solution fit
presentation="popover": the trigger opens as the finger presses (FR29), the opening finger picks after the 10 px handoff, and a cancelled touch or a second finger clears the lit row (FR28).useMenuPresswith nolongPressDelayMsfollows FR29 on every device: the coarse-pointer read goes.module:DropdownMenu/useMenuPressFR1–FR3). The touch modes stay the device's: the touch styling and the grow and shrink (FR31) are unchanged.Current specification
spec:AST-094FR26–FR30, FR43 and DEC-21 (approved at c46ace4);module:DropdownMenu/useMenuPressFR1, FR2, FR9 (current, as amended there).Public delta and caller burden
No new prop or option. On a fine primary pointer, a finger now opens the menu as it presses;
presentation="popover"keeps the released finger behavior, andlongPressDelayMson the hook keeps its wait. The changeset and the docs of DropdownMenu, MoreMenu anduseMenuPresssay so.Design and alternatives
The press model is chosen per gesture by the pointer type the model already reads, so nothing reads the primary pointer for behavior. Rejected: changing what the touch modes mean, which also drive the touch styling and the 44 px target, both of which FR43 keeps coarse-only.
Scope
Testing
pnpm vitest runhooks, DropdownMenu, AlertDialog, ContextMenu, MoreMenu, Selector, MultiSelector, Layer, utils, gestures, motion and BottomSheet at this head: 3711 passed.pnpm test:interactionDropdownMenuTouchPress, DropdownMenuTouchHandoff and DropdownMenuTouchMotion: Chromium 19/19, WebKit (Playwright image) 15 + 4 skipped.pnpm build,pnpm typecheckandCI=true pnpm lintwith no error; CLI isolation and golden reads 50/50;check:knowledge,check:changesets.scripts/touch-support-bundle.test.mjs"is absent from the root entry" fails here: DropdownMenu and the publicuseMenuPressnow reach the gesture module (gestures/nativeScroll.ts, for FR29's scroll-view wait). test(core): a touch component's import carries its gesture code, and nothing else does #8671 makes that check per component and lists them.Stack (merge in this order): #8076, #8298 and #8446 (any order) → #8627 → #8078 → #8508 and #8531 and #8186 → #8080 → #8089; #7839 → #8220; then this PR.