@jsenv/navi 0.29.462 → 0.29.463
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 +88 -51
- package/dist/dev/jsenv_navi.js.map +3 -3
- package/dist/jsenv_navi.js +88 -51
- package/dist/jsenv_navi.js.map +3 -3
- package/docs/navigation.md +20 -19
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -11434,6 +11434,26 @@ const activeRouteActionsSignal = computed(() => {
|
|
|
11434
11434
|
return active;
|
|
11435
11435
|
});
|
|
11436
11436
|
|
|
11437
|
+
/**
|
|
11438
|
+
* Whether a route action of the page on screen asks nothing yet: its route
|
|
11439
|
+
* matches and its params (or `when`) say no. Gated on app state not there yet,
|
|
11440
|
+
* or never meant to ask on this page — nothing tells one from the other before
|
|
11441
|
+
* it asks. Read by the scroll restoration, which stays ready for it.
|
|
11442
|
+
*
|
|
11443
|
+
* @type {import("@preact/signals").ReadonlySignal<boolean>}
|
|
11444
|
+
*/
|
|
11445
|
+
const routeActionYetToAskSignal = computed(() => {
|
|
11446
|
+
for (const {
|
|
11447
|
+
routeMatchingSignal,
|
|
11448
|
+
askingSignal,
|
|
11449
|
+
} of routeActionEntriesSignal.value) {
|
|
11450
|
+
if (routeMatchingSignal.value && !askingSignal.value) {
|
|
11451
|
+
return true;
|
|
11452
|
+
}
|
|
11453
|
+
}
|
|
11454
|
+
return false;
|
|
11455
|
+
});
|
|
11456
|
+
|
|
11437
11457
|
let activeRouteReadsPrevious = new Set();
|
|
11438
11458
|
/**
|
|
11439
11459
|
* `activeRouteActionsSignal` without the page's code: what to rerun to read
|
|
@@ -11547,6 +11567,7 @@ const routeAction = (
|
|
|
11547
11567
|
...routeActionEntriesSignal.peek(),
|
|
11548
11568
|
{
|
|
11549
11569
|
routeAction: actionBoundToRoute,
|
|
11570
|
+
routeMatchingSignal,
|
|
11550
11571
|
askingSignal: computed(() => Boolean(readParamsAsked())),
|
|
11551
11572
|
},
|
|
11552
11573
|
];
|
|
@@ -11703,11 +11724,11 @@ const haveSameMembers = (setA, setB) => {
|
|
|
11703
11724
|
* moment it is put back, and a position beyond what has arrived is clamped.
|
|
11704
11725
|
* What navi knows a first render left out, it waits for: a `<Box
|
|
11705
11726
|
* mount="after-paint">` is built before the offset that shows it is written
|
|
11706
|
-
* (see mount_after_paint.jsx), and a reload waits for the route
|
|
11707
|
-
*
|
|
11708
|
-
* that starts asking
|
|
11709
|
-
*
|
|
11710
|
-
*
|
|
11727
|
+
* (see mount_after_paint.jsx), and a reload or a return waits for the route
|
|
11728
|
+
* actions the page has nothing to draw for — its code among them — including
|
|
11729
|
+
* one that starts asking after the position is written (see
|
|
11730
|
+
* putBackOnceAnswered). Anything else only the page knows, and it says so
|
|
11731
|
+
* (useHoldScrollRestoration). A wait can be long, so the reader reaching for
|
|
11711
11732
|
* the page during it cancels the position: moved afterwards, they would lose
|
|
11712
11733
|
* whatever they had started on.
|
|
11713
11734
|
*
|
|
@@ -11822,17 +11843,16 @@ const suspendScrollRecording = () => {
|
|
|
11822
11843
|
};
|
|
11823
11844
|
};
|
|
11824
11845
|
|
|
11825
|
-
// The position a reload or a return is about to put back, until it is written
|
|
11826
|
-
//
|
|
11827
|
-
//
|
|
11828
|
-
//
|
|
11829
|
-
//
|
|
11830
|
-
// are is theirs (see
|
|
11846
|
+
// The position a reload or a return is about to put back, until it is written
|
|
11847
|
+
// into a page that will ask nothing more (see stayForLateAsks). Where the
|
|
11848
|
+
// document lands is the last arrival's to say: an arrival decided meanwhile — a
|
|
11849
|
+
// page that sends the reader elsewhere as it mounts — replaces it (see
|
|
11850
|
+
// startAtTop), and must not be scrolled to where the return was going. So does
|
|
11851
|
+
// the reader reaching for the page: where they are is theirs (see
|
|
11852
|
+
// installScrollRestoration).
|
|
11831
11853
|
let pendingRestoration = null;
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
const startRestoration = (position, { untilReached = false } = {}) => {
|
|
11835
|
-
const restoration = { position, due: false, untilReached };
|
|
11854
|
+
const startRestoration = (position) => {
|
|
11855
|
+
const restoration = { position, due: false };
|
|
11836
11856
|
pendingRestoration = restoration;
|
|
11837
11857
|
return restoration;
|
|
11838
11858
|
};
|
|
@@ -11847,16 +11867,13 @@ const putBack = (restoration) => {
|
|
|
11847
11867
|
// The page is not whole yet: the last release puts it back.
|
|
11848
11868
|
return;
|
|
11849
11869
|
}
|
|
11850
|
-
pendingRestoration = null;
|
|
11851
11870
|
if (documentHoldCount) {
|
|
11871
|
+
pendingRestoration = null;
|
|
11852
11872
|
return;
|
|
11853
11873
|
}
|
|
11854
11874
|
scrollTo(restoration.position);
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
pendingRestoration = restoration;
|
|
11858
|
-
putBackOnLateAnswer(restoration);
|
|
11859
|
-
}
|
|
11875
|
+
restoration.due = false;
|
|
11876
|
+
stayForLateAsks(restoration);
|
|
11860
11877
|
};
|
|
11861
11878
|
|
|
11862
11879
|
// A list scrolling the document that opens on a row puts the document where
|
|
@@ -11914,9 +11931,10 @@ const holdScrollRestoration = () => {
|
|
|
11914
11931
|
* `held` turns false — unless the reader has reached for the page in the
|
|
11915
11932
|
* meantime (a finger, a click, a wheel, a key), or gone elsewhere.
|
|
11916
11933
|
*
|
|
11917
|
-
*
|
|
11918
|
-
*
|
|
11919
|
-
* cannot see: a read the page starts itself, content whose height
|
|
11934
|
+
* The page's route actions are waited for without this — its code, a read
|
|
11935
|
+
* with no answer yet, one gated on app state that asks later. This is for
|
|
11936
|
+
* what navi cannot see: a read the page starts itself, content whose height
|
|
11937
|
+
* arrives late.
|
|
11920
11938
|
*
|
|
11921
11939
|
* ```jsx
|
|
11922
11940
|
* const [games] = useAsyncData(GAMES, { loading: true, run: true });
|
|
@@ -12001,19 +12019,22 @@ const installScrollRestoration = ({ readAddress }) => {
|
|
|
12001
12019
|
// page the position was taken on, and the position stays for the reload that
|
|
12002
12020
|
// brings the page back.
|
|
12003
12021
|
const restoreScrollPositionOnLoad = (position) => {
|
|
12004
|
-
const restoration = startRestoration(position
|
|
12022
|
+
const restoration = startRestoration(position);
|
|
12005
12023
|
const stopListening = observeRouteRender(() => {
|
|
12006
12024
|
stopListening();
|
|
12007
12025
|
putBackOnceAnswered(restoration);
|
|
12008
12026
|
});
|
|
12009
12027
|
};
|
|
12010
12028
|
|
|
12011
|
-
// Reads the actions to wait for at once: from the commit of
|
|
12029
|
+
// Reads the actions to wait for at once: called from the commit of a render,
|
|
12012
12030
|
// the ones it had nothing to draw for are those still running then. An answer
|
|
12013
12031
|
// can start another read (params made of that answer), so the actions asking
|
|
12014
12032
|
// are read again before the position is written.
|
|
12033
|
+
//
|
|
12034
|
+
// The same for a reload and a return: a return to a page this document has
|
|
12035
|
+
// never drawn, or whose read was reset since, finds no answer either.
|
|
12015
12036
|
const putBackOnceAnswered = (restoration) => {
|
|
12016
|
-
const unanswered =
|
|
12037
|
+
const unanswered = readUnansweredRouteActions();
|
|
12017
12038
|
if (unanswered.length === 0) {
|
|
12018
12039
|
queueMicrotask(() => {
|
|
12019
12040
|
putBack(restoration);
|
|
@@ -12041,10 +12062,10 @@ const putBackOnceAnswered = (restoration) => {
|
|
|
12041
12062
|
|
|
12042
12063
|
const readUnansweredRouteActions = () => {
|
|
12043
12064
|
const unanswered = [];
|
|
12044
|
-
for (const routeAction of activeRouteActionsSignal.
|
|
12065
|
+
for (const routeAction of activeRouteActionsSignal.peek()) {
|
|
12045
12066
|
if (
|
|
12046
|
-
routeAction.runningStateSignal.
|
|
12047
|
-
routeAction.dataSignal.
|
|
12067
|
+
routeAction.runningStateSignal.peek() === RUNNING &&
|
|
12068
|
+
routeAction.dataSignal.peek() === undefined
|
|
12048
12069
|
) {
|
|
12049
12070
|
unanswered.push(routeAction);
|
|
12050
12071
|
}
|
|
@@ -12052,33 +12073,48 @@ const readUnansweredRouteActions = () => {
|
|
|
12052
12073
|
return unanswered;
|
|
12053
12074
|
};
|
|
12054
12075
|
|
|
12055
|
-
// A page can start asking after
|
|
12056
|
-
// state
|
|
12057
|
-
//
|
|
12058
|
-
//
|
|
12059
|
-
//
|
|
12060
|
-
//
|
|
12061
|
-
//
|
|
12062
|
-
//
|
|
12063
|
-
const
|
|
12076
|
+
// A page can start asking after the position is written: an action gated on
|
|
12077
|
+
// app state not there yet (`when`, or params reading an app-wide read), and
|
|
12078
|
+
// nothing tells "not yet" from "never" before it asks (see
|
|
12079
|
+
// routeActionYetToAskSignal). So a written position stays pending while a
|
|
12080
|
+
// route action of the page has not asked: the next one that starts without an
|
|
12081
|
+
// answer is waited for like the first ones, and the position put back once it
|
|
12082
|
+
// is drawn. Not told when the restoration is dropped (pendingRestoration is no
|
|
12083
|
+
// signal): it notices at its next run.
|
|
12084
|
+
const stayForLateAsks = (restoration) => {
|
|
12064
12085
|
effect(function () {
|
|
12086
|
+
let running = false;
|
|
12087
|
+
let starting = false;
|
|
12088
|
+
for (const routeAction of activeRouteActionsSignal.value) {
|
|
12089
|
+
if (routeAction.dataSignal.value !== undefined) {
|
|
12090
|
+
continue;
|
|
12091
|
+
}
|
|
12092
|
+
const runningState = routeAction.runningStateSignal.value;
|
|
12093
|
+
if (runningState === RUNNING) {
|
|
12094
|
+
running = true;
|
|
12095
|
+
} else if (runningState === IDLE) {
|
|
12096
|
+
// Asking, and run by an effect of the same batch that has not run yet.
|
|
12097
|
+
starting = true;
|
|
12098
|
+
}
|
|
12099
|
+
}
|
|
12100
|
+
const yetToAsk = routeActionYetToAskSignal.value;
|
|
12065
12101
|
if (pendingRestoration !== restoration) {
|
|
12066
12102
|
this.dispose();
|
|
12067
12103
|
return;
|
|
12068
12104
|
}
|
|
12069
|
-
if (
|
|
12105
|
+
if (running) {
|
|
12106
|
+
this.dispose();
|
|
12107
|
+
putBackOnceAnswered(restoration);
|
|
12108
|
+
return;
|
|
12109
|
+
}
|
|
12110
|
+
if (starting || yetToAsk) {
|
|
12070
12111
|
return;
|
|
12071
12112
|
}
|
|
12072
12113
|
this.dispose();
|
|
12073
|
-
|
|
12114
|
+
pendingRestoration = null;
|
|
12074
12115
|
});
|
|
12075
12116
|
};
|
|
12076
12117
|
|
|
12077
|
-
// A pixel of slack: the offset kept is the fractional one the browser reports.
|
|
12078
|
-
const fellShortOf = (position) => {
|
|
12079
|
-
return position.y - window.scrollY > 1;
|
|
12080
|
-
};
|
|
12081
|
-
|
|
12082
12118
|
// Calls back once none of the actions is running, saying whether all of them
|
|
12083
12119
|
// completed.
|
|
12084
12120
|
const whenSettled = (actions, callback) => {
|
|
@@ -12127,10 +12163,11 @@ const restoreScrollPosition = (url) => {
|
|
|
12127
12163
|
};
|
|
12128
12164
|
|
|
12129
12165
|
// A traversal back to `url`. The offset is written once the page returned to is
|
|
12130
|
-
// rendered: written sooner, the document
|
|
12131
|
-
//
|
|
12132
|
-
//
|
|
12133
|
-
//
|
|
12166
|
+
// rendered and answered (see putBackOnceAnswered): written sooner, the document
|
|
12167
|
+
// still holds the page being left, or a stand-in for the one arriving, and
|
|
12168
|
+
// clamps it to that height. It is read at once all the same — a page left
|
|
12169
|
+
// taller than the one arriving is clamped by the render itself, and that clamp
|
|
12170
|
+
// is a scroll recorded under the url already returned to.
|
|
12134
12171
|
const restoreScrollPositionOnReturn = (url) => {
|
|
12135
12172
|
pendingRestoration = null;
|
|
12136
12173
|
const href = new URL(url, window.location.href).href;
|
|
@@ -12141,7 +12178,7 @@ const restoreScrollPositionOnReturn = (url) => {
|
|
|
12141
12178
|
}
|
|
12142
12179
|
const restoration = startRestoration(position);
|
|
12143
12180
|
whenPageRendered(() => {
|
|
12144
|
-
|
|
12181
|
+
putBackOnceAnswered(restoration);
|
|
12145
12182
|
});
|
|
12146
12183
|
};
|
|
12147
12184
|
|