@sigmela/router 0.7.1 → 0.8.0

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
@@ -311,13 +311,17 @@ function SheetSurface({
311
311
  return () => controls.stop();
312
312
  }, [closeWithAnimation, open, y]);
313
313
  useEffect(() => {
314
- if (!canDismiss) return;
314
+ if (!canDismiss || !open) return;
315
315
  const handler = (event) => {
316
- if (event.key === "Escape") requestClose();
316
+ if (event.key !== "Escape") return;
317
+ const higherSurface = Array.from(
318
+ document.querySelectorAll(".router-overlay-shell")
319
+ ).some((shell2) => Number(shell2.style.zIndex) > zIndex);
320
+ if (!higherSurface) requestClose();
317
321
  };
318
322
  document.addEventListener("keydown", handler);
319
323
  return () => document.removeEventListener("keydown", handler);
320
- }, [canDismiss, requestClose]);
324
+ }, [canDismiss, open, requestClose, zIndex]);
321
325
  const shouldStartDrag = useCallback(
322
326
  (update) => update.totalY > 0 && sheetScrollAtTop(scrollElement ?? null),
323
327
  [scrollElement]
@@ -527,6 +531,10 @@ function ModalFrame({
527
531
  backdropClassName: `router-modal-backdrop${isContentSized ? " router-modal-backdrop--transparent" : ""}`,
528
532
  shellClassName: "router-modal-shell",
529
533
  surfaceClassName: `router-modal-surface${isContentSized ? " router-modal-surface--content-sized" : ""}${options?.size === "wide" ? " router-modal-surface--wide" : ""}`,
534
+ surfaceStyle: {
535
+ "--router-modal-width": options?.width ? `${options.width}px` : void 0,
536
+ "--router-modal-height": options?.height ? `${options.height}px` : void 0
537
+ },
530
538
  scrollElement: swipeTarget,
531
539
  ariaLabel,
532
540
  closeTarget: isContentSized ? "surface" : "viewport",
@@ -539,14 +547,16 @@ function ModalRoot({
539
547
  swipeToClose = true,
540
548
  ariaLabel,
541
549
  size = "regular",
550
+ width,
551
+ height,
542
552
  presentation = "fullscreen"
543
553
  }) {
544
554
  const { setOptions } = useModalContext();
545
555
  const contentKey = useOverlayContentKey();
546
556
  useLayoutEffect(() => {
547
- setOptions(contentKey, { swipeToClose, ariaLabel, size, presentation });
557
+ setOptions(contentKey, { swipeToClose, ariaLabel, size, width, height, presentation });
548
558
  return () => setOptions(contentKey, null);
549
- }, [ariaLabel, contentKey, presentation, setOptions, size, swipeToClose]);
559
+ }, [ariaLabel, contentKey, height, presentation, setOptions, size, swipeToClose, width]);
550
560
  return /* @__PURE__ */ jsx(Fragment, { children });
551
561
  }
552
562
  function ModalHeader({
@@ -941,7 +951,7 @@ function splitNavigationFromState(state) {
941
951
  if (!isRecord(state)) return void 0;
942
952
  const value = state[SPLIT_NAVIGATION_KEY];
943
953
  if (!isRecord(value)) return void 0;
944
- if (typeof value.splitViewId !== "string" || typeof value.detailPathId !== "string" || typeof value.detailDepth !== "number" || value.operation !== "selection" && value.operation !== "push" && value.operation !== "replace") {
954
+ if (typeof value.splitViewId !== "string" || typeof value.detailPathId !== "string" || typeof value.detailDepth !== "number" || value.operation !== "selection" && value.operation !== "push" && value.operation !== "replace" && value.operation !== "pop") {
945
955
  return void 0;
946
956
  }
947
957
  return {
@@ -994,7 +1004,7 @@ function historyNavigationKeyFromState(state) {
994
1004
  function historyCommitFromState(state) {
995
1005
  if (isRecord(state)) {
996
1006
  const commit = state[HISTORY_COMMIT_KEY];
997
- if (commit === "sync" || commit === "replace") return commit;
1007
+ if (commit === "sync" || commit === "replace" || commit === "pop") return commit;
998
1008
  }
999
1009
  return "push";
1000
1010
  }
@@ -1119,6 +1129,7 @@ function splitRouteChildren(children) {
1119
1129
  const routeChildren = [];
1120
1130
  const nonRouteChildren = [];
1121
1131
  for (const child of childArray) {
1132
+ if (child == null || typeof child === "boolean") continue;
1122
1133
  if (isStackRouteElement(child)) {
1123
1134
  routeChildren.push(child);
1124
1135
  } else {
@@ -1506,7 +1517,8 @@ function compileStackRoutes(children, parentPath, parentId = "root", inheritedGu
1506
1517
  container: descriptor.container,
1507
1518
  childRoute,
1508
1519
  drawerAside: isTabBar ? void 0 : props.aside,
1509
- drawerFooter: isTabBar ? void 0 : props.footer
1520
+ drawerFooter: isTabBar ? void 0 : props.footer,
1521
+ drawerDesktopCollapsible: isTabBar ? void 0 : props.desktopCollapsible
1510
1522
  };
1511
1523
  if (isTabBar && tabs) {
1512
1524
  containerRoute.tabBar = {
@@ -1622,8 +1634,8 @@ function stateForSplitNavigationTarget(state, targetMatch, action) {
1622
1634
  } : {
1623
1635
  splitViewId: origin.splitViewId,
1624
1636
  detailPathId: origin.detailPathId,
1625
- detailDepth: action === "navigate" ? origin.detailDepth + 1 : origin.detailDepth,
1626
- operation: action === "navigate" ? "push" : "replace"
1637
+ detailDepth: action === "navigate" ? origin.detailDepth + 1 : action === "pop" ? Math.max(0, origin.detailDepth - 1) : origin.detailDepth,
1638
+ operation: action === "navigate" ? "push" : action
1627
1639
  }
1628
1640
  );
1629
1641
  next[SPLIT_NAVIGATION_KEY] = metadata;
@@ -2099,6 +2111,12 @@ function useRouter() {
2099
2111
  ...options,
2100
2112
  state: stateWithSplitNavigationOrigin(options.state, splitPaneOrigin)
2101
2113
  });
2114
+ },
2115
+ popTo: (to, options = {}) => {
2116
+ router.popTo(to, {
2117
+ ...options,
2118
+ state: stateWithSplitNavigationOrigin(options.state, splitPaneOrigin)
2119
+ });
2102
2120
  }
2103
2121
  };
2104
2122
  }, [router, splitPaneOrigin]);
@@ -2118,7 +2136,6 @@ function useCurrentModal() {
2118
2136
  return current;
2119
2137
  }
2120
2138
  function DefaultSplitViewLayout({
2121
- children,
2122
2139
  primary,
2123
2140
  secondary,
2124
2141
  isWide,
@@ -2135,8 +2152,15 @@ function DefaultSplitViewLayout({
2135
2152
  "data-router-split-view-id": splitViewLayoutId,
2136
2153
  style: { "--sigmela-split-primary-max-width": `${primaryMaxWidth}px` },
2137
2154
  children: [
2138
- /* @__PURE__ */ jsx("div", { className: "sigmela-split-view__primary", children: isWide ? primary : children }),
2139
- isWide ? /* @__PURE__ */ jsx("div", { className: "sigmela-split-view__secondary", children: secondary }) : null
2155
+ /* @__PURE__ */ jsx(
2156
+ "div",
2157
+ {
2158
+ className: "sigmela-split-view__primary",
2159
+ style: !isWide && secondary ? { display: "none" } : void 0,
2160
+ children: primary
2161
+ }
2162
+ ),
2163
+ /* @__PURE__ */ jsx("div", { className: "sigmela-split-view__secondary", children: secondary })
2140
2164
  ]
2141
2165
  }
2142
2166
  );
@@ -2161,88 +2185,119 @@ function DefaultDrawerLayout({
2161
2185
  return next;
2162
2186
  });
2163
2187
  }, []);
2164
- return /* @__PURE__ */ jsxs("div", { className: "sigmela-drawer", "data-drawer-open": drawer?.isOpen ? "true" : "false", children: [
2165
- /* @__PURE__ */ jsxs("aside", { className: "sigmela-drawer__sidebar", children: [
2166
- /* @__PURE__ */ jsx("div", { className: "sigmela-drawer__sidebar-content", children: items.filter((node) => !node.hidden).map((node) => {
2167
- if (node.type === "item") {
2168
- return /* @__PURE__ */ jsxs(
2169
- "button",
2170
- {
2171
- type: "button",
2172
- className: "sigmela-drawer__item",
2173
- "data-active": node.id === activeItem ? "true" : void 0,
2174
- onClick: () => onItemChange?.(node.id),
2175
- children: [
2176
- node.icon ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-icon", children: node.icon }) : null,
2177
- /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-label", children: node.label }),
2178
- node.badge ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-badge", children: node.badge }) : null
2179
- ]
2180
- },
2181
- node.id
2182
- );
2183
- }
2184
- if (node.type === "section") {
2185
- return /* @__PURE__ */ jsx("div", { className: "sigmela-drawer__section-label", children: node.title }, node.id);
2186
- }
2187
- const visibleItems = node.items.filter((item) => !item.hidden);
2188
- if (visibleItems.length === 0) return null;
2189
- const isExpanded = expandedGroups.has(node.id);
2190
- const itemsId = `${node.id}-drawer-items`;
2191
- return /* @__PURE__ */ jsxs("div", { className: "sigmela-drawer__group-container", children: [
2192
- /* @__PURE__ */ jsxs(
2193
- "button",
2194
- {
2195
- type: "button",
2196
- className: "sigmela-drawer__item sigmela-drawer__group-label",
2197
- "aria-controls": itemsId,
2198
- "aria-expanded": isExpanded,
2199
- onClick: () => toggleGroup(node.id),
2200
- children: [
2201
- node.icon ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-icon", children: node.icon }) : null,
2202
- /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-label", children: node.label }),
2203
- node.chevron ? /* @__PURE__ */ jsx(
2204
- "span",
2205
- {
2206
- className: "sigmela-drawer__group-chevron",
2207
- "data-expanded": isExpanded ? "true" : void 0,
2208
- "aria-hidden": "true",
2209
- children: node.chevron
2210
- }
2211
- ) : null
2212
- ]
2213
- }
2214
- ),
2215
- isExpanded ? /* @__PURE__ */ jsx("div", { id: itemsId, className: "sigmela-drawer__group-items", children: visibleItems.map((item) => /* @__PURE__ */ jsxs(
2216
- "button",
2217
- {
2218
- type: "button",
2219
- className: "sigmela-drawer__item sigmela-drawer__subitem",
2220
- "data-active": item.id === activeItem ? "true" : void 0,
2221
- onClick: () => onItemChange?.(item.id),
2222
- children: [
2223
- item.icon ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-icon", children: item.icon }) : null,
2224
- /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-label", children: item.label }),
2225
- item.badge ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-badge", children: item.badge }) : null
2226
- ]
2227
- },
2228
- item.id
2229
- )) }) : null
2230
- ] }, node.id);
2231
- }) }),
2232
- footer ? /* @__PURE__ */ jsx("div", { className: "sigmela-drawer__footer", children: footer }) : null
2233
- ] }),
2234
- /* @__PURE__ */ jsx(
2235
- "button",
2236
- {
2237
- type: "button",
2238
- className: "sigmela-drawer__overlay",
2239
- "aria-label": "Close navigation",
2240
- onClick: drawer?.close
2241
- }
2242
- ),
2243
- /* @__PURE__ */ jsx("main", { className: "sigmela-drawer__main", children }),
2244
- aside ? /* @__PURE__ */ jsx("aside", { className: "sigmela-drawer__assistant", children: aside }) : null
2245
- ] });
2188
+ return /* @__PURE__ */ jsxs(
2189
+ "div",
2190
+ {
2191
+ className: "sigmela-drawer",
2192
+ "data-drawer-open": drawer?.isOpen ? "true" : "false",
2193
+ "data-drawer-collapsed": drawer?.isCollapsed ? "true" : "false",
2194
+ "data-drawer-desktop": drawer?.desktopCollapsible,
2195
+ children: [
2196
+ /* @__PURE__ */ jsxs(
2197
+ "aside",
2198
+ {
2199
+ className: "sigmela-drawer__sidebar",
2200
+ inert: drawer?.isMobile && !drawer.isOpen ? true : void 0,
2201
+ children: [
2202
+ /* @__PURE__ */ jsx("div", { className: "sigmela-drawer__sidebar-content", children: items.filter((node) => !node.hidden).map((node) => {
2203
+ if (node.type === "item") {
2204
+ return /* @__PURE__ */ jsxs(
2205
+ "button",
2206
+ {
2207
+ type: "button",
2208
+ className: "sigmela-drawer__item",
2209
+ "aria-label": node.label,
2210
+ title: drawer?.isCollapsed ? node.label : void 0,
2211
+ "data-active": node.id === activeItem ? "true" : void 0,
2212
+ onClick: () => onItemChange?.(node.id),
2213
+ children: [
2214
+ node.icon || drawer?.isCollapsed ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-icon", "aria-hidden": "true", children: node.icon ?? node.label.slice(0, 1) }) : null,
2215
+ /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-label", children: node.label }),
2216
+ node.badge ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-badge", children: node.badge }) : null
2217
+ ]
2218
+ },
2219
+ node.id
2220
+ );
2221
+ }
2222
+ if (node.type === "section") {
2223
+ return /* @__PURE__ */ jsx("div", { className: "sigmela-drawer__section-label", children: node.title }, node.id);
2224
+ }
2225
+ const visibleItems = node.items.filter((item) => !item.hidden);
2226
+ if (visibleItems.length === 0) return null;
2227
+ const isExpanded = expandedGroups.has(node.id);
2228
+ const itemsId = `${node.id}-drawer-items`;
2229
+ return /* @__PURE__ */ jsxs("div", { className: "sigmela-drawer__group-container", children: [
2230
+ /* @__PURE__ */ jsxs(
2231
+ "button",
2232
+ {
2233
+ type: "button",
2234
+ className: "sigmela-drawer__item sigmela-drawer__group-label",
2235
+ "aria-label": node.label,
2236
+ title: drawer?.isCollapsed ? node.label : void 0,
2237
+ "aria-controls": itemsId,
2238
+ "aria-expanded": isExpanded,
2239
+ onClick: () => {
2240
+ if (drawer?.isCollapsed) drawer.open();
2241
+ toggleGroup(node.id);
2242
+ },
2243
+ children: [
2244
+ node.icon || drawer?.isCollapsed ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-icon", "aria-hidden": "true", children: node.icon ?? node.label.slice(0, 1) }) : null,
2245
+ /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-label", children: node.label }),
2246
+ node.chevron ? /* @__PURE__ */ jsx(
2247
+ "span",
2248
+ {
2249
+ className: "sigmela-drawer__group-chevron",
2250
+ "data-expanded": isExpanded ? "true" : void 0,
2251
+ "aria-hidden": "true",
2252
+ children: node.chevron
2253
+ }
2254
+ ) : null
2255
+ ]
2256
+ }
2257
+ ),
2258
+ isExpanded ? /* @__PURE__ */ jsx("div", { id: itemsId, className: "sigmela-drawer__group-items", children: visibleItems.map((item) => /* @__PURE__ */ jsxs(
2259
+ "button",
2260
+ {
2261
+ type: "button",
2262
+ className: "sigmela-drawer__item sigmela-drawer__subitem",
2263
+ "data-active": item.id === activeItem ? "true" : void 0,
2264
+ onClick: () => onItemChange?.(item.id),
2265
+ children: [
2266
+ item.icon ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-icon", children: item.icon }) : null,
2267
+ /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-label", children: item.label }),
2268
+ item.badge ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-badge", children: item.badge }) : null
2269
+ ]
2270
+ },
2271
+ item.id
2272
+ )) }) : null
2273
+ ] }, node.id);
2274
+ }) }),
2275
+ footer ? /* @__PURE__ */ jsx("div", { className: "sigmela-drawer__footer", children: footer }) : null
2276
+ ]
2277
+ }
2278
+ ),
2279
+ /* @__PURE__ */ jsx(
2280
+ "button",
2281
+ {
2282
+ type: "button",
2283
+ className: "sigmela-drawer__overlay",
2284
+ "aria-label": "Close navigation",
2285
+ tabIndex: drawer?.isMobile && drawer.isOpen ? 0 : -1,
2286
+ onClick: drawer?.close
2287
+ }
2288
+ ),
2289
+ /* @__PURE__ */ jsx(
2290
+ "main",
2291
+ {
2292
+ className: "sigmela-drawer__main",
2293
+ inert: drawer?.isMobile && drawer.isOpen ? true : void 0,
2294
+ children
2295
+ }
2296
+ ),
2297
+ aside ? /* @__PURE__ */ jsx("aside", { className: "sigmela-drawer__assistant", children: aside }) : null
2298
+ ]
2299
+ }
2300
+ );
2246
2301
  }
2247
2302
  function FrozenPaneRouteContent({
2248
2303
  route,
@@ -2568,7 +2623,7 @@ function SplitViewRouteContent({
2568
2623
  fallback: splitView.primaryRoute.fallback ?? fallback
2569
2624
  }
2570
2625
  ) });
2571
- const secondary = splitView.secondaryRoute ? /* @__PURE__ */ jsx(SplitPaneNavigationContext.Provider, { value: secondaryNavigationOrigin, children: isWide ? /* @__PURE__ */ jsx(
2626
+ const secondary = splitView.secondaryRoute ? /* @__PURE__ */ jsx(SplitPaneNavigationContext.Provider, { value: secondaryNavigationOrigin, children: /* @__PURE__ */ jsx(
2572
2627
  RouteStackViewport,
2573
2628
  {
2574
2629
  route: splitView.secondaryRoute,
@@ -2577,13 +2632,6 @@ function SplitViewRouteContent({
2577
2632
  stackKey: `${route.id}:secondary`
2578
2633
  },
2579
2634
  detailPathId
2580
- ) : /* @__PURE__ */ jsx(
2581
- FrozenPaneRouteContent,
2582
- {
2583
- route: splitView.secondaryRoute,
2584
- props: secondaryProps,
2585
- fallback: splitView.secondaryRoute.fallback ?? fallback
2586
- }
2587
2635
  ) }) : null;
2588
2636
  const activeContent = splitView.activePane === "secondary" && secondary ? secondary : primary;
2589
2637
  const layoutProps = {
@@ -2614,13 +2662,44 @@ function DrawerRouteContent({
2614
2662
  route,
2615
2663
  props
2616
2664
  }) {
2617
- const [isOpen, setIsOpen] = useState(false);
2618
- const open = useCallback(() => setIsOpen(true), []);
2619
- const close = useCallback(() => setIsOpen(false), []);
2620
- const toggle = useCallback(() => setIsOpen((value) => !value), []);
2665
+ const isMobile = !useMinWidth(641);
2666
+ const [mobileOpen, setMobileOpen] = useState(false);
2667
+ const [desktopCollapsed, setDesktopCollapsed] = useState(false);
2668
+ const desktopCollapsible = route.drawerDesktopCollapsible ?? "offcanvas";
2669
+ const isCollapsed = !isMobile && desktopCollapsed;
2670
+ const isOpen = isMobile ? mobileOpen : !desktopCollapsed;
2671
+ const open = useCallback(() => {
2672
+ if (isMobile) setMobileOpen(true);
2673
+ else setDesktopCollapsed(false);
2674
+ }, [isMobile]);
2675
+ const close = useCallback(() => {
2676
+ if (isMobile) setMobileOpen(false);
2677
+ else setDesktopCollapsed(true);
2678
+ }, [isMobile]);
2679
+ const toggle = useCallback(() => {
2680
+ if (isMobile) setMobileOpen((value) => !value);
2681
+ else setDesktopCollapsed((value) => !value);
2682
+ }, [isMobile]);
2683
+ useEffect(() => setMobileOpen(false), [isMobile, props.pathname]);
2684
+ useEffect(() => {
2685
+ if (!isMobile || !mobileOpen) return;
2686
+ const onKeyDown = (event) => {
2687
+ if (event.key === "Escape" && !event.defaultPrevented) setMobileOpen(false);
2688
+ };
2689
+ window.addEventListener("keydown", onKeyDown);
2690
+ return () => window.removeEventListener("keydown", onKeyDown);
2691
+ }, [isMobile, mobileOpen]);
2621
2692
  const drawerApi = useMemo(
2622
- () => ({ isOpen, open, close, toggle }),
2623
- [close, isOpen, open, toggle]
2693
+ () => ({
2694
+ isOpen,
2695
+ isMobile,
2696
+ isCollapsed,
2697
+ desktopCollapsible,
2698
+ open,
2699
+ close,
2700
+ toggle
2701
+ }),
2702
+ [close, isOpen, isMobile, isCollapsed, desktopCollapsible, open, toggle]
2624
2703
  );
2625
2704
  const childFallback = route.childRoute?.fallback ?? route.fallback ?? null;
2626
2705
  const drawer = route.drawer;
@@ -2640,7 +2719,7 @@ function DrawerRouteContent({
2640
2719
  if (item && normalizedPath(item.path) !== props.pathname) {
2641
2720
  navigateWithHistoryIndex(item.path);
2642
2721
  }
2643
- close();
2722
+ setMobileOpen(false);
2644
2723
  } : void 0;
2645
2724
  const layoutProps = {
2646
2725
  children: content,
@@ -2909,6 +2988,13 @@ function resolveStackChange(prev, snapshot) {
2909
2988
  };
2910
2989
  }
2911
2990
  const direction = resolveDirection(top, snapshot);
2991
+ if (snapshot.historyCommit === "pop" && snapshot.splitPane === "secondary") {
2992
+ const existingIndex2 = existingStackEntryIndexForSnapshot(prev, snapshot);
2993
+ return {
2994
+ entries: existingIndex2 !== -1 && existingIndex2 < prev.length - 1 ? entriesRevealingSnapshot(prev, existingIndex2, snapshot) : [createEntry(snapshot, "back", true)],
2995
+ direction: "back"
2996
+ };
2997
+ }
2912
2998
  if (isExplicitStackReplace(top, snapshot)) {
2913
2999
  return {
2914
3000
  entries: replaceActiveStackEntry(prev, top.id, snapshot, direction, true),
@@ -2970,6 +3056,10 @@ function visibleEntriesForSnapshot(entries, snapshot) {
2970
3056
  (entry) => entry.id === top.id ? mergeEntrySnapshot(entry, snapshot) : entry
2971
3057
  );
2972
3058
  }
3059
+ if (snapshot.historyCommit === "pop" && snapshot.splitPane === "secondary") {
3060
+ const existingIndex2 = existingStackEntryIndexForSnapshot(entries, snapshot);
3061
+ return existingIndex2 !== -1 && existingIndex2 < entries.length - 1 ? entriesRevealingSnapshot(entries, existingIndex2, snapshot) : [createEntry(snapshot, "back", true)];
3062
+ }
2973
3063
  if (isExplicitStackReplace(top, snapshot)) {
2974
3064
  return replaceActiveStackEntry(
2975
3065
  entries,
@@ -3123,6 +3213,16 @@ function RouterRuntime({
3123
3213
  const modalEntriesRef = useRef(modalEntries);
3124
3214
  const modalSeqRef = useRef(0);
3125
3215
  modalEntriesRef.current = modalEntries;
3216
+ const navigationScopeRef = useRef({
3217
+ compiledRoutes,
3218
+ pathname,
3219
+ parser: wouterRouter.parser
3220
+ });
3221
+ navigationScopeRef.current = {
3222
+ compiledRoutes,
3223
+ pathname,
3224
+ parser: wouterRouter.parser
3225
+ };
3126
3226
  useLayoutEffect(() => {
3127
3227
  ensureInitialHistoryIndex();
3128
3228
  }, []);
@@ -3504,10 +3604,11 @@ function RouterRuntime({
3504
3604
  window.history.go(to);
3505
3605
  return;
3506
3606
  }
3507
- const target = parseNavigationTarget(to, pathname);
3607
+ const { compiledRoutes: compiledRoutes2, pathname: pathname2, parser } = navigationScopeRef.current;
3608
+ const target = parseNavigationTarget(to, pathname2);
3508
3609
  const overlayMatch = matchStackRouteByPresentation(
3509
- compiledRoutes,
3510
- wouterRouter.parser,
3610
+ compiledRoutes2,
3611
+ parser,
3511
3612
  target.pathname,
3512
3613
  OVERLAY_PRESENTATIONS
3513
3614
  );
@@ -3549,8 +3650,8 @@ function RouterRuntime({
3549
3650
  return;
3550
3651
  }
3551
3652
  const pushMatch = matchStackRouteByPresentation(
3552
- compiledRoutes,
3553
- wouterRouter.parser,
3653
+ compiledRoutes2,
3654
+ parser,
3554
3655
  target.pathname,
3555
3656
  PUSH_PRESENTATIONS
3556
3657
  );
@@ -3559,21 +3660,17 @@ function RouterRuntime({
3559
3660
  state: stateForSplitNavigationTarget(options.state, pushMatch, "navigate")
3560
3661
  });
3561
3662
  },
3562
- [
3563
- compiledRoutes,
3564
- goBackTopModal,
3565
- pathname,
3566
- pushModalEntry,
3567
- updateActiveModalFlow,
3568
- wouterRouter.parser
3569
- ]
3663
+ // Every value this reads travels through navigationScopeRef, and the three
3664
+ // callbacks below are themselves identity-stable, so this keeps one identity.
3665
+ [goBackTopModal, pushModalEntry, updateActiveModalFlow]
3570
3666
  );
3571
3667
  const replace = useCallback(
3572
3668
  (to, options = {}) => {
3573
- const target = parseNavigationTarget(to, pathname);
3669
+ const { compiledRoutes: compiledRoutes2, pathname: pathname2, parser } = navigationScopeRef.current;
3670
+ const target = parseNavigationTarget(to, pathname2);
3574
3671
  const overlayMatch = matchStackRouteByPresentation(
3575
- compiledRoutes,
3576
- wouterRouter.parser,
3672
+ compiledRoutes2,
3673
+ parser,
3577
3674
  target.pathname,
3578
3675
  OVERLAY_PRESENTATIONS
3579
3676
  );
@@ -3621,8 +3718,8 @@ function RouterRuntime({
3621
3718
  return;
3622
3719
  }
3623
3720
  const pushMatch = matchStackRouteByPresentation(
3624
- compiledRoutes,
3625
- wouterRouter.parser,
3721
+ compiledRoutes2,
3722
+ parser,
3626
3723
  target.pathname,
3627
3724
  PUSH_PRESENTATIONS
3628
3725
  );
@@ -3631,14 +3728,22 @@ function RouterRuntime({
3631
3728
  state: stateForSplitNavigationTarget(options.state, pushMatch, "replace")
3632
3729
  });
3633
3730
  },
3634
- [
3635
- compiledRoutes,
3636
- pathname,
3637
- pushModalEntry,
3638
- updateActiveModalFlow,
3639
- wouterRouter.parser
3640
- ]
3731
+ [pushModalEntry, updateActiveModalFlow]
3641
3732
  );
3733
+ const popTo = useCallback((to, options = {}) => {
3734
+ const { compiledRoutes: compiledRoutes2, pathname: pathname2, parser } = navigationScopeRef.current;
3735
+ const target = parseNavigationTarget(to, pathname2);
3736
+ const pushMatch = matchStackRouteByPresentation(
3737
+ compiledRoutes2,
3738
+ parser,
3739
+ target.pathname,
3740
+ PUSH_PRESENTATIONS
3741
+ );
3742
+ navigateWithHistoryIndex(to, {
3743
+ commit: "pop",
3744
+ state: stateForSplitNavigationTarget(options.state, pushMatch, "pop")
3745
+ });
3746
+ }, []);
3642
3747
  const goBack = useCallback(() => {
3643
3748
  if (goBackTopModal()) return true;
3644
3749
  const activeEntry = visibleActiveEntryRef.current;
@@ -3685,11 +3790,12 @@ function RouterRuntime({
3685
3790
  () => ({
3686
3791
  navigate,
3687
3792
  replace,
3793
+ popTo,
3688
3794
  close: closeModal,
3689
3795
  closeAll: closeAllModals,
3690
3796
  goBack
3691
3797
  }),
3692
- [closeAllModals, closeModal, goBack, navigate, replace]
3798
+ [closeAllModals, closeModal, goBack, navigate, popTo, replace]
3693
3799
  );
3694
3800
  useEffect(() => {
3695
3801
  if (visibleActiveEntry) {
@@ -3761,6 +3867,8 @@ function RouterRuntime({
3761
3867
  "data-route-stack-phase": phase,
3762
3868
  "data-route-stack-transition": transitionStatus,
3763
3869
  className: screenClassName(entry, phase, transitionStatus, animationType),
3870
+ "aria-hidden": phase === "active" ? void 0 : true,
3871
+ inert: phase === "active" ? void 0 : true,
3764
3872
  style: { zIndex },
3765
3873
  children: /* @__PURE__ */ jsx("div", { className: "stack-screen-container", children: /* @__PURE__ */ jsx(FrozenStackEntry, { entry, fallback: fallbackNode }) })
3766
3874
  },