Repository navigation
Conversation
…cebook#8446 (1ddea08) into the experimental touch layer
… into the selection hold
…an isEnabled sync) into the arena fixes This PR keeps usePressTracker in its own module, so meta#8309's deliver queue (a press tracker's callbacks wait for a microtask while a gesture hook's isEnabled sync runs) moves into usePressTracker.ts, and the sync depth both hooks read moves into useAttachment.ts (syncingGestures, isSyncingGestures). Gestures, utils and hooks 1432/1432; tsc clean.
…t serves under a fine pointer family:action-groups FR5: the host's attach takes actionGroup (AttachOptions.actionGroup), and the hook did not pass it through. In a document whose primary pointer is fine (a jsdom frame, with no matchMedia), a mouse press on an action of a non-group element reaches no recognizer; with actionGroup the hook attaches the element again and the press reaches it. Fails on this head (no such option).
UseGestureRecognizerOptions gains actionGroup (default false), passed to the host's attach and part of the attachment's key, so changing it attaches the element again. An adapter that drives an action group (an AlertDialog's action row) attaches its recognizer with it and adds no listener of its own. The README says so.
facebook#8056 landed squashed; main's motion README, animator and its tests equal facebook#8056's final head (2b264d3), so this PR's versions, built on that head, stand.
… listening uninstall() removed the scroll and scrollend listeners but kept fingerScrollers and endedWhileDown, so a finger scroll still running when the host stopped listening (a per-event press lifting under a fine pointer, or the pointer turning fine mid-momentum) never ended, and isFingerScroll answered true for later program scrolls. Both sets now clear with the listeners. The new test fails on the previous head. Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
…ning) into the arena fixes Here the finger-scroll watch lives in fingerScroll.ts, whose listen(false) already clears both sets, so the host keeps this PR's version; the new test reads the watch through fingerScroll's functions.
…nce, so a still finger's long press begins A browser with Touch Events sends them beside the pointer events for one finger. With inputMode 'pointer-events' the host still read both, so the finger was two touches and a long press (one touch required) never began; a moving finger's pan still worked, which hid it. The host now registers no Touch Event listener in that mode: the finger comes from its pointer events alone. Test (failing first): under 'pointer-events', a touch pointerdown plus its touchstart is one touch, and a long press held past its duration begins and ends.
spec:AST-094 FR20: a watcher registers only passive listeners, but the host gave every tracked finger's target a cancelable touchend. The host now counts, per target, the fingers a participant could claim (a recognizer that claims touches, or a press consumer): touchend is cancelable only while that count is above zero and passive otherwise, swapped like touchmove. The new test (a watcher's touch gets passive touchmove, touchend and touchcancel; a tap's touchend stays cancelable) fails on the previous head.
…ond route around ./internal/* Lab's per-module tsconfig paths (core's motion, gestures and touchPresentation) and the Sandbox/docsite resolver that served them opened a route around architecture:react-component-runtime INV13's ./internal/* subpath exports. Nothing in Lab imports those paths here; they're dropped, and the import guard now fails on any Lab import core doesn't export and on any Lab tsconfig alias for core. Co-authored-by: Christopher Chedeau <vjeux@users.noreply.github.com>
…) into the host line The watchTarget call keeps this PR's blocking test (a kept-press consumer under a coarse pointer blocks too) beside meta#8589's claiming flag.
…ports) as a dependency
… a mouse press At the document, an event from inside an open shadow root is retargeted to the shadow host, and so is a touch's target, so the host looked for participants from the shadow host and found none inside the root: the browser then sent its own mousedown and click. Both cases, a finger and a mouse, attach a tap to an element in the shadow root. Fail until the host resolves targets through the composed path.
…composed path Inside an open shadow root the document's listeners see the shadow host, in event.target and in a touch's target alike, so a recognizer on an element in the root never got its finger, and nothing claimed it. landedOn takes the composed path's first element for the event's own target and for a touch whose target is the event's (another finger keeps its own): a touch's start, a pointer's press and its pairing, the last touch a scroll reads, a cancelled pointer's touch and hover.
…he host resolves targets through the composed path) into meta#8320
…s finger: the host resolves targets through the composed path) into meta#8293 # Conflicts: # packages/core/src/gestures/GestureHost.ts
…wn start Under load the trace's round trip could begin well after the rise had started (38 % up at its first frame), so "150 ms into the rise" landed about 190 ms in, at 84 % of the rise, and the bar's carried-on turn passed the 85 % bound (92 %; once in two WebKit runs). The sampler now times the cancel from the rise animation's start; when a busy runner begins the trace too late for three frames before the cancel point, the bar is let back down and the activation tried again, up to three times.
… (spec:AST-094 FR43)
Gestures attach for every pointer: a mouse or pen drag across the content is
the content swipe, tracked 1:1 and released by the same rule as a finger's.
The edge swipe stays a finger's, the screen-edge pan's default. Both
recognizers lose their coarse-pointer gate; the docs and the mouse proof say
so ("under a mouse a drag across the content pops").
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
PR Analysis Report📚 Storybook Preview🧪 Sandbox PreviewNew ComponentsSearchField (@astryxdesign/lab) · View in Storybook
Modified ComponentsItem (@astryxdesign/core) · View in Storybook
TreeList (@astryxdesign/core) · View in Storybook
Component Size
Sizes are gzip. Import counts the JavaScript the import loads eagerly; CSS counts the package stylesheet rules referenced by the modules it can load from its own package, lazily loaded ones included. Bundle Size SummaryGzip figures measure each package's entry file, not the code an import pulls in.
Accessibility AuditStatus: 2 accessibility violation(s) found — 2 serious. Radio group pattern - 1 issue(s)
RadioList - 1 issue(s)
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 |
There was a problem hiding this comment.
Approval is on hold for an engineering owner's spec decision. Letting NavigationStack's content back swipe answer a mouse or pen contradicts spec:AST-094 FR19 and FR24 (and architecture:react-component-runtime INV13) on main, and the FR43/DEC-21 amendment it implements is approved only in the still-open #8646. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request (the author may do so as an engineering owner), or #8646 lands first; either way, the decision should cover the rightward mouse drag clearing text selection across a pushed screen and the deferred trackpad pan. Separately from that decision, please:
- Once #8204, #8665, #8666 and #8535 have landed, rebase so this pull request carries only its three-file change instead of 44,878 authored lines.
- Make
shouldFinishPopcancel a release that moves back (packages/lab/src/NavigationStack/navigationTransition.ts:121-130), asspec:AST-094FR37 requires. - Let a mouse drag start in the leading 40.2 px band in a browser tab (
NavigationStackTouch.ts:227-234), or say in the doc that it cannot.
[Automated review]
| export function shouldFinishPop( | ||
| progress: number, | ||
| velocityWidthsPerSecond: number, | ||
| ): boolean { | ||
| return ( | ||
| progress + | ||
| navigation.interactivePopFinishProjection * velocityWidthsPerSecond > | ||
| navigation.interactivePopFinishThreshold | ||
| ); | ||
| } |
There was a problem hiding this comment.
spec:AST-094 FR37 says a release whose velocity points back toward the start must cancel, whatever the travel so far. Here, progress 0.9 released at −0.5 widths/s gives 0.9 + 0.35 × (−0.5) = 0.725 > 0.5, so the pop commits. Returning false for a negative velocity, and adding a row such as [0.9, -0.5, false] to the table in navigationTransition.test.ts, would conform. Mouse and pen releases now go through this rule as well.
| shouldReceiveTouch: (_recognizer, touch) => | ||
| !( | ||
| browserOwnsEdgeRef.current && | ||
| startsInEdgeBand( | ||
| touch.startX, | ||
| stackRef.current?.direction() ?? 'ltr', | ||
| ) | ||
| ), |
There was a problem hiding this comment.
In a browser tab this refuses every pointer that starts in the leading 40.2 px, including a mouse or a pen, though the band only protects a finger's browser back swipe (FR22). The edge recognizer is off in a tab, so a mouse drag from the left of a full-width stack does nothing. The isInteractivePopEnabled doc promises a drag "anywhere in the content, under any pointer". Skipping the band for touch.pointerType === 'mouse', and adding the tab case to the mouse proof (which runs with standalone: true), would close the gap.
|
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 #8204, #8665, #8666 and #8535. Review only this PR's change.
User need
What led here: we are bringing Astryx's touch components to iOS quality on the mobile web, and
spec:AST-094FR43, now on main, has gestures work for every pointer. On a laptop or a desktop, NavigationStack's back swipe did nothing under a mouse or a pen: only the bar's Back returned.Problem and solution fit
A sideways drag across a pushed screen now goes back under a mouse or a pen as it does under a finger: tracked 1:1 and released by the same rule. The swipe from the screen edge stays a finger's, since FR43 keeps the screen-edge pan off under a mouse, a trackpad or a pen.
Current specification
spec:AST-094FR43 and DEC-21 (#8646). The edge band and its 10 px decision are FR18 and DEC-22.Public delta and caller burden
No API change.
isInteractivePopEnabled's doc now names every pointer for the content swipe and a finger for the edge swipe.Design and alternatives
Evidence
In Chromium with a real mouse on a desktop page, a drag across a pushed screen pops it; on the base, the same drag leaves it in place.
Scope
Testing
How to check it, in a desktop browser with a mouse, "Lab/NavigationStack › Pushed two levels":
What I ran and what it showed: "under a mouse a drag across the content pops, as a finger's does" fails on the base, where the coarse-pointer gate is kept, and passes here. The NavigationStack, SearchField and row-selection browser proofs passed in Chromium and WebKit; the unit tests, typecheck and lint passed.
Takes #8108's latest test fix at its next push, or when #8108 merges; it does not merge before that. Under a finger nothing changes; touch behavior was recorded on the iOS 26.2 simulator and the Android 15 emulator, and a real-device check is the release gate.