@jsenv/navi 0.29.23 → 0.29.25

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.
@@ -25120,6 +25120,9 @@ const createOpenController = (
25120
25120
  openEffectCleanup = null;
25121
25121
  closeHandlers?.onClose?.(closeEvent);
25122
25122
  closeHandlers = null;
25123
+ // Last: the close effects above are what starts the exit transition the
25124
+ // content must outlive (see popup_content_mount.js).
25125
+ controller.unmountContent?.();
25123
25126
  };
25124
25127
  const controller = {
25125
25128
  opened: false,
@@ -25128,6 +25131,9 @@ const createOpenController = (
25128
25131
  // content is still waiting for a first open to be built. Called below,
25129
25132
  // before openEffect, so the popup measures and positions the real thing.
25130
25133
  mountContent: null,
25134
+ // The counterpart, set only when the popup was told to throw its content
25135
+ // away on close (`unmountWhenClosed`). Called from performClose above.
25136
+ unmountContent: null,
25131
25137
  open: (e, detail) => {
25132
25138
  if (controller.opened || !controller.openEffect) {
25133
25139
  return;
@@ -25407,7 +25413,166 @@ const flushSyncRendering = (fn) => {
25407
25413
  };
25408
25414
 
25409
25415
  /**
25410
- * When a popup builds what it holds.
25416
+ * Small, renderer-agnostic helpers shared by Popover and Dialog's own custom
25417
+ * (non-top-layer) renderers — operate on a plain DOM element, no knowledge
25418
+ * of which of the two owns it.
25419
+ */
25420
+
25421
+
25422
+ /**
25423
+ * Calls `onSettled` once `el`'s current CSS transition is over — via
25424
+ * `transitionend`, with a safety `setTimeout` fallback matching the longest
25425
+ * `transition-duration`, in case nothing actually transitions or an event is
25426
+ * missed.
25427
+ *
25428
+ * Returns a "cancel" function, so a caller whose instance has been superseded
25429
+ * (a fresh open/close about to set its own state) can keep this stale one from
25430
+ * firing later. Cancelling only stops `onSettled`: undoing whatever the caller
25431
+ * did up front is that fresh call's business, not this one's.
25432
+ */
25433
+ const whenTransitionSettles = (el, onSettled) => {
25434
+ let settled = false;
25435
+ const onTransitionEnd = (transitionEvent) => {
25436
+ if (transitionEvent.target === el) {
25437
+ finish();
25438
+ }
25439
+ };
25440
+ const stopWatching = () => {
25441
+ settled = true;
25442
+ el.removeEventListener("transitionend", onTransitionEnd);
25443
+ clearTimeout(safetyTimeoutId);
25444
+ };
25445
+ const finish = () => {
25446
+ if (settled) {
25447
+ return;
25448
+ }
25449
+ stopWatching();
25450
+ onSettled();
25451
+ };
25452
+ el.addEventListener("transitionend", onTransitionEnd);
25453
+ const durationsInSeconds = getComputedStyle(el)
25454
+ .transitionDuration.split(",")
25455
+ .map((value) => parseFloat(value) || 0);
25456
+ const longestDurationMs = Math.max(0, ...durationsInSeconds) * 1000;
25457
+ const safetyTimeoutId = setTimeout(finish, longestDurationMs + 50);
25458
+ return () => {
25459
+ if (settled) {
25460
+ return;
25461
+ }
25462
+ stopWatching();
25463
+ };
25464
+ };
25465
+
25466
+ /**
25467
+ * Disables pointer-events on `el` until its current CSS transition settles —
25468
+ * avoids the cursor changing/something becoming clickable while the popup is
25469
+ * still visually moving into or out of place.
25470
+ *
25471
+ * Returns whenTransitionSettles' own "cancel" function: it doesn't restore
25472
+ * pointer-events, since a fresh call for the next open/close is about to set
25473
+ * its own state.
25474
+ */
25475
+ const suppressPointerEventsDuringTransition = (el) => {
25476
+ el.style.pointerEvents = "none";
25477
+ return whenTransitionSettles(el, () => {
25478
+ el.style.pointerEvents = "";
25479
+ });
25480
+ };
25481
+
25482
+ /**
25483
+ * Hides the backdrop, deferring until the browser's matching "click" fires
25484
+ * when `closeEvent` was triggered by a mousedown (see popover.jsx's top
25485
+ * comment for why) — same capture-phase-on-document pattern as
25486
+ * armSuppressNextOpenRequest in open_controller.js, which a plain timeout
25487
+ * can't safely replace: mouseup (and the click that follows it) can land an
25488
+ * arbitrarily long time after mousedown (the user is still holding the
25489
+ * button down), so a short timeout can fire first and hide the backdrop
25490
+ * before its own click ever arrives. A capture-phase listener on document
25491
+ * fires for every click regardless of what any bubble-phase handler does
25492
+ * downstream, so no fallback timer is needed.
25493
+ *
25494
+ * `hide` is the caller's own way to actually hide the backdrop
25495
+ * (`hidePopover()` for a top-layer backdrop, a plain `style.display = "none"`
25496
+ * for a plain div) — this helper only owns the mousedown/click timing.
25497
+ *
25498
+ * Returns a disarm function (or undefined if hidden immediately), so a
25499
+ * fresh open can cancel a pending hide it's about to make redundant.
25500
+ */
25501
+ const armPointerDownOutsideClose = (closeEvent, hide) => {
25502
+ const mousedownEvent = findEvent(closeEvent, "mousedown");
25503
+ if (!mousedownEvent) {
25504
+ hide();
25505
+ return undefined;
25506
+ }
25507
+ const onClick = () => {
25508
+ document.removeEventListener("click", onClick, { capture: true });
25509
+ hide();
25510
+ };
25511
+ document.addEventListener("click", onClick, { capture: true });
25512
+ return () => {
25513
+ document.removeEventListener("click", onClick, { capture: true });
25514
+ };
25515
+ };
25516
+
25517
+ /**
25518
+ * Maps a positionArea y/x pair to a concrete `navi-animation` value (a
25519
+ * `prefix` plus a direction word), or `null` if both axes overlap the anchor
25520
+ * (no direction at all — that's `resolvedAnimationKind === "scaling"`
25521
+ * territory instead, see resolveAutoAnimationKind below).
25522
+ *
25523
+ * `prefix: "slide-from"` (used with no real anchor — Dialog always, Popover
25524
+ * when docked) keeps the word as the compass direction the popup comes
25525
+ * from: placed "top" (a point/corner), it slides in from the top.
25526
+ * `prefix: "expand"` (a real anchor, Popover-only) uses the motion/growth
25527
+ * direction instead, the opposite compass point: placed "top" of the
25528
+ * anchor, it moves/grows up, away from the anchor (which sits below it).
25529
+ *
25530
+ * "inset-*"/"center" contribute no direction on their axis either way.
25531
+ */
25532
+ const resolveDirectionValue = (y, x, { prefix }) => {
25533
+ const yWord =
25534
+ y === "top"
25535
+ ? prefix === "expand"
25536
+ ? "up"
25537
+ : "top"
25538
+ : y === "bottom"
25539
+ ? prefix === "expand"
25540
+ ? "down"
25541
+ : "bottom"
25542
+ : null;
25543
+ const xWord = x === "left" ? "left" : x === "right" ? "right" : null;
25544
+ if (!yWord && !xWord) {
25545
+ return null;
25546
+ }
25547
+ return yWord && xWord
25548
+ ? `${prefix}-${yWord}-${xWord}`
25549
+ : `${prefix}-${yWord || xWord}`;
25550
+ };
25551
+
25552
+ /**
25553
+ * Shared `animation="auto"`/`true` resolution: "scaling" reads best overall
25554
+ * — picked for any real anchor, or for a point/corner placed dead-center
25555
+ * (both positionArea axes overlapping — there's no sensible direction to
25556
+ * slide from in that case). "sliding" otherwise. `anchor` is `undefined`
25557
+ * for any no-anchor/docked case (Dialog always, Popover's own custom
25558
+ * renderer when there's no real anchor), so this collapses to "scaling"
25559
+ * there only for the dead-center case, "sliding" otherwise. The two
25560
+ * "overlapping" booleans below describe the *positionArea* itself (a bare
25561
+ * word vs. "inset-"/"center"), not anything about the anchor — they'd
25562
+ * mean exactly the same thing even with no anchor at all, since it's the
25563
+ * position strategy, not the anchor, that decides whether there's a
25564
+ * direction to slide from.
25565
+ */
25566
+ const resolveAutoAnimationKind = (anchor, parsedPositionArea) => {
25567
+ const yIsOverlapping =
25568
+ parsedPositionArea.y !== "top" && parsedPositionArea.y !== "bottom";
25569
+ const xIsOverlapping =
25570
+ parsedPositionArea.x !== "left" && parsedPositionArea.x !== "right";
25571
+ return anchor || (yIsOverlapping && xIsOverlapping) ? "scaling" : "sliding";
25572
+ };
25573
+
25574
+ /**
25575
+ * When a popup builds what it holds, and when it throws it away.
25411
25576
  *
25412
25577
  * A closed popup shows nothing, focuses nothing, and answers nothing: what it
25413
25578
  * holds is out of reach until it opens. Building that content at mount time
@@ -25430,12 +25595,18 @@ const flushSyncRendering = (fn) => {
25430
25595
  * `mountWhenClosed` is for content something else depends on before any of
25431
25596
  * this: a value the popup's owner reads off its own children, fields a form
25432
25597
  * around it collects on submit, a size measured from outside.
25598
+ *
25599
+ * `unmountWhenClosed` is the opposite end: content that must be rebuilt from
25600
+ * scratch every time, because what it shows is read once at build time and can
25601
+ * change while the popup is closed — an uncontrolled field seeded from a
25602
+ * `defaultValue`, a form whose fresh state is its initial state.
25433
25603
  */
25434
25604
 
25435
25605
 
25436
25606
  const usePopupContentMount = (
25437
25607
  openController,
25438
- { children, mountWhenClosed },
25608
+ ref,
25609
+ { children, mountWhenClosed, unmountWhenClosed },
25439
25610
  ) => {
25440
25611
  const [contentMounted, setContentMounted] = useState(
25441
25612
  () => Boolean(mountWhenClosed) || openController.opened,
@@ -25447,6 +25618,27 @@ const usePopupContentMount = (
25447
25618
  setContentMounted(true);
25448
25619
  });
25449
25620
  };
25621
+ openController.unmountContent =
25622
+ unmountWhenClosed && !mountWhenClosed
25623
+ ? () => {
25624
+ const element = ref?.current;
25625
+ if (!element) {
25626
+ setContentMounted(false);
25627
+ return;
25628
+ }
25629
+ // The popup is still on screen while it plays its exit transition;
25630
+ // emptying it right away would show that transition running on a
25631
+ // blank surface.
25632
+ whenTransitionSettles(element, () => {
25633
+ if (openController.opened) {
25634
+ // reopened while it was leaving — the content it holds is the
25635
+ // one that open just asked for
25636
+ return;
25637
+ }
25638
+ setContentMounted(false);
25639
+ });
25640
+ }
25641
+ : null;
25450
25642
  useLayoutEffect(() => {
25451
25643
  if (mountWhenClosed) {
25452
25644
  setContentMounted(true);
@@ -25728,147 +25920,36 @@ const popupCss = /* css */ `
25728
25920
  `;
25729
25921
 
25730
25922
  /**
25731
- * Small, renderer-agnostic helpers shared by Popover and Dialog's own custom
25732
- * (non-top-layer) renderers operate on a plain DOM element, no knowledge
25733
- * of which of the two owns it.
25734
- */
25735
-
25736
-
25737
- /**
25738
- * Disables pointer-events on `el` until its current CSS transition settles
25739
- * (via `transitionend`, with a safety `setTimeout` fallback matching the
25740
- * longest `transition-duration` in case nothing actually transitions or an
25741
- * event is missed) — avoids the cursor changing/something becoming
25742
- * clickable while the popup is still visually moving into or out of place.
25743
- *
25744
- * Returns a "cancel" function: doesn't restore pointer-events (a fresh call
25745
- * for the next open/close is about to set its own state) — only prevents
25746
- * this stale instance's `transitionend` listener/timeout from firing later
25747
- * and clobbering that fresh state.
25748
- */
25749
- const suppressPointerEventsDuringTransition = (el) => {
25750
- el.style.pointerEvents = "none";
25751
- let settled = false;
25752
- const onTransitionEnd = (transitionEvent) => {
25753
- if (transitionEvent.target === el) {
25754
- finish();
25755
- }
25756
- };
25757
- const finish = () => {
25758
- if (settled) {
25759
- return;
25760
- }
25761
- settled = true;
25762
- el.style.pointerEvents = "";
25763
- el.removeEventListener("transitionend", onTransitionEnd);
25764
- clearTimeout(safetyTimeoutId);
25765
- };
25766
- el.addEventListener("transitionend", onTransitionEnd);
25767
- const durationsInSeconds = getComputedStyle(el)
25768
- .transitionDuration.split(",")
25769
- .map((value) => parseFloat(value) || 0);
25770
- const longestDurationMs = Math.max(0, ...durationsInSeconds) * 1000;
25771
- const safetyTimeoutId = setTimeout(finish, longestDurationMs + 50);
25772
- return () => {
25773
- if (settled) {
25774
- return;
25775
- }
25776
- settled = true;
25777
- el.removeEventListener("transitionend", onTransitionEnd);
25778
- clearTimeout(safetyTimeoutId);
25779
- };
25780
- };
25781
-
25782
- /**
25783
- * Hides the backdrop, deferring until the browser's matching "click" fires
25784
- * when `closeEvent` was triggered by a mousedown (see popover.jsx's top
25785
- * comment for why) — same capture-phase-on-document pattern as
25786
- * armSuppressNextOpenRequest in open_controller.js, which a plain timeout
25787
- * can't safely replace: mouseup (and the click that follows it) can land an
25788
- * arbitrarily long time after mousedown (the user is still holding the
25789
- * button down), so a short timeout can fire first and hide the backdrop
25790
- * before its own click ever arrives. A capture-phase listener on document
25791
- * fires for every click regardless of what any bubble-phase handler does
25792
- * downstream, so no fallback timer is needed.
25793
- *
25794
- * `hide` is the caller's own way to actually hide the backdrop
25795
- * (`hidePopover()` for a top-layer backdrop, a plain `style.display = "none"`
25796
- * for a plain div) — this helper only owns the mousedown/click timing.
25923
+ * Holding a box at the size it has right now — what the `sizing="frozen"` prop
25924
+ * on Dialog, Popover and SlideContainer is made of.
25797
25925
  *
25798
- * Returns a disarm function (or undefined if hidden immediately), so a
25799
- * fresh open can cancel a pending hide it's about to make redundant.
25926
+ * The need: a surface being used is a frame that has been put down. What moves
25927
+ * inside it is its content, not the frame — a list one empties by acting on it
25928
+ * (marking as read, archiving) must not resize the box under the finger, or the
25929
+ * next row moves while it is being aimed at.
25800
25930
  */
25801
- const armPointerDownOutsideClose = (closeEvent, hide) => {
25802
- const mousedownEvent = findEvent(closeEvent, "mousedown");
25803
- if (!mousedownEvent) {
25804
- hide();
25805
- return undefined;
25806
- }
25807
- const onClick = () => {
25808
- document.removeEventListener("click", onClick, { capture: true });
25809
- hide();
25810
- };
25811
- document.addEventListener("click", onClick, { capture: true });
25812
- return () => {
25813
- document.removeEventListener("click", onClick, { capture: true });
25814
- };
25815
- };
25816
25931
 
25817
25932
  /**
25818
- * Maps a positionArea y/x pair to a concrete `navi-animation` value (a
25819
- * `prefix` plus a direction word), or `null` if both axes overlap the anchor
25820
- * (no direction at all that's `resolvedAnimationKind === "scaling"`
25821
- * territory instead, see resolveAutoAnimationKind below).
25822
- *
25823
- * `prefix: "slide-from"` (used with no real anchor — Dialog always, Popover
25824
- * when docked) keeps the word as the compass direction the popup comes
25825
- * from: placed "top" (a point/corner), it slides in from the top.
25826
- * `prefix: "expand"` (a real anchor, Popover-only) uses the motion/growth
25827
- * direction instead, the opposite compass point: placed "top" of the
25828
- * anchor, it moves/grows up, away from the anchor (which sits below it).
25829
- *
25830
- * "inset-*"/"center" contribute no direction on their axis either way.
25933
+ * Read back through getComputedStyle rather than offsetWidth/offsetHeight: the
25934
+ * used values honour whatever `box-sizing` is in effect, so writing them back
25935
+ * reproduces exactly the box that was measured, to the subpixel. Neither is
25936
+ * affected by a transform, so this is safe to call while an entrance animation
25937
+ * is scaling the box.
25938
+ *
25939
+ * `width`/`height`, never `min-width`/`min-height`: a `max-*` the caller's
25940
+ * own, or the container ceiling a popup already computes for itself must keep
25941
+ * winning, so a box frozen at 500px on a phone held upright still fits once it
25942
+ * is turned.
25831
25943
  */
25832
- const resolveDirectionValue = (y, x, { prefix }) => {
25833
- const yWord =
25834
- y === "top"
25835
- ? prefix === "expand"
25836
- ? "up"
25837
- : "top"
25838
- : y === "bottom"
25839
- ? prefix === "expand"
25840
- ? "down"
25841
- : "bottom"
25842
- : null;
25843
- const xWord = x === "left" ? "left" : x === "right" ? "right" : null;
25844
- if (!yWord && !xWord) {
25845
- return null;
25846
- }
25847
- return yWord && xWord
25848
- ? `${prefix}-${yWord}-${xWord}`
25849
- : `${prefix}-${yWord || xWord}`;
25944
+ const freezeSize = (el) => {
25945
+ const { width, height } = getComputedStyle(el);
25946
+ el.style.width = width;
25947
+ el.style.height = height;
25850
25948
  };
25851
25949
 
25852
- /**
25853
- * Shared `animation="auto"`/`true` resolution: "scaling" reads best overall
25854
- * — picked for any real anchor, or for a point/corner placed dead-center
25855
- * (both positionArea axes overlapping — there's no sensible direction to
25856
- * slide from in that case). "sliding" otherwise. `anchor` is `undefined`
25857
- * for any no-anchor/docked case (Dialog always, Popover's own custom
25858
- * renderer when there's no real anchor), so this collapses to "scaling"
25859
- * there only for the dead-center case, "sliding" otherwise. The two
25860
- * "overlapping" booleans below describe the *positionArea* itself (a bare
25861
- * word vs. "inset-"/"center"), not anything about the anchor — they'd
25862
- * mean exactly the same thing even with no anchor at all, since it's the
25863
- * position strategy, not the anchor, that decides whether there's a
25864
- * direction to slide from.
25865
- */
25866
- const resolveAutoAnimationKind = (anchor, parsedPositionArea) => {
25867
- const yIsOverlapping =
25868
- parsedPositionArea.y !== "top" && parsedPositionArea.y !== "bottom";
25869
- const xIsOverlapping =
25870
- parsedPositionArea.x !== "left" && parsedPositionArea.x !== "right";
25871
- return anchor || (yIsOverlapping && xIsOverlapping) ? "scaling" : "sliding";
25950
+ const unfreezeSize = (el) => {
25951
+ el.style.width = "";
25952
+ el.style.height = "";
25872
25953
  };
25873
25954
 
25874
25955
  installImportMetaCssBuild(import.meta);/**
@@ -26311,6 +26392,19 @@ const css$V = /* css */`
26311
26392
  * @param {string} [props.minHeight] - Maps to `--dialog-min-height`, same
26312
26393
  * clamping as `minWidth`.
26313
26394
  * @param {string} [props.maxHeight] - Maps to `--dialog-max-height`.
26395
+ * @param {"auto"|"frozen"} [props.sizing="auto"] - `"auto"`: the dialog follows
26396
+ * its content for as long as it stays open. `"frozen"`: it is measured once
26397
+ * and held at that size until it closes — what no longer fits (or no longer
26398
+ * fills it) is the scroll's business. For a surface acted upon while it is
26399
+ * open: marking a notification as read, emptying a queue, swapping between
26400
+ * two slides of different heights — the row being aimed at must not move
26401
+ * under the finger. The measure is taken at the first render where this says
26402
+ * `"frozen"`, so a dialog opening on skeletons can say
26403
+ * `sizing={loading ? "auto" : "frozen"}` and be measured once the real
26404
+ * content is there. The freeze writes a `height`/`width`, never a `min-*`:
26405
+ * `maxHeight`/`maxWidth` and the container ceiling keep winning, so a frozen
26406
+ * dialog still fits when the phone is turned. Closing releases it — the next
26407
+ * opening measures again.
26314
26408
  * @param {number} [props.tabIndex=-1] - Set on the dialog element itself so
26315
26409
  * `autoFocus="last-resort"` below has somewhere to land when the dialog has
26316
26410
  * no other focusable descendant of its own.
@@ -26338,6 +26432,11 @@ const css$V = /* css */`
26338
26432
  * content something depends on while the popup is still closed: a value read
26339
26433
  * off it, fields a surrounding form collects on submit, a size measured from
26340
26434
  * outside.
26435
+ * @param {boolean} [props.unmountWhenClosed] - Throws `children` away once the
26436
+ * popup has finished closing (see popup_content_mount.js). For content whose
26437
+ * fresh state is its initial state: an uncontrolled field seeded from a
26438
+ * `defaultValue` that changed while the popup was closed. Ignored when
26439
+ * `mountWhenClosed` is set.
26341
26440
  * @param {import("ignore:preact").ComponentChildren} props.children
26342
26441
  */
26343
26442
  const Dialog = props => {
@@ -26515,6 +26614,9 @@ const useDialogProps = props => {
26515
26614
  // actually makes "capture"/"none" behave the same way here too.
26516
26615
  pointerInteractionOutsideEffect = "close",
26517
26616
  scrollCapture: scrollCaptureProp,
26617
+ // "auto" (default) → the dialog follows its content. "frozen" → measured
26618
+ // once, held at that size while open. See this prop's own JSDoc above.
26619
+ sizing = "auto",
26518
26620
  animation,
26519
26621
  // Only ever affects --anchor-width/--anchor-height (see this file's top
26520
26622
  // comment) — Dialog's own positioning is never relative to it.
@@ -26531,11 +26633,13 @@ const useDialogProps = props => {
26531
26633
  onKeyDown,
26532
26634
  children: childrenProp,
26533
26635
  mountWhenClosed,
26636
+ unmountWhenClosed,
26534
26637
  ...rest
26535
26638
  } = props;
26536
- const children = usePopupContentMount(openController, {
26639
+ const children = usePopupContentMount(openController, props.ref, {
26537
26640
  children: childrenProp,
26538
- mountWhenClosed
26641
+ mountWhenClosed,
26642
+ unmountWhenClosed
26539
26643
  });
26540
26644
  const isModal = layer === "top";
26541
26645
  const ref = props.ref;
@@ -26577,6 +26681,21 @@ const useDialogProps = props => {
26577
26681
  detail: {}
26578
26682
  }));
26579
26683
  }, [positionArea, marginWithContainer]);
26684
+ // The freeze is taken where the value changes, not only at open time: a
26685
+ // dialog showing skeletons first says sizing="auto" until its content is
26686
+ // there, and would otherwise be held at the size of the waiting state.
26687
+ // Opening while already "frozen" is openEffect's own case.
26688
+ useEffect(() => {
26689
+ const dialogEl = ref.current;
26690
+ if (!dialogEl || !openController.opened) {
26691
+ return;
26692
+ }
26693
+ if (sizing === "frozen") {
26694
+ freezeSize(dialogEl);
26695
+ } else {
26696
+ unfreezeSize(dialogEl);
26697
+ }
26698
+ }, [sizing]);
26580
26699
  const positionAreaParseResult = parsePositionArea(positionArea);
26581
26700
  if (!positionAreaParseResult) {
26582
26701
  console.warn(`Dialog: invalid positionArea="${positionArea}"`);
@@ -26808,6 +26927,13 @@ const useDialogProps = props => {
26808
26927
  // navi_position_change on every call) — nothing to do here.
26809
26928
  };
26810
26929
  positionDialog();
26930
+ if (sizing === "frozen") {
26931
+ // After positionDialog: the caps it writes
26932
+ // (--container-position-remaining-*) are part of what decides the size
26933
+ // being taken, so measuring before it would freeze a box the dialog
26934
+ // never actually had.
26935
+ freezeSize(dialogEl);
26936
+ }
26811
26937
 
26812
26938
  // Reposition on the same triggers Popover's own visibleRectEffect
26813
26939
  // already reacts to generically — window resize/scroll/visual-viewport
@@ -26939,6 +27065,9 @@ const useDialogProps = props => {
26939
27065
  // property is actually present — harmless the rest of the time.
26940
27066
  dialogEl.setAttribute("navi-hidden", "");
26941
27067
  dialogEl.close();
27068
+ // The freeze only ever holds for one opening: the next one has its own
27069
+ // content to be measured against.
27070
+ unfreezeSize(dialogEl);
26942
27071
  cancelOpenInteractionSuppression?.();
26943
27072
  if (hasCssTransitionAnimation) {
26944
27073
  suppressPointerEventsDuringTransition(dialogEl);
@@ -27516,6 +27645,18 @@ const css$U = /* css */`
27516
27645
  * @param {string} [props.minHeight] - Maps to `--popover-min-height`, same
27517
27646
  * clamping as `minWidth`.
27518
27647
  * @param {string} [props.maxHeight] - Maps to `--popover-max-height`.
27648
+ * @param {"auto"|"frozen"} [props.sizing="auto"] - `"auto"`: the popover
27649
+ * follows its content for as long as it stays open. `"frozen"`: it is
27650
+ * measured once and held at that size until it closes — what no longer fits
27651
+ * (or no longer fills it) is the scroll's business. For a surface acted upon
27652
+ * while it is open: emptying a list, swapping between two panels of
27653
+ * different heights — the row being aimed at must not move under the
27654
+ * pointer. The measure is taken at the first render where this says
27655
+ * `"frozen"`, so a popover opening on skeletons can say
27656
+ * `sizing={loading ? "auto" : "frozen"}` and be measured once the real
27657
+ * content is there. The freeze writes a `height`/`width`, never a `min-*`:
27658
+ * `maxHeight`/`maxWidth` and the container ceiling keep winning. Closing
27659
+ * releases it — the next opening measures again.
27519
27660
  * @param {number} [props.tabIndex=-1] - Set on the popover element itself
27520
27661
  * so `autoFocus="last-resort"` below has somewhere to land when the popover
27521
27662
  * has no other focusable descendant of its own.
@@ -27546,6 +27687,11 @@ const css$U = /* css */`
27546
27687
  * content something depends on while the popup is still closed: a value read
27547
27688
  * off it, fields a surrounding form collects on submit, a size measured from
27548
27689
  * outside.
27690
+ * @param {boolean} [props.unmountWhenClosed] - Throws `children` away once the
27691
+ * popup has finished closing (see popup_content_mount.js). For content whose
27692
+ * fresh state is its initial state: an uncontrolled field seeded from a
27693
+ * `defaultValue` that changed while the popup was closed. Ignored when
27694
+ * `mountWhenClosed` is set.
27549
27695
  * @param {import("ignore:preact").ComponentChildren} props.children
27550
27696
  */
27551
27697
  const Popover = props => {
@@ -27716,6 +27862,9 @@ const usePopoverProps = props => {
27716
27862
  pointerInteractionOutsideEffect = "none",
27717
27863
  scrollCapture,
27718
27864
  focusCapture,
27865
+ // "auto" (default) → the popover follows its content. "frozen" → measured
27866
+ // once, held at that size while open. See this prop's own JSDoc above.
27867
+ sizing = "auto",
27719
27868
  animation,
27720
27869
  anchor,
27721
27870
  anchorCustomEventDetail = "override",
@@ -27732,11 +27881,13 @@ const usePopoverProps = props => {
27732
27881
  onKeyDown,
27733
27882
  children: childrenProp,
27734
27883
  mountWhenClosed,
27884
+ unmountWhenClosed,
27735
27885
  ...rest
27736
27886
  } = props;
27737
- const children = usePopupContentMount(openController, {
27887
+ const children = usePopupContentMount(openController, props.ref, {
27738
27888
  children: childrenProp,
27739
- mountWhenClosed
27889
+ mountWhenClosed,
27890
+ unmountWhenClosed
27740
27891
  });
27741
27892
  const isTopLayer = layer === "top";
27742
27893
  const ref = props.ref;
@@ -27774,6 +27925,21 @@ const usePopoverProps = props => {
27774
27925
  detail: {}
27775
27926
  }));
27776
27927
  }, [positionArea, positionAreaFixed, positionAreaWhenAnchorIsInvalid, marginWithAnchor, marginWithContainer]);
27928
+ // The freeze is taken where the value changes, not only at open time: a
27929
+ // popover showing skeletons first says sizing="auto" until its content is
27930
+ // there, and would otherwise be held at the size of the waiting state.
27931
+ // Opening while already "frozen" is openEffect's own case.
27932
+ useEffect(() => {
27933
+ const popoverEl = ref.current;
27934
+ if (!popoverEl || !openController.opened) {
27935
+ return;
27936
+ }
27937
+ if (sizing === "frozen") {
27938
+ freezeSize(popoverEl);
27939
+ } else {
27940
+ unfreezeSize(popoverEl);
27941
+ }
27942
+ }, [sizing]);
27777
27943
  // The custom renderer's own starting-hidden state is a stylesheet default
27778
27944
  // now (&:not([popover]) { display: none } on .navi_popover/
27779
27945
  // .navi_popover_backdrop above) rather than set here imperatively — a
@@ -28170,6 +28336,14 @@ const usePopoverProps = props => {
28170
28336
  addCleanup(() => {
28171
28337
  rectEffect.disconnect();
28172
28338
  });
28339
+ if (sizing === "frozen") {
28340
+ // After rectEffect's own setup, which has already placed the popover:
28341
+ // the caps that placement writes
28342
+ // (--container-position-remaining-*) are part of what decides the size
28343
+ // being taken, so measuring before it would freeze a box the popover
28344
+ // never actually had.
28345
+ freezeSize(popoverEl);
28346
+ }
28173
28347
 
28174
28348
  // "sliding"/"expanding" need a concrete direction (see
28175
28349
  // resolveDirectionValue) — resolved here, once, now that rectEffect's
@@ -28265,6 +28439,9 @@ const usePopoverProps = props => {
28265
28439
  } else {
28266
28440
  openLocalPopoverCount = Math.max(0, openLocalPopoverCount - 1);
28267
28441
  }
28442
+ // The freeze only ever holds for one opening: the next one has its own
28443
+ // content to be measured against.
28444
+ unfreezeSize(popoverEl);
28268
28445
  // Not interactive while it's leaving either — cancel the open side's
28269
28446
  // still-pending suppression first, since a fresh one below fully
28270
28447
  // replaces it (nothing ever needs to cancel this one in turn: a
@@ -34769,7 +34946,14 @@ const css$R = /* css */`
34769
34946
 
34770
34947
  &::view-transition-old(navi-route-travel),
34771
34948
  &::view-transition-new(navi-route-travel) {
34772
- height: 100%;
34949
+ /* Each picture at the size it was taken at: a page is not resized by the
34950
+ page it crosses. Told to fill a box whose height is being animated, a
34951
+ picture is STRETCHED with it — the page leaving is then seen squashing
34952
+ upwards, or zooming, over the length of the travel, when all it is
34953
+ doing is walking off the edge. */
34954
+ height: auto;
34955
+ object-fit: none;
34956
+ object-position: top left;
34773
34957
  /* The default cross-fade, dropped: two pages sliding past each other are
34774
34958
  two solid things, and seeing through one to the other says they are the
34775
34959
  same page changing its mind. */
@@ -34785,7 +34969,21 @@ const css$R = /* css */`
34785
34969
  overflow: clip;
34786
34970
  }
34787
34971
  &::view-transition-group(navi-route-travel) {
34972
+ /* The window the two pictures are seen through, held still for the whole
34973
+ travel at the taller of the two boxes (see holdTravelHeight): the group
34974
+ is what CLIPS, and the browser animates its height from the box being
34975
+ left to the box arriving — so the window shrinks under the pictures and
34976
+ cuts the page leaving from the bottom, progressively. The box does end
34977
+ up at the arriving page's height, and that is right; what must not
34978
+ happen is the user watching it get there.
34979
+
34980
+ The height is held by dropping the group's animation rather than by
34981
+ winning against it with !important — which also drops its position
34982
+ animation, fine while a travel box stands in the same place from one
34983
+ route to the next. */
34984
+ height: var(--navi-route-travel-height);
34788
34985
  animation-duration: var(--navi-route-travel-duration, 300ms);
34986
+ animation-name: none;
34789
34987
  }
34790
34988
  }
34791
34989
 
@@ -35015,18 +35213,26 @@ const RouteTravel = ({
35015
35213
  document.documentElement.setAttribute(DRAGGED_ATTRIBUTE, "");
35016
35214
  }
35017
35215
  routeAskedForRef.current = route;
35216
+ // The box as it stands before anything moves: rendering is held, so this is
35217
+ // still the page being left (see holdTravelHeight).
35218
+ const heightBefore = elementRef.current.getBoundingClientRect().height;
35018
35219
  // The hold a navigation already took, if this travel is the answer to one:
35019
35220
  // taking another would be taking a hold on a page that is holding still.
35020
35221
  const releaseRendering = renderingHeldForRouting || holdRendering();
35021
35222
  renderingHeldForRouting = null;
35022
35223
  // The picture the browser is about to take must be of the page that was
35023
35224
  // asked for, and a route matching is not yet a page rendered.
35024
- const viewTransition = startViewTransition(() => whileRouteRenders(route, async () => {
35025
- releaseRendering();
35026
- if (change) {
35027
- await change();
35028
- }
35029
- }));
35225
+ const viewTransition = startViewTransition(async () => {
35226
+ await whileRouteRenders(route, async () => {
35227
+ releaseRendering();
35228
+ if (change) {
35229
+ await change();
35230
+ }
35231
+ });
35232
+ // The page arriving is in the DOM and the transition has not started
35233
+ // playing: the one moment both boxes can be known.
35234
+ holdTravelHeight(elementRef.current, heightBefore);
35235
+ });
35030
35236
  travel.viewTransition = viewTransition;
35031
35237
  if (scrub) {
35032
35238
  // Said only now: the release has to have something to let go of, and the
@@ -35320,6 +35526,7 @@ const RouteTravel = ({
35320
35526
  document.documentElement.removeAttribute(TRAVEL_ATTRIBUTE);
35321
35527
  document.documentElement.removeAttribute(DRAGGED_ATTRIBUTE);
35322
35528
  document.documentElement.removeAttribute(TURNED_ATTRIBUTE);
35529
+ releaseTravelHeight();
35323
35530
  }
35324
35531
  };
35325
35532
 
@@ -35701,6 +35908,24 @@ const releaseHold = travel => {
35701
35908
  travelHoldingPictures = null;
35702
35909
  document.documentElement.removeAttribute(HOLD_ATTRIBUTE);
35703
35910
  };
35911
+ const TRAVEL_HEIGHT_PROPERTY = "--navi-route-travel-height";
35912
+ // The height the group is held at for the whole travel: the taller of the two
35913
+ // boxes, so neither picture is ever cut. It cannot be said in CSS — neither box
35914
+ // is knowable there — and it cannot be measured from one side alone: a page
35915
+ // arriving shorter than the one it replaces would cut the one leaving, a page
35916
+ // arriving taller would be cut itself.
35917
+ const holdTravelHeight = (element, heightBefore) => {
35918
+ const heightAfter = element.getBoundingClientRect().height;
35919
+ const height = heightBefore > heightAfter ? heightBefore : heightAfter;
35920
+ document.documentElement.style.setProperty(TRAVEL_HEIGHT_PROPERTY, `${height}px`);
35921
+ };
35922
+ // The live layout takes the box back. A discontinuity by construction — the
35923
+ // group stands at the held height, the box is at the new one — and an invisible
35924
+ // one: the page arriving is fully in place, and the strip below it that the
35925
+ // group still covers shows the page leaving only while it is still on screen.
35926
+ const releaseTravelHeight = () => {
35927
+ document.documentElement.style.removeProperty(TRAVEL_HEIGHT_PROPERTY);
35928
+ };
35704
35929
 
35705
35930
  // The browser does not take the picture of the page being left when a
35706
35931
  // transition is ASKED for — it takes it at the next frame, just before running
@@ -45593,6 +45818,18 @@ const readArea = slideElement => slideElement.getAttribute("data-slide-area") ||
45593
45818
  * scrolling the document — so a map that travels vertically has to be told
45594
45819
  * (`travelByScroll` / `"y"` / `"xy"`) before a wheel moves it.
45595
45820
  * @param {string} [props.duration="300ms"] - how long a slide change takes.
45821
+ * @param {"largest"|"frozen"} [props.sizing="largest"] - what decides the size
45822
+ * of the box. "largest": the largest slide, at every moment — the box follows
45823
+ * whatever the slides do. "frozen": the box is measured once and kept at that
45824
+ * size, and what no longer fits (or no longer fills it) is the scroll's
45825
+ * business. For slides one ACTS on rather than merely reads: a row marked as
45826
+ * read leaves one panel for the other, the tallest slide is not the same one
45827
+ * anymore, and with "largest" the box resizes under the finger aiming at the
45828
+ * next row. The measure is taken at the first render where this says
45829
+ * "frozen", so a container opening on skeletons says
45830
+ * `sizing={loading ? "largest" : "frozen"}` and is measured once the real
45831
+ * content is there. The freeze holds against what happens INSIDE the box, not
45832
+ * against the room it is given: a window resize measures again.
45596
45833
  */
45597
45834
  const SlideContainer = ({
45598
45835
  layout = "row",
@@ -45606,6 +45843,7 @@ const SlideContainer = ({
45606
45843
  travelByDrag = true,
45607
45844
  travelByScroll = "x",
45608
45845
  duration = "300ms",
45846
+ sizing = "largest",
45609
45847
  children,
45610
45848
  ...rest
45611
45849
  }) => {
@@ -45740,6 +45978,31 @@ const SlideContainer = ({
45740
45978
  };
45741
45979
  }, [noTravel]);
45742
45980
 
45981
+ // The box, held at the size it has right now (sizing="frozen"): what the
45982
+ // slides do afterwards moves their own content and not this box. Taken where
45983
+ // the value changes rather than at mount alone, so a container opening on
45984
+ // skeletons is measured on the real thing (see the prop's own doc).
45985
+ useLayoutEffect(() => {
45986
+ if (sizing !== "frozen") {
45987
+ return undefined;
45988
+ }
45989
+ const containerEl = containerRef.current;
45990
+ freezeSize(containerEl);
45991
+ // The freeze is about the content, never about the room: a box frozen on a
45992
+ // phone held upright has to fit once it is turned, and one frozen in a
45993
+ // window has to follow that window. So the measure is taken again whenever
45994
+ // the room changes — the only moment the box is allowed to resize.
45995
+ const onWindowResize = () => {
45996
+ unfreezeSize(containerEl);
45997
+ freezeSize(containerEl);
45998
+ };
45999
+ window.addEventListener("resize", onWindowResize);
46000
+ return () => {
46001
+ window.removeEventListener("resize", onWindowResize);
46002
+ unfreezeSize(containerEl);
46003
+ };
46004
+ }, [sizing]);
46005
+
45743
46006
  // What the user was doing on each slide, so coming back comes back to it. The
45744
46007
  // ways out are left out on purpose: pressing one is how one LEAVES a slide,
45745
46008
  // and remembering it would mean coming back to the exit rather than to the
@@ -48151,6 +48414,10 @@ const css$z = /* css */`
48151
48414
  * @param {string} [props.minWidth] - Forwarded as-is.
48152
48415
  * @param {string} [props.minHeight] - Forwarded as-is.
48153
48416
  * @param {string} [props.maxHeight] - Forwarded as-is.
48417
+ * @param {"auto"|"frozen"} [props.sizing] - Forwarded as-is to both, which
48418
+ * understand it identically: `"frozen"` holds the surface at the size it was
48419
+ * measured at while it stays open, so acting on what it contains moves the
48420
+ * content and not the surface. See either component's own doc.
48154
48421
  * @param {boolean} [props.expand] - Dialog-mode only: shorthand for both
48155
48422
  * `expandX`/`expandY` below. No effect in popover mode.
48156
48423
  * @param {boolean} [props.expandX] - Dialog-mode only: stretches the dialog
@@ -48172,6 +48439,11 @@ const css$z = /* css */`
48172
48439
  * content something depends on while the popup is still closed: a value read
48173
48440
  * off it, fields a surrounding form collects on submit, a size measured from
48174
48441
  * outside.
48442
+ * @param {boolean} [props.unmountWhenClosed] - Throws `children` away once the
48443
+ * popup has finished closing (see popup_content_mount.js). For content whose
48444
+ * fresh state is its initial state: an uncontrolled field seeded from a
48445
+ * `defaultValue` that changed while the popup was closed. Ignored when
48446
+ * `mountWhenClosed` is set.
48175
48447
  * @param {import("ignore:preact").ComponentChildren} props.children
48176
48448
  */
48177
48449
  const Popup = props => {
@@ -51166,10 +51438,17 @@ const ListUI = props => {
51166
51438
  // search fallback), otherwise an empty list is the "empty" state.
51167
51439
  const searchFallbackShown = (allNoMatch || searching && itemCount === 0) && !searchFallbackDisabled;
51168
51440
  const emptyFallbackShown = !searching && itemCount === 0 && !fallbackDisabled;
51441
+ // A loading state only holds the list on screen when it has something to
51442
+ // draw. A count of 0 (or no loadingFallback at all) says the list is known to
51443
+ // be empty before the response arrives, so the empty state can already be
51444
+ // shown — nothing jumps when the response lands, exactly as three skeletons
51445
+ // become three rows.
51446
+ const loadingPlaceholderShown = Boolean(loading) && Boolean(loadingFallback) && (loadingFallback !== "skeleton" || loadingSkeletonCount > 0);
51169
51447
  // Hide the whole list — border included — when there is genuinely nothing to
51170
- // show: no visible items AND no fallback message. Never while loading or in
51171
- // error (the placeholder / error message ARE the content to display).
51172
- const nothingToDisplay = !loading && !error && noVisibleItems && !searchFallbackShown && !emptyFallbackShown;
51448
+ // show: no visible items AND no fallback message. Never while a loading
51449
+ // placeholder or an error message is on screen (they ARE the content to
51450
+ // display).
51451
+ const nothingToDisplay = !loadingPlaceholderShown && !error && noVisibleItems && !searchFallbackShown && !emptyFallbackShown;
51173
51452
 
51174
51453
  // Placeholder content replaces the real children: an error message when the
51175
51454
  // load failed (takes precedence), otherwise — while loading — whatever
@@ -51264,7 +51543,7 @@ const ListUI = props => {
51264
51543
  fallbackShown: emptyFallbackShown,
51265
51544
  searchFallback: searchFallback,
51266
51545
  searchFallbackShown: searchFallbackShown,
51267
- loading: loading,
51546
+ loadingPlaceholderShown: loadingPlaceholderShown,
51268
51547
  error: error,
51269
51548
  searchNoMatchMode: searchNoMatchMode,
51270
51549
  separator: separator,
@@ -51364,6 +51643,10 @@ const ListFirstResolver = props => {
51364
51643
  * displays nothing. A list that knows how many rows it will have has no use
51365
51644
  * for this — see `<List.Items count>`, whose not-yet-loaded rows are drawn
51366
51645
  * as skeletons in place, one per row, virtualized like the rest.
51646
+ * @param {number} [props.loadingSkeletonCount=3]
51647
+ * How many placeholder rows `loadingFallback="skeleton"` draws. `0` says the
51648
+ * list is already known to be empty: the empty `fallback` shows right away
51649
+ * rather than an empty frame, so nothing moves when the response arrives.
51367
51650
  * @param {"start"|"end"|number|{id: string, offset?: number}} [props.defaultScrolled="start"]
51368
51651
  * Where the list opens, after which the user owns the scroll. `"end"` is a
51369
51652
  * thread read backwards — the last rows are the ones to show, and the ones
@@ -51424,7 +51707,7 @@ const ListContent = ({
51424
51707
  fallbackShown,
51425
51708
  searchFallback,
51426
51709
  searchFallbackShown,
51427
- loading,
51710
+ loadingPlaceholderShown,
51428
51711
  error,
51429
51712
  searchNoMatchMode,
51430
51713
  separator,
@@ -51448,7 +51731,7 @@ const ListContent = ({
51448
51731
  fallbackShown: fallbackShown,
51449
51732
  searchFallback: searchFallback,
51450
51733
  searchFallbackShown: searchFallbackShown,
51451
- loading: loading,
51734
+ loadingPlaceholderShown: loadingPlaceholderShown,
51452
51735
  error: error,
51453
51736
  searchNoMatchMode: searchNoMatchMode,
51454
51737
  separator: separator,
@@ -52736,7 +53019,7 @@ const UnorderedList = ({
52736
53019
  fallbackShown,
52737
53020
  searchFallback,
52738
53021
  searchFallbackShown,
52739
- loading,
53022
+ loadingPlaceholderShown,
52740
53023
  error,
52741
53024
  searchNoMatchMode,
52742
53025
  separator,
@@ -52747,9 +53030,11 @@ const UnorderedList = ({
52747
53030
  children,
52748
53031
  ...rest
52749
53032
  }) => {
52750
- // No empty/no-match message while loading or in error — the placeholder /
52751
- // error message is the content, even though no items are tracked yet.
52752
- const suppressFallback = loading || Boolean(error);
53033
+ // No empty/no-match message while a loading placeholder or an error message
53034
+ // is on screen — that IS the content, even though no items are tracked yet.
53035
+ // A loading state drawing nothing keeps the message: an announced count of 0
53036
+ // already tells us the list is empty (see ListUI's loadingPlaceholderShown).
53037
+ const suppressFallback = loadingPlaceholderShown || Boolean(error);
52753
53038
  return jsxs(Box, {
52754
53039
  as: "ul",
52755
53040
  flex: columns ? undefined : horizontal ? "x" : "y",
@@ -66660,5 +66945,5 @@ const UserSvg = () => jsx("svg", {
66660
66945
  })
66661
66946
  });
66662
66947
 
66663
- export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineNaviConfirmPopupOptions, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, valueInLocalStorage, windowWidthSignal };
66948
+ export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineNaviConfirmPopupOptions, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, valueInLocalStorage, windowWidthSignal };
66664
66949
  //# sourceMappingURL=jsenv_navi.js.map