Repository navigation
feat(SegmentedControl): a mouse or a pen drags the selection as a finger does (spec:AST-094 FR43) - #8724
Closed
vjeux wants to merge 1992 commits into
Closed
feat(SegmentedControl): a mouse or a pen drags the selection as a finger does (spec:AST-094 FR43)#8724vjeux wants to merge 1992 commits into
vjeux wants to merge 1992 commits into
Conversation
Main's meta#8521 is its head 25b468c squashed, which this pull request already carries: every file it touches is byte-identical to 25b468c. In the two files that conflicted (the motion README and animator.ts), the resolution is this side's version, main's merged meta#8521 with this pull request's later work on top. Nothing of main is dropped.
The note's reword joined its next sentence onto one long line; it now wraps like the rest of the file. No code change.
…w press release change
…election hold change
cixzhang's meta#8589 keeps the host's touchend passive, and its contextmenu and click listeners off, until a claimer is attached (syncClaimingListeners). Ported onto this pull request's input stream with no change to what it does: install() subscribes the touchend passive, and syncClaimingListeners swaps it for a cancelling subscription and adds the contextmenu and click subscriptions while a claimer is attached. A group press brings its own click subscription only while the host has none, and the host's takes it over. Her watcher test reads those listeners on the window, where the input stream registers them; her new group test (a finger, then a mouse, under a fine pointer) sits before the detach test. The test file and the bare fixture import both her ScreenEdgePanGestureRecognizer and this layer's selection lock from utils.
Here the click guard (utils/clickGuard.ts) owns clicks, so cixzhang's meta#8589 syncClaimingListeners, ported at meta#8356, switches only the touchend's cancelability and the contextmenu subscription; the group press subscribes no click of its own. Her meta#8072 5c59af1 (a finger on an action group under a fine pointer reaches its recognizers) arms the click guard where it armed the swallower. Her watcher test reads the touchend and the contextmenu on the window, and no click listener: the guard's comes only once a gesture acts.
… the lift, under real touch input spec:AST-094 FR20's "follows the scroll to the lift" had only a jsdom test with a synthetic takeover; the FR18-FR22 verification row asks for real touch input. In Chromium with injected touches, a vertical drag on a list a watcher pan is attached to scrolls the list natively, no touchmove is cancelled, and the watcher goes from began to ended at the lift.
… at the edge stays the page's spec:AST-094 FR10: two fingers before a claim are the page's pinch. The scroll-edge hold (DEC-18) counted any finger, so with two fingers down it cancelled the pinch's moves before any drag was claimed. It now holds only while a single finger is down. The new two-finger test fails on the previous head. Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
… other native-scroll cases
… paint Button.spec.md FR15: main's text (meta#8613: the 200 ms release at tempo 1, scaled by the theme's tempo, spec:AST-108 FR4) with this pull request's own wording kept; the measured 250 ms release is likewise stated at tempo 1.
…busy first frame outran pressFeedback.ts removes data-astryx-press="fading" on a timer of the release's duration, started at the write. When the main thread is busy right after the lift, the release animation starts only at the first frame after the block, so the timer removes the release partway through it: on a list row after a 150 ms block, 0.736 -> 0.558 -> gone about 52 ms in. These fail on that code: - pressFeedback.test.ts: with a release animation the stylesheet started late (or still waiting for its first frame) when the clock runs out, the controller's release and another painter's (paintPress) stay fading until the animation finishes. Guards: a cancelled animation ends it, no animation ends it on the clock (reduced motion's instant release, a surface without a press layer), a descendant's, an endless or an earlier animation is not waited for, and a new press keeps its paint when the old release ends later. - ButtonPressRelease.interaction.browser.spec.ts, both engines: after 150 ms of script at the release, the button's layer keeps the release until its strength reaches 0.
…y first frame started it endAfterRelease replaces the clock that removed data-astryx-press="fading" at the release's duration after the write, for the controller's release and for every other painter's (paintPress). The clock now only looks: at the release's duration, a release animation the write started on the surface (one of its own CSS animations, on it or its ::before or ::after, finite, started at or after the write or still waiting for its first frame) is waited for until it finishes or is cancelled, so a busy main thread that starts the animation late ends it late too, at strength 0. With no such animation (reduced motion's instant release, a surface without a press layer, an engine without Web Animations) the release ends on the clock, as before. Any new write, or a new press, cancels the wait. A slot's pending clock is now a cancel function, so the release's wait and every timer clear the same way.
…the press-paint base
…nto the fine-pointer press paint
…ompare from it shows only this PR's change
There was a problem hiding this comment.
This pull request needs an engineering owner's spec decision before it can be approved, and two fixes are required either way. An engineering owner (the author is one) can approve the new segmented.selectionSpring row in spec:AST-094 FR4, the reworded component:Button FR15 release timing, and SegmentedControl's mouse and pen drag-to-select, whose component record is still draft, including its release-time selection and the spring its release plays, by commenting /approve-spec <full head SHA> on this pull request, or say which of them to drop or amend.
- The comparison with
maincarries 46,752 authored lines from #8127, #8665, #8682 and #8666, so please land those first and rebase until this pull request shows only its own 9-file change. - The new layout effect at
useSegmentedSelectionMotion.ts:1526-1528needs a comment naming the system it synchronizes, asarchitecture:react-component-runtimeINV3 requires. - The earlier request to retime or reword the fine-pointer settle is withdrawn:
mainnow lets a theme's tempo and character reachsprings.stateChange, so which motion that release plays is part of the decision above.
[Automated review]
vjeux
marked this pull request as draft
October 29, 2026 22:39
Merged
4 tasks done
vjeux
added a commit
to vjeux/astryx
that referenced
this pull request
Nov 2, 2026
…s with any pointer Under a finger the selection slides between segments on UISegmentedControl's measured spring (the internal role `segmented.selectionSpring`), for taps, arrow keys and programmatic changes alike; under Reduce Motion it cross-fades in place. A press shows the shared pressed overlay and nothing dims. Dragging the selected segment carries the selection with the pointer and commits once, at the lift, on the segment under it; a lift outside the control selects nothing. Under Reduce Motion a drag still follows the pointer, and only the release cross-fades. A mouse or a pen drags the selection the same way, every change slides on the theme's timing, a click included, and a press while focus is in the control no longer selects before the click. useMediaQuery, which SegmentedControl now reads, returns its server default where the window has no matchMedia, instead of throwing. The SegmentedControl record states the slide, the drag, the label color and the commit at the lift (FR9-FR13, PR2), carried over from meta#8596's owner-decided clauses as built. Both SegmentedControl records drop the implementation gaps this closes, and the touch presentation spec names the new motion role. The action-group tracker owns the tap path's click helpers, which SegmentedControl now calls. Combines meta#8094, meta#8127 and meta#8724. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
vjeux
added a commit
to vjeux/astryx
that referenced
this pull request
Nov 2, 2026
…s with any pointer Under a finger the selection slides between segments on UISegmentedControl's measured spring (the internal role `segmented.selectionSpring`), for taps, arrow keys and programmatic changes alike; under Reduce Motion it cross-fades in place. A press shows the shared pressed overlay and nothing dims. Dragging the selected segment carries the selection with the pointer and commits once, at the lift, on the segment under it; a lift outside the control selects nothing. Under Reduce Motion a drag still follows the pointer, and only the release cross-fades. A mouse or a pen drags the selection the same way, every change slides on the theme's timing, a click included, and a press while focus is in the control no longer selects before the click. useMediaQuery, which SegmentedControl now reads, returns its server default where the window has no matchMedia, instead of throwing. The SegmentedControl record states the slide, the drag, the label color and the commit at the lift (FR9-FR13, PR2), carried over from meta#8596's owner-decided clauses as built. Both SegmentedControl records drop the implementation gaps this closes, and the touch presentation spec names the new motion role. The action-group tracker owns the tap path's click helpers, which SegmentedControl now calls. Combines meta#8094, meta#8127 and meta#8724. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
vjeux
added a commit
to vjeux/astryx
that referenced
this pull request
Nov 3, 2026
…s with any pointer Under a finger the selection slides between segments on UISegmentedControl's measured spring (the internal role `segmented.selectionSpring`), for taps, arrow keys and programmatic changes alike; under Reduce Motion it cross-fades in place. A press shows the shared pressed overlay and nothing dims. Dragging the selected segment carries the selection with the pointer and commits once, at the lift, on the segment under it; a lift outside the control selects nothing. Under Reduce Motion a drag still follows the pointer, and only the release cross-fades. A mouse or a pen drags the selection the same way, every change slides on the theme's timing, a click included, and a press while focus is in the control no longer selects before the click. useMediaQuery, which SegmentedControl now reads, returns its server default where the window has no matchMedia, instead of throwing. The SegmentedControl record states the slide, the drag, the label color and the commit at the lift (FR9-FR13, PR2), carried over from meta#8596's owner-decided clauses as built. Both SegmentedControl records drop the implementation gaps this closes, and the touch presentation spec names the new motion role. The action-group tracker owns the tap path's click helpers, which SegmentedControl now calls. Combines meta#8094, meta#8127 and meta#8724. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
vjeux
added a commit
to vjeux/astryx
that referenced
this pull request
Nov 3, 2026
…s with any pointer Under a finger the selection slides between segments on UISegmentedControl's measured spring (the internal role `segmented.selectionSpring`), for taps, arrow keys and programmatic changes alike; under Reduce Motion it cross-fades in place. A press shows the shared pressed overlay and nothing dims. Dragging the selected segment carries the selection with the pointer and commits once, at the lift, on the segment under it; a lift outside the control selects nothing. Under Reduce Motion a drag still follows the pointer, and only the release cross-fades. A mouse or a pen drags the selection the same way, every change slides on the theme's timing, a click included, and a press while focus is in the control no longer selects before the click. useMediaQuery, which SegmentedControl now reads, returns its server default where the window has no matchMedia, instead of throwing. The SegmentedControl record states the slide, the drag, the label color and the commit at the lift (FR9-FR13, PR2), carried over from meta#8596's owner-decided clauses as built. Both SegmentedControl records drop the implementation gaps this closes, and the touch presentation spec names the new motion role. The action-group tracker owns the tap path's click helpers, which SegmentedControl now calls. Combines meta#8094, meta#8127 and meta#8724. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
vjeux
added a commit
to vjeux/astryx
that referenced
this pull request
Nov 3, 2026
…s with any pointer Under a finger the selection slides between segments on UISegmentedControl's measured spring (the internal role `segmented.selectionSpring`), for taps, arrow keys and programmatic changes alike; under Reduce Motion it cross-fades in place. A press shows the shared pressed overlay and nothing dims. Dragging the selected segment carries the selection with the pointer and commits once, at the lift, on the segment under it; a lift outside the control selects nothing. Under Reduce Motion a drag still follows the pointer, and only the release cross-fades. A mouse or a pen drags the selection the same way, every change slides on the theme's timing, a click included, and a press while focus is in the control no longer selects before the click. useMediaQuery, which SegmentedControl now reads, returns its server default where the window has no matchMedia, instead of throwing. The SegmentedControl record states the slide, the drag, the label color and the commit at the lift (FR9-FR13, PR2), carried over from meta#8596's owner-decided clauses as built. Both SegmentedControl records drop the implementation gaps this closes, and the touch presentation spec names the new motion role. The action-group tracker owns the tap path's click helpers, which SegmentedControl now calls. Combines meta#8094, meta#8127 and meta#8724. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
vjeux
added a commit
to vjeux/astryx
that referenced
this pull request
Nov 3, 2026
…s with any pointer Under a finger the selection slides between segments on UISegmentedControl's measured spring (the internal role `segmented.selectionSpring`), for taps, arrow keys and programmatic changes alike; under Reduce Motion it cross-fades in place. A press shows the shared pressed overlay and nothing dims. Dragging the selected segment carries the selection with the pointer and commits once, at the lift, on the segment under it; a lift outside the control selects nothing. Under Reduce Motion a drag still follows the pointer, and only the release cross-fades. A mouse or a pen drags the selection the same way, every change slides on the theme's timing, a click included, and a press while focus is in the control no longer selects before the click. useMediaQuery, which SegmentedControl now reads, returns its server default where the window has no matchMedia, instead of throwing. The SegmentedControl record states the slide, the drag, the label color and the commit at the lift (FR9-FR13, PR2), carried over from meta#8596's owner-decided clauses as built. Both SegmentedControl records drop the implementation gaps this closes, and the touch presentation spec names the new motion role. The action-group tracker owns the tap path's click helpers, which SegmentedControl now calls. Combines meta#8094, meta#8127 and meta#8724. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
vjeux
added a commit
to vjeux/astryx
that referenced
this pull request
Nov 3, 2026
…s with any pointer Under a finger the selection slides between segments on UISegmentedControl's measured spring (the internal role `segmented.selectionSpring`), for taps, arrow keys and programmatic changes alike; under Reduce Motion it cross-fades in place. A press shows the shared pressed overlay and nothing dims. Dragging the selected segment carries the selection with the pointer and commits once, at the lift, on the segment under it; a lift outside the control selects nothing. Under Reduce Motion a drag still follows the pointer, and only the release cross-fades. A mouse or a pen drags the selection the same way, every change slides on the theme's timing, a click included, and a press while focus is in the control no longer selects before the click. useMediaQuery, which SegmentedControl now reads, returns its server default where the window has no matchMedia, instead of throwing. The SegmentedControl record states the slide, the drag, the label color and the commit at the lift (FR9-FR13, PR2), carried over from meta#8596's owner-decided clauses as built. Both SegmentedControl records drop the implementation gaps this closes, and the touch presentation spec names the new motion role. The action-group tracker owns the tap path's click helpers, which SegmentedControl now calls. Combines meta#8094, meta#8127 and meta#8724. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
vjeux
added a commit
to vjeux/astryx
that referenced
this pull request
Nov 3, 2026
…s with any pointer Under a finger the selection slides between segments on UISegmentedControl's measured spring (the internal role `segmented.selectionSpring`), for taps, arrow keys and programmatic changes alike; under Reduce Motion it cross-fades in place. A press shows the shared pressed overlay and nothing dims. Dragging the selected segment carries the selection with the pointer and commits once, at the lift, on the segment under it; a lift outside the control selects nothing. Under Reduce Motion a drag still follows the pointer, and only the release cross-fades. A mouse or a pen drags the selection the same way, every change slides on the theme's timing, a click included, and a press while focus is in the control no longer selects before the click. useMediaQuery, which SegmentedControl now reads, returns its server default where the window has no matchMedia, instead of throwing. The SegmentedControl record states the slide, the drag, the label color and the commit at the lift (FR9-FR13, PR2), carried over from meta#8596's owner-decided clauses as built. Both SegmentedControl records drop the implementation gaps this closes, and the touch presentation spec names the new motion role. The action-group tracker owns the tap path's click helpers, which SegmentedControl now calls. Combines meta#8094, meta#8127 and meta#8724. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
cixzhang
added a commit
that referenced
this pull request
Nov 3, 2026
…s with any pointer (#9632) feat(SegmentedControl): the selection slides on every change and drags with any pointer Under a finger the selection slides between segments on UISegmentedControl's measured spring (the internal role `segmented.selectionSpring`), for taps, arrow keys and programmatic changes alike; under Reduce Motion it cross-fades in place. A press shows the shared pressed overlay and nothing dims. Dragging the selected segment carries the selection with the pointer and commits once, at the lift, on the segment under it; a lift outside the control selects nothing. Under Reduce Motion a drag still follows the pointer, and only the release cross-fades. A mouse or a pen drags the selection the same way, every change slides on the theme's timing, a click included, and a press while focus is in the control no longer selects before the click. useMediaQuery, which SegmentedControl now reads, returns its server default where the window has no matchMedia, instead of throwing. The SegmentedControl record states the slide, the drag, the label color and the commit at the lift (FR9-FR13, PR2), carried over from #8596's owner-decided clauses as built. Both SegmentedControl records drop the implementation gaps this closes, and the touch presentation spec names the new motion role. The action-group tracker owns the tap path's click helpers, which SegmentedControl now calls. Combines #8094, #8127 and #8724. Co-authored-by: Cindy Zhang <cindyxz@meta.com>
Contributor
Author
|
Closing: superseded by #9632, which shipped this behavior. |
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #8127, #8665, #8682, #8666. Review only this PR's change: vjeux/astryx@e2d6971...e7243b2
User need
Astryx's touch work now covers every pointer:
spec:AST-094FR43 (current since #8646) puts press tracking on a mouse and a pen too, and its exceptions don't include this drag. Today, with a mouse or a pen, SegmentedControl's selection can't be dragged, and once focus is in the control a press on another segment selects it as the button goes down.Problem and solution fit
onChangeonce); a drag that starts elsewhere selects where it lifts.Current specification
spec:AST-094FR43 and its non-goals; the mouse press highlight comes with #8682.component:SegmentedControlFR7–FR10 (#8596, a draft) still say "under a finger"; widening them is the record owner's to do.Public delta and caller burden
No API or theme change. With focus already in the control, a mouse click now selects at release rather than at pointerdown, which drag-to-select under FR43 requires. Keyboard and screen readers are unchanged.
Design and alternatives
onChangetwice).Evidence
Scope
Testing
Automated, written to fail first and now passing (a mouse or pen drag selected nothing; a press after a click selected at pointerdown):
pnpm test:interaction SegmentedControl.interactiondrags with a real mouse (Chromium, WebKit) and pen (Chromium); unit tests cover the drag, the settle and Reduce Motion. Known red:touch-support-bundle"is absent from the root entry" (#8671). Apr-rtlred is main's newer RTL test, not this change.