@jsenv/navi 0.29.22 → 0.29.24
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/dist/jsenv_navi.js +157 -0
- package/dist/jsenv_navi.js.map +13 -7
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -25727,6 +25727,39 @@ const popupCss = /* css */ `
|
|
|
25727
25727
|
}
|
|
25728
25728
|
`;
|
|
25729
25729
|
|
|
25730
|
+
/**
|
|
25731
|
+
* Holding a box at the size it has right now — what the `sizing="frozen"` prop
|
|
25732
|
+
* on Dialog, Popover and SlideContainer is made of.
|
|
25733
|
+
*
|
|
25734
|
+
* The need: a surface being used is a frame that has been put down. What moves
|
|
25735
|
+
* inside it is its content, not the frame — a list one empties by acting on it
|
|
25736
|
+
* (marking as read, archiving) must not resize the box under the finger, or the
|
|
25737
|
+
* next row moves while it is being aimed at.
|
|
25738
|
+
*/
|
|
25739
|
+
|
|
25740
|
+
/**
|
|
25741
|
+
* Read back through getComputedStyle rather than offsetWidth/offsetHeight: the
|
|
25742
|
+
* used values honour whatever `box-sizing` is in effect, so writing them back
|
|
25743
|
+
* reproduces exactly the box that was measured, to the subpixel. Neither is
|
|
25744
|
+
* affected by a transform, so this is safe to call while an entrance animation
|
|
25745
|
+
* is scaling the box.
|
|
25746
|
+
*
|
|
25747
|
+
* `width`/`height`, never `min-width`/`min-height`: a `max-*` — the caller's
|
|
25748
|
+
* own, or the container ceiling a popup already computes for itself — must keep
|
|
25749
|
+
* winning, so a box frozen at 500px on a phone held upright still fits once it
|
|
25750
|
+
* is turned.
|
|
25751
|
+
*/
|
|
25752
|
+
const freezeSize = (el) => {
|
|
25753
|
+
const { width, height } = getComputedStyle(el);
|
|
25754
|
+
el.style.width = width;
|
|
25755
|
+
el.style.height = height;
|
|
25756
|
+
};
|
|
25757
|
+
|
|
25758
|
+
const unfreezeSize = (el) => {
|
|
25759
|
+
el.style.width = "";
|
|
25760
|
+
el.style.height = "";
|
|
25761
|
+
};
|
|
25762
|
+
|
|
25730
25763
|
/**
|
|
25731
25764
|
* Small, renderer-agnostic helpers shared by Popover and Dialog's own custom
|
|
25732
25765
|
* (non-top-layer) renderers — operate on a plain DOM element, no knowledge
|
|
@@ -26311,6 +26344,19 @@ const css$V = /* css */`
|
|
|
26311
26344
|
* @param {string} [props.minHeight] - Maps to `--dialog-min-height`, same
|
|
26312
26345
|
* clamping as `minWidth`.
|
|
26313
26346
|
* @param {string} [props.maxHeight] - Maps to `--dialog-max-height`.
|
|
26347
|
+
* @param {"auto"|"frozen"} [props.sizing="auto"] - `"auto"`: the dialog follows
|
|
26348
|
+
* its content for as long as it stays open. `"frozen"`: it is measured once
|
|
26349
|
+
* and held at that size until it closes — what no longer fits (or no longer
|
|
26350
|
+
* fills it) is the scroll's business. For a surface acted upon while it is
|
|
26351
|
+
* open: marking a notification as read, emptying a queue, swapping between
|
|
26352
|
+
* two slides of different heights — the row being aimed at must not move
|
|
26353
|
+
* under the finger. The measure is taken at the first render where this says
|
|
26354
|
+
* `"frozen"`, so a dialog opening on skeletons can say
|
|
26355
|
+
* `sizing={loading ? "auto" : "frozen"}` and be measured once the real
|
|
26356
|
+
* content is there. The freeze writes a `height`/`width`, never a `min-*`:
|
|
26357
|
+
* `maxHeight`/`maxWidth` and the container ceiling keep winning, so a frozen
|
|
26358
|
+
* dialog still fits when the phone is turned. Closing releases it — the next
|
|
26359
|
+
* opening measures again.
|
|
26314
26360
|
* @param {number} [props.tabIndex=-1] - Set on the dialog element itself so
|
|
26315
26361
|
* `autoFocus="last-resort"` below has somewhere to land when the dialog has
|
|
26316
26362
|
* no other focusable descendant of its own.
|
|
@@ -26515,6 +26561,9 @@ const useDialogProps = props => {
|
|
|
26515
26561
|
// actually makes "capture"/"none" behave the same way here too.
|
|
26516
26562
|
pointerInteractionOutsideEffect = "close",
|
|
26517
26563
|
scrollCapture: scrollCaptureProp,
|
|
26564
|
+
// "auto" (default) → the dialog follows its content. "frozen" → measured
|
|
26565
|
+
// once, held at that size while open. See this prop's own JSDoc above.
|
|
26566
|
+
sizing = "auto",
|
|
26518
26567
|
animation,
|
|
26519
26568
|
// Only ever affects --anchor-width/--anchor-height (see this file's top
|
|
26520
26569
|
// comment) — Dialog's own positioning is never relative to it.
|
|
@@ -26577,6 +26626,21 @@ const useDialogProps = props => {
|
|
|
26577
26626
|
detail: {}
|
|
26578
26627
|
}));
|
|
26579
26628
|
}, [positionArea, marginWithContainer]);
|
|
26629
|
+
// The freeze is taken where the value changes, not only at open time: a
|
|
26630
|
+
// dialog showing skeletons first says sizing="auto" until its content is
|
|
26631
|
+
// there, and would otherwise be held at the size of the waiting state.
|
|
26632
|
+
// Opening while already "frozen" is openEffect's own case.
|
|
26633
|
+
useEffect(() => {
|
|
26634
|
+
const dialogEl = ref.current;
|
|
26635
|
+
if (!dialogEl || !openController.opened) {
|
|
26636
|
+
return;
|
|
26637
|
+
}
|
|
26638
|
+
if (sizing === "frozen") {
|
|
26639
|
+
freezeSize(dialogEl);
|
|
26640
|
+
} else {
|
|
26641
|
+
unfreezeSize(dialogEl);
|
|
26642
|
+
}
|
|
26643
|
+
}, [sizing]);
|
|
26580
26644
|
const positionAreaParseResult = parsePositionArea(positionArea);
|
|
26581
26645
|
if (!positionAreaParseResult) {
|
|
26582
26646
|
console.warn(`Dialog: invalid positionArea="${positionArea}"`);
|
|
@@ -26808,6 +26872,13 @@ const useDialogProps = props => {
|
|
|
26808
26872
|
// navi_position_change on every call) — nothing to do here.
|
|
26809
26873
|
};
|
|
26810
26874
|
positionDialog();
|
|
26875
|
+
if (sizing === "frozen") {
|
|
26876
|
+
// After positionDialog: the caps it writes
|
|
26877
|
+
// (--container-position-remaining-*) are part of what decides the size
|
|
26878
|
+
// being taken, so measuring before it would freeze a box the dialog
|
|
26879
|
+
// never actually had.
|
|
26880
|
+
freezeSize(dialogEl);
|
|
26881
|
+
}
|
|
26811
26882
|
|
|
26812
26883
|
// Reposition on the same triggers Popover's own visibleRectEffect
|
|
26813
26884
|
// already reacts to generically — window resize/scroll/visual-viewport
|
|
@@ -26939,6 +27010,9 @@ const useDialogProps = props => {
|
|
|
26939
27010
|
// property is actually present — harmless the rest of the time.
|
|
26940
27011
|
dialogEl.setAttribute("navi-hidden", "");
|
|
26941
27012
|
dialogEl.close();
|
|
27013
|
+
// The freeze only ever holds for one opening: the next one has its own
|
|
27014
|
+
// content to be measured against.
|
|
27015
|
+
unfreezeSize(dialogEl);
|
|
26942
27016
|
cancelOpenInteractionSuppression?.();
|
|
26943
27017
|
if (hasCssTransitionAnimation) {
|
|
26944
27018
|
suppressPointerEventsDuringTransition(dialogEl);
|
|
@@ -27516,6 +27590,18 @@ const css$U = /* css */`
|
|
|
27516
27590
|
* @param {string} [props.minHeight] - Maps to `--popover-min-height`, same
|
|
27517
27591
|
* clamping as `minWidth`.
|
|
27518
27592
|
* @param {string} [props.maxHeight] - Maps to `--popover-max-height`.
|
|
27593
|
+
* @param {"auto"|"frozen"} [props.sizing="auto"] - `"auto"`: the popover
|
|
27594
|
+
* follows its content for as long as it stays open. `"frozen"`: it is
|
|
27595
|
+
* measured once and held at that size until it closes — what no longer fits
|
|
27596
|
+
* (or no longer fills it) is the scroll's business. For a surface acted upon
|
|
27597
|
+
* while it is open: emptying a list, swapping between two panels of
|
|
27598
|
+
* different heights — the row being aimed at must not move under the
|
|
27599
|
+
* pointer. The measure is taken at the first render where this says
|
|
27600
|
+
* `"frozen"`, so a popover opening on skeletons can say
|
|
27601
|
+
* `sizing={loading ? "auto" : "frozen"}` and be measured once the real
|
|
27602
|
+
* content is there. The freeze writes a `height`/`width`, never a `min-*`:
|
|
27603
|
+
* `maxHeight`/`maxWidth` and the container ceiling keep winning. Closing
|
|
27604
|
+
* releases it — the next opening measures again.
|
|
27519
27605
|
* @param {number} [props.tabIndex=-1] - Set on the popover element itself
|
|
27520
27606
|
* so `autoFocus="last-resort"` below has somewhere to land when the popover
|
|
27521
27607
|
* has no other focusable descendant of its own.
|
|
@@ -27716,6 +27802,9 @@ const usePopoverProps = props => {
|
|
|
27716
27802
|
pointerInteractionOutsideEffect = "none",
|
|
27717
27803
|
scrollCapture,
|
|
27718
27804
|
focusCapture,
|
|
27805
|
+
// "auto" (default) → the popover follows its content. "frozen" → measured
|
|
27806
|
+
// once, held at that size while open. See this prop's own JSDoc above.
|
|
27807
|
+
sizing = "auto",
|
|
27719
27808
|
animation,
|
|
27720
27809
|
anchor,
|
|
27721
27810
|
anchorCustomEventDetail = "override",
|
|
@@ -27774,6 +27863,21 @@ const usePopoverProps = props => {
|
|
|
27774
27863
|
detail: {}
|
|
27775
27864
|
}));
|
|
27776
27865
|
}, [positionArea, positionAreaFixed, positionAreaWhenAnchorIsInvalid, marginWithAnchor, marginWithContainer]);
|
|
27866
|
+
// The freeze is taken where the value changes, not only at open time: a
|
|
27867
|
+
// popover showing skeletons first says sizing="auto" until its content is
|
|
27868
|
+
// there, and would otherwise be held at the size of the waiting state.
|
|
27869
|
+
// Opening while already "frozen" is openEffect's own case.
|
|
27870
|
+
useEffect(() => {
|
|
27871
|
+
const popoverEl = ref.current;
|
|
27872
|
+
if (!popoverEl || !openController.opened) {
|
|
27873
|
+
return;
|
|
27874
|
+
}
|
|
27875
|
+
if (sizing === "frozen") {
|
|
27876
|
+
freezeSize(popoverEl);
|
|
27877
|
+
} else {
|
|
27878
|
+
unfreezeSize(popoverEl);
|
|
27879
|
+
}
|
|
27880
|
+
}, [sizing]);
|
|
27777
27881
|
// The custom renderer's own starting-hidden state is a stylesheet default
|
|
27778
27882
|
// now (&:not([popover]) { display: none } on .navi_popover/
|
|
27779
27883
|
// .navi_popover_backdrop above) rather than set here imperatively — a
|
|
@@ -28170,6 +28274,14 @@ const usePopoverProps = props => {
|
|
|
28170
28274
|
addCleanup(() => {
|
|
28171
28275
|
rectEffect.disconnect();
|
|
28172
28276
|
});
|
|
28277
|
+
if (sizing === "frozen") {
|
|
28278
|
+
// After rectEffect's own setup, which has already placed the popover:
|
|
28279
|
+
// the caps that placement writes
|
|
28280
|
+
// (--container-position-remaining-*) are part of what decides the size
|
|
28281
|
+
// being taken, so measuring before it would freeze a box the popover
|
|
28282
|
+
// never actually had.
|
|
28283
|
+
freezeSize(popoverEl);
|
|
28284
|
+
}
|
|
28173
28285
|
|
|
28174
28286
|
// "sliding"/"expanding" need a concrete direction (see
|
|
28175
28287
|
// resolveDirectionValue) — resolved here, once, now that rectEffect's
|
|
@@ -28265,6 +28377,9 @@ const usePopoverProps = props => {
|
|
|
28265
28377
|
} else {
|
|
28266
28378
|
openLocalPopoverCount = Math.max(0, openLocalPopoverCount - 1);
|
|
28267
28379
|
}
|
|
28380
|
+
// The freeze only ever holds for one opening: the next one has its own
|
|
28381
|
+
// content to be measured against.
|
|
28382
|
+
unfreezeSize(popoverEl);
|
|
28268
28383
|
// Not interactive while it's leaving either — cancel the open side's
|
|
28269
28384
|
// still-pending suppression first, since a fresh one below fully
|
|
28270
28385
|
// replaces it (nothing ever needs to cancel this one in turn: a
|
|
@@ -45593,6 +45708,18 @@ const readArea = slideElement => slideElement.getAttribute("data-slide-area") ||
|
|
|
45593
45708
|
* scrolling the document — so a map that travels vertically has to be told
|
|
45594
45709
|
* (`travelByScroll` / `"y"` / `"xy"`) before a wheel moves it.
|
|
45595
45710
|
* @param {string} [props.duration="300ms"] - how long a slide change takes.
|
|
45711
|
+
* @param {"largest"|"frozen"} [props.sizing="largest"] - what decides the size
|
|
45712
|
+
* of the box. "largest": the largest slide, at every moment — the box follows
|
|
45713
|
+
* whatever the slides do. "frozen": the box is measured once and kept at that
|
|
45714
|
+
* size, and what no longer fits (or no longer fills it) is the scroll's
|
|
45715
|
+
* business. For slides one ACTS on rather than merely reads: a row marked as
|
|
45716
|
+
* read leaves one panel for the other, the tallest slide is not the same one
|
|
45717
|
+
* anymore, and with "largest" the box resizes under the finger aiming at the
|
|
45718
|
+
* next row. The measure is taken at the first render where this says
|
|
45719
|
+
* "frozen", so a container opening on skeletons says
|
|
45720
|
+
* `sizing={loading ? "largest" : "frozen"}` and is measured once the real
|
|
45721
|
+
* content is there. The freeze holds against what happens INSIDE the box, not
|
|
45722
|
+
* against the room it is given: a window resize measures again.
|
|
45596
45723
|
*/
|
|
45597
45724
|
const SlideContainer = ({
|
|
45598
45725
|
layout = "row",
|
|
@@ -45606,6 +45733,7 @@ const SlideContainer = ({
|
|
|
45606
45733
|
travelByDrag = true,
|
|
45607
45734
|
travelByScroll = "x",
|
|
45608
45735
|
duration = "300ms",
|
|
45736
|
+
sizing = "largest",
|
|
45609
45737
|
children,
|
|
45610
45738
|
...rest
|
|
45611
45739
|
}) => {
|
|
@@ -45740,6 +45868,31 @@ const SlideContainer = ({
|
|
|
45740
45868
|
};
|
|
45741
45869
|
}, [noTravel]);
|
|
45742
45870
|
|
|
45871
|
+
// The box, held at the size it has right now (sizing="frozen"): what the
|
|
45872
|
+
// slides do afterwards moves their own content and not this box. Taken where
|
|
45873
|
+
// the value changes rather than at mount alone, so a container opening on
|
|
45874
|
+
// skeletons is measured on the real thing (see the prop's own doc).
|
|
45875
|
+
useLayoutEffect(() => {
|
|
45876
|
+
if (sizing !== "frozen") {
|
|
45877
|
+
return undefined;
|
|
45878
|
+
}
|
|
45879
|
+
const containerEl = containerRef.current;
|
|
45880
|
+
freezeSize(containerEl);
|
|
45881
|
+
// The freeze is about the content, never about the room: a box frozen on a
|
|
45882
|
+
// phone held upright has to fit once it is turned, and one frozen in a
|
|
45883
|
+
// window has to follow that window. So the measure is taken again whenever
|
|
45884
|
+
// the room changes — the only moment the box is allowed to resize.
|
|
45885
|
+
const onWindowResize = () => {
|
|
45886
|
+
unfreezeSize(containerEl);
|
|
45887
|
+
freezeSize(containerEl);
|
|
45888
|
+
};
|
|
45889
|
+
window.addEventListener("resize", onWindowResize);
|
|
45890
|
+
return () => {
|
|
45891
|
+
window.removeEventListener("resize", onWindowResize);
|
|
45892
|
+
unfreezeSize(containerEl);
|
|
45893
|
+
};
|
|
45894
|
+
}, [sizing]);
|
|
45895
|
+
|
|
45743
45896
|
// What the user was doing on each slide, so coming back comes back to it. The
|
|
45744
45897
|
// ways out are left out on purpose: pressing one is how one LEAVES a slide,
|
|
45745
45898
|
// and remembering it would mean coming back to the exit rather than to the
|
|
@@ -48151,6 +48304,10 @@ const css$z = /* css */`
|
|
|
48151
48304
|
* @param {string} [props.minWidth] - Forwarded as-is.
|
|
48152
48305
|
* @param {string} [props.minHeight] - Forwarded as-is.
|
|
48153
48306
|
* @param {string} [props.maxHeight] - Forwarded as-is.
|
|
48307
|
+
* @param {"auto"|"frozen"} [props.sizing] - Forwarded as-is to both, which
|
|
48308
|
+
* understand it identically: `"frozen"` holds the surface at the size it was
|
|
48309
|
+
* measured at while it stays open, so acting on what it contains moves the
|
|
48310
|
+
* content and not the surface. See either component's own doc.
|
|
48154
48311
|
* @param {boolean} [props.expand] - Dialog-mode only: shorthand for both
|
|
48155
48312
|
* `expandX`/`expandY` below. No effect in popover mode.
|
|
48156
48313
|
* @param {boolean} [props.expandX] - Dialog-mode only: stretches the dialog
|