@jsenv/navi 0.27.55 → 0.27.57
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 +235 -152
- package/dist/jsenv_navi.js.map +15 -12
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -14530,6 +14530,14 @@ computed(() => {
|
|
|
14530
14530
|
return reasonArray;
|
|
14531
14531
|
});
|
|
14532
14532
|
|
|
14533
|
+
const documentStateSignal = signal(null);
|
|
14534
|
+
const useDocumentState = () => {
|
|
14535
|
+
return documentStateSignal.value;
|
|
14536
|
+
};
|
|
14537
|
+
const updateDocumentState = (value) => {
|
|
14538
|
+
documentStateSignal.value = value;
|
|
14539
|
+
};
|
|
14540
|
+
|
|
14533
14541
|
const documentUrlSignal = signal(
|
|
14534
14542
|
typeof window === "undefined" ? "http://localhost" : window.location.href,
|
|
14535
14543
|
);
|
|
@@ -14574,14 +14582,6 @@ const urlToScheme = (url) => {
|
|
|
14574
14582
|
return scheme;
|
|
14575
14583
|
};
|
|
14576
14584
|
|
|
14577
|
-
const documentStateSignal = signal(null);
|
|
14578
|
-
const useDocumentState = () => {
|
|
14579
|
-
return documentStateSignal.value;
|
|
14580
|
-
};
|
|
14581
|
-
const updateDocumentState = (value) => {
|
|
14582
|
-
documentStateSignal.value = value;
|
|
14583
|
-
};
|
|
14584
|
-
|
|
14585
14585
|
const getHrefTargetInfo = (href) => {
|
|
14586
14586
|
href = String(href);
|
|
14587
14587
|
|
|
@@ -14664,18 +14664,6 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
14664
14664
|
return window.history.state ? { ...window.history.state } : null;
|
|
14665
14665
|
};
|
|
14666
14666
|
|
|
14667
|
-
const replaceDocumentState = (
|
|
14668
|
-
newState,
|
|
14669
|
-
{ reason = "replaceDocumentState called" } = {},
|
|
14670
|
-
) => {
|
|
14671
|
-
const url = window.location.href;
|
|
14672
|
-
handleRoutingTask(url, {
|
|
14673
|
-
reason,
|
|
14674
|
-
navigationType: "replace",
|
|
14675
|
-
state: newState,
|
|
14676
|
-
});
|
|
14677
|
-
};
|
|
14678
|
-
|
|
14679
14667
|
const historyStartAtStart = getDocumentState();
|
|
14680
14668
|
const visitedUrlSet = historyStartAtStart
|
|
14681
14669
|
? new Set(historyStartAtStart.jsenv_visited_urls || [])
|
|
@@ -14695,19 +14683,7 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
14695
14683
|
return;
|
|
14696
14684
|
}
|
|
14697
14685
|
visitedUrlSet.add(url);
|
|
14698
|
-
visitedUrlsSignal.value++;
|
|
14699
|
-
|
|
14700
|
-
const historyState = getDocumentState() || {};
|
|
14701
|
-
const historyStateWithVisitedUrls = {
|
|
14702
|
-
...historyState,
|
|
14703
|
-
jsenv_visited_urls: Array.from(visitedUrlSet),
|
|
14704
|
-
};
|
|
14705
|
-
window.history.replaceState(
|
|
14706
|
-
historyStateWithVisitedUrls,
|
|
14707
|
-
null,
|
|
14708
|
-
window.location.href,
|
|
14709
|
-
);
|
|
14710
|
-
updateDocumentState(historyStateWithVisitedUrls);
|
|
14686
|
+
visitedUrlsSignal.value++;
|
|
14711
14687
|
};
|
|
14712
14688
|
|
|
14713
14689
|
let abortController = null;
|
|
@@ -14715,19 +14691,38 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
14715
14691
|
url,
|
|
14716
14692
|
{
|
|
14717
14693
|
reason,
|
|
14718
|
-
navigationType, // "
|
|
14694
|
+
navigationType, // "load", "reload", "replace", "push", "traverse"
|
|
14719
14695
|
state,
|
|
14720
14696
|
},
|
|
14721
14697
|
) => {
|
|
14722
|
-
|
|
14723
|
-
|
|
14724
|
-
|
|
14725
|
-
|
|
14698
|
+
const isSameUrl = url === window.location.href;
|
|
14699
|
+
|
|
14700
|
+
if (navigationType === "push" || navigationType === "replace") {
|
|
14701
|
+
// Pre-merge visited URLs so push/replaceState is called only once.
|
|
14702
|
+
markUrlAsVisited(url);
|
|
14703
|
+
const effectiveState = {
|
|
14704
|
+
...(state || {}),
|
|
14705
|
+
jsenv_visited_urls: Array.from(visitedUrlSet),
|
|
14706
|
+
};
|
|
14707
|
+
if (navigationType === "push") {
|
|
14708
|
+
window.history.pushState(effectiveState, null, url);
|
|
14709
|
+
} else {
|
|
14710
|
+
window.history.replaceState(effectiveState, null, url);
|
|
14711
|
+
}
|
|
14712
|
+
updateDocumentUrl(url);
|
|
14713
|
+
updateDocumentState(effectiveState);
|
|
14714
|
+
} else {
|
|
14715
|
+
// traverse / reload: state comes from the history entry, no push/replace needed.
|
|
14716
|
+
markUrlAsVisited(url);
|
|
14717
|
+
updateDocumentUrl(url);
|
|
14718
|
+
updateDocumentState(state);
|
|
14719
|
+
}
|
|
14720
|
+
|
|
14721
|
+
// Routes only match on URL — skip route matching for state-only changes.
|
|
14722
|
+
if (isSameUrl && navigationType !== "reload" && navigationType !== "load") {
|
|
14723
|
+
return undefined;
|
|
14726
14724
|
}
|
|
14727
14725
|
|
|
14728
|
-
updateDocumentUrl(url);
|
|
14729
|
-
updateDocumentState(state);
|
|
14730
|
-
markUrlAsVisited(url);
|
|
14731
14726
|
if (abortController) {
|
|
14732
14727
|
abortController.abort(`navigating to ${url}`);
|
|
14733
14728
|
}
|
|
@@ -14847,7 +14842,7 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
14847
14842
|
const state = history.state;
|
|
14848
14843
|
handleRoutingTask(url, {
|
|
14849
14844
|
reason: "routing initialization",
|
|
14850
|
-
navigationType: "
|
|
14845
|
+
navigationType: "load",
|
|
14851
14846
|
state,
|
|
14852
14847
|
});
|
|
14853
14848
|
};
|
|
@@ -14861,7 +14856,6 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
14861
14856
|
navBack,
|
|
14862
14857
|
navForward,
|
|
14863
14858
|
getDocumentState,
|
|
14864
|
-
replaceDocumentState,
|
|
14865
14859
|
isVisited,
|
|
14866
14860
|
visitedUrlsSignal,
|
|
14867
14861
|
};
|
|
@@ -14947,12 +14941,19 @@ setOnAllRouteReady((v) => {
|
|
|
14947
14941
|
});
|
|
14948
14942
|
setRouteIntegration(browserIntegration);
|
|
14949
14943
|
|
|
14950
|
-
const
|
|
14944
|
+
const navIntegratedVia = browserIntegration.integration;
|
|
14951
14945
|
const navTo = (target, options) => {
|
|
14952
14946
|
const url = new URL(target, window.location.href).href;
|
|
14953
14947
|
const currentUrl = documentUrlSignal.peek();
|
|
14954
14948
|
if (url === currentUrl) {
|
|
14955
|
-
|
|
14949
|
+
if (options?.state === undefined) {
|
|
14950
|
+
return null;
|
|
14951
|
+
}
|
|
14952
|
+
// State-only update on same URL: skip if state is identical to current.
|
|
14953
|
+
const currentState = browserIntegration.getDocumentState();
|
|
14954
|
+
if (compareTwoJsValues(options.state, currentState)) {
|
|
14955
|
+
return null;
|
|
14956
|
+
}
|
|
14956
14957
|
}
|
|
14957
14958
|
return browserIntegration.navTo(url, options);
|
|
14958
14959
|
};
|
|
@@ -14973,78 +14974,113 @@ const isVisited = browserIntegration.isVisited;
|
|
|
14973
14974
|
const visitedUrlsSignal = browserIntegration.visitedUrlsSignal;
|
|
14974
14975
|
browserIntegration.handleActionTask;
|
|
14975
14976
|
|
|
14976
|
-
const NOT_SET = {};
|
|
14977
14977
|
const NO_OP = () => {};
|
|
14978
14978
|
const NO_ID_GIVEN = [undefined, NO_OP, NO_OP];
|
|
14979
|
-
const useNavStateBasic = (
|
|
14980
|
-
|
|
14979
|
+
const useNavStateBasic = (
|
|
14980
|
+
id,
|
|
14981
|
+
initialValue,
|
|
14982
|
+
{ debug, type = "replace", onLeave } = {},
|
|
14983
|
+
) => {
|
|
14984
|
+
// Hooks must be called unconditionally — before the !id early return.
|
|
14985
|
+
const state = documentStateSignal.value;
|
|
14986
|
+
const valueInState = id
|
|
14987
|
+
? state !== null
|
|
14988
|
+
? state[id]
|
|
14989
|
+
: undefined
|
|
14990
|
+
: undefined;
|
|
14991
|
+
const onLeaveRef = useRef(onLeave);
|
|
14992
|
+
onLeaveRef.current = onLeave;
|
|
14993
|
+
const prevValueInStateRef = useRef(valueInState);
|
|
14994
|
+
// enteredRef tracks whether enter() was called without a matching leave() yet.
|
|
14995
|
+
// It lets the effect distinguish an external disappearance (back button → fire onLeave)
|
|
14996
|
+
// from a programmatic one (leave() already set it to false before the state updates).
|
|
14997
|
+
const enteredRef = useRef(false);
|
|
14998
|
+
useEffect(() => {
|
|
14999
|
+
const prevValue = prevValueInStateRef.current;
|
|
15000
|
+
prevValueInStateRef.current = valueInState;
|
|
15001
|
+
if (
|
|
15002
|
+
prevValue !== undefined &&
|
|
15003
|
+
valueInState === undefined &&
|
|
15004
|
+
enteredRef.current
|
|
15005
|
+
) {
|
|
15006
|
+
enteredRef.current = false;
|
|
15007
|
+
onLeaveRef.current?.();
|
|
15008
|
+
}
|
|
15009
|
+
}, [valueInState]);
|
|
15010
|
+
|
|
14981
15011
|
if (!id) {
|
|
14982
15012
|
return NO_ID_GIVEN;
|
|
14983
15013
|
}
|
|
14984
15014
|
|
|
14985
|
-
|
|
14986
|
-
const documentState = browserIntegration.getDocumentState();
|
|
14987
|
-
const valueInDocumentState = documentState ? documentState[id] : undefined;
|
|
14988
|
-
if (valueInDocumentState === undefined) {
|
|
14989
|
-
navStateRef.current = initialValue;
|
|
14990
|
-
if (initialValue !== undefined) {
|
|
14991
|
-
console.debug(
|
|
14992
|
-
`useNavState(${id}) initial value is ${initialValue} (from initialValue passed in as argument)`,
|
|
14993
|
-
);
|
|
14994
|
-
}
|
|
14995
|
-
} else {
|
|
14996
|
-
navStateRef.current = valueInDocumentState;
|
|
14997
|
-
if (debug) {
|
|
14998
|
-
console.debug(
|
|
14999
|
-
`useNavState(${id}) initial value is ${initialValue} (from nav state)`,
|
|
15000
|
-
);
|
|
15001
|
-
}
|
|
15002
|
-
}
|
|
15003
|
-
}
|
|
15015
|
+
const currentValue = valueInState !== undefined ? valueInState : initialValue;
|
|
15004
15016
|
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
value = value(currentValue);
|
|
15009
|
-
}
|
|
15010
|
-
if (debug) {
|
|
15011
|
-
console.debug(
|
|
15012
|
-
`useNavState(${id}) set ${value} (previous was ${currentValue})`,
|
|
15013
|
-
);
|
|
15014
|
-
}
|
|
15017
|
+
if (debug) {
|
|
15018
|
+
console.debug(`useNavState(${id}) current value is ${currentValue}`);
|
|
15019
|
+
}
|
|
15015
15020
|
|
|
15021
|
+
// enter(value): navigate TO this state (push or replace depending on type).
|
|
15022
|
+
// Calling enter() without a value stores "on" — the mere presence of the key
|
|
15023
|
+
// in the document state is enough to match; the value just allows associating
|
|
15024
|
+
// extra data with the entry when needed.
|
|
15025
|
+
const enter = (value = "on") => {
|
|
15026
|
+
enteredRef.current = true;
|
|
15016
15027
|
const currentState = browserIntegration.getDocumentState() || {};
|
|
15017
|
-
|
|
15018
|
-
if (value === undefined) {
|
|
15019
|
-
if (!Object.hasOwn(currentState, id)) {
|
|
15020
|
-
return;
|
|
15021
|
-
}
|
|
15022
|
-
delete currentState[id];
|
|
15023
|
-
browserIntegration.replaceDocumentState(currentState, {
|
|
15024
|
-
reason: `delete "${id}" from browser state`,
|
|
15025
|
-
});
|
|
15028
|
+
if (currentState[id] === value) {
|
|
15026
15029
|
return;
|
|
15027
15030
|
}
|
|
15031
|
+
const newState = { ...currentState, [id]: value };
|
|
15032
|
+
navTo(window.location.href, {
|
|
15033
|
+
replace: type !== "push",
|
|
15034
|
+
state: newState,
|
|
15035
|
+
});
|
|
15036
|
+
};
|
|
15028
15037
|
|
|
15029
|
-
|
|
15030
|
-
|
|
15038
|
+
// leave(): navigate AWAY FROM this state (navBack in push mode, replace in replace mode).
|
|
15039
|
+
const leave = () => {
|
|
15040
|
+
enteredRef.current = false;
|
|
15041
|
+
const currentState = browserIntegration.getDocumentState() || {};
|
|
15042
|
+
if (!Object.hasOwn(currentState, id)) {
|
|
15031
15043
|
return;
|
|
15032
15044
|
}
|
|
15033
|
-
|
|
15034
|
-
|
|
15035
|
-
|
|
15036
|
-
|
|
15045
|
+
if (type === "push") {
|
|
15046
|
+
browserIntegration.navBack();
|
|
15047
|
+
} else {
|
|
15048
|
+
const newState = { ...currentState };
|
|
15049
|
+
delete newState[id];
|
|
15050
|
+
navTo(window.location.href, { replace: true, state: newState });
|
|
15051
|
+
}
|
|
15037
15052
|
};
|
|
15038
15053
|
|
|
15039
|
-
return [
|
|
15040
|
-
navStateRef.current,
|
|
15041
|
-
set,
|
|
15042
|
-
() => {
|
|
15043
|
-
set(undefined);
|
|
15044
|
-
},
|
|
15045
|
-
];
|
|
15054
|
+
return [currentValue, enter, leave];
|
|
15046
15055
|
};
|
|
15047
15056
|
|
|
15057
|
+
/**
|
|
15058
|
+
* Stores a named value in the browser's document state and returns it reactively.
|
|
15059
|
+
* The component re-renders whenever the value changes (navigation, back/forward button).
|
|
15060
|
+
*
|
|
15061
|
+
* @param {string} id
|
|
15062
|
+
* Unique key used to store the value in document state. Must be stable across renders.
|
|
15063
|
+
*
|
|
15064
|
+
* @param {*} initialValue
|
|
15065
|
+
* Value returned when `id` is absent from document state (e.g. before enter() is called).
|
|
15066
|
+
*
|
|
15067
|
+
* @param {object} [options]
|
|
15068
|
+
* @param {"push"|"replace"} [options.type="replace"]
|
|
15069
|
+
* Controls how enter() adds the state to browser history.
|
|
15070
|
+
* - "push": creates a new history entry — pressing the back button removes it and calls onLeave.
|
|
15071
|
+
* - "replace": updates the current history entry — no extra history entry is created.
|
|
15072
|
+
* @param {() => void} [options.onLeave]
|
|
15073
|
+
* Called when the state key disappears **externally** — e.g. the user presses the browser
|
|
15074
|
+
* back button. Not called when leave() is invoked programmatically.
|
|
15075
|
+
*
|
|
15076
|
+
* @returns {[value, enter, leave]}
|
|
15077
|
+
* - `value`: current value from document state, or `initialValue` when the key is absent.
|
|
15078
|
+
* - `enter(value = "on")`: navigate TO this state (stores `value` under `id`).
|
|
15079
|
+
* Calling without an argument stores `"on"` — the presence of the key is enough to match;
|
|
15080
|
+
* the value allows associating extra data when needed.
|
|
15081
|
+
* - `leave()`: navigate AWAY FROM this state (removes `id` from document state,
|
|
15082
|
+
* or goes back in history when `type` is "push").
|
|
15083
|
+
*/
|
|
15048
15084
|
const useNavState = useNavStateBasic;
|
|
15049
15085
|
|
|
15050
15086
|
const NEVER_SET = {};
|
|
@@ -35382,14 +35418,17 @@ const markAutofocusRestoreOnClose = (containerEl) => {
|
|
|
35382
35418
|
// meaning the focus did not yet reach the element receiving the mousedown
|
|
35383
35419
|
// as a result document.activeElement is not up-to-date (can be document.body for instance)
|
|
35384
35420
|
const getFocusedBeforeTransfer = (e) => {
|
|
35385
|
-
const initiator =
|
|
35386
|
-
|
|
35387
|
-
|
|
35388
|
-
|
|
35389
|
-
|
|
35390
|
-
|
|
35391
|
-
|
|
35392
|
-
|
|
35421
|
+
const initiator =
|
|
35422
|
+
e.detail && e.detail.eventChain ? e.detail.eventChain[0] : null;
|
|
35423
|
+
if (initiator) {
|
|
35424
|
+
if (initiator.type === "mousedown") {
|
|
35425
|
+
// if we we had let browser give focus, the element would be the one that would be focused
|
|
35426
|
+
return initiator.currentTarget;
|
|
35427
|
+
}
|
|
35428
|
+
if (initiator.type === "click") {
|
|
35429
|
+
// label use case
|
|
35430
|
+
return initiator.currentTarget;
|
|
35431
|
+
}
|
|
35393
35432
|
}
|
|
35394
35433
|
return document.activeElement;
|
|
35395
35434
|
};
|
|
@@ -35486,6 +35525,7 @@ installImportMetaCssBuild(import.meta);const css$r = /* css */`
|
|
|
35486
35525
|
margin-top: var(--dialog-top-inset, auto);
|
|
35487
35526
|
margin-bottom: auto;
|
|
35488
35527
|
flex-direction: column;
|
|
35528
|
+
transition: margin-top 0.1s ease-in-out;
|
|
35489
35529
|
}
|
|
35490
35530
|
|
|
35491
35531
|
&::backdrop {
|
|
@@ -35496,6 +35536,8 @@ installImportMetaCssBuild(import.meta);const css$r = /* css */`
|
|
|
35496
35536
|
const Dialog = props => {
|
|
35497
35537
|
import.meta.css = [css$r, "@jsenv/navi/src/popup/dialog.jsx"];
|
|
35498
35538
|
const {
|
|
35539
|
+
open: openProp = false,
|
|
35540
|
+
anchorRef,
|
|
35499
35541
|
children,
|
|
35500
35542
|
scrollTrap,
|
|
35501
35543
|
pointerTrap,
|
|
@@ -35508,11 +35550,14 @@ const Dialog = props => {
|
|
|
35508
35550
|
const debugPopup = useDebugPopup();
|
|
35509
35551
|
const debugFocus = useDebugFocus();
|
|
35510
35552
|
const autoFocusProps = useAutoFocus(ref, props.autoFocus);
|
|
35553
|
+
|
|
35554
|
+
// Tracks actual DOM state (open/closed), updated only by open() and close().
|
|
35555
|
+
// Intentionally NOT synced to openProp on every render so the useEffect guard
|
|
35556
|
+
// below can detect whether a prop change requires action.
|
|
35511
35557
|
const openedRef = useRef(false);
|
|
35512
35558
|
const [addCleanup, cleanup] = useCleanup();
|
|
35513
|
-
const open =
|
|
35514
|
-
anchor
|
|
35515
|
-
}) => {
|
|
35559
|
+
const open = e => {
|
|
35560
|
+
const anchor = anchorRef?.current ?? null;
|
|
35516
35561
|
const effectiveAnchor = anchor || document.documentElement;
|
|
35517
35562
|
debugPopup(`"${e.type}" on ${getElementSignature(e.target)} -> openDialog`);
|
|
35518
35563
|
const dialogEl = ref.current;
|
|
@@ -35581,9 +35626,7 @@ const Dialog = props => {
|
|
|
35581
35626
|
...detail
|
|
35582
35627
|
});
|
|
35583
35628
|
};
|
|
35584
|
-
const onRequestOpen =
|
|
35585
|
-
anchor
|
|
35586
|
-
}) => {
|
|
35629
|
+
const onRequestOpen = e => {
|
|
35587
35630
|
const dialogEl = ref.current;
|
|
35588
35631
|
if (!dialogEl) {
|
|
35589
35632
|
return;
|
|
@@ -35591,9 +35634,7 @@ const Dialog = props => {
|
|
|
35591
35634
|
if (openedRef.current) {
|
|
35592
35635
|
return;
|
|
35593
35636
|
}
|
|
35594
|
-
open(e
|
|
35595
|
-
anchor
|
|
35596
|
-
});
|
|
35637
|
+
open(e);
|
|
35597
35638
|
};
|
|
35598
35639
|
const onRequestClose = (e, detail = {}) => {
|
|
35599
35640
|
const dialogEl = ref.current;
|
|
@@ -35623,6 +35664,25 @@ const Dialog = props => {
|
|
|
35623
35664
|
}
|
|
35624
35665
|
close(e, detail);
|
|
35625
35666
|
};
|
|
35667
|
+
useLayoutEffect(() => {
|
|
35668
|
+
if (openProp === undefined) {
|
|
35669
|
+
return;
|
|
35670
|
+
}
|
|
35671
|
+
// Skip when the popover is already in the desired state.
|
|
35672
|
+
// This avoids a feedback loop: our own close/open dispatches navi_close/navi_open,
|
|
35673
|
+
// the parent updates the prop, and the effect would fire again for a change we
|
|
35674
|
+
// already handled. Comparing against openedRef is the authoritative check because
|
|
35675
|
+
// it reflects the actual DOM state, not the React render cycle.
|
|
35676
|
+
if (openProp === openedRef.current) {
|
|
35677
|
+
return;
|
|
35678
|
+
}
|
|
35679
|
+
const e = new CustomEvent("open_prop_change");
|
|
35680
|
+
if (openProp) {
|
|
35681
|
+
onRequestOpen(e);
|
|
35682
|
+
} else {
|
|
35683
|
+
onRequestClose(e);
|
|
35684
|
+
}
|
|
35685
|
+
}, [openProp]);
|
|
35626
35686
|
return jsx(Box, {
|
|
35627
35687
|
...rest,
|
|
35628
35688
|
...autoFocusProps,
|
|
@@ -35652,12 +35712,7 @@ const Dialog = props => {
|
|
|
35652
35712
|
onRequestClose(e);
|
|
35653
35713
|
},
|
|
35654
35714
|
onnavi_request_open: e => {
|
|
35655
|
-
|
|
35656
|
-
anchor
|
|
35657
|
-
} = e.detail;
|
|
35658
|
-
onRequestOpen(e, {
|
|
35659
|
-
anchor
|
|
35660
|
-
});
|
|
35715
|
+
onRequestOpen(e);
|
|
35661
35716
|
},
|
|
35662
35717
|
onnavi_request_close: e => {
|
|
35663
35718
|
onRequestClose(e);
|
|
@@ -35692,6 +35747,8 @@ installImportMetaCssBuild(import.meta);const css$q = /* css */`
|
|
|
35692
35747
|
const Popover = props => {
|
|
35693
35748
|
import.meta.css = [css$q, "@jsenv/navi/src/popup/popover.jsx"];
|
|
35694
35749
|
const {
|
|
35750
|
+
open: openProp = false,
|
|
35751
|
+
anchorRef,
|
|
35695
35752
|
scrollTrap,
|
|
35696
35753
|
pointerTrap,
|
|
35697
35754
|
focusTrap,
|
|
@@ -35712,18 +35769,19 @@ const Popover = props => {
|
|
|
35712
35769
|
const debugPopup = useDebugPopup();
|
|
35713
35770
|
const debugFocus = useDebugFocus();
|
|
35714
35771
|
const autoFocusProps = useAutoFocus(ref, props.autoFocus);
|
|
35715
|
-
|
|
35716
|
-
|
|
35717
|
-
|
|
35772
|
+
|
|
35773
|
+
// Tracks actual DOM state (open/closed), updated only by open() and close().
|
|
35774
|
+
// Intentionally NOT synced to openProp on every render so the useEffect guard
|
|
35775
|
+
// below can detect whether a prop change requires action.
|
|
35776
|
+
const openedRef = useRef(false);
|
|
35718
35777
|
const [addCleanup, cleanup] = useCleanup();
|
|
35719
|
-
const open =
|
|
35720
|
-
anchor
|
|
35721
|
-
}) => {
|
|
35778
|
+
const open = e => {
|
|
35722
35779
|
debugPopup(e, `openPopover()`);
|
|
35723
35780
|
const popoverEl = ref.current;
|
|
35724
35781
|
const focusedBeforeOpen = getFocusedBeforeTransfer(e);
|
|
35725
35782
|
popoverEl.showPopover();
|
|
35726
35783
|
transferFocus(popoverEl, debugFocus, e, focusedBeforeOpen);
|
|
35784
|
+
const anchor = anchorRef?.current ?? null;
|
|
35727
35785
|
const effectiveAnchor = anchor || document.documentElement;
|
|
35728
35786
|
const positionPopover = positionEvent => {
|
|
35729
35787
|
const {
|
|
@@ -35796,7 +35854,6 @@ const Popover = props => {
|
|
|
35796
35854
|
rectEffect.disconnect();
|
|
35797
35855
|
});
|
|
35798
35856
|
openedRef.current = true;
|
|
35799
|
-
setOpened(true);
|
|
35800
35857
|
dispatchCustomEvent(popoverEl, "navi_open", {
|
|
35801
35858
|
event: e,
|
|
35802
35859
|
focusedBeforeOpen
|
|
@@ -35809,15 +35866,12 @@ const Popover = props => {
|
|
|
35809
35866
|
popoverEl.hidePopover();
|
|
35810
35867
|
cleanup();
|
|
35811
35868
|
openedRef.current = false;
|
|
35812
|
-
setOpened(false);
|
|
35813
35869
|
dispatchCustomEvent(popoverEl, "navi_close", {
|
|
35814
35870
|
event: e,
|
|
35815
35871
|
...detail
|
|
35816
35872
|
});
|
|
35817
35873
|
};
|
|
35818
|
-
const onRequestOpen =
|
|
35819
|
-
anchor
|
|
35820
|
-
}) => {
|
|
35874
|
+
const onRequestOpen = e => {
|
|
35821
35875
|
const popoverEl = ref.current;
|
|
35822
35876
|
if (!popoverEl) {
|
|
35823
35877
|
return;
|
|
@@ -35825,9 +35879,7 @@ const Popover = props => {
|
|
|
35825
35879
|
if (openedRef.current) {
|
|
35826
35880
|
return;
|
|
35827
35881
|
}
|
|
35828
|
-
open(e
|
|
35829
|
-
anchor
|
|
35830
|
-
});
|
|
35882
|
+
open(e);
|
|
35831
35883
|
};
|
|
35832
35884
|
const onRequestClose = (e, detail = {}) => {
|
|
35833
35885
|
const popoverEl = ref.current;
|
|
@@ -35857,6 +35909,27 @@ const Popover = props => {
|
|
|
35857
35909
|
}
|
|
35858
35910
|
close(e, detail);
|
|
35859
35911
|
};
|
|
35912
|
+
useLayoutEffect(() => {
|
|
35913
|
+
if (openProp === undefined) {
|
|
35914
|
+
return;
|
|
35915
|
+
}
|
|
35916
|
+
// Skip when the popover is already in the desired state.
|
|
35917
|
+
// This avoids a feedback loop: our own close/open dispatches navi_close/navi_open,
|
|
35918
|
+
// the parent updates the prop, and the effect would fire again for a change we
|
|
35919
|
+
// already handled. Comparing against openedRef is the authoritative check because
|
|
35920
|
+
// it reflects the actual DOM state, not the React render cycle.
|
|
35921
|
+
if (openProp === openedRef.current) {
|
|
35922
|
+
return;
|
|
35923
|
+
}
|
|
35924
|
+
const e = new CustomEvent("open_prop_change", {
|
|
35925
|
+
detail: {}
|
|
35926
|
+
});
|
|
35927
|
+
if (openProp) {
|
|
35928
|
+
onRequestOpen(e);
|
|
35929
|
+
} else {
|
|
35930
|
+
onRequestClose(e);
|
|
35931
|
+
}
|
|
35932
|
+
}, [openProp]);
|
|
35860
35933
|
return jsxs(Box, {
|
|
35861
35934
|
id: id,
|
|
35862
35935
|
popover: "manual",
|
|
@@ -35866,12 +35939,7 @@ const Popover = props => {
|
|
|
35866
35939
|
baseClassName: "navi_popover",
|
|
35867
35940
|
pseudoClasses: POPOVER_PSEUDO_CLASSES,
|
|
35868
35941
|
onnavi_request_open: e => {
|
|
35869
|
-
|
|
35870
|
-
anchor
|
|
35871
|
-
} = e.detail;
|
|
35872
|
-
onRequestOpen(e, {
|
|
35873
|
-
anchor
|
|
35874
|
-
});
|
|
35942
|
+
onRequestOpen(e);
|
|
35875
35943
|
},
|
|
35876
35944
|
onnavi_request_close: e => {
|
|
35877
35945
|
onRequestClose(e);
|
|
@@ -36177,8 +36245,9 @@ const PickerCustom = props => {
|
|
|
36177
36245
|
const popupRef = useRef(null);
|
|
36178
36246
|
popupProps.ref = popupRef;
|
|
36179
36247
|
// aria-controls + id
|
|
36248
|
+
const autoId = useId();
|
|
36249
|
+
const popupId = `picker_popup_${autoId}`;
|
|
36180
36250
|
{
|
|
36181
|
-
const popupId = useId();
|
|
36182
36251
|
Object.assign(pickerProps, {
|
|
36183
36252
|
"aria-controls": popupId
|
|
36184
36253
|
});
|
|
@@ -36190,14 +36259,27 @@ const PickerCustom = props => {
|
|
|
36190
36259
|
{
|
|
36191
36260
|
const debugFocus = useDebugFocus();
|
|
36192
36261
|
const debugPopup = useDebugPopup();
|
|
36193
|
-
|
|
36194
|
-
|
|
36195
|
-
|
|
36262
|
+
// In "dialog" mode, enterExpanded() pushes a history entry so the back button closes.
|
|
36263
|
+
// In "popover" mode, it replaces the current history state (no history entry added).
|
|
36264
|
+
const pickerNavType = mode === "dialog" ? "push" : "replace";
|
|
36265
|
+
const expandedRef = useRef(false);
|
|
36266
|
+
const [expanded, enterExpanded, leaveExpanded] = useNavState(popupId, false, {
|
|
36267
|
+
type: pickerNavType,
|
|
36268
|
+
// onLeave fires only when the state key disappears externally (back button/gesture most of the time).
|
|
36269
|
+
onLeave: () => {
|
|
36270
|
+
requestClose(new CustomEvent("navi_nav_away", {
|
|
36271
|
+
detail: {}
|
|
36272
|
+
}), {
|
|
36273
|
+
isCancel: true
|
|
36274
|
+
});
|
|
36275
|
+
}
|
|
36276
|
+
});
|
|
36277
|
+
expandedRef.current = Boolean(expanded);
|
|
36196
36278
|
const valueAtOpenRef = useRef(null);
|
|
36197
36279
|
const activeElementAtOpenRef = useRef(null);
|
|
36198
36280
|
const onOpen = e => {
|
|
36199
36281
|
expandedRef.current = true;
|
|
36200
|
-
|
|
36282
|
+
enterExpanded();
|
|
36201
36283
|
const focusedBeforeOpen = e.detail.focusedBeforeOpen;
|
|
36202
36284
|
activeElementAtOpenRef.current = focusedBeforeOpen;
|
|
36203
36285
|
debugFocus(e, "picked opened, store element focused", focusedBeforeOpen);
|
|
@@ -36240,7 +36322,7 @@ const PickerCustom = props => {
|
|
|
36240
36322
|
}
|
|
36241
36323
|
}
|
|
36242
36324
|
expandedRef.current = false;
|
|
36243
|
-
|
|
36325
|
+
leaveExpanded();
|
|
36244
36326
|
// Reset so the next opening re-evaluates screen size
|
|
36245
36327
|
defaultModeRef.current = null;
|
|
36246
36328
|
restoreFocus(e);
|
|
@@ -36256,8 +36338,7 @@ const PickerCustom = props => {
|
|
|
36256
36338
|
});
|
|
36257
36339
|
const popupEl = popupRef.current;
|
|
36258
36340
|
return dispatchCustomEvent(popupEl, "navi_request_open", {
|
|
36259
|
-
event: e
|
|
36260
|
-
anchor: pickerEl
|
|
36341
|
+
event: e
|
|
36261
36342
|
});
|
|
36262
36343
|
};
|
|
36263
36344
|
const requestClose = (e = new CustomEvent("programmatic"), {
|
|
@@ -36278,7 +36359,7 @@ const PickerCustom = props => {
|
|
|
36278
36359
|
uiAction: uiActionProp
|
|
36279
36360
|
} = props;
|
|
36280
36361
|
Object.assign(pickerProps, {
|
|
36281
|
-
"aria-expanded": expanded,
|
|
36362
|
+
"aria-expanded": Boolean(expanded),
|
|
36282
36363
|
"onActionStart": e => {
|
|
36283
36364
|
onActionStart?.(e);
|
|
36284
36365
|
// requestClose(e);
|
|
@@ -36309,6 +36390,8 @@ const PickerCustom = props => {
|
|
|
36309
36390
|
children
|
|
36310
36391
|
});
|
|
36311
36392
|
Object.assign(popupProps, {
|
|
36393
|
+
anchorRef: props.ref,
|
|
36394
|
+
open: Boolean(expanded),
|
|
36312
36395
|
closeRequestHandler: (requestCloseEvent, closePermission, {
|
|
36313
36396
|
isClickOutside
|
|
36314
36397
|
} = {}) => {
|
|
@@ -47089,5 +47172,5 @@ const UserSvg = () => jsx("svg", {
|
|
|
47089
47172
|
})
|
|
47090
47173
|
});
|
|
47091
47174
|
|
|
47092
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Box, Button, ButtonCopyToClipboard, Caption, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, Details, Dialog, DialogLayout, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, Paragraph, Picker, Popover, Quantity, RadioGroup, Route, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Thead, Time, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout,
|
|
47175
|
+
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Box, Button, ButtonCopyToClipboard, Caption, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, Details, Dialog, DialogLayout, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, Paragraph, Picker, Popover, Quantity, RadioGroup, Route, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Thead, Time, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, compareTwoJsValues, createAction, createAvailableConstraint, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isToday, langSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, setBaseUrl, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSidePanelClose, useSignalSync, useStateArray, useTitleLevel, useUrlSearchParam, valueInLocalStorage, windowWidthSignal };
|
|
47093
47176
|
//# sourceMappingURL=jsenv_navi.js.map
|