@jsenv/navi 0.29.463 → 0.29.465
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/README.md +1 -1
- package/dist/dev/jsenv_navi.js +2715 -1308
- package/dist/dev/jsenv_navi.js.map +22 -20
- package/dist/jsenv_navi.js +2169 -766
- package/dist/jsenv_navi.js.map +22 -20
- package/docs/AI_INSTRUCTIONS.md +5 -3
- package/docs/navigation.md +24 -9
- package/docs/popup_backdrop.md +126 -9
- package/package.json +2 -2
package/docs/AI_INSTRUCTIONS.md
CHANGED
|
@@ -147,9 +147,11 @@ start when unsure which export solves a problem.
|
|
|
147
147
|
own actions). Before passing `open`, calling `triggerNaviCommand`, or
|
|
148
148
|
writing a close button.
|
|
149
149
|
- `popup_backdrop.md` — three independent questions: a wall or not, what an
|
|
150
|
-
outside press does, how far the page withdraws;
|
|
151
|
-
|
|
152
|
-
`theme-color`
|
|
150
|
+
outside press does, how far the page withdraws; popups that hand over to
|
|
151
|
+
each other in one press (`PopupGroup`: the wall around a box of the page);
|
|
152
|
+
and the browser's `theme-color` that a popup paints over. Before CSS for a
|
|
153
|
+
backdrop, a `theme-color` meta, or making a popup's neighbours pressable
|
|
154
|
+
above its wall.
|
|
153
155
|
- `popup_lift.md` — `animation="lifting"`: the pressed card brought to the front.
|
|
154
156
|
Before giving a `Dialog` or a `Picker` `animation="lifting"`.
|
|
155
157
|
- `dialog_shape.md` — bounds rather than a width, the container ceiling, the
|
package/docs/navigation.md
CHANGED
|
@@ -511,15 +511,30 @@ on, and the position stays for the visit that brings it back. What it costs: a
|
|
|
511
511
|
slow read that every page asks for delays the position of every page, since
|
|
512
512
|
nothing tells navi which reads set the height.
|
|
513
513
|
|
|
514
|
-
A route action that starts asking later is waited for
|
|
515
|
-
state the page did not have yet (
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
and
|
|
521
|
-
|
|
522
|
-
|
|
514
|
+
A route action that starts asking later is waited for when it says what it
|
|
515
|
+
waits for: its `when`, a condition on app state the page did not have yet (the
|
|
516
|
+
signed-in user, an age gate). The position is written into what the page shows
|
|
517
|
+
and stays pending while a `when` of the page says no; the action that starts
|
|
518
|
+
once it holds is waited for the same way, and the position is put back again
|
|
519
|
+
once it is drawn. Params that say no are not waited for: they read the address,
|
|
520
|
+
and this one asks nothing of the action — when it asks later (a search typed),
|
|
521
|
+
the page has changed under the reader, and writing the old position would undo
|
|
522
|
+
whatever they or the app did meanwhile. So a gate on app state goes in `when`,
|
|
523
|
+
and the params keep what depends on the page:
|
|
524
|
+
|
|
525
|
+
```js
|
|
526
|
+
const BOOK_ACTION = routeAction(
|
|
527
|
+
BOOK_ROUTE,
|
|
528
|
+
BOOK.GET,
|
|
529
|
+
() => ({ slug: bookSlugSignal.value }),
|
|
530
|
+
{ when: () => catalogOpenSignal.value },
|
|
531
|
+
);
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
A `when` that never holds on this page (a reader who is not an admin) keeps the
|
|
535
|
+
position pending until the reader takes over. An action whose params are
|
|
536
|
+
another's answer needs nothing: the position is read again once that answer is
|
|
537
|
+
drawn, and the action asking by then is waited for.
|
|
523
538
|
|
|
524
539
|
**What navi cannot see, the page says**: a read it starts itself
|
|
525
540
|
(`useAsyncData(…, { run: true })`), content whose height arrives late. While
|
package/docs/popup_backdrop.md
CHANGED
|
@@ -55,10 +55,12 @@ absorb at all.
|
|
|
55
55
|
|
|
56
56
|
**It is what decides whether a popup is modal.** `Popover` is never modal,
|
|
57
57
|
either layer, and neither is a `layer="local"` dialog. A top-layer `Dialog` is
|
|
58
|
-
modal exactly when it has a wall: with one it is `showModal()`'d and
|
|
59
|
-
makes everything behind genuinely inert; without one it goes to the
|
|
60
|
-
layer through the Popover API, placed against the screen just the
|
|
61
|
-
page that stays live
|
|
58
|
+
modal exactly when it has a wall of its own: with one it is `showModal()`'d and
|
|
59
|
+
the browser makes everything behind genuinely inert; without one it goes to the
|
|
60
|
+
same top layer through the Popover API, placed against the screen just the
|
|
61
|
+
same, over a page that stays live — or, in a `PopupGroup`, over a page the
|
|
62
|
+
group's wall covers (see
|
|
63
|
+
[Popups that hand over to each other](#popups-that-hand-over-to-each-other-popupgroup)). `Popup` forwards `backdrop` in both modes, so which of
|
|
62
64
|
popover or dialog the small-screen resolution picks says nothing about whether
|
|
63
65
|
one press or two are needed.
|
|
64
66
|
|
|
@@ -77,14 +79,17 @@ Going `layer="local"` to keep the map live instead confines the sheet to the box
|
|
|
77
79
|
it was declared in — full width and flush against _that_, which on a partial
|
|
78
80
|
container reads as a sheet that failed rather than as a sheet.
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
the one thing only a modal gets natively: the
|
|
82
|
-
longer dismisses it. No web API hooks into that outside the browser's own
|
|
82
|
+
Outside a `PopupGroup`, what a dialog that is not modal — wall-less, or
|
|
83
|
+
`layer="local"` — gives up is the one thing only a modal gets natively: the
|
|
84
|
+
hardware/gesture back button no longer dismisses it. No web API hooks into that outside the browser's own
|
|
83
85
|
modal-dismissal stack, which only a genuine `showModal()` element joins: an
|
|
84
86
|
accepted limitation, not an oversight. A wall-less `Dialog` does not trap the
|
|
85
87
|
focus either, deliberately — a page meant to be reachable is meant to be
|
|
86
88
|
reachable with the keyboard too. (A `SidePanel` that closes on an outside press
|
|
87
|
-
does trap Tab in popover mode: it sets `focusCapture`.)
|
|
89
|
+
does trap Tab in popover mode: it sets `focusCapture`.) In a group the group is
|
|
90
|
+
the modal: the back gesture closes the open popup as a cancel, and the keyboard
|
|
91
|
+
stays in the group (see
|
|
92
|
+
[Popups that hand over to each other](#popups-that-hand-over-to-each-other-popupgroup)).
|
|
88
93
|
|
|
89
94
|
`pressOutside="capture"` and `backdrop={false}` contradict each other —
|
|
90
95
|
absorbing is what a wall does — and navi warns rather than silently behaving
|
|
@@ -183,7 +188,8 @@ beside the board still closes when a card is pressed. Unlike the outside marker,
|
|
|
183
188
|
this one answers for its whole subtree: it adds a whole thing to the popup's
|
|
184
189
|
ground — the card, its count, its buttons — and nothing in it is a dismissal. It
|
|
185
190
|
reads a press the page hears, so it belongs with a popup that has no wall
|
|
186
|
-
(`backdrop={false}`)
|
|
191
|
+
(`backdrop={false}`), or with a popup of a `PopupGroup` and a box of that group:
|
|
192
|
+
behind a wall, no card is pressable.
|
|
187
193
|
|
|
188
194
|
### `pointer-events: none` and `inert` are not this
|
|
189
195
|
|
|
@@ -196,6 +202,116 @@ the marked box, either makes the marker unreachable (navi warns about
|
|
|
196
202
|
`pointer-events: none` is the one useful case: the press falls to the marked
|
|
197
203
|
box, which is the answer wanted.
|
|
198
204
|
|
|
205
|
+
## Popups that hand over to each other: `PopupGroup`
|
|
206
|
+
|
|
207
|
+
A wall spends the first press, and between popups that follow one another that
|
|
208
|
+
is the wrong press to spend. On a row of score cells, each a picker, going from
|
|
209
|
+
one cell to the next costs a press to dismiss and a second one to open, though
|
|
210
|
+
the user pointed at the next cell straight away. `backdrop={false}` gives the
|
|
211
|
+
press back, but leaves the whole page live; `"invisible"` hides the dim and
|
|
212
|
+
gives nothing back.
|
|
213
|
+
|
|
214
|
+
`PopupGroup` keeps the wall and moves it: around a box of the page, with that
|
|
215
|
+
box in front.
|
|
216
|
+
|
|
217
|
+
```jsx
|
|
218
|
+
<PopupGroup backdropColor="rgb(6 10 20 / 60%)">
|
|
219
|
+
<Picker mode="popover">…</Picker>
|
|
220
|
+
<Picker mode="popover">…</Picker>
|
|
221
|
+
<Button action={addSet}>+</Button>
|
|
222
|
+
</PopupGroup>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
While one of its popups is open, what the group holds stays lit and answers for
|
|
226
|
+
real — hover, the pressed state, a gesture, the focus — and the rest of the
|
|
227
|
+
screen is behind the wall, which can now be dimmed: the screen says what
|
|
228
|
+
answers. A row of score cells, a menu bar, a toolbar, the players of a court
|
|
229
|
+
each opening a bubble.
|
|
230
|
+
|
|
231
|
+
### Inside is the DOM
|
|
232
|
+
|
|
233
|
+
A popup is a member by being rendered inside the group: no name, no id.
|
|
234
|
+
`Popover`, `Dialog` and what is built on them count — `Popup`, `SidePanel`,
|
|
235
|
+
`SplitButton`, a `Picker`'s popover, dialog or callout. Every other control the
|
|
236
|
+
group holds is pressable too: a "+" or a delete button needs nothing.
|
|
237
|
+
|
|
238
|
+
**What is not a descendant of the group is behind the wall, wherever it is
|
|
239
|
+
painted.** While a popup of the group is open, the group is the modal, and the
|
|
240
|
+
browser makes everything else inert: a neighbour lifted above the wall with a
|
|
241
|
+
`z-index`, or rendered elsewhere through a portal, is painted and dead.
|
|
242
|
+
|
|
243
|
+
A popup opened from inside a member is in front of it, not a peer of it: the
|
|
244
|
+
group stops at each popup. What is lit is the group's whole box, its background
|
|
245
|
+
and border with it, so the group is the smallest box that holds the peers — on a
|
|
246
|
+
court that also draws its links and its empty seats, a layer that holds only
|
|
247
|
+
the players.
|
|
248
|
+
|
|
249
|
+
### What a press does while one is open
|
|
250
|
+
|
|
251
|
+
- On another popup's trigger: the open popup closes — a `Picker` notes its
|
|
252
|
+
value — and that one opens, in the same press.
|
|
253
|
+
- On anything else in the group: the open popup closes, and the press does what
|
|
254
|
+
it does there.
|
|
255
|
+
- On the wall: the open popup closes, and nothing else hears the press.
|
|
256
|
+
- Inside the open popup, or on a box marked `data-navi-popup-inside` for it:
|
|
257
|
+
nothing closes.
|
|
258
|
+
|
|
259
|
+
One popup of the group is open at a time: a press on another trigger, or a
|
|
260
|
+
popup opened from the keyboard or from code, asks the open one to close first.
|
|
261
|
+
A popup that refuses to close (a value it cannot note) stays open: it keeps the
|
|
262
|
+
press, and nothing else opens; one opened from the keyboard or from code opens
|
|
263
|
+
beside it.
|
|
264
|
+
|
|
265
|
+
**`pressOutside` stays the popup's, read against a wall that is always there.**
|
|
266
|
+
`"close"` and `"cancel"` work as everywhere, `"capture"` spends the press and
|
|
267
|
+
stays open, and `"ignore"` closes like `"close"`: there is no page under it to
|
|
268
|
+
reach. `backdrop={false}` on a member changes nothing.
|
|
269
|
+
|
|
270
|
+
### The group is the modal, its popups are not
|
|
271
|
+
|
|
272
|
+
A `Dialog` in a group is never `showModal()`'d: it goes to the top layer
|
|
273
|
+
through the Popover API, above the group. It keeps what a modal gives all the
|
|
274
|
+
same, since the group is one: everything outside is inert, the keyboard reaches
|
|
275
|
+
only what the group holds, and Escape and the back gesture close the open popup
|
|
276
|
+
as a cancel, wherever the focus is in the group. A group works inside a sheet,
|
|
277
|
+
and closes with it.
|
|
278
|
+
|
|
279
|
+
**The wall is the group's.** `backdropVariant`, `backdropColor` and
|
|
280
|
+
`backdropFilter` go on `<PopupGroup>`, whose default paint is the `close` pair,
|
|
281
|
+
like a top-layer `Dialog`'s. A member's own are not read, and navi warns in
|
|
282
|
+
dev; neither is what its `"capture"` or its `animation="lifting"` would paint —
|
|
283
|
+
`backdropVariant="lift"` on the group is that wall. Above the screen, the
|
|
284
|
+
browser's colour is the group's wall over the page's colour, and the group's
|
|
285
|
+
`themeColor` answers for it; a member flush with the top edge still lays its
|
|
286
|
+
surface over that, and a member's own `themeColor`, when it names a colour,
|
|
287
|
+
gives the colour while it is open.
|
|
288
|
+
|
|
289
|
+
**The page behind stays still while a `Popover` or a `Dialog` of the group is
|
|
290
|
+
open**, the wheel or the finger on the wall or over a part of the group with
|
|
291
|
+
nothing to scroll. A callout of the group lets the page scroll, as a callout
|
|
292
|
+
does anywhere, and goes away on that scroll when told to
|
|
293
|
+
(`calloutCloseByScroll`) — where the group stands in the page's own scroll. In
|
|
294
|
+
a sheet, a list that scrolls on its own or a fixed bar, a gesture on the wall
|
|
295
|
+
could only scroll the document behind, never the box the group stands in: the
|
|
296
|
+
page stays still there too, and a wheel over the bubble still scrolls that
|
|
297
|
+
box.
|
|
298
|
+
|
|
299
|
+
### While one is open, the group leaves the page
|
|
300
|
+
|
|
301
|
+
To stand in front, the group is shown in the top layer where it stands. An
|
|
302
|
+
invisible placeholder keeps its place in the page — the element just before it,
|
|
303
|
+
marked `navi-out-of-flow` (see
|
|
304
|
+
[`css_architecture.md`](./css_architecture.md#counting-children-what-navi-puts-in-your-tree))
|
|
305
|
+
— and the group stays pinned to that place: it follows a scroll, a resize, a
|
|
306
|
+
sheet that moves, and is clipped as its ancestors clip it. At rest it is a
|
|
307
|
+
`<dialog>` the app styles as it would a `<div>`.
|
|
308
|
+
|
|
309
|
+
Two things follow from the group being the modal. A `Dialog` rendered among
|
|
310
|
+
the group's peers is a member, never modal; one opened from inside a member is
|
|
311
|
+
in front of it, and modal as anywhere. And a drag that starts in the group
|
|
312
|
+
while one of its popups is open cannot land outside it until that press is
|
|
313
|
+
over.
|
|
314
|
+
|
|
199
315
|
## Painting one popup: two props
|
|
200
316
|
|
|
201
317
|
```jsx
|
|
@@ -333,6 +449,7 @@ renderer:
|
|
|
333
449
|
| `Dialog layer="top"` | the native `::backdrop` pseudo-element of the `<dialog>` |
|
|
334
450
|
| `Dialog layer="local"` | a sibling `div.navi_dialog_backdrop`, before the dialog's wrapper |
|
|
335
451
|
| `Popover` | a sibling `.navi_popover_backdrop` |
|
|
452
|
+
| a popup of a group | the native `::backdrop` of the `PopupGroup`'s `<dialog>` |
|
|
336
453
|
|
|
337
454
|
The pseudo-element inherits custom properties from the dialog — which is what
|
|
338
455
|
makes the tokens above reach it at all. The sibling elements do not: they
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/navi",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.465",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Library of components including navigation to create frontend applications",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"preact": "^11.0.0-rc.2"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@jsenv/dom": "0.17.
|
|
37
|
+
"@jsenv/dom": "0.17.91",
|
|
38
38
|
"@jsenv/humanize": "1.8.7",
|
|
39
39
|
"@jsenv/validity": "0.4.5"
|
|
40
40
|
},
|