@jsenv/navi 0.29.473 → 0.29.474
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/dev/jsenv_navi.js +84 -24
- package/dist/dev/jsenv_navi.js.map +6 -6
- package/dist/jsenv_navi.js +84 -24
- package/dist/jsenv_navi.js.map +6 -6
- package/docs/control_value.md +3 -2
- package/docs/popup_open.md +10 -9
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -18307,6 +18307,20 @@ const openCallout = (
|
|
|
18307
18307
|
// deduped away on the grounds that the anchor did not move.
|
|
18308
18308
|
callout.updatePosition = () =>
|
|
18309
18309
|
positioner.update(new CustomEvent(ELEMENT_SIZE_CHANGE));
|
|
18310
|
+
// Content that sizes itself once on screen (a shrink-wrapped BadgeList) does
|
|
18311
|
+
// so after the callout measured it. The size observer positionCallout puts
|
|
18312
|
+
// on the message sees the change, but answers a frame later (see observeSize
|
|
18313
|
+
// in @jsenv/dom for why); placed again on the spot, the first painted frame
|
|
18314
|
+
// is the right one. Stopped here, since a popup further up holds the callout
|
|
18315
|
+
// out of flow and does not resize with it.
|
|
18316
|
+
const onContentSizeChange = (sizeChangeEvent) => {
|
|
18317
|
+
sizeChangeEvent.stopPropagation();
|
|
18318
|
+
callout.updatePosition();
|
|
18319
|
+
};
|
|
18320
|
+
calloutElement.addEventListener("navi_size_change", onContentSizeChange);
|
|
18321
|
+
addTeardown(() => {
|
|
18322
|
+
calloutElement.removeEventListener("navi_size_change", onContentSizeChange);
|
|
18323
|
+
});
|
|
18310
18324
|
|
|
18311
18325
|
return callout;
|
|
18312
18326
|
};
|
|
@@ -46870,8 +46884,8 @@ const useControlgroupProps = (props, {
|
|
|
46870
46884
|
const [boundAction] = useActionBoundToOneParam(action, uiGroupStateController.uiStateSignal);
|
|
46871
46885
|
// Mirror single-input behaviour: a controlled value with nobody listening
|
|
46872
46886
|
// makes the group read-only so children don't appear interactive when they
|
|
46873
|
-
// can't change. A form or a group
|
|
46874
|
-
// what will send the value and hand a new one back.
|
|
46887
|
+
// can't change. A form or a group collecting it IS someone listening — that
|
|
46888
|
+
// is what will send the value and hand a new one back.
|
|
46875
46889
|
const listenedTo = useIsControlListenedTo(props, uiGroupStateController);
|
|
46876
46890
|
useRef(false);
|
|
46877
46891
|
const implicitReadOnly = uiGroupStateController.hasValueProp && !listenedTo;
|
|
@@ -70210,6 +70224,15 @@ const useDialogProps = props => {
|
|
|
70210
70224
|
freezeSize(dialogEl);
|
|
70211
70225
|
}
|
|
70212
70226
|
rectEffect.observeSize(dialogEl);
|
|
70227
|
+
// Content that sizes itself once on screen (a shrink-wrapped BadgeList)
|
|
70228
|
+
// says so, after the placement read it: placed again on the spot, where
|
|
70229
|
+
// observeSize answers a frame late. Stopped here, since a popup further up
|
|
70230
|
+
// holds this one out of flow and does not resize with it.
|
|
70231
|
+
const onContentSizeChange = sizeChangeEvent => {
|
|
70232
|
+
sizeChangeEvent.stopPropagation();
|
|
70233
|
+
rectEffect.check(new CustomEvent(ELEMENT_SIZE_CHANGE));
|
|
70234
|
+
};
|
|
70235
|
+
dialogEl.addEventListener("navi_size_change", onContentSizeChange);
|
|
70213
70236
|
// Exposed for the placement-props effect below, which needs to re-place an
|
|
70214
70237
|
// already-open dialog.
|
|
70215
70238
|
repositionRef.current = repositionEvent => {
|
|
@@ -70223,6 +70246,7 @@ const useDialogProps = props => {
|
|
|
70223
70246
|
};
|
|
70224
70247
|
addCleanup(() => {
|
|
70225
70248
|
repositionRef.current = null;
|
|
70249
|
+
dialogEl.removeEventListener("navi_size_change", onContentSizeChange);
|
|
70226
70250
|
rectEffect.disconnect();
|
|
70227
70251
|
});
|
|
70228
70252
|
// A descendant anchored to something inside this dialog (a Callout, a
|
|
@@ -72015,6 +72039,18 @@ const usePopoverProps = props => {
|
|
|
72015
72039
|
// while open (e.g. an expand/collapse toggle inside it) — not just when
|
|
72016
72040
|
// the anchor itself moves/resizes/re-anchors.
|
|
72017
72041
|
rectEffect.observeSize(popoverEl);
|
|
72042
|
+
// Content that sizes itself once on screen (a shrink-wrapped BadgeList)
|
|
72043
|
+
// says so, after the placement read it: placed again on the spot, where
|
|
72044
|
+
// observeSize answers a frame late. Stopped here, since a popup further up
|
|
72045
|
+
// holds this one out of flow and does not resize with it.
|
|
72046
|
+
const onContentSizeChange = sizeChangeEvent => {
|
|
72047
|
+
sizeChangeEvent.stopPropagation();
|
|
72048
|
+
rectEffect.check(new CustomEvent(ELEMENT_SIZE_CHANGE));
|
|
72049
|
+
};
|
|
72050
|
+
popoverEl.addEventListener("navi_size_change", onContentSizeChange);
|
|
72051
|
+
addCleanup(() => {
|
|
72052
|
+
popoverEl.removeEventListener("navi_size_change", onContentSizeChange);
|
|
72053
|
+
});
|
|
72018
72054
|
// Exposed for the placement-props effect above, which needs to re-place an
|
|
72019
72055
|
// already-open popover.
|
|
72020
72056
|
repositionRef.current = repositionEvent => {
|
|
@@ -83478,39 +83514,29 @@ const BadgeListShrinkWrap = ({
|
|
|
83478
83514
|
const registry = useBadgeRegistry(children, max !== undefined || fallback !== undefined);
|
|
83479
83515
|
const measureRef = useRef();
|
|
83480
83516
|
const visibleRef = useRef();
|
|
83517
|
+
|
|
83518
|
+
// In a closed popup the rows have no size to read: the list is measured when
|
|
83519
|
+
// it comes on screen, before the frame is painted.
|
|
83520
|
+
useDisplayedLayoutEffect(visibleRef, () => {
|
|
83521
|
+
shrinkWrapToWidestRow(visibleRef.current, measureRef.current);
|
|
83522
|
+
}, [children]);
|
|
83481
83523
|
useLayoutEffect(() => {
|
|
83482
83524
|
const measureEl = measureRef.current;
|
|
83483
83525
|
const visibleEl = visibleRef.current;
|
|
83484
83526
|
if (!measureEl || !visibleEl) {
|
|
83485
83527
|
return undefined;
|
|
83486
83528
|
}
|
|
83487
|
-
|
|
83488
|
-
let rafId;
|
|
83489
|
-
const measure = () => {
|
|
83490
|
-
visibleEl.style.width = "";
|
|
83491
|
-
// Clone the already-rendered DOM nodes instead of letting React/Preact
|
|
83492
|
-
// render the children a second time into the ghost: re-rendering would
|
|
83493
|
-
// instantiate Badge/Badge.Button a second time, double-registering their
|
|
83494
|
-
// controllers (and any other mount side effect) under the same id.
|
|
83495
|
-
measureEl.replaceChildren(...Array.from(visibleEl.children, child => child.cloneNode(true)));
|
|
83496
|
-
const optimalWidth = measureWidestChildRow(measureEl);
|
|
83497
|
-
if (optimalWidth !== null) {
|
|
83498
|
-
visibleEl.style.width = `${Math.ceil(optimalWidth)}px`;
|
|
83499
|
-
}
|
|
83500
|
-
};
|
|
83501
|
-
|
|
83502
|
-
// A single badge is a single row, and a single row is already the widest
|
|
83503
|
-
// one: there is nothing to narrow, and no width the list could be given
|
|
83504
|
-
// that would change that.
|
|
83529
|
+
// A single row whatever room it is given: nothing a resize could narrow.
|
|
83505
83530
|
if (visibleEl.children.length < 2) {
|
|
83506
|
-
visibleEl.style.width = "";
|
|
83507
|
-
measureEl.replaceChildren();
|
|
83508
83531
|
return undefined;
|
|
83509
83532
|
}
|
|
83510
|
-
|
|
83533
|
+
let observer;
|
|
83534
|
+
let rafId;
|
|
83511
83535
|
const onResize = () => {
|
|
83512
83536
|
cancelAnimationFrame(rafId);
|
|
83513
|
-
rafId = requestAnimationFrame(
|
|
83537
|
+
rafId = requestAnimationFrame(() => {
|
|
83538
|
+
shrinkWrapToWidestRow(visibleEl, measureEl);
|
|
83539
|
+
});
|
|
83514
83540
|
};
|
|
83515
83541
|
const outerParent = measureEl.parentElement?.parentElement;
|
|
83516
83542
|
if (outerParent) {
|
|
@@ -83556,6 +83582,40 @@ const BadgeListShrinkWrap = ({
|
|
|
83556
83582
|
})
|
|
83557
83583
|
);
|
|
83558
83584
|
};
|
|
83585
|
+
|
|
83586
|
+
// Narrows the list to its widest row, read from clones of its badges in the
|
|
83587
|
+
// ghost.
|
|
83588
|
+
//
|
|
83589
|
+
// What holds the list may have sized itself around the width it had before —
|
|
83590
|
+
// a callout pins its body to the width it measured — so a change is announced
|
|
83591
|
+
// with a bubbling navi_size_change for it to measure again, in the same frame.
|
|
83592
|
+
const shrinkWrapToWidestRow = (visibleEl, measureEl) => {
|
|
83593
|
+
// Not drawn (back in a closed popup): no row to read. The width is kept for
|
|
83594
|
+
// the next opening, which measures again before painting.
|
|
83595
|
+
if (visibleEl.getClientRects().length === 0) {
|
|
83596
|
+
return;
|
|
83597
|
+
}
|
|
83598
|
+
const widthBefore = visibleEl.style.width;
|
|
83599
|
+
visibleEl.style.width = "";
|
|
83600
|
+
if (visibleEl.children.length < 2) {
|
|
83601
|
+
// A single badge is a single row, and a single row is already the widest
|
|
83602
|
+
// one: there is nothing to narrow.
|
|
83603
|
+
measureEl.replaceChildren();
|
|
83604
|
+
} else {
|
|
83605
|
+
// Clone the already-rendered DOM nodes instead of letting React/Preact
|
|
83606
|
+
// render the children a second time into the ghost: re-rendering would
|
|
83607
|
+
// instantiate Badge/Badge.Button a second time, double-registering their
|
|
83608
|
+
// controllers (and any other mount side effect) under the same id.
|
|
83609
|
+
measureEl.replaceChildren(...Array.from(visibleEl.children, child => child.cloneNode(true)));
|
|
83610
|
+
const optimalWidth = measureWidestChildRow(measureEl);
|
|
83611
|
+
if (optimalWidth !== null) {
|
|
83612
|
+
visibleEl.style.width = `${Math.ceil(optimalWidth)}px`;
|
|
83613
|
+
}
|
|
83614
|
+
}
|
|
83615
|
+
if (visibleEl.style.width !== widthBefore) {
|
|
83616
|
+
dispatchPublicCustomEvent(visibleEl, "navi_size_change");
|
|
83617
|
+
}
|
|
83618
|
+
};
|
|
83559
83619
|
const BadgeListMaxLines = ({
|
|
83560
83620
|
fallback,
|
|
83561
83621
|
children,
|