Repository navigation
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
…d on the restacked head (no emulation; a second finger now gives the swipe up, meta#8336), Android 15 emulator takes (Chrome 124, 540x1200)
…der the List reorder # Conflicts: # packages/core/src/utils/pressFeedback.ts
There was a problem hiding this comment.
This can't be approved yet: the new gesture host behaviors rest only on spec:AST-094, a draft this branch adds, and the second-finger rule, holdPageScroll and keepsTouch contradict its FR10 and FR19 as written. An engineering owner needs to decide that direction, either by amending the record and commenting /approve-spec <full head SHA> on this pull request (the author can do this as an engineering owner) or by naming the behaviors to remove. Whatever that decision, these need changes:
- Rebase once the dependency pull requests land, or retarget the base, so this diff carries the 13 host files instead of 30,301 changed lines.
- The adaptive compact test hard-codes
width < 744px, butspec:AST-012FR9 defines compact as width below the theme'smdpoint or height below 500, with no component-owned numbers. when.platformis outsidespec:AST-012FR2's closed condition list, and the press selection lock changes stable components under an[experimental]changeset, whichspec:AST-017FR22 does not allow.- The motion docs and changeset describe theme motion roles as driving touch motion, but no component reads them yet.
- Detaching a
keepsTouchpress tracker can leave its non-passivetouchmovelistener behind, and the description needs the writing-rubric results for the new record.
[Automated review]
|
|
||
| /** Both: presentations such as a sheet in place of a popover. */ | ||
| export const COMPACT_TOUCH_QUERY = | ||
| '(width < 744px) and (pointer: coarse), (height < 500px) and (pointer: coarse)'; |
There was a problem hiding this comment.
This hard-codes 744px. spec:AST-012 FR9 defines compact as width below the theme's effective md point (768 by default) or height below 500, and architecture:public-component-api INV17 rules out component-owned compact numbers, so coarse-pointer viewports 744–767 px wide get the popover here where FR9 gives the sheet.
| if (registration.consumer === consumer) { | ||
| registration.consumer = null; | ||
| } | ||
| this.cleanup(element, registration); |
There was a problem hiding this comment.
Detaching a keepsTouch: true consumer goes straight to cleanup, which returns early while tap, hover or watcher recognizers stay attached, so the non-passive touchmove listener refresh added for keepsTouch stays too. Calling this.refresh(element, registration) before cleanup removes it.
| ) { | ||
| const ids = recognizer.trackedTouches.map(touch => touch.id); | ||
| recognizer.cancel(); | ||
| this.arena.releaseClaims(ids); |
There was a problem hiding this comment.
releaseClaims clears the claim on these touches even when another claiming recognizer, set to recognize simultaneously with this pan, is already active on the same finger; claim() only runs at recognition, so that recognizer never gets the claim back. Releasing only touches no other active claiming recognizer tracks would avoid that.
| * is everything else. Pair `ios` with `pointer: 'coarse'` for a touch | ||
| * profile, such as touch motion tuned per platform. | ||
| */ | ||
| platform?: 'ios' | 'other'; |
There was a problem hiding this comment.
spec:AST-012 FR2 lists width, height, pointer, contrast and motion, and says future environment fields need an explicit addition to that closed vocabulary, so platform needs that amendment first.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [experimental] A held finger or pen no longer starts text selection or the iOS callout on a press-tracked control or cell, or on a recognizer created with the new `preventsTextSelection` option (`@astryxdesign/core/gestures/experimental`), as on UIKit controls. The gesture host turns them off for the contact and restores them when it ends, on a non-text input under the finger inside such an element too (WebKit's own style makes every input selectable), and only for recognizers that would take the touch; the document press controller (`usePressFeedback` rows) takes the same lock on any coarse pointer. Text fields keep their selection, a mouse keeps desktop selection, and other pans leave their content selectable. |
There was a problem hiding this comment.
This changeset also changes the document press controller behind Button, Item, Link and other stable components; spec:AST-017 FR22 keeps stable runtime behavior out of the [experimental] exception.
| content: [ | ||
| { | ||
| type: 'prose', | ||
| text: 'On a touch screen, sheets, alerts, menus, navigation and scrolling play the springs, durations and curves measured from iOS. Each value is a motion role named by its table path, such as `springs.sheetPresent` or `timing.curves.easeInOut`. A theme retunes a role through `motion.springs`, `motion.durations` or `motion.curves`, at its root or in an adaptation rule; a role it leaves alone keeps its measured default.', |
There was a problem hiding this comment.
No component reads the motion roles or --motion-* variables yet, so this sentence (and “Components read the variable when a motion starts” below) describes behavior builders will not see after retuning a role.
4fbb274 to
04e8048
Compare
…achments asks, and only then The host let the last attachment decide whether an element is an action group (useGestureRecognizer always passes the option), and never recomputed it at a detach: a plain attachment after the group's turned the group off, and a group's attachment released left a recognizer that never asked taking presses under a fine pointer. Fails until the host counts the attachments that ask.
…ents asks The host counts the attachments that pass actionGroup: true and keeps the element a group while one remains, recomputed at each detach. An attachment that passes false, as useGestureRecognizer does by default, no longer turns a group off, and a group's last asking attachment taking its leave ends the group.
… that content The presented hit test takes the document's elementFromPoint, which answers a shadow host for anything inside its open root, and compares with Node.contains, which stops at the boundary: a recognizer on a control inside a moving sheet's shadow root gets no finger, and the tap's click goes to the host. Fails until the hit test descends into open roots and compares along the flat tree.
Where a moving element is drawn away from its layout box, the host hit-tests what is drawn under the finger with the document's elementFromPoint, which answers a shadow host for anything inside its open root. The hit test now descends through each open root's own elementFromPoint and compares with the movers along the flat tree, so a recognizer on a control inside a moving sheet's shadow root gets its finger and the tap clicks that control.
…nt lets go The host's touch-action rules sit in an adopted sheet or a style element per document or shadow root, and were never removed, so a page whose last recognizer detached kept them, against spec:AST-094 FR24 (nothing touch-related remains after the last instance unmounts). Fails until a root's rules go with its last marked element.
…th its last marked element The touch-action writer records which elements it marked in each document or shadow root, and when the last one is unmarked (its last recognizer detached) it removes that root's adopted sheet or style element, so nothing touch-related remains after the last instance unmounts (spec:AST-094 FR24). A later mark writes the rules again; an element marked in another root first lets go of the old one.
… the inline style it reads - The motion registry, the host's engine gate and the README name the presented hit test as an engine workaround (spec:AST-013 FR6) with its review condition (re-measure on each iOS release; remove it once iOS WebKit hit-tests a compositor-moved element where it is drawn) and its regression fixture. - While a curve runs the animator holds the value it starts from inline, so presentation.ts and the README no longer say the inline style holds the end.
…core/internal/touch Core ships the internal entry @astryxdesign/core/internal/touch, which re-exports the gesture primitives for Astryx's own packages, and the Lab-import guard fails a Lab import of core outside core's exports (architecture:react-component-runtime INV13). The README and the barrel's header named internal workspace imports and said nothing here is exported; they now name that entry.
…ill needs the element, and the listeners that cancel The release note, the stream's header, the host's comment and the README said the stream is one passive listener per event type and that a document-capture stopPropagation no longer starves the recognizers. Such a stop never hid a listener on the same node, and some subscriptions cancel. They now say what changed: an app's window-capture stopPropagation, or a document-capture stopImmediatePropagation registered before core, no longer hides a touch; a recognizer that must claim still needs the touch to reach its element; the gesture host's touchend, click and contextmenu cancel while a claimer is attached and the page hold's touchmove while it holds; and the stream stays ahead of an app's window-capture listener only until a type's listener is re-added. The README's list of blocking touchmove listeners gains the claimed touch's target and the document while a claiming element moves.
…default it moves
The {stylexOptions} form of astryxStylex now passes StyleX
treeshakeCompensation: true unless the caller sets it. spec:AST-017 FR3
asks the release note to name the old and new default and the option
that restores the old one; it now does.
…er and for the two stops the input stream fixes - A still finger with sub-pixel jitter over a scroll view keeps its tap and scrolls nothing (spec:AST-094 FR18–FR22). - A tap is still recognized under an app's window-capture stopPropagation, and under a document-capture stopImmediatePropagation registered before core's: the recognizers hear touches on the window's capture phase. Chromium and WebKit pass.
…s once Where nothing that claims is attached, the host's touchend listener is passive and cannot cancel the engine's own click after a retargeted tap clicks what is drawn. The shared click guard swallows that late click whatever is attached; this case pins it, as asked on meta#8293.
meta#8360 now carries the review fixes of the foundation it holds. The two conflicts are this pull request's own files: - GestureHost.ts: meta#8360 counts the attachments that ask for an action group; here the fine-pointer group listener and the per-event recognizer set are gone (every pointer is served, FR43), so the merge keeps the count and drops those two. - GestureHost.test.ts: both sides added the same assertion to the second-finger case; the merge keeps this side's comment. meta#8360's action-group lifetime case counts a group's own pointerdown under a fine pointer, which this pull request replaces with one passive pointerdown on any element that uses a gesture (its own idle-listener case), so the merge leaves it out.
… attached, in this host's terms meta#8360's action-group lifetime case pins that an element stays a group while any attachment asks and stops with the last. Here every element that uses a gesture is served as a group under a fine pointer, so the same invariant reads as the element's one passive pointerdown: with two gestures attached, detaching either keeps it, and detaching the last removes it. It passes on this head.
meta#8360 carries this pull request's predecessors and the review fixes of the foundation it holds; this pull request's own diff is now its six commits (a second finger fails a one-finger pan wherever fingers come from, and the check reruns once that finger is delivered; a press's selection lock gets what it landed on; a pinch's second finger on another pan claims neither; the wider-band edge pan's real-touch proof; text). Two conflicts: - GestureHost.ts: the selection lock's target takes meta#8360's presented target, which is what the press landed on unless an element under it is moving, so it keeps this pull request's fix and meta#8293's correction. - .changeset/gesture-press-tracking.md: meta#8360's text, which names both writers of the press attribute.
There was a problem hiding this comment.
This needs changes before it can be approved. The gesture host still attaches only where the primary pointer is coarse, while architecture:react-component-runtime INV13 and spec:AST-094 FR19 and FR43 on main now say it attaches for every pointer, so please conform it here (as #8665 does) or keep it out until it does, and update the description, which says the press tracker already follows every pointer. The diff is also 27,428 authored lines, all but 214 of them the content of #8360; landing #8360 and #8589 on their own and rebasing would leave this pull request its own second-finger change.
ScreenEdgePanGestureRecognizeraccepts a mouse, trackpad or pen, which FR43 keeps off, andPressTrackeris a class where INV13 asks for acreate…factory.
[Automated review]
| private isActive(recognizer: GestureRecognizer): boolean { | ||
| return this.isCoarse || this.perEventRecognizers.has(recognizer); | ||
| } | ||
|
|
||
| /** | ||
| * Whether a press consumer takes input now (FR19; its per-event opt-in, | ||
| * an FR19 exception for press feedback pending in AST-094's amendment). | ||
| */ | ||
| private isConsumerActive(consumer: TouchConsumer): boolean { | ||
| return this.isCoarse || this.perEventConsumers.has(consumer); | ||
| } | ||
|
|
||
| /** Whether anything attached takes input now. */ | ||
| private needsInput(): boolean { | ||
| return ( | ||
| this.isCoarse || | ||
| this.perEventRecognizers.size > 0 || | ||
| this.perEventConsumers.size > 0 | ||
| ); | ||
| } |
There was a problem hiding this comment.
Under a fine pointer nothing here takes input except perEventPointer participants, so a pan, swipe or long press never runs for a mouse, trackpad or pen, and hover recognizers (line 1459) never hear a desktop mouse. CURRENT architecture:react-component-runtime INV13 says "The gesture host attaches for every pointer, with no touch-only query", and spec:AST-094 FR19 and FR43 say the same. Conforming here, as #8665 does, also means updating the header (lines 12–28), the development warning (lines 1357–1363) and the coarse-only tests.
| constructor(options: ScreenEdgePanGestureRecognizerOptions = {}) { | ||
| super({ | ||
| maximumNumberOfTouches: 1, | ||
| hysteresis: UIKIT_SCREEN_EDGE_PAN.hysteresis.value, | ||
| ...options, |
There was a problem hiding this comment.
There is no allowedPointerTypes default here, so wherever the host delivers a mouse or a pen (an iPad's Pencil today, and every pointer once the host follows FR43), a press-drag from the band begins the edge pan. spec:AST-094 FR43 keeps "the screen-edge pan under a mouse, a trackpad, or a pen" off; defaulting allowedPointerTypes to ['touch'], with a mouse and a pen case in the tests, would cover it.
| return tap.numberOfTapsRequired === 1 && tap.numberOfTouchesRequired === 1; | ||
| } | ||
|
|
||
| export class PressTracker implements TouchConsumer { |
There was a problem hiding this comment.
architecture:react-component-runtime INV13 keeps classes to the recognizer family ("its base, the arena, and the host") and builds every other module from create… factories. A createPressTracker factory (constructed in one place, usePressTracker.ts) would fit, or INV13 could list this as a known gap.
| // compiled (the TypeScript transform drops it as unused), the module is | ||
| // never loaded, and its constants never reach the CSS: a media-query key | ||
| // fails the build and a value ships as an undefined custom property. | ||
| treeshakeCompensation: true, |
There was a problem hiding this comment.
This flips the published {stylexOptions} form's default, which no current record covers (spec:AST-080 leaves the bundler integration out of scope). It is unrelated to the gesture host and would be easier to review and release in its own pull request.
…esture host's subscriptions The gesture host subscribes no click: the one cancelling click subscriber is the click guard's, added at a window's first arm and kept. The release note, the stream's header and the gestures README listed click with the host's touchend and contextmenu; they now name the guard's own.
… lift and the browser's menu, keeps a kept press's finger, and goes when the page hides From the review of this pull request, three cases fail on its head: - an armed press installed the page listeners without the claiming ones, so a claimed finger's touchend stayed passive and the browser's menu opened during a claimed long press (spec:AST-094 FR21); - a kept press got its blocking touchmove during an armed press, but its moves were cancelled only where the primary pointer is coarse, so a held stepper on a touchscreen laptop lost its finger (FR11, DEC-21); - a page hidden mid-press cancelled the press but kept the page's listeners until the next lift anywhere (FR24).
…lift and the browser's menu, keeps a kept press's finger, and goes when the page hides - A finger's or a pen's armed press now brings the claiming listeners (the cancelable touchend and the contextmenu canceller) with the page listeners, as wherever the host installs, so a claimed lift clicks nothing and a claimed hold opens no browser menu (spec:AST-094 FR21). A mouse's press needs neither and adds neither. - A kept press keeps its finger under any pointer: its target blocks and its moves are cancelled once its press is delivered, not only where the primary pointer is coarse (FR11, DEC-21). - A page hidden mid-press releases the armed press as a blur does, so the page's listeners go with the cancelled press (FR24).
…'s selection An action group's press is a long press with no minimum duration, so it claims at the press's pointerdown, before any movement. The drag selection hold then cleared the page's selection, so a plain click on a group's button left its Copy or Cut with nothing to act on. The case fails on this head: the selection is gone after the pointerdown.
…age's selection The drag selection hold cleared the page's selection when any mouse or pen contact was claimed, but an action group's press claims at its pointerdown, before it has moved or selected anything, so a plain click on a group's button left its Copy or Cut with nothing to act on. A claim before any movement now keeps the page's selection: the hold still refuses a new one, and puts the page's back over a selection that appears while it runs. A claim after movement clears what its drag selected, as before (spec:AST-094 FR41). The case before the new one now waits for its hold to end a task after the lift, so no hold outlives that test.
…t every pointer does - GestureHost's header, the gestures README and coarsePointer.ts no longer describe the coarse-only host: a finger or a pen takes the touch-down lock under any primary pointer, a kept press's finger is kept during a fine pointer's press too, and recognizers attach for every pointer while the page listeners last a press (spec:AST-094 FR19, FR24, FR41, FR43). - The fine-pointer selection case asserted the replaced rule and passed only because it read iOS WebKit's inline lock without iOS WebKit: it now asserts the lock a fine pointer's finger and pen take, and that a mouse takes none. - FR41's sentence and its verification row say that a press claimed at its own pointerdown, before it moves, keeps the page's selection, while a drag that moved before its claim loses what it selected.
…ion, in the present tense The sentence said a drag that moved before its claim loses the selection it started, but the hold clears the page's selection then, whatever made it; and an unclaimed mouse drag kept "today's" behavior, a phrase that ages. It now says a claim after movement clears the page's selection, a press claimed at its own pointerdown keeps it, and an unclaimed mouse drag keeps its released behavior.
This pull request now sits on meta#8665, so a stack carrying it and meta#8665's other dependants (meta#8676) needs no second GestureHost resolution. Two hunks in GestureHost.ts's pointerdown, both this pull request's: - the second-finger check where fingers come from pointer events stays, followed by meta#8665's rule that a right click, a pen's barrel button or a macOS ctrl-click starts no press; - the check that runs again once the new finger is delivered stays, followed by meta#8665's selection lock call, which no longer takes the window.
There was a problem hiding this comment.
This can't be approved yet: the head now rewrites spec:AST-094 FR41 so that a mouse or pen drag a gesture claims takes no text selection and clears the page's, which current FR41 (a mouse keeps its released behavior) does not allow, so the amendment needs an engineering owner's decision. An engineering owner records it by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner; otherwise say what should change in the amendment. Whatever that decision, these changes are still needed:
- Land #8360 and #8665 on their own and rebase, since all but 214 of the 28,602 authored lines here are their diff.
- Map a wheel or trackpad scroll to a pan and a ctrl-wheel to a pinch, as FR43 requires, or stop describing the host as FR43-conformant.
- Build
PressTrackeras acreate…factory, asarchitecture:react-component-runtimeINV13 requires outside the recognizer family, or record it as a known gap there. - Update the description, which still names closed #8315 and does not mention the FR41 amendment.
[Automated review]
| acts as a finger's; a mouse makes the context request by a right click, not | ||
| a long press (FR43), and otherwise keeps its released behavior | ||
| (`spec:AST-017` FR59; DEC-9). | ||
| (`spec:AST-017` FR59; DEC-9). A mouse or pen drag that a recognizer claims |
There was a problem hiding this comment.
This adds a requirement CURRENT FR41 does not state (a mouse "otherwise keeps its released behavior"), so it needs an engineering owner's decision before the code that implements it can be approved: an engineering owner records it with /approve-spec <full head SHA> on this pull request.
| } | ||
| }; | ||
| if (!keepsSelection) { | ||
| view?.getSelection()?.removeAllRanges(); |
There was a problem hiding this comment.
This empties whatever is selected when a moved drag is claimed. On an element that cannot be selected (a grab handle with user-select: none) the browser itself keeps the reader's selection through a mouse drag, so this is where the amendment changes behavior people meet.
| * @position Browser adapter of packages/core/src/gestures: turns real events | ||
| * into GestureArena input, and carries out what the arena decides | ||
| * | ||
| * FOR EVERY POINTER |
There was a problem hiding this comment.
FR43 also says a pan takes wheel and trackpad scrolling and a pinch takes a ctrl-wheel; the host listens for no wheel event, so a pan attached here ignores both. Map them, or keep this header and the changeset to what the host does.
| return tap.numberOfTapsRequired === 1 && tap.numberOfTouchesRequired === 1; | ||
| } | ||
|
|
||
| export class PressTracker implements TouchConsumer { |
There was a problem hiding this comment.
architecture:react-component-runtime INV13 keeps classes to the recognizer family (its base, the arena and the host) and builds every other module from create… factories; a createPressTracker factory, or a recorded gap in INV13, would conform.
|
Folded into #8309 under the maintainers' section plan; the commit credits this PR's authors. Closing; the branch stays. |
Depends on #8665. Review only this pull request's change: vjeux/astryx@c6c02fc...283e7b2
Why
A one-finger pan, such as a swipeable row, could still claim a pinch where fingers reach the page as pointer events, so two fingers on the row moved it instead of zooming the page (WCAG 1.4.4). The same happened when a pinch's second finger landed on another one-finger pan. A press inside an open shadow root locked selection on the shadow host instead of what it touched.
What led to it
The host as the one document touch owner, which this pull request introduced, now rides in #8360, and the host attaches for every pointer in #8665, both of which merge first in the maintainer's order. These follow-ups from its review remain.
Options and tradeoffs
Why this choice
The third keeps the page's pinch however fingers arrive: an unclaimed one-finger pan fails at a second finger, and the check reruns once that finger is delivered, so a pinch's second finger on another pan fails both (
spec:AST-094FR10). A press's selection lock takes what the press landed on, along the flat tree.How to check it
Evidence
spec:AST-094FR18–FR22, DEC-22).