@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/dev/jsenv_navi.js
CHANGED
|
@@ -18934,6 +18934,20 @@ const openCallout = (
|
|
|
18934
18934
|
// deduped away on the grounds that the anchor did not move.
|
|
18935
18935
|
callout.updatePosition = () =>
|
|
18936
18936
|
positioner.update(new CustomEvent(ELEMENT_SIZE_CHANGE));
|
|
18937
|
+
// Content that sizes itself once on screen (a shrink-wrapped BadgeList) does
|
|
18938
|
+
// so after the callout measured it. The size observer positionCallout puts
|
|
18939
|
+
// on the message sees the change, but answers a frame later (see observeSize
|
|
18940
|
+
// in @jsenv/dom for why); placed again on the spot, the first painted frame
|
|
18941
|
+
// is the right one. Stopped here, since a popup further up holds the callout
|
|
18942
|
+
// out of flow and does not resize with it.
|
|
18943
|
+
const onContentSizeChange = (sizeChangeEvent) => {
|
|
18944
|
+
sizeChangeEvent.stopPropagation();
|
|
18945
|
+
callout.updatePosition();
|
|
18946
|
+
};
|
|
18947
|
+
calloutElement.addEventListener("navi_size_change", onContentSizeChange);
|
|
18948
|
+
addTeardown(() => {
|
|
18949
|
+
calloutElement.removeEventListener("navi_size_change", onContentSizeChange);
|
|
18950
|
+
});
|
|
18937
18951
|
|
|
18938
18952
|
return callout;
|
|
18939
18953
|
};
|
|
@@ -48054,8 +48068,8 @@ const useControlgroupProps = (props, {
|
|
|
48054
48068
|
const [boundAction] = useActionBoundToOneParam(action, uiGroupStateController.uiStateSignal);
|
|
48055
48069
|
// Mirror single-input behaviour: a controlled value with nobody listening
|
|
48056
48070
|
// makes the group read-only so children don't appear interactive when they
|
|
48057
|
-
// can't change. A form or a group
|
|
48058
|
-
// what will send the value and hand a new one back.
|
|
48071
|
+
// can't change. A form or a group collecting it IS someone listening — that
|
|
48072
|
+
// is what will send the value and hand a new one back.
|
|
48059
48073
|
const listenedTo = useIsControlListenedTo(props, uiGroupStateController);
|
|
48060
48074
|
const implicitReadOnlyWarnedRef = useRef(false);
|
|
48061
48075
|
const implicitReadOnly = uiGroupStateController.hasValueProp && !listenedTo;
|
|
@@ -71583,6 +71597,15 @@ const useDialogProps = props => {
|
|
|
71583
71597
|
freezeSize(dialogEl);
|
|
71584
71598
|
}
|
|
71585
71599
|
rectEffect.observeSize(dialogEl);
|
|
71600
|
+
// Content that sizes itself once on screen (a shrink-wrapped BadgeList)
|
|
71601
|
+
// says so, after the placement read it: placed again on the spot, where
|
|
71602
|
+
// observeSize answers a frame late. Stopped here, since a popup further up
|
|
71603
|
+
// holds this one out of flow and does not resize with it.
|
|
71604
|
+
const onContentSizeChange = sizeChangeEvent => {
|
|
71605
|
+
sizeChangeEvent.stopPropagation();
|
|
71606
|
+
rectEffect.check(new CustomEvent(ELEMENT_SIZE_CHANGE));
|
|
71607
|
+
};
|
|
71608
|
+
dialogEl.addEventListener("navi_size_change", onContentSizeChange);
|
|
71586
71609
|
// Exposed for the placement-props effect below, which needs to re-place an
|
|
71587
71610
|
// already-open dialog.
|
|
71588
71611
|
repositionRef.current = repositionEvent => {
|
|
@@ -71596,6 +71619,7 @@ const useDialogProps = props => {
|
|
|
71596
71619
|
};
|
|
71597
71620
|
addCleanup(() => {
|
|
71598
71621
|
repositionRef.current = null;
|
|
71622
|
+
dialogEl.removeEventListener("navi_size_change", onContentSizeChange);
|
|
71599
71623
|
rectEffect.disconnect();
|
|
71600
71624
|
});
|
|
71601
71625
|
// A descendant anchored to something inside this dialog (a Callout, a
|
|
@@ -73426,6 +73450,18 @@ const usePopoverProps = props => {
|
|
|
73426
73450
|
// while open (e.g. an expand/collapse toggle inside it) — not just when
|
|
73427
73451
|
// the anchor itself moves/resizes/re-anchors.
|
|
73428
73452
|
rectEffect.observeSize(popoverEl);
|
|
73453
|
+
// Content that sizes itself once on screen (a shrink-wrapped BadgeList)
|
|
73454
|
+
// says so, after the placement read it: placed again on the spot, where
|
|
73455
|
+
// observeSize answers a frame late. Stopped here, since a popup further up
|
|
73456
|
+
// holds this one out of flow and does not resize with it.
|
|
73457
|
+
const onContentSizeChange = sizeChangeEvent => {
|
|
73458
|
+
sizeChangeEvent.stopPropagation();
|
|
73459
|
+
rectEffect.check(new CustomEvent(ELEMENT_SIZE_CHANGE));
|
|
73460
|
+
};
|
|
73461
|
+
popoverEl.addEventListener("navi_size_change", onContentSizeChange);
|
|
73462
|
+
addCleanup(() => {
|
|
73463
|
+
popoverEl.removeEventListener("navi_size_change", onContentSizeChange);
|
|
73464
|
+
});
|
|
73429
73465
|
// Exposed for the placement-props effect above, which needs to re-place an
|
|
73430
73466
|
// already-open popover.
|
|
73431
73467
|
repositionRef.current = repositionEvent => {
|
|
@@ -85079,39 +85115,29 @@ const BadgeListShrinkWrap = ({
|
|
|
85079
85115
|
const registry = useBadgeRegistry(children, max !== undefined || fallback !== undefined);
|
|
85080
85116
|
const measureRef = useRef();
|
|
85081
85117
|
const visibleRef = useRef();
|
|
85118
|
+
|
|
85119
|
+
// In a closed popup the rows have no size to read: the list is measured when
|
|
85120
|
+
// it comes on screen, before the frame is painted.
|
|
85121
|
+
useDisplayedLayoutEffect(visibleRef, () => {
|
|
85122
|
+
shrinkWrapToWidestRow(visibleRef.current, measureRef.current);
|
|
85123
|
+
}, [children]);
|
|
85082
85124
|
useLayoutEffect(() => {
|
|
85083
85125
|
const measureEl = measureRef.current;
|
|
85084
85126
|
const visibleEl = visibleRef.current;
|
|
85085
85127
|
if (!measureEl || !visibleEl) {
|
|
85086
85128
|
return undefined;
|
|
85087
85129
|
}
|
|
85088
|
-
|
|
85089
|
-
let rafId;
|
|
85090
|
-
const measure = () => {
|
|
85091
|
-
visibleEl.style.width = "";
|
|
85092
|
-
// Clone the already-rendered DOM nodes instead of letting React/Preact
|
|
85093
|
-
// render the children a second time into the ghost: re-rendering would
|
|
85094
|
-
// instantiate Badge/Badge.Button a second time, double-registering their
|
|
85095
|
-
// controllers (and any other mount side effect) under the same id.
|
|
85096
|
-
measureEl.replaceChildren(...Array.from(visibleEl.children, child => child.cloneNode(true)));
|
|
85097
|
-
const optimalWidth = measureWidestChildRow(measureEl);
|
|
85098
|
-
if (optimalWidth !== null) {
|
|
85099
|
-
visibleEl.style.width = `${Math.ceil(optimalWidth)}px`;
|
|
85100
|
-
}
|
|
85101
|
-
};
|
|
85102
|
-
|
|
85103
|
-
// A single badge is a single row, and a single row is already the widest
|
|
85104
|
-
// one: there is nothing to narrow, and no width the list could be given
|
|
85105
|
-
// that would change that.
|
|
85130
|
+
// A single row whatever room it is given: nothing a resize could narrow.
|
|
85106
85131
|
if (visibleEl.children.length < 2) {
|
|
85107
|
-
visibleEl.style.width = "";
|
|
85108
|
-
measureEl.replaceChildren();
|
|
85109
85132
|
return undefined;
|
|
85110
85133
|
}
|
|
85111
|
-
|
|
85134
|
+
let observer;
|
|
85135
|
+
let rafId;
|
|
85112
85136
|
const onResize = () => {
|
|
85113
85137
|
cancelAnimationFrame(rafId);
|
|
85114
|
-
rafId = requestAnimationFrame(
|
|
85138
|
+
rafId = requestAnimationFrame(() => {
|
|
85139
|
+
shrinkWrapToWidestRow(visibleEl, measureEl);
|
|
85140
|
+
});
|
|
85115
85141
|
};
|
|
85116
85142
|
const outerParent = measureEl.parentElement?.parentElement;
|
|
85117
85143
|
if (outerParent) {
|
|
@@ -85157,6 +85183,40 @@ const BadgeListShrinkWrap = ({
|
|
|
85157
85183
|
})
|
|
85158
85184
|
);
|
|
85159
85185
|
};
|
|
85186
|
+
|
|
85187
|
+
// Narrows the list to its widest row, read from clones of its badges in the
|
|
85188
|
+
// ghost.
|
|
85189
|
+
//
|
|
85190
|
+
// What holds the list may have sized itself around the width it had before —
|
|
85191
|
+
// a callout pins its body to the width it measured — so a change is announced
|
|
85192
|
+
// with a bubbling navi_size_change for it to measure again, in the same frame.
|
|
85193
|
+
const shrinkWrapToWidestRow = (visibleEl, measureEl) => {
|
|
85194
|
+
// Not drawn (back in a closed popup): no row to read. The width is kept for
|
|
85195
|
+
// the next opening, which measures again before painting.
|
|
85196
|
+
if (visibleEl.getClientRects().length === 0) {
|
|
85197
|
+
return;
|
|
85198
|
+
}
|
|
85199
|
+
const widthBefore = visibleEl.style.width;
|
|
85200
|
+
visibleEl.style.width = "";
|
|
85201
|
+
if (visibleEl.children.length < 2) {
|
|
85202
|
+
// A single badge is a single row, and a single row is already the widest
|
|
85203
|
+
// one: there is nothing to narrow.
|
|
85204
|
+
measureEl.replaceChildren();
|
|
85205
|
+
} else {
|
|
85206
|
+
// Clone the already-rendered DOM nodes instead of letting React/Preact
|
|
85207
|
+
// render the children a second time into the ghost: re-rendering would
|
|
85208
|
+
// instantiate Badge/Badge.Button a second time, double-registering their
|
|
85209
|
+
// controllers (and any other mount side effect) under the same id.
|
|
85210
|
+
measureEl.replaceChildren(...Array.from(visibleEl.children, child => child.cloneNode(true)));
|
|
85211
|
+
const optimalWidth = measureWidestChildRow(measureEl);
|
|
85212
|
+
if (optimalWidth !== null) {
|
|
85213
|
+
visibleEl.style.width = `${Math.ceil(optimalWidth)}px`;
|
|
85214
|
+
}
|
|
85215
|
+
}
|
|
85216
|
+
if (visibleEl.style.width !== widthBefore) {
|
|
85217
|
+
dispatchPublicCustomEvent(visibleEl, "navi_size_change");
|
|
85218
|
+
}
|
|
85219
|
+
};
|
|
85160
85220
|
const BadgeListMaxLines = ({
|
|
85161
85221
|
fallback,
|
|
85162
85222
|
children,
|