@jsenv/navi 0.29.327 → 0.29.329

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.
@@ -7071,6 +7071,16 @@ const positionCallout = (
7071
7071
  }
7072
7072
  return;
7073
7073
  }
7074
+ if (!calloutElement.isConnected) {
7075
+ // The callout is drawn inside the anchor's subtree (calloutContainer),
7076
+ // so it leaves the document with it — a Suspense boundary parking
7077
+ // the screen while an action re-runs keeps the subtree mounted, refs
7078
+ // intact, but detached. showPopover() throws on a detached element,
7079
+ // and leaving the document already took it out of the top layer, so
7080
+ // there is nothing to hide either: same as ancestorClosed, wait for
7081
+ // the next check, which shows it again once the subtree is back.
7082
+ return;
7083
+ }
7074
7084
  if (!calloutElement.matches(":popover-open")) {
7075
7085
  if (debug) {
7076
7086
  debug(event, "showing callout because anchor is visible again");
@@ -23516,10 +23526,16 @@ const createRoutePattern = (
23516
23526
  // search params are updated. When buildMostPreciseUrl performs an ancestor
23517
23527
  // optimisation (e.g. "/map/isochrone/compare" → "/map/isochrone") it is trusted
23518
23528
  // as-is because the built pathname will differ from the route's own base pathname.
23519
- // Weak params are never inherited from their signal, but a url that already
23520
- // carries one keeps it: staying on the same screen while another param
23521
- // changes must not end the visit this param qualifies.
23522
- const carryOverWeakParams = (currentUrl, params) => {
23529
+ // A weak param is never inherited from its signal by a url built for a link
23530
+ // (see readSignalForUrlBuild). A url amended in place is another matter: the
23531
+ // route is matching, so the signal IS this visit's state, kept in step with
23532
+ // the address by every routing pass — and what it holds is carried over, so
23533
+ // that changing another param does not end the visit this one qualifies.
23534
+ // Read from the signal, not from the url being amended: several signals
23535
+ // written in one batch flush one url write at a time, and the routing pass
23536
+ // of the first would take the others for gone — and reset them — if the url
23537
+ // it wrote did not already carry what they hold.
23538
+ const carryOverWeakParams = (params) => {
23523
23539
  let paramsWithWeak = params;
23524
23540
  for (const connection of connections) {
23525
23541
  const { paramName } = connection;
@@ -23527,17 +23543,13 @@ const createRoutePattern = (
23527
23543
  continue;
23528
23544
  }
23529
23545
  const currentValue = connection.signal.peek();
23530
- if (currentValue === undefined) {
23531
- continue;
23532
- }
23533
- const currentParams = applyOn(currentUrl);
23534
- if (!currentParams || currentParams[paramName] === undefined) {
23546
+ if (connection.isDefaultValue(currentValue)) {
23535
23547
  continue;
23536
23548
  }
23537
23549
  if (paramsWithWeak === params) {
23538
23550
  paramsWithWeak = { ...params };
23539
23551
  }
23540
- paramsWithWeak[paramName] = currentParams[paramName];
23552
+ paramsWithWeak[paramName] = currentValue;
23541
23553
  }
23542
23554
  return paramsWithWeak;
23543
23555
  };
@@ -23573,7 +23585,7 @@ const createRoutePattern = (
23573
23585
  };
23574
23586
 
23575
23587
  const buildUrlPreservingPath = (currentUrl, params = {}) => {
23576
- params = carryOverWeakParams(currentUrl, params);
23588
+ params = carryOverWeakParams(params);
23577
23589
  if (currentUrl) {
23578
23590
  params = carryOverPathParams(currentUrl, params);
23579
23591
  }
@@ -26438,20 +26450,28 @@ const setupBrowserIntegrationViaHistory = ({
26438
26450
  // traversal to another document is a full page load, which no press on a
26439
26451
  // link asked for — the entries that are ours are recorded as they are
26440
26452
  // created, starting with the one this document was loaded into.
26453
+ //
26454
+ // The presence of window.navigation says nothing about the entry: the spec
26455
+ // makes currentEntry null in a document that is not fully active, and iOS
26456
+ // Safari answers null in a document that plainly is (a booted app, minutes
26457
+ // into a session). Nothing can be done about it; an entry that cannot be
26458
+ // read is treated as a browser without the API, which only costs the
26459
+ // adjacency.
26441
26460
  const sameDocumentEntryKeys = new Set();
26442
26461
  const rememberEntryIsOfThisDocument = () => {
26443
- if (window.navigation) {
26444
- sameDocumentEntryKeys.add(window.navigation.currentEntry.key);
26462
+ const currentEntry = window.navigation?.currentEntry;
26463
+ if (currentEntry) {
26464
+ sameDocumentEntryKeys.add(currentEntry.key);
26445
26465
  }
26446
26466
  };
26447
26467
  rememberEntryIsOfThisDocument();
26448
26468
  const adjacentEntryDelta = (url) => {
26449
- const { navigation } = window;
26450
- if (!navigation) {
26469
+ const currentEntry = window.navigation?.currentEntry;
26470
+ if (!currentEntry) {
26451
26471
  return 0;
26452
26472
  }
26453
- const entries = navigation.entries();
26454
- const index = navigation.currentEntry.index;
26473
+ const entries = window.navigation.entries();
26474
+ const index = currentEntry.index;
26455
26475
  // Behind first: when the same page stands on both sides (A, B, A and one
26456
26476
  // is on B), a link to it reads as going back.
26457
26477
  for (const delta of [-1, 1]) {
@@ -35880,7 +35900,7 @@ const useUIStateController = (
35880
35900
  // reach until that popup opens. Letting it block interaction would make
35881
35901
  // a picker whose content is loading impossible to open at all.
35882
35902
  getInteractionBlockingControls: () => [],
35883
- onUIAction: (e, { skipCommand } = {}) => {
35903
+ onUIAction: (e, { skipCommand, skipBoundSignal } = {}) => {
35884
35904
  if (controlType === "button" && controller.controlHostProps.name) {
35885
35905
  const buttonName = controller.controlHostProps.name;
35886
35906
  const parentController = controller.parentUIStateController;
@@ -35906,7 +35926,9 @@ const useUIStateController = (
35906
35926
  }
35907
35927
  // Trigger uiAction/command side effects without changing UI state.
35908
35928
  const currentUIState = controller.uiState;
35909
- writeBoundSignal(currentUIState);
35929
+ if (!skipBoundSignal) {
35930
+ writeBoundSignal(currentUIState);
35931
+ }
35910
35932
  s.uiActionInternal?.(currentUIState, e);
35911
35933
  if (s.uiAction) {
35912
35934
  s.uiAction(currentUIState, e);
@@ -36128,7 +36150,15 @@ const useUIStateController = (
36128
36150
  // sync, but do NOT fire the command and do NOT notify the parent —
36129
36151
  // both would cause an infinite loop when a parent cascades state
36130
36152
  // down to its children (child command would re-trigger the cascade).
36131
- controller.onUIAction(e, { skipCommand: true });
36153
+ // A control following what it was given (state_prop_change) does
36154
+ // not write its bound signal either: the signal is what it
36155
+ // follows. The one write that would change it is an emptied
36156
+ // signal refilled with the suggestion the control fell back on —
36157
+ // a state the app just took back, put back by the control.
36158
+ controller.onUIAction(e, {
36159
+ skipCommand: true,
36160
+ skipBoundSignal: e.type === "state_prop_change",
36161
+ });
36132
36162
  }
36133
36163
  if (
36134
36164
  e.type === "facade_propagate_up" ||
@@ -73060,8 +73090,11 @@ const Spin = ({
73060
73090
  // elsewhere on the page — and the control follows it. A control seeded from a
73061
73091
  // signal only writes back into it (see resolveInputProps), which is enough
73062
73092
  // for a field one only ever types into and not for a value that is also moved
73063
- // from outside. Undefined is not a value: the signal has nothing to say, so
73064
- // the control goes back to what it started on.
73093
+ // from outside. A signal holding nothing is left alone: the control goes back
73094
+ // to the suggestion it started on by itself (see resolveValueState), and
73095
+ // asking it to reset would be an act of its own, written into the signal —
73096
+ // the signal could then never hold nothing while a spin is bound to it, and a
73097
+ // day the app took back would come back as today.
73065
73098
  const signalValue = signalProp ? signalProp.value : undefined;
73066
73099
  // The value as of the render this effect belongs to, and not one the closure
73067
73100
  // captured a while ago: a step writes the signal, the signal brings us back
@@ -73075,7 +73108,6 @@ const Spin = ({
73075
73108
  return;
73076
73109
  }
73077
73110
  if (signalValue === undefined) {
73078
- dispatchRequestResetUIState(controlEl);
73079
73111
  return;
73080
73112
  }
73081
73113
  if (signalValue !== valueRef.current) {