@ionic/react-router 8.7.12-dev.11765307927.1f491e92 → 8.7.12-dev.11765377112.16762e5b

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
@@ -13,10 +13,13 @@ const IonRouteInner = ({ path, element }) => {
13
13
  * @see https://reactrouter.com/v6/utils/match-path
14
14
  */
15
15
  const matchPath = ({ pathname, componentProps }) => {
16
- var _a, _b;
17
16
  const { path, index } = componentProps, restProps = __rest(componentProps, ["path", "index"]);
18
- // Handle index routes - they match when pathname is empty or just "/"
17
+ // Handle index routes
19
18
  if (index && !path) {
19
+ // Index routes match when there's no additional path after the parent route
20
+ // For example, in a nested outlet at /routing/*, the index route matches
21
+ // when the relative path is empty (i.e., we're exactly at /routing)
22
+ // If pathname is empty or just "/", it should match the index route
20
23
  if (pathname === '' || pathname === '/') {
21
24
  return {
22
25
  params: {},
@@ -29,25 +32,14 @@ const matchPath = ({ pathname, componentProps }) => {
29
32
  },
30
33
  };
31
34
  }
35
+ // Otherwise, index routes don't match when there's additional path
32
36
  return null;
33
37
  }
34
- // Handle empty path routes - they match when pathname is also empty or just "/"
35
- if (path === '' || path === undefined) {
36
- if (pathname === '' || pathname === '/') {
37
- return {
38
- params: {},
39
- pathname: pathname,
40
- pathnameBase: pathname || '/',
41
- pattern: {
42
- path: '',
43
- caseSensitive: (_a = restProps.caseSensitive) !== null && _a !== void 0 ? _a : false,
44
- end: (_b = restProps.end) !== null && _b !== void 0 ? _b : true,
45
- },
46
- };
47
- }
38
+ if (!path) {
48
39
  return null;
49
40
  }
50
- // For relative paths (don't start with '/'), normalize both path and pathname for matching
41
+ // For relative paths in nested routes (those that don't start with '/'),
42
+ // use React Router's matcher against a normalized path.
51
43
  if (!path.startsWith('/')) {
52
44
  const matchOptions = Object.assign({ path: `/${path}` }, restProps);
53
45
  if ((matchOptions === null || matchOptions === void 0 ? void 0 : matchOptions.end) === undefined) {
@@ -59,6 +51,7 @@ const matchPath = ({ pathname, componentProps }) => {
59
51
  // Adjust the match to remove the leading '/' we added
60
52
  return Object.assign(Object.assign({}, match), { pathname: pathname, pathnameBase: match.pathnameBase === '/' ? '' : match.pathnameBase.slice(1), pattern: Object.assign(Object.assign({}, match.pattern), { path: path }) });
61
53
  }
54
+ // No match found
62
55
  return null;
63
56
  }
64
57
  // For absolute paths, use React Router's matcher directly.
@@ -78,16 +71,12 @@ const matchPath = ({ pathname, componentProps }) => {
78
71
  */
79
72
  const derivePathnameToMatch = (fullPathname, routePath) => {
80
73
  var _a;
81
- // For absolute or empty routes, use the full pathname as-is
82
74
  if (!routePath || routePath === '' || routePath.startsWith('/')) {
83
75
  return fullPathname;
84
76
  }
85
77
  const trimmedPath = fullPathname.startsWith('/') ? fullPathname.slice(1) : fullPathname;
86
78
  if (!trimmedPath) {
87
- // For root-level relative routes (pathname is "/" and routePath is relative),
88
- // return the full pathname so matchPath can normalize both.
89
- // This allows routes like <Route path="foo/*" .../> at root level to work correctly.
90
- return fullPathname;
79
+ return '';
91
80
  }
92
81
  const fullSegments = trimmedPath.split('/').filter(Boolean);
93
82
  if (fullSegments.length === 0) {
@@ -815,9 +804,7 @@ class ReactRouterViewStack extends ViewStacks {
815
804
  let parentPath = undefined;
816
805
  try {
817
806
  // Only attempt parent path computation for non-root outlets
818
- // Root outlets have IDs like 'routerOutlet' or 'routerOutlet-2'
819
- const isRootOutlet = outletId.startsWith('routerOutlet');
820
- if (!isRootOutlet) {
807
+ if (outletId !== 'routerOutlet') {
821
808
  const routeChildren = extractRouteChildren(ionRouterOutlet.props.children);
822
809
  const { hasRelativeRoutes, hasIndexRoute, hasWildcardRoute } = analyzeRouteChildren(routeChildren);
823
810
  if (hasRelativeRoutes || hasIndexRoute) {
@@ -1059,7 +1046,7 @@ class ReactRouterViewStack extends ViewStacks {
1059
1046
  * Matches a view with no path prop (default fallback route) or index route.
1060
1047
  */
1061
1048
  function matchDefaultRoute(v) {
1062
- var _a, _b, _c;
1049
+ var _a;
1063
1050
  const childProps = v.routeData.childProps;
1064
1051
  const isDefaultRoute = childProps.path === undefined || childProps.path === '';
1065
1052
  const isIndexRoute = !!childProps.index;
@@ -1072,22 +1059,14 @@ class ReactRouterViewStack extends ViewStacks {
1072
1059
  }
1073
1060
  return false;
1074
1061
  }
1075
- // For empty path routes, only match if we're at the same level as when the view was created.
1076
- // This prevents an empty path view item from being reused for different routes.
1077
1062
  if (isDefaultRoute) {
1078
- const previousPathnameBase = ((_b = (_a = v.routeData) === null || _a === void 0 ? void 0 : _a.match) === null || _b === void 0 ? void 0 : _b.pathnameBase) || '';
1079
- const normalizedBase = normalizePathnameForComparison(previousPathnameBase);
1080
- const normalizedPathname = normalizePathnameForComparison(pathname);
1081
- if (normalizedPathname !== normalizedBase) {
1082
- return false;
1083
- }
1084
1063
  match = {
1085
1064
  params: {},
1086
1065
  pathname,
1087
1066
  pathnameBase: pathname === '' ? '/' : pathname,
1088
1067
  pattern: {
1089
1068
  path: '',
1090
- caseSensitive: (_c = childProps.caseSensitive) !== null && _c !== void 0 ? _c : false,
1069
+ caseSensitive: (_a = childProps.caseSensitive) !== null && _a !== void 0 ? _a : false,
1091
1070
  end: true,
1092
1071
  },
1093
1072
  };
@@ -1212,30 +1191,24 @@ class StackManager extends React.PureComponent {
1212
1191
  if (this.outletMountPath && !currentPathname.startsWith(this.outletMountPath)) {
1213
1192
  return undefined;
1214
1193
  }
1215
- // Check if this outlet has route children to analyze
1216
- if (this.ionRouterOutlet) {
1194
+ // If this is a nested outlet (has an explicit ID like "main"),
1195
+ // we need to figure out what part of the path was already matched
1196
+ if (this.id !== 'routerOutlet' && this.ionRouterOutlet) {
1217
1197
  const routeChildren = extractRouteChildren(this.ionRouterOutlet.props.children);
1218
1198
  const { hasRelativeRoutes, hasIndexRoute, hasWildcardRoute } = analyzeRouteChildren(routeChildren);
1219
- // Root outlets have IDs like 'routerOutlet' or 'routerOutlet-2'
1220
- // But even outlets with auto-generated IDs may need parent path computation
1221
- // if they have relative routes (indicating they're nested outlets)
1222
- const isRootOutlet = this.id.startsWith('routerOutlet');
1223
- const needsParentPath = !isRootOutlet || hasRelativeRoutes || hasIndexRoute;
1224
- if (needsParentPath) {
1225
- const result = computeParentPath({
1226
- currentPathname,
1227
- outletMountPath: this.outletMountPath,
1228
- routeChildren,
1229
- hasRelativeRoutes,
1230
- hasIndexRoute,
1231
- hasWildcardRoute,
1232
- });
1233
- // Update the outlet mount path if it was set
1234
- if (result.outletMountPath && !this.outletMountPath) {
1235
- this.outletMountPath = result.outletMountPath;
1236
- }
1237
- return result.parentPath;
1199
+ const result = computeParentPath({
1200
+ currentPathname,
1201
+ outletMountPath: this.outletMountPath,
1202
+ routeChildren,
1203
+ hasRelativeRoutes,
1204
+ hasIndexRoute,
1205
+ hasWildcardRoute,
1206
+ });
1207
+ // Update the outlet mount path if it was set
1208
+ if (result.outletMountPath && !this.outletMountPath) {
1209
+ this.outletMountPath = result.outletMountPath;
1238
1210
  }
1211
+ return result.parentPath;
1239
1212
  }
1240
1213
  return this.outletMountPath;
1241
1214
  }
@@ -1324,9 +1297,7 @@ class StackManager extends React.PureComponent {
1324
1297
  */
1325
1298
  handleOutOfContextNestedOutlet(parentPath, leavingViewItem) {
1326
1299
  var _a;
1327
- // Root outlets have IDs like 'routerOutlet' or 'routerOutlet-2'
1328
- const isRootOutlet = this.id.startsWith('routerOutlet');
1329
- if (isRootOutlet || parentPath !== undefined || !this.ionRouterOutlet) {
1300
+ if (this.id === 'routerOutlet' || parentPath !== undefined || !this.ionRouterOutlet) {
1330
1301
  return false;
1331
1302
  }
1332
1303
  const routesChildren = (_a = getRoutesChildren(this.ionRouterOutlet.props.children)) !== null && _a !== void 0 ? _a : this.ionRouterOutlet.props.children;
@@ -1351,9 +1322,7 @@ class StackManager extends React.PureComponent {
1351
1322
  * Returns true if the transition should be aborted.
1352
1323
  */
1353
1324
  handleNoMatchingRoute(enteringRoute, enteringViewItem, leavingViewItem) {
1354
- // Root outlets have IDs like 'routerOutlet' or 'routerOutlet-2'
1355
- const isRootOutlet = this.id.startsWith('routerOutlet');
1356
- if (isRootOutlet || enteringRoute || enteringViewItem) {
1325
+ if (this.id === 'routerOutlet' || enteringRoute || enteringViewItem) {
1357
1326
  return false;
1358
1327
  }
1359
1328
  // Hide any visible views in this outlet since it has no matching route
@@ -1900,59 +1869,27 @@ function findRouteByRouteInfo(node, routeInfo, parentPath) {
1900
1869
  });
1901
1870
  // For nested routes in React Router 6, we need to extract the relative path
1902
1871
  // that this outlet should be responsible for matching
1903
- const originalPathname = routeInfo.pathname;
1904
- let relativePathnameToMatch = routeInfo.pathname;
1872
+ let pathnameToMatch = routeInfo.pathname;
1905
1873
  // Check if we have relative routes (routes that don't start with '/')
1906
1874
  const hasRelativeRoutes = sortedRoutes.some((r) => r.props.path && !r.props.path.startsWith('/'));
1907
1875
  const hasIndexRoute = sortedRoutes.some((r) => r.props.index);
1908
1876
  // SIMPLIFIED: Trust React Router 6's matching more, compute relative path when parent is known
1909
1877
  if ((hasRelativeRoutes || hasIndexRoute) && parentPath) {
1910
1878
  const parentPrefix = parentPath.replace('/*', '');
1911
- // Normalize both paths to start with '/' for consistent comparison
1912
- const normalizedParent = stripTrailingSlash(parentPrefix.startsWith('/') ? parentPrefix : `/${parentPrefix}`);
1879
+ const normalizedParent = stripTrailingSlash(parentPrefix);
1913
1880
  const normalizedPathname = stripTrailingSlash(routeInfo.pathname);
1914
1881
  // Only compute relative path if pathname is within parent scope
1915
1882
  if (normalizedPathname.startsWith(normalizedParent + '/') || normalizedPathname === normalizedParent) {
1916
1883
  const pathSegments = routeInfo.pathname.split('/').filter(Boolean);
1917
1884
  const parentSegments = normalizedParent.split('/').filter(Boolean);
1918
1885
  const relativeSegments = pathSegments.slice(parentSegments.length);
1919
- relativePathnameToMatch = relativeSegments.join('/'); // Empty string is valid for index routes
1886
+ pathnameToMatch = relativeSegments.join('/'); // Empty string is valid for index routes
1920
1887
  }
1921
1888
  }
1922
1889
  // Find the first matching route
1923
1890
  for (const child of sortedRoutes) {
1924
- const childPath = child.props.path;
1925
- const isAbsoluteRoute = childPath && childPath.startsWith('/');
1926
- // Determine which pathname to match against:
1927
- // - For absolute routes: use the original full pathname
1928
- // - For relative routes with a parent: use the computed relative pathname
1929
- // - For relative routes at root level (no parent): use the original pathname
1930
- // (matchPath will handle the relative-to-absolute normalization)
1931
- const pathnameToMatch = isAbsoluteRoute ? originalPathname : relativePathnameToMatch;
1932
- // Determine the path portion to match:
1933
- // - For absolute routes: use derivePathnameToMatch
1934
- // - For relative routes at root level (no parent): use original pathname
1935
- // directly since matchPath normalizes both path and pathname
1936
- // - For relative routes with parent: use derivePathnameToMatch for wildcards,
1937
- // or the computed relative pathname for non-wildcards
1938
- let pathForMatch;
1939
- if (isAbsoluteRoute) {
1940
- pathForMatch = derivePathnameToMatch(pathnameToMatch, childPath);
1941
- }
1942
- else if (!parentPath && childPath) {
1943
- // Root-level relative route: use the full pathname and let matchPath
1944
- // handle the normalization (it adds '/' to both path and pathname)
1945
- pathForMatch = originalPathname;
1946
- }
1947
- else if (childPath && childPath.includes('*')) {
1948
- // Relative wildcard route with parent path: use derivePathnameToMatch
1949
- pathForMatch = derivePathnameToMatch(pathnameToMatch, childPath);
1950
- }
1951
- else {
1952
- pathForMatch = pathnameToMatch;
1953
- }
1954
1891
  const match = matchPath({
1955
- pathname: pathForMatch,
1892
+ pathname: pathnameToMatch,
1956
1893
  componentProps: child.props,
1957
1894
  });
1958
1895
  if (match) {
@@ -2210,10 +2147,16 @@ const IonRouter = ({ children, registerHistoryListener }) => {
2210
2147
  * tab and use its `pushedByRoute`.
2211
2148
  */
2212
2149
  const lastRoute = locationHistory.current.getCurrentRouteInfoForTab(routeInfo.tab);
2213
- // This helps maintain correct back stack behavior within tabs.
2214
- // If this is the first time entering this tab from a different context,
2215
- // use the leaving route's pathname as the pushedByRoute to maintain the back stack.
2216
- routeInfo.pushedByRoute = (_e = lastRoute === null || lastRoute === void 0 ? void 0 : lastRoute.pushedByRoute) !== null && _e !== void 0 ? _e : leavingLocationInfo.pathname;
2150
+ /**
2151
+ * Tab bar switches (direction 'none') should not create cross-tab back
2152
+ * navigation. Only inherit pushedByRoute from the tab's own history.
2153
+ */
2154
+ if (routeInfo.routeDirection === 'none') {
2155
+ routeInfo.pushedByRoute = lastRoute === null || lastRoute === void 0 ? void 0 : lastRoute.pushedByRoute;
2156
+ }
2157
+ else {
2158
+ routeInfo.pushedByRoute = (_e = lastRoute === null || lastRoute === void 0 ? void 0 : lastRoute.pushedByRoute) !== null && _e !== void 0 ? _e : leavingLocationInfo.pathname;
2159
+ }
2217
2160
  // Triggered by `history.replace()` or a `<Redirect />` component, etc.
2218
2161
  }
2219
2162
  else if (routeInfo.routeAction === 'replace') {
@@ -2380,11 +2323,14 @@ const IonRouter = ({ children, registerHistoryListener }) => {
2380
2323
  handleNavigate(defaultHref, 'pop', 'back', routeAnimation);
2381
2324
  }
2382
2325
  /**
2383
- * No `pushedByRoute`
2384
- * e.g., initial page load
2326
+ * No `pushedByRoute` (e.g., initial page load or tab root).
2327
+ * Tabs with no back history should not navigate.
2385
2328
  */
2386
2329
  }
2387
2330
  else {
2331
+ if (routeInfo && routeInfo.tab) {
2332
+ return;
2333
+ }
2388
2334
  handleNavigate(defaultHref, 'pop', 'back', routeAnimation);
2389
2335
  }
2390
2336
  };