@jsenv/navi 0.29.317 → 0.29.319

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.
@@ -60017,24 +60017,18 @@ const useDialogProps = props => {
60017
60017
  // comment): this opening has its own content to be measured against, and
60018
60018
  // measuring it inside last time's box would answer with last time's size.
60019
60019
  unfreezeSize(dialogEl);
60020
- positionDialog();
60021
- if (sizing === "frozen") {
60022
- // After positionDialog: the caps it writes
60023
- // (--container-position-remaining-*) are part of what decides the size
60024
- // being taken, so measuring before it would freeze a box the dialog
60025
- // never actually had.
60026
- freezeSize(dialogEl);
60027
- }
60028
60020
 
60029
- // Reposition on the same triggers Popover's own visibleRectEffect
60030
- // already reacts to generically — window resize/scroll/visual-viewport
60031
- // changes for layer="top" (positionedAncestor is already
60021
+ // Placed by the effect's own first check, synchronously, on the opening
60022
+ // event — and repositioned on the same triggers Popover's own
60023
+ // visibleRectEffect reacts to generically: window resize/scroll/
60024
+ // visual-viewport changes for layer="top" (positionedAncestor is already
60032
60025
  // document.documentElement there, see its own computation above;
60033
- // visibleRectEffect already debounces visualViewport resize by 100ms
60034
- // to avoid the mobile tap-to-tap-input keyboard flicker, so no
60035
- // separate mechanism is needed here for that), or the positioned
60036
- // ancestor's own resize for layer="local" — see this file's top
60037
- // comment.
60026
+ // visibleRectEffect already debounces visualViewport resize by 100ms to
60027
+ // avoid the mobile tap-to-tap-input keyboard flicker, so no separate
60028
+ // mechanism is needed here for that), or the positioned ancestor's own
60029
+ // resize for layer="local" — see this file's top comment. One placement
60030
+ // for the opening: a positionDialog() of our own before the effect would
60031
+ // be a second pick, and a second layout between two reads of the viewport.
60038
60032
  const rectEffect = visibleRectEffect(positionedAncestor, (visibleRect, {
60039
60033
  event
60040
60034
  }) => {
@@ -60049,6 +60043,13 @@ const useDialogProps = props => {
60049
60043
  event: e,
60050
60044
  skipElementResize: true
60051
60045
  });
60046
+ if (sizing === "frozen") {
60047
+ // After the placement: the caps it writes
60048
+ // (--container-position-remaining-*) are part of what decides the size
60049
+ // being taken, so measuring before it would freeze a box the dialog
60050
+ // never actually had.
60051
+ freezeSize(dialogEl);
60052
+ }
60052
60053
  rectEffect.observeSize(dialogEl);
60053
60054
  // Exposed for the placement-props effect below, which needs to re-place an
60054
60055
  // already-open dialog.
@@ -62379,6 +62380,13 @@ const PickerCustomResolver = props => {
62379
62380
  if (props.calloutIcon === undefined) {
62380
62381
  props.calloutIcon = props.variant !== "text";
62381
62382
  }
62383
+ // A door's content is never where its value comes from (see `standalone`
62384
+ // above), so nothing needs it before the first open: built then, like a
62385
+ // popup's. A name in a list wrapped in a tooltip is otherwise a card in
62386
+ // the DOM per name, and each one asks the layout whether it is on screen.
62387
+ if (props.mount === undefined) {
62388
+ props.mount = MOUNT_DEFAULT;
62389
+ }
62382
62390
  if (props.rightSlotIcon === undefined) {
62383
62391
  props.rightSlotIcon = jsx(CalloutStatusIcon, {
62384
62392
  status: props.calloutStatus,
@@ -63230,13 +63238,15 @@ const PICKER_CALLOUT_CONTENT_TOKEN = createOpenToken();
63230
63238
  * The content is rendered into an element this component owns, handed to the
63231
63239
  * callout as its message (a Node, appended as-is) and taken back when the
63232
63240
  * callout closes. Between two opens it is docked, hidden, in the span below:
63233
- * in the document the whole time, the way a popup's `mount="always"` keeps its
63234
- * content — so what it holds survives a close, and what it measures of itself
63235
- * at mount (a computed color, a light-dark() pair) resolves against a real
63236
- * ancestry. An element with no document has no computed style, and a badge
63237
- * reading its own background there would see nothing at all. The element
63238
- * carries data-picker-content: the callout is appended inside the picker root,
63239
- * and a press in there must read as inside the popup, not on the trigger.
63241
+ * in the document the whole time once built, so what it holds survives a
63242
+ * close, and what it measures of itself at mount (a computed color, a
63243
+ * light-dark() pair) resolves against a real ancestry. An element with no
63244
+ * document has no computed style, and a badge reading its own background
63245
+ * there would see nothing at all. When it is built is the popup's own `mount`
63246
+ * rule (see popup_content_mount.js) — the first open, for a callout. The
63247
+ * element carries data-picker-content: the callout is appended inside the
63248
+ * picker root, and a press in there must read as inside the popup, not on the
63249
+ * trigger.
63240
63250
  */
63241
63251
  const PickerCalloutPopup = ({
63242
63252
  ref,
@@ -63251,9 +63261,18 @@ const PickerCalloutPopup = ({
63251
63261
  onnavi_request_open,
63252
63262
  onnavi_request_close,
63253
63263
  onnavi_request_confirm,
63254
- children
63264
+ mount,
63265
+ children: childrenProp
63255
63266
  }) => {
63256
63267
  const hostRef = useRef(null);
63268
+ // The dock is what the content finds as its openable ancestor (the
63269
+ // aria-expanded below), which is what the mount marks as being built for
63270
+ // its own opening.
63271
+ const contentMounted = usePopupContentMount(openController, ref, {
63272
+ mount,
63273
+ anchor
63274
+ });
63275
+ const children = contentMounted ? childrenProp : null;
63257
63276
  // Reassigned on every render, like Popover's own, so it closes over the
63258
63277
  // latest props.
63259
63278
  openController.getElement = () => pickerRef.current;
@@ -63300,20 +63319,33 @@ const PickerCalloutPopup = ({
63300
63319
  onnavi_request_confirm?.(e);
63301
63320
  };
63302
63321
  calloutElement.addEventListener("navi_request_confirm", forwardConfirm);
63322
+ // Said once the content is in the shown callout: what the content defers
63323
+ // until it is displayed (useDisplayedLayoutEffect, watching this attribute)
63324
+ // then measures it where it is drawn. Same call stack as the open, like
63325
+ // Popover's own — see observeAncestorOpenState in @jsenv/dom.
63326
+ dock.setAttribute("aria-expanded", "true");
63303
63327
  return closeEvent => {
63304
63328
  calloutElement.removeEventListener("navi_request_confirm", forwardConfirm);
63305
63329
  calloutManager.removeOpenToken(PICKER_CALLOUT_CONTENT_TOKEN, closeEvent);
63306
63330
  dock.appendChild(host);
63331
+ dock.setAttribute("aria-expanded", "false");
63307
63332
  };
63308
63333
  };
63309
63334
  return (
63310
63335
  // What the picker addresses (aria-controls, the request events it
63311
63336
  // forwards); the callout itself lives where the callout manager puts it.
63337
+ // aria-expanded here, on the dock rather than on the content element: the
63338
+ // openable ancestor the content finds at mount (the only time it looks),
63339
+ // and one that is no ancestor of the content once the callout holds it —
63340
+ // so a --navi-close said in there still resolves to the callout element,
63341
+ // the closest [aria-expanded] from the button. The open effect keeps it
63342
+ // current; preact leaves it alone, the prop never changes.
63312
63343
  jsx(Box, {
63313
63344
  as: "span",
63314
63345
  ref: ref,
63315
63346
  id: id,
63316
63347
  className: "navi_picker_callout_dock",
63348
+ "aria-expanded": "false",
63317
63349
  style: {
63318
63350
  display: "contents"
63319
63351
  },
@@ -66121,7 +66153,8 @@ const useListScrollSync = ({
66121
66153
  const debugScroll = useDebugScroll();
66122
66154
  const virtualItemSizeSignal = useVirtualItemSizeSignal(ref, virtualItemSize, horizontal, {
66123
66155
  virtual,
66124
- renderBudget
66156
+ renderBudget,
66157
+ scrolledWanted: scrolled ?? defaultScrolled
66125
66158
  });
66126
66159
  const getScroller = () => getScrollerEl(ref.current, scroller, horizontal);
66127
66160
  const getListEl = () => ref.current.querySelector(".navi_list");
@@ -66178,27 +66211,39 @@ const useListScrollSync = ({
66178
66211
  });
66179
66212
  const renderWindowRef = useRef(null);
66180
66213
  renderWindowRef.current = renderWindow;
66181
- // A budget that changes (the first paint's giving way to the scrolling one)
66182
- // re-frames the window where it stands, in this very render — the way
66183
- // holdWindow below moves it: nothing else would, the scroll listener only
66184
- // moves a window the user is about to leave.
66185
- const renderBudgetRef = useRef(renderBudget);
66186
- if (renderBudgetRef.current !== renderBudget) {
66187
- renderBudgetRef.current = renderBudget;
66214
+ // The window is as wide as the budget says, whatever the state holds: a
66215
+ // budget that changes (the first paint's giving way to the scrolling one)
66216
+ // re-frames the window where it stands, in the render itself, the way
66217
+ // holdWindow below moves it — nothing else would, the scroll listener only
66218
+ // moves a window the user is about to leave. Derived on every render and
66219
+ // not written once: the state keeps the width it had, and the next render
66220
+ // copies it back into the ref above. The same object is handed out for as
66221
+ // long as the numbers hold, so the rows are not told about a window that
66222
+ // did not move.
66223
+ const framedWindowRef = useRef(null);
66224
+ {
66188
66225
  const {
66189
- start
66226
+ start,
66227
+ end
66190
66228
  } = renderWindowRef.current;
66191
66229
  const total = virtual.totalSignal.peek();
66192
- let newStart = start;
66193
- let newEnd = start + renderBudget;
66194
- if (total > 0 && newEnd > total) {
66195
- newEnd = total;
66196
- newStart = total - renderBudget < 0 ? 0 : total - renderBudget;
66230
+ let framedStart = start;
66231
+ let framedEnd = start + renderBudget;
66232
+ if (total > 0 && framedEnd > total) {
66233
+ framedEnd = total;
66234
+ framedStart = total - renderBudget < 0 ? 0 : total - renderBudget;
66235
+ }
66236
+ if (framedStart !== start || framedEnd !== end) {
66237
+ const framed = framedWindowRef.current;
66238
+ if (framed && framed.start === framedStart && framed.end === framedEnd) {
66239
+ renderWindowRef.current = framed;
66240
+ } else {
66241
+ framedWindowRef.current = renderWindowRef.current = {
66242
+ start: framedStart,
66243
+ end: framedEnd
66244
+ };
66245
+ }
66197
66246
  }
66198
- renderWindowRef.current = {
66199
- start: newStart,
66200
- end: newEnd
66201
- };
66202
66247
  }
66203
66248
  const updateRenderWindow = (newStart, newEnd, reason) => {
66204
66249
  const {
@@ -67463,7 +67508,8 @@ const measureItemSize = (listEl, horizontal) => {
67463
67508
  };
67464
67509
  const useVirtualItemSizeSignal = (ref, virtualItemSizeProp = 0, horizontal, {
67465
67510
  virtual,
67466
- renderBudget
67511
+ renderBudget,
67512
+ scrolledWanted
67467
67513
  }) => {
67468
67514
  const virtualSizeSignalRef = useRef(null);
67469
67515
  if (!virtualSizeSignalRef.current) {
@@ -67533,6 +67579,14 @@ const useVirtualItemSizeSignal = (ref, virtualItemSizeProp = 0, horizontal, {
67533
67579
  if (virtualSizeSignal.peek() !== 0) {
67534
67580
  return undefined;
67535
67581
  }
67582
+ // Measured only for what reads the size: the fillers of rows held off
67583
+ // screen, and a list held somewhere (placeWhereHeld) before it knows where
67584
+ // that is. A list drawing every row it has, opening at its start, would
67585
+ // pay a layout in every commit for a number nobody reads.
67586
+ const sizeRead = virtual.totalSignal.peek() > renderBudget || scrolledWanted !== undefined && scrolledWanted !== "start";
67587
+ if (!sizeRead) {
67588
+ return undefined;
67589
+ }
67536
67590
  const listEl = ref.current?.querySelector(".navi_list");
67537
67591
  if (!listEl) {
67538
67592
  return undefined;
@@ -75239,9 +75293,13 @@ const useSearchText = (searchText, items, matchFn = applySearch) => {
75239
75293
  return { orderedItems, matchInfoMap };
75240
75294
  }, [items, searchText, matchFn]);
75241
75295
 
75242
- const getItemMatchInfo = (item) => {
75243
- return matchInfoMap.get(item);
75244
- };
75296
+ // The same function for as long as the map is the same: a `renderItem`
75297
+ // reading it is stable only if this is, and a run keeps the rows it drew
75298
+ // only for a stable `renderItem` (see List.Items).
75299
+ const getItemMatchInfo = useCallback(
75300
+ (item) => matchInfoMap.get(item),
75301
+ [matchInfoMap],
75302
+ );
75245
75303
 
75246
75304
  return [orderedItems, getItemMatchInfo];
75247
75305
  };