@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.
@@ -40608,6 +40608,50 @@ const callLiftOverCallbacks = () => {
40608
40608
  });
40609
40609
  };
40610
40610
 
40611
+ /**
40612
+ * What a gesture changes in a closed picker is sent at once.
40613
+ *
40614
+ * A picker's `action` waits for its popup to close: what is done in there is
40615
+ * one answer, given when the person is done. A change made while the popup is
40616
+ * closed — a chip's cross, a paste, a cut, a value proposed from beside it
40617
+ * (`--navi-update`) — has no close coming, so nothing would ever send it: the
40618
+ * value moves on screen and the record behind it stays. Each of those gestures
40619
+ * calls this right after setting the value; a failing action puts the value
40620
+ * back (resetOnError), as it does after a close.
40621
+ *
40622
+ * Called by gestures only. A value set without one — a bound signal followed, a
40623
+ * cancel or a failure rolled back — is the picker being told, not asked, which
40624
+ * is why this is not a reaction to every value set on a closed picker.
40625
+ *
40626
+ * The clear cross does the same through `--navi-send` (see --navi-clear in
40627
+ * commands.js), and so does not come through here.
40628
+ */
40629
+
40630
+
40631
+ const sendClosedPickerChange = (element, { event, name, requester }) => {
40632
+ const controlHost = findControlHost(element) || element;
40633
+ const controlRoot = findControlRoot(controlHost);
40634
+ if (!controlRoot || controlRoot.getAttribute("navi-control") !== "picker") {
40635
+ return;
40636
+ }
40637
+ if (controlRoot.getAttribute("aria-expanded") === "true") {
40638
+ return;
40639
+ }
40640
+ const { props } = controlHost.__uiStateController__;
40641
+ if (!props.action) {
40642
+ return;
40643
+ }
40644
+ // A picker that picks nothing (`type="confirm"`, a menu of actions) holds no
40645
+ // value its action would be sending: that action is the yes of its popup,
40646
+ // and a paste landing on the trigger must not say it.
40647
+ if (props.picksNothing) {
40648
+ return;
40649
+ }
40650
+ // `requester`: the source of a command may have claimed the press to send
40651
+ // this very change (see isAimedAtSelfInteractionsBelow).
40652
+ dispatchRequestAction(controlHost, { event, name, requester });
40653
+ };
40654
+
40611
40655
  /**
40612
40656
  * Runs a navi command from JS. It is the LAST RESORT: the attributes say the
40613
40657
  * same thing declaratively and go through this very function, so a press that
@@ -40999,6 +41043,11 @@ registerNaviCommand("--navi-update", (source, event, { argument }) => {
40999
41043
  event,
41000
41044
  behavior: argument,
41001
41045
  });
41046
+ sendClosedPickerChange(target, {
41047
+ event,
41048
+ name: "--navi-update",
41049
+ requester: source,
41050
+ });
41002
41051
  },
41003
41052
  });
41004
41053
  },
@@ -52062,11 +52111,15 @@ const LinkWithRoute = ({
52062
52111
  also: currentAlso
52063
52112
  });
52064
52113
  const innerCurrent = current || linkMatching;
52114
+
52115
+ // What a childless link shows is LinkPlain's call (hrefFallback); a route
52116
+ // only says it the way its pattern writes it, not as the resolved href.
52065
52117
  return jsx(Link, {
52066
52118
  href: url,
52119
+ hrefText: route.buildRelativeUrl(routeParams),
52067
52120
  current: innerCurrent,
52068
52121
  ...rest,
52069
- children: children || route.buildRelativeUrl(routeParams)
52122
+ children: children
52070
52123
  });
52071
52124
  };
52072
52125
  const LinkPlain = props => {
@@ -52092,6 +52145,7 @@ const LinkPlain = props => {
52092
52145
  revealOnInteraction = false,
52093
52146
  stretch,
52094
52147
  hrefFallback = !anchor,
52148
+ hrefText = href,
52095
52149
  routeTransition,
52096
52150
  pressableDuringRouteTransition,
52097
52151
  replace,
@@ -52227,7 +52281,7 @@ const LinkPlain = props => {
52227
52281
  const documentRequest = isDocument ? {
52228
52282
  [LINK_DOCUMENT_ATTRIBUTE]: ""
52229
52283
  } : null;
52230
- const innerChildren = children || (hrefFallback ? href : children);
52284
+ const innerChildren = children || (hrefFallback ? hrefText : children);
52231
52285
  const startIconEl = startIcon;
52232
52286
  const endIconEl = innerEndIcon;
52233
52287
 
@@ -52278,6 +52332,7 @@ const LinkPlain = props => {
52278
52332
  startIcon: undefined,
52279
52333
  endIcon: undefined,
52280
52334
  hrefFallback: undefined,
52335
+ hrefText: undefined,
52281
52336
  routeTransition: undefined,
52282
52337
  pressableDuringRouteTransition: undefined,
52283
52338
  replace: undefined,
@@ -82760,7 +82815,9 @@ const PickerArrayUI = /*#__PURE__*/asPickerOwnUI(() => {
82760
82815
  /**
82761
82816
  * One value the picker holds, drawn as a chip with a cross that takes it back
82762
82817
  * out. Sits wherever the application draws what was picked — on the picker's
82763
- * façade (`ui`) or inside its popup — and both behave the same.
82818
+ * façade (`ui`) or inside its popup. Inside the open popup the removal waits
82819
+ * for the close like any change made there; pressed while the popup is closed
82820
+ * it is the whole gesture, and the picker's `action` runs at once.
82764
82821
  *
82765
82822
  * The cross asks with `--navi-unselect` rather than writing a new list, and it
82766
82823
  * asks the picker — which holds what was picked, and hands it down to whatever
@@ -83792,15 +83849,25 @@ const PickerButton = props => {
83792
83849
  const displayText = pickerEl.querySelector(".navi_picker_value")?.textContent ?? String(uiState);
83793
83850
  e.clipboardData.setData("text/plain", displayText);
83794
83851
  e.clipboardData.setData("application/x-navi", JSON.stringify(uiState));
83852
+ e.preventDefault();
83853
+ // A picker that asks before its cross clears anything is not
83854
+ // emptied by a keystroke without the question: the cut copies,
83855
+ // and the value stays.
83856
+ if (clearConfirm !== undefined) {
83857
+ return;
83858
+ }
83795
83859
  // the clear ui state part need control to be interactable
83796
83860
  dispatchRequestInteraction(pickerEl, {
83797
83861
  event: e,
83798
83862
  name: "cut",
83799
83863
  allowed: () => {
83800
83864
  dispatchRequestClearUIState(inputRef.current, e);
83865
+ sendClosedPickerChange(inputRef.current, {
83866
+ event: e,
83867
+ name: "cut"
83868
+ });
83801
83869
  }
83802
83870
  });
83803
- e.preventDefault();
83804
83871
  },
83805
83872
  onPaste: e => {
83806
83873
  const pickerEl = ref.current;
@@ -83826,6 +83893,10 @@ const PickerButton = props => {
83826
83893
  dispatchRequestSetUIState(inputRef.current, pasteValue, {
83827
83894
  event: e
83828
83895
  });
83896
+ sendClosedPickerChange(inputRef.current, {
83897
+ event: e,
83898
+ name: "paste"
83899
+ });
83829
83900
  }
83830
83901
  });
83831
83902
  e.preventDefault();
@@ -84071,6 +84142,10 @@ const requestPickerListEntry = (pickerEl, pickerInputEl, e, goal) => {
84071
84142
  dispatchRequestSetUIState(pickerInputEl, uiStateNext, {
84072
84143
  event: e
84073
84144
  });
84145
+ sendClosedPickerChange(pickerInputEl, {
84146
+ event: e,
84147
+ name: goal
84148
+ });
84074
84149
  }
84075
84150
  });
84076
84151
  };
@@ -96218,6 +96293,10 @@ const css$5 = /* css */`@keyframes navi_image_shimmer {
96218
96293
  `;
96219
96294
  const DEFAULT_PLACEHOLDER_LIGHT = "#d4d8dd";
96220
96295
  const DEFAULT_PLACEHOLDER_DARK = "#374151";
96296
+ // On an <img>, width/height describe the image: they stay HTML attributes.
96297
+ // Box would write them as inline styles, which beat every stylesheet rule, so
96298
+ // an image given its file's size (3840×2160) would overflow the box sizing it.
96299
+ const IMAGE_ATTRIBUTE_PROP_SET = new Set(["width", "height"]);
96221
96300
 
96222
96301
  /**
96223
96302
  * The props may come in any order: `src` and `srcset` reach the element after
@@ -96226,6 +96305,12 @@ const DEFAULT_PLACEHOLDER_DARK = "#374151";
96226
96305
  * decides whether to load when `src` is set, and an image whose `src` came
96227
96306
  * first loads, lazy or not.
96228
96307
  *
96308
+ * @param {number} [props.width] - The image's width in pixels, written as the
96309
+ * `<img>` attribute, like `height`. Given both, the browser reserves the
96310
+ * aspect ratio before the bytes arrive. They size the element only while no
96311
+ * stylesheet rule does: a box that holds the image sizes it with CSS (or
96312
+ * `style`) and keeps the ratio.
96313
+ * @param {number} [props.height] - The image's height in pixels, see `width`.
96229
96314
  * @param {string|false} [props.placeholderColor] - Background color shown while the image loads.
96230
96315
  * Defaults to a light gray. Pass `false` to disable.
96231
96316
  * @param {boolean} [props.placeholderDark] - Use a dark default placeholder color,
@@ -96250,6 +96335,7 @@ const Image = ({
96250
96335
  ...rest,
96251
96336
  as: "img",
96252
96337
  baseClassName: "navi_image",
96338
+ childPropSet: IMAGE_ATTRIBUTE_PROP_SET,
96253
96339
  "navi-placeholder": loadedRef.current ? undefined : "",
96254
96340
  style: {
96255
96341
  "--placeholder-color": resolvedPlaceholder || undefined,