@jsenv/navi 0.29.323 → 0.29.324

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.
package/README.md CHANGED
@@ -29,6 +29,8 @@ between related actions. Parent/child relations are first-class — `.one`,
29
29
 
30
30
  **`Box`** is the main layout primitive. It wraps CSS Flexbox with a friendlier API: `flex` for horizontal layout, `flex="y"` for vertical (no more guessing what `flex-direction: column` does visually). Supports `grid`, `inline`, alignment via `alignX`/`alignY`, and spacing props.
31
31
 
32
+ **`createSlot`** renders content declared in one place at another: a toolbar, a status, or one `SidePanel` for a whole board that stays open as long as something fills it (`open={isFilled}`, see [docs/popup_open.md](./docs/popup_open.md)).
33
+
32
34
  **`Text`** and related components (`Title`, `Paragraph`, `Code`, `Caption`) handle typography consistently across the app — including the parts of a line that are not text: icons, counts, truncation (`maxLines`), skeletons, and where a line may break. See [docs/typography.md](./docs/typography.md).
33
35
 
34
36
  ## Texts & i18n
@@ -7,7 +7,7 @@ export { disableVirtualKeyboardOverlay } from "./jsenv_navi_side_effects.js";
7
7
  import { elementIsFocusable, createIterableWeakSet, dispatchInternalCustomEvent, dispatchCustomEvent, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, getElementSignature, createPubSub, findEvent, createValueEffect, findFocusDelegateTarget, findFocusable, scrollIntoViewThroughScrollables, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, keepTouchRefusable, isPressDrivenClick, waitForTap, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, dragSourceThatStoodDown, markDragSource, refuseDragTo, startDragTo, installPanZoom, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, normalizeStyles, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, scrollRoomTowards, getScrollContainer, isTouchDrivenEvent, scrollIntoViewScoped, closestOpenableAncestor, isAncestorOpen, isDisplayedDespiteClosedAncestor, observeAncestorOpenState, getAncestorOpenType, findBefore, findAfter, resolveCSSSize, hasCSSSizeUnit, releaseWheelGesture, getScrollIntoViewScopedOffsets, wheelGestureIsTakenFrom, claimWheelGesture, initFocusGroup, stringifyStyle as stringifyStyle$1, resolveOklchLightness, contrastColor, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, getVirtualKeyboardOverlayHeight, onAncestorReopen, isPressDisputedByDrag, canScroll, measureWidestChildRow, performTabNavigation, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement } from "@jsenv/dom";
8
8
  export { chainEvent, clickIsSuppressed, contrastColor, createDragGestureController, dragAfterIntent, findEvent, markDragSource, startDragTo } from "@jsenv/dom";
9
9
  import { signal, computed, effect, untracked, batch, useComputed, useSignal } from "@preact/signals";
10
- import { isValidElement, createContext, render, h, toChildArray, options, cloneElement, Fragment as Fragment$1 } from "preact";
10
+ import { isValidElement, createContext, render, h, toChildArray, options, cloneElement, createElement, Fragment as Fragment$1 } from "preact";
11
11
  import { useErrorBoundary, useLayoutEffect, useContext, useCallback, useRef, useState, useEffect, useMemo, useId } from "preact/hooks";
12
12
  import { humanizeI18n, prefixFirstAndIndentRemainingLines, setRuntimeLangSource, formatDuration, formatMonth, formatDay, resolveTimeRangePrecision, formatDatePlaceholder, toDate, getRelativeDay, formatDayRelative, formatMonthPlaceholder, formatWeekPlaceholder, formatDatetimePlaceholder, formatDatetime, toTimeOfDay, formatTimeOfDay, formatTime, formatMinuteDuration, formatSecondDuration, formatHourDuration, formatTimeRelative, formatNumber, interpolateText, installInterpolateJsx } from "@jsenv/humanize";
13
13
  export { createI18n, formatDatetime, formatDay, formatDayRelative, formatDuration, formatHourDuration, formatMinuteDuration, formatMonth, formatNumber, formatSecondDuration, formatTime, formatTimeOfDay, formatTimeRange, formatTimeRelative, interpolateText } from "@jsenv/humanize";
@@ -32476,8 +32476,26 @@ const resolveExplicitTarget = (element) => {
32476
32476
  }
32477
32477
  return undefined;
32478
32478
  };
32479
+ // The control around the source that a command can be about: one holding a
32480
+ // value of its own. A popup is a control root too (it answers interactions,
32481
+ // carries a callout), but it has no host of its own — asked for one, it
32482
+ // answers with the first control INSIDE it, which for a field sending
32483
+ // --navi-update from a picker's popup is that very field: the command then
32484
+ // sets the field to what it already holds, which fires the command again,
32485
+ // without end. Such a surface is walked past, up to the picker or the form.
32479
32486
  const resolveFirstParentControl = (el) => {
32480
- return getParentControl(el);
32487
+ let parentControl = getParentControl(el);
32488
+ while (parentControl && !ownsControlHost(parentControl)) {
32489
+ parentControl = getParentControl(parentControl);
32490
+ }
32491
+ return parentControl;
32492
+ };
32493
+ const ownsControlHost = (controlRoot) => {
32494
+ const controlHost = findControlHost(controlRoot);
32495
+ return (
32496
+ Boolean(controlHost) &&
32497
+ controlHost.closest("[navi-control]") === controlRoot
32498
+ );
32481
32499
  };
32482
32500
  const resolveFirstChildControl = (el) => {
32483
32501
  let startEl;
@@ -35278,6 +35296,21 @@ const warnPopupHasNoElementToOpen = (popupKind) => {
35278
35296
  */
35279
35297
  const OUTSIDE_REGION_ATTRIBUTE = "data-navi-popup-outside";
35280
35298
 
35299
+ /**
35300
+ * `data-navi-popup-inside` is the other direction: a box of the PAGE the
35301
+ * caller says belongs to a popup, so a press on it is not outside that popup
35302
+ * even though it lies beyond the border box — a card whose press is what
35303
+ * fills the one panel of a board. Its value names the popup by `id`, the way
35304
+ * `commandfor` does (several ids, space separated), because the exemption is
35305
+ * one popup's: a menu open beside the board still closes on the same press.
35306
+ *
35307
+ * Unlike the outside marker it answers for its whole subtree: the outside
35308
+ * marker carves free space out of a surface whose controls stay surface, this
35309
+ * one adds a whole thing — the card, its count, its buttons — to the popup's
35310
+ * own ground, and nothing in it is a dismissal.
35311
+ */
35312
+ const INSIDE_ATTRIBUTE = "data-navi-popup-inside";
35313
+
35281
35314
  /**
35282
35315
  * What a press landing on a region the caller declared as not-its-surface
35283
35316
  * does: exactly what the same press on the backdrop would do.
@@ -35424,6 +35457,14 @@ const armOutsidePressClose = (
35424
35457
  ) {
35425
35458
  return;
35426
35459
  }
35460
+ if (
35461
+ popupEl.id &&
35462
+ pointerDownEvent.target.closest?.(
35463
+ `[${INSIDE_ATTRIBUTE}~="${CSS.escape(popupEl.id)}"]`,
35464
+ )
35465
+ ) {
35466
+ return;
35467
+ }
35427
35468
  // Real DOM containment wins over the rectangle below — an element
35428
35469
  // genuinely inside the popup (`overflow: visible`, a negative margin, an
35429
35470
  // absolutely-positioned child) can be painted outside its border box, and
@@ -48844,33 +48885,6 @@ const createToggleEvent = open => {
48844
48885
  return toggleEvent;
48845
48886
  };
48846
48887
 
48847
- const NextResolverContext = createContext(null);
48848
- const useNextResolver = () => useContext(NextResolverContext);
48849
-
48850
- /**
48851
- * Creates a renderComponent function that passes props through a chain of resolvers.
48852
- * Each resolver is a Preact component rendered in sequence (hooks are allowed).
48853
- * To pass through to the next resolver, call useNextResolver() and render the
48854
- * returned Next component with the desired props.
48855
- * To terminate the chain early (e.g. render a specialized component), render
48856
- * directly without calling Next.
48857
- *
48858
- * The last entry in the array is the final/target component — it receives null
48859
- * from useNextResolver() indicating it is terminal.
48860
- *
48861
- * Usage:
48862
- * const renderButton = createComponentResolver([ResolverA, ResolverB, ButtonTarget]);
48863
- * // Then inside a component render:
48864
- * renderButton(props)
48865
- *
48866
- * Each position of the chain has a runner of its own, defined once: it renders
48867
- * its resolver under a NextResolverContext holding the runner after it. A
48868
- * resolver that re-renders on its own and renders <Next> therefore resumes at
48869
- * its own position without any bookkeeping — the Next it was given is the one
48870
- * made for it. A chain rendered by the hundred (a list's rows) pays two
48871
- * components per position, the runner and the resolver, so nothing here is a
48872
- * component that merely forwards.
48873
- */
48874
48888
  const createComponentResolver = (resolvers, {
48875
48889
  pure
48876
48890
  } = {}) => {
@@ -48879,13 +48893,10 @@ const createComponentResolver = (resolvers, {
48879
48893
  for (let index = 0; index < resolvers.length; index++) {
48880
48894
  const Resolver = resolvers[index];
48881
48895
  const isLast = index === lastIndex;
48882
- const Runner = props => jsx(NextResolverContext.Provider, {
48883
- value: isLast ? null : runners[index + 1],
48884
- children: jsx(Resolver, {
48885
- ...props
48886
- })
48887
- });
48888
- Runner.displayName = `${Resolver.displayName || Resolver.name}Runner`;
48896
+ function Runner(props) {
48897
+ return runResolverBody(this, Resolver, props, isLast ? null : runners[index + 1]);
48898
+ }
48899
+ Runner.displayName = Resolver.displayName || Resolver.name;
48889
48900
  runners.push(Runner);
48890
48901
  }
48891
48902
  const FirstRunner = runners[0];
@@ -48916,6 +48927,54 @@ const createComponentResolver = (resolvers, {
48916
48927
  }
48917
48928
  return PureRenderComponent;
48918
48929
  };
48930
+ const useNextResolver = () => {
48931
+ if (currentNext === OUTSIDE_RESOLVER_BODY) {
48932
+ throw new Error("useNextResolver() called outside a resolver body: a resolver rendered by another resolver is rendered with renderResolver(Resolver, props), not as a JSX child.");
48933
+ }
48934
+ return currentNext;
48935
+ };
48936
+ const renderResolver = (Resolver, props) => {
48937
+ if (currentNext === OUTSIDE_RESOLVER_BODY) {
48938
+ throw new Error(`renderResolver(${Resolver.displayName || Resolver.name}) called outside a resolver body: only a resolver can render another one.`);
48939
+ }
48940
+ let SubRunner = subRunnerByResolver.get(Resolver);
48941
+ if (!SubRunner) {
48942
+ SubRunner = function (props) {
48943
+ const {
48944
+ [NEXT_PROP]: next,
48945
+ ...ownProps
48946
+ } = props;
48947
+ return runResolverBody(this, Resolver, ownProps, next);
48948
+ };
48949
+ SubRunner.displayName = Resolver.displayName || Resolver.name;
48950
+ subRunnerByResolver.set(Resolver, SubRunner);
48951
+ }
48952
+ return createElement(SubRunner, {
48953
+ ...props,
48954
+ [NEXT_PROP]: currentNext
48955
+ });
48956
+ };
48957
+
48958
+ // What useNextResolver() answers: the runner after the one whose body is
48959
+ // running, null for the last — and, outside any body, a value that is neither.
48960
+ const OUTSIDE_RESOLVER_BODY = Symbol("outside_resolver_body");
48961
+ let currentNext = OUTSIDE_RESOLVER_BODY;
48962
+ const runResolverBody = (instance, Resolver, props, next) => {
48963
+ const nextBefore = currentNext;
48964
+ currentNext = next;
48965
+ try {
48966
+ return Resolver.call(instance, props);
48967
+ } finally {
48968
+ currentNext = nextBefore;
48969
+ }
48970
+ };
48971
+
48972
+ // One runner per resolver rendered through renderResolver, made on first use:
48973
+ // the same component type on every render, so a resolver picked again is
48974
+ // updated in place and another one picked is a remount, like any child.
48975
+ const subRunnerByResolver = new WeakMap();
48976
+ // Rides on the props of a sub-runner, stripped before the body sees them.
48977
+ const NEXT_PROP = "navi-resolver-next";
48919
48978
  function pureShouldComponentUpdate(nextProps) {
48920
48979
  return shallowDiffers(this.props, nextProps);
48921
48980
  }
@@ -49741,9 +49800,7 @@ const ButtonConfirm = ({
49741
49800
  const ButtonRouteResolver = props => {
49742
49801
  const Next = useNextResolver();
49743
49802
  if (props.route) {
49744
- return jsx(ButtonWithRoute, {
49745
- ...props
49746
- });
49803
+ return renderResolver(ButtonWithRoute, props);
49747
49804
  }
49748
49805
  return jsx(Next, {
49749
49806
  ...props
@@ -52347,9 +52404,7 @@ const installInputCss = () => {
52347
52404
  const InputModeResolver = props => {
52348
52405
  const Next = useNextResolver();
52349
52406
  if (props.inputMode === "numeric" || props.inputMode === "decimal") {
52350
- return jsx(InputModeNumericOrDecimal, {
52351
- ...props
52352
- });
52407
+ return renderResolver(InputModeNumericOrDecimal, props);
52353
52408
  }
52354
52409
  return jsx(Next, {
52355
52410
  ...props
@@ -52577,39 +52632,25 @@ const InputTypeResolver = props => {
52577
52632
  // very same "icon while empty, clear button once filled" affordance without
52578
52633
  // pretending to be a search box.
52579
52634
  if (props.clearable && props.type !== "search") {
52580
- return jsx(InputClearable, {
52581
- ...props
52582
- });
52635
+ return renderResolver(InputClearable, props);
52583
52636
  }
52584
52637
  if (props.type === "search") {
52585
- return jsx(InputSearch, {
52586
- ...props
52587
- });
52638
+ return renderResolver(InputSearch, props);
52588
52639
  }
52589
52640
  if (props.type === "email") {
52590
- return jsx(InputEmail, {
52591
- ...props
52592
- });
52641
+ return renderResolver(InputEmail, props);
52593
52642
  }
52594
52643
  if (props.type === "tel") {
52595
- return jsx(InputTel, {
52596
- ...props
52597
- });
52644
+ return renderResolver(InputTel, props);
52598
52645
  }
52599
52646
  if (props.type === "number") {
52600
- return jsx(InputNumber, {
52601
- ...props
52602
- });
52647
+ return renderResolver(InputNumber, props);
52603
52648
  }
52604
52649
  if (props.type === "color") {
52605
- return jsx(InputColor, {
52606
- ...props
52607
- });
52650
+ return renderResolver(InputColor, props);
52608
52651
  }
52609
52652
  if (props.type === "datetime-local") {
52610
- return jsx(InputDatetimeLocal, {
52611
- ...props
52612
- });
52653
+ return renderResolver(InputDatetimeLocal, props);
52613
52654
  }
52614
52655
  return jsx(Next, {
52615
52656
  ...props
@@ -52722,9 +52763,7 @@ const InputDatetimeLocal = props => {
52722
52763
  const InputWithListResolver = props => {
52723
52764
  const Next = useNextResolver();
52724
52765
  if (props["navi-list"]) {
52725
- return jsx(InputWithList, {
52726
- ...props
52727
- });
52766
+ return renderResolver(InputWithList, props);
52728
52767
  }
52729
52768
  return jsx(Next, {
52730
52769
  ...props
@@ -52831,9 +52870,7 @@ const ChevronDownSvg$1 = () => {
52831
52870
  const InputWithSuggestionsResolver = props => {
52832
52871
  const Next = useNextResolver();
52833
52872
  if (props["navi-suggestions"]) {
52834
- return jsx(InputTextualWithSuggestions, {
52835
- ...props
52836
- });
52873
+ return renderResolver(InputTextualWithSuggestions, props);
52837
52874
  }
52838
52875
  return jsx(Next, {
52839
52876
  ...props
@@ -63717,9 +63754,7 @@ const css$B = /* css */`.navi_picker_callout_dock > [data-picker-content] {
63717
63754
  const PickerCustomResolver = props => {
63718
63755
  import.meta.css = [css$B, "@jsenv/navi/src/control/picker/picker_custom.jsx"];
63719
63756
  if (props.children === undefined) {
63720
- return jsx(PickerNative, {
63721
- ...props
63722
- });
63757
+ return renderResolver(PickerNative, props);
63723
63758
  }
63724
63759
  if (props.mode === "callout") {
63725
63760
  // A tooltip is an icon one presses, unless told otherwise. Own-property
@@ -63781,14 +63816,12 @@ const PickerCustomResolver = props => {
63781
63816
  // how a field says its value is a JS one, kept beside the DOM (see
63782
63817
  // controller_registry.js) — the same thing type="array"/"object" already
63783
63818
  // say for their shapes.
63784
- return jsx(PickerCustom, {
63819
+ return renderResolver(PickerCustom, {
63785
63820
  ...props,
63786
63821
  type: "navi_js"
63787
63822
  });
63788
63823
  }
63789
- return jsx(PickerCustom, {
63790
- ...props
63791
- });
63824
+ return renderResolver(PickerCustom, props);
63792
63825
  };
63793
63826
  const PickerNative = props => {
63794
63827
  const Next = useNextResolver();
@@ -64414,9 +64447,9 @@ const PickerCustom = props => {
64414
64447
  });
64415
64448
  }
64416
64449
  }
64417
- return jsx(PickerContentInsidePopup, {
64450
+ return renderResolver(PickerContentInsidePopup, {
64418
64451
  ...pickerProps,
64419
- mode: mode
64452
+ mode
64420
64453
  });
64421
64454
  };
64422
64455
 
@@ -65683,14 +65716,10 @@ const createItemTracker = (onChange) => {
65683
65716
  const ListItemHeaderOrFooterResolver = props => {
65684
65717
  const Next = useNextResolver();
65685
65718
  if (props.header) {
65686
- return jsx(ListItemHeader, {
65687
- ...props
65688
- });
65719
+ return renderResolver(ListItemHeader, props);
65689
65720
  }
65690
65721
  if (props.footer) {
65691
- return jsx(ListItemFooter, {
65692
- ...props
65693
- });
65722
+ return renderResolver(ListItemFooter, props);
65694
65723
  }
65695
65724
  return jsx(Next, {
65696
65725
  ...props
@@ -65922,9 +65951,7 @@ const ListSelectableContext = createContext(false);
65922
65951
  const ListSelectableResolver = props => {
65923
65952
  const Next = useNextResolver();
65924
65953
  if (props.selectable) {
65925
- return jsx(ListSelectable, {
65926
- ...props
65927
- });
65954
+ return renderResolver(ListSelectable, props);
65928
65955
  }
65929
65956
  return jsx(ListSelectableContext.Provider, {
65930
65957
  value: false,
@@ -66224,7 +66251,7 @@ const ListItemSelectableResolver = props => {
66224
66251
  const isHeaderOrFooter = Boolean(props.header || props.footer);
66225
66252
  const selectable = props.selectable ?? (isHeaderOrFooter || props.role === "presentation" ? false : listSelectable);
66226
66253
  if (selectable) {
66227
- return jsx(ListItemSelectable, {
66254
+ return renderResolver(ListItemSelectable, {
66228
66255
  ...props,
66229
66256
  selectable: true
66230
66257
  });
@@ -71532,14 +71559,10 @@ const generateTimeSlots = (min, max, stepSeconds) => {
71532
71559
  const PickerPresetResolver = props => {
71533
71560
  const Next = useNextResolver();
71534
71561
  if (props.type === "navi_time") {
71535
- return jsx(PickerNaviTime, {
71536
- ...props
71537
- });
71562
+ return renderResolver(PickerNaviTime, props);
71538
71563
  }
71539
71564
  if (props.type === "navi_minute") {
71540
- return jsx(PickerNaviMinute, {
71541
- ...props
71542
- });
71565
+ return renderResolver(PickerNaviMinute, props);
71543
71566
  }
71544
71567
  return jsx(Next, {
71545
71568
  ...props
@@ -72337,59 +72360,37 @@ const PencilSvg = () => {
72337
72360
  const PickerTypeResolver = props => {
72338
72361
  const Next = useNextResolver();
72339
72362
  if (props.type === "color") {
72340
- return jsx(PickerColor, {
72341
- ...props
72342
- });
72363
+ return renderResolver(PickerColor, props);
72343
72364
  }
72344
72365
  if (props.type === "datetime") {
72345
- return jsx(PickerDatetime, {
72346
- ...props
72347
- });
72366
+ return renderResolver(PickerDatetime, props);
72348
72367
  }
72349
72368
  if (props.type === "date") {
72350
- return jsx(PickerDate, {
72351
- ...props
72352
- });
72369
+ return renderResolver(PickerDate, props);
72353
72370
  }
72354
72371
  if (props.type === "month") {
72355
- return jsx(PickerMonth, {
72356
- ...props
72357
- });
72372
+ return renderResolver(PickerMonth, props);
72358
72373
  }
72359
72374
  if (props.type === "week") {
72360
- return jsx(PickerWeek, {
72361
- ...props
72362
- });
72375
+ return renderResolver(PickerWeek, props);
72363
72376
  }
72364
72377
  if (props.type === "time") {
72365
- return jsx(PickerTime, {
72366
- ...props
72367
- });
72378
+ return renderResolver(PickerTime, props);
72368
72379
  }
72369
72380
  if (props.type === "duration") {
72370
- return jsx(PickerDuration, {
72371
- ...props
72372
- });
72381
+ return renderResolver(PickerDuration, props);
72373
72382
  }
72374
72383
  if (props.type === "file") {
72375
- return jsx(PickerFile, {
72376
- ...props
72377
- });
72384
+ return renderResolver(PickerFile, props);
72378
72385
  }
72379
72386
  if (props.type === "text") {
72380
- return jsx(PickerText, {
72381
- ...props
72382
- });
72387
+ return renderResolver(PickerText, props);
72383
72388
  }
72384
72389
  if (props.type === "array") {
72385
- return jsx(PickerArray, {
72386
- ...props
72387
- });
72390
+ return renderResolver(PickerArray, props);
72388
72391
  }
72389
72392
  if (props.type === "object") {
72390
- return jsx(PickerObject, {
72391
- ...props
72392
- });
72393
+ return renderResolver(PickerObject, props);
72393
72394
  }
72394
72395
  return jsx(Next, {
72395
72396
  ...props
@@ -86097,7 +86098,9 @@ const css = /* css */`.navi_side_panel {
86097
86098
  * closes the panel on an outside click instead, and also enables trapping
86098
86099
  * Tab navigation inside the panel (`focusCapture`) — closing on outside
86099
86100
  * interaction only makes sense paired with not letting focus silently
86100
- * leave the panel first.
86101
+ * leave the panel first. A box of the page whose press must not close the
86102
+ * panel (a card that fills it) names the panel:
86103
+ * `data-navi-popup-inside={id}` — see docs/popup_backdrop.md.
86101
86104
  * @param {boolean} [props.swipeToClose=true] - Pushing the panel back
86102
86105
  * towards the edge it is docked to closes it: the panel follows the
86103
86106
  * pointer and finishes leaving (or comes back to rest) when it is
@@ -86232,6 +86235,7 @@ SidePanel.Foot = SidePanelFoot;
86232
86235
 
86233
86236
  const createSlot = (SlotRenderer = Box) => {
86234
86237
  const slotPropsSignal = signal();
86238
+ let filler = null;
86235
86239
  const Slot = () => {
86236
86240
  const props = slotPropsSignal.value;
86237
86241
  return jsx(SlotRenderer, {
@@ -86240,9 +86244,15 @@ const createSlot = (SlotRenderer = Box) => {
86240
86244
  });
86241
86245
  };
86242
86246
  const SlotFill = props => {
86247
+ const fillerRef = useRef();
86248
+ filler = fillerRef;
86243
86249
  slotPropsSignal.value = props;
86244
86250
  useLayoutEffect(() => {
86245
86251
  return () => {
86252
+ if (filler !== fillerRef) {
86253
+ return;
86254
+ }
86255
+ filler = null;
86246
86256
  slotPropsSignal.value = null;
86247
86257
  };
86248
86258
  }, []);