@jsenv/navi 0.29.434 → 0.29.435
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 +96 -17
- package/dist/dev/jsenv_navi.js.map +4 -4
- package/dist/jsenv_navi.js +36 -5
- package/dist/jsenv_navi.js.map +4 -4
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -12938,15 +12938,12 @@ const useNavStateWithWarnings = (id, options) => {
|
|
|
12938
12938
|
ownerRef.current = {};
|
|
12939
12939
|
}
|
|
12940
12940
|
const owner = ownerRef.current;
|
|
12941
|
-
// Claimed during render so warnAboutOrphanGeneratedKeys sees every id of
|
|
12942
|
-
// the commit, including the ones whose effect has not run yet. The conflict
|
|
12943
|
-
// is judged in the effect: when a screen replaces another in one commit,
|
|
12944
|
-
// preact renders the new children before unmounting the old ones, so at
|
|
12945
|
-
// render time the id may still be held by a component about to go.
|
|
12946
|
-
if (id !== undefined && !idUsageMap.has(id)) {
|
|
12947
|
-
idUsageMap.set(id, { owner, stackTrace: new Error().stack });
|
|
12948
|
-
}
|
|
12949
12941
|
|
|
12942
|
+
// Claimed in the effect, never during render: only the effect has a cleanup
|
|
12943
|
+
// to give the id back, and a render is not always followed by its effects.
|
|
12944
|
+
// A page parked by a Suspense boundary (every hook cleanup run), rendered
|
|
12945
|
+
// again and taken down before the paint, leaves a render-time claim holding
|
|
12946
|
+
// the id for nobody — and the next mount reads a conflict.
|
|
12950
12947
|
useEffect(() => {
|
|
12951
12948
|
if (id === undefined) {
|
|
12952
12949
|
return undefined;
|
|
@@ -12960,12 +12957,9 @@ This can cause UI state conflicts and unexpected behavior.
|
|
|
12960
12957
|
Consider using unique IDs for each component instance.`,
|
|
12961
12958
|
);
|
|
12962
12959
|
} else {
|
|
12963
|
-
// Also set when absent: preact/compat's Suspense parks a subtree by
|
|
12964
|
-
// running every hook cleanup in it, and the render resuming it carries
|
|
12965
|
-
// the same id.
|
|
12966
12960
|
idUsageMap.set(id, { owner, stackTrace: new Error().stack });
|
|
12967
12961
|
}
|
|
12968
|
-
|
|
12962
|
+
scheduleOrphanGeneratedKeysCheck();
|
|
12969
12963
|
return () => {
|
|
12970
12964
|
if (idUsageMap.get(id)?.owner === owner) {
|
|
12971
12965
|
idUsageMap.delete(id);
|
|
@@ -12981,6 +12975,20 @@ Consider using unique IDs for each component instance.`,
|
|
|
12981
12975
|
// and the mount coming back to the entry generates another id: the state is
|
|
12982
12976
|
// there, and nothing reads it. Reported when a component mounting on the entry
|
|
12983
12977
|
// finds such a key, the moment someone expected the state back.
|
|
12978
|
+
//
|
|
12979
|
+
// Checked once every effect of the flush has claimed its id: a component
|
|
12980
|
+
// whose effect comes later in the same flush is not an orphan.
|
|
12981
|
+
let orphanGeneratedKeysCheckScheduled = false;
|
|
12982
|
+
const scheduleOrphanGeneratedKeysCheck = () => {
|
|
12983
|
+
if (orphanGeneratedKeysCheckScheduled) {
|
|
12984
|
+
return;
|
|
12985
|
+
}
|
|
12986
|
+
orphanGeneratedKeysCheckScheduled = true;
|
|
12987
|
+
queueMicrotask(() => {
|
|
12988
|
+
orphanGeneratedKeysCheckScheduled = false;
|
|
12989
|
+
warnAboutOrphanGeneratedKeys();
|
|
12990
|
+
});
|
|
12991
|
+
};
|
|
12984
12992
|
const warnAboutOrphanGeneratedKeys = () => {
|
|
12985
12993
|
const state = browserIntegration.getDocumentState();
|
|
12986
12994
|
if (!state) {
|
|
@@ -54662,6 +54670,26 @@ const Expandable = props => {
|
|
|
54662
54670
|
return stopRide;
|
|
54663
54671
|
};
|
|
54664
54672
|
|
|
54673
|
+
// A silent change is a state the page was already in (see openEffect): it
|
|
54674
|
+
// lands at once. Not playing a reveal is not enough — the stylesheet's own
|
|
54675
|
+
// transition plays it whenever the previous state's style was resolved
|
|
54676
|
+
// before the render: an expandable mounted closed and measured in the same
|
|
54677
|
+
// commit (a List sizing its items) before its deferred mount-time open, or
|
|
54678
|
+
// one kept on screen across the page change.
|
|
54679
|
+
const renderWithoutMovement = (contentContainer, render) => {
|
|
54680
|
+
if (!animation || !contentContainer) {
|
|
54681
|
+
flushSyncRendering(render);
|
|
54682
|
+
return;
|
|
54683
|
+
}
|
|
54684
|
+
contentContainer.style.transitionProperty = "none";
|
|
54685
|
+
flushSyncRendering(render);
|
|
54686
|
+
// The new style must be resolved while transitions are off — re-enabled
|
|
54687
|
+
// in the same recalc, the change would transition after all.
|
|
54688
|
+
// eslint-disable-next-line no-unused-expressions
|
|
54689
|
+
getComputedStyle(contentContainer).transitionProperty;
|
|
54690
|
+
contentContainer.style.transitionProperty = "";
|
|
54691
|
+
};
|
|
54692
|
+
|
|
54665
54693
|
// What opening LOOKS like here, and how to undo it — the one thing an
|
|
54666
54694
|
// expandable owns that a popup does not (see open_controller.js). Reassigned
|
|
54667
54695
|
// on every render so it always closes over the latest props.
|
|
@@ -54682,10 +54710,15 @@ const Expandable = props => {
|
|
|
54682
54710
|
// reopening during a collapse — read while the DOM still says closed.
|
|
54683
54711
|
const startRect = revealing ? contentContainer.getBoundingClientRect() : null;
|
|
54684
54712
|
cancelSettleWatch();
|
|
54685
|
-
|
|
54713
|
+
const renderOpened = () => {
|
|
54686
54714
|
setOpened(true);
|
|
54687
54715
|
setSettled(!revealing);
|
|
54688
|
-
}
|
|
54716
|
+
};
|
|
54717
|
+
if (silent) {
|
|
54718
|
+
renderWithoutMovement(contentContainer, renderOpened);
|
|
54719
|
+
} else {
|
|
54720
|
+
flushSyncRendering(renderOpened);
|
|
54721
|
+
}
|
|
54689
54722
|
if (hasAction) {
|
|
54690
54723
|
runUnwatched(() => effectiveAction.run());
|
|
54691
54724
|
}
|
|
@@ -54724,16 +54757,22 @@ const Expandable = props => {
|
|
|
54724
54757
|
cancelSettleWatch();
|
|
54725
54758
|
// `silent`: closed by the navigation that puts another page on screen,
|
|
54726
54759
|
// which is its movement (see page_change.js).
|
|
54727
|
-
const
|
|
54760
|
+
const silentClose = Boolean(closeEvent.detail.silent);
|
|
54761
|
+
const collapsing = animation && !silentClose && Boolean(contentContainerAtClose);
|
|
54728
54762
|
if (collapsing) {
|
|
54729
54763
|
// Now, while the content is still fully laid out — the collapsing
|
|
54730
54764
|
// track uncovers a content frozen at that size.
|
|
54731
54765
|
freezeContentSize();
|
|
54732
54766
|
}
|
|
54733
|
-
|
|
54767
|
+
const renderClosed = () => {
|
|
54734
54768
|
setOpened(false);
|
|
54735
54769
|
setSettled(!collapsing);
|
|
54736
|
-
}
|
|
54770
|
+
};
|
|
54771
|
+
if (silentClose) {
|
|
54772
|
+
renderWithoutMovement(contentContainerAtClose, renderClosed);
|
|
54773
|
+
} else {
|
|
54774
|
+
flushSyncRendering(renderClosed);
|
|
54775
|
+
}
|
|
54737
54776
|
if (restoreFocus) {
|
|
54738
54777
|
restoreFocus(closeEvent);
|
|
54739
54778
|
} else if (focusedBeforeClose && contentContainerAtClose && contentContainerAtClose.contains(focusedBeforeClose)) {
|
|
@@ -74651,6 +74690,45 @@ const LIST_STYLE_CSS_VARS = {
|
|
|
74651
74690
|
backgroundColor: "--list-background-color"
|
|
74652
74691
|
};
|
|
74653
74692
|
const LIST_PSEUDO_CLASSES = [":hover", ":focus", ":focus-visible", ":focus-within", ":read-only", ":disabled", ":-navi-void", ":-navi-expanded"];
|
|
74693
|
+
// Two lists mounted at once under one `id` are one entry to the scroll memory:
|
|
74694
|
+
// each overwrites the other's position (see docs/scroll.md). Said the moment
|
|
74695
|
+
// the second one mounts, once per id — on a route transition, that is the page
|
|
74696
|
+
// arriving while the page being left is still mounted, hidden.
|
|
74697
|
+
const createSharedRememberedIdWarning = () => {
|
|
74698
|
+
const mountedListsById = new Map();
|
|
74699
|
+
const warnedIdSet = new Set();
|
|
74700
|
+
return (listId, rememberScroll, ref) => {
|
|
74701
|
+
useLayoutEffect(() => {
|
|
74702
|
+
if (!rememberScroll) {
|
|
74703
|
+
return undefined;
|
|
74704
|
+
}
|
|
74705
|
+
let mountedLists = mountedListsById.get(listId);
|
|
74706
|
+
if (!mountedLists) {
|
|
74707
|
+
mountedLists = new Set();
|
|
74708
|
+
mountedListsById.set(listId, mountedLists);
|
|
74709
|
+
}
|
|
74710
|
+
const mounted = {
|
|
74711
|
+
element: ref.current
|
|
74712
|
+
};
|
|
74713
|
+
if (mountedLists.size > 0 && !warnedIdSet.has(listId)) {
|
|
74714
|
+
warnedIdSet.add(listId);
|
|
74715
|
+
const [other] = mountedLists;
|
|
74716
|
+
console.warn(`<List id="${listId}"> mounted while another list with this id is mounted. The position a list comes back to is kept under its id, so the two overwrite each other's: the one arriving can open where the other one was, or come back at its top. Lists are mounted together on one page, under a popup, in a bar every page keeps, and during a route transition, whose page being left stays mounted until the movement ends. Give each list an id naming it in the app (a component drawing a list on several pages takes its id from the page), or set scrollResetOnNavigation on a list with no use for coming back. See docs/scroll.md.`, {
|
|
74717
|
+
list: mounted.element,
|
|
74718
|
+
otherList: other.element
|
|
74719
|
+
});
|
|
74720
|
+
}
|
|
74721
|
+
mountedLists.add(mounted);
|
|
74722
|
+
return () => {
|
|
74723
|
+
mountedLists.delete(mounted);
|
|
74724
|
+
if (mountedLists.size === 0) {
|
|
74725
|
+
mountedListsById.delete(listId);
|
|
74726
|
+
}
|
|
74727
|
+
};
|
|
74728
|
+
}, [listId, rememberScroll]);
|
|
74729
|
+
};
|
|
74730
|
+
};
|
|
74731
|
+
const useWarnSharedRememberedId = createSharedRememberedIdWarning() ;
|
|
74654
74732
|
const useListScrollSync = ({
|
|
74655
74733
|
ref,
|
|
74656
74734
|
listItems,
|
|
@@ -75557,6 +75635,7 @@ const useListScrollSync = ({
|
|
|
75557
75635
|
}
|
|
75558
75636
|
};
|
|
75559
75637
|
}, []);
|
|
75638
|
+
useWarnSharedRememberedId(listId, rememberScroll, ref);
|
|
75560
75639
|
|
|
75561
75640
|
// A list that gets narrower rewraps every item it holds, so everything below
|
|
75562
75641
|
// moves and the reader loses their place — the very thing scrolling a long
|