@ionic/react-router 9.0.8-dev.11791471364.170c7cdb → 9.0.8-dev.11791991772.1a974f6b
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
CHANGED
|
@@ -8,6 +8,30 @@ const IonRouteInner = ({ path, index, caseSensitive, element }) => {
|
|
|
8
8
|
return React.createElement(Route, { path: path, index: index, caseSensitive: caseSensitive, element: element });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Compares two route param objects by value, treating a missing object as empty.
|
|
13
|
+
*/
|
|
14
|
+
const areParamsEqual = (a, b) => {
|
|
15
|
+
const paramsA = a || {};
|
|
16
|
+
const paramsB = b || {};
|
|
17
|
+
const keysA = Object.keys(paramsA);
|
|
18
|
+
const keysB = Object.keys(paramsB);
|
|
19
|
+
if (keysA.length !== keysB.length) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return keysA.every((key) => {
|
|
23
|
+
const valueA = paramsA[key];
|
|
24
|
+
const valueB = paramsB[key];
|
|
25
|
+
if (Array.isArray(valueA) && Array.isArray(valueB)) {
|
|
26
|
+
if (valueA.length !== valueB.length) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return valueA.every((entry, idx) => entry === valueB[idx]);
|
|
30
|
+
}
|
|
31
|
+
return valueA === valueB;
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
11
35
|
/**
|
|
12
36
|
* The matchPath function is used only for matching paths, not rendering components or elements.
|
|
13
37
|
* @see https://reactrouter.com/v6/utils/match-path
|
|
@@ -694,6 +718,40 @@ const resolveIndexRouteMatch = (viewItem, pathname, parentPath) => {
|
|
|
694
718
|
const normalizedBase = normalizePathnameForComparison(previousMatch.pathnameBase || previousMatch.pathname || '');
|
|
695
719
|
return normalizedPathname === normalizedBase ? previousMatch : null;
|
|
696
720
|
};
|
|
721
|
+
const shallowEqual = (a, b) => {
|
|
722
|
+
const aRecord = a;
|
|
723
|
+
const bRecord = b;
|
|
724
|
+
const aKeys = Object.keys(aRecord);
|
|
725
|
+
return aKeys.length === Object.keys(bRecord).length && aKeys.every((key) => aRecord[key] === bRecord[key]);
|
|
726
|
+
};
|
|
727
|
+
const isSameMatch = (a, b) => a === b ||
|
|
728
|
+
(a.pathname === b.pathname &&
|
|
729
|
+
a.pathnameBase === b.pathnameBase &&
|
|
730
|
+
areParamsEqual(a.params, b.params) &&
|
|
731
|
+
(a.route === b.route || shallowEqual(a.route, b.route)));
|
|
732
|
+
/**
|
|
733
|
+
* The outlet rebuilds a view's `RouteContext` several times per navigation, so
|
|
734
|
+
* this compares by content to tell whether anything actually changed.
|
|
735
|
+
*/
|
|
736
|
+
const isSameRouteContext = (a, b) => {
|
|
737
|
+
const { matches: aMatches, ...aRest } = a;
|
|
738
|
+
const { matches: bMatches, ...bRest } = b;
|
|
739
|
+
return (aMatches.length === bMatches.length &&
|
|
740
|
+
shallowEqual(aRest, bRest) &&
|
|
741
|
+
aMatches.every((match, i) => isSameMatch(match, bMatches[i])));
|
|
742
|
+
};
|
|
743
|
+
/**
|
|
744
|
+
* Reuses the last `RouteContext` while its content is the same or `hold` is set,
|
|
745
|
+
* so hidden pages that only call `useParams` don't re-render. It lives in
|
|
746
|
+
* component state because outlets with the same id share view items.
|
|
747
|
+
*/
|
|
748
|
+
const ViewRouteContextProvider = ({ value, hold, children }) => {
|
|
749
|
+
const [heldValue, setHeldValue] = React.useState(value);
|
|
750
|
+
if (!hold && heldValue !== value && !isSameRouteContext(heldValue, value)) {
|
|
751
|
+
setHeldValue(value);
|
|
752
|
+
}
|
|
753
|
+
return React.createElement(UNSAFE_RouteContext.Provider, { value: heldValue }, children);
|
|
754
|
+
};
|
|
697
755
|
class ReactRouterViewStack extends ViewStacks {
|
|
698
756
|
/**
|
|
699
757
|
* Stores the computed parent path for each outlet.
|
|
@@ -894,6 +952,8 @@ class ReactRouterViewStack extends ViewStacks {
|
|
|
894
952
|
viewItem.mount = true;
|
|
895
953
|
viewItem.routeData.match = match;
|
|
896
954
|
}
|
|
955
|
+
// A more specific sibling owns the pathname, so this view is hidden even though it matches.
|
|
956
|
+
let hiddenBySpecificMatch = false;
|
|
897
957
|
// Deactivate wildcard (catch-all) and empty-path (default) routes when a more-specific route matches.
|
|
898
958
|
// This prevents "Not found" or fallback pages from showing alongside valid routes.
|
|
899
959
|
if (routePath === '*' || routePath === '/*' || routePath === '') {
|
|
@@ -950,6 +1010,7 @@ class ReactRouterViewStack extends ViewStacks {
|
|
|
950
1010
|
}
|
|
951
1011
|
}
|
|
952
1012
|
if (hasSpecificMatch && !isSwipeRevealed(viewItem)) {
|
|
1013
|
+
hiddenBySpecificMatch = true;
|
|
953
1014
|
// A splat can be the outlet's container page rather than a "not found" fallback.
|
|
954
1015
|
// Pushed over it is the page underneath, so unmounting it destroys its state and
|
|
955
1016
|
// leaves nothing for back to reveal. Hiding it below covers that. A view with no
|
|
@@ -972,6 +1033,11 @@ class ReactRouterViewStack extends ViewStacks {
|
|
|
972
1033
|
viewItem.routeData.match = match;
|
|
973
1034
|
}
|
|
974
1035
|
const routeMatch = shouldSkipForDifferentParam ? viewItem.routeData?.match : match || viewItem.routeData?.match;
|
|
1036
|
+
// Hold the context when a sibling owns the pathname, the view is parked on another param
|
|
1037
|
+
// instance, or it doesn't match. A pathless default only strictly matches at "/", so no match
|
|
1038
|
+
// there doesn't mean it's hidden.
|
|
1039
|
+
const isPathlessDefault = !routePath && !viewItem.routeData?.childProps?.index;
|
|
1040
|
+
const holdRouteContext = hiddenBySpecificMatch || !!shouldSkipForDifferentParam || (!match && !isPathlessDefault);
|
|
975
1041
|
return (React.createElement(UNSAFE_RouteContext.Consumer, { key: `view-context-${viewItem.id}` }, (parentContext) => {
|
|
976
1042
|
const parentMatches = (parentContext?.matches ?? []);
|
|
977
1043
|
// Accumulate params from parent matches, with fallback to other outlets
|
|
@@ -987,7 +1053,7 @@ class ReactRouterViewStack extends ViewStacks {
|
|
|
987
1053
|
? { ...parentContext, matches: contextMatches }
|
|
988
1054
|
: { outlet: null, matches: contextMatches, isDataRoute: false };
|
|
989
1055
|
return (React.createElement(ViewLifeCycleManager, { key: `view-${viewItem.id}`, mount: viewItem.mount, removeView: () => this.remove(viewItem) },
|
|
990
|
-
React.createElement(
|
|
1056
|
+
React.createElement(ViewRouteContextProvider, { value: routeContextValue, hold: holdRouteContext }, componentElement)));
|
|
991
1057
|
}));
|
|
992
1058
|
};
|
|
993
1059
|
/**
|
|
@@ -3106,26 +3172,6 @@ const checkIsMultiStepBack = (prevInfo, destinationPathname, history) => {
|
|
|
3106
3172
|
}
|
|
3107
3173
|
return false;
|
|
3108
3174
|
};
|
|
3109
|
-
const areParamsEqual = (a, b) => {
|
|
3110
|
-
const paramsA = a || {};
|
|
3111
|
-
const paramsB = b || {};
|
|
3112
|
-
const keysA = Object.keys(paramsA);
|
|
3113
|
-
const keysB = Object.keys(paramsB);
|
|
3114
|
-
if (keysA.length !== keysB.length) {
|
|
3115
|
-
return false;
|
|
3116
|
-
}
|
|
3117
|
-
return keysA.every((key) => {
|
|
3118
|
-
const valueA = paramsA[key];
|
|
3119
|
-
const valueB = paramsB[key];
|
|
3120
|
-
if (Array.isArray(valueA) && Array.isArray(valueB)) {
|
|
3121
|
-
if (valueA.length !== valueB.length) {
|
|
3122
|
-
return false;
|
|
3123
|
-
}
|
|
3124
|
-
return valueA.every((entry, idx) => entry === valueB[idx]);
|
|
3125
|
-
}
|
|
3126
|
-
return valueA === valueB;
|
|
3127
|
-
});
|
|
3128
|
-
};
|
|
3129
3175
|
const IonRouter = ({ children, registerHistoryListener }) => {
|
|
3130
3176
|
const location = useLocation();
|
|
3131
3177
|
const navigate = useNavigate();
|