@jsenv/navi 0.29.213 → 0.29.215

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.
@@ -44924,6 +44924,10 @@ const FOCUS_DELAY_ON_KEYBOARD_MS = 250;
44924
44924
  * controller (not exposed as a property) and invoked when the popup actually
44925
44925
  * closes, however that happens.
44926
44926
  *
44927
+ * `controller.getElement` is assigned alongside it and answers with the
44928
+ * element that effect acts on, so a mount-time open can tell whether there is
44929
+ * anything in the document to open yet (see useOpenPropsEffectOnOpenController).
44930
+ *
44927
44931
  * Dialog/Popover also call `openController.requestClose(e, { isCancel })` for
44928
44932
  * their own internal triggers (backdrop click, Escape).
44929
44933
  *
@@ -45192,6 +45196,10 @@ const createOpenController = (
45192
45196
  // openedDuringThisPress). Never any press before there has been one.
45193
45197
  pressCountAtOpen: null,
45194
45198
  openEffect: null,
45199
+ // The element openEffect acts on, asked for before the mount-time open is
45200
+ // let through: it has to be in the document for showModal()/showPopover()
45201
+ // to be legal at all (see useOpenPropsEffectOnOpenController).
45202
+ getElement: null,
45195
45203
  // Set by the controlled element (see popup_content_mount.js) when its
45196
45204
  // content is still waiting for a first open to be built. Called below,
45197
45205
  // before openEffect, so the popup measures and positions the real thing.
@@ -45454,32 +45462,27 @@ const useOpenController = (openHandler) => {
45454
45462
  // relative to each other, but there's no meaningful "correct" order between
45455
45463
  // those anyway.
45456
45464
  //
45457
- // The microtask is also why the scheduling is undone by the effect that asked
45458
- // for it: a `<Loading>` above the popup can park the whole subtree between the
45459
- // two — preact/compat's Suspense runs every hook cleanup of the suspended
45460
- // children and moves their dom into a detached <div> — and the flush would
45461
- // then be opening a popup whose element has left the document.
45462
- let pendingMountOpens = [];
45465
+ // The cancel this returns answers whether the open was still pending: a
45466
+ // `<Loading>` above the popup can park the whole subtree between the effect
45467
+ // and the flush (preact/compat's Suspense runs every hook cleanup of the
45468
+ // suspended children and moves their dom into a detached <div>), and the flush
45469
+ // would then be opening a popup whose element has left the document.
45470
+ let pendingMountOpens = new Set();
45463
45471
  let mountOpenFlushScheduled = false;
45464
45472
  const scheduleMountOpen = (run) => {
45465
- pendingMountOpens.push(run);
45473
+ pendingMountOpens.add(run);
45466
45474
  if (!mountOpenFlushScheduled) {
45467
45475
  mountOpenFlushScheduled = true;
45468
45476
  queueMicrotask(() => {
45469
- const entries = pendingMountOpens;
45470
- pendingMountOpens = [];
45477
+ const entries = [...pendingMountOpens];
45478
+ pendingMountOpens = new Set();
45471
45479
  mountOpenFlushScheduled = false;
45472
45480
  for (let i = entries.length - 1; i >= 0; i--) {
45473
45481
  entries[i]();
45474
45482
  }
45475
45483
  });
45476
45484
  }
45477
- return () => {
45478
- const index = pendingMountOpens.indexOf(run);
45479
- if (index > -1) {
45480
- pendingMountOpens.splice(index, 1);
45481
- }
45482
- };
45485
+ return () => pendingMountOpens.delete(run);
45483
45486
  };
45484
45487
 
45485
45488
  // Where the popup's open state is kept, when it is kept anywhere: `navState`
@@ -45657,6 +45660,9 @@ const useOpenPropsEffectOnOpenController = (
45657
45660
  // subsequent `open` change is a real, later toggle and should animate
45658
45661
  // normally like any other interactive open/close.
45659
45662
  const isFirstRunRef = useRef(true);
45663
+ // The mount-time open, from the first run below until the effect after it
45664
+ // could schedule it.
45665
+ const mountOpenOwedRef = useRef(null);
45660
45666
 
45661
45667
  useLayoutEffect(() => {
45662
45668
  const isFirstRun = isFirstRunRef.current;
@@ -45673,15 +45679,10 @@ const useOpenPropsEffectOnOpenController = (
45673
45679
  // shown as "closed" for the user to see it transition away from, so the
45674
45680
  // entrance is skipped (`silent`, see popover.jsx's own openEffect).
45675
45681
  //
45676
- // Deferred + batched (see scheduleMountOpen above) rather than called
45677
- // directly, so nested popups that both mount already-open end up
45678
- // stacked ancestor-first instead of Preact's own child-first effect
45679
- // order.
45680
- return scheduleMountOpen(() =>
45682
+ mountOpenOwedRef.current = () =>
45681
45683
  openController.open(new CustomEvent("open_by_prop", { detail: {} }), {
45682
45684
  silent: mountOpenReason !== "interaction",
45683
- }),
45684
- );
45685
+ });
45685
45686
  }
45686
45687
  return undefined;
45687
45688
  }
@@ -45716,6 +45717,38 @@ const useOpenPropsEffectOnOpenController = (
45716
45717
  }
45717
45718
  return undefined;
45718
45719
  }, [open]);
45720
+
45721
+ // Schedules the owed mount-time open — on every render, until it can. It has
45722
+ // to wait for the element to be IN THE DOCUMENT, and a mount does not
45723
+ // guarantee that: a `<Loading>` above the popup parks a suspended subtree by
45724
+ // moving its dom into a detached <div> while keeping its components alive
45725
+ // (preact/compat), and a render there re-creates the hooks, so the first run
45726
+ // above happens against dom that is not in the page — where showModal() and
45727
+ // showPopover() throw. The boundary settling re-renders the subtree with its
45728
+ // dom back, and that render is the one that schedules.
45729
+ //
45730
+ // Deferred + batched (see scheduleMountOpen) rather than called directly,
45731
+ // so nested popups that both mount already-open end up stacked
45732
+ // ancestor-first instead of Preact's own child-first effect order. An open
45733
+ // still pending when this cleans up goes back to being owed: the parking
45734
+ // itself runs this cleanup, and the fresh run that follows re-asks.
45735
+ useLayoutEffect(() => {
45736
+ const mountOpen = mountOpenOwedRef.current;
45737
+ if (!mountOpen) {
45738
+ return undefined;
45739
+ }
45740
+ const element = openController.getElement?.();
45741
+ if (element && !element.isConnected) {
45742
+ return undefined;
45743
+ }
45744
+ mountOpenOwedRef.current = null;
45745
+ const cancelMountOpen = scheduleMountOpen(mountOpen);
45746
+ return () => {
45747
+ if (cancelMountOpen()) {
45748
+ mountOpenOwedRef.current = mountOpen;
45749
+ }
45750
+ };
45751
+ });
45719
45752
  };
45720
45753
 
45721
45754
  const useOpenControllerByProps = (props, name) => {
@@ -46807,6 +46840,7 @@ const Expandable = props => {
46807
46840
  // What opening LOOKS like here, and how to undo it — the one thing an
46808
46841
  // expandable owns that a popup does not (see open_controller.js). Reassigned
46809
46842
  // on every render so it always closes over the latest props.
46843
+ openController.getElement = () => rootRef.current;
46810
46844
  openController.openEffect = openEvent => {
46811
46845
  const contentContainer = contentContainerRef.current;
46812
46846
  // `silent`: the expandable was already open when the page appeared
@@ -57161,8 +57195,11 @@ const getAvailableWidth = (layer, element) => {
57161
57195
  * (a top-docked SidePanel: title and tabs at its top). So here the cut alone
57162
57196
  * does the reveal — the same curtain descending from the container's top
57163
57197
  * edge a full slide draws — while the box travels only `--popup-cover-travel`
57164
- * (default 15%) of its own height: the head is on screen from the first
57165
- * frame, settling its last few pixels into place. The exit is the same
57198
+ * (default 24px): the head is on screen from the first frame, settling its
57199
+ * last few pixels into place. A length, not a share of the panel: the travel
57200
+ * is a settle, the curtain is what reveals, and a share would grow with a
57201
+ * full-screen panel while its head does not — at 15% of 800px the head spent
57202
+ * the first 40% of the entrance above the cut. The exit is the same
57166
57203
  * movement backwards and still reads as a slide, the head leading. One
57167
57204
  * direction only: a bottom sheet leads with its head by itself, and a
57168
57205
  * left/right panel keeps its head on the axis it does not travel.
@@ -57192,7 +57229,7 @@ const popupCss = /* css */ `
57192
57229
  .navi_dialog {
57193
57230
  --popup-animation-duration: 0.18s;
57194
57231
  --popup-scale-from: 0.9;
57195
- --popup-cover-travel: 15%;
57232
+ --popup-cover-travel: 24px;
57196
57233
 
57197
57234
  --popup-opacity-duration: var(--popup-animation-duration);
57198
57235
  --popup-translate-duration: var(--popup-animation-duration);
@@ -57373,7 +57410,7 @@ const popupCss = /* css */ `
57373
57410
 
57374
57411
  /* cover — a top-docked popup unrolling from the top edge of its area (see
57375
57412
  this file's top comment): the cut does the reveal, the box travels
57376
- --popup-cover-travel of its own height. The far cut sits where the
57413
+ --popup-cover-travel, a length. The far cut sits where the
57377
57414
  popup's shadow ends (--navi-popup-box-shadow reaches about 50px) or
57378
57415
  where the room does, whichever is nearer, so the reveal finishes on
57379
57416
  the box and its shadow at the pace of the box's own settle. On the
@@ -59076,6 +59113,7 @@ const useDialogProps = props => {
59076
59113
  // *when* this runs. openEffect runs outside of render (triggered by
59077
59114
  // openController.open()), so it cannot call hooks — cleanup is a plain
59078
59115
  // pub/sub.
59116
+ openController.getElement = () => ref.current;
59079
59117
  openController.openEffect = e => {
59080
59118
  const dialogEl = ref.current;
59081
59119
  const backdropEl = backdropRef.current;
@@ -60539,6 +60577,7 @@ const usePopoverProps = props => {
60539
60577
  // slower, flash-prone `toggle` event instead of the fast, pre-paint
60540
60578
  // MutationObserver path.
60541
60579
 
60580
+ openController.getElement = () => ref.current;
60542
60581
  openController.openEffect = e => {
60543
60582
  const popoverEl = ref.current;
60544
60583
  // backdropEl is null when pointerInteractionOutsideEffect is "none" —
@@ -62467,6 +62506,7 @@ const PickerCalloutPopup = ({
62467
62506
  }
62468
62507
  // Reassigned on every render, like Popover's own, so it closes over the
62469
62508
  // latest props.
62509
+ openController.getElement = () => pickerRef.current;
62470
62510
  openController.openEffect = openEvent => {
62471
62511
  const pickerEl = pickerRef.current;
62472
62512
  const calloutManager = getPickerInput(pickerEl).__uiStateController__.rules.callout;
@@ -83131,6 +83171,7 @@ const ANIMATION_BY_SIDE = {
83131
83171
  };
83132
83172
  const css = /* css */`.navi_side_panel {
83133
83173
  --popup-border-radius: 0px;
83174
+ --popup-animation-duration: .3s;
83134
83175
  width: var(--navi-side-panel-width, auto);
83135
83176
  height: var(--navi-side-panel-height, auto);
83136
83177
 
@@ -83295,10 +83336,12 @@ const css = /* css */`.navi_side_panel {
83295
83336
  * @param {boolean|"fading"} [props.animation] - Off by default (unlike
83296
83337
  * `Dialog`/`Popover` themselves) — SidePanel is commonly toggled instead
83297
83338
  * of opened/closed as a one-off, where a slide transition is more often
83298
- * undesired noise than not. `true` slides in from `side` (a top panel
83299
- * unrolls from its edge instead, head first — see `ANIMATION_BY_SIDE`);
83300
- * `"fading"` is the other common choice. Other values are forwarded as-is but not a
83301
- * documented/encouraged part of this component's own API.
83339
+ * undesired noise than not. `true` plays the entrance of `side`:
83340
+ * `cover-from-top` for a top panel (it unrolls from its edge, head first),
83341
+ * `slide-from-<side>` for the three others — see `ANIMATION_BY_SIDE` for
83342
+ * why the top differs. `"fading"` is the other common choice. Other values
83343
+ * are forwarded as-is but not a documented/encouraged part of this
83344
+ * component's own API.
83302
83345
  * @param {boolean} [props.closeOnClickOutside=false] - `false` (default):
83303
83346
  * maps to `pointerInteractionOutsideEffect="none"` — in popover mode, no
83304
83347
  * backdrop at all, outside clicks pass straight through; in dialog mode,