@jsenv/navi 0.29.457 → 0.29.459

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.
@@ -11310,27 +11310,29 @@ const updateDocumentState = (value) => {
11310
11310
  * `state`:
11311
11311
  * - `undefined` — a neutral navigation: a link click, a `replaceUrl` writing a
11312
11312
  * search param. A **replace** stays on the entry the document is already on,
11313
- * so what that entry holds stays with it. A **push** opens a NEW entry, and
11314
- * what was written for the one being left — an open dialog, an expanded
11315
- * picker (see `useNavState`) — describes that entry alone: carried forward it
11316
- * would reopen on the next screen, and on the one after that, until something
11317
- * mounting the same id opens out of nowhere.
11313
+ * so what that entry holds stays with it. A **push** opens a NEW entry. One
11314
+ * arriving at another page (`isArrival`, see scroll_restoration.js) starts
11315
+ * empty: what was written for the entry being left — an open dialog, an
11316
+ * expanded picker (see `useNavState`) — describes that entry alone, and
11317
+ * carried forward it would reopen on the next screen, and on the one after
11318
+ * that, until something mounting the same id opens out of nowhere. A push
11319
+ * that keeps the page (a layer bound to a search param, opened over it)
11320
+ * carries the state: whatever holds it is still mounted under the new
11321
+ * address, and an empty entry would close it there — an expanded row
11322
+ * collapsing under the layer, expanding again when the back takes it away.
11318
11323
  * - `null` — an explicit reset.
11319
11324
  * - an object — built by the caller (`enter()`/`leave()` copy the current state
11320
11325
  * themselves), taken as given.
11321
11326
  *
11322
- * `sharedState` always wins: it describes the document, not the entry.
11323
- *
11324
- * The push/replace split is the Navigation API's own rule for a `navigate()`
11325
- * carrying no state — which is why via_navigation.js gets it from the browser
11326
- * and via_history.js has to spell it out to say the same thing.
11327
+ * `currentState` is what the entry being left holds. `sharedState` always
11328
+ * wins: it describes the document, not the entry.
11327
11329
  */
11328
11330
  const resolveEffectiveDocumentState = (
11329
11331
  state,
11330
- { navigationType, currentState, sharedState },
11332
+ { navigationType, isArrival, currentState, sharedState },
11331
11333
  ) => {
11332
11334
  if (state === undefined) {
11333
- if (navigationType === "push") {
11335
+ if (navigationType === "push" && isArrival) {
11334
11336
  return sharedState;
11335
11337
  }
11336
11338
  return { ...(currentState || {}), ...sharedState };
@@ -11874,6 +11876,8 @@ const startAtTop = (url, { from } = {}) => {
11874
11876
  positionByUrl.set(href, { x: 0, y: 0 });
11875
11877
  window.scrollTo({ top: 0, left: 0, behavior: "instant" });
11876
11878
  };
11879
+ // Also what decides whether a push carries the state of the page it keeps
11880
+ // (see resolveEffectiveDocumentState).
11877
11881
  const isArrival = (url, { from }) => {
11878
11882
  if (
11879
11883
  from !== undefined &&
@@ -12309,7 +12313,8 @@ const setupBrowserIntegrationViaHistory = ({
12309
12313
 
12310
12314
  const applyRoutingTask = (url, options) => {
12311
12315
  // Read before the history is written: the url the reader is being taken
12312
- // away from, which is what decides where a push lands (see startAtTop).
12316
+ // away from, which is what decides where a push lands (see startAtTop) and
12317
+ // whether it carries the state (see resolveEffectiveDocumentState).
12313
12318
  const urlLeft = readAddress();
12314
12319
  const {
12315
12320
  reason,
@@ -12329,6 +12334,7 @@ const setupBrowserIntegrationViaHistory = ({
12329
12334
  markUrlAsVisited(url);
12330
12335
  const effectiveState = resolveEffectiveDocumentState(state, {
12331
12336
  navigationType,
12337
+ isArrival: isArrival(url, { from: urlLeft }),
12332
12338
  currentState: getDocumentState(),
12333
12339
  sharedState: {
12334
12340
  jsenv_visited_urls: Array.from(visitedUrlSet),
@@ -39484,6 +39490,14 @@ const activeRouteReadsSignal = computed(() => {
39484
39490
  * params read: that address is the current one with the state set to the
39485
39491
  * popup's value (see `prefetch` on Button).
39486
39492
  * @param {object} [options]
39493
+ * @param {() => any} [options.when] - a condition on app state the action
39494
+ * asks under, read off signals like the params: falsy, it runs nothing.
39495
+ * Unlike a params function it leaves the action asking nothing of the
39496
+ * address, so a link to the route still prefetches an action without
39497
+ * params when the condition holds at that moment — a page's code only some
39498
+ * readers may download (`() => meIsAdminSignal.value`). A condition reading
39499
+ * the address belongs in the params: a link reads `when` as it is, not as
39500
+ * the address it leads to will make it.
39487
39501
  * @param {boolean} [options.prefetch=true] - `false` keeps the action from
39488
39502
  * being prerun ahead of what asks for it, when the read is not worth a
39489
39503
  * hover or a press that may not end in an opening.
@@ -39492,7 +39506,7 @@ const routeAction = (
39492
39506
  routeOrRoutes,
39493
39507
  action,
39494
39508
  paramsEffect,
39495
- { prefetch = true, ...options } = {},
39509
+ { when, prefetch = true, ...options } = {},
39496
39510
  ) => {
39497
39511
  if (typeof action === "function") {
39498
39512
  action = createAction(action);
@@ -39502,7 +39516,12 @@ const routeAction = (
39502
39516
  routes.length === 1
39503
39517
  ? routes[0].matchingSignal
39504
39518
  : anyMatchingRouteSignal(routes);
39505
- const readParams = paramsEffect || (() => true);
39519
+ const readParams = () => {
39520
+ if (when && !when()) {
39521
+ return null;
39522
+ }
39523
+ return paramsEffect ? paramsEffect() : true;
39524
+ };
39506
39525
  const readParamsAsked = () => {
39507
39526
  const matching = routeMatchingSignal.value;
39508
39527
  const params = readParams();
@@ -39538,10 +39557,13 @@ const routeAction = (
39538
39557
  for (const route of routes) {
39539
39558
  if (paramsEffect) {
39540
39559
  registerRouteStatePreload(route, (values) => {
39541
- prefetchAsIf(action, paramsEffect, values);
39560
+ prefetchAsIf(action, readParams, values);
39542
39561
  });
39543
39562
  } else {
39544
39563
  registerRoutePreload(route, () => {
39564
+ if (!untracked(readParams)) {
39565
+ return;
39566
+ }
39545
39567
  prerunAhead(action.bindParams(true));
39546
39568
  });
39547
39569
  }
@@ -39554,12 +39576,12 @@ const routeAction = (
39554
39576
  // (see readStateAsIf), prerun only when that changes them: params the state
39555
39577
  // leaves alone are the ones the action already runs on — or is meant not to,
39556
39578
  // and a prerun would retry a FAILED one behind the screen's back.
39557
- const prefetchAsIf = (action, paramsEffect, values) => {
39558
- const params = readStateAsIf(values, paramsEffect);
39579
+ const prefetchAsIf = (action, readParams, values) => {
39580
+ const params = readStateAsIf(values, readParams);
39559
39581
  if (!params) {
39560
39582
  return;
39561
39583
  }
39562
- if (compareTwoJsValues(params, untracked(paramsEffect))) {
39584
+ if (compareTwoJsValues(params, untracked(readParams))) {
39563
39585
  return;
39564
39586
  }
39565
39587
  prerunAhead(action.bindParams(params));