@jsenv/navi 0.29.435 → 0.29.437
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 +1015 -250
- package/dist/dev/jsenv_navi.js.map +25 -20
- package/dist/jsenv_navi.js +1015 -250
- package/dist/jsenv_navi.js.map +25 -20
- package/docs/AI_INSTRUCTIONS.md +3 -2
- package/docs/data_states.md +3 -1
- package/docs/list_refresh.md +4 -1
- package/docs/network_policy.md +9 -4
- package/docs/resource.md +93 -1
- package/docs/route_transitions.md +7 -0
- package/docs/scroll.md +3 -1
- package/package.json +1 -1
package/docs/AI_INSTRUCTIONS.md
CHANGED
|
@@ -84,8 +84,9 @@ start when unsure which export solves a problem.
|
|
|
84
84
|
action and a command on one press. Before running an action from a component.
|
|
85
85
|
- `resource.md` — `resource()` and its relations (never an `op` discriminator),
|
|
86
86
|
`GET_RANGE`, a search as the same `GET_MANY`, `withParams()` scopes and
|
|
87
|
-
`dependencies`, `persist
|
|
88
|
-
writing a resource, and before
|
|
87
|
+
`dependencies`, `persist` (and `keepPageOnScreen` for the page on screen), a
|
|
88
|
+
function versus the verb's instance. Before writing a resource, and before
|
|
89
|
+
caching a response yourself.
|
|
89
90
|
- `data_states.md` — `data`, `loading` and `error` are three questions;
|
|
90
91
|
`loading: true` never suspends; a skeleton is told whether it is loading.
|
|
91
92
|
Before drawing a skeleton on `!data` or hiding content because `error` is set.
|
package/docs/data_states.md
CHANGED
|
@@ -79,7 +79,9 @@ action, a `bindParams` holding one) included, when the signal moves to other
|
|
|
79
79
|
params. A row known from the previous page load counts too, when the resource
|
|
80
80
|
asks for it:
|
|
81
81
|
[`persist`](./resource.md#persist-the-last-answer-drawn-again-after-a-reload)
|
|
82
|
-
puts a reload on the second line as well
|
|
82
|
+
puts a reload on the second line as well, and so does
|
|
83
|
+
[`keepPageOnScreen`](./resource.md#keeppageonscreen-the-page-on-screen-drawn-again-by-the-next-document) for
|
|
84
|
+
what the page on screen reads — a `GET_MANY` and a `GET_RANGE` included.
|
|
83
85
|
|
|
84
86
|
**The emptiness test is `data === undefined`, never `loading`.** Reading
|
|
85
87
|
`loading` as "there is nothing to display" blanks the screen on every refresh —
|
package/docs/list_refresh.md
CHANGED
|
@@ -127,7 +127,10 @@ A composition is about the **values** its params hold, not the reader instance:
|
|
|
127
127
|
writes the same one. A verb in `rerunOn.GET_RANGE`, or `reader.invalidate()`,
|
|
128
128
|
drops the compositions — they stand for an order that is gone — and
|
|
129
129
|
`memoryBudget` (1000 ranks by default) trims the ranks far from any window,
|
|
130
|
-
which are asked for again if the user goes back to them.
|
|
130
|
+
which are asked for again if the user goes back to them. A composition read by a
|
|
131
|
+
list on screen also outlives the document when the page is kept
|
|
132
|
+
([`keepPageOnScreen`](./resource.md#keeppageonscreen-the-page-on-screen-drawn-again-by-the-next-document)): the ranks around the window it draws are written with
|
|
133
|
+
their rows, and a reload is the same return after a longer absence.
|
|
131
134
|
|
|
132
135
|
### Who decides the re-read — and who does not
|
|
133
136
|
|
package/docs/network_policy.md
CHANGED
|
@@ -67,9 +67,13 @@ was asked:
|
|
|
67
67
|
what it holds instead of falling into its error boundary. A route opening a
|
|
68
68
|
user by id **or** by slug gives navi both keys to look by:
|
|
69
69
|
`resource("user", { uniqueKeys: ["slug"], … })`.
|
|
70
|
-
- **A
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
- **A read drawing a kept answer completes with it.** A root `GET` or
|
|
71
|
+
`GET_MANY` whose first run drew what the page kept from the previous document
|
|
72
|
+
([`keepPageOnScreen`](./resource.md#keeppageonscreen-the-page-on-screen-drawn-again-by-the-next-document)) answers with those rows: a reload with no network reopens
|
|
73
|
+
on the page.
|
|
74
|
+
- **A completed read stays completed.** Otherwise `GET_MANY` (and every other
|
|
75
|
+
read) has nowhere to answer from: the store holds items, not queries, and only
|
|
76
|
+
the action's own value knows which ids answered `/users?scope=shareable`. So a
|
|
73
77
|
rerun asked of a completed read under the policy is held — the action keeps
|
|
74
78
|
its state, its value and its data, exactly as a `run()` on a completed action
|
|
75
79
|
would.
|
|
@@ -185,7 +189,8 @@ has nothing to decide which one says why.
|
|
|
185
189
|
which signal, for which session: a resource declaring
|
|
186
190
|
[`persist`](./resource.md#persist-the-last-answer-drawn-again-after-a-reload)
|
|
187
191
|
has its kept row answer the `GET` under the policy, so a reload offline
|
|
188
|
-
reopens on it.
|
|
192
|
+
reopens on it; [`keepPageOnScreen`](./resource.md#keeppageonscreen-the-page-on-screen-drawn-again-by-the-next-document) does the same for the page
|
|
193
|
+
on screen.
|
|
189
194
|
- **A queue of writes to replay** once the network is back — not navi's: what a
|
|
190
195
|
replayed write means (a score entered twice? a seat taken since?) is the app's
|
|
191
196
|
business.
|
package/docs/resource.md
CHANGED
|
@@ -34,6 +34,7 @@ Actions are read in components through the action system (`useAsyncData`,
|
|
|
34
34
|
|
|
35
35
|
- [`store.upsert()` is not how data enters the store](#storeupsert-is-not-how-data-enters-the-store)
|
|
36
36
|
- [`persist`: the last answer, drawn again after a reload](#persist-the-last-answer-drawn-again-after-a-reload)
|
|
37
|
+
- [`keepPageOnScreen`: the page on screen, drawn again by the next document](#keeppageonscreen-the-page-on-screen-drawn-again-by-the-next-document)
|
|
37
38
|
- [`GET_RANGE`: feeding a list that loads as it scrolls](#get_range-feeding-a-list-that-loads-as-it-scrolls)
|
|
38
39
|
- [Searching the same collection](#searching-the-same-collection)
|
|
39
40
|
- [Relations: pick one of the four methods](#relations-pick-one-of-the-four-methods)
|
|
@@ -129,11 +130,102 @@ What the copy is not:
|
|
|
129
130
|
- **an answer.** The `GET` goes out every time; a screen must not read `data` as
|
|
130
131
|
"the server confirmed" — that is `loading`, and the two are independent.
|
|
131
132
|
- **a store on disk.** One row per params key, `GET` only: meant for a singleton
|
|
132
|
-
or a handful of rows, not a resource whose `GET` is asked for every id.
|
|
133
|
+
or a handful of rows, not a resource whose `GET` is asked for every id. What
|
|
134
|
+
the page on screen reads, lists included, is
|
|
135
|
+
[`keepPageOnScreen`](#keeppageonscreen-the-page-on-screen-drawn-again-by-the-next-document),
|
|
136
|
+
below.
|
|
133
137
|
- **seeded at declaration.** The row enters the store at the first run of the
|
|
134
138
|
`GET`, once the relations are declared, so its relation values are normalized
|
|
135
139
|
as a real answer's are. `ME.store` read before that first run is empty.
|
|
136
140
|
|
|
141
|
+
## `keepPageOnScreen`: the page on screen, drawn again by the next document
|
|
142
|
+
|
|
143
|
+
A reload, a pull-to-refresh, a tab the system discarded in the background: a
|
|
144
|
+
new document opens at the same address, on the page that was left, and what
|
|
145
|
+
that page reads almost always answers what it answered a moment ago. `persist`
|
|
146
|
+
keeps one row of one resource. A page reads several things, lists included,
|
|
147
|
+
and which ones depends on the page. `keepPageOnScreen` keeps that set, one
|
|
148
|
+
slot, replaced when the user moves:
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
const pageKeptSignal = stateSignal(undefined, {
|
|
152
|
+
id: `page@${APP_VERSION}`,
|
|
153
|
+
persists: true,
|
|
154
|
+
type: "object",
|
|
155
|
+
});
|
|
156
|
+
// Before the routes start: the first runs are the ones that look.
|
|
157
|
+
keepPageOnScreen({
|
|
158
|
+
signal: pageKeptSignal,
|
|
159
|
+
when: () => !viewAsSignal.value,
|
|
160
|
+
always: [MY_GAMES_PAGE],
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**The unit is the page, not the resource.** One resource is read by several
|
|
165
|
+
screens: `GAME.GET` for any game opened, `USER.GET_MANY` for a tab and for
|
|
166
|
+
every search. Kept per resource, that is every game ever opened. The page's
|
|
167
|
+
reads are what navi already runs for it: the route actions asking something for
|
|
168
|
+
the page (`activeRouteActionsSignal`, which the app also reads to rerun the page
|
|
169
|
+
when it comes back to the foreground), and the compositions the `<List.Items>`
|
|
170
|
+
on screen read. A navigation replaces the slot with the next page's reads.
|
|
171
|
+
|
|
172
|
+
What is kept, for the next document's first run of the same read with the same
|
|
173
|
+
params:
|
|
174
|
+
|
|
175
|
+
| read | kept |
|
|
176
|
+
| --------------------------------------- | ------------------------------------------------------------ |
|
|
177
|
+
| a route action on a resource `GET` | the row, its relations inline |
|
|
178
|
+
| a route action on a resource `GET_MANY` | the rows in order, written the same way |
|
|
179
|
+
| a `<List.Items>` reading a `GET_RANGE` | the count and the ranks around the window drawn, rows inline |
|
|
180
|
+
|
|
181
|
+
The `GET` and the `GET_MANY` draw it as their provisional value: `data` set
|
|
182
|
+
while `loading` is `true`, the answer replacing it. The list finds it as a
|
|
183
|
+
composition it left
|
|
184
|
+
([list_refresh.md](./list_refresh.md#leaving-the-screen-and-coming-back)): every
|
|
185
|
+
rank stale, one request for the window. Under a network policy answering reads
|
|
186
|
+
from the store, the kept answer answers the read
|
|
187
|
+
([network_policy.md](./network_policy.md)).
|
|
188
|
+
|
|
189
|
+
What is not kept, and why:
|
|
190
|
+
|
|
191
|
+
- **a relationship read** (`.one()`, `.many()`, `.scopedOne()`,
|
|
192
|
+
`.scopedMany()`). Its answer enters the store through its owner: the parent
|
|
193
|
+
row it is nested in, or the store of the owner it is scoped to. It is not rows
|
|
194
|
+
of its own that could be written back alone.
|
|
195
|
+
- **a plain `createAction`** (a summary, a count the server computes). Its value
|
|
196
|
+
is whatever the callback returns, with no store to give it a shape to write
|
|
197
|
+
and to read back. A page's code is a route action too, and it is a function.
|
|
198
|
+
- **a read that is not a route action**: a search typed on the page, a popup's
|
|
199
|
+
own load.
|
|
200
|
+
|
|
201
|
+
A read writes what it shows once it has answered in this document. Until then
|
|
202
|
+
the slot keeps what the previous document kept for it, never what the read
|
|
203
|
+
shows meanwhile: an answer handed over from other params (a list standing in
|
|
204
|
+
for the next search) is not about these. The write waits for the page to settle
|
|
205
|
+
and is flushed when the document is hidden, which is when the system may discard
|
|
206
|
+
it. Between those moments a navigation goes through half-built pages: the route
|
|
207
|
+
matched but its list not mounted, or the page left still drawn under a
|
|
208
|
+
transition.
|
|
209
|
+
|
|
210
|
+
**The start page.** An installed app launched after being killed opens at its
|
|
211
|
+
start address, not on the page that was left. The reads made while a route of
|
|
212
|
+
`always` matches stay kept once that page is left, until the next visit
|
|
213
|
+
replaces them.
|
|
214
|
+
|
|
215
|
+
What stays the app's, as for `persist`: **the key** (the deployed version in the
|
|
216
|
+
signal's `id`), **`when`** (`false` reads nothing, writes nothing, and empties
|
|
217
|
+
the signal), and **the moment**: sign-out writes `undefined` into the signal,
|
|
218
|
+
whatever page it holds, and nothing is written again until a read lands. Also
|
|
219
|
+
**what the reads feed beside their data**: a count the app sets from an answer
|
|
220
|
+
is its own signal to keep. When a list's opening position comes from such a
|
|
221
|
+
count, `defaultScrolled` follows the fresh value as long as nobody has moved the
|
|
222
|
+
list ([scroll.md](./scroll.md#where-the-list-opens-and-where-it-is)).
|
|
223
|
+
|
|
224
|
+
What it costs: the copy follows the store, so a `PUT` on a row rewrites it, and
|
|
225
|
+
every change in the stores the page reads serializes the page's reads again.
|
|
226
|
+
Only the write to the signal is coalesced. A list's window with its rows inline
|
|
227
|
+
is tens of kilobytes.
|
|
228
|
+
|
|
137
229
|
## `GET_RANGE`: feeding a list that loads as it scrolls
|
|
138
230
|
|
|
139
231
|
A `<List.Items>` asks for the rows it is about to draw and keeps what it gets.
|
|
@@ -451,6 +451,9 @@ What that means for a page and for the application around it:
|
|
|
451
451
|
running, and a component reading the URL, a route or a signal directly
|
|
452
452
|
re-renders in it too, hidden. What it reads through `useAsyncData` does not:
|
|
453
453
|
navi leaves a page alone as soon as its container would no longer show it.
|
|
454
|
+
Its `navState` keeps the entry it was shown on: a section or a popup open
|
|
455
|
+
there stays open until the page is taken down, rather than being closed by
|
|
456
|
+
the entry that replaced it.
|
|
454
457
|
- **It is taken down after the page arriving is up.** A cleanup that puts back
|
|
455
458
|
what it found — a document title, a class on `<body>`, a value in a shared store
|
|
456
459
|
— puts a stale value back over the new page's. Such a registration is written as
|
|
@@ -475,6 +478,10 @@ What that means for a page and for the application around it:
|
|
|
475
478
|
itself out of reach — the page arriving could not even take the focus — and one
|
|
476
479
|
in a page kept while leaving would stay open for the whole movement. The page is
|
|
477
480
|
then taken down at once, as without a movement.
|
|
481
|
+
- **Not past a `<Loading>` around both pages.** When the page arriving suspends
|
|
482
|
+
to a boundary that holds the page being left too, the boundary parks both and
|
|
483
|
+
runs every cleanup in them: the page being left is then taken down when the
|
|
484
|
+
boundary resumes, instead of having all its effects run again while hidden.
|
|
478
485
|
- **Only where the page would have been taken down anyway.** A page rendered by
|
|
479
486
|
the same element on both sides — two routes, one component — is the same
|
|
480
487
|
instance carried over.
|
package/docs/scroll.md
CHANGED
|
@@ -121,7 +121,9 @@ say so with `"document"` or the element itself.
|
|
|
121
121
|
by MEASURING it: it lands where it was even if rows were inserted before it,
|
|
122
122
|
whatever the screen it was saved on. "Reopen a thread where I left it" is
|
|
123
123
|
already provided; keep the position whole, its `visibleCount` sizes the first
|
|
124
|
-
paint. A
|
|
124
|
+
paint. A `defaultScrolled` that changes before anyone has moved the list is
|
|
125
|
+
followed — a place read from an answer that refreshes (yesterday's "today", then
|
|
126
|
+
today's) — and it is compared by value, so it can be computed at every render. A list with an `id` also **comes back where it was** when its screen is
|
|
125
127
|
left and come back to, the way the page does (its `id` must name one list of the
|
|
126
128
|
app, see [below](#a-list-that-comes-back-one-id-one-list));
|
|
127
129
|
**`scrollResetOnNavigation`** opts out. A list scrolling the document that
|