@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.
@@ -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 actions that
11707
- * render had nothing to draw for — the page's code among them — and for one
11708
- * that starts asking later, when the position did not fit what was there (see
11709
- * restoreScrollPositionOnLoad). Anything else only the page knows, and it says
11710
- * so (useHoldScrollRestoration). A wait can be long, so the reader reaching for
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
- // Where the document lands is the last arrival's to say: an arrival decided
11827
- // meanwhile — a page that sends the reader elsewhere as it mounts — replaces it
11828
- // (see startAtTop), and must not be scrolled to where the return was going.
11829
- // So does the reader reaching for the page before it is written: where they
11830
- // are is theirs (see installScrollRestoration).
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
- // `untilReached`: a write the document is too short for leaves it pending (see
11833
- // putBackOnLateAnswer).
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
- if (restoration.untilReached && fellShortOf(restoration.position)) {
11856
- restoration.due = false;
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
- * After a reload, the page's route actions are waited for without this — its
11918
- * code, a read the previous document did not keep. This is for what navi
11919
- * cannot see: a read the page starts itself, content whose height arrives late.
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, { untilReached: true });
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 the first render,
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 = untracked(readUnansweredRouteActions);
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.value) {
12065
+ for (const routeAction of activeRouteActionsSignal.peek()) {
12045
12066
  if (
12046
- routeAction.runningStateSignal.value === RUNNING &&
12047
- routeAction.dataSignal.value === undefined
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 its first render: an action gated on app
12056
- // state that render did not have yet (`when`, or params reading an app-wide
12057
- // read), and nothing tells "not yet" from "never" before it asks. So the
12058
- // position is written into what the page lets it, and a write the document
12059
- // could not hold is not the end of it: the next route action that starts
12060
- // without an answer is waited for like the first ones, and the position put
12061
- // back again. Not told when the restoration is dropped (pendingRestoration is
12062
- // no signal): it notices at its next run.
12063
- const putBackOnLateAnswer = (restoration) => {
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 (readUnansweredRouteActions().length === 0) {
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
- putBackOnceAnswered(restoration);
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 still holds the page being left and
12131
- // clamps it to that page's height. It is read at once all the same — a page
12132
- // left taller than the one arriving is clamped by the render itself, and that
12133
- // clamp is a scroll recorded under the url already returned to.
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
- putBack(restoration);
12181
+ putBackOnceAnswered(restoration);
12145
12182
  });
12146
12183
  };
12147
12184