@jsenv/navi 0.29.418 → 0.29.419
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 +192 -43
- package/dist/dev/jsenv_navi.js.map +10 -10
- package/dist/jsenv_navi.js +190 -41
- package/dist/jsenv_navi.js.map +10 -10
- package/docs/AI_INSTRUCTIONS.md +177 -261
- package/docs/actions.md +164 -251
- package/docs/autofocus.md +66 -83
- package/docs/control_object.md +117 -214
- package/docs/control_value.md +121 -185
- package/docs/create_and_edit.md +159 -254
- package/docs/css_architecture.md +196 -260
- package/docs/data_states.md +41 -44
- package/docs/dialog_shape.md +91 -134
- package/docs/drag_interactions.md +115 -174
- package/docs/drag_to_travel.md +10 -9
- package/docs/dynamic_import.md +119 -136
- package/docs/error_handling.md +75 -127
- package/docs/field_validation.md +49 -89
- package/docs/form_changed.md +61 -78
- package/docs/group.md +141 -0
- package/docs/i18n.md +31 -76
- package/docs/interactions.md +6 -4
- package/docs/list_action.md +36 -51
- package/docs/list_refresh.md +73 -115
- package/docs/mobile_touch.md +56 -62
- package/docs/navigation.md +377 -723
- package/docs/network_policy.md +61 -75
- package/docs/pan_zoom.md +62 -103
- package/docs/popup_backdrop.md +128 -168
- package/docs/popup_lift.md +179 -268
- package/docs/popup_open.md +380 -651
- package/docs/resource.md +228 -408
- package/docs/route_transitions.md +333 -482
- package/docs/safe_area.md +102 -89
- package/docs/scroll.md +175 -335
- package/docs/state_binding.md +2 -1
- package/docs/testid.md +18 -23
- package/docs/typography.md +111 -141
- package/docs/view_transitions.md +106 -104
- package/docs/z_index.md +61 -109
- package/package.json +2 -2
- package/docs/badge_list.md +0 -83
- package/docs/control_group.md +0 -168
- package/docs/mobile_layout_pitfalls.md +0 -145
package/docs/data_states.md
CHANGED
|
@@ -27,10 +27,9 @@ whether a missing list is a skeleton, a sentence or an illustration.
|
|
|
27
27
|
|
|
28
28
|
Without options, `useAsyncData` **delegates**: it suspends while the data is not
|
|
29
29
|
there and throws when the action failed, so the states are drawn by the
|
|
30
|
-
`<Loading>` and `<ErrorBoundary>` above rather than by the component.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
value the component reads:
|
|
30
|
+
`<Loading>` and `<ErrorBoundary>` above rather than by the component. Each
|
|
31
|
+
option turns one of those from something done _to_ the component into a value
|
|
32
|
+
the component reads:
|
|
34
33
|
|
|
35
34
|
| passed | the wait | the failure |
|
|
36
35
|
| --------------- | ------------------------- | ---------------------------- |
|
|
@@ -44,24 +43,19 @@ suspends.** Not while the action runs, not while a debounced binding is
|
|
|
44
43
|
settling, not while it holds params nobody has started yet, not after a failure
|
|
45
44
|
whose message was dismissed. Every one of those comes back as a value.
|
|
46
45
|
|
|
47
|
-
That is
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
`error: true` says the same thing about failure, and it has the same
|
|
56
|
-
consequence: the component stays, so it is still there to offer the way out.
|
|
46
|
+
That is what keeps the component alive. Suspending does not draw a spinner over
|
|
47
|
+
a subtree: it takes the subtree away and puts the boundary's fallback in its
|
|
48
|
+
place, and everything under it is remounted when it comes back — a dialog open
|
|
49
|
+
inside it leaves and is reopened, a scroll position is lost, half-typed text is
|
|
50
|
+
gone, and the retry button the component was offering disappears with it.
|
|
51
|
+
`error: true` says the same thing about failure: the component stays, so it is
|
|
52
|
+
still there to offer the way out.
|
|
57
53
|
|
|
58
54
|
A component that delegates nothing needs no `<Loading>` above it. One that
|
|
59
55
|
delegates its wait does, since that is where the suspension lands, and navi says
|
|
60
56
|
so rather than leaving the subtree blank. Inside a popup that boundary is the
|
|
61
|
-
popup's own
|
|
62
|
-
|
|
63
|
-
the page it copies, and the popup does not open at all (see
|
|
64
|
-
[popup_open.md](./popup_open.md#a-popup-that-loads-data)).
|
|
57
|
+
popup's own, never the one above the page
|
|
58
|
+
([popup_open.md](./popup_open.md#a-popup-that-loads-data)).
|
|
65
59
|
|
|
66
60
|
## `data` and `loading` are independent
|
|
67
61
|
|
|
@@ -75,28 +69,28 @@ means something different:
|
|
|
75
69
|
| set | `false` | settled | the content |
|
|
76
70
|
| `undefined` | `false` | nothing here, nothing coming | an empty state |
|
|
77
71
|
|
|
78
|
-
Two consequences worth stating out loud.
|
|
79
|
-
|
|
80
72
|
**A first load is only a first load when nothing is known.** A resource `GET`
|
|
81
73
|
whose params name a row the store already holds — read earlier by a `GET_MANY`,
|
|
82
|
-
a relation, a list — starts on the second line, not the first: the row is
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
74
|
+
a relation, a list — starts on the second line, not the first: the row is
|
|
75
|
+
drawn, the request goes out behind it, and a failure lands beside it like any
|
|
76
|
+
refresh failure. A row known from the previous page load counts too, when the
|
|
77
|
+
resource asks for it:
|
|
78
|
+
[`persist`](./resource.md#persist-the-last-answer-drawn-again-after-a-reload)
|
|
79
|
+
puts a reload on the second line as well. A `GET` bound through a signal — a
|
|
80
|
+
route action, a `bindParams` holding one — is on the second line when the signal
|
|
81
|
+
moves to params it has not answered yet, with the previous params' row: its
|
|
82
|
+
`data` stays that row until the new answer lands, whether or not the store holds
|
|
83
|
+
the new one.
|
|
89
84
|
|
|
90
85
|
**The emptiness test is `data === undefined`, never `loading`.** Reading
|
|
91
86
|
`loading` as "there is nothing to display" blanks the screen on every refresh —
|
|
92
|
-
for a checkbox ticked on one row of a list, the whole list goes.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
before dereferencing (`items?.map`, `items ?? []`, or the branch above).
|
|
87
|
+
for a checkbox ticked on one row of a list, the whole list goes. A list is no
|
|
88
|
+
exception: a plural action — `GET_MANY`, a `.many()` or `.scopedMany()` read —
|
|
89
|
+
has no data until its first answer, `undefined`, not `[]`. An empty array is an
|
|
90
|
+
answer ("the collection is empty"), and the two are drawn differently: a
|
|
91
|
+
skeleton for the first, an empty state for the second. So a component reading a
|
|
92
|
+
plural action accepts `undefined` before dereferencing (`items?.map`,
|
|
93
|
+
`items ?? []`, or the branch below).
|
|
100
94
|
|
|
101
95
|
**A skeleton is told whether it is loading; it does not deduce it.** The last
|
|
102
96
|
row of the table is the one that gets forgotten, and it is reachable: a first
|
|
@@ -112,16 +106,19 @@ if (items === undefined) {
|
|
|
112
106
|
return <ItemList items={items} busy={loading} />;
|
|
113
107
|
```
|
|
114
108
|
|
|
109
|
+
`loading` is not proof that a request is out, either: an action holding params
|
|
110
|
+
that nothing started — idle, or aborted — reads `loading: true`, so a skeleton
|
|
111
|
+
told it shimmers until something runs that action.
|
|
112
|
+
|
|
115
113
|
navi's own placeholders take that flag rather than inventing it: `<Text loading>`
|
|
116
|
-
shimmers, `<Text skeleton>` is the same bar held still, and
|
|
117
|
-
loading={loading}>` is one that tells the truth either way.
|
|
114
|
+
shimmers, `<Text skeleton>` is the same bar held still, and
|
|
115
|
+
`<Text skeleton loading={loading}>` is one that tells the truth either way.
|
|
118
116
|
|
|
119
|
-
**Knowing the count beforehand beats guessing it.**
|
|
120
|
-
|
|
121
|
-
a
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
response lands.
|
|
117
|
+
**Knowing the count beforehand beats guessing it.** A screen that already knows
|
|
118
|
+
how many rows are coming — a count carried by the parent resource, a total
|
|
119
|
+
returned alongside a previous page — says so, and
|
|
120
|
+
`<List loading loadingSkeletonCount={0}>` draws the empty state right away
|
|
121
|
+
instead of three rows that will vanish.
|
|
125
122
|
|
|
126
123
|
## An error is a message on the screen, not the screen
|
|
127
124
|
|
|
@@ -156,7 +153,7 @@ drawn, not as a stronger version of the same thing. Which of the two, and where
|
|
|
156
153
|
an error goes when neither takes it, is
|
|
157
154
|
[error_handling.md](./error_handling.md).
|
|
158
155
|
|
|
159
|
-
##
|
|
156
|
+
## See also
|
|
160
157
|
|
|
161
158
|
- [error_handling.md](./error_handling.md) — the two kinds of error, where each
|
|
162
159
|
is shown, and what happens to one nobody displays.
|
package/docs/dialog_shape.md
CHANGED
|
@@ -16,11 +16,10 @@ Where a `Dialog` sits and how big it gets. What happens _inside_ it (scrolling,
|
|
|
16
16
|
|
|
17
17
|
## A dialog is sized by its content, never by a `width`
|
|
18
18
|
|
|
19
|
-
`Dialog` has no `width` prop of its own
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
content, a ceiling over one that would sprawl:
|
|
19
|
+
`Dialog` has no `width` prop of its own, and that is the whole design: a dialog
|
|
20
|
+
is a surface laid over the app, and what it holds is what knows how wide it
|
|
21
|
+
should be. What a caller states are **bounds** — a floor under a dialog too
|
|
22
|
+
narrow for its content, a ceiling over one that would sprawl:
|
|
24
23
|
|
|
25
24
|
| what you want to say | how |
|
|
26
25
|
| --------------------------------------- | ------------------------- |
|
|
@@ -29,32 +28,26 @@ content, a ceiling over one that would sprawl:
|
|
|
29
28
|
| "as wide as the container allows" | `expandX` / `expandY` |
|
|
30
29
|
| "as wide as the control that opened it" | `sizeFromAnchor` (opt-in) |
|
|
31
30
|
|
|
32
|
-
`sizeFromAnchor` is off by default and that is deliberate: unlike a `Popover`,
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
`sizeFromAnchor` is off by default and that is deliberate: unlike a `Popover`, a
|
|
32
|
+
dialog is not attached to what opened it, so following that element's box is a
|
|
33
|
+
choice, not the norm. It only ever sets a **floor**
|
|
35
34
|
(`--anchor-width`/`--anchor-height`), never a width.
|
|
36
35
|
|
|
37
36
|
## The ceiling nobody sets
|
|
38
37
|
|
|
39
38
|
Above every bound a caller passes there is one navi always applies: the
|
|
40
|
-
container, minus `marginWithContainer` on both sides
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
the browser reflows the dialog as the keyboard opens. Nothing to wire.
|
|
53
|
-
- "the container" is the **app's own screen** for `layer="top"` — the visual
|
|
54
|
-
viewport minus the app's bands, whether `--navi-app-max-width` centers them
|
|
55
|
-
or the app writes `--navi-app-inset-*` itself (see
|
|
56
|
-
[`safe_area.md`](./safe_area.md)) — and the positioned ancestor for
|
|
57
|
-
`layer="local"`.
|
|
39
|
+
container, minus `marginWithContainer` on both sides. It is not a default that a
|
|
40
|
+
larger `maxWidth` overrides — it wins, always, and a `minWidth` too large for
|
|
41
|
+
the screen is clamped by it too, so **no combination of props can produce a
|
|
42
|
+
dialog that overflows its container.** Stop trying to defend against that case.
|
|
43
|
+
|
|
44
|
+
The caps are applied to the dialog's **size**, not merely to its position, and
|
|
45
|
+
expressed against the app's live screen (`--navi-app-width`/`--navi-app-height`,
|
|
46
|
+
which track the visual viewport): the browser reflows a centered dialog as the
|
|
47
|
+
mobile keyboard opens, with nothing to wire. "The container" is the **app's own
|
|
48
|
+
screen** for `layer="top"` — the visual viewport minus the app's bands (see
|
|
49
|
+
[`safe_area.md`](./safe_area.md)) — and the positioned ancestor for
|
|
50
|
+
`layer="local"`.
|
|
58
51
|
|
|
59
52
|
## One dialog, two shapes
|
|
60
53
|
|
|
@@ -69,9 +62,9 @@ below it, and the question and the field stay where the finger found them.
|
|
|
69
62
|
reads and taps (a list, a couple of buttons, a confirmation) and that holds
|
|
70
63
|
nothing raising a keyboard. A bottom sheet one types into is reflowed into the
|
|
71
64
|
strip left above the keyboard at every keystroke, and the mistake is silent on
|
|
72
|
-
the desktop the author tests on — which is why the safe edge is the default
|
|
73
|
-
|
|
74
|
-
|
|
65
|
+
the desktop the author tests on — which is why the safe edge is the default, and
|
|
66
|
+
why a `"bottom"` sheet found holding a field is warned about in dev, on any
|
|
67
|
+
screen.
|
|
75
68
|
|
|
76
69
|
```jsx
|
|
77
70
|
<Dialog dockedOnSmallTouchScreen /> // top: the keyboard cannot reach it
|
|
@@ -82,36 +75,32 @@ The edge is the prop's own value rather than `positionArea` because
|
|
|
82
75
|
`positionArea` places _both_ shapes: `positionArea="bottom"` also pins the
|
|
83
76
|
centered box to the bottom of a desktop window.
|
|
84
77
|
|
|
85
|
-
Both halves of the name matter
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
sheet, up for a top one), held by the sheet's `header` (a `Box` with the
|
|
112
|
-
`header` prop) and by anything carrying `data-swipe-grip` — never by the whole
|
|
113
|
-
sheet, so a board something is dragged across keeps its own gestures. See
|
|
114
|
-
[`drag_to_travel.md`](./drag_to_travel.md).
|
|
78
|
+
Both halves of the name matter: touch alone would dock a tablet or a kiosk panel,
|
|
79
|
+
size alone a narrow desktop window, which is still a mouse.
|
|
80
|
+
`smallTouchScreenSignal` answers both, by shape rather than by a box of maximum
|
|
81
|
+
dimensions (a phone is a narrow slab, in either orientation). A `Dialog` follows
|
|
82
|
+
it live: turning the phone re-resolves it. A `Picker` decides popover or dialog
|
|
83
|
+
again at each opening, but a `Popup` decides once, for its lifetime — one
|
|
84
|
+
mounted as a popover never docks.
|
|
85
|
+
|
|
86
|
+
What docking supplies are defaults — `positionArea` (the edge),
|
|
87
|
+
`marginWithContainer={0}` (a sheet is flush, or it is not a sheet), `expandX`
|
|
88
|
+
and `scrollCapture` — so any single axis of the sheet can be adjusted without
|
|
89
|
+
giving up the rest.
|
|
90
|
+
|
|
91
|
+
A `layer="top"` dialog flush with an edge of the screen — `marginWithContainer`
|
|
92
|
+
at `0` with a `positionArea` on that edge or an `expand` across it, as a docked
|
|
93
|
+
sheet is — keeps the band the device reserves there (`env(safe-area-inset-*)`:
|
|
94
|
+
the notch, the home indicator, Safari's floating bar): the surface reaches the
|
|
95
|
+
edge, what it holds stops at the band. A centered dialog with a `0` margin
|
|
96
|
+
touches no edge, and gets no band. Only the device's own inset: the dialog is in
|
|
97
|
+
front of the app's fixed bars, so `--navi-safe-area-inset-*`, which counts them
|
|
98
|
+
too (see [`safe_area.md`](./safe_area.md)), is not what it reads.
|
|
99
|
+
|
|
100
|
+
A docked sheet closes by a swipe through the edge it rests on, held by its
|
|
101
|
+
`header` and by anything carrying `data-swipe-grip` — never by the whole sheet,
|
|
102
|
+
so a board something is dragged across keeps its own gestures (see
|
|
103
|
+
[`drag_to_travel.md`](./drag_to_travel.md)).
|
|
115
104
|
|
|
116
105
|
**`expandY` (or `expand`) cancels docking outright.** A dialog already filling
|
|
117
106
|
the height touches both edges; all docking could still do is take away the
|
|
@@ -131,17 +120,12 @@ Both halves are written together, and each applies where it means something:
|
|
|
131
120
|
<Dialog dockedOnSmallTouchScreen minWidth="12rem" maxWidth="16rem">
|
|
132
121
|
```
|
|
133
122
|
|
|
134
|
-
| prop | centered box | docked sheet |
|
|
135
|
-
| ----------- | ------------ | ----------------------------------------------------- |
|
|
136
|
-
| `maxWidth` | applies | **withdrawn** — the sheet is container-wide |
|
|
137
|
-
| `minWidth` | applies | stops mattering (the floor is below full width) |
|
|
138
|
-
| `maxHeight` | applies | applies — a sheet is content-tall, not container-tall |
|
|
139
|
-
| `minHeight` | applies | applies |
|
|
140
|
-
|
|
141
123
|
`maxWidth` is an answer about the _centered_ shape: "do not sprawl on a wide
|
|
142
|
-
window". A sheet spanning its container's full width
|
|
143
|
-
|
|
144
|
-
|
|
124
|
+
window". A sheet spanning its container's full width **is** the docked mode, so
|
|
125
|
+
docking withdraws that ceiling rather than capping the sheet with it; the
|
|
126
|
+
container ceiling still holds. `minWidth` stops mattering there (the floor is
|
|
127
|
+
below full width), and `maxHeight`/`minHeight` keep applying — a sheet is
|
|
128
|
+
content-tall, not container-tall.
|
|
145
129
|
|
|
146
130
|
> **Trap: do not re-derive the docking condition in the app.** This looks like
|
|
147
131
|
> the way to say it and is subtly wrong:
|
|
@@ -156,18 +140,15 @@ than capping the sheet with it. The container ceiling still holds, as always.
|
|
|
156
140
|
> dialog that also sets `expandY` never docks, so the cap must never be
|
|
157
141
|
> withdrawn there, and the line above withdraws it anyway. Beyond being wrong,
|
|
158
142
|
> it duplicates a condition navi owns at every call site (it drifts the day
|
|
159
|
-
> "docked" gains a rule), and it
|
|
160
|
-
>
|
|
161
|
-
> resolve its own shape.
|
|
143
|
+
> "docked" gains a rule), and it says nothing about docked sheets to the next
|
|
144
|
+
> person. State both bounds plainly and let the dialog resolve its own shape.
|
|
162
145
|
|
|
163
146
|
## `expand` is not "docked", and `expandX={false}` is not "do not sprawl"
|
|
164
147
|
|
|
165
148
|
`expandX`/`expandY` mean "grow to the ceiling" — the container ceiling above,
|
|
166
149
|
capped in turn by `maxWidth`/`maxHeight` when they apply. `expand` is the
|
|
167
|
-
shorthand for both.
|
|
168
|
-
|
|
169
|
-
Since docking _supplies_ `expandX`, passing it explicitly takes the caller out
|
|
170
|
-
of that default:
|
|
150
|
+
shorthand for both. Since docking _supplies_ `expandX`, passing it explicitly
|
|
151
|
+
takes the caller out of that default:
|
|
171
152
|
|
|
172
153
|
```jsx
|
|
173
154
|
// The sheet stops being flush: a floating box against its edge of the screen.
|
|
@@ -177,8 +158,7 @@ of that default:
|
|
|
177
158
|
That is consistent — an explicitly passed prop wins over a docked default — and
|
|
178
159
|
it is still the trap, because the two props read as answering different
|
|
179
160
|
questions (one about the centered shape, one about the phone) when they answer
|
|
180
|
-
the same one.
|
|
181
|
-
and docking withdraws it on its own.
|
|
161
|
+
the same one. "Cap the centered box" is `maxWidth`.
|
|
182
162
|
|
|
183
163
|
## `marginWithContainer` decides the gap AND the ceiling
|
|
184
164
|
|
|
@@ -190,74 +170,51 @@ It defaults to a share of whatever holds the dialog (`3appw` for `layer="top"`,
|
|
|
190
170
|
`3cqw` for `layer="local"`) and accepts a number of pixels, a viewport length —
|
|
191
171
|
`appw`/`apph` being the app's own screen, `vvw`/`vvh` the visual viewport, which
|
|
192
172
|
shrinks when the keyboard opens — or a container length (`cqw`/`cqh`). A
|
|
193
|
-
spacing token (`"s"`, `"m"`…) cannot be resolved to pixels here: navi warns
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
already passes `0` for you.
|
|
173
|
+
spacing token (`"s"`, `"m"`…) cannot be resolved to pixels here: navi warns and
|
|
174
|
+
places the dialog flush. Pass `0` for something meant to sit flush (a side
|
|
175
|
+
panel); docking already passes it.
|
|
197
176
|
|
|
198
177
|
## Holding a size while the dialog is open
|
|
199
178
|
|
|
200
|
-
`sizing="frozen"` measures the dialog once and holds it until it closes
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
Two things make it safe to reach for:
|
|
207
|
-
|
|
208
|
-
- the freeze writes a `width`/`height`, never a `min-*` — the caps above keep
|
|
209
|
-
winning, so a frozen dialog still fits when the phone is turned;
|
|
210
|
-
- the measure is taken at the first render where the prop says `"frozen"`, so a
|
|
211
|
-
dialog opening on skeletons says `sizing={loading ? "auto" : "frozen"}` and
|
|
212
|
-
is measured once the real content has arrived. Closing releases it.
|
|
179
|
+
`sizing="frozen"` measures the dialog once and holds it until it closes — for a
|
|
180
|
+
surface acted upon while it is open (emptying a queue, swapping between two
|
|
181
|
+
slides of different heights), where the row being aimed at must not move under
|
|
182
|
+
the finger. The caps above keep winning; a dialog opening on skeletons says
|
|
183
|
+
`sizing={loading ? "auto" : "frozen"}`, to be measured once the real content has
|
|
184
|
+
arrived.
|
|
213
185
|
|
|
214
186
|
## A `layer="local"` dialog answers to its container
|
|
215
187
|
|
|
216
|
-
`layer="top"` (the default) is
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
188
|
+
`layer="top"` (the default) is placed against the screen and clipped by nothing;
|
|
189
|
+
`layer="local"` stays in normal document flow, confined to and clipped by its
|
|
190
|
+
own positioned ancestor, which every bound is then measured against (the
|
|
191
|
+
default margin in container units). Its container's scroll is locked while it
|
|
192
|
+
is open — its backdrop covers the scrollport, not the scrolled content — and
|
|
193
|
+
`scrollCapture` extends that lock to the whole page.
|
|
221
194
|
|
|
222
|
-
For a **docked** dialog
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
`
|
|
227
|
-
|
|
228
|
-
For the shape, that swaps what every bound is measured against: the container
|
|
229
|
-
becomes that ancestor's box, read through
|
|
230
|
-
`--container-position-remaining-width`/`-height`, and the default margin is
|
|
231
|
-
read in container units. Its own container's scroll is always locked while it
|
|
232
|
-
is open — its backdrop covers the scrollport, not the scrolled content, so
|
|
233
|
-
scrolling would slide the dialog away and reveal what the backdrop does not
|
|
234
|
-
cover. `scrollCapture` is what extends that lock to the whole page.
|
|
235
|
-
|
|
236
|
-
One accepted limitation, not an oversight: a dialog that is not modal —
|
|
237
|
-
`layer="local"`, or `backdrop={false}` — **cannot** be dismissed by the
|
|
238
|
-
hardware/gesture back button. No web API hooks into that outside the browser's
|
|
239
|
-
own modal-dismissal stack, which only a genuine `showModal()` element joins.
|
|
195
|
+
For a **docked** dialog, `layer` is the choice of what the sheet rests against.
|
|
196
|
+
A sheet meant for the bottom of the screen is `layer="top"`, even when the page
|
|
197
|
+
behind it must stay live — that is `backdrop={false}`, not `layer="local"`.
|
|
198
|
+
Whether a dialog is modal, and what one that is not gives up (the back button),
|
|
199
|
+
is [`popup_backdrop.md`](./popup_backdrop.md#is-there-a-backdrop-at-all)'s.
|
|
240
200
|
|
|
241
201
|
## Reaching all of this through a `Picker` or a `SplitButton`
|
|
242
202
|
|
|
243
|
-
A picker's popup is a popover or a dialog depending on the screen, so it
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
`dockedOnSmallTouchScreen`, `marginWithContainer` — plus `popoverMaxHeight` and
|
|
250
|
-
`popupWidthFitContent` for the other shape.
|
|
203
|
+
A picker's popup is a popover or a dialog depending on the screen, so it exposes
|
|
204
|
+
the dialog's bounds under prefixed names (`dialogMinWidth`, `dialogMaxWidth`,
|
|
205
|
+
`dialogExpandX`…), next to `dockedOnSmallTouchScreen` and `marginWithContainer`.
|
|
206
|
+
They mean what they mean on `Dialog`, with one exception: `dialogSizeFromAnchor`
|
|
207
|
+
makes the trigger's width the dialog's ceiling as well as its floor (see
|
|
208
|
+
[popup_lift.md](./popup_lift.md#same-width-or-a-wider-box)).
|
|
251
209
|
|
|
252
|
-
`SplitButton` forwards the
|
|
253
|
-
it
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
210
|
+
`SplitButton` forwards its popup props to the picker it wraps, and its menu is a
|
|
211
|
+
popover unless it says `mode="dialog"` — its `dialog*` props do nothing until
|
|
212
|
+
then. Anything not in its `POPUP_PROP_SET` (`src/control/input/split_button.jsx`)
|
|
213
|
+
lands on the split button's own box instead, so a prop that seems to do nothing
|
|
214
|
+
to the menu is worth checking against that list first.
|
|
257
215
|
|
|
258
216
|
---
|
|
259
217
|
|
|
260
218
|
Reference: `src/layout/dialog.jsx` (the stylesheet at the top of the file holds
|
|
261
|
-
the cap arithmetic), `src/layout/responsive.js` (`smallTouchScreenSignal`),
|
|
262
|
-
`src/
|
|
263
|
-
`dialogMax*` to the dialog element), and `src/layout/demos/1_dialog_demo.html`.
|
|
219
|
+
the cap arithmetic), `src/layout/responsive.js` (`smallTouchScreenSignal`), and
|
|
220
|
+
`src/layout/demos/1_dialog_demo.html`.
|