Repository navigation
Conversation
… (spec:AST-094 FR43) A drag from the edge band begins a screen-edge pan for a finger only, unless the caller allows another pointer. The mouse and pen case fails until the recognizer defaults to touches.
… FR19, FR24, DEC-21) Recognizers and press consumers attach for a mouse, a pen and a finger alike. Where the primary pointer is fine, each element with an enabled recognizer or consumer keeps one passive pointerdown listener and the page has none: a press on it (a mouse's primary button, a pen, or a finger) installs the host's page listeners until that press's contacts end. The action group's own fine-pointer path folds into this one, so a group is served like every attachment. - touch-action is written for every pointer, a kept press's manipulation too (FR18); the element's blocking touchmove stays a coarse pointer's, so under a fine one the element keeps its one listener (FR24). - A press consumer takes a mouse press as a press (DEC-21): no feedback-only mode; PressTracker brakes only a finger's press (FR11). - The screen-edge pan allows touches only unless its caller allows another pointer (FR43). - perEventPointer is accepted and does nothing; the fine-pointer warning goes. Tests that encoded the coarse-only design now state FR43's; a finger's pointerdown comes before its touchstart in them, as in a browser.
…th real mouse drags (spec:AST-094 FR43, FR24) Under a desktop's mouse the host still writes touch-action, leaves the page no listener while idle and warns for nothing; a real mouse drag pans both the bare page's pan and the fixture's horizontal pan, and the page's touch listeners are gone again after the release.
…(spec:AST-094 FR18, DEC-13) A screen-edge pan attached to a stack container, as wide as the screen, with a touch 4 px from its edge: inside the 40.2 px band the pan decides at UIKit's 15 px whatever the element's width, and then tracks the travel past it. From several edges too. Both fail while a wider element decides at FR19's 10 px.
…'s width (spec:AST-094 FR18, DEC-13) FR18: inside the 40.2 px band a screen-edge pan decides at UIKit's 15 px screen-edge hysteresis; outside it the author's scrolling is untouched and FR19's 10 px governs every other claim. The pan decided at 10 px on any element wider than the band, so NavigationStack's edge pan, attached to the stack container, began 5 px early. The edge zone still decides only the touch-action; a touch outside every band still fails at once. The two cases that pinned the 10 px decision go.
…than the band (spec:AST-094 FR18, DEC-22) FR18 as amended in meta#8631 (DEC-22): the band's pan-x claim and 15 px start hold only where the screen-edge pan is attached to an element within the band; on an element that reaches beyond it, whose touch-action cannot claim per touch, a touch in the band decides at FR19's 10 px like every other claim. This restores the 10 px decision 3d85c30 removed and keeps 15 px on an element within the band. The cases pin DEC-22 both ways (a wide element with a touch at x=4 begins at 10, an in-band strip at 15, and from several edges at 10 on a wide element); the 10 px cases fail on 3d85c30.
…ads its end before the arena's reset) into meta#8336
The case walked ContextMenu, DropdownMenu, useLongPress and useMenuPress's imports and failed on the UIKit gestures table: it kept that table out of the menus' bundles, the lazy design the no-lazy-load ruling and direct imports retire. The long-press numbers keep one source, still pinned by the table, recognizer and hooks case and by the hooks reaching longPress.ts.
# Conflicts: # .github/workflows/ci.yml
…xposes no ontouchstart is taken once Chromium fixes ontouchstart when the page is created, so turning touch emulation on later sends Touch Events without it: the host reads Pointer Events for the finger and must not read its Touch Events too. A one-finger long press fails here, the finger tracked twice.
…from their pointer events install() reads a finger from its pointer events when the window has no ontouchstart, but the four touch handlers still took the Touch Events a browser sends anyway (Chromium after touch emulation turns on), tracking the finger twice, so a one-finger long press never recognized. Each touch handler now returns while usesTouchEvents is false, after noting the fingers down, as install()'s comment promised.
…ter events) into meta#8320
…ter events) into meta#8293
…ter events) into meta#8356
…ter events) into meta#8358
…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.
|
This needs an owner's call on wheel input. FR43 says a pan takes wheel and trackpad scrolling, with no exception. This slider takes none, so a scroll over it stays the page's, as over a native range input; taking it would let a page scroll change a value by accident. The options: amend FR43 to exclude a control's thumb, or give the slider wheel input. My pick is the FR43 amendment. |
…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.
…r path Each case fails on this head: - Under Reduce Motion a release's momentum keeps moving and a track tap sets its value at once, both at full opacity (design:reduced-motion DR2: a throw's momentum is direct movement; an in-place value change applies at once). The thumb cross-fades both today. - Range thumbs closer than their touch targets split the gap, so a finger on the lower thumb's exposed side takes the lower thumb (spec:AST-094 FR42). Today the upper thumb's 48 px target covers it. - A valueDisplay change that remounts the thumbs keeps their gestures. Today the new touch targets have none. - A finger's drag moves the thumb even when the owner renders nothing, its position written straight to the thumb (spec:AST-094 FR9). Today the thumb moves only through the owner's render. The pen case now advances the frames before it asserts, so it checks what a pen's tap on the track does: it sets the value on its lift, as a finger's does.
…s gap, remounts, and a drag off the render path - Under Reduce Motion a release's coast plays as momentum (the animator's decay), so it keeps moving, and a tap on the track sets its value at once: design:reduced-motion DR2 calls a throw's momentum direct movement and an in-place value change immediate. Neither cross-fades now. - Each thumb's touch target grows to UISlider's 48 pt, but on the side facing a range's other thumb only to half the gap between them (spec:AST-094 FR42), computed in container units so it needs no layout read. A finger on the lower thumb no longer drags the upper one. - The thumbs' gestures re-attach when a valueDisplay change wraps or unwraps the thumbs in their value tooltips, which mounts new targets. - Each move of a drag writes the thumb's and the fill's positions straight to their translates (spec:AST-094 FR9), with onChange still reported per value; the parts go back to the stylesheet when the interaction ends, and a finished interaction no longer hands back the parts of one still being dragged. - The Effects that hand the controller its model and parts getter, and the one that disposes it, name what they synchronize (architecture:react-component-runtime INV3).
…EC-4) AST-094 FR13 leaves each component's touch presentation to its own record. FR6 records Slider's: under a coarse primary pointer a thumb takes a finger or a pen through UISlider's 48 pt target, clamped to half the gap facing a range's other thumb; a drag moves the value by the finger's travel past the 10 px threshold with no render per move; the release coasts on UISlider's measured spring; a tap on the track sets the value on lift; and Reduce Motion follows DR2. FR5's interaction ends, under a finger, when the coast settles. DEC-4 records the decision, with no prop to restore the released behavior.
… thumb A mouse's first 12 px straight across a horizontal slider, then 60 px along it: the thumb should move by the 60 px, since a mouse has no page scroll to yield to. Today the thumb's pan, limited to the travel's direction, fails on the first movement, and the thumb never moves.
…thumb The thumb's pan was limited to the travel's direction for every pointer, so a mouse drag whose first 10 px ran across the travel failed it and the thumb never moved. A mouse now has a pan of its own that claims in any direction after the same 10 px, and the value follows the drag's travel along the axis, counted from the threshold's projection on it. A finger and a pen keep the pan limited to the travel, so a swipe across the track still scrolls the page.
…se drag Slider.doc.mjs said a mouse keeps the pointer behavior, the changeset counted listeners, and the primary-click comment named a pen rule the gesture host does not apply. They now describe the mouse drag, past the same 10 px in any direction, and the primary click. component:Slider FR6 covers every pointer (spec:AST-094 FR43): a mouse drag tracks past the same threshold in any direction, a press on the track sets the value where a primary click lands, and a scroll over the slider stays the page's.
Inside a scrolling sheet a wheel or trackpad scroll over a slider goes to the sheet, not the page, so the record, the docs, the changeset and the controller's comments say it goes to the scroll under the slider, as spec:AST-094 FR43's exception for a control's thumb does.
…ame window Both 'a dip held before its first frame lands once the hold is …' proofs waited for no running animation, then one frame and a timeout, assuming the next step starts within a frame. The dip lands on a timer due on the call's own clock, though, and the proofs passed only because the evaluate round trips covered the gap: in WebKit on a loaded box the window closed first and the proof read the element mid-dip (opacity 0, translate none; reproduced 1 in 4 with the round trips removed). They now wait on the call's own finished, then on the browser's own animations still running or waiting for their first frame, and read nothing from the animator.
…ment at once and settles then A Reduce Motion dip whose landing runs more than its whole fade back in late (a hold released long after the dip was due) played a 1 ms opacity animation, which WebKit keeps pending until its next frame, and settled the call on a 1 ms timer: the call's finished could resolve while the element still showed opacity 0. The animator's notes say the element appears at once there. Fails now: the landing plays one opacity animation and the call has not settled.
…element at once and settles A Reduce Motion landing that runs at least the whole fade back in late had nothing left of the cross-fade, but still played a 1 ms opacity animation and settled the call 1 ms later. WebKit keeps that animation pending until its next frame, so the call's finished could resolve with the element still at opacity 0. Such a landing now writes the end opacity at once (handing a borrowed one back to the stylesheet), plays nothing, and settles the call then, as the animator's notes say.
|
Pushed ce50d06, answering the Oct 28 review point by point:
|
There was a problem hiding this comment.
Approval waits on an engineering owner: the slider's thumb pans take no wheel or trackpad scroll, which AST-094 FR43 on main still requires of a pan until the amendment in #8905 lands, and the new every-pointer clause in component:Slider FR6 needs owner approval. An engineering owner records the decision by landing that FR43 amendment and commenting /approve-spec ce50d067e2324902a7f6c0b5c3db9669531a3ded here, or by rejecting the exception so the thumb's pan takes wheel input; the author may do so if they are an engineering owner. Whatever that decision, these changes are needed:
- Rebase once #8190, #8665, #8676, #8666, #8671 and #8360 land, so the diff against
mainholds only this change (36,403 authored lines today; its own ten commits touch about 715). - A mouse drag that starts diagonally counts up to 2.9 px of the 10 px threshold when the pan begins (
sliderTouchController.ts:531-534); subtract the travel the recognizer swallowed and add a diagonal test. - A pen under a fine pointer now lifts, coasts and sets the value on a track tap's lift, which FR6, the docs and the description do not describe.
- Add
## Writing rubric resultsfor the two edited records, asAGENTS.mdasks of a record pull request.
[Automated review]
| interaction.beganOffset = interaction.isMouse | ||
| ? distance > 0 | ||
| ? (pan.hysteresis * along) / distance | ||
| : 0 |
There was a problem hiding this comment.
The mouse pan begins once either axis reaches 10 px (a square in PanGestureRecognizer.tryBegin), but this subtracts the projection of a 10 px circle, so a 45° start counts about 2.9 px of threshold travel at once, against AST-094 FR17 (“its translation excludes that travel”). Subtracting what the recognizer swallowed, Math.sign(along) * Math.min(Math.abs(along), pan.hysteresis), keeps straight starts as they are; a diagonal test would pin it.
| | FR3 | Filled range, tick marks, mark labels, and adjacent text value display are stable rendered parts without their own current Slider target. | Current source and public docs | Verified current asymmetry; not ratified as policy | | ||
| | FR4 | Label and status presentation continue to use Field and FieldStatus; value tooltips continue to use Tooltip. | Current source and focused tests | Verified composition boundary | | ||
| | FR5 | `onChangeEnd` fires once when an interaction ends — a pointer drag or track press is released or cancelled (under a finger, once the release's coast settles; FR6), or a keyboard step (arrow, Page, Home, End) completes — and carries the value the owner renders after its synchronous response to that interaction's `onChange` calls, including any clamp, snap, or rejection; it never carries a value Slider emitted that the owner did not render. A range reports both values the same way. | Maintainer decision (DEC-3) | Approved | | ||
| | FR6 | Under a coarse primary pointer, after hydration (`spec:AST-094/FR13`), each thumb takes a finger or a pen through an invisible touch target of UISlider's measured 48 pt; on the side facing a range's other thumb it grows only to half the gap between them (`spec:AST-094/FR42`). A drag on a thumb moves the value by the finger's travel past `spec:AST-094/FR19`'s 10 px threshold over the track's length, writing each move to the thumb with no component render (`spec:AST-094/FR9`). The release coasts on UISlider's measured spring toward 0.4 of the momentum projection. A tap on the track sets the value when the finger lifts; a swipe across the track scrolls the page. Under reduced motion the thumb does not grow, a tap's value applies at once, and the coast continues (`design:reduced-motion/DR2`). Under every other pointer (`spec:AST-094/FR43`) a mouse drag on a thumb moves the value the same way, past the same threshold in any direction; a press on the track sets the value where a primary click lands; and a wheel or trackpad scroll over the slider goes to the scroll under it. | UIKit measurements (`motion/uikit/slider`) and maintainer decision (DEC-4) | Approved | |
There was a problem hiding this comment.
This clause covers a mouse, but under a fine primary pointer a pen now takes the finger path: Slider.tsx leaves every pen press to the gesture host, so a pen on a thumb lifts and coasts and a pen press on the track sets the value only when its tap ends. AST-017 FR59 asks for the owner decision on a new fine-pointer feel in this record, so the pen needs a sentence here (or the mouse path).
…R43, DEC-23) FR43 gives every pan wheel and trackpad scrolling, with no exception, so a slider's thumb, which tracks a drag through a pan, would read a page scroll passing over it as a change of value. FR43 now excepts a control's thumb: a scroll over it stays the page's. DEC-23 records why, and FR43's verification row names the case. meta#8669 is the implementation.
…croll (FR43) (#8905) <!-- template: specification.md --> ## Exact decision needed This comes from Astryx's UIKit-parity work (`spec:AST-094`), which brings iOS's touch behavior to the web components. FR43 said a pan takes wheel and trackpad scrolling alike. Read literally, a few quick mouse-wheel notches would move or close a sheet, and a page scroll that passes over a slider would change its value. Two answers are needed: which scroll input drives a pan, given that the web reports no trackpad contact or phase, and whether a thumb that sets a value takes any. Slider's every-pointer change, #8669, needs the second. ## Existing authority searched FR3, FR19, FR36, FR37, FR41, FR43, DEC-1 and DEC-21 in `spec:AST-094`; `spec:AST-017` FR59; `spec:AST-057` DEC-7; `component:Slider`; `component:BottomSheet`; and the gesture host's per-recognizer scroll types. Touch work keeps released mouse behavior (AST-017 FR59, AST-094 FR41), and native parity (DEC-1) matches a range input, which ignores the wheel. No record told a trackpad from a mouse wheel or said where a trackpad gesture starts and ends. All 51 other open pull requests that edit AST-094 were checked at this head, and none changes FR43, DEC-23 or DEC-24. ## Decision - **FR43:** a pan takes a trackpad's two-finger scroll where its record allows it, and never a mouse wheel's notches. A scroll counts as a trackpad's only when it carries the trackpad signature Chromium and WebKit report. Any other scroll, Shift+wheel and a tilt wheel included, is a mouse wheel's, and scrolls what is under it. - **DEC-24** (decided by the record's owner): one splitter reads every trackpad stream, since the web reports no trackpad contact, phase or momentum. It ends a gesture on a pause and reads the fingers' lift from a shrinking run of deltas. Momentum never moves a surface. A reversal, a rising delta or a gap starts a new gesture. While the splitter has not yet decided between a lift and fingers still moving, the surface keeps moving at the fingers' last speed, never farther than that speed carries it over the held deltas, so a lift continues without a stop. A pan released at the lift keeps the fingers' velocity (FR36). - **Surfaces that follow a trackpad:** - A BottomSheet follows a trackpad as it follows a finger. A `form` or `required` sheet stretches on FR3's rubber band and springs back; an `info` sheet closes on a flick or past its close distance; a mouse wheel never moves a sheet. - A pager moves at most one page per swipe. - A swipe row follows the fingers, settles to the nearest detent, and only closes within one gesture. - **DEC-23** keeps its exception: a thumb that sets a value, a slider's or a switch's where it pans, takes neither, so a scroll over it goes to the scroll under it. - **Engines:** a trackpad drives a pan only in a browser that reports `wheelDelta` (Chromium and WebKit). Elsewhere every scroll counts as a mouse wheel's. - **Verification:** FR43 gains a trackpad verification row. ## Rejected alternatives - DEC-24: a mouse wheel driving a pan; a discrete step per trackpad swipe for rows and paging; a longer end timeout instead of the splitter. - DEC-23: the thumb taking scroll input like other pans. ## Claim scope and non-goals In scope: which scroll input a pan takes and where a trackpad gesture starts and ends; the surfaces that follow it (BottomSheet, the shared pager, swipe rows); and a thumb that sets a value. Touch input, a pinch's ctrl-wheel, and keyboard and focus behavior stay outside this decision. ## Evidence and implementation relationship - Slider's every-pointer change (#8669) implements the thumb rule: its thumb pan opts out of scroll input. Browser tests there scroll a wheel and a trackpad over a thumb. - The trackpad rule's implementation, with its unit tests on timed wheel streams and WebKit trackpad tests, lands in the gesture and component pull requests that follow FR43 (#8676 and its dependents). ## Writing rubric results - R1 narration: none. The rules are stated in the present tense, and the `component:BottomSheet` line under affected records names the finger's one-for-one drag below rest as an implementation gap. - R2 research in the record: none. DEC-24 keeps its reasons as two sentences inside the decision. - R3 evidence naming a change: none. FR43's verification rows name the behaviors and engines tested. - R4 rejected alternatives over one line or outside a decision: two, each one line inside its decision (DEC-24, DEC-23). - R5 pull request or issue references remaining: none in the record. - R6 ruled open questions still listing options: none touched. ## Scope - [x] This PR contains specification records only. - [x] It records observable behavior and preserves equivalent internal implementations. - [x] Any named internal mechanism is intentionally public, names its dependent caller/system, and explains why an equivalent implementation would not satisfy the contract. - [x] Verification states the evidence layer and failure signal without prescribing CI job/workflow topology. - [x] It records one intentional durable decision, not a rescue for a separable tagalong. - [x] The canonical knowledge owner, affected current claims, and overlapping open work are named; any ownership collision is resolved or called out for human decision. - [x] Adjacent API, visual, accessibility, composition, and implementation facts remain outside scope unless this exact decision depends on them. - [x] Public text and artifacts contain no internal Meta context. ## Validation Local gates at a0bf771: `pnpm check:knowledge` (records aligned), Prettier, `git diff --check`, and `pnpm check:repo`.
Depends on #8190 (which carries #8671) and #8676 (which carries #8665 and #8666). Review only this PR's change: vjeux/astryx@93e4ce4...ce50d06 (it also shows the parents' newer commits: #8676's last two, #8671 and #8360's animator fix)
User need
Part of Astryx's UIKit-parity work (
spec:AST-094): #8190 made a Slider under a finger track like iOS's UISlider, but on a desktop a mouse drag still jumped the value to the pointer. FR43 asks gestures to work for every pointer.Problem and solution fit
Current specification
spec:AST-094FR43, FR19, FR24 and DEC-21; #8905 adds FR43's exception for a control's thumb.component:SliderFR6 (from #8190) now covers every pointer.Public delta and caller burden
No API change. Size (
component-size.js, base to head): the Slider import's JavaScript 85.1 → 85.2 KB gzip, CSS unchanged.Design and alternatives
Left to the owner (
component:Slider, per FR13; today's behavior stays): a lift on a mouse press, a coast after its release, a track press that sets the value on release.Evidence
The test plan below, run in Chromium and WebKit.
Scope
Testing
In Storybook (Slider › Default), with a mouse:
Run on this head, locally (fork pull requests start no CI): steps 1, 3 and 4 are browser tests in Chromium and WebKit (the ctrl-click a unit test), each failing before its change and passing now; the full core suite, typecheck, lint, the builds and
pnpm check:knowledgepass. #8671 per-component bundle check, carried through #8190, passes here.Writing rubric results