@ionic/react-router 8.8.1-dev.11773676615.1d6c4cf7 → 8.8.1-dev.11773873479.192398dc
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/index.js +93 -17
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1540,9 +1540,19 @@ class StackManager extends React.PureComponent {
|
|
|
1540
1540
|
clearTimeout(this.outOfScopeUnmountTimeout);
|
|
1541
1541
|
this.outOfScopeUnmountTimeout = undefined;
|
|
1542
1542
|
}
|
|
1543
|
+
// Remove view items from the stack but do NOT apply ion-page-hidden.
|
|
1544
|
+
// ion-page-hidden sets display:none which immediately removes content
|
|
1545
|
+
// from the layout, causing the parent outlet's leaving page to flash
|
|
1546
|
+
// blank during its transition animation (issue #25477).
|
|
1547
|
+
//
|
|
1548
|
+
// Removing from the stack triggers React reconciliation via forceUpdate,
|
|
1549
|
+
// which removes the DOM elements. React batches this re-render after all
|
|
1550
|
+
// componentDidUpdate calls in the current cycle, so the parent outlet's
|
|
1551
|
+
// commit() captures the current DOM state (with content visible) before
|
|
1552
|
+
// React processes the removal. The compositor's cached layer is unaffected
|
|
1553
|
+
// by subsequent DOM changes during the animation.
|
|
1543
1554
|
const allViewsInOutlet = this.context.getViewItemsForOutlet(this.id);
|
|
1544
1555
|
allViewsInOutlet.forEach((viewItem) => {
|
|
1545
|
-
hideIonPageElement(viewItem.ionPageElement);
|
|
1546
1556
|
this.context.unMountViewItem(viewItem);
|
|
1547
1557
|
});
|
|
1548
1558
|
this.forceUpdate();
|
|
@@ -1782,13 +1792,15 @@ class StackManager extends React.PureComponent {
|
|
|
1782
1792
|
}
|
|
1783
1793
|
/**
|
|
1784
1794
|
* Determines whether to skip the transition animation and, if so, immediately
|
|
1785
|
-
* hides the leaving view with inline `
|
|
1795
|
+
* hides the leaving view with inline `visibility:hidden`.
|
|
1786
1796
|
*
|
|
1787
|
-
* Skips transitions
|
|
1788
|
-
*
|
|
1789
|
-
*
|
|
1790
|
-
*
|
|
1791
|
-
*
|
|
1797
|
+
* Skips transitions only for outlets nested inside a parent IonPage's content
|
|
1798
|
+
* area (i.e., an ion-content sits between the outlet and the .ion-page). These
|
|
1799
|
+
* outlets render child pages inside a parent page's scrollable area, and the MD
|
|
1800
|
+
* animation shows both entering and leaving pages simultaneously — causing text
|
|
1801
|
+
* overlap and nested scrollbars. Standard page-level outlets (tabs, routing,
|
|
1802
|
+
* swipe-to-go-back) animate normally even though they sit inside a framework-
|
|
1803
|
+
* managed .ion-page wrapper from the parent outlet's view stack.
|
|
1792
1804
|
*
|
|
1793
1805
|
* Uses inline visibility:hidden rather than ion-page-hidden class because
|
|
1794
1806
|
* core's beforeTransition() removes ion-page-hidden via setPageHidden().
|
|
@@ -1798,9 +1810,23 @@ class StackManager extends React.PureComponent {
|
|
|
1798
1810
|
* can resolve normally.
|
|
1799
1811
|
*/
|
|
1800
1812
|
applySkipAnimationIfNeeded(enteringViewItem, leavingViewItem) {
|
|
1801
|
-
var _a;
|
|
1802
|
-
|
|
1803
|
-
|
|
1813
|
+
var _a, _b;
|
|
1814
|
+
// Only skip for outlets genuinely nested inside a page's content area.
|
|
1815
|
+
// Walk from the outlet up to the nearest .ion-page; if an ion-content
|
|
1816
|
+
// sits in between, the outlet is inside scrollable page content and
|
|
1817
|
+
// animating would cause overlapping pages with duplicate scrollbars.
|
|
1818
|
+
let isInsidePageContent = false;
|
|
1819
|
+
let el = (_b = (_a = this.routerOutletElement) === null || _a === void 0 ? void 0 : _a.parentElement) !== null && _b !== void 0 ? _b : null;
|
|
1820
|
+
while (el) {
|
|
1821
|
+
if (el.classList.contains('ion-page'))
|
|
1822
|
+
break;
|
|
1823
|
+
if (el.tagName === 'ION-CONTENT') {
|
|
1824
|
+
isInsidePageContent = true;
|
|
1825
|
+
break;
|
|
1826
|
+
}
|
|
1827
|
+
el = el.parentElement;
|
|
1828
|
+
}
|
|
1829
|
+
const shouldSkip = isInsidePageContent && !!leavingViewItem && enteringViewItem !== leavingViewItem;
|
|
1804
1830
|
if (shouldSkip && (leavingViewItem === null || leavingViewItem === void 0 ? void 0 : leavingViewItem.ionPageElement)) {
|
|
1805
1831
|
leavingViewItem.ionPageElement.style.setProperty('visibility', 'hidden');
|
|
1806
1832
|
leavingViewItem.ionPageElement.setAttribute('aria-hidden', 'true');
|
|
@@ -2596,6 +2622,27 @@ const filterUndefinedParams = (params) => {
|
|
|
2596
2622
|
}
|
|
2597
2623
|
return result;
|
|
2598
2624
|
};
|
|
2625
|
+
/**
|
|
2626
|
+
* Checks if a POP event is a multi-step back navigation (navigate(-n) where n > 1).
|
|
2627
|
+
* Walks the pushedByRoute chain from prevInfo to verify the destination is an ancestor
|
|
2628
|
+
* in the same navigation chain. This distinguishes multi-step back from tab-crossing
|
|
2629
|
+
* back navigation where prevInfo.pathname also differs from the browser destination.
|
|
2630
|
+
*/
|
|
2631
|
+
const checkIsMultiStepBack = (prevInfo, destinationPathname, history) => {
|
|
2632
|
+
if (!prevInfo || prevInfo.pathname === destinationPathname)
|
|
2633
|
+
return false;
|
|
2634
|
+
const visited = new Set();
|
|
2635
|
+
let walker = prevInfo;
|
|
2636
|
+
while (walker === null || walker === void 0 ? void 0 : walker.pushedByRoute) {
|
|
2637
|
+
if (visited.has(walker.id))
|
|
2638
|
+
break; // cycle guard
|
|
2639
|
+
visited.add(walker.id);
|
|
2640
|
+
if (walker.pushedByRoute === destinationPathname)
|
|
2641
|
+
return true;
|
|
2642
|
+
walker = history.findLastLocation(walker);
|
|
2643
|
+
}
|
|
2644
|
+
return false;
|
|
2645
|
+
};
|
|
2599
2646
|
const areParamsEqual = (a, b) => {
|
|
2600
2647
|
const paramsA = a || {};
|
|
2601
2648
|
const paramsB = b || {};
|
|
@@ -2769,7 +2816,22 @@ const IonRouter = ({ children, registerHistoryListener }) => {
|
|
|
2769
2816
|
// Back navigation. Record current location key for potential forward
|
|
2770
2817
|
forwardStack.current.push(currentLocationKeyRef.current);
|
|
2771
2818
|
const prevInfo = locationHistory.current.findLastLocation(currentRoute);
|
|
2772
|
-
|
|
2819
|
+
const isMultiStepBack = checkIsMultiStepBack(prevInfo, location.pathname, locationHistory.current);
|
|
2820
|
+
if (isMultiStepBack) {
|
|
2821
|
+
const destinationInfo = locationHistory.current.findLastLocationByPathname(location.pathname);
|
|
2822
|
+
incomingRouteParams.current = Object.assign(Object.assign({}, (destinationInfo || {})), { routeAction: 'pop', routeDirection: 'back' });
|
|
2823
|
+
}
|
|
2824
|
+
else if (prevInfo && prevInfo.pathname !== location.pathname && currentRoute.tab) {
|
|
2825
|
+
// Browser POP destination differs from within-tab back target.
|
|
2826
|
+
// Sync URL via replace, like handleNavigateBack's non-linear path (#25141).
|
|
2827
|
+
incomingRouteParams.current = Object.assign(Object.assign({}, prevInfo), { routeAction: 'pop', routeDirection: 'back' });
|
|
2828
|
+
forwardStack.current = [];
|
|
2829
|
+
handleNavigate(prevInfo.pathname + (prevInfo.search || ''), 'pop', 'back', undefined, undefined, prevInfo.tab);
|
|
2830
|
+
return;
|
|
2831
|
+
}
|
|
2832
|
+
else {
|
|
2833
|
+
incomingRouteParams.current = Object.assign(Object.assign({}, prevInfo), { routeAction: 'pop', routeDirection: 'back' });
|
|
2834
|
+
}
|
|
2773
2835
|
}
|
|
2774
2836
|
else {
|
|
2775
2837
|
// It's a non-linear history path like a direct link.
|
|
@@ -2825,9 +2887,19 @@ const IonRouter = ({ children, registerHistoryListener }) => {
|
|
|
2825
2887
|
// This preserves tab context for same-tab navigation while allowing cross-tab navigation.
|
|
2826
2888
|
routeInfo.tab = routeInfo.tab || leavingLocationInfo.tab;
|
|
2827
2889
|
routeInfo.pushedByRoute = leavingLocationInfo.pathname;
|
|
2828
|
-
|
|
2890
|
+
}
|
|
2891
|
+
else if (routeInfo.routeAction === 'push' &&
|
|
2892
|
+
routeInfo.routeDirection === 'none' &&
|
|
2893
|
+
routeInfo.tab === leavingLocationInfo.tab) {
|
|
2894
|
+
// Push with routerDirection="none" within the same tab (or non-tab) context.
|
|
2895
|
+
// Still needs pushedByRoute so the back button can navigate back correctly.
|
|
2896
|
+
// Cross-tab navigations with direction "none" are handled by the tab-switching
|
|
2897
|
+
// block below which has different pushedByRoute semantics.
|
|
2898
|
+
routeInfo.tab = routeInfo.tab || leavingLocationInfo.tab;
|
|
2899
|
+
routeInfo.pushedByRoute = leavingLocationInfo.pathname;
|
|
2829
2900
|
}
|
|
2830
2901
|
else if (routeInfo.routeAction === 'pop') {
|
|
2902
|
+
// Triggered by a browser back button or handleNavigateBack.
|
|
2831
2903
|
// Find the route that pushed this one.
|
|
2832
2904
|
const r = locationHistory.current.findLastLocation(routeInfo);
|
|
2833
2905
|
routeInfo.pushedByRoute = r === null || r === void 0 ? void 0 : r.pushedByRoute;
|
|
@@ -2900,8 +2972,10 @@ const IonRouter = ({ children, registerHistoryListener }) => {
|
|
|
2900
2972
|
const handleResetTab = (tab, originalHref, originalRouteOptions) => {
|
|
2901
2973
|
const routeInfo = locationHistory.current.getFirstRouteInfoForTab(tab);
|
|
2902
2974
|
if (routeInfo) {
|
|
2975
|
+
const [pathname, search] = originalHref.split('?');
|
|
2903
2976
|
const newRouteInfo = Object.assign({}, routeInfo);
|
|
2904
|
-
newRouteInfo.pathname =
|
|
2977
|
+
newRouteInfo.pathname = pathname;
|
|
2978
|
+
newRouteInfo.search = search ? '?' + search : '';
|
|
2905
2979
|
newRouteInfo.routeOptions = originalRouteOptions;
|
|
2906
2980
|
incomingRouteParams.current = Object.assign(Object.assign({}, newRouteInfo), { routeAction: 'pop', routeDirection: 'back' });
|
|
2907
2981
|
navigate(newRouteInfo.pathname + (newRouteInfo.search || ''));
|
|
@@ -2928,21 +3002,23 @@ const IonRouter = ({ children, registerHistoryListener }) => {
|
|
|
2928
3002
|
* e.g., `/tabs/home` → `/tabs/home`
|
|
2929
3003
|
*/
|
|
2930
3004
|
if (routeInfo.pathname === pathname) {
|
|
2931
|
-
|
|
2932
|
-
|
|
3005
|
+
const newSearch = search ? '?' + search : routeInfo.search;
|
|
3006
|
+
incomingRouteParams.current = Object.assign(Object.assign({}, routeParams), { search: newSearch || '', routeOptions });
|
|
3007
|
+
navigate(routeInfo.pathname + (newSearch || ''));
|
|
2933
3008
|
/**
|
|
2934
3009
|
* User is navigating to a different tab.
|
|
2935
3010
|
* e.g., `/tabs/home` → `/tabs/settings`
|
|
2936
3011
|
*/
|
|
2937
3012
|
}
|
|
2938
3013
|
else {
|
|
2939
|
-
incomingRouteParams.current = Object.assign(Object.assign({}, routeParams), { pathname, search: search ? '?' + search :
|
|
3014
|
+
incomingRouteParams.current = Object.assign(Object.assign({}, routeParams), { pathname, search: search ? '?' + search : '', routeOptions });
|
|
2940
3015
|
navigate(pathname + (search ? '?' + search : ''));
|
|
2941
3016
|
}
|
|
2942
3017
|
// User has not navigated to this tab before.
|
|
2943
3018
|
}
|
|
2944
3019
|
else {
|
|
2945
|
-
|
|
3020
|
+
const fullPath = pathname + (search ? '?' + search : '');
|
|
3021
|
+
handleNavigate(fullPath, 'push', 'none', undefined, routeOptions, tab);
|
|
2946
3022
|
}
|
|
2947
3023
|
};
|
|
2948
3024
|
/**
|