@jsenv/navi 0.29.523 → 0.30.1
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 +1486 -2386
- package/dist/dev/jsenv_navi.js.map +35 -32
- package/dist/jsenv_navi.js +1486 -2372
- package/dist/jsenv_navi.js.map +35 -32
- package/docs/AI_INSTRUCTIONS.md +4 -3
- package/docs/create_and_edit.md +6 -17
- package/docs/dialog_shape.md +7 -0
- package/docs/drag_to_travel.md +115 -132
- package/docs/dynamic_import.md +1 -1
- package/docs/interactions.md +6 -0
- package/docs/mobile_touch.md +3 -3
- package/docs/navigation.md +54 -31
- package/docs/popup_open.md +7 -0
- package/docs/route_transitions.md +6 -16
- package/docs/scroll.md +3 -1
- package/docs/view_transitions.md +4 -7
- package/package.json +2 -2
package/docs/AI_INSTRUCTIONS.md
CHANGED
|
@@ -66,7 +66,7 @@ start when unsure which export solves a problem.
|
|
|
66
66
|
`SlideContainer` read from the URL, what a layer drawn over the screen may say
|
|
67
67
|
in its address. Before any routing code.
|
|
68
68
|
- `route_transitions.md` — a transition states a relation between two pages;
|
|
69
|
-
`RouteTravel` or a transition
|
|
69
|
+
`RouteTravel` or a transition on one pair; fixed bars and
|
|
70
70
|
`RouteTransitionArea`; the page being left stays mounted, hidden, until the
|
|
71
71
|
movement is over; a test waits for the page, never for its address; what a
|
|
72
72
|
transition costs. Before animating a navigation, and before believing a
|
|
@@ -77,8 +77,9 @@ start when unsure which export solves a problem.
|
|
|
77
77
|
whole document, what cannot be pointed at while one plays. Before
|
|
78
78
|
`document.startViewTransition`, and before putting a value on `:root` for a
|
|
79
79
|
movement.
|
|
80
|
-
- `drag_to_travel.md` — a
|
|
81
|
-
a
|
|
80
|
+
- `drag_to_travel.md` — a hand pushing a screen aside: what a row the browser
|
|
81
|
+
scrolls answers, what a box pushed aside reads, and who owns a press several
|
|
82
|
+
boxes want. Before putting anything that reads the pointer
|
|
82
83
|
inside a box that travels.
|
|
83
84
|
|
|
84
85
|
### Data
|
package/docs/create_and_edit.md
CHANGED
|
@@ -410,30 +410,19 @@ right. **A page left out of `routes` does not travel**: one opens the create
|
|
|
410
410
|
screen from the list, one does not slide there, so the list is absent and that
|
|
411
411
|
move plays nothing — leaving a page out is how a movement is refused.
|
|
412
412
|
|
|
413
|
-
A pair with a movement of its own
|
|
414
|
-
game and its edit screen are the same thing seen two ways, so they
|
|
415
|
-
|
|
416
|
-
(the column's order is the children's), coming down over the game, sent back up
|
|
417
|
-
by saving:
|
|
413
|
+
A pair with a movement of its own takes a route transition rather than a row. The
|
|
414
|
+
game and its edit screen are the same thing seen two ways, so they turn over inside
|
|
415
|
+
the place the outer row holds for them, with `defineRouteTransition`:
|
|
418
416
|
|
|
419
417
|
```jsx
|
|
420
|
-
|
|
421
|
-
<RouteTravel axis="y">
|
|
422
|
-
<Route>
|
|
423
|
-
<Route route={EDIT_GAME_ROUTE} element={EditGamePage} />
|
|
424
|
-
<Route route={GAME_ROUTE} element={GamePage} />
|
|
425
|
-
</Route>
|
|
426
|
-
</RouteTravel>
|
|
427
|
-
);
|
|
418
|
+
defineRouteTransition(GAME_ROUTE, EDIT_GAME_ROUTE, "flip");
|
|
428
419
|
|
|
429
|
-
// in the
|
|
420
|
+
// in the row: the same element on both branches
|
|
430
421
|
<Route route={EDIT_GAME_ROUTE} element={GameArea} />
|
|
431
422
|
<Route route={GAME_ROUTE} element={GameArea} />
|
|
432
423
|
```
|
|
433
424
|
|
|
434
|
-
The
|
|
435
|
-
the same element on both branches keeps the inner row mounted, with something
|
|
436
|
-
to travel between.
|
|
425
|
+
The row does not move for it: the edit url is not one of its pages.
|
|
437
426
|
|
|
438
427
|
## The same loop in a dialog
|
|
439
428
|
|
package/docs/dialog_shape.md
CHANGED
|
@@ -49,6 +49,13 @@ screen** for `layer="top"` — the visual viewport minus the app's bands (see
|
|
|
49
49
|
[`safe_area.md`](./safe_area.md)) — and the positioned ancestor for
|
|
50
50
|
`layer="local"`.
|
|
51
51
|
|
|
52
|
+
A modal dialog covered by another modal dialog is not in use, so it does not
|
|
53
|
+
follow the keyboard: it stays where it is, and the keyboard covers it as it
|
|
54
|
+
covers anything else behind that sheet. Once back on top it answers the screen
|
|
55
|
+
as it is: at once when the focus landed on a field, since the keyboard stays;
|
|
56
|
+
once the keyboard has left otherwise, so it never jumps up and back down. What
|
|
57
|
+
sits under a non-modal dialog is still usable, and keeps following the keyboard.
|
|
58
|
+
|
|
52
59
|
## One dialog, two shapes
|
|
53
60
|
|
|
54
61
|
`dockedOnSmallTouchScreen` is the whole small-screen story in one prop: on a
|
package/docs/drag_to_travel.md
CHANGED
|
@@ -1,78 +1,93 @@
|
|
|
1
|
-
# Travelling
|
|
1
|
+
# Travelling: a hand, a screen, and who owns the gesture
|
|
2
2
|
|
|
3
|
-
A travel is a
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
A travel is a hand pushing a whole screen aside to bring in the next one. There
|
|
4
|
+
are two families, and they do not work the same way:
|
|
5
|
+
|
|
6
|
+
- **A row of screens** (`SlideContainer`, `RouteTravel`) is a real scroller with
|
|
7
|
+
one snap point per screen. The browser reads the hand. See
|
|
8
|
+
[The browser scrolls the row](#the-browser-scrolls-the-row).
|
|
9
|
+
- **A box pushed aside** (a docked `Dialog` or a `SidePanel` pushed back to close
|
|
10
|
+
it, a row swiped with `interactions`) has nothing to scroll to, so the press is
|
|
11
|
+
read by one module, `drag_to_travel.js` in @jsenv/dom. See
|
|
12
|
+
[A box pushed aside: the press](#a-box-pushed-aside-the-press).
|
|
9
13
|
|
|
10
14
|
What an application has to know is who gets a press that lands inside such a
|
|
11
|
-
box. It is decided
|
|
15
|
+
box. It is decided before anything moves, so read
|
|
12
16
|
[Who owns a gesture](#who-owns-a-gesture) before putting anything that reads
|
|
13
17
|
the pointer inside a box that travels.
|
|
14
18
|
|
|
15
|
-
- [
|
|
19
|
+
- [The browser scrolls the row](#the-browser-scrolls-the-row)
|
|
20
|
+
- [A box pushed aside: the press](#a-box-pushed-aside-the-press)
|
|
16
21
|
- [Who owns a gesture](#who-owns-a-gesture)
|
|
17
22
|
- [Boxes inside boxes](#boxes-inside-boxes)
|
|
18
23
|
- [Something being carried inside a box](#something-being-carried-inside-a-box)
|
|
19
24
|
- [A surface in the top layer](#a-surface-in-the-top-layer)
|
|
20
25
|
- [A navi component that reads the pointer marks ITSELF](#a-navi-component-that-reads-the-pointer-marks-itself)
|
|
21
26
|
- [What is contained, and what still leaks](#what-is-contained-and-what-still-leaks)
|
|
22
|
-
- [The two
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
- [The two rows](#the-two-rows)
|
|
28
|
+
|
|
29
|
+
## The browser scrolls the row
|
|
30
|
+
|
|
31
|
+
A touch pans on the compositor, a trackpad follows the fingers, a wheel is read
|
|
32
|
+
for what it is. So a row answers a hand the way any scroll snap carousel on the
|
|
33
|
+
web does, and these are its rules, not ours:
|
|
34
|
+
|
|
35
|
+
- **A trackpad or a finger holds the screen.** Let go past half the box, or with
|
|
36
|
+
momentum, it goes; let go short of that with the hand at rest, it comes back.
|
|
37
|
+
- **One notch of a wheel is one screen**, however small the notch. Only the
|
|
38
|
+
browser can tell a notch from a trackpad: the page cannot.
|
|
39
|
+
- **A big swipe can cross more than one screen.** `scroll-snap-stop` stops the
|
|
40
|
+
momentum at the next screen, not a finger still pushing past it. With
|
|
41
|
+
`mount="near"`, a screen reached ahead of its neighbours is built on the way, so
|
|
42
|
+
it can show empty for a moment.
|
|
43
|
+
- **A stroke stays on what it started on.** A vertical scroll cannot change
|
|
44
|
+
screen, whatever it leans towards on the way. The price is the mirror case: a
|
|
45
|
+
stroke that opens on a sideways crumb is the row's, and the vertical scroll it
|
|
46
|
+
turns into does nothing until the hand lifts.
|
|
47
|
+
- **A mouse does not drag**, as it does not scroll anything else on the web.
|
|
48
|
+
|
|
49
|
+
What stays ours:
|
|
50
|
+
|
|
51
|
+
- **A travel asked for** (a tab, a key, a command, a state) plays in `duration`,
|
|
52
|
+
one box long whatever the distance, in `SlideContainer`. In `RouteTravel` it is
|
|
53
|
+
the browser's smooth scroll.
|
|
54
|
+
- **A direction that is refused is the end of the row.** Whatever holds the user
|
|
55
|
+
where they are (a `<Slide required>` waiting for its answer, a `preventNavNext`)
|
|
56
|
+
takes what lies beyond off the row, so nothing can be scrolled to. There is no
|
|
57
|
+
screen walked to and then taken back.
|
|
58
|
+
- **What a box cannot say to a scroller is said per axis.** `travelByDrag` is about
|
|
59
|
+
a finger, `travelByScroll` about a wheel or a trackpad: off on an axis, that hand
|
|
60
|
+
is handed on to what scrolls inside, then around.
|
|
61
|
+
|
|
62
|
+
**`position: sticky` does not stick inside a `RouteTravel` row of two pages or more
|
|
63
|
+
while the document scrolls.** A horizontal scroller cannot let the other axis
|
|
64
|
+
through, so the row is what sticky sticks to, and the row never scrolls vertically.
|
|
65
|
+
A page that scrolls by itself inside the row is not concerned.
|
|
66
|
+
|
|
67
|
+
## A box pushed aside: the press
|
|
26
68
|
|
|
27
69
|
- A press is not a gesture until it has wandered 10px with a mouse, 6px with a
|
|
28
|
-
finger or a pen, and the axis it leans on then is the axis it walks, for good
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
pointer it has already cancelled (see
|
|
70
|
+
finger or a pen, and the axis it leans on then is the axis it walks, for good.
|
|
71
|
+
The reading leans towards the box's own axis: a thumb swiping sideways moves
|
|
72
|
+
along an arc, and its first pixels lean off-axis far more than the swipe does,
|
|
73
|
+
so the cross axis takes the press only when it clearly dominates. Where
|
|
74
|
+
something under the finger scrolls on that axis, the page included, the reading
|
|
75
|
+
is even, the way the browser's is: a scroll that wobbles stays a scroll, and a
|
|
76
|
+
swipe whose first pixels are steeper than 45° scrolls too. A finger's 6px is a
|
|
77
|
+
deadline rather than a feel: the browser commits a touch to its own pan about
|
|
78
|
+
8px in, and whatever is read after that is read about a pointer it has already
|
|
79
|
+
cancelled (see
|
|
39
80
|
[mobile_touch.md](./mobile_touch.md#the-browser-decides-about-8px-in-and-does-not-wait)).
|
|
40
81
|
Its price is the click of a tap that shook more than 6px.
|
|
41
|
-
- **
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- **Letting go asks where the hand is about to put things.** Where the picture
|
|
49
|
-
stands, plus half a second at the speed the fingertip has as it lifts, against
|
|
50
|
-
half the box. A flick lands far and goes. A hand thrown back lands short and
|
|
51
|
-
everything goes back, otherwise a screen caught in flight and thrown back
|
|
52
|
-
still arrives. A hand that slowed to a stop has no speed left, and the picture
|
|
53
|
-
answers alone. Speed counts from 25px of finger, a tap that slipped not being
|
|
54
|
-
a flick, and past 0.4 px/ms it answers on its own, or half a wide box would be
|
|
55
|
-
out of reach of any flick.
|
|
82
|
+
- **Letting go asks where the hand is about to put things.** Where the box stands,
|
|
83
|
+
plus half a second at the speed the fingertip has as it lifts, against half the
|
|
84
|
+
box. A flick lands far and goes. A hand thrown back lands short and everything
|
|
85
|
+
goes back. A hand that slowed to a stop has no speed left, and the picture
|
|
86
|
+
answers alone. Speed counts from 25px of finger, a tap that slipped not being a
|
|
87
|
+
flick, and past 0.4 px/ms it answers on its own, or half a wide box would be out
|
|
88
|
+
of reach of any flick.
|
|
56
89
|
- Pulling towards nothing follows the finger at a fraction of its distance and
|
|
57
90
|
comes back: a wall one can lean on, never walk through.
|
|
58
|
-
- **A direction that will be refused is one of those walls, and it must be a
|
|
59
|
-
wall from the first pixel.** Whatever holds the user where they are — a
|
|
60
|
-
`<Slide required>` waiting for its answer, a `preventNavNext` — is read where
|
|
61
|
-
the gesture is ARMED, not only when it is let go of. Read at the release
|
|
62
|
-
alone, the screen one may not reach is walked to, read on the way, and then
|
|
63
|
-
taken back: a wall that shows what is behind it and pushes you back reads as a
|
|
64
|
-
bug even when it is a rule. What a lock answers is therefore not "is there a
|
|
65
|
-
screen that way" but "may I go there".
|
|
66
|
-
- **A hand that walks a whole box and keeps going is asking for the next one**,
|
|
67
|
-
and the gesture walks on into it without being let go of.
|
|
68
|
-
- **A wheel push moves one screen**, the way an arrow key does — never a
|
|
69
|
-
distance. What keeps arriving after the fingers are gone is momentum and is
|
|
70
|
-
not counted: a second screen takes a second push, or one that insists.
|
|
71
|
-
- **A press on a travel still moving is the next swipe first.** A step the way
|
|
72
|
-
it is going asks for the screen after, and the travel goes on there without
|
|
73
|
-
ever having stopped; only a step against it, or a finger saying nothing for
|
|
74
|
-
about 100ms, catches it where it stands. Let go before either — a tap — and
|
|
75
|
-
the travel was never touched.
|
|
76
91
|
|
|
77
92
|
How to drive a gesture without a device, and why the obvious way hides the race
|
|
78
93
|
that loses swipes on a phone, is
|
|
@@ -80,8 +95,10 @@ that loses swipes on a phone, is
|
|
|
80
95
|
|
|
81
96
|
## Who owns a gesture
|
|
82
97
|
|
|
83
|
-
|
|
84
|
-
|
|
98
|
+
A row of screens leaves this to the browser, which hands a touch to the innermost
|
|
99
|
+
scroller that can take it. A box pushed aside reads it itself: five things can
|
|
100
|
+
claim a pointer that landed on it, and all five are read at the press, before the
|
|
101
|
+
box moves:
|
|
85
102
|
|
|
86
103
|
1. **What says so itself.** What the browser already answers the pointer on — a
|
|
87
104
|
field, a `contenteditable`, a `select`, a range; a dedicated drag handle
|
|
@@ -118,20 +135,17 @@ is not pushed back at all. A `SidePanel` is pushed from its whole surface.
|
|
|
118
135
|
### Boxes inside boxes
|
|
119
136
|
|
|
120
137
|
A row of slides inside a page that walks between pages, a carousel inside a
|
|
121
|
-
carousel, a `SlideContainer` inside a `RouteTravel`:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
So an inner box sitting on its last slide does not hand the gesture over
|
|
133
|
-
mid-drag: it leans on its wall, the way it does when it is alone. Travelling the
|
|
134
|
-
box around it means starting the gesture outside it.
|
|
138
|
+
carousel, a `SlideContainer` inside a `RouteTravel`: the innermost is the one the
|
|
139
|
+
hand is pointing at. It takes the gesture on the axis it walks, and the boxes
|
|
140
|
+
around it keep whatever axis it does not, so a row swiped sideways inside a column
|
|
141
|
+
of screens keeps the sideways gesture, and the column still answers a finger going
|
|
142
|
+
down.
|
|
143
|
+
|
|
144
|
+
Decided when the stroke starts, once and for all. An inner row sitting on its last
|
|
145
|
+
slide does not hand the gesture over: it leans on its wall, the way it does when
|
|
146
|
+
it is alone. Travelling the box around it means starting the gesture outside it. A
|
|
147
|
+
box pushed aside reads the axes of what is inside it in the DOM
|
|
148
|
+
(`data-travel-by-drag`).
|
|
135
149
|
|
|
136
150
|
When the boxes say ONE place between them — a row of titles in each slide of a
|
|
137
151
|
column of levels, the address naming a title — they bind one state: see
|
|
@@ -199,68 +213,37 @@ than while a finger is down, because a browser decides what a gesture may do
|
|
|
199
213
|
when the gesture begins; the bounce is refused only while a gesture runs, so a
|
|
200
214
|
page that bounces the rest of the time goes on bouncing.
|
|
201
215
|
|
|
202
|
-
What still leaks, only on Blink (Chrome, Edge), and only under a box
|
|
203
|
-
a scroll container
|
|
204
|
-
|
|
205
|
-
bare `<div style="overflow: auto">`, a widget from elsewhere. Its leftovers
|
|
216
|
+
What still leaks, only on Blink (Chrome, Edge), and only under a box pushed aside,
|
|
217
|
+
which is not a scroll container: a scroller **nobody declared and no tag names** —
|
|
218
|
+
a bare `<div style="overflow: auto">`, a widget from elsewhere. Its leftovers
|
|
206
219
|
reach the page. Two ways out, per case: give the scroller a `Box` with an
|
|
207
220
|
`overflow` (it is then declared), or contain it by hand
|
|
208
221
|
(`overscroll-behavior-x: contain` under a box travelling sideways). The general
|
|
209
222
|
fix belongs to Blink.
|
|
210
223
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
|
218
|
-
|
|
|
219
|
-
| what
|
|
220
|
-
| what says
|
|
221
|
-
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
what
|
|
229
|
-
`--slide-travel-progress`
|
|
230
|
-
in boxes: 0 on it, +1 a whole box before it, -1 one after
|
|
224
|
+
A row of screens is a scroll container, and contains itself.
|
|
225
|
+
|
|
226
|
+
## The two rows
|
|
227
|
+
|
|
228
|
+
| | `SlideContainer` | `RouteTravel` |
|
|
229
|
+
| ----------------------------- | --------------------------------- | ------------------------------------------- |
|
|
230
|
+
| what the screens are | `<Slide>`s in one box | routes |
|
|
231
|
+
| what says which is here | a `signal`, `current` / a command | the URL |
|
|
232
|
+
| what a screen not on it shows | itself, built or `mount="near"` | itself once visited; before that, see below |
|
|
233
|
+
| what says the order | the layout map | the `<Route>` children, or `routes` |
|
|
234
|
+
| a travel asked for | plays in `duration`, one box long | the browser's smooth scroll |
|
|
235
|
+
|
|
236
|
+
A `RouteTravel` page not visited yet can only be built by the address moving, since
|
|
237
|
+
a route's actions ask only while it matches. So the address moves once the hand has
|
|
238
|
+
really pulled, 16px, and the page fills in under the finger. See
|
|
239
|
+
[navigation.md](./navigation.md#tabs-that-travel-routetravel).
|
|
240
|
+
|
|
241
|
+
Both tell what follows them how far the row has come. `SlideContainer` writes
|
|
242
|
+
`--slide-travel-progress` (where the picture stands against the slide it counts
|
|
243
|
+
from, in boxes: 0 on it, +1 a whole box before it, -1 one after) on its box, for
|
|
231
244
|
anything drawn inside it, and on every element marked
|
|
232
|
-
`data-slide-container-follows={id}` outside it
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
A travel is not a queue: a tab pressed while a page is arriving does not wait
|
|
239
|
-
its turn, and does not start a second travel on top of the first either. The
|
|
240
|
-
travel in flight is aimed there instead. Back where it set off from, it is
|
|
241
|
-
undone — the same pictures, run backwards; further the same way, it costs
|
|
242
|
-
nothing; the other way, the pictures start again from the beginning, and a page
|
|
243
|
-
nearly arrived snaps back first.
|
|
244
|
-
|
|
245
|
-
A hand that walks a `RouteTravel` page whole and keeps going gets the next page,
|
|
246
|
-
but out of the far end there is a short gap: the page it was leaving is gone,
|
|
247
|
-
the next travel needs pictures of its own (a navigation, a render, a snapshot),
|
|
248
|
-
and over those frames nothing follows the finger before catching up with it.
|
|
249
|
-
Walked back out of the start there is none — the pair in hand is already the
|
|
250
|
-
right one. A `SlideContainer` has every slide mounted and hands over without a
|
|
251
|
-
gap either way.
|
|
252
|
-
|
|
253
|
-
### One gesture that bar cannot follow
|
|
254
|
-
|
|
255
|
-
A `RouteTravel` travel that turns around mid-gesture, or is aimed further the
|
|
256
|
-
same way while it plays — a tab pressed two pages along, the next swipe landing
|
|
257
|
-
on a page still arriving — keeps its pages by pointing the router elsewhere
|
|
258
|
-
under the same transition. But everything ELSE the transition carries was
|
|
259
|
-
photographed when it began, on its way to a tab nobody is going to anymore, and
|
|
260
|
-
the thing itself has already moved on in the live page: one would see two bars.
|
|
261
|
-
|
|
262
|
-
So on those gestures the pictures of everything that is not the pages are
|
|
263
|
-
dropped, and those things are left where they are, live: the bar jumps to the
|
|
264
|
-
tab one is heading for instead of sliding to a tab one is not. A slide would be
|
|
265
|
-
nicer, and it is not available — the browser measured both of its ends before
|
|
266
|
-
the hand changed its mind, and neither can be asked for again.
|
|
245
|
+
`data-slide-container-follows={id}` outside it, a tab bar above the box. It counts
|
|
246
|
+
from the current slide (`data-slide-current`), or once a hand has carried the row
|
|
247
|
+
past it from `data-slide-travel-base`, towards `data-slide-travel-toward`.
|
|
248
|
+
`RouteTravel` needs nothing: a `<Nav currentIndicator>` draws its bar under the hand
|
|
249
|
+
for any row it holds tabs for.
|
package/docs/dynamic_import.md
CHANGED
|
@@ -179,7 +179,7 @@ nothing to preload — the navigation itself asks.
|
|
|
179
179
|
|
|
180
180
|
A route element still waiting is what the container rendered — its pending
|
|
181
181
|
screen, or the fallback of the boundary it suspended into — so a route transition
|
|
182
|
-
|
|
182
|
+
takes its picture of **that**, code and data alike. navi does
|
|
183
183
|
not hold a navigation for a chunk: the honest answer to "a chunk is a hundred
|
|
184
184
|
milliseconds" is the prefetch above — the code is there before the press — and a
|
|
185
185
|
skeleton in the page's own shape is what gets photographed when it is not.
|
package/docs/interactions.md
CHANGED
|
@@ -560,6 +560,12 @@ teardown, so a travelling container above it does not take the gesture (see
|
|
|
560
560
|
has decided. One gesture cannot mean both. What they leave alone keeps its
|
|
561
561
|
drag: an element inside marked `data-no-drag-travel` reads the pointer itself.
|
|
562
562
|
An element declaring a swipe also gets `draggable={false}`.
|
|
563
|
+
- **A popup is in front of the box it was written in, not part of it.** It stays
|
|
564
|
+
a DOM descendant of its trigger, so its events bubble through that box, but a
|
|
565
|
+
hold, a swipe, a counted tap or a `"keyboard:<shortcut>"` pressed in the popup
|
|
566
|
+
is not read by the box around it. A box inside the popup, or the popup itself,
|
|
567
|
+
answers as usual. A plain `onKeyDown` still hears the event: it is the
|
|
568
|
+
browser's, so it asks `event.target` itself if it should not.
|
|
563
569
|
- **A popup can open while the finger is still down** — a menu under a waiting
|
|
564
570
|
finger is the native gesture (see
|
|
565
571
|
[popup_open.md](./popup_open.md#opening-while-the-finger-is-still-down), and
|
package/docs/mobile_touch.md
CHANGED
|
@@ -55,11 +55,11 @@ intent before about 8px**, and measure it as a distance (`Math.hypot`), not per
|
|
|
55
55
|
axis: the browser measures its slop as a distance, and a per-axis threshold is
|
|
56
56
|
up to 1.4 times later on a diagonal. Waiting for more evidence is not
|
|
57
57
|
available, because whatever is read after the browser's moment is read about
|
|
58
|
-
a pointer that has already been cancelled. It is why a
|
|
59
|
-
6px, before the browser reads the same pixels evenly: leaning towards its own
|
|
58
|
+
a pointer that has already been cancelled. It is why a box pushed aside reads a
|
|
59
|
+
finger at 6px, before the browser reads the same pixels evenly: leaning towards its own
|
|
60
60
|
axis where nothing scrolls on the other one, evenly where something does, and
|
|
61
61
|
waiting for the browser's own pixels when that reading says the pan is the
|
|
62
|
-
browser's (see [drag_to_travel.md](./drag_to_travel.md#
|
|
62
|
+
browser's (see [drag_to_travel.md](./drag_to_travel.md#a-box-pushed-aside-the-press)).
|
|
63
63
|
|
|
64
64
|
Whether a `touchmove` can be refused at all is settled even earlier, when the
|
|
65
65
|
finger lands: the non-passive listener that will refuse must already be on the
|
package/docs/navigation.md
CHANGED
|
@@ -461,10 +461,11 @@ says once, for the whole row, where the bar that marks the current tab goes:
|
|
|
461
461
|
</Nav>
|
|
462
462
|
```
|
|
463
463
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
for a movement between two tabs
|
|
464
|
+
A hand on a `RouteTravel` row carries the bar with it, for any row the `<Nav>` holds
|
|
465
|
+
tabs for. It also glides from one tab to the next when the change between them plays
|
|
466
|
+
as a route transition, because `<Nav>` names it for that movement
|
|
467
|
+
(`currentIndicatorSlides`, on by default), and only for a movement between two tabs
|
|
468
|
+
of its own row: during any other route movement
|
|
468
469
|
the bar leaves or arrives with its row, a named element inside a page that moves
|
|
469
470
|
standing still (see
|
|
470
471
|
[view_transitions.md](./view_transitions.md#a-name-is-unique-per-document)).
|
|
@@ -527,9 +528,9 @@ by a button, it is a command carrying the fallback:
|
|
|
527
528
|
|
|
528
529
|
## Tabs that travel: `RouteTravel`
|
|
529
530
|
|
|
530
|
-
`<RouteTravel>` wraps the `<Route>` tree of a row of tabs and makes
|
|
531
|
-
|
|
532
|
-
|
|
531
|
+
`<RouteTravel>` wraps the `<Route>` tree of a row of tabs and makes it a row the
|
|
532
|
+
browser scrolls, with one snap point per page: a tab pressed, the back button, and
|
|
533
|
+
a hand pushing the pages:
|
|
533
534
|
|
|
534
535
|
```jsx
|
|
535
536
|
<SectionNav />
|
|
@@ -542,41 +543,63 @@ dragging the pages:
|
|
|
542
543
|
</RouteTravel>
|
|
543
544
|
```
|
|
544
545
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
replace
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
546
|
+
A page is built when its route first matches, since a route's actions only ask
|
|
547
|
+
while it does, and stays once built: a hand brings back the page itself, with where
|
|
548
|
+
it was scrolled to. The order of the tabs — "one step that way", which no URL says —
|
|
549
|
+
is the order the children are written; the row is on the **first** of its pages
|
|
550
|
+
that matches, like a `<Route>`; a page left out of the row does not travel. A swipe
|
|
551
|
+
**replaces** the current entry, as `<Link replace>` does for a tab pressed: two
|
|
552
|
+
gestures towards the same neighbour must not write two different histories — and a
|
|
553
|
+
row of tabs is the one place a replace moves the scroll
|
|
554
|
+
([below](#a-row-of-tabs-where-a-replace-is-an-arrival)).
|
|
555
|
+
|
|
556
|
+
**A window over something longer** says its pages with `routes`, and its children
|
|
557
|
+
are then one page, rendered once, whatever the address:
|
|
558
|
+
|
|
559
|
+
```jsx
|
|
560
|
+
<RouteTravel routes={ids.map((id) => ({ route: GAME_ROUTE, params: { id } }))}>
|
|
561
|
+
<GamePage />
|
|
562
|
+
</RouteTravel>
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
That is the form for pages that are one route with other params, the same component
|
|
566
|
+
and so only one of it, and for pages that are not children of the box. What a hand
|
|
567
|
+
leaves behind is a picture of it, a clone, in the pane it stood in. `routes` may be
|
|
568
|
+
given again around the page on screen as the reader moves through the list: it is
|
|
569
|
+
taken once the row has come to rest, and nothing is seen moving for it. With fewer
|
|
570
|
+
than two pages, the box is not a scroller at all.
|
|
571
|
+
|
|
572
|
+
**The box has a height, or the document scrolls.** Pages may scroll themselves in a
|
|
573
|
+
box the page gives a height to. If the document is what scrolls, it goes on doing
|
|
574
|
+
so, and a page brought in stands where the reader is looking; what is lost is
|
|
575
|
+
`position: sticky` inside the row (see
|
|
576
|
+
[drag_to_travel.md](./drag_to_travel.md#the-browser-scrolls-the-row)).
|
|
577
|
+
|
|
578
|
+
**The URL can say where the hand is heading, not where the reader is.** For a page
|
|
579
|
+
that is not there yet, a page of a window or a tab never visited, it changes once
|
|
580
|
+
the hand has really pulled, and comes back if the page is let go of. A tab already
|
|
581
|
+
built waits for the landing. So a counter, a title or a breadcrumb around the box
|
|
582
|
+
reads `routeTravelSettledPageSignal` first, or it moves for a pull that goes
|
|
583
|
+
nowhere:
|
|
561
584
|
|
|
562
585
|
```jsx
|
|
563
586
|
const settledPage = routeTravelSettledPageSignal.value;
|
|
564
587
|
const shownId = settledPage ? settledPage.params.gameId : currentId;
|
|
565
588
|
```
|
|
566
589
|
|
|
567
|
-
It holds the `{ route, params }` the
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
there is one transition at a time: on a page with several boxes, `route` says
|
|
572
|
-
which row is travelling.
|
|
590
|
+
It holds the `{ route, params }` the hand set off from, and is `null` when the row
|
|
591
|
+
has come to rest, the URL being the truth again. A tab pressed holds nothing, and
|
|
592
|
+
`null` is all it ever reads. One signal for the document: on a page with several
|
|
593
|
+
boxes, `route` says which row it is.
|
|
573
594
|
|
|
574
595
|
`RouteTravel` is for pages that form a ROW the finger can push; pages related
|
|
575
596
|
pair by pair are animated with `defineRouteTransition`, and a given pair by one
|
|
576
597
|
of the two, never both (see
|
|
577
598
|
[route_transitions.md](./route_transitions.md#route-transitions-and-routetravel--one-pair-one-system)).
|
|
578
599
|
The gesture itself: [drag_to_travel.md](./drag_to_travel.md). Demos:
|
|
579
|
-
`src/nav/demos/route_travel/route_travel.html`,
|
|
600
|
+
`src/nav/demos/route_travel/route_travel.html`,
|
|
601
|
+
`src/nav/demos/route_travel/route_travel_window.html`,
|
|
602
|
+
`src/nav/demos/tabs/tabs.html`.
|
|
580
603
|
|
|
581
604
|
## Where a navigation lands: the scroll
|
|
582
605
|
|
|
@@ -773,7 +796,7 @@ The tabs of a `<RouteTravel>` navigate by replacing, and yet each one is another
|
|
|
773
796
|
route, sharing one scrollport — the document — that the tab on screen makes
|
|
774
797
|
tall: the moment the arriving tab is shorter, the browser clamps, and the
|
|
775
798
|
reader's place is gone. The row is the only thing that knows this, so on every
|
|
776
|
-
|
|
799
|
+
movement — a tab pressed, a hand, a page let go of and coming back — it gives the
|
|
777
800
|
arriving tab the offset it was read at, once it is really rendered; opens a tab
|
|
778
801
|
never read at its **top**, not wherever its neighbour happened to be; and never
|
|
779
802
|
records the clamp, which, recorded against the arriving tab's url, would destroy
|
package/docs/popup_open.md
CHANGED
|
@@ -576,6 +576,13 @@ that must offer a way out that KEEPS what was chosen offers it with a close
|
|
|
576
576
|
cross, or by letting the click outside close** — not by teaching Escape to say
|
|
577
577
|
something else.
|
|
578
578
|
|
|
579
|
+
**One Escape closes one popup: the one it was pressed in.** Each popup answers
|
|
580
|
+
Escape itself, and a popup around it never hears the key, so backing out of a
|
|
581
|
+
sheet in a dialog takes two presses. A `popover="manual"` element of your own has
|
|
582
|
+
no Escape to answer with: in a `layer="local"` dialog, the key does nothing while
|
|
583
|
+
the focus is in it. A modal dialog around it still closes, on the browser's own
|
|
584
|
+
close request.
|
|
585
|
+
|
|
579
586
|
### What "cancel" actually undoes
|
|
580
587
|
|
|
581
588
|
Cancelling is not itself an undo: it marks the close, and whoever holds a value
|
|
@@ -315,14 +315,7 @@ answering: the click is caught at the document and handed to it when it fell
|
|
|
315
315
|
inside its rectangle, and a navigation that is the exact way back of the movement
|
|
316
316
|
playing turns that movement round. Only on furniture that stands still: a control
|
|
317
317
|
travelling with the pages has a rectangle where it will be, not where it is seen
|
|
318
|
-
(navi warns about one inside the area).
|
|
319
|
-
finger is on the box — and leaves the bars live, so its pictures cover what a bar
|
|
320
|
-
paints outside its box, except two things photographed on their own, which stand
|
|
321
|
-
over the pages: the bar's `shadow`, and a child wearing
|
|
322
|
-
`data-navi-fixed-bar-overhang` (a raised button, the notch around it). That child
|
|
323
|
-
is deaf for the length of the travel, and the rest of the bar for its first frames,
|
|
324
|
-
when a press falls to the document whatever it was aimed at;
|
|
325
|
-
`pressableDuringRouteTransition` on a control keeps it answering in both cases.
|
|
318
|
+
(navi warns about one inside the area).
|
|
326
319
|
|
|
327
320
|
A **modal `Dialog` open on a page** is furniture of the same kind: it travels with
|
|
328
321
|
its page, or holds if both states have it. One the navigation itself opens or
|
|
@@ -461,17 +454,14 @@ once in the console, naming both routes.
|
|
|
461
454
|
|
|
462
455
|
## Route transitions and `RouteTravel` — one pair, one system
|
|
463
456
|
|
|
464
|
-
- **`RouteTravel`** is a ROW: a total order of
|
|
465
|
-
|
|
457
|
+
- **`RouteTravel`** is a ROW: a total order of pages the browser scrolls. Use it
|
|
458
|
+
when the pages are genuinely a row the finger should push.
|
|
466
459
|
- **`defineRouteTransition`** declares individual relations, with no gesture and
|
|
467
460
|
no order beyond each pair.
|
|
468
461
|
|
|
469
|
-
A given PAIR of routes
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
cross-fade would break. A travel in flight wins and the route transition is
|
|
473
|
-
skipped with a console warning: the sign of a misconfiguration to fix, not a
|
|
474
|
-
mechanism to rely on.
|
|
462
|
+
A given PAIR of routes takes one of the two: a page one can push sideways has
|
|
463
|
+
promised a translation, which a cross-fade would break. What a hand does on the row
|
|
464
|
+
asks for no movement, and a tab pressed goes the way its `<Link>` says.
|
|
475
465
|
|
|
476
466
|
The two DO live together in one app, on different pairs, including a
|
|
477
467
|
`<RouteTravel>` nested inside a `<RouteTransitionArea>` — a row of sections the
|
package/docs/scroll.md
CHANGED
|
@@ -387,7 +387,9 @@ other flush, instead of each arriving already inset.
|
|
|
387
387
|
|
|
388
388
|
Pass `travelByKeyboard={false}` when the arrow keys belong to the content (a list
|
|
389
389
|
one walks through, a picker whose slides are steps): otherwise the right arrow
|
|
390
|
-
changes screen mid-reading.
|
|
390
|
+
changes screen mid-reading. A popup opened from a slide does not need it: a key
|
|
391
|
+
pressed in the popup never walks the slides behind it (see
|
|
392
|
+
[interactions.md](./interactions.md#things-worth-knowing-before-guessing)).
|
|
391
393
|
|
|
392
394
|
Reference: `src/layout/slide_container.jsx`, and the "One slide much taller
|
|
393
395
|
than the others" case in `src/layout/demos/8_slide_container_demo.html`.
|