@ionic/react-router 9.0.7-dev.11791381676.118a7a15 → 9.0.7-dev.11792524803.14f0e7d2

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
@@ -1,41 +1,12 @@
1
1
  import React, { useRef, useState, useEffect, useCallback } from 'react';
2
- import { Route, matchPath as matchPath$1, Routes, Navigate, UNSAFE_RouteContext, matchRoutes, useLocation, useNavigate, BrowserRouter, useNavigationType, HashRouter } from 'react-router-dom';
2
+ import { Route, matchPath as matchPath$1, Routes, Navigate, UNSAFE_RouteContext, matchRoutes, useLocation, useNavigate, BrowserRouter, useNavigationType, MemoryRouter, HashRouter } from 'react-router-dom';
3
3
  import { IonRoute, ViewStacks, generateId, ViewLifeCycleManager, createDebugLogger, StackContext, RouteManagerContext, getConfig, LocationHistory, NavManager } from '@ionic/react';
4
4
  import { LIFECYCLE_WILL_LEAVE, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_DID_ENTER } from '@ionic/core/components';
5
- import { MemoryRouter, useLocation as useLocation$1, useNavigationType as useNavigationType$1 } from 'react-router';
6
5
 
7
6
  const IonRouteInner = ({ path, index, caseSensitive, element }) => {
8
7
  return React.createElement(Route, { path: path, index: index, caseSensitive: caseSensitive, element: element });
9
8
  };
10
9
 
11
- /**
12
- * Compares two route param objects by value, treating a missing object as empty.
13
- *
14
- * @param a The first params object.
15
- * @param b The second params object.
16
- * @returns True if both have the same keys with equal values.
17
- */
18
- const areParamsEqual = (a, b) => {
19
- const paramsA = a || {};
20
- const paramsB = b || {};
21
- const keysA = Object.keys(paramsA);
22
- const keysB = Object.keys(paramsB);
23
- if (keysA.length !== keysB.length) {
24
- return false;
25
- }
26
- return keysA.every((key) => {
27
- const valueA = paramsA[key];
28
- const valueB = paramsB[key];
29
- if (Array.isArray(valueA) && Array.isArray(valueB)) {
30
- if (valueA.length !== valueB.length) {
31
- return false;
32
- }
33
- return valueA.every((entry, idx) => entry === valueB[idx]);
34
- }
35
- return valueA === valueB;
36
- });
37
- };
38
-
39
10
  /**
40
11
  * The matchPath function is used only for matching paths, not rendering components or elements.
41
12
  * @see https://reactrouter.com/v6/utils/match-path
@@ -650,14 +621,16 @@ const getFallbackParamsFromViewItems = (allViewItems, currentOutletId, currentPa
650
621
  return params;
651
622
  };
652
623
  /**
653
- * Builds the matches array for RouteContext.
624
+ * Builds the matches array for RouteContext. The leaf `pathname` uses the absolute base,
625
+ * because the raw match is relative for a route without a leading slash, which breaks
626
+ * relative links, and includes a splat's tail, which loops an index `<Navigate>`.
654
627
  */
655
- const buildContextMatches = (parentMatches, combinedParams, routeMatch, routeInfoPathname, absolutePathnameBase, viewItem, routeElement, componentElement) => {
628
+ const buildContextMatches = (parentMatches, combinedParams, absolutePathnameBase, viewItem, routeElement, componentElement) => {
656
629
  return [
657
630
  ...parentMatches,
658
631
  {
659
632
  params: combinedParams,
660
- pathname: routeMatch?.pathname || routeInfoPathname,
633
+ pathname: absolutePathnameBase,
661
634
  pathnameBase: absolutePathnameBase,
662
635
  route: {
663
636
  id: viewItem.id,
@@ -722,42 +695,6 @@ const resolveIndexRouteMatch = (viewItem, pathname, parentPath) => {
722
695
  const normalizedBase = normalizePathnameForComparison(previousMatch.pathnameBase || previousMatch.pathname || '');
723
696
  return normalizedPathname === normalizedBase ? previousMatch : null;
724
697
  };
725
- const shallowEqual = (a, b) => {
726
- const aRecord = a;
727
- const bRecord = b;
728
- const aKeys = Object.keys(aRecord);
729
- return aKeys.length === Object.keys(bRecord).length && aKeys.every((key) => aRecord[key] === bRecord[key]);
730
- };
731
- const isSameMatch = (a, b) => a === b ||
732
- (a.pathname === b.pathname &&
733
- a.pathnameBase === b.pathnameBase &&
734
- areParamsEqual(a.params, b.params) &&
735
- (a.route === b.route || shallowEqual(a.route, b.route)));
736
- /**
737
- * The outlet rebuilds a view's RouteContext on every render, and it renders
738
- * several times per navigation, so this compares by content to tell whether
739
- * the view's route state actually changed.
740
- */
741
- const isSameRouteContext = (a, b) => {
742
- const { matches: aMatches, ...aRest } = a;
743
- const { matches: bMatches, ...bRest } = b;
744
- return (shallowEqual(aRest, bRest) &&
745
- aMatches.length === bMatches.length &&
746
- aMatches.every((match, i) => isSameMatch(match, bMatches[i])));
747
- };
748
- /**
749
- * Provides a view's RouteContext, reusing the previous value while its content
750
- * is unchanged or while `hold` is set (the view is not the one being shown), so
751
- * RouteContext-only readers like useParams skip re-rendering. Kept in component
752
- * state since two outlets with the same id share view items.
753
- */
754
- const ViewRouteContextProvider = ({ value, hold, children }) => {
755
- const [heldValue, setHeldValue] = React.useState(value);
756
- if (!hold && heldValue !== value && !isSameRouteContext(heldValue, value)) {
757
- setHeldValue(value);
758
- }
759
- return React.createElement(UNSAFE_RouteContext.Provider, { value: heldValue }, children);
760
- };
761
698
  class ReactRouterViewStack extends ViewStacks {
762
699
  /**
763
700
  * Stores the computed parent path for each outlet.
@@ -958,8 +895,6 @@ class ReactRouterViewStack extends ViewStacks {
958
895
  viewItem.mount = true;
959
896
  viewItem.routeData.match = match;
960
897
  }
961
- // Set when a more specific sibling owns the pathname, so this view is hidden even though it matches.
962
- let hiddenBySpecificMatch = false;
963
898
  // Deactivate wildcard (catch-all) and empty-path (default) routes when a more-specific route matches.
964
899
  // This prevents "Not found" or fallback pages from showing alongside valid routes.
965
900
  if (routePath === '*' || routePath === '/*' || routePath === '') {
@@ -1016,7 +951,6 @@ class ReactRouterViewStack extends ViewStacks {
1016
951
  }
1017
952
  }
1018
953
  if (hasSpecificMatch && !isSwipeRevealed(viewItem)) {
1019
- hiddenBySpecificMatch = true;
1020
954
  // A splat can be the outlet's container page rather than a "not found" fallback.
1021
955
  // Pushed over it is the page underneath, so unmounting it destroys its state and
1022
956
  // leaves nothing for back to reveal. Hiding it below covers that. A view with no
@@ -1039,11 +973,6 @@ class ReactRouterViewStack extends ViewStacks {
1039
973
  viewItem.routeData.match = match;
1040
974
  }
1041
975
  const routeMatch = shouldSkipForDifferentParam ? viewItem.routeData?.match : match || viewItem.routeData?.match;
1042
- // Hold the RouteContext while the view isn't the one shown: a sibling owns the pathname, it's
1043
- // parked on another param instance, or it doesn't match. A pathless default only strictly
1044
- // matches at "/", so a missing match there doesn't mean it's hidden.
1045
- const isPathlessDefault = !routePath && !viewItem.routeData?.childProps?.index;
1046
- const holdRouteContext = hiddenBySpecificMatch || !!shouldSkipForDifferentParam || (!match && !isPathlessDefault);
1047
976
  return (React.createElement(UNSAFE_RouteContext.Consumer, { key: `view-context-${viewItem.id}` }, (parentContext) => {
1048
977
  const parentMatches = (parentContext?.matches ?? []);
1049
978
  // Accumulate params from parent matches, with fallback to other outlets
@@ -1054,12 +983,12 @@ class ReactRouterViewStack extends ViewStacks {
1054
983
  const combinedParams = { ...accumulatedParentParams, ...(routeMatch?.params ?? {}) };
1055
984
  const parentPathnameBase = parentMatches.length > 0 ? parentMatches[parentMatches.length - 1].pathnameBase : '/';
1056
985
  const absolutePathnameBase = computeAbsolutePathnameBase(routeElement, routeMatch, parentPathnameBase, routeInfo.pathname);
1057
- const contextMatches = buildContextMatches(parentMatches, combinedParams, routeMatch, routeInfo.pathname, absolutePathnameBase, viewItem, routeElement, componentElement);
986
+ const contextMatches = buildContextMatches(parentMatches, combinedParams, absolutePathnameBase, viewItem, routeElement, componentElement);
1058
987
  const routeContextValue = parentContext
1059
988
  ? { ...parentContext, matches: contextMatches }
1060
989
  : { outlet: null, matches: contextMatches, isDataRoute: false };
1061
990
  return (React.createElement(ViewLifeCycleManager, { key: `view-${viewItem.id}`, mount: viewItem.mount, removeView: () => this.remove(viewItem) },
1062
- React.createElement(ViewRouteContextProvider, { value: routeContextValue, hold: holdRouteContext }, componentElement)));
991
+ React.createElement(UNSAFE_RouteContext.Provider, { value: routeContextValue }, componentElement)));
1063
992
  }));
1064
993
  };
1065
994
  /**
@@ -3178,6 +3107,26 @@ const checkIsMultiStepBack = (prevInfo, destinationPathname, history) => {
3178
3107
  }
3179
3108
  return false;
3180
3109
  };
3110
+ const areParamsEqual = (a, b) => {
3111
+ const paramsA = a || {};
3112
+ const paramsB = b || {};
3113
+ const keysA = Object.keys(paramsA);
3114
+ const keysB = Object.keys(paramsB);
3115
+ if (keysA.length !== keysB.length) {
3116
+ return false;
3117
+ }
3118
+ return keysA.every((key) => {
3119
+ const valueA = paramsA[key];
3120
+ const valueB = paramsB[key];
3121
+ if (Array.isArray(valueA) && Array.isArray(valueB)) {
3122
+ if (valueA.length !== valueB.length) {
3123
+ return false;
3124
+ }
3125
+ return valueA.every((entry, idx) => entry === valueB[idx]);
3126
+ }
3127
+ return valueA === valueB;
3128
+ });
3129
+ };
3181
3130
  const IonRouter = ({ children, registerHistoryListener }) => {
3182
3131
  const location = useLocation();
3183
3132
  const navigate = useNavigate();
@@ -3760,6 +3709,18 @@ const IonRouter = ({ children, registerHistoryListener }) => {
3760
3709
  };
3761
3710
  IonRouter.displayName = 'IonRouter';
3762
3711
 
3712
+ /**
3713
+ * Defaults React Router 7's `useTransitions` to `false`, since a transition can defer the
3714
+ * commit past Ionic's page-visible signal. React Router 6 ignores the prop.
3715
+ *
3716
+ * Uses `??` instead of spreading a default first, because an explicit
3717
+ * `useTransitions={undefined}` would override it and React Router reads that as on.
3718
+ */
3719
+ const withTransitionDefaults = (props) => {
3720
+ const { useTransitions } = props;
3721
+ return { ...props, useTransitions: useTransitions ?? false };
3722
+ };
3723
+
3763
3724
  /**
3764
3725
  * `IonReactRouter` facilitates the integration of Ionic's specific
3765
3726
  * navigation and UI management with the standard React Router mechanisms,
@@ -3804,7 +3765,7 @@ const RouterContent$2 = ({ children }) => {
3804
3765
  return React.createElement(IonRouter, { registerHistoryListener: registerHistoryListener }, children);
3805
3766
  };
3806
3767
  const IonReactRouter = ({ children, ...browserRouterProps }) => {
3807
- return (React.createElement(BrowserRouter, { ...browserRouterProps },
3768
+ return (React.createElement(BrowserRouter, { ...withTransitionDefaults(browserRouterProps) },
3808
3769
  React.createElement(RouterContent$2, null, children)));
3809
3770
  };
3810
3771
 
@@ -3814,8 +3775,8 @@ const IonReactRouter = ({ children, ...browserRouterProps }) => {
3814
3775
  * isn't available or desirable.
3815
3776
  */
3816
3777
  const RouterContent$1 = ({ children }) => {
3817
- const location = useLocation$1();
3818
- const navigationType = useNavigationType$1();
3778
+ const location = useLocation();
3779
+ const navigationType = useNavigationType();
3819
3780
  const historyListenHandler = useRef();
3820
3781
  const registerHistoryListener = useCallback((cb) => {
3821
3782
  historyListenHandler.current = cb;
@@ -3843,7 +3804,7 @@ const RouterContent$1 = ({ children }) => {
3843
3804
  return React.createElement(IonRouter, { registerHistoryListener: registerHistoryListener }, children);
3844
3805
  };
3845
3806
  const IonReactMemoryRouter = ({ children, ...routerProps }) => {
3846
- return (React.createElement(MemoryRouter, { ...routerProps },
3807
+ return (React.createElement(MemoryRouter, { ...withTransitionDefaults(routerProps) },
3847
3808
  React.createElement(RouterContent$1, null, children)));
3848
3809
  };
3849
3810
 
@@ -3881,7 +3842,7 @@ const RouterContent = ({ children }) => {
3881
3842
  return React.createElement(IonRouter, { registerHistoryListener: registerHistoryListener }, children);
3882
3843
  };
3883
3844
  const IonReactHashRouter = ({ children, ...routerProps }) => {
3884
- return (React.createElement(HashRouter, { ...routerProps },
3845
+ return (React.createElement(HashRouter, { ...withTransitionDefaults(routerProps) },
3885
3846
  React.createElement(RouterContent, null, children)));
3886
3847
  };
3887
3848