@ionic/react-router 9.0.6-dev.11790280751.1fc334cc → 9.0.6-nightly.20260925

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
@@ -499,41 +499,6 @@ const compareRouteSpecificity = (a, b) => {
499
499
  }
500
500
  return 0;
501
501
  };
502
- /**
503
- * True when a route matches more pathnames than its own path, so a splat, an index route,
504
- * or a route with an empty or absent path. A lookup can return one of these for a pathname
505
- * a more specific sibling owns, so callers must confirm ownership against React Router's
506
- * ranking before reusing the view item.
507
- *
508
- * This is deliberately wider than the catch-all checks in ReactRouterViewStack, which each
509
- * gate on a narrower shape for a different reason. Don't unify them with this helper.
510
- */
511
- const isOverMatchingRoute = (route) => {
512
- const { path, index } = route;
513
- return !path || path.includes('*') || !!index;
514
- };
515
- /** True when the navigation pushed a new page forward on top of the current one. */
516
- const isForwardPush = (routeInfo) => routeInfo.routeAction === 'push' && routeInfo.routeDirection === 'forward';
517
- const swipeRevealed = new WeakSet();
518
- /**
519
- * Marks the page a swipe-back gesture has revealed. For the length of the drag that page
520
- * is on screen while a more specific sibling still matches the current pathname, and the
521
- * deactivation scan in `renderViewItem` would otherwise re-hide it on the next render and
522
- * leave the user dragging a blank page.
523
- */
524
- const markSwipeRevealed = (viewItem) => {
525
- if (viewItem) {
526
- swipeRevealed.add(viewItem);
527
- }
528
- };
529
- /** Drops the mark once the gesture ends, so the view is hidden normally again. */
530
- const clearSwipeRevealed = (viewItem) => {
531
- if (viewItem) {
532
- swipeRevealed.delete(viewItem);
533
- }
534
- };
535
- /** True while a swipe-back gesture is showing this view. */
536
- const isSwipeRevealed = (viewItem) => swipeRevealed.has(viewItem);
537
502
  /**
538
503
  * Sorts view items by route specificity (most specific first).
539
504
  *
@@ -895,7 +860,7 @@ class ReactRouterViewStack extends ViewStacks {
895
860
  }
896
861
  // Deactivate wildcard (catch-all) and empty-path (default) routes when a more-specific route matches.
897
862
  // This prevents "Not found" or fallback pages from showing alongside valid routes.
898
- if (routePath === '*' || routePath === '/*' || routePath === '') {
863
+ if (routePath === '*' || routePath === '') {
899
864
  // Check if any other view in this outlet has a match for the current route
900
865
  const outletViews = this.getViewItemsForOutlet(viewItem.outletId);
901
866
  // When parent path context is available, compute the relative pathname once
@@ -948,15 +913,8 @@ class ReactRouterViewStack extends ViewStacks {
948
913
  }
949
914
  }
950
915
  }
951
- if (hasSpecificMatch && !isSwipeRevealed(viewItem)) {
952
- // A splat can be the outlet's container page rather than a "not found" fallback.
953
- // Pushed over it is the page underneath, so unmounting it destroys its state and
954
- // leaves nothing for back to reveal. Hiding it below covers that. A view with no
955
- // ion-page was never a page in the stack and has nothing to hide, so it still
956
- // unmounts.
957
- if (!isForwardPush(routeInfo) || !viewItem.ionPageElement) {
958
- viewItem.mount = false;
959
- }
916
+ if (hasSpecificMatch) {
917
+ viewItem.mount = false;
960
918
  if (viewItem.ionPageElement) {
961
919
  viewItem.ionPageElement.classList.add('ion-page-hidden');
962
920
  viewItem.ionPageElement.setAttribute('aria-hidden', 'true');
@@ -1026,11 +984,8 @@ class ReactRouterViewStack extends ViewStacks {
1026
984
  parentPath = result.parentPath;
1027
985
  // Persist the mount path for subsequent calls, mirroring StackManager.outletMountPath.
1028
986
  // Unlike outletParentPaths (cleared when parentPath is undefined), the mount path is
1029
- // intentionally sticky, it anchors the outlet's scope and is only removed in clear().
1030
- // A root outlet is skipped because it is mounted under nothing, so an inferred path
1031
- // would scope it to whatever route was active. parentPathnameBase is undefined
1032
- // exactly when the outlet has no parent matches.
1033
- if (parentPathnameBase && result.outletMountPath && !this.outletMountPaths.has(outletId)) {
987
+ // intentionally sticky — it anchors the outlet's scope and is only removed in clear().
988
+ if (result.outletMountPath && !this.outletMountPaths.has(outletId)) {
1034
989
  this.outletMountPaths.set(outletId, result.outletMountPath);
1035
990
  }
1036
991
  }
@@ -1480,8 +1435,6 @@ class StackManager extends React.PureComponent {
1480
1435
  routerOutletElement;
1481
1436
  prevProps;
1482
1437
  skipTransition;
1483
- /** The view item the in-flight swipe gesture revealed, so the same one gets cleared. */
1484
- swipeRevealedViewItem;
1485
1438
  stackContextValue = {
1486
1439
  registerIonPage: this.registerIonPage.bind(this),
1487
1440
  isInOutlet: () => true,
@@ -1573,9 +1526,7 @@ class StackManager extends React.PureComponent {
1573
1526
  hasIndexRoute,
1574
1527
  hasWildcardRoute,
1575
1528
  });
1576
- // A root outlet is mounted under nothing, so caching the inferred path would scope
1577
- // it to whatever route was active and make every other pathname look out of scope.
1578
- if (!this.isRootOutlet && result.outletMountPath && !this.outletMountPath) {
1529
+ if (result.outletMountPath && !this.outletMountPath) {
1579
1530
  this.outletMountPath = result.outletMountPath;
1580
1531
  }
1581
1532
  return result.parentPath;
@@ -1630,16 +1581,12 @@ class StackManager extends React.PureComponent {
1630
1581
  return true;
1631
1582
  }
1632
1583
  // For non-replace actions, only unmount for back navigation
1633
- if (!isForwardPush(routeInfo) && routeInfo.routeDirection !== 'none' && enteringViewItem !== leavingViewItem) {
1584
+ const isForwardPush = routeInfo.routeAction === 'push' && routeInfo.routeDirection === 'forward';
1585
+ if (!isForwardPush && routeInfo.routeDirection !== 'none' && enteringViewItem !== leavingViewItem) {
1634
1586
  return true;
1635
1587
  }
1636
1588
  return false;
1637
1589
  }
1638
- /** Clears the reveal flag from the view item the gesture marked. */
1639
- clearSwipeRevealedView() {
1640
- clearSwipeRevealed(this.swipeRevealedViewItem);
1641
- this.swipeRevealedViewItem = undefined;
1642
- }
1643
1590
  /**
1644
1591
  * Handles out-of-scope outlet. Returns true if transition should be aborted.
1645
1592
  */
@@ -1809,9 +1756,7 @@ class StackManager extends React.PureComponent {
1809
1756
  const containerBase = routePath.replace(/\/\*$/, '');
1810
1757
  const currentInContainer = routeInfo.pathname.startsWith(containerBase + '/') || routeInfo.pathname === containerBase;
1811
1758
  const previousInContainer = routeInfo.lastPathname.startsWith(containerBase + '/') || routeInfo.lastPathname === containerBase;
1812
- // A root-level "/*" leaves an empty base, so both checks above are true for every
1813
- // pathname and the shortcut would otherwise skip every navigation in this outlet.
1814
- if (containerBase !== '' && currentInContainer && previousInContainer) {
1759
+ if (currentInContainer && previousInContainer) {
1815
1760
  const updatedMatch = matchComponent(enteringViewItem.reactElement, routeInfo.pathname, true, this.outletMountPath);
1816
1761
  if (updatedMatch) {
1817
1762
  enteringViewItem.routeData.match = updatedMatch;
@@ -2273,7 +2218,6 @@ class StackManager extends React.PureComponent {
2273
2218
  }
2274
2219
  this.waitingForIonPage = false;
2275
2220
  this.preservedViewItems.clear();
2276
- this.clearSwipeRevealedView();
2277
2221
  // Hide all views in this outlet before clearing.
2278
2222
  // This is critical for nested outlets - when the parent component unmounts,
2279
2223
  // the nested outlet's componentDidUpdate won't be called, so we must hide
@@ -2305,32 +2249,13 @@ class StackManager extends React.PureComponent {
2305
2249
  this.pendingPageTransition = true;
2306
2250
  return;
2307
2251
  }
2308
- // A completed swipe keeps its reveal flag past goBack() so the deactivation scan does
2309
- // not re-hide the page between the gesture ending and the URL settling. Clear it at
2310
- // the start of the transition for the new pathname.
2311
- this.clearSwipeRevealedView();
2312
2252
  // Find entering and leaving view items
2313
2253
  const viewItems = this.findViewItems(routeInfo);
2314
2254
  let enteringViewItem = viewItems.enteringViewItem;
2315
2255
  let leavingViewItem = viewItems.leavingViewItem;
2256
+ let shouldUnmountLeavingViewItem = this.shouldUnmountLeavingView(routeInfo, enteringViewItem, leavingViewItem);
2316
2257
  // Get parent path for nested outlets
2317
2258
  const parentPath = this.getParentPath();
2318
- // Find the matching route element. This is React Router's own ranking of the outlet's
2319
- // routes, so it decides which route owns the pathname.
2320
- const enteringRoute = findRouteByRouteInfo(this.ionRouterOutlet?.props.children, routeInfo, parentPath);
2321
- // The lookup above only matches view items that already exist, so a sibling route with
2322
- // no view item yet never wins there and an over-matching route (splat, index, empty
2323
- // path) comes back for pathnames it does not own. Drop it rather than reusing it,
2324
- // because overwriting its reactElement below would swap its page for the winning
2325
- // route's and unmount it. This has to resolve before shouldUnmountLeavingView and
2326
- // handleRootNavigation, which both branch on the entering view item.
2327
- if (enteringViewItem &&
2328
- enteringRoute &&
2329
- isOverMatchingRoute(enteringViewItem.routeData?.childProps ?? {}) &&
2330
- enteringRoute.props.path !== enteringViewItem.routeData?.childProps?.path) {
2331
- enteringViewItem = undefined;
2332
- }
2333
- let shouldUnmountLeavingViewItem = this.shouldUnmountLeavingView(routeInfo, enteringViewItem, leavingViewItem);
2334
2259
  // Handle out-of-scope outlet (route outside mount path)
2335
2260
  if (this.handleOutOfScopeOutlet(routeInfo)) {
2336
2261
  return;
@@ -2350,6 +2275,8 @@ class StackManager extends React.PureComponent {
2350
2275
  if (this.handleOutOfContextNestedOutlet(parentPath, leavingViewItem)) {
2351
2276
  return;
2352
2277
  }
2278
+ // Find the matching route element
2279
+ const enteringRoute = findRouteByRouteInfo(this.ionRouterOutlet?.props.children, routeInfo, parentPath);
2353
2280
  // Handle nested outlet with no matching route
2354
2281
  if (this.handleNoMatchingRoute(enteringRoute, enteringViewItem, leavingViewItem)) {
2355
2282
  return;
@@ -2570,9 +2497,6 @@ class StackManager extends React.PureComponent {
2570
2497
  return canStartSwipe;
2571
2498
  };
2572
2499
  const onStart = async () => {
2573
- // Core only calls onEnd when a progress animation exists, so with animations off a
2574
- // previous gesture can leave its mark behind. Drop any stale one before marking.
2575
- this.clearSwipeRevealedView();
2576
2500
  const { routeInfo } = this.props;
2577
2501
  const swipeBackRouteInfo = this.getSwipeBackRouteInfo();
2578
2502
  const enteringViewItem = this.findEnteringViewForSwipe(swipeBackRouteInfo);
@@ -2599,11 +2523,6 @@ class StackManager extends React.PureComponent {
2599
2523
  revealIonPageForSwipeBack(enteringViewItem?.ionPageElement);
2600
2524
  // When the gesture starts, kick off a transition controlled via swipe gesture
2601
2525
  if (enteringViewItem && leavingViewItem) {
2602
- // Without a leaving view there is no progress animation, and core only calls
2603
- // swipeHandler.onEnd when one exists, so nothing would clear the mark until the
2604
- // next transition.
2605
- markSwipeRevealed(enteringViewItem);
2606
- this.swipeRevealedViewItem = enteringViewItem;
2607
2526
  await this.transitionPage(routeInfo, enteringViewItem, leavingViewItem, 'back', true);
2608
2527
  }
2609
2528
  debug('SwipeBackOnStart:exit', () => ({
@@ -2628,7 +2547,6 @@ class StackManager extends React.PureComponent {
2628
2547
  const swipeBackRouteInfo = this.getSwipeBackRouteInfo();
2629
2548
  const enteringViewItem = this.findEnteringViewForSwipe(swipeBackRouteInfo);
2630
2549
  const leavingViewItem = this.context.findViewItemByRouteInfo(routeInfo, this.id, false);
2631
- this.clearSwipeRevealedView();
2632
2550
  // Don't hide if entering and leaving are the same (parameterized route edge case)
2633
2551
  if (enteringViewItem !== leavingViewItem && enteringViewItem?.ionPageElement !== undefined) {
2634
2552
  hideIonPageElement(enteringViewItem.ionPageElement);