@jsenv/navi 0.29.453 → 0.29.454

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.
@@ -39577,6 +39577,50 @@ const callLiftOverCallbacks = () => {
39577
39577
  });
39578
39578
  };
39579
39579
 
39580
+ /**
39581
+ * What a gesture changes in a closed picker is sent at once.
39582
+ *
39583
+ * A picker's `action` waits for its popup to close: what is done in there is
39584
+ * one answer, given when the person is done. A change made while the popup is
39585
+ * closed — a chip's cross, a paste, a cut, a value proposed from beside it
39586
+ * (`--navi-update`) — has no close coming, so nothing would ever send it: the
39587
+ * value moves on screen and the record behind it stays. Each of those gestures
39588
+ * calls this right after setting the value; a failing action puts the value
39589
+ * back (resetOnError), as it does after a close.
39590
+ *
39591
+ * Called by gestures only. A value set without one — a bound signal followed, a
39592
+ * cancel or a failure rolled back — is the picker being told, not asked, which
39593
+ * is why this is not a reaction to every value set on a closed picker.
39594
+ *
39595
+ * The clear cross does the same through `--navi-send` (see --navi-clear in
39596
+ * commands.js), and so does not come through here.
39597
+ */
39598
+
39599
+
39600
+ const sendClosedPickerChange = (element, { event, name, requester }) => {
39601
+ const controlHost = findControlHost(element) || element;
39602
+ const controlRoot = findControlRoot(controlHost);
39603
+ if (!controlRoot || controlRoot.getAttribute("navi-control") !== "picker") {
39604
+ return;
39605
+ }
39606
+ if (controlRoot.getAttribute("aria-expanded") === "true") {
39607
+ return;
39608
+ }
39609
+ const { props } = controlHost.__uiStateController__;
39610
+ if (!props.action) {
39611
+ return;
39612
+ }
39613
+ // A picker that picks nothing (`type="confirm"`, a menu of actions) holds no
39614
+ // value its action would be sending: that action is the yes of its popup,
39615
+ // and a paste landing on the trigger must not say it.
39616
+ if (props.picksNothing) {
39617
+ return;
39618
+ }
39619
+ // `requester`: the source of a command may have claimed the press to send
39620
+ // this very change (see isAimedAtSelfInteractionsBelow).
39621
+ dispatchRequestAction(controlHost, { event, name, requester });
39622
+ };
39623
+
39580
39624
  /**
39581
39625
  * Runs a navi command from JS. It is the LAST RESORT: the attributes say the
39582
39626
  * same thing declaratively and go through this very function, so a press that
@@ -39952,6 +39996,11 @@ registerNaviCommand("--navi-update", (source, event, { argument }) => {
39952
39996
  event,
39953
39997
  behavior: argument,
39954
39998
  });
39999
+ sendClosedPickerChange(target, {
40000
+ event,
40001
+ name: "--navi-update",
40002
+ requester: source,
40003
+ });
39955
40004
  },
39956
40005
  });
39957
40006
  },
@@ -50859,11 +50908,15 @@ const LinkWithRoute = ({
50859
50908
  also: currentAlso
50860
50909
  });
50861
50910
  const innerCurrent = current || linkMatching;
50911
+
50912
+ // What a childless link shows is LinkPlain's call (hrefFallback); a route
50913
+ // only says it the way its pattern writes it, not as the resolved href.
50862
50914
  return jsx(Link, {
50863
50915
  href: url,
50916
+ hrefText: route.buildRelativeUrl(routeParams),
50864
50917
  current: innerCurrent,
50865
50918
  ...rest,
50866
- children: children || route.buildRelativeUrl(routeParams)
50919
+ children: children
50867
50920
  });
50868
50921
  };
50869
50922
  const LinkPlain = props => {
@@ -50889,6 +50942,7 @@ const LinkPlain = props => {
50889
50942
  revealOnInteraction = false,
50890
50943
  stretch,
50891
50944
  hrefFallback = !anchor,
50945
+ hrefText = href,
50892
50946
  routeTransition,
50893
50947
  pressableDuringRouteTransition,
50894
50948
  replace,
@@ -51024,7 +51078,7 @@ const LinkPlain = props => {
51024
51078
  const documentRequest = isDocument ? {
51025
51079
  [LINK_DOCUMENT_ATTRIBUTE]: ""
51026
51080
  } : null;
51027
- const innerChildren = children || (hrefFallback ? href : children);
51081
+ const innerChildren = children || (hrefFallback ? hrefText : children);
51028
51082
  const startIconEl = startIcon;
51029
51083
  const endIconEl = innerEndIcon;
51030
51084
 
@@ -51075,6 +51129,7 @@ const LinkPlain = props => {
51075
51129
  startIcon: undefined,
51076
51130
  endIcon: undefined,
51077
51131
  hrefFallback: undefined,
51132
+ hrefText: undefined,
51078
51133
  routeTransition: undefined,
51079
51134
  pressableDuringRouteTransition: undefined,
51080
51135
  replace: undefined,
@@ -81179,7 +81234,9 @@ const PickerArrayUI = /*#__PURE__*/asPickerOwnUI(() => {
81179
81234
  /**
81180
81235
  * One value the picker holds, drawn as a chip with a cross that takes it back
81181
81236
  * out. Sits wherever the application draws what was picked — on the picker's
81182
- * façade (`ui`) or inside its popup — and both behave the same.
81237
+ * façade (`ui`) or inside its popup. Inside the open popup the removal waits
81238
+ * for the close like any change made there; pressed while the popup is closed
81239
+ * it is the whole gesture, and the picker's `action` runs at once.
81183
81240
  *
81184
81241
  * The cross asks with `--navi-unselect` rather than writing a new list, and it
81185
81242
  * asks the picker — which holds what was picked, and hands it down to whatever
@@ -82207,15 +82264,25 @@ const PickerButton = props => {
82207
82264
  const displayText = pickerEl.querySelector(".navi_picker_value")?.textContent ?? String(uiState);
82208
82265
  e.clipboardData.setData("text/plain", displayText);
82209
82266
  e.clipboardData.setData("application/x-navi", JSON.stringify(uiState));
82267
+ e.preventDefault();
82268
+ // A picker that asks before its cross clears anything is not
82269
+ // emptied by a keystroke without the question: the cut copies,
82270
+ // and the value stays.
82271
+ if (clearConfirm !== undefined) {
82272
+ return;
82273
+ }
82210
82274
  // the clear ui state part need control to be interactable
82211
82275
  dispatchRequestInteraction(pickerEl, {
82212
82276
  event: e,
82213
82277
  name: "cut",
82214
82278
  allowed: () => {
82215
82279
  dispatchRequestClearUIState(inputRef.current, e);
82280
+ sendClosedPickerChange(inputRef.current, {
82281
+ event: e,
82282
+ name: "cut"
82283
+ });
82216
82284
  }
82217
82285
  });
82218
- e.preventDefault();
82219
82286
  },
82220
82287
  onPaste: e => {
82221
82288
  const pickerEl = ref.current;
@@ -82241,6 +82308,10 @@ const PickerButton = props => {
82241
82308
  dispatchRequestSetUIState(inputRef.current, pasteValue, {
82242
82309
  event: e
82243
82310
  });
82311
+ sendClosedPickerChange(inputRef.current, {
82312
+ event: e,
82313
+ name: "paste"
82314
+ });
82244
82315
  }
82245
82316
  });
82246
82317
  e.preventDefault();
@@ -82474,6 +82545,10 @@ const requestPickerListEntry = (pickerEl, pickerInputEl, e, goal) => {
82474
82545
  dispatchRequestSetUIState(pickerInputEl, uiStateNext, {
82475
82546
  event: e
82476
82547
  });
82548
+ sendClosedPickerChange(pickerInputEl, {
82549
+ event: e,
82550
+ name: goal
82551
+ });
82477
82552
  }
82478
82553
  });
82479
82554
  };
@@ -93963,6 +94038,10 @@ const css$5 = /* css */`@keyframes navi_image_shimmer {
93963
94038
  `;
93964
94039
  const DEFAULT_PLACEHOLDER_LIGHT = "#d4d8dd";
93965
94040
  const DEFAULT_PLACEHOLDER_DARK = "#374151";
94041
+ // On an <img>, width/height describe the image: they stay HTML attributes.
94042
+ // Box would write them as inline styles, which beat every stylesheet rule, so
94043
+ // an image given its file's size (3840×2160) would overflow the box sizing it.
94044
+ const IMAGE_ATTRIBUTE_PROP_SET = new Set(["width", "height"]);
93966
94045
 
93967
94046
  /**
93968
94047
  * The props may come in any order: `src` and `srcset` reach the element after
@@ -93971,6 +94050,12 @@ const DEFAULT_PLACEHOLDER_DARK = "#374151";
93971
94050
  * decides whether to load when `src` is set, and an image whose `src` came
93972
94051
  * first loads, lazy or not.
93973
94052
  *
94053
+ * @param {number} [props.width] - The image's width in pixels, written as the
94054
+ * `<img>` attribute, like `height`. Given both, the browser reserves the
94055
+ * aspect ratio before the bytes arrive. They size the element only while no
94056
+ * stylesheet rule does: a box that holds the image sizes it with CSS (or
94057
+ * `style`) and keeps the ratio.
94058
+ * @param {number} [props.height] - The image's height in pixels, see `width`.
93974
94059
  * @param {string|false} [props.placeholderColor] - Background color shown while the image loads.
93975
94060
  * Defaults to a light gray. Pass `false` to disable.
93976
94061
  * @param {boolean} [props.placeholderDark] - Use a dark default placeholder color,
@@ -93995,6 +94080,7 @@ const Image = ({
93995
94080
  ...rest,
93996
94081
  as: "img",
93997
94082
  baseClassName: "navi_image",
94083
+ childPropSet: IMAGE_ATTRIBUTE_PROP_SET,
93998
94084
  "navi-placeholder": loadedRef.current ? undefined : "",
93999
94085
  style: {
94000
94086
  "--placeholder-color": resolvedPlaceholder || undefined,