@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.
@@ -61198,24 +61198,18 @@ const useDialogProps = props => {
61198
61198
  // comment): this opening has its own content to be measured against, and
61199
61199
  // measuring it inside last time's box would answer with last time's size.
61200
61200
  unfreezeSize(dialogEl);
61201
- positionDialog();
61202
- if (sizing === "frozen") {
61203
- // After positionDialog: the caps it writes
61204
- // (--container-position-remaining-*) are part of what decides the size
61205
- // being taken, so measuring before it would freeze a box the dialog
61206
- // never actually had.
61207
- freezeSize(dialogEl);
61208
- }
61209
61201
 
61210
- // Reposition on the same triggers Popover's own visibleRectEffect
61211
- // already reacts to generically — window resize/scroll/visual-viewport
61212
- // changes for layer="top" (positionedAncestor is already
61202
+ // Placed by the effect's own first check, synchronously, on the opening
61203
+ // event — and repositioned on the same triggers Popover's own
61204
+ // visibleRectEffect reacts to generically: window resize/scroll/
61205
+ // visual-viewport changes for layer="top" (positionedAncestor is already
61213
61206
  // document.documentElement there, see its own computation above;
61214
- // visibleRectEffect already debounces visualViewport resize by 100ms
61215
- // to avoid the mobile tap-to-tap-input keyboard flicker, so no
61216
- // separate mechanism is needed here for that), or the positioned
61217
- // ancestor's own resize for layer="local" — see this file's top
61218
- // comment.
61207
+ // visibleRectEffect already debounces visualViewport resize by 100ms to
61208
+ // avoid the mobile tap-to-tap-input keyboard flicker, so no separate
61209
+ // mechanism is needed here for that), or the positioned ancestor's own
61210
+ // resize for layer="local" — see this file's top comment. One placement
61211
+ // for the opening: a positionDialog() of our own before the effect would
61212
+ // be a second pick, and a second layout between two reads of the viewport.
61219
61213
  const rectEffect = visibleRectEffect(positionedAncestor, (visibleRect, {
61220
61214
  event
61221
61215
  }) => {
@@ -61230,6 +61224,13 @@ const useDialogProps = props => {
61230
61224
  event: e,
61231
61225
  skipElementResize: true
61232
61226
  });
61227
+ if (sizing === "frozen") {
61228
+ // After the placement: the caps it writes
61229
+ // (--container-position-remaining-*) are part of what decides the size
61230
+ // being taken, so measuring before it would freeze a box the dialog
61231
+ // never actually had.
61232
+ freezeSize(dialogEl);
61233
+ }
61233
61234
  rectEffect.observeSize(dialogEl);
61234
61235
  // Exposed for the placement-props effect below, which needs to re-place an
61235
61236
  // already-open dialog.
@@ -63569,6 +63570,13 @@ const PickerCustomResolver = props => {
63569
63570
  if (props.calloutIcon === undefined) {
63570
63571
  props.calloutIcon = props.variant !== "text";
63571
63572
  }
63573
+ // A door's content is never where its value comes from (see `standalone`
63574
+ // above), so nothing needs it before the first open: built then, like a
63575
+ // popup's. A name in a list wrapped in a tooltip is otherwise a card in
63576
+ // the DOM per name, and each one asks the layout whether it is on screen.
63577
+ if (props.mount === undefined) {
63578
+ props.mount = MOUNT_DEFAULT;
63579
+ }
63572
63580
  if (props.rightSlotIcon === undefined) {
63573
63581
  props.rightSlotIcon = jsx(CalloutStatusIcon, {
63574
63582
  status: props.calloutStatus,
@@ -64434,13 +64442,15 @@ const PICKER_CALLOUT_CONTENT_TOKEN = createOpenToken();
64434
64442
  * The content is rendered into an element this component owns, handed to the
64435
64443
  * callout as its message (a Node, appended as-is) and taken back when the
64436
64444
  * callout closes. Between two opens it is docked, hidden, in the span below:
64437
- * in the document the whole time, the way a popup's `mount="always"` keeps its
64438
- * content — so what it holds survives a close, and what it measures of itself
64439
- * at mount (a computed color, a light-dark() pair) resolves against a real
64440
- * ancestry. An element with no document has no computed style, and a badge
64441
- * reading its own background there would see nothing at all. The element
64442
- * carries data-picker-content: the callout is appended inside the picker root,
64443
- * and a press in there must read as inside the popup, not on the trigger.
64445
+ * in the document the whole time once built, so what it holds survives a
64446
+ * close, and what it measures of itself at mount (a computed color, a
64447
+ * light-dark() pair) resolves against a real ancestry. An element with no
64448
+ * document has no computed style, and a badge reading its own background
64449
+ * there would see nothing at all. When it is built is the popup's own `mount`
64450
+ * rule (see popup_content_mount.js) — the first open, for a callout. The
64451
+ * element carries data-picker-content: the callout is appended inside the
64452
+ * picker root, and a press in there must read as inside the popup, not on the
64453
+ * trigger.
64444
64454
  */
64445
64455
  const PickerCalloutPopup = ({
64446
64456
  ref,
@@ -64455,9 +64465,18 @@ const PickerCalloutPopup = ({
64455
64465
  onnavi_request_open,
64456
64466
  onnavi_request_close,
64457
64467
  onnavi_request_confirm,
64458
- children
64468
+ mount,
64469
+ children: childrenProp
64459
64470
  }) => {
64460
64471
  const hostRef = useRef(null);
64472
+ // The dock is what the content finds as its openable ancestor (the
64473
+ // aria-expanded below), which is what the mount marks as being built for
64474
+ // its own opening.
64475
+ const contentMounted = usePopupContentMount(openController, ref, {
64476
+ mount,
64477
+ anchor
64478
+ });
64479
+ const children = contentMounted ? childrenProp : null;
64461
64480
  // Reassigned on every render, like Popover's own, so it closes over the
64462
64481
  // latest props.
64463
64482
  openController.getElement = () => pickerRef.current;
@@ -64504,20 +64523,33 @@ const PickerCalloutPopup = ({
64504
64523
  onnavi_request_confirm?.(e);
64505
64524
  };
64506
64525
  calloutElement.addEventListener("navi_request_confirm", forwardConfirm);
64526
+ // Said once the content is in the shown callout: what the content defers
64527
+ // until it is displayed (useDisplayedLayoutEffect, watching this attribute)
64528
+ // then measures it where it is drawn. Same call stack as the open, like
64529
+ // Popover's own — see observeAncestorOpenState in @jsenv/dom.
64530
+ dock.setAttribute("aria-expanded", "true");
64507
64531
  return closeEvent => {
64508
64532
  calloutElement.removeEventListener("navi_request_confirm", forwardConfirm);
64509
64533
  calloutManager.removeOpenToken(PICKER_CALLOUT_CONTENT_TOKEN, closeEvent);
64510
64534
  dock.appendChild(host);
64535
+ dock.setAttribute("aria-expanded", "false");
64511
64536
  };
64512
64537
  };
64513
64538
  return (
64514
64539
  // What the picker addresses (aria-controls, the request events it
64515
64540
  // forwards); the callout itself lives where the callout manager puts it.
64541
+ // aria-expanded here, on the dock rather than on the content element: the
64542
+ // openable ancestor the content finds at mount (the only time it looks),
64543
+ // and one that is no ancestor of the content once the callout holds it —
64544
+ // so a --navi-close said in there still resolves to the callout element,
64545
+ // the closest [aria-expanded] from the button. The open effect keeps it
64546
+ // current; preact leaves it alone, the prop never changes.
64516
64547
  jsx(Box, {
64517
64548
  as: "span",
64518
64549
  ref: ref,
64519
64550
  id: id,
64520
64551
  className: "navi_picker_callout_dock",
64552
+ "aria-expanded": "false",
64521
64553
  style: {
64522
64554
  display: "contents"
64523
64555
  },
@@ -67325,7 +67357,8 @@ const useListScrollSync = ({
67325
67357
  const debugScroll = useDebugScroll();
67326
67358
  const virtualItemSizeSignal = useVirtualItemSizeSignal(ref, virtualItemSize, horizontal, {
67327
67359
  virtual,
67328
- renderBudget
67360
+ renderBudget,
67361
+ scrolledWanted: scrolled ?? defaultScrolled
67329
67362
  });
67330
67363
  const getScroller = () => getScrollerEl(ref.current, scroller, horizontal);
67331
67364
  const getListEl = () => ref.current.querySelector(".navi_list");
@@ -67382,27 +67415,39 @@ const useListScrollSync = ({
67382
67415
  });
67383
67416
  const renderWindowRef = useRef(null);
67384
67417
  renderWindowRef.current = renderWindow;
67385
- // A budget that changes (the first paint's giving way to the scrolling one)
67386
- // re-frames the window where it stands, in this very render — the way
67387
- // holdWindow below moves it: nothing else would, the scroll listener only
67388
- // moves a window the user is about to leave.
67389
- const renderBudgetRef = useRef(renderBudget);
67390
- if (renderBudgetRef.current !== renderBudget) {
67391
- renderBudgetRef.current = renderBudget;
67418
+ // The window is as wide as the budget says, whatever the state holds: a
67419
+ // budget that changes (the first paint's giving way to the scrolling one)
67420
+ // re-frames the window where it stands, in the render itself, the way
67421
+ // holdWindow below moves it — nothing else would, the scroll listener only
67422
+ // moves a window the user is about to leave. Derived on every render and
67423
+ // not written once: the state keeps the width it had, and the next render
67424
+ // copies it back into the ref above. The same object is handed out for as
67425
+ // long as the numbers hold, so the rows are not told about a window that
67426
+ // did not move.
67427
+ const framedWindowRef = useRef(null);
67428
+ {
67392
67429
  const {
67393
- start
67430
+ start,
67431
+ end
67394
67432
  } = renderWindowRef.current;
67395
67433
  const total = virtual.totalSignal.peek();
67396
- let newStart = start;
67397
- let newEnd = start + renderBudget;
67398
- if (total > 0 && newEnd > total) {
67399
- newEnd = total;
67400
- newStart = total - renderBudget < 0 ? 0 : total - renderBudget;
67434
+ let framedStart = start;
67435
+ let framedEnd = start + renderBudget;
67436
+ if (total > 0 && framedEnd > total) {
67437
+ framedEnd = total;
67438
+ framedStart = total - renderBudget < 0 ? 0 : total - renderBudget;
67439
+ }
67440
+ if (framedStart !== start || framedEnd !== end) {
67441
+ const framed = framedWindowRef.current;
67442
+ if (framed && framed.start === framedStart && framed.end === framedEnd) {
67443
+ renderWindowRef.current = framed;
67444
+ } else {
67445
+ framedWindowRef.current = renderWindowRef.current = {
67446
+ start: framedStart,
67447
+ end: framedEnd
67448
+ };
67449
+ }
67401
67450
  }
67402
- renderWindowRef.current = {
67403
- start: newStart,
67404
- end: newEnd
67405
- };
67406
67451
  }
67407
67452
  const updateRenderWindow = (newStart, newEnd, reason) => {
67408
67453
  const {
@@ -68719,7 +68764,8 @@ const measureItemSize = (listEl, horizontal) => {
68719
68764
  };
68720
68765
  const useVirtualItemSizeSignal = (ref, virtualItemSizeProp = 0, horizontal, {
68721
68766
  virtual,
68722
- renderBudget
68767
+ renderBudget,
68768
+ scrolledWanted
68723
68769
  }) => {
68724
68770
  const virtualSizeSignalRef = useRef(null);
68725
68771
  if (!virtualSizeSignalRef.current) {
@@ -68789,6 +68835,14 @@ const useVirtualItemSizeSignal = (ref, virtualItemSizeProp = 0, horizontal, {
68789
68835
  if (virtualSizeSignal.peek() !== 0) {
68790
68836
  return undefined;
68791
68837
  }
68838
+ // Measured only for what reads the size: the fillers of rows held off
68839
+ // screen, and a list held somewhere (placeWhereHeld) before it knows where
68840
+ // that is. A list drawing every row it has, opening at its start, would
68841
+ // pay a layout in every commit for a number nobody reads.
68842
+ const sizeRead = virtual.totalSignal.peek() > renderBudget || scrolledWanted !== undefined && scrolledWanted !== "start";
68843
+ if (!sizeRead) {
68844
+ return undefined;
68845
+ }
68792
68846
  const listEl = ref.current?.querySelector(".navi_list");
68793
68847
  if (!listEl) {
68794
68848
  return undefined;
@@ -77075,9 +77129,13 @@ const useSearchText = (searchText, items, matchFn = applySearch) => {
77075
77129
  return { orderedItems, matchInfoMap };
77076
77130
  }, [items, searchText, matchFn]);
77077
77131
 
77078
- const getItemMatchInfo = (item) => {
77079
- return matchInfoMap.get(item);
77080
- };
77132
+ // The same function for as long as the map is the same: a `renderItem`
77133
+ // reading it is stable only if this is, and a run keeps the rows it drew
77134
+ // only for a stable `renderItem` (see List.Items).
77135
+ const getItemMatchInfo = useCallback(
77136
+ (item) => matchInfoMap.get(item),
77137
+ [matchInfoMap],
77138
+ );
77081
77139
 
77082
77140
  return [orderedItems, getItemMatchInfo];
77083
77141
  };