@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.
@@ -10882,27 +10882,29 @@ const updateDocumentState = (value) => {
10882
10882
  * `state`:
10883
10883
  * - `undefined` — a neutral navigation: a link click, a `replaceUrl` writing a
10884
10884
  * search param. A **replace** stays on the entry the document is already on,
10885
- * so what that entry holds stays with it. A **push** opens a NEW entry, and
10886
- * what was written for the one being left — an open dialog, an expanded
10887
- * picker (see `useNavState`) — describes that entry alone: carried forward it
10888
- * would reopen on the next screen, and on the one after that, until something
10889
- * mounting the same id opens out of nowhere.
10885
+ * so what that entry holds stays with it. A **push** opens a NEW entry. One
10886
+ * arriving at another page (`isArrival`, see scroll_restoration.js) starts
10887
+ * empty: what was written for the entry being left — an open dialog, an
10888
+ * expanded picker (see `useNavState`) — describes that entry alone, and
10889
+ * carried forward it would reopen on the next screen, and on the one after
10890
+ * that, until something mounting the same id opens out of nowhere. A push
10891
+ * that keeps the page (a layer bound to a search param, opened over it)
10892
+ * carries the state: whatever holds it is still mounted under the new
10893
+ * address, and an empty entry would close it there — an expanded row
10894
+ * collapsing under the layer, expanding again when the back takes it away.
10890
10895
  * - `null` — an explicit reset.
10891
10896
  * - an object — built by the caller (`enter()`/`leave()` copy the current state
10892
10897
  * themselves), taken as given.
10893
10898
  *
10894
- * `sharedState` always wins: it describes the document, not the entry.
10895
- *
10896
- * The push/replace split is the Navigation API's own rule for a `navigate()`
10897
- * carrying no state — which is why via_navigation.js gets it from the browser
10898
- * and via_history.js has to spell it out to say the same thing.
10899
+ * `currentState` is what the entry being left holds. `sharedState` always
10900
+ * wins: it describes the document, not the entry.
10899
10901
  */
10900
10902
  const resolveEffectiveDocumentState = (
10901
10903
  state,
10902
- { navigationType, currentState, sharedState },
10904
+ { navigationType, isArrival, currentState, sharedState },
10903
10905
  ) => {
10904
10906
  if (state === undefined) {
10905
- if (navigationType === "push") {
10907
+ if (navigationType === "push" && isArrival) {
10906
10908
  return sharedState;
10907
10909
  }
10908
10910
  return { ...(currentState || {}), ...sharedState };
@@ -11446,6 +11448,8 @@ const startAtTop = (url, { from } = {}) => {
11446
11448
  positionByUrl.set(href, { x: 0, y: 0 });
11447
11449
  window.scrollTo({ top: 0, left: 0, behavior: "instant" });
11448
11450
  };
11451
+ // Also what decides whether a push carries the state of the page it keeps
11452
+ // (see resolveEffectiveDocumentState).
11449
11453
  const isArrival = (url, { from }) => {
11450
11454
  if (
11451
11455
  from !== undefined &&
@@ -11881,7 +11885,8 @@ const setupBrowserIntegrationViaHistory = ({
11881
11885
 
11882
11886
  const applyRoutingTask = (url, options) => {
11883
11887
  // Read before the history is written: the url the reader is being taken
11884
- // away from, which is what decides where a push lands (see startAtTop).
11888
+ // away from, which is what decides where a push lands (see startAtTop) and
11889
+ // whether it carries the state (see resolveEffectiveDocumentState).
11885
11890
  const urlLeft = readAddress();
11886
11891
  const {
11887
11892
  reason,
@@ -11901,6 +11906,7 @@ const setupBrowserIntegrationViaHistory = ({
11901
11906
  markUrlAsVisited(url);
11902
11907
  const effectiveState = resolveEffectiveDocumentState(state, {
11903
11908
  navigationType,
11909
+ isArrival: isArrival(url, { from: urlLeft }),
11904
11910
  currentState: getDocumentState(),
11905
11911
  sharedState: {
11906
11912
  jsenv_visited_urls: Array.from(visitedUrlSet),
@@ -38488,6 +38494,14 @@ const activeRouteReadsSignal = computed(() => {
38488
38494
  * params read: that address is the current one with the state set to the
38489
38495
  * popup's value (see `prefetch` on Button).
38490
38496
  * @param {object} [options]
38497
+ * @param {() => any} [options.when] - a condition on app state the action
38498
+ * asks under, read off signals like the params: falsy, it runs nothing.
38499
+ * Unlike a params function it leaves the action asking nothing of the
38500
+ * address, so a link to the route still prefetches an action without
38501
+ * params when the condition holds at that moment — a page's code only some
38502
+ * readers may download (`() => meIsAdminSignal.value`). A condition reading
38503
+ * the address belongs in the params: a link reads `when` as it is, not as
38504
+ * the address it leads to will make it.
38491
38505
  * @param {boolean} [options.prefetch=true] - `false` keeps the action from
38492
38506
  * being prerun ahead of what asks for it, when the read is not worth a
38493
38507
  * hover or a press that may not end in an opening.
@@ -38496,7 +38510,7 @@ const routeAction = (
38496
38510
  routeOrRoutes,
38497
38511
  action,
38498
38512
  paramsEffect,
38499
- { prefetch = true, ...options } = {},
38513
+ { when, prefetch = true, ...options } = {},
38500
38514
  ) => {
38501
38515
  if (typeof action === "function") {
38502
38516
  action = createAction(action);
@@ -38506,7 +38520,12 @@ const routeAction = (
38506
38520
  routes.length === 1
38507
38521
  ? routes[0].matchingSignal
38508
38522
  : anyMatchingRouteSignal(routes);
38509
- const readParams = paramsEffect || (() => true);
38523
+ const readParams = () => {
38524
+ if (when && !when()) {
38525
+ return null;
38526
+ }
38527
+ return paramsEffect ? paramsEffect() : true;
38528
+ };
38510
38529
  const readParamsAsked = () => {
38511
38530
  const matching = routeMatchingSignal.value;
38512
38531
  const params = readParams();
@@ -38542,10 +38561,13 @@ const routeAction = (
38542
38561
  for (const route of routes) {
38543
38562
  if (paramsEffect) {
38544
38563
  registerRouteStatePreload(route, (values) => {
38545
- prefetchAsIf(action, paramsEffect, values);
38564
+ prefetchAsIf(action, readParams, values);
38546
38565
  });
38547
38566
  } else {
38548
38567
  registerRoutePreload(route, () => {
38568
+ if (!untracked(readParams)) {
38569
+ return;
38570
+ }
38549
38571
  prerunAhead(action.bindParams(true));
38550
38572
  });
38551
38573
  }
@@ -38558,12 +38580,12 @@ const routeAction = (
38558
38580
  // (see readStateAsIf), prerun only when that changes them: params the state
38559
38581
  // leaves alone are the ones the action already runs on — or is meant not to,
38560
38582
  // and a prerun would retry a FAILED one behind the screen's back.
38561
- const prefetchAsIf = (action, paramsEffect, values) => {
38562
- const params = readStateAsIf(values, paramsEffect);
38583
+ const prefetchAsIf = (action, readParams, values) => {
38584
+ const params = readStateAsIf(values, readParams);
38563
38585
  if (!params) {
38564
38586
  return;
38565
38587
  }
38566
- if (compareTwoJsValues(params, untracked(paramsEffect))) {
38588
+ if (compareTwoJsValues(params, untracked(readParams))) {
38567
38589
  return;
38568
38590
  }
38569
38591
  prerunAhead(action.bindParams(params));