@ionic/react-router 8.8.1-dev.11773665268.1511a6c7 → 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 +94 -24
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -701,15 +701,9 @@ class ReactRouterViewStack extends ViewStacks {
|
|
|
701
701
|
if (existingIsIndexRoute && newIsIndexRoute) {
|
|
702
702
|
return true;
|
|
703
703
|
}
|
|
704
|
-
// Reuse empty-path routes (path="") that are not index routes.
|
|
705
|
-
// These function like index routes and should be reused to prevent
|
|
706
|
-
// duplicate views when navigating back to the default path.
|
|
707
|
-
if (existingPath === '' && routePath === '' && !existingIsIndexRoute && !newIsIndexRoute) {
|
|
708
|
-
return true;
|
|
709
|
-
}
|
|
710
704
|
// Reuse view items with the same path
|
|
711
705
|
// Special case: reuse tabs/* and other specific wildcard routes
|
|
712
|
-
// Don't reuse generic catch-all wildcards (*)
|
|
706
|
+
// Don't reuse index routes (empty path) or generic catch-all wildcards (*)
|
|
713
707
|
if (existingPath === routePath && existingPath !== '' && existingPath !== '*') {
|
|
714
708
|
// Parameterized routes need pathname matching to ensure /details/1 and /details/2
|
|
715
709
|
// get separate view items. For wildcard routes (e.g., user/:userId/*), compare
|
|
@@ -1546,9 +1540,19 @@ class StackManager extends React.PureComponent {
|
|
|
1546
1540
|
clearTimeout(this.outOfScopeUnmountTimeout);
|
|
1547
1541
|
this.outOfScopeUnmountTimeout = undefined;
|
|
1548
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.
|
|
1549
1554
|
const allViewsInOutlet = this.context.getViewItemsForOutlet(this.id);
|
|
1550
1555
|
allViewsInOutlet.forEach((viewItem) => {
|
|
1551
|
-
hideIonPageElement(viewItem.ionPageElement);
|
|
1552
1556
|
this.context.unMountViewItem(viewItem);
|
|
1553
1557
|
});
|
|
1554
1558
|
this.forceUpdate();
|
|
@@ -1788,13 +1792,15 @@ class StackManager extends React.PureComponent {
|
|
|
1788
1792
|
}
|
|
1789
1793
|
/**
|
|
1790
1794
|
* Determines whether to skip the transition animation and, if so, immediately
|
|
1791
|
-
* hides the leaving view with inline `
|
|
1795
|
+
* hides the leaving view with inline `visibility:hidden`.
|
|
1792
1796
|
*
|
|
1793
|
-
* Skips transitions
|
|
1794
|
-
*
|
|
1795
|
-
*
|
|
1796
|
-
*
|
|
1797
|
-
*
|
|
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.
|
|
1798
1804
|
*
|
|
1799
1805
|
* Uses inline visibility:hidden rather than ion-page-hidden class because
|
|
1800
1806
|
* core's beforeTransition() removes ion-page-hidden via setPageHidden().
|
|
@@ -1804,9 +1810,23 @@ class StackManager extends React.PureComponent {
|
|
|
1804
1810
|
* can resolve normally.
|
|
1805
1811
|
*/
|
|
1806
1812
|
applySkipAnimationIfNeeded(enteringViewItem, leavingViewItem) {
|
|
1807
|
-
var _a;
|
|
1808
|
-
|
|
1809
|
-
|
|
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;
|
|
1810
1830
|
if (shouldSkip && (leavingViewItem === null || leavingViewItem === void 0 ? void 0 : leavingViewItem.ionPageElement)) {
|
|
1811
1831
|
leavingViewItem.ionPageElement.style.setProperty('visibility', 'hidden');
|
|
1812
1832
|
leavingViewItem.ionPageElement.setAttribute('aria-hidden', 'true');
|
|
@@ -2602,6 +2622,27 @@ const filterUndefinedParams = (params) => {
|
|
|
2602
2622
|
}
|
|
2603
2623
|
return result;
|
|
2604
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
|
+
};
|
|
2605
2646
|
const areParamsEqual = (a, b) => {
|
|
2606
2647
|
const paramsA = a || {};
|
|
2607
2648
|
const paramsB = b || {};
|
|
@@ -2775,7 +2816,22 @@ const IonRouter = ({ children, registerHistoryListener }) => {
|
|
|
2775
2816
|
// Back navigation. Record current location key for potential forward
|
|
2776
2817
|
forwardStack.current.push(currentLocationKeyRef.current);
|
|
2777
2818
|
const prevInfo = locationHistory.current.findLastLocation(currentRoute);
|
|
2778
|
-
|
|
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
|
+
}
|
|
2779
2835
|
}
|
|
2780
2836
|
else {
|
|
2781
2837
|
// It's a non-linear history path like a direct link.
|
|
@@ -2831,9 +2887,19 @@ const IonRouter = ({ children, registerHistoryListener }) => {
|
|
|
2831
2887
|
// This preserves tab context for same-tab navigation while allowing cross-tab navigation.
|
|
2832
2888
|
routeInfo.tab = routeInfo.tab || leavingLocationInfo.tab;
|
|
2833
2889
|
routeInfo.pushedByRoute = leavingLocationInfo.pathname;
|
|
2834
|
-
|
|
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;
|
|
2835
2900
|
}
|
|
2836
2901
|
else if (routeInfo.routeAction === 'pop') {
|
|
2902
|
+
// Triggered by a browser back button or handleNavigateBack.
|
|
2837
2903
|
// Find the route that pushed this one.
|
|
2838
2904
|
const r = locationHistory.current.findLastLocation(routeInfo);
|
|
2839
2905
|
routeInfo.pushedByRoute = r === null || r === void 0 ? void 0 : r.pushedByRoute;
|
|
@@ -2906,8 +2972,10 @@ const IonRouter = ({ children, registerHistoryListener }) => {
|
|
|
2906
2972
|
const handleResetTab = (tab, originalHref, originalRouteOptions) => {
|
|
2907
2973
|
const routeInfo = locationHistory.current.getFirstRouteInfoForTab(tab);
|
|
2908
2974
|
if (routeInfo) {
|
|
2975
|
+
const [pathname, search] = originalHref.split('?');
|
|
2909
2976
|
const newRouteInfo = Object.assign({}, routeInfo);
|
|
2910
|
-
newRouteInfo.pathname =
|
|
2977
|
+
newRouteInfo.pathname = pathname;
|
|
2978
|
+
newRouteInfo.search = search ? '?' + search : '';
|
|
2911
2979
|
newRouteInfo.routeOptions = originalRouteOptions;
|
|
2912
2980
|
incomingRouteParams.current = Object.assign(Object.assign({}, newRouteInfo), { routeAction: 'pop', routeDirection: 'back' });
|
|
2913
2981
|
navigate(newRouteInfo.pathname + (newRouteInfo.search || ''));
|
|
@@ -2934,21 +3002,23 @@ const IonRouter = ({ children, registerHistoryListener }) => {
|
|
|
2934
3002
|
* e.g., `/tabs/home` → `/tabs/home`
|
|
2935
3003
|
*/
|
|
2936
3004
|
if (routeInfo.pathname === pathname) {
|
|
2937
|
-
|
|
2938
|
-
|
|
3005
|
+
const newSearch = search ? '?' + search : routeInfo.search;
|
|
3006
|
+
incomingRouteParams.current = Object.assign(Object.assign({}, routeParams), { search: newSearch || '', routeOptions });
|
|
3007
|
+
navigate(routeInfo.pathname + (newSearch || ''));
|
|
2939
3008
|
/**
|
|
2940
3009
|
* User is navigating to a different tab.
|
|
2941
3010
|
* e.g., `/tabs/home` → `/tabs/settings`
|
|
2942
3011
|
*/
|
|
2943
3012
|
}
|
|
2944
3013
|
else {
|
|
2945
|
-
incomingRouteParams.current = Object.assign(Object.assign({}, routeParams), { pathname, search: search ? '?' + search :
|
|
3014
|
+
incomingRouteParams.current = Object.assign(Object.assign({}, routeParams), { pathname, search: search ? '?' + search : '', routeOptions });
|
|
2946
3015
|
navigate(pathname + (search ? '?' + search : ''));
|
|
2947
3016
|
}
|
|
2948
3017
|
// User has not navigated to this tab before.
|
|
2949
3018
|
}
|
|
2950
3019
|
else {
|
|
2951
|
-
|
|
3020
|
+
const fullPath = pathname + (search ? '?' + search : '');
|
|
3021
|
+
handleNavigate(fullPath, 'push', 'none', undefined, routeOptions, tab);
|
|
2952
3022
|
}
|
|
2953
3023
|
};
|
|
2954
3024
|
/**
|