Repository navigation
Conversation
… that finger until it lifts (spec:AST-094 FR14) A recognition that claimed its touches and was then cancelled gave them back to the browser unless another recognition owned them, so a finger resting there could pinch-zoom the page. A gesture that cancels itself on a finger's move, a long press dragged past its tolerance after it began, gave back a finger that was dragging: the host stopped cancelling its moves and the page scrolled the rest of the drag, a hand change FR14 forbids and UIKit never makes (its scroll view never takes a touch another recognizer had). A cancel during a finger's move now keeps that finger claimed until it lifts; a resting finger is given back as before, and a waiting recognition that fails still gives its claim back. The new arena case fails on the previous code: the dragging finger reads unclaimed right after the cancel.
… scroller under it is at the edge spec:AST-094 FR19 and DEC-18 (proposed in meta#8628): a scrollEdge pan may cancel its touch's moves from the first one, only while every scroller under the finger sits at its edge and the finger moves away from it, and only the moves the browser would rubber-band; it still begins at FR17's 10 px. The first case reads the first 2 px move cancelled at the edge and fails on this head; the others keep what must not change: room to scroll leaves every move to the browser, a first move toward the content too, a pan without scrollEdge cancels nothing before it begins, and a touch the browser took for a scroll stays the browser's when the list reaches its edge.
… cancelled at the edge spec:AST-094 FR19 and DEC-18 (proposed in meta#8628). The sheet's scroll-edge pan at the top of its content: every touchmove the page sees is cancelled, the first one included. WebKit sends that first move at 2 px, before the sheet's 10 px begin, and it reaches the page uncancelled on this head; Chromium sends none inside its own 15 px slop, so there it proves only that nothing scrolls.
…e away from an edge spec:AST-094 FR19 and DEC-18 (proposed in meta#8628). GestureRecognizer gains keepsTouch(touchId), false by default and reached by the host through recognizerInternals: whether it holds that finger before it begins. A scrollEdge pan does while it is possible, the touch is a finger the browser has not taken, its travel so far goes the pan's own way, and no scroller under the finger can scroll for it (the probe its begin already reads), so only moves the browser would rubber-band are held, never a real scroll. The host's blocking touchmove check cancels a move a participant holds, as it does a claimed one; the arena already holds that move when the check runs, since the host feeds it from its window-capture subscriber first. The pan still begins at 10 px; a mouse or a pen is never held. The README says so.
…straight, (7, 7) and (8, 8), and the screen-edge band The FR18–FR22 verification row's real-touch states, in the gesture browser spec on a bare page: - a finger moved 9 px straight or (7, 7) over a scroll view keeps its tap and scrolls nothing; one moved 10 px or (8, 8) is a tap or a scroll, never both; a tap never cancels a touchmove; - a screen-edge pan on a strip as wide as its band, landing 40 px from the edge, has not claimed at 14 px of travel and claims at 15; - a touch landing 41 px from the edge never begins the edge pan, and the horizontal scroll it starts is not blocked. The bare fixture exposes the tap and screen-edge pan recognizers.
…edge-zone strip decides at UIKit's 15 px The host's arena environment had no viewSize, so under the real host a screen-edge pan never saw its edge zone while a touch was down and decided at 10 px, while its attach-time touch-action (read through the default environment) had already claimed the axis (spec:AST-094 FR18). Co-authored-by: Christopher Chedeau <vjeux@users.noreply.github.com>
…fter layout rounding Lane 0's real-host browser spec found a strip authored at the 40.2 px edge region lays out at 40.203 px (layout snaps to 1/64 px), so the strict comparison read it as off the zone and the pan decided at 10 px instead of UIKit's 15 (spec:AST-094 FR18). The zone takes half a pixel of tolerance: the rounded strip is the zone, a view a pixel wider still is not. Failing-first test at 40.203 px: it claims pan-x and decides at 15. Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
…w) as a dependency # Conflicts: # packages/core/src/gestures/__tests__/GestureHost.test.ts
…now that a strip within half a pixel is the edge zone (meta#8061)
…, and under inputMode 'pointer-events' a claimed finger's touchmoves are cancelled - spec:AST-094 FR20 and DEC-8: a native scroll above an unclaimed finger counts as the browser taking it only once that finger has travelled the take distance (10 px) from where it landed. A scroll under a still finger (scroll anchoring, a carousel advancing, a transcript pinned to its end) is the page's own, and the touch and its click stay. (The same rule meta#8309 carried, moved down to the host that introduces it.) - spec:AST-094 FR19 and FR21 under inputMode 'pointer-events': a finger comes from its pointer events, so its touches carried no id of ours and a claim never cancelled them. The blocking touchmove listener now matches a touch to the nearest live pointer-fed finger on its target, so a claimed pan's moves are cancelled and the browser cannot scroll under it. Tests (failing first): 9 px and (7, 7) are not taken, 10 px and (8, 8) are, a still finger is not; the FR30 own-scroll case moves its finger first; under 'pointer-events' a pan's touchmoves pass before its claim and are cancelled after it.
…n: pan-y only on a strip within its band, left as is on a wider view
…es its recognizers, and the group keeps answering afterwards Under a fine pointer the host is not installed, so usesTouchEvents keeps its initial true, and a group press's own touch pointermove and pointerup went down the touch-pairing path and returned: the finger's moves and lift never reached the group, its contact stayed in the arena, and the group answered no press after it, a mouse's included. The group press now feeds its own events through the pointer path for every pointer type. Test (failing first): under a fine pointer a finger tap on a group recognizes, and a mouse tap afterwards does too.
… are all passive spec:AST-094 FR20: a watcher registers only passive listeners, but a watcher-only page still got the host's cancelable document touchend and its contextmenu and window click listeners. The document touchend is now passive until an enabled claimer (a recognizer that claims touches, or a consumer) is attached, and the contextmenu and click listeners are on only then, swapped as attachments change. The new test (a lone watcher leaves no cancelable document or window listener; a claiming pan brings the three) fails on the previous head.
…r watchers) into the host line The host keeps this PR's isRtlElement and its fingers held before a recognizer begins (a kept press, a scroll-edge pan), beside meta#8061's view size and meta#8309's pointer-events finger lookup. meta#8589's claimer check counts only participants active on this pointer (isActive, isConsumerActive), and a group press's click listener now stays while a claimer keeps it installed. The browser spec keeps the fine-pointer describe beside the FR18-FR22 real-touch states.
…'s passive-only watcher listeners) into the arena fixes The host-owner changeset keeps this PR's own listeners for the scroll lock and finger-scroll watch, and adds meta#8336's scroll-edge hold.
…inter reaches its recognizers) as a dependency
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 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.
There was a problem hiding this comment.
This is on hold for an engineering owner's decision: the branch adds a sentence to spec:AST-094 FR41 saying that a mouse or pen drag a recognizer claims suppresses text selection for that drag, which the current record does not state. An engineering owner can approve it by commenting /approve-spec <full head SHA> on this pull request (the author may do so as an engineering owner), or reject it or settle it in #8665. Whatever that decision, these still need changes:
- Land #8665 and #8671 first, or retarget onto them, so this diff is only the pan, about 1,500 of its 29,925 authored lines.
- Give the pan wheel and trackpad input, as
spec:AST-094FR43 requires, by landing #8676 beneath this pull request or carrying it here with a test. - Amend FR41's Verification row, which still fails any change to mouse selection.
- Stop a quick second Discard from skipping a card in both playing-card examples.
[Automated review]
…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.
meta#8665's review fixes: an armed press under a fine pointer brings the claiming listeners, keeps a kept press's finger and goes when the page hides; a press claimed at its own pointerdown keeps the page's selection; FR41 says what a claimed drag does with the selection; meta#8360's input-stream notes. The merge is clean.
… card From the review of this pull request: in both playing-card blocks, a second Discard pressed while the first throw still runs adds a second transitionend listener, and the throw's end then deals twice, skipping a card. The case fails in both blocks: the top card is Q♣ where K♥ should be next.
Both playing-card blocks now ignore a Discard, or a flick, while a throw is in flight, and deal once when it ends, so a quick second Discard no longer skips a card.
There was a problem hiding this comment.
This is on hold for an engineering owner's decision: the branch adds three sentences to spec:AST-094 FR41 and rewrites its Verification row, saying that a mouse or pen drag a recognizer claims takes no new text selection and clears an earlier one once it has moved, which the current record does not state. 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 it can be rejected or settled in #8665. Whatever that decision, these still need changes:
- Land #8665 and #8671 first, or retarget onto them, so this diff is only the pan, about 1,500 of its 30,252 authored lines.
- Give the pan wheel and trackpad input, which
spec:AST-094FR43 requires, by landing #8676 beneath this or carrying it here with a test. - In both playing-card examples, a flick that starts during a card's throw cancels the throw's transition, so the card stays invisible and Discard stops working; ignore
onPanwhile a throw runs, or deal ontransitioncanceltoo. - If FR41's sentences are approved, add Chromium and WebKit cases for a claimed mouse drag's selection and for the action-group click.
[Automated review]
| (`spec:AST-017` FR59; DEC-9). A mouse or pen drag that a recognizer claims | ||
| takes no new text selection for that drag only. A claim made after the | ||
| drag has moved clears the page's selection; a press claimed at its own | ||
| pointerdown, before it moves, keeps it. An unclaimed mouse drag keeps its | ||
| released behavior. |
There was a problem hiding this comment.
These sentences, and the Verification row change at line 663, add a requirement the current FR41 does not state: on main a mouse "otherwise keeps its released behavior", and spec:AST-017 FR59 sends a new fine-pointer feel to its own owner decision. They need an engineering owner's /approve-spec <full head SHA> on this pull request, or a decision in #8665. If approved, the claimed mouse drag and the action-group click still need Chromium and WebKit cases: the hold's document-wide selectstart refusal rests on a WebKit behavior that only jsdom tests cover today.
| if (state === 'began' || state === 'changed') { | ||
| // Follows the finger: translation leaves out the travel the drag | ||
| // needed to begin, so the card does not jump. | ||
| moveTo(translation.x, translation.y, 'none'); | ||
| } else if (state === 'ended' && Math.abs(velocity.x) > THROW_SPEED) { | ||
| discard(Math.sign(velocity.x)); |
There was a problem hiding this comment.
A drag that begins during the 250 ms throw calls moveTo(…, 'none'), and transition: none cancels the throw's transitions: they end in transitioncancel, never transitionend, so deal never runs. If that drag ends fast, discard returns at the isThrowing guard and nothing clears it: the card stays at opacity 0 and the Discard button does nothing until a slow drag happens to settle it. Ignoring onPan while a throw runs (or dealing on transitioncancel too) would fix it; PanPlayingCard.tsx has the same flow.
…nGesture, on the animator's own ref A drag and an animation share one element: usePanGesture reports the drag (translation, and the release velocity at its end) and useAnimator moves the card, both on the card's one ref, so the example no longer samples pointer events itself. Needs usePanGesture (meta#8655); lands right after it. The examples' test reports the React example's drags through the pan.
…nGesture, on the animator's own ref A drag and an animation share one element: usePanGesture reports the drag (translation, and the release velocity at its end) and useAnimator moves the card, both on the card's one ref, so the example no longer samples pointer events itself. Needs usePanGesture (meta#8655); lands right after it. The examples' test reports the React example's drags through the pan.
Depends on #8665. Review only this pull request's change: vjeux/astryx@c6c02fc...75d7704
Why
A product that wants a card to follow a finger, or a drawer pulled by its handle, has no public way to read a drag from Astryx: the gesture recognizers behind Astryx's own components are internal, so a product writes its own pointer handling and gets none of the rules Core follows (claiming at 10 pt, leaving taps and scrolls alone, the release velocity).
What led to it
The approved spec for public gesture hooks,
spec:AST-107, definescreatePanGestureandusePanGesture: a drag reported as values, with no gesture machinery handed out.Options and tradeoffs
spec:AST-107FR4 keeps recognizers internal, and products would depend on internals.{state, translation, velocity}(FR1).Why this choice
It follows the spec and reuses Core's own pan. A finger, a mouse and a pen all drive it (FR1, through #8665's every-pointer host).
axislimits it to one direction.translationstarts where the drag began, so an element following it does not jump.velocityat the end is the release velocity Core's own components use. The hook never callsonPanfrom an effect, so an unmount mid-drag ends silently (architecture:react-component-runtimeINV2). Two playing-card examples, one per layer, throw one card at a time.How to check it
usePanGesture(ref, {axis: 'x', onPan})to a card and log the events.began,changed…endedeach time, withtranslation.yalways 0.Evidence
destroymid-drag, no work at import, and the hook's effects calling nothing.