@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.
@@ -44357,6 +44357,10 @@ const FOCUS_DELAY_ON_KEYBOARD_MS = 250;
44357
44357
  * controller (not exposed as a property) and invoked when the popup actually
44358
44358
  * closes, however that happens.
44359
44359
  *
44360
+ * `controller.getElement` is assigned alongside it and answers with the
44361
+ * element that effect acts on, so a mount-time open can tell whether there is
44362
+ * anything in the document to open yet (see useOpenPropsEffectOnOpenController).
44363
+ *
44360
44364
  * Dialog/Popover also call `openController.requestClose(e, { isCancel })` for
44361
44365
  * their own internal triggers (backdrop click, Escape).
44362
44366
  *
@@ -44625,6 +44629,10 @@ const createOpenController = (
44625
44629
  // openedDuringThisPress). Never any press before there has been one.
44626
44630
  pressCountAtOpen: null,
44627
44631
  openEffect: null,
44632
+ // The element openEffect acts on, asked for before the mount-time open is
44633
+ // let through: it has to be in the document for showModal()/showPopover()
44634
+ // to be legal at all (see useOpenPropsEffectOnOpenController).
44635
+ getElement: null,
44628
44636
  // Set by the controlled element (see popup_content_mount.js) when its
44629
44637
  // content is still waiting for a first open to be built. Called below,
44630
44638
  // before openEffect, so the popup measures and positions the real thing.
@@ -44887,32 +44895,27 @@ const useOpenController = (openHandler) => {
44887
44895
  // relative to each other, but there's no meaningful "correct" order between
44888
44896
  // those anyway.
44889
44897
  //
44890
- // The microtask is also why the scheduling is undone by the effect that asked
44891
- // for it: a `<Loading>` above the popup can park the whole subtree between the
44892
- // two — preact/compat's Suspense runs every hook cleanup of the suspended
44893
- // children and moves their dom into a detached <div> — and the flush would
44894
- // then be opening a popup whose element has left the document.
44895
- let pendingMountOpens = [];
44898
+ // The cancel this returns answers whether the open was still pending: a
44899
+ // `<Loading>` above the popup can park the whole subtree between the effect
44900
+ // and the flush (preact/compat's Suspense runs every hook cleanup of the
44901
+ // suspended children and moves their dom into a detached <div>), and the flush
44902
+ // would then be opening a popup whose element has left the document.
44903
+ let pendingMountOpens = new Set();
44896
44904
  let mountOpenFlushScheduled = false;
44897
44905
  const scheduleMountOpen = (run) => {
44898
- pendingMountOpens.push(run);
44906
+ pendingMountOpens.add(run);
44899
44907
  if (!mountOpenFlushScheduled) {
44900
44908
  mountOpenFlushScheduled = true;
44901
44909
  queueMicrotask(() => {
44902
- const entries = pendingMountOpens;
44903
- pendingMountOpens = [];
44910
+ const entries = [...pendingMountOpens];
44911
+ pendingMountOpens = new Set();
44904
44912
  mountOpenFlushScheduled = false;
44905
44913
  for (let i = entries.length - 1; i >= 0; i--) {
44906
44914
  entries[i]();
44907
44915
  }
44908
44916
  });
44909
44917
  }
44910
- return () => {
44911
- const index = pendingMountOpens.indexOf(run);
44912
- if (index > -1) {
44913
- pendingMountOpens.splice(index, 1);
44914
- }
44915
- };
44918
+ return () => pendingMountOpens.delete(run);
44916
44919
  };
44917
44920
 
44918
44921
  // Where the popup's open state is kept, when it is kept anywhere: `navState`
@@ -45073,6 +45076,9 @@ const useOpenPropsEffectOnOpenController = (
45073
45076
  // subsequent `open` change is a real, later toggle and should animate
45074
45077
  // normally like any other interactive open/close.
45075
45078
  const isFirstRunRef = useRef(true);
45079
+ // The mount-time open, from the first run below until the effect after it
45080
+ // could schedule it.
45081
+ const mountOpenOwedRef = useRef(null);
45076
45082
 
45077
45083
  useLayoutEffect(() => {
45078
45084
  const isFirstRun = isFirstRunRef.current;
@@ -45089,15 +45095,10 @@ const useOpenPropsEffectOnOpenController = (
45089
45095
  // shown as "closed" for the user to see it transition away from, so the
45090
45096
  // entrance is skipped (`silent`, see popover.jsx's own openEffect).
45091
45097
  //
45092
- // Deferred + batched (see scheduleMountOpen above) rather than called
45093
- // directly, so nested popups that both mount already-open end up
45094
- // stacked ancestor-first instead of Preact's own child-first effect
45095
- // order.
45096
- return scheduleMountOpen(() =>
45098
+ mountOpenOwedRef.current = () =>
45097
45099
  openController.open(new CustomEvent("open_by_prop", { detail: {} }), {
45098
45100
  silent: mountOpenReason !== "interaction",
45099
- }),
45100
- );
45101
+ });
45101
45102
  }
45102
45103
  return undefined;
45103
45104
  }
@@ -45132,6 +45133,38 @@ const useOpenPropsEffectOnOpenController = (
45132
45133
  }
45133
45134
  return undefined;
45134
45135
  }, [open]);
45136
+
45137
+ // Schedules the owed mount-time open — on every render, until it can. It has
45138
+ // to wait for the element to be IN THE DOCUMENT, and a mount does not
45139
+ // guarantee that: a `<Loading>` above the popup parks a suspended subtree by
45140
+ // moving its dom into a detached <div> while keeping its components alive
45141
+ // (preact/compat), and a render there re-creates the hooks, so the first run
45142
+ // above happens against dom that is not in the page — where showModal() and
45143
+ // showPopover() throw. The boundary settling re-renders the subtree with its
45144
+ // dom back, and that render is the one that schedules.
45145
+ //
45146
+ // Deferred + batched (see scheduleMountOpen) rather than called directly,
45147
+ // so nested popups that both mount already-open end up stacked
45148
+ // ancestor-first instead of Preact's own child-first effect order. An open
45149
+ // still pending when this cleans up goes back to being owed: the parking
45150
+ // itself runs this cleanup, and the fresh run that follows re-asks.
45151
+ useLayoutEffect(() => {
45152
+ const mountOpen = mountOpenOwedRef.current;
45153
+ if (!mountOpen) {
45154
+ return undefined;
45155
+ }
45156
+ const element = openController.getElement?.();
45157
+ if (element && !element.isConnected) {
45158
+ return undefined;
45159
+ }
45160
+ mountOpenOwedRef.current = null;
45161
+ const cancelMountOpen = scheduleMountOpen(mountOpen);
45162
+ return () => {
45163
+ if (cancelMountOpen()) {
45164
+ mountOpenOwedRef.current = mountOpen;
45165
+ }
45166
+ };
45167
+ });
45135
45168
  };
45136
45169
 
45137
45170
  const useOpenControllerByProps = (props, name) => {
@@ -46185,6 +46218,7 @@ const Expandable = props => {
46185
46218
  // What opening LOOKS like here, and how to undo it — the one thing an
46186
46219
  // expandable owns that a popup does not (see open_controller.js). Reassigned
46187
46220
  // on every render so it always closes over the latest props.
46221
+ openController.getElement = () => rootRef.current;
46188
46222
  openController.openEffect = openEvent => {
46189
46223
  const contentContainer = contentContainerRef.current;
46190
46224
  // `silent`: the expandable was already open when the page appeared
@@ -56483,8 +56517,11 @@ const getAvailableWidth = (layer, element) => {
56483
56517
  * (a top-docked SidePanel: title and tabs at its top). So here the cut alone
56484
56518
  * does the reveal — the same curtain descending from the container's top
56485
56519
  * edge a full slide draws — while the box travels only `--popup-cover-travel`
56486
- * (default 15%) of its own height: the head is on screen from the first
56487
- * frame, settling its last few pixels into place. The exit is the same
56520
+ * (default 24px): the head is on screen from the first frame, settling its
56521
+ * last few pixels into place. A length, not a share of the panel: the travel
56522
+ * is a settle, the curtain is what reveals, and a share would grow with a
56523
+ * full-screen panel while its head does not — at 15% of 800px the head spent
56524
+ * the first 40% of the entrance above the cut. The exit is the same
56488
56525
  * movement backwards and still reads as a slide, the head leading. One
56489
56526
  * direction only: a bottom sheet leads with its head by itself, and a
56490
56527
  * left/right panel keeps its head on the axis it does not travel.
@@ -56514,7 +56551,7 @@ const popupCss = /* css */ `
56514
56551
  .navi_dialog {
56515
56552
  --popup-animation-duration: 0.18s;
56516
56553
  --popup-scale-from: 0.9;
56517
- --popup-cover-travel: 15%;
56554
+ --popup-cover-travel: 24px;
56518
56555
 
56519
56556
  --popup-opacity-duration: var(--popup-animation-duration);
56520
56557
  --popup-translate-duration: var(--popup-animation-duration);
@@ -56695,7 +56732,7 @@ const popupCss = /* css */ `
56695
56732
 
56696
56733
  /* cover — a top-docked popup unrolling from the top edge of its area (see
56697
56734
  this file's top comment): the cut does the reveal, the box travels
56698
- --popup-cover-travel of its own height. The far cut sits where the
56735
+ --popup-cover-travel, a length. The far cut sits where the
56699
56736
  popup's shadow ends (--navi-popup-box-shadow reaches about 50px) or
56700
56737
  where the room does, whichever is nearer, so the reveal finishes on
56701
56738
  the box and its shadow at the pace of the box's own settle. On the
@@ -58395,6 +58432,7 @@ const useDialogProps = props => {
58395
58432
  // *when* this runs. openEffect runs outside of render (triggered by
58396
58433
  // openController.open()), so it cannot call hooks — cleanup is a plain
58397
58434
  // pub/sub.
58435
+ openController.getElement = () => ref.current;
58398
58436
  openController.openEffect = e => {
58399
58437
  const dialogEl = ref.current;
58400
58438
  const backdropEl = backdropRef.current;
@@ -59852,6 +59890,7 @@ const usePopoverProps = props => {
59852
59890
  // slower, flash-prone `toggle` event instead of the fast, pre-paint
59853
59891
  // MutationObserver path.
59854
59892
 
59893
+ openController.getElement = () => ref.current;
59855
59894
  openController.openEffect = e => {
59856
59895
  const popoverEl = ref.current;
59857
59896
  // backdropEl is null when pointerInteractionOutsideEffect is "none" —
@@ -61774,6 +61813,7 @@ const PickerCalloutPopup = ({
61774
61813
  }
61775
61814
  // Reassigned on every render, like Popover's own, so it closes over the
61776
61815
  // latest props.
61816
+ openController.getElement = () => pickerRef.current;
61777
61817
  openController.openEffect = openEvent => {
61778
61818
  const pickerEl = pickerRef.current;
61779
61819
  const calloutManager = getPickerInput(pickerEl).__uiStateController__.rules.callout;
@@ -81913,6 +81953,7 @@ const ANIMATION_BY_SIDE = {
81913
81953
  };
81914
81954
  const css = /* css */`.navi_side_panel {
81915
81955
  --popup-border-radius: 0px;
81956
+ --popup-animation-duration: .3s;
81916
81957
  width: var(--navi-side-panel-width, auto);
81917
81958
  height: var(--navi-side-panel-height, auto);
81918
81959
 
@@ -82077,10 +82118,12 @@ const css = /* css */`.navi_side_panel {
82077
82118
  * @param {boolean|"fading"} [props.animation] - Off by default (unlike
82078
82119
  * `Dialog`/`Popover` themselves) — SidePanel is commonly toggled instead
82079
82120
  * of opened/closed as a one-off, where a slide transition is more often
82080
- * undesired noise than not. `true` slides in from `side` (a top panel
82081
- * unrolls from its edge instead, head first — see `ANIMATION_BY_SIDE`);
82082
- * `"fading"` is the other common choice. Other values are forwarded as-is but not a
82083
- * documented/encouraged part of this component's own API.
82121
+ * undesired noise than not. `true` plays the entrance of `side`:
82122
+ * `cover-from-top` for a top panel (it unrolls from its edge, head first),
82123
+ * `slide-from-<side>` for the three others — see `ANIMATION_BY_SIDE` for
82124
+ * why the top differs. `"fading"` is the other common choice. Other values
82125
+ * are forwarded as-is but not a documented/encouraged part of this
82126
+ * component's own API.
82084
82127
  * @param {boolean} [props.closeOnClickOutside=false] - `false` (default):
82085
82128
  * maps to `pointerInteractionOutsideEffect="none"` — in popover mode, no
82086
82129
  * backdrop at all, outside clicks pass straight through; in dialog mode,