@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.
@@ -7325,6 +7325,16 @@ const positionCallout = (
7325
7325
  }
7326
7326
  return;
7327
7327
  }
7328
+ if (!calloutElement.isConnected) {
7329
+ // The callout is drawn inside the anchor's subtree (calloutContainer),
7330
+ // so it leaves the document with it — a Suspense boundary parking
7331
+ // the screen while an action re-runs keeps the subtree mounted, refs
7332
+ // intact, but detached. showPopover() throws on a detached element,
7333
+ // and leaving the document already took it out of the top layer, so
7334
+ // there is nothing to hide either: same as ancestorClosed, wait for
7335
+ // the next check, which shows it again once the subtree is back.
7336
+ return;
7337
+ }
7328
7338
  if (!calloutElement.matches(":popover-open")) {
7329
7339
  if (debug) {
7330
7340
  debug(event, "showing callout because anchor is visible again");
@@ -24214,10 +24224,16 @@ const createRoutePattern = (
24214
24224
  // search params are updated. When buildMostPreciseUrl performs an ancestor
24215
24225
  // optimisation (e.g. "/map/isochrone/compare" → "/map/isochrone") it is trusted
24216
24226
  // as-is because the built pathname will differ from the route's own base pathname.
24217
- // Weak params are never inherited from their signal, but a url that already
24218
- // carries one keeps it: staying on the same screen while another param
24219
- // changes must not end the visit this param qualifies.
24220
- const carryOverWeakParams = (currentUrl, params) => {
24227
+ // A weak param is never inherited from its signal by a url built for a link
24228
+ // (see readSignalForUrlBuild). A url amended in place is another matter: the
24229
+ // route is matching, so the signal IS this visit's state, kept in step with
24230
+ // the address by every routing pass — and what it holds is carried over, so
24231
+ // that changing another param does not end the visit this one qualifies.
24232
+ // Read from the signal, not from the url being amended: several signals
24233
+ // written in one batch flush one url write at a time, and the routing pass
24234
+ // of the first would take the others for gone — and reset them — if the url
24235
+ // it wrote did not already carry what they hold.
24236
+ const carryOverWeakParams = (params) => {
24221
24237
  let paramsWithWeak = params;
24222
24238
  for (const connection of connections) {
24223
24239
  const { paramName } = connection;
@@ -24225,17 +24241,13 @@ const createRoutePattern = (
24225
24241
  continue;
24226
24242
  }
24227
24243
  const currentValue = connection.signal.peek();
24228
- if (currentValue === undefined) {
24229
- continue;
24230
- }
24231
- const currentParams = applyOn(currentUrl);
24232
- if (!currentParams || currentParams[paramName] === undefined) {
24244
+ if (connection.isDefaultValue(currentValue)) {
24233
24245
  continue;
24234
24246
  }
24235
24247
  if (paramsWithWeak === params) {
24236
24248
  paramsWithWeak = { ...params };
24237
24249
  }
24238
- paramsWithWeak[paramName] = currentParams[paramName];
24250
+ paramsWithWeak[paramName] = currentValue;
24239
24251
  }
24240
24252
  return paramsWithWeak;
24241
24253
  };
@@ -24271,7 +24283,7 @@ const createRoutePattern = (
24271
24283
  };
24272
24284
 
24273
24285
  const buildUrlPreservingPath = (currentUrl, params = {}) => {
24274
- params = carryOverWeakParams(currentUrl, params);
24286
+ params = carryOverWeakParams(params);
24275
24287
  if (currentUrl) {
24276
24288
  params = carryOverPathParams(currentUrl, params);
24277
24289
  }
@@ -27188,20 +27200,28 @@ const setupBrowserIntegrationViaHistory = ({
27188
27200
  // traversal to another document is a full page load, which no press on a
27189
27201
  // link asked for — the entries that are ours are recorded as they are
27190
27202
  // created, starting with the one this document was loaded into.
27203
+ //
27204
+ // The presence of window.navigation says nothing about the entry: the spec
27205
+ // makes currentEntry null in a document that is not fully active, and iOS
27206
+ // Safari answers null in a document that plainly is (a booted app, minutes
27207
+ // into a session). Nothing can be done about it; an entry that cannot be
27208
+ // read is treated as a browser without the API, which only costs the
27209
+ // adjacency.
27191
27210
  const sameDocumentEntryKeys = new Set();
27192
27211
  const rememberEntryIsOfThisDocument = () => {
27193
- if (window.navigation) {
27194
- sameDocumentEntryKeys.add(window.navigation.currentEntry.key);
27212
+ const currentEntry = window.navigation?.currentEntry;
27213
+ if (currentEntry) {
27214
+ sameDocumentEntryKeys.add(currentEntry.key);
27195
27215
  }
27196
27216
  };
27197
27217
  rememberEntryIsOfThisDocument();
27198
27218
  const adjacentEntryDelta = (url) => {
27199
- const { navigation } = window;
27200
- if (!navigation) {
27219
+ const currentEntry = window.navigation?.currentEntry;
27220
+ if (!currentEntry) {
27201
27221
  return 0;
27202
27222
  }
27203
- const entries = navigation.entries();
27204
- const index = navigation.currentEntry.index;
27223
+ const entries = window.navigation.entries();
27224
+ const index = currentEntry.index;
27205
27225
  // Behind first: when the same page stands on both sides (A, B, A and one
27206
27226
  // is on B), a link to it reads as going back.
27207
27227
  for (const delta of [-1, 1]) {
@@ -36819,7 +36839,7 @@ const useUIStateController = (
36819
36839
  // reach until that popup opens. Letting it block interaction would make
36820
36840
  // a picker whose content is loading impossible to open at all.
36821
36841
  getInteractionBlockingControls: () => [],
36822
- onUIAction: (e, { skipCommand } = {}) => {
36842
+ onUIAction: (e, { skipCommand, skipBoundSignal } = {}) => {
36823
36843
  if (controlType === "button" && controller.controlHostProps.name) {
36824
36844
  const buttonName = controller.controlHostProps.name;
36825
36845
  const parentController = controller.parentUIStateController;
@@ -36849,7 +36869,9 @@ const useUIStateController = (
36849
36869
  }
36850
36870
  // Trigger uiAction/command side effects without changing UI state.
36851
36871
  const currentUIState = controller.uiState;
36852
- writeBoundSignal(currentUIState);
36872
+ if (!skipBoundSignal) {
36873
+ writeBoundSignal(currentUIState);
36874
+ }
36853
36875
  s.uiActionInternal?.(currentUIState, e);
36854
36876
  if (s.uiAction) {
36855
36877
  debugUIState(`calling uiAction for ${controlType}`, currentUIState);
@@ -37075,7 +37097,15 @@ const useUIStateController = (
37075
37097
  // sync, but do NOT fire the command and do NOT notify the parent —
37076
37098
  // both would cause an infinite loop when a parent cascades state
37077
37099
  // down to its children (child command would re-trigger the cascade).
37078
- controller.onUIAction(e, { skipCommand: true });
37100
+ // A control following what it was given (state_prop_change) does
37101
+ // not write its bound signal either: the signal is what it
37102
+ // follows. The one write that would change it is an emptied
37103
+ // signal refilled with the suggestion the control fell back on —
37104
+ // a state the app just took back, put back by the control.
37105
+ controller.onUIAction(e, {
37106
+ skipCommand: true,
37107
+ skipBoundSignal: e.type === "state_prop_change",
37108
+ });
37079
37109
  }
37080
37110
  if (
37081
37111
  e.type === "facade_propagate_up" ||
@@ -74967,8 +74997,11 @@ const Spin = ({
74967
74997
  // elsewhere on the page — and the control follows it. A control seeded from a
74968
74998
  // signal only writes back into it (see resolveInputProps), which is enough
74969
74999
  // for a field one only ever types into and not for a value that is also moved
74970
- // from outside. Undefined is not a value: the signal has nothing to say, so
74971
- // the control goes back to what it started on.
75000
+ // from outside. A signal holding nothing is left alone: the control goes back
75001
+ // to the suggestion it started on by itself (see resolveValueState), and
75002
+ // asking it to reset would be an act of its own, written into the signal —
75003
+ // the signal could then never hold nothing while a spin is bound to it, and a
75004
+ // day the app took back would come back as today.
74972
75005
  const signalValue = signalProp ? signalProp.value : undefined;
74973
75006
  // The value as of the render this effect belongs to, and not one the closure
74974
75007
  // captured a while ago: a step writes the signal, the signal brings us back
@@ -74982,7 +75015,6 @@ const Spin = ({
74982
75015
  return;
74983
75016
  }
74984
75017
  if (signalValue === undefined) {
74985
- dispatchRequestResetUIState(controlEl);
74986
75018
  return;
74987
75019
  }
74988
75020
  if (signalValue !== valueRef.current) {