@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.
@@ -11862,6 +11862,26 @@ const activeRouteActionsSignal = computed(() => {
11862
11862
  return active;
11863
11863
  });
11864
11864
 
11865
+ /**
11866
+ * Whether a route action of the page on screen asks nothing yet: its route
11867
+ * matches and its params (or `when`) say no. Gated on app state not there yet,
11868
+ * or never meant to ask on this page — nothing tells one from the other before
11869
+ * it asks. Read by the scroll restoration, which stays ready for it.
11870
+ *
11871
+ * @type {import("@preact/signals").ReadonlySignal<boolean>}
11872
+ */
11873
+ const routeActionYetToAskSignal = computed(() => {
11874
+ for (const {
11875
+ routeMatchingSignal,
11876
+ askingSignal,
11877
+ } of routeActionEntriesSignal.value) {
11878
+ if (routeMatchingSignal.value && !askingSignal.value) {
11879
+ return true;
11880
+ }
11881
+ }
11882
+ return false;
11883
+ });
11884
+
11865
11885
  let activeRouteReadsPrevious = new Set();
11866
11886
  /**
11867
11887
  * `activeRouteActionsSignal` without the page's code: what to rerun to read
@@ -11975,6 +11995,7 @@ const routeAction = (
11975
11995
  ...routeActionEntriesSignal.peek(),
11976
11996
  {
11977
11997
  routeAction: actionBoundToRoute,
11998
+ routeMatchingSignal,
11978
11999
  askingSignal: computed(() => Boolean(readParamsAsked())),
11979
12000
  },
11980
12001
  ];
@@ -12131,11 +12152,11 @@ const haveSameMembers = (setA, setB) => {
12131
12152
  * moment it is put back, and a position beyond what has arrived is clamped.
12132
12153
  * What navi knows a first render left out, it waits for: a `<Box
12133
12154
  * mount="after-paint">` is built before the offset that shows it is written
12134
- * (see mount_after_paint.jsx), and a reload waits for the route actions that
12135
- * render had nothing to draw for — the page's code among them — and for one
12136
- * that starts asking later, when the position did not fit what was there (see
12137
- * restoreScrollPositionOnLoad). Anything else only the page knows, and it says
12138
- * so (useHoldScrollRestoration). A wait can be long, so the reader reaching for
12155
+ * (see mount_after_paint.jsx), and a reload or a return waits for the route
12156
+ * actions the page has nothing to draw for — its code among them — including
12157
+ * one that starts asking after the position is written (see
12158
+ * putBackOnceAnswered). Anything else only the page knows, and it says so
12159
+ * (useHoldScrollRestoration). A wait can be long, so the reader reaching for
12139
12160
  * the page during it cancels the position: moved afterwards, they would lose
12140
12161
  * whatever they had started on.
12141
12162
  *
@@ -12250,17 +12271,16 @@ const suspendScrollRecording = () => {
12250
12271
  };
12251
12272
  };
12252
12273
 
12253
- // The position a reload or a return is about to put back, until it is written.
12254
- // Where the document lands is the last arrival's to say: an arrival decided
12255
- // meanwhile — a page that sends the reader elsewhere as it mounts — replaces it
12256
- // (see startAtTop), and must not be scrolled to where the return was going.
12257
- // So does the reader reaching for the page before it is written: where they
12258
- // are is theirs (see installScrollRestoration).
12274
+ // The position a reload or a return is about to put back, until it is written
12275
+ // into a page that will ask nothing more (see stayForLateAsks). Where the
12276
+ // document lands is the last arrival's to say: an arrival decided meanwhile — a
12277
+ // page that sends the reader elsewhere as it mounts — replaces it (see
12278
+ // startAtTop), and must not be scrolled to where the return was going. So does
12279
+ // the reader reaching for the page: where they are is theirs (see
12280
+ // installScrollRestoration).
12259
12281
  let pendingRestoration = null;
12260
- // `untilReached`: a write the document is too short for leaves it pending (see
12261
- // putBackOnLateAnswer).
12262
- const startRestoration = (position, { untilReached = false } = {}) => {
12263
- const restoration = { position, due: false, untilReached };
12282
+ const startRestoration = (position) => {
12283
+ const restoration = { position, due: false };
12264
12284
  pendingRestoration = restoration;
12265
12285
  return restoration;
12266
12286
  };
@@ -12275,16 +12295,13 @@ const putBack = (restoration) => {
12275
12295
  // The page is not whole yet: the last release puts it back.
12276
12296
  return;
12277
12297
  }
12278
- pendingRestoration = null;
12279
12298
  if (documentHoldCount) {
12299
+ pendingRestoration = null;
12280
12300
  return;
12281
12301
  }
12282
12302
  scrollTo(restoration.position);
12283
- if (restoration.untilReached && fellShortOf(restoration.position)) {
12284
- restoration.due = false;
12285
- pendingRestoration = restoration;
12286
- putBackOnLateAnswer(restoration);
12287
- }
12303
+ restoration.due = false;
12304
+ stayForLateAsks(restoration);
12288
12305
  };
12289
12306
 
12290
12307
  // A list scrolling the document that opens on a row puts the document where
@@ -12342,9 +12359,10 @@ const holdScrollRestoration = () => {
12342
12359
  * `held` turns false — unless the reader has reached for the page in the
12343
12360
  * meantime (a finger, a click, a wheel, a key), or gone elsewhere.
12344
12361
  *
12345
- * After a reload, the page's route actions are waited for without this — its
12346
- * code, a read the previous document did not keep. This is for what navi
12347
- * cannot see: a read the page starts itself, content whose height arrives late.
12362
+ * The page's route actions are waited for without this — its code, a read
12363
+ * with no answer yet, one gated on app state that asks later. This is for
12364
+ * what navi cannot see: a read the page starts itself, content whose height
12365
+ * arrives late.
12348
12366
  *
12349
12367
  * ```jsx
12350
12368
  * const [games] = useAsyncData(GAMES, { loading: true, run: true });
@@ -12429,19 +12447,22 @@ const installScrollRestoration = ({ readAddress }) => {
12429
12447
  // page the position was taken on, and the position stays for the reload that
12430
12448
  // brings the page back.
12431
12449
  const restoreScrollPositionOnLoad = (position) => {
12432
- const restoration = startRestoration(position, { untilReached: true });
12450
+ const restoration = startRestoration(position);
12433
12451
  const stopListening = observeRouteRender(() => {
12434
12452
  stopListening();
12435
12453
  putBackOnceAnswered(restoration);
12436
12454
  });
12437
12455
  };
12438
12456
 
12439
- // Reads the actions to wait for at once: from the commit of the first render,
12457
+ // Reads the actions to wait for at once: called from the commit of a render,
12440
12458
  // the ones it had nothing to draw for are those still running then. An answer
12441
12459
  // can start another read (params made of that answer), so the actions asking
12442
12460
  // are read again before the position is written.
12461
+ //
12462
+ // The same for a reload and a return: a return to a page this document has
12463
+ // never drawn, or whose read was reset since, finds no answer either.
12443
12464
  const putBackOnceAnswered = (restoration) => {
12444
- const unanswered = untracked(readUnansweredRouteActions);
12465
+ const unanswered = readUnansweredRouteActions();
12445
12466
  if (unanswered.length === 0) {
12446
12467
  queueMicrotask(() => {
12447
12468
  putBack(restoration);
@@ -12469,10 +12490,10 @@ const putBackOnceAnswered = (restoration) => {
12469
12490
 
12470
12491
  const readUnansweredRouteActions = () => {
12471
12492
  const unanswered = [];
12472
- for (const routeAction of activeRouteActionsSignal.value) {
12493
+ for (const routeAction of activeRouteActionsSignal.peek()) {
12473
12494
  if (
12474
- routeAction.runningStateSignal.value === RUNNING &&
12475
- routeAction.dataSignal.value === undefined
12495
+ routeAction.runningStateSignal.peek() === RUNNING &&
12496
+ routeAction.dataSignal.peek() === undefined
12476
12497
  ) {
12477
12498
  unanswered.push(routeAction);
12478
12499
  }
@@ -12480,33 +12501,48 @@ const readUnansweredRouteActions = () => {
12480
12501
  return unanswered;
12481
12502
  };
12482
12503
 
12483
- // A page can start asking after its first render: an action gated on app
12484
- // state that render did not have yet (`when`, or params reading an app-wide
12485
- // read), and nothing tells "not yet" from "never" before it asks. So the
12486
- // position is written into what the page lets it, and a write the document
12487
- // could not hold is not the end of it: the next route action that starts
12488
- // without an answer is waited for like the first ones, and the position put
12489
- // back again. Not told when the restoration is dropped (pendingRestoration is
12490
- // no signal): it notices at its next run.
12491
- const putBackOnLateAnswer = (restoration) => {
12504
+ // A page can start asking after the position is written: an action gated on
12505
+ // app state not there yet (`when`, or params reading an app-wide read), and
12506
+ // nothing tells "not yet" from "never" before it asks (see
12507
+ // routeActionYetToAskSignal). So a written position stays pending while a
12508
+ // route action of the page has not asked: the next one that starts without an
12509
+ // answer is waited for like the first ones, and the position put back once it
12510
+ // is drawn. Not told when the restoration is dropped (pendingRestoration is no
12511
+ // signal): it notices at its next run.
12512
+ const stayForLateAsks = (restoration) => {
12492
12513
  effect(function () {
12514
+ let running = false;
12515
+ let starting = false;
12516
+ for (const routeAction of activeRouteActionsSignal.value) {
12517
+ if (routeAction.dataSignal.value !== undefined) {
12518
+ continue;
12519
+ }
12520
+ const runningState = routeAction.runningStateSignal.value;
12521
+ if (runningState === RUNNING) {
12522
+ running = true;
12523
+ } else if (runningState === IDLE) {
12524
+ // Asking, and run by an effect of the same batch that has not run yet.
12525
+ starting = true;
12526
+ }
12527
+ }
12528
+ const yetToAsk = routeActionYetToAskSignal.value;
12493
12529
  if (pendingRestoration !== restoration) {
12494
12530
  this.dispose();
12495
12531
  return;
12496
12532
  }
12497
- if (readUnansweredRouteActions().length === 0) {
12533
+ if (running) {
12534
+ this.dispose();
12535
+ putBackOnceAnswered(restoration);
12536
+ return;
12537
+ }
12538
+ if (starting || yetToAsk) {
12498
12539
  return;
12499
12540
  }
12500
12541
  this.dispose();
12501
- putBackOnceAnswered(restoration);
12542
+ pendingRestoration = null;
12502
12543
  });
12503
12544
  };
12504
12545
 
12505
- // A pixel of slack: the offset kept is the fractional one the browser reports.
12506
- const fellShortOf = (position) => {
12507
- return position.y - window.scrollY > 1;
12508
- };
12509
-
12510
12546
  // Calls back once none of the actions is running, saying whether all of them
12511
12547
  // completed.
12512
12548
  const whenSettled = (actions, callback) => {
@@ -12555,10 +12591,11 @@ const restoreScrollPosition = (url) => {
12555
12591
  };
12556
12592
 
12557
12593
  // A traversal back to `url`. The offset is written once the page returned to is
12558
- // rendered: written sooner, the document still holds the page being left and
12559
- // clamps it to that page's height. It is read at once all the same — a page
12560
- // left taller than the one arriving is clamped by the render itself, and that
12561
- // clamp is a scroll recorded under the url already returned to.
12594
+ // rendered and answered (see putBackOnceAnswered): written sooner, the document
12595
+ // still holds the page being left, or a stand-in for the one arriving, and
12596
+ // clamps it to that height. It is read at once all the same — a page left
12597
+ // taller than the one arriving is clamped by the render itself, and that clamp
12598
+ // is a scroll recorded under the url already returned to.
12562
12599
  const restoreScrollPositionOnReturn = (url) => {
12563
12600
  pendingRestoration = null;
12564
12601
  const href = new URL(url, window.location.href).href;
@@ -12569,7 +12606,7 @@ const restoreScrollPositionOnReturn = (url) => {
12569
12606
  }
12570
12607
  const restoration = startRestoration(position);
12571
12608
  whenPageRendered(() => {
12572
- putBack(restoration);
12609
+ putBackOnceAnswered(restoration);
12573
12610
  });
12574
12611
  };
12575
12612