@jsenv/navi 0.30.5 → 0.30.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev/jsenv_navi.js +357 -65
- package/dist/dev/jsenv_navi.js.map +8 -7
- package/dist/jsenv_navi.js +357 -65
- package/dist/jsenv_navi.js.map +8 -7
- package/docs/drag_to_travel.md +12 -0
- package/docs/mobile_touch.md +64 -5
- package/docs/navigation.md +4 -3
- package/package.json +1 -1
package/docs/drag_to_travel.md
CHANGED
|
@@ -51,6 +51,18 @@ What stays ours:
|
|
|
51
51
|
- **A travel asked for** (a tab, a key, a command, a state) plays in `duration`,
|
|
52
52
|
one box long whatever the distance, in `SlideContainer`. In `RouteTravel` it is
|
|
53
53
|
the browser's smooth scroll.
|
|
54
|
+
- **A press beats a row still moving.** It sets off at once from what is on screen,
|
|
55
|
+
and the row never lands where the hand was sending it. In `SlideContainer`
|
|
56
|
+
`onCurrentChange` hears about the slide pressed and not about that one, and on iOS a
|
|
57
|
+
swipe during that travel is ignored, and the next one works.
|
|
58
|
+
- **What says where one is follows the screen.** While a hand holds the row, one is on
|
|
59
|
+
the page held. Once it has let go, what reads the row reads where it is going:
|
|
60
|
+
`useSlideContainer` (`current`, `can`, `held`), a `Nav`'s tabs, a `StepList`'s steps
|
|
61
|
+
and the chevrons in `SlideContainer`, so Next pressed during a snap counts from there;
|
|
62
|
+
a link's current state and `routeTravelSettledPageSignal` in `RouteTravel`. A trackpad
|
|
63
|
+
has no lift, so the nearest page answers. What the state holds waits for rest: the
|
|
64
|
+
bound signal and `onCurrentChange`, and in `RouteTravel` the address, except where it
|
|
65
|
+
runs ahead to build a page.
|
|
54
66
|
- **A direction that is refused is the end of the row.** Whatever holds the user
|
|
55
67
|
where they are (a `<Slide required>` waiting for its answer, a `preventNavNext`)
|
|
56
68
|
takes what lies beyond off the row, so nothing can be scrolled to. There is no
|
package/docs/mobile_touch.md
CHANGED
|
@@ -9,8 +9,8 @@ fixed, they differ between engines, and **none of them shows with a mouse**. A
|
|
|
9
9
|
gesture can pass a whole session of desktop testing and still fail one swipe
|
|
10
10
|
in three on a phone.
|
|
11
11
|
|
|
12
|
-
What follows is measured, not remembered: Chromium 153
|
|
13
|
-
emulator, and Safari on the iOS 26.5 simulator driven with real touches (see
|
|
12
|
+
What follows is measured, not remembered: Chromium 153 and Chrome 156 through the
|
|
13
|
+
mobile touch emulator, and Safari on the iOS 26.5 simulator driven with real touches (see
|
|
14
14
|
[Verifying without a device](#verifying-without-a-device)). When an engine
|
|
15
15
|
changes, measure again before trusting a number here.
|
|
16
16
|
|
|
@@ -22,6 +22,7 @@ changes, measure again before trusting a number here.
|
|
|
22
22
|
- [A tap dropped after a touch drag](#a-tap-dropped-after-a-touch-drag)
|
|
23
23
|
- [A finger is captured to what it touched, inside shadow trees too](#a-finger-is-captured-to-what-it-touched-inside-shadow-trees-too)
|
|
24
24
|
- [A finger never makes a wheel event](#a-finger-never-makes-a-wheel-event)
|
|
25
|
+
- [A snap keeps running after the finger](#a-snap-keeps-running-after-the-finger)
|
|
25
26
|
- [Verifying without a device](#verifying-without-a-device)
|
|
26
27
|
|
|
27
28
|
## The browser decides about 8px in, and does not wait
|
|
@@ -229,6 +230,11 @@ where it lands, never asked for: on a touch press, a capture announced on the
|
|
|
229
230
|
press's own target (compared from the same tree) is the browser's, and a gesture
|
|
230
231
|
taking the press onto that same element has to say so another way.
|
|
231
232
|
|
|
233
|
+
The rest of the touch goes there too, **even once that element has left the document**:
|
|
234
|
+
a page re-rendering under the finger takes it out, and its `touchend` is dispatched on
|
|
235
|
+
the detached node and reaches no ancestor. Nothing on the page around it hears the
|
|
236
|
+
lift. What has to hear it listens on `event.target` from the `touchstart`.
|
|
237
|
+
|
|
232
238
|
Measured in Chromium 153 (`Input.dispatchTouchEvent`: the capture does not
|
|
233
239
|
depend on the slop) and on an Android phone (Chrome 153); not in Safari.
|
|
234
240
|
Reference: `implicitCaptureHolder` and `takePress` in `@jsenv/dom`'s
|
|
@@ -243,6 +249,51 @@ paths differ in timing: a wheel gesture is claimed and refused on its very first
|
|
|
243
249
|
event, so the browser never gets a moment to decide, while a finger has to
|
|
244
250
|
travel a few pixels before anything can be read.
|
|
245
251
|
|
|
252
|
+
## A snap keeps running after the finger
|
|
253
|
+
|
|
254
|
+
A row with `scroll-snap-type: mandatory` goes on moving once the finger has lifted:
|
|
255
|
+
about 600ms in Chrome, about 700ms in Safari, slowing all the way, so the last 300ms
|
|
256
|
+
cover barely 40px of a 390px box. A press on something outside the row arrives during
|
|
257
|
+
that time and gets its click, in Safari too. What it does to the row depends on the
|
|
258
|
+
engine, because neither plays that movement on the main thread:
|
|
259
|
+
|
|
260
|
+
| Asked of a row mid-snap | Chrome | Safari on iOS |
|
|
261
|
+
| --------------------------------- | --------------------------------------------------------------------- | --------------------------- |
|
|
262
|
+
| Instant `scrollTo` | Ignored: the snap goes on and overwrites it | Stops it |
|
|
263
|
+
| Smooth `scrollTo` | Replaces it until it ends: a short trip gets the snap back, and loses | Not measured |
|
|
264
|
+
| `scroll-snap-type: none` | Ignored | Jumps to the target at once |
|
|
265
|
+
| `overflow: hidden`, held | Moves nothing, but the snap resumes when it is taken back | Stops it |
|
|
266
|
+
| `overflow: clip`, held two frames | Stops it: there is no scroller left to play it on | Stops it |
|
|
267
|
+
| `display` changed for one layout | Stops it (see below) | Stops it (see below) |
|
|
268
|
+
|
|
269
|
+
A change undone in the same task reaches nothing in Chrome, whatever the property,
|
|
270
|
+
since the compositor never hears of it: only a `display` change acts that fast. It
|
|
271
|
+
rebuilds the box, and it costs: `display: none` starts every CSS animation and
|
|
272
|
+
transition inside over, `display: block` brings a scroller inside back to its top
|
|
273
|
+
unless what the row holds keeps its size for that layout, and WebKit (desktop, under a
|
|
274
|
+
synthetic wheel) then ignores the next write to the position and loses a scroller's
|
|
275
|
+
own position inside.
|
|
276
|
+
|
|
277
|
+
On Safari the last three rows were measured with a write following them, and a write
|
|
278
|
+
alone is enough, so they say nothing of their own there.
|
|
279
|
+
|
|
280
|
+
`scrollend` says little here: in Chrome a write gets one at once, with the row still
|
|
281
|
+
moving. A row moved by other means with its snap left running ends worse than late:
|
|
282
|
+
with a track animated over it, tens of pixels off any slide in Chrome, half a box off
|
|
283
|
+
in Safari, and it stays there. Anything that moves a row on a press has to end the snap
|
|
284
|
+
first.
|
|
285
|
+
|
|
286
|
+
A box given `overflow: clip` is no scroller until it is given back, and that has its
|
|
287
|
+
own cost: **a touch landing meanwhile starts no scroll in Safari**, even when the page
|
|
288
|
+
gives the scroller back on `pointerdown`, since the scroll view does not exist yet when
|
|
289
|
+
the finger lands. Chrome takes that swipe. Given back, the row stands where it stood
|
|
290
|
+
with nothing written, but for the rest of that task WebKit still starts from 0: a smooth
|
|
291
|
+
scroll to 0 sees nothing to do, and the row stays where it is, off any page. Ask a frame
|
|
292
|
+
later.
|
|
293
|
+
|
|
294
|
+
Firefox is not in this table: Playwright's synthetic wheel never leaves its row
|
|
295
|
+
mid-snap, so it takes real input, events posted through the OS (`CGEvent` on macOS).
|
|
296
|
+
|
|
246
297
|
## Verifying without a device
|
|
247
298
|
|
|
248
299
|
Both engines can show the browser's own answers — the pan, the
|
|
@@ -262,6 +313,10 @@ configuration: "mobile" })`, then `Input.dispatchMouseEvent` with
|
|
|
262
313
|
- those calls are **never acknowledged**: send them without awaiting, with a
|
|
263
314
|
sleep between moves (~16ms is a frame).
|
|
264
315
|
|
|
316
|
+
A tap through this path waits about 300ms for a possible double tap, under
|
|
317
|
+
`isMobile` too, so a press meant for a moment in a movement lands 300ms after it, at
|
|
318
|
+
the end of a snap. `touch-action: manipulation` on what is pressed removes the wait.
|
|
319
|
+
|
|
265
320
|
`Input.dispatchTouchEvent` — what Playwright's `touchscreen` uses — goes
|
|
266
321
|
through the desktop configuration instead: a 15px slop. Anything deciding at
|
|
267
322
|
10px wins there, and the race that loses swipes on a phone is invisible.
|
|
@@ -304,9 +359,13 @@ its momentum included (over 3 s: flings closer than that add up). The window
|
|
|
304
359
|
has to be on screen in the current Space: when it is not, activating the
|
|
305
360
|
Simulator does not bring it, and the events land in whatever window is under
|
|
306
361
|
the pointer — check which window is topmost at the points of the drag before
|
|
307
|
-
sending any, and wait for the Space to switch after activating.
|
|
308
|
-
|
|
309
|
-
|
|
362
|
+
sending any, and wait for the Space to switch after activating. On screen is not
|
|
363
|
+
on top: behind another app's window, activating does not raise it either, System
|
|
364
|
+
Events does (`set frontmost to true`, then `AXRaise`), and another app can come back
|
|
365
|
+
over it within seconds, so raise it and check right before each gesture, and again
|
|
366
|
+
after any pause inside one. A tap works the same way, and reaches other elements
|
|
367
|
+
during momentum. What the page records is read back through a request it sends itself
|
|
368
|
+
(the page runs on a local server), since nothing can query it.
|
|
310
369
|
|
|
311
370
|
What to synthesize is the hand's imperfection — a gesture simulated as clean,
|
|
312
371
|
evenly spaced points passes forever and proves nothing: a slow start landing a
|
package/docs/navigation.md
CHANGED
|
@@ -613,9 +613,10 @@ const shownId = settledPage ? settledPage.params.gameId : currentId;
|
|
|
613
613
|
|
|
614
614
|
It holds the `{ route, params }` the hand set off from, and is `null` from the moment
|
|
615
615
|
a lifted finger leaves the row moving, the URL being the truth again: the row is only
|
|
616
|
-
playing out what the browser decided. A trackpad has no lift, so there it
|
|
617
|
-
the row has come to rest, and a tab already built
|
|
618
|
-
holds nothing, and `null` is all it ever reads
|
|
616
|
+
playing out what the browser decided. A trackpad has no lift, so there it holds the
|
|
617
|
+
page nearest the row until the row has come to rest, and the URL of a tab already built
|
|
618
|
+
waits for that too. A tab pressed holds nothing, and `null` is all it ever reads, even
|
|
619
|
+
over a row a hand has left moving: the press wins at once. One signal for the document: on a
|
|
619
620
|
page with several boxes, `route` says which row it is.
|
|
620
621
|
|
|
621
622
|
`RouteTravel` is for pages that form a ROW the finger can push; pages related
|