Repository navigation
Conversation
meta#8270's selection lock writes no style where iOS WebKit does not run: it cancels selectstart while the press holds. The press tracker's case reads that, in jsdom, as the lock in force during the press and gone after.
…er reports no highlight change The press ends inside the release guard, so the control hears no onHighlightChange(false): the handler that disables it clears its own highlight, as the review asks.
…into the gesture hook fixes
… in the edge zone spec:AST-094 FR18's screen-edge refinement. A pan from one edge derives a touch-action that takes the axis across that edge (pan-x for a side edge, pan-y for top or bottom) only when its view is the edge zone: a thin element along the edge, no wider than `edgeRegion` across it. There the browser has nothing to scroll, so the pan still decides at its 15 px hysteresis without losing the touch. On any wider view it leaves the browser every touch behavior, so the author's scrolling there is untouched. The view's size comes from a new optional `viewSize` on the gesture environment (the default measures the element). Pans from several edges are unchanged. The edge-zone tests fail on the previous head. The pan and swipe tests also read their own table groups (UIKIT_PAN, UIKIT_VELOCITY, UIKIT_SWIPE), which completes the per-group imports in this change.
…-action mark, per-group UIKit imports) onto cixzhang's daf723b
Where the primary pointer is fine, one attached perEventPointer press tracker made the host install every document touch and pointer listener and the window click listener for as long as it stayed attached. The tracker's element now listens for a finger's press itself (passive, on the element only); the host adds its document and window listeners at that press's start, hands it the press, and removes them once its contacts end, so they last only the press (spec:AST-094 FR19's exception for a press's feedback). A mouse press on the element adds nothing. Test: an idle attached tracker leaves no document or window touch, pointer or click listener; a finger's press paints and adds them; its lift removes them. It fails on the previous head. Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
… an entrance catching an exit starts where it is
- decay accepts touchRelease and hands it to the spring back's transition,
so a spring back a touch drag's release starts cross-fades on a fine
pointer too (design:reduced-motion DR2); the README and coast.ts say so.
- A move taken back mid-dip (animate({x: 100}) then animate({x: 0})) left
an emptied landing that kept the dip going down to 0 and back; a dip
left with nothing to land now fades straight back in from where it is.
- An entrance by role caught mid-exit opened its fade at 0 and headed for
the exit's 0; it now starts from the opacity on screen and fades back to
what the exit would have restored, the exit's landing taken over as a
named opacity's is.
Three tests fail on the previous head.
…to play(curves) decay's doc keeps play(curves)'s 'play it with play or animate' beside the new touchRelease sentence.
…to pseudo-element support
… per-event press tracker's listener lifetime Where the pointer is fine, a per-event press tracker's element gets passive touchstart and pointerdown listeners and no style, and the host's document and window listeners come only for a finger's press on it; a per-event recognizer keeps its listeners and style. The host's header now names the carve-out, and AttachOptions and the gestures README stop saying the host styles the tracker's element there. Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
…cross-fade for a spring back With decay taking touchRelease, a spring back from past an edge also cross-fades on a fine pointer when a touch drag's release starts it (design:reduced-motion DR2); the header and the changeset now say so, as decay's docs, the README and coast.ts do.
…eset) into play(curves)
…eset) into pseudo-element support
…Hold`) `architecture:layer-runtime` INV14 already uses "presentation hold" for keeping a layer's chosen presentation from open until the close settles, a latch. This module does something else: it drops input while a presentation animates. Renamed as cixzhang asked on this pull request: `Layer/inputHold.ts` exports `holdInput`, `useInputHold`, `isInputShield` and `isInHeldLayer`, its types are `InputHoldScope`, `InputHoldEnd` and `InputHoldOptions`, and its tests, fixture, browser spec and changeset take the same name, with "input hold" in the docs and comments. Behavior is unchanged.
# Conflicts: # internal/interaction-harness/README.md # internal/interaction-harness/package.json # internal/interaction-harness/src/harness.interaction.browser.spec.ts # internal/interaction-harness/src/story.ts
…the pointer is fine Beside an attached per-event recognizer or press tracker, a watchFingerScrolls watch kept every document touch and pointer listener and the window click listener installed for its whole life where the primary pointer is fine. A watch now counts only under a coarse pointer; under a fine one the page listeners still come only for a finger's press on a per-event press tracker (spec:AST-094 FR24), and the watch hears that press. The idle fine-pointer test now adds a finger-scroll watch and still finds no page listener; it fails on the previous head. Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
There was a problem hiding this comment.
Changes requested: the touch hold works on phones, but it also runs on desktops and touchscreen laptops, where spec:AST-094 FR19 and FR24 keep touch work off. Please gate holdEntranceInput on a coarse primary pointer (keeping the per-event check inside that gate), let a mouse press that comes before any movement through, and get the root-entry case of scripts/touch-support-bundle.test.mjs passing, for example by landing #8671 first. Then rebase onto #8342 and move the @astryxdesign/build StyleX changes to their own pull request, so this one carries only the entrance hold.
[Automated review]
| * pinch still zooms the page. A mouse keeps today's behavior: the hold decides | ||
| * by each event's pointer, never by the primary pointer, so a mouse or a | ||
| * trackpad keeps every click on a touch screen, and a finger is held on a | ||
| * laptop whose primary pointer is a mouse. |
There was a problem hiding this comment.
This makes the hold run where the primary pointer is fine: spec:AST-094 FR19 and FR24 (and architecture:react-component-runtime INV13) keep touch listeners and the finger hold off there. Returning null unless isCoarsePointer(layer.ownerDocument.defaultView), with the per-event check kept inside that gate, would keep a mouse on a touch screen unheld and leave desktops untouched.
There was a problem hiding this comment.
On the four points:
- Every pointer: the entrance hold under every pointer follows
spec:AST-094FR43 as amended in spec(AST-094, AST-057): gestures work on the web for every pointer (FR43) #8646 (approved); it governs once spec(AST-094, AST-057): gestures work on the web for every pointer (FR43) #8646 merges. Under it the hold still decides by each event's pointer, so a mouse or a trackpad keeps its clicks and only a finger's or a pen's press is held. - A mouse press before any movement: fixed in the offer at cfb40df, carried here at e048800: a press is hit-tested before its pointerdown fires, so the shield can't step aside mid-press; it now starts down whenever a mouse is the page's latest pointer, which the hold remembers from the page's first hold on. In the first layer a page holds, a still mouse press still meets the shield (the offer states why).
- The root entry of
scripts/touch-support-bundle.test.mjs: test(core): a touch component's import carries its gesture code, and nothing else does #8671 replaces that whole-entry case with a per-component one that lists Dialog and Drawer; it passes on this branch, as Testing notes. - The base: a pull request can't target feat(Layer): one input hold, first used by Dialog, Drawer and MobileNav (spec:AST-094 FR35) #8342's fork branch, so the body's first line links this change's own diff (12 files). The
@astryxdesign/buildchanges come from the stack under feat(Layer): one input hold, first used by Dialog, Drawer and MobileNav (spec:AST-094 FR35) #8342, not from this branch; feat(Layer): one input hold, first used by Dialog, Drawer and MobileNav (spec:AST-094 FR35) #8342 merges first.
There was a problem hiding this comment.
Since the offer's FR24 change (b16ad3c), nothing listens while no layer is held, so the hold can't know a pointer used before it began: a mouse press made before the mouse moves during the hold still meets the shield and reaches nothing, in every layer.
| if (view == null) { | ||
| return null; | ||
| } | ||
| latestPointer.delete(doc); |
There was a problem hiding this comment.
Forgetting the last pointer here means a mouse press made before any movement meets the shield (its pointerdown and mousedown are swallowed), against FR35's “A mouse keeps today's behavior”. Keeping the document's latest pointer type across the hold's start would let a layer a mouse opened pass mouse hits from the start.
…t press (spec:AST-094 FR35) Red on the offer as it stands: a hold forgets the page's latest pointer at its start, so its shield starts taking hits, and a mouse press made before any movement meets it (its press and click swallowed), against FR35's "A mouse keeps today's behavior". - A presentation's shield starts letting a mouse through when a mouse is the page's latest pointer, and the gesture host's gate passes it from the start; a finger as the latest pointer starts it taking hits, and a dismissal's shield takes every hit whatever the pointer. - In the browser: once the page has held input, a layer that opens under a resting mouse takes the mouse's press before any movement, as with no hold, while a finger's tap on it is still held.
… the start (spec:AST-094 FR35) The hold forgot the page's latest pointer at its start, so its shield started taking hits and a mouse press made before any movement met it. From a document's first hold on, a passive watch of its pointers keeps the latest one, between holds too. A presentation's shield starts taking no hits when that pointer is a mouse, and the gesture host's gate passes the mouse from the start; a finger's press makes the shield take hits again, captured to it if it landed past it, as before. A dismissal's shield still takes every hit. What stays: nothing watches a document's pointers before its first hold, so in the first layer a page holds, a mouse press made before any movement still meets the shield. Reading the opening click instead fails in WebKit, which reports a tap's click as a mouse's, and starting the shield down would let a finger's first contact reach what lies beneath (WebKit shows the control under it as :active).
…d826) into the Dialog and Drawer entrance hold
… action during the entrance (spec:AST-094 FR35) After a first presentation, a key opens the dialog again with the mouse resting where its action appears; a press there with no movement first lands on the action, as with no hold. On the previous offer it met the shield.
There was a problem hiding this comment.
Approval needs an engineering owner's decision: on main, spec:AST-094 FR13, FR19 and FR24 and architecture:react-component-runtime INV13 keep touch handling off where the primary pointer is fine, but this entrance hold also holds a finger and adds touch listeners there, relying on FR43 from the unmerged #8646. An engineering owner can settle that by landing #8646 or by commenting /approve-spec <full head SHA> on this pull request, which the author may do as an engineering owner; otherwise the hold should apply only where the primary pointer is coarse. Independently of that decision, these need changes:
- Rebase onto the landed #8342 stack so the diff is this change's own 12 files; 24,964 of its 26,175 authored lines come from that stack.
- CI's node tests fail
scripts/touch-support-bundle.test.mjs"is absent from the root entry", because Dialog now imports the gesture host; #8671 or an equivalent test change needs to land first. - In the first Dialog or Drawer a page opens, a mouse press made without moving meets the input shield during the entrance instead of the control under the cursor (
Layer/inputHold.ts:58-60), so the changeset's and docs' "a mouse or a trackpad is unchanged" does not hold yet.
[Automated review]
| * stays the hold's and a light-dismissing layer stays open). Nothing watches | ||
| * a document's pointers before its first hold, so in the first layer a page | ||
| * holds, a mouse press made before any movement still meets the shield. A |
There was a problem hiding this comment.
In the first layer a page holds, a still mouse press meets the shield, so in the first Dialog or Drawer a page opens, a mouse or trackpad press made without moving does not reach the control under the cursor. spec:AST-094 FR35 says "A mouse keeps today's behavior", and the changeset and both doc descriptions say a mouse is unchanged. Starting this pointer watch before the first hold (for example when Dialog or Drawer mounts) and adding a first-presentation still-mouse case to DialogTouchEntrance.interaction.browser.spec.ts would close the gap.
| import {layerStructureReset} from '../Layer/layerStructureReset.stylex'; | ||
| import {LayerContentBoundary} from '../Layer/layerScopedContext'; | ||
| import {LayerDepthProvider} from '../Layer/LayerDepthContext'; | ||
| import {holdEntranceInput} from '../Layer/holdEntranceInput'; |
There was a problem hiding this comment.
Through inputHold.ts, this import brings gestures/GestureHost and eleven other gesture modules into Core's root entry (56,018 minified bytes), so main's scripts/touch-support-bundle.test.mjs fails "is absent from the root entry" on this head (CI test-node 2/4). #8671's per-component boundary, or another owner-approved change to that test, has to land first.
Red on the offer as it stands, against the merged FR24: "the number of document- or window-level touch listeners MUST NOT grow with the number of component instances, ... and nothing touch-related remains after the last instance unmounts" (with architecture:react-component-runtime INV5: a document singleton persists across consumer gaps only where current authority names that lifetime). - A hold adds its window listeners only while a layer is held, once per document however many layers are held, and removes every one after the last hold ends (two held layers add 28 today, and the pointer watch stays for good). - It registers the gesture host's gate only while a layer is held and releases it after the last hold ends (it stays registered today). - It remembers no pointer while nothing is held: a mouse that moved before a layer opened leaves its presentation's shield taking hits until the mouse moves again during the hold. The browser case for a mouse resting before the layer opened goes: with nothing listening at rest, that press meets the shield again.
Against the merged FR24 ("the number of document- or window-level touch
listeners MUST NOT grow with the number of component instances, ... and
nothing touch-related remains after the last instance unmounts", with
architecture:react-component-runtime INV5), the hold is a touch resource:
- One press hold per document, shared by every held layer: its window
listeners come with the document's first held layer and go once no
layer is held and the presses it holds have ended. Two held layers no
longer add two sets.
- The gesture host's gate is registered with the document's first held
layer and released after the last hold ends, rather than staying for
the page's lifetime.
- The pointer watch that stayed on the window from a page's first hold on
goes. Only the pointers heard while a layer is held count, and nothing
is remembered once the last hold ends: a mouse press made before the
mouse has moved during the hold meets the shield again.
… Dialog and Drawer entrance hold
…mit (spec:AST-094 FR35, FR24) Since the input hold listens to nothing while nothing is held (FR24), it cannot know a pointer used before it began: during the entrance a mouse press made before the mouse moves meets the shield, and once the mouse moves its press reaches the action. On the previous offer the still press reached the action.
…'s to its end (spec:AST-094 FR35) Red on the offer as it stands (found by meta#8630's Dialog case): a mouse that has not moved during a presentation's hold presses, its pointerdown meets the shield, and the shield then steps aside because that pointerdown named a mouse. The release is hit-tested on what lies beneath (an action), so the browser sends the click to the common ancestor of the two targets, the dialog, which Dialog reads as a backdrop click and closes on. - The shield stays up through a mouse press that begins on it, a drag included; only a mouse moving with no button down lets it through. - The gesture host's gate holds what lands on a shield, a mouse's press included. - In the browser: a still mouse press during a dialog's hold lands its release and click on the shield and reaches nothing; once the mouse moves, its press reaches the dialog's recognizer, as with no hold. - The gate case moves its mouse before pressing, as a mouse that passes now must.
…s to its end (spec:AST-094 FR35) Found by meta#8630's Dialog case: a mouse that had not moved during a presentation's hold pressed, its pointerdown met the shield, and the shield stepped aside because that pointerdown named a mouse. The release was hit-tested on what lay beneath, so the browser sent the click to the common ancestor of the two targets, the dialog, which Dialog read as a backdrop click and closed on. Only a mouse moving with no button down lowers a presentation's shields now; a finger's or a pen's press or move raises them as before. A mouse press that begins on a shield keeps it up to its end, so its release and click land on the shield, and the gesture host's gate holds whatever lands on a shield, a mouse's press included.
… the Dialog and Drawer entrance hold
There was a problem hiding this comment.
Approval needs an engineering owner's decision: spec:AST-094 FR35 holds a finger while a touch presentation enters, which FR13 scopes to a coarse primary pointer, but this entrance hold also runs where the primary pointer is fine, holding a finger and adding window listeners in components that use no gesture, where FR43, FR24 and architecture:react-component-runtime INV13 let only components that use a gesture listen. An engineering owner can approve that by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner; otherwise the hold should apply only where the primary pointer is coarse. Independently of that decision, these need changes:
- During every Dialog or Drawer entrance, a mouse press made before the pointer moves lands on the input shield and reaches nothing (
Layer/inputHold.ts:58-61), against FR35's "A mouse keeps today's behavior" and the changeset's "a mouse or a trackpad is unchanged". - Core's root entry now carries the gesture host, so
scripts/touch-support-bundle.test.mjs"is absent from the root entry" fails; #8671 or an equivalent test change needs to land first. - Rebase onto the landed #8342 stack so the diff is this change's own 12 files; 25,087 of its 26,303 authored lines come from that stack.
[Automated review]
| return [...state.layers]; | ||
| } | ||
| heldDocuments.delete(doc); | ||
| latestPointer.delete(doc); |
There was a problem hiding this comment.
Forgetting the latest pointer here means a presentation that opens while nothing else is held always starts with its shield raised for a mouse, so a mouse or trackpad press made before the pointer moves lands on the shield and reaches nothing (lines 58-61), in every Dialog and Drawer entrance. spec:AST-094 FR35 says "A mouse keeps today's behavior", and its verification row fails a changed mouse path. Seeding the pointer from input already heard, for example by recording the pointer type in the interaction-modality store whose document pointerdown listener Dialog already installs (Drawer would need the same tracking), would let a presentation a mouse opened pass mouse hits from the start without a new idle listener.
| * pinch still zooms the page. A mouse keeps today's behavior: the hold decides | ||
| * by each event's pointer, never by the primary pointer, so a mouse or a | ||
| * trackpad keeps every click on a touch screen, and a finger is held on a | ||
| * laptop whose primary pointer is a mouse. |
There was a problem hiding this comment.
#8646 has landed, but its FR43 governs gestures and never names the entrance hold, while FR35 reaches a touch presentation, which FR13 applies under a coarse primary pointer; FR43 and INV13 still let only a component that uses a gesture listen under a fine pointer. Holding a finger on a mouse-primary laptop, with the hold's 14 window listeners for every entrance, needs an engineering owner's /approve-spec on this head; otherwise return null unless the primary pointer is coarse, keeping the per-event check inside that gate.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [fix] Under a finger, a tap or a drag that lands while a `Dialog` or a `Drawer` is still entering no longer closes it, runs one of its actions, moves focus or scrolls the page: it reaches nothing until the entrance has played, and the dialog or drawer finishes entering and stays open. Keys and assistive technology stay live during the entrance, the content stays in the accessibility tree, a pinch still zooms the page, and a mouse or a trackpad is unchanged, even on a touch screen. |
There was a problem hiding this comment.
"a mouse or a trackpad is unchanged, even on a touch screen" does not hold at this head: during every entrance a mouse press made before the pointer moves lands on the shield (Layer/inputHold.ts:58-61). The same sentence is in Dialog.doc.mjs:72 and Drawer.doc.mjs:175; fix the hold, or narrow all three.
|
/approve-spec 7aa9117 |
- MobileNav: the touch-presentation proofs stop the page's clock and run it on by exact amounts, so each probe and tap lands inside the slide-in or the close, and the close is read exactly (at most 250 ms) (from meta#8374). - Dialog and Drawer: each entrance check plays one drag and proves it arrived (a capture-phase touch probe added before the page's scripts); the page's movement is read from the visual viewport (from meta#8630). - Layer: a hold under a moving touch is released at an exact step of the drag, and the document's log covers the touch from its start to its end.
…av (spec:AST-094 FR35)
Layer/inputHold holds a layer's pointer input while its presentation
enters or leaves: holdInput(layer, {until, scope}) raises a shield (scope
'viewport' while it enters; 'layer' while it leaves, its content inert),
decides by each event's pointer (a finger's or a pen's press is held to
its end; a mouse passes once it moves with no button down, DEC-16), never
cancels a touch's start, and cancels a one-finger move so a pinch still
zooms (FR10). The gesture host asks the hold for each touch (holdsTouch),
so the hold imports nothing from gestures/ and a component in the root
entry may use it statically (architecture:react-component-runtime INV13).
Its first users: Dialog and Drawer hold a finger's tap or drag during
their entrance (holdEntranceInput). MobileNav's drawer takes no finger
input while it slides in (a 'viewport' hold until the slide-in's
transition ends) or out (a 'layer' hold from the close request until the
dialog closes), and AppShell lets the slide-out play to its end.
Every browser proof lands its input at an exact point (an entrance held
where it is, the page's clock, or an exact step of a drag) and plays once.
From meta#8342 and its per-event offer, meta#8630 (Dialog and Drawer) and meta#8374
(MobileNav).
Co-authored-by: Cindy Zhang <cindyxz@meta.com>
|
Folded into #8342 under the maintainers' section plan; the commit credits this PR's authors. Closing; the branch stays. |
…av (spec:AST-094 FR35)
Layer/inputHold holds a layer's pointer input while its presentation
enters or leaves: holdInput(layer, {until, scope}) raises a shield (scope
'viewport' while it enters; 'layer' while it leaves, its content inert),
decides by each event's pointer (a finger's or a pen's press is held to
its end; a mouse passes once it moves with no button down, DEC-16), never
cancels a touch's start, and cancels a one-finger move so a pinch still
zooms (FR10). The gesture host asks the hold for each touch (holdsTouch),
so the hold imports nothing from gestures/ and a component in the root
entry may use it statically (architecture:react-component-runtime INV13).
Its first users: Dialog and Drawer hold a finger's tap or drag during
their entrance (holdEntranceInput). MobileNav's drawer takes no finger
input while it slides in (a 'viewport' hold until the slide-in's
transition ends) or out (a 'layer' hold from the close request until the
dialog closes), and AppShell lets the slide-out play to its end.
Every browser proof lands its input at an exact point (an entrance held
where it is, the page's clock, or an exact step of a drag) and plays once.
From meta#8342 and its per-event offer, meta#8630 (Dialog and Drawer) and meta#8374
(MobileNav).
Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…av (spec:AST-094 FR35)
Layer/inputHold holds a layer's pointer input while its presentation
enters or leaves. holdInput(layer, {until, scope}) raises a shield:
'viewport' over the whole viewport while a modal layer enters; 'surface'
over the layer's own box while a non-modal one enters, the page around it
live; 'layer' over the viewport, its content inert, while a layer leaves.
It decides by each event's pointer (a finger's or a pen's press is held to
its end; a mouse passes once it moves with no button down, DEC-16), never
cancels a touch's start, and cancels a one-finger move so a pinch still
zooms (FR10). The gesture host asks the hold for each touch (holdsTouch),
so the hold imports nothing from gestures/ and a component in the root
entry may use it statically (architecture:react-component-runtime INV13).
Its first users hold only under a coarse primary pointer, a touch
presentation (FR13): Dialog and Drawer hold a finger's tap or drag during
their entrance (holdEntranceInput), a non-modal Drawer only on its panel;
MobileNav's drawer takes no finger input while it slides in; AppShell lets
the slide-out play to its end. Under a fine primary pointer nothing is held
and nothing listens (FR24). Where the primary pointer is fine and the
screen also takes touch, a finger is not held yet; AST-094's current-state
impact and Drawer's record name that gap.
Every browser proof lands its input at an exact point (an entrance held
where it is, the page's clock, or an exact step of a drag) and plays once.
From meta#8342 and its per-event offer, meta#8630 (Dialog and Drawer) and meta#8374
(MobileNav).
Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…av (spec:AST-094 FR35)
Layer/inputHold holds a layer's pointer input while its presentation
enters or leaves. holdInput(layer, {until, scope}) raises a shield:
'viewport' over the whole viewport while a modal layer enters; 'surface'
over the layer's own box while a non-modal one enters, the page around it
live; 'layer' over the viewport, its content inert, while a layer leaves.
It decides by each event's pointer (a finger's or a pen's press is held to
its end; a mouse passes once it moves with no button down, DEC-16), never
cancels a touch's start, and cancels a one-finger move so a pinch still
zooms (FR10). The gesture host asks the hold for each touch (holdsTouch),
so the hold imports nothing from gestures/ and a component in the root
entry may use it statically (architecture:react-component-runtime INV13).
Its first users hold only under a coarse primary pointer, a touch
presentation (FR13): Dialog and Drawer hold a finger's tap or drag during
their entrance (holdEntranceInput), a non-modal Drawer only on its panel;
MobileNav's drawer takes no finger input while it slides in; AppShell lets
the slide-out play to its end. Under a fine primary pointer nothing is held
and nothing listens (FR24). Where the primary pointer is fine and the
screen also takes touch, a finger is not held yet; AST-094's current-state
impact and Drawer's record name that gap.
Every browser proof lands its input at an exact point (an entrance held
where it is, the page's clock, or an exact step of a drag) and plays once.
The WebKit interaction lane's scope includes Layer/inputHold.ts: the
gesture host imports it, so the gesture specs' fixture bundles it.
From meta#8342 and its per-event offer, meta#8630 (Dialog and Drawer) and meta#8374
(MobileNav).
Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…av (spec:AST-094 FR35)
Layer/inputHold holds a layer's pointer input while its presentation
enters or leaves. holdInput(layer, {until, scope}) raises a shield:
'viewport' over the whole viewport while a modal layer enters; 'surface'
over the layer's own box while a non-modal one enters, the page around it
live; 'layer' over the viewport, its content inert, while a layer leaves.
It decides by each event's pointer (a finger's or a pen's press is held to
its end; a mouse passes once it moves with no button down, DEC-16), never
cancels a touch's start, and cancels a one-finger move so a pinch still
zooms (FR10). The gesture host asks the hold for each touch (holdsTouch),
so the hold imports nothing from gestures/ and a component in the root
entry may use it statically (architecture:react-component-runtime INV13).
Its first users hold only under a coarse primary pointer, a touch
presentation (FR13): Dialog and Drawer hold a finger's tap or drag during
their entrance (holdEntranceInput), a non-modal Drawer only on its panel;
MobileNav's drawer takes no finger input while it slides in; AppShell lets
the slide-out play to its end. Under a fine primary pointer nothing is held
and nothing listens (FR24). Where the primary pointer is fine and the
screen also takes touch, a finger is not held yet; AST-094's current-state
impact and Drawer's record name that gap.
Every browser proof lands its input at an exact point (an entrance held
where it is, the page's clock, or an exact step of a drag) and plays once.
The WebKit interaction lane's scope includes Layer/inputHold.ts: the
gesture host imports it, so the gesture specs' fixture bundles it.
From meta#8342 and its per-event offer, meta#8630 (Dialog and Drawer) and meta#8374
(MobileNav).
Co-authored-by: Cindy Zhang <cindyxz@meta.com>
…av (spec:AST-094 FR35)
Layer/inputHold holds a layer's pointer input while its presentation
enters or leaves. holdInput(layer, {until, scope}) raises a shield:
'viewport' over the whole viewport while a modal layer enters; 'surface'
over the layer's own box while a non-modal one enters, the page around it
live; 'layer' over the viewport, its content inert, while a layer leaves.
It decides by each event's pointer (a finger's or a pen's press is held to
its end; a mouse passes once it moves with no button down, DEC-16), never
cancels a touch's start, and cancels a one-finger move so a pinch still
zooms (FR10). The gesture host asks the hold for each touch (holdsTouch),
so the hold imports nothing from gestures/ and a component in the root
entry may use it statically (architecture:react-component-runtime INV13).
Its first users hold only under a coarse primary pointer, a touch
presentation (FR13): Dialog and Drawer hold a finger's tap or drag during
their entrance (holdEntranceInput), a non-modal Drawer only on its panel;
MobileNav's drawer takes no finger input while it slides in; AppShell lets
the slide-out play to its end. Under a fine primary pointer nothing is held
and nothing listens (FR24). Where the primary pointer is fine and the
screen also takes touch, a finger is not held yet; AST-094's current-state
impact and Drawer's record name that gap.
Every browser proof lands its input at an exact point (an entrance held
where it is, the page's clock, or an exact step of a drag) and plays once.
The WebKit interaction lane's scope includes Layer/inputHold.ts: the
gesture host imports it, so the gesture specs' fixture bundles it.
MobileNav's record states what the rest of the slide-out after the close
does: it takes no input and is hidden from assistive technology (FR5). A
browser case proves both against the open drawer.
From meta#8342 and its per-event offer, meta#8630 (Dialog and Drawer) and meta#8374
(MobileNav).
Co-authored-by: Cindy Zhang <cindyxz@meta.com>
Depends on #8342 and its per-event hold offer (#8342 (comment)). Review only this PR's change: vjeux/astryx@0f68401...7aa9117
Why
On a phone, a tap that lands while a Dialog or a Drawer is still entering closes it, runs the button under the finger, or reaches the page; a drag scrolls its content, or the page beside a non-modal drawer. The person loses what they just opened.
What led to it
Part of the touch-presentations program (
spec:AST-094). UIKit delivers no touch until a presentation finishes, and FR35, as amended by #8618, asks the same of Astryx. Dialog and Drawer enter through CSS and take every input from the first frame.Options and tradeoffs
holdInput: a finger's or a pen's press reaches nothing until the entrance ends; keyboard, screen readers and a mouse are untouched, even on a touch screen. The cost is size: Dialog and Drawer now load the shared hold.Why this choice
The second is what FR35 asks, and deciding per event keeps a mouse on a touch screen unchanged too. The hold lasts exactly as long as the layer's own entrance animations, read with
getAnimations()as it opens, so a theme's duration, Reduce Motion or a close midway ends it with them. No API, style or default changes.How to check it
In Chrome's or Safari's phone emulation, on the Dialog stories (the Drawer stories behave the same):
Evidence
touch-support-bundlefails its root-entry case because Dialog and Drawer now reach the gesture host through the hold; test(core): a touch component's import carries its gesture code, and nothing else does #8671's per-component test replaces it and passes here.