@jsenv/navi 0.29.425 → 0.29.426
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 +372 -119
- package/dist/dev/jsenv_navi.js.map +15 -13
- package/dist/jsenv_navi.js +372 -119
- package/dist/jsenv_navi.js.map +15 -13
- package/docs/AI_INSTRUCTIONS.md +9 -5
- package/docs/control_object.md +76 -2
- package/docs/navigation.md +4 -1
- package/docs/popup_open.md +6 -3
- package/docs/scroll.md +53 -0
- package/package.json +1 -1
package/docs/AI_INSTRUCTIONS.md
CHANGED
|
@@ -116,8 +116,11 @@ start when unsure which export solves a problem.
|
|
|
116
116
|
keeps the shape of the question. Before wiring a value with `value` +
|
|
117
117
|
`uiAction`.
|
|
118
118
|
- `control_object.md` — one value made of several controls: `ControlGroup` versus
|
|
119
|
-
`Form`, naming, `<Picker type="object">`, a settings sheet
|
|
120
|
-
|
|
119
|
+
`Form`, naming, `<Picker type="object">`, a settings sheet, controls that are
|
|
120
|
+
not there (lazy slides, popups, deferred boxes, items outside the render
|
|
121
|
+
window: the group keeps their keys, shared state is written through it).
|
|
122
|
+
Before putting anything in a picker popup, and before lazy-mounting anything
|
|
123
|
+
that holds named controls.
|
|
121
124
|
- `group.md` — `<Group>`: several controls reading as one framed object. Before
|
|
122
125
|
negative margins or `border-radius: 0` by hand.
|
|
123
126
|
- `form_changed.md` — a form sends nothing when nothing changed; what "changed"
|
|
@@ -179,9 +182,10 @@ start when unsure which export solves a problem.
|
|
|
179
182
|
- `scroll.md` — where scrolling happens: `header`/`body`/`footer`, a `List`'s
|
|
180
183
|
`scroller`, where a list opens (a position handed back whole, `visibleCount`
|
|
181
184
|
included), a popup that scrolls, many rows (`<List.Items>`, the render window
|
|
182
|
-
and its first picture, `findText`),
|
|
183
|
-
|
|
184
|
-
|
|
185
|
+
and its first picture, `findText`), many sections (`<Box mount="after-paint">`
|
|
186
|
+
and what it leaves out of reach), hover while scrolling. Before writing CSS to
|
|
187
|
+
make something scroll, before rendering a collection as `<List.Item>`
|
|
188
|
+
children, and before deferring part of a page.
|
|
185
189
|
- `z_index.md` — DOM order first; a `z-index` without `isolation: isolate`
|
|
186
190
|
competes with the page; navi's bands. Before writing a `z-index`.
|
|
187
191
|
- `typography.md` — text is a component; `maxLines` is the one truncation; one
|
package/docs/control_object.md
CHANGED
|
@@ -12,6 +12,7 @@ whose value is an object needs in its popup.
|
|
|
12
12
|
- [Naming, and what a nameless group does](#naming-and-what-a-nameless-group-does)
|
|
13
13
|
- [A picker whose value is an object](#a-picker-whose-value-is-an-object)
|
|
14
14
|
- [A group holds what it was given](#a-group-holds-what-it-was-given)
|
|
15
|
+
- [A control that is not there](#a-control-that-is-not-there)
|
|
15
16
|
- [One line, one key](#one-line-one-key)
|
|
16
17
|
- [A settings sheet](#a-settings-sheet)
|
|
17
18
|
- [`Group` is not `ControlGroup`](#group-is-not-controlgroup)
|
|
@@ -142,7 +143,9 @@ not while the children are not all there yet:
|
|
|
142
143
|
|
|
143
144
|
- a group **told** a value holds it whole, before any child has registered to
|
|
144
145
|
show it: items still loading, a popup built at open, a row scrolled out of a
|
|
145
|
-
virtualized list do not make the value smaller
|
|
146
|
+
virtualized list do not make the value smaller, nor does a child acting while
|
|
147
|
+
others are away (see
|
|
148
|
+
[A control that is not there](#a-control-that-is-not-there));
|
|
146
149
|
- **a child mounting or unmounting is not somebody answering.** While children
|
|
147
150
|
arrive, their aggregate is a partial reading; the group takes it for its value
|
|
148
151
|
only once it derived that value itself (nobody handed it one), or once a child
|
|
@@ -209,6 +212,76 @@ const moveTo = (from, to) => {
|
|
|
209
212
|
};
|
|
210
213
|
```
|
|
211
214
|
|
|
215
|
+
## A control that is not there
|
|
216
|
+
|
|
217
|
+
The answer lives in the group — and in the signal bound to it, or to the
|
|
218
|
+
picker around it — and the controls are views of it. A view can be missing
|
|
219
|
+
while the answer still matters: a slide not built yet
|
|
220
|
+
(`SlideContainer mount="near"`), a popup or an `Expandable` not opened yet, a
|
|
221
|
+
`<Box mount="after-paint">` before its first frame, an item outside a list's
|
|
222
|
+
render window, a page parked while a route action reruns. None of that makes
|
|
223
|
+
the answer smaller:
|
|
224
|
+
|
|
225
|
+
- **a key stays until a control of that name, there, says otherwise.** The
|
|
226
|
+
group keeps what it holds for a control that is not there, the way a
|
|
227
|
+
selectable list keeps the selection of items it does not draw. A control that
|
|
228
|
+
arrives is placed from it; one that leaves takes nothing with it;
|
|
229
|
+
- **a value put ON the group reaches every key**, whether its control is there
|
|
230
|
+
or not: the group's `signal` or `value`, a picker filling its popup, a cancel
|
|
231
|
+
putting back what the picker held at open, a `--navi-update` aimed at the
|
|
232
|
+
group. It replaces the whole object, which is also how a key is dropped on
|
|
233
|
+
purpose;
|
|
234
|
+
- **a control's own `signal` reaches that control only.** Written while the
|
|
235
|
+
control is not there, nobody is listening: the group never hears of it, and a
|
|
236
|
+
cancel has nothing to take back. The answer is ONE signal, on the group or on
|
|
237
|
+
the picker holding it — never one per control, assembled by hand;
|
|
238
|
+
- **only a control that is there checks itself.** `required`, a pattern, a range
|
|
239
|
+
are verified by the control, so a field not built yet is not validated;
|
|
240
|
+
- **a field hidden on purpose keeps its key too**: the group cannot tell
|
|
241
|
+
"removed" from "not built yet". Empty it before hiding it, or put the value
|
|
242
|
+
without it on the group.
|
|
243
|
+
|
|
244
|
+
So something shared by controls that are not all there — seven days of which
|
|
245
|
+
one is on screen, a copy of one day onto the others — is written as one value,
|
|
246
|
+
through the group:
|
|
247
|
+
|
|
248
|
+
```jsx
|
|
249
|
+
// the week, keyed by day: what the tabs, the summary and the save read
|
|
250
|
+
const weekSignal = useSignal({ 1: [], 2: [18, 19], 3: [], … });
|
|
251
|
+
|
|
252
|
+
<Picker type="object" signal={weekSignal} action={saveWeek} ui={<WeekSummary />}>
|
|
253
|
+
<ControlGroup id="week">
|
|
254
|
+
<SlideContainer mount="near" signal={daySignal}>
|
|
255
|
+
{DAYS.map((day) => (
|
|
256
|
+
<Slide key={day} area={day}>
|
|
257
|
+
<List selectable multiple name={day}>…</List>
|
|
258
|
+
</Slide>
|
|
259
|
+
))}
|
|
260
|
+
</SlideContainer>
|
|
261
|
+
{/* every day, the ones not built included — and a cancel takes it back */}
|
|
262
|
+
<Button
|
|
263
|
+
command="--navi-update"
|
|
264
|
+
commandFor="week"
|
|
265
|
+
value={sameEveryDay(weekSignal.value[daySignal.value])}
|
|
266
|
+
>
|
|
267
|
+
Same hours every day
|
|
268
|
+
</Button>
|
|
269
|
+
</ControlGroup>
|
|
270
|
+
</Picker>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
```jsx
|
|
274
|
+
// ✗ one signal per day, written by hand: the days not built never hear it,
|
|
275
|
+
// and Escape leaves them changed
|
|
276
|
+
for (const day of DAYS) {
|
|
277
|
+
daySignals[day].value = [...shownHours];
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
The picker's signal follows the popup gesture by gesture, so the button reads
|
|
282
|
+
the day shown from it; the dots on the tabs read it too, for days whose slide
|
|
283
|
+
was never built.
|
|
284
|
+
|
|
212
285
|
## One line, one key
|
|
213
286
|
|
|
214
287
|
A row that opens a popup is one control, so what it answers arrives under its
|
|
@@ -274,7 +347,8 @@ one being tried.
|
|
|
274
347
|
|
|
275
348
|
**Every field answers**, including those on another tab: they stay mounted and
|
|
276
349
|
come back in the object, and whoever receives it reads `origin` to know which
|
|
277
|
-
count.
|
|
350
|
+
count. Under `mount="near"` a tab not built yet answers with what the group
|
|
351
|
+
holds for it ([A control that is not there](#a-control-that-is-not-there)). Aggregating the sheet to a single value of its own
|
|
278
352
|
(`aggregateChildStates`) throws away what makes the answer readable, starting
|
|
279
353
|
with the tab. **A button inside is not a field**: the one asking for a position
|
|
280
354
|
acts on the press, and what it answers goes into a control that IS one (a
|
package/docs/navigation.md
CHANGED
|
@@ -565,7 +565,10 @@ of the document's scrollport — it holds an offset even without a scrollbar, wh
|
|
|
565
565
|
`overflow: clip` holds none — so it is the first thing to look for when a row
|
|
566
566
|
does not give positions back. And a page whose height depends on something still
|
|
567
567
|
loading is not tall enough when its position is put back: only the page knows
|
|
568
|
-
when it is whole.
|
|
568
|
+
when it is whole. What navi itself builds after the first frame
|
|
569
|
+
(`<Box mount="after-paint">`, see
|
|
570
|
+
[scroll.md](./scroll.md#many-sections-box-mountafter-paint)) is not such a
|
|
571
|
+
page: the part the position shows is built before it is written.
|
|
569
572
|
|
|
570
573
|
## Creating something, then editing it
|
|
571
574
|
|
package/docs/popup_open.md
CHANGED
|
@@ -895,9 +895,12 @@ opening — so the `mount` prop answers both at once:
|
|
|
895
895
|
| `"while-opened"` | not mounted | not mounted |
|
|
896
896
|
|
|
897
897
|
`"always"` is for content something depends on before any opening: a value read
|
|
898
|
-
off it (the default of a picker told no value whose value is read), fields a
|
|
899
|
-
|
|
900
|
-
|
|
898
|
+
off it (the default of a picker told no value whose value is read), fields a
|
|
899
|
+
surrounding form must validate or read their own `defaultValue` from, a size
|
|
900
|
+
measured from outside. What such a form was handed it keeps for fields not built
|
|
901
|
+
yet, and hands them once they are
|
|
902
|
+
([control_object.md](./control_object.md#a-control-that-is-not-there)).
|
|
903
|
+
`"idle"` is `"always"` minus the cost on the render that draws the page.
|
|
901
904
|
|
|
902
905
|
Whatever the value but `"while-opened"`, intent on what opens the popup builds
|
|
903
906
|
the content ahead of the click, `onOpen` or not: a pointer over its anchor, or
|
package/docs/scroll.md
CHANGED
|
@@ -307,6 +307,11 @@ A run whose rows all fit the window is just rows: nothing is virtualized, and
|
|
|
307
307
|
it costs what the same rows would as children. There is no reason to hold back
|
|
308
308
|
from it for a list that might grow.
|
|
309
309
|
|
|
310
|
+
What an item outside the window holds is not lost with its row: a selection
|
|
311
|
+
keeps the items it does not draw (`--navi-select` reaches them by id), and a
|
|
312
|
+
group keeps the keys of named controls that are not there
|
|
313
|
+
([control_object.md](./control_object.md#a-control-that-is-not-there)).
|
|
314
|
+
|
|
310
315
|
### What the list knows, and what it guesses
|
|
311
316
|
|
|
312
317
|
A virtualized list has one aim: draw as few items as it can, and never show a
|
|
@@ -433,6 +438,54 @@ on the run when the collection changes as a whole (never to refresh it, see
|
|
|
433
438
|
searched), `src/control/demos/integration/1_list_loaded_by_scroll_demo.html` (a
|
|
434
439
|
run reading a slice at a time).
|
|
435
440
|
|
|
441
|
+
## Many sections: `<Box mount="after-paint">`
|
|
442
|
+
|
|
443
|
+
The render window's twin for a page that is not a list — a profile, a settings
|
|
444
|
+
screen, sections stacked in a column. The browser paints nothing until the
|
|
445
|
+
render that builds the page has ended, and the sections below the first screen
|
|
446
|
+
weigh in it as much as those on it.
|
|
447
|
+
|
|
448
|
+
```jsx
|
|
449
|
+
<Box flex="y" spacing="l">
|
|
450
|
+
<ProfileHeader user={user} />
|
|
451
|
+
<UpcomingGames user={user} />
|
|
452
|
+
<Box flex="y" spacing="l" mount="after-paint">
|
|
453
|
+
<Searches user={user} />
|
|
454
|
+
<Badges user={user} />
|
|
455
|
+
<LinkedAccounts />
|
|
456
|
+
</Box>
|
|
457
|
+
</Box>
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
The box is empty in the render that mounts it, and built right after the first
|
|
461
|
+
frame that shows the page (the first frame of its route transition, when there
|
|
462
|
+
is one), then kept: a later render of the page renders it as usual.
|
|
463
|
+
|
|
464
|
+
Which sections lie below the screen is not left to the app's guess, which a
|
|
465
|
+
taller phone or an empty first section breaks. navi looks at each box before
|
|
466
|
+
that frame is painted, and one the frame would show — on the screen, or above
|
|
467
|
+
it — is built before the paint all the same: marking too much costs a second
|
|
468
|
+
render in that frame, never a section popping in. A page put back where it was
|
|
469
|
+
left (see
|
|
470
|
+
[navigation.md](./navigation.md#where-a-navigation-lands-the-scroll)) has the
|
|
471
|
+
boxes that offset shows built before the offset is written, so the offset is not
|
|
472
|
+
clamped by a document still missing its bottom.
|
|
473
|
+
|
|
474
|
+
What navi cannot do is make the content exist before it is built. So the box is
|
|
475
|
+
only for content nothing reaches during that first frame:
|
|
476
|
+
|
|
477
|
+
- **Named controls** inside are not there until built: what a group holds for
|
|
478
|
+
them waits, a value written into their own `signal` reaches nothing, and
|
|
479
|
+
nothing inside checks itself (see
|
|
480
|
+
[control_object.md](./control_object.md#a-control-that-is-not-there)).
|
|
481
|
+
- **Ids**: a `commandFor`, a popup `anchor` or a `triggerNaviCommand` aimed
|
|
482
|
+
inside finds nothing, and neither does a focus put back there. A `#fragment`
|
|
483
|
+
aimed inside lands a frame late, once the element is there; a popup opened
|
|
484
|
+
from the address opens once built, since it reads its signal then.
|
|
485
|
+
- **Size**: the box has no height until it is built (a `minHeight` reserves
|
|
486
|
+
one). Anything measuring it in that frame — a sticky offset, a size read from
|
|
487
|
+
outside — sees it empty.
|
|
488
|
+
|
|
436
489
|
## Hover while scrolling
|
|
437
490
|
|
|
438
491
|
A scroll moves the content under a pointer that does not move, and the browser
|