@sigmela/router 0.3.7 → 0.5.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.
Files changed (118) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/LICENSE +2 -1
  3. package/README.md +127 -471
  4. package/dist/Router.d.ts +190 -0
  5. package/dist/Router.d.ts.map +1 -0
  6. package/dist/components/Modal.d.ts +56 -0
  7. package/dist/components/Modal.d.ts.map +1 -0
  8. package/dist/components/OverlayContentScope.d.ts +8 -0
  9. package/dist/components/OverlayContentScope.d.ts.map +1 -0
  10. package/dist/components/RouterCloseButton.d.ts +9 -0
  11. package/dist/components/RouterCloseButton.d.ts.map +1 -0
  12. package/dist/components/Sheet.d.ts +56 -0
  13. package/dist/components/Sheet.d.ts.map +1 -0
  14. package/dist/components/SheetSurface.d.ts +27 -0
  15. package/dist/components/SheetSurface.d.ts.map +1 -0
  16. package/dist/components/index.d.ts +4 -0
  17. package/dist/components/index.d.ts.map +1 -0
  18. package/dist/components/pageScrollLock.d.ts +3 -0
  19. package/dist/components/pageScrollLock.d.ts.map +1 -0
  20. package/dist/components/sheetDragEngine.d.ts +27 -0
  21. package/dist/components/sheetDragEngine.d.ts.map +1 -0
  22. package/dist/index.d.ts +5 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +3696 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/navigationHistory.d.ts +17 -0
  27. package/dist/navigationHistory.d.ts.map +1 -0
  28. package/dist/router.css +975 -0
  29. package/dist/types.d.ts +3 -0
  30. package/dist/types.d.ts.map +1 -0
  31. package/dist/utils/id.d.ts +2 -0
  32. package/dist/utils/id.d.ts.map +1 -0
  33. package/package.json +73 -174
  34. package/lib/module/Drawer/Drawer.js +0 -250
  35. package/lib/module/Drawer/DrawerContext.js +0 -4
  36. package/lib/module/Drawer/DrawerIcon.web.js +0 -47
  37. package/lib/module/Drawer/RenderDrawer.native.js +0 -248
  38. package/lib/module/Drawer/RenderDrawer.web.js +0 -197
  39. package/lib/module/Drawer/useDrawer.js +0 -11
  40. package/lib/module/Navigation.js +0 -80
  41. package/lib/module/NavigationStack.js +0 -254
  42. package/lib/module/Router.js +0 -2171
  43. package/lib/module/RouterContext.js +0 -33
  44. package/lib/module/ScreenStack/ScreenStack.native.js +0 -3
  45. package/lib/module/ScreenStack/ScreenStack.web.js +0 -561
  46. package/lib/module/ScreenStack/ScreenStackContext.js +0 -21
  47. package/lib/module/ScreenStack/animationHelpers.js +0 -87
  48. package/lib/module/ScreenStack/index.js +0 -3
  49. package/lib/module/ScreenStackItem/ScreenStackItem.js +0 -70
  50. package/lib/module/ScreenStackItem/ScreenStackItem.types.js +0 -3
  51. package/lib/module/ScreenStackItem/ScreenStackItem.web.js +0 -171
  52. package/lib/module/ScreenStackItem/index.js +0 -3
  53. package/lib/module/ScreenStackSheetItem/ScreenStackSheetItem.native.js +0 -70
  54. package/lib/module/ScreenStackSheetItem/ScreenStackSheetItem.web.js +0 -5
  55. package/lib/module/ScreenStackSheetItem/index.js +0 -3
  56. package/lib/module/SplitView/RenderSplitView.native.js +0 -101
  57. package/lib/module/SplitView/RenderSplitView.web.js +0 -107
  58. package/lib/module/SplitView/SplitView.js +0 -97
  59. package/lib/module/SplitView/SplitViewContext.js +0 -4
  60. package/lib/module/SplitView/index.js +0 -5
  61. package/lib/module/SplitView/useSplitView.js +0 -11
  62. package/lib/module/StackRenderer.js +0 -42
  63. package/lib/module/TabBar/RenderTabBar.native.js +0 -418
  64. package/lib/module/TabBar/RenderTabBar.web.js +0 -218
  65. package/lib/module/TabBar/TabBar.js +0 -208
  66. package/lib/module/TabBar/TabBarContext.js +0 -4
  67. package/lib/module/TabBar/TabIcon.web.js +0 -47
  68. package/lib/module/TabBar/index.js +0 -6
  69. package/lib/module/TabBar/useTabBar.js +0 -11
  70. package/lib/module/TabBar/useTabBarHeight.js +0 -22
  71. package/lib/module/createController.js +0 -5
  72. package/lib/module/index.js +0 -15
  73. package/lib/module/navigationNode.js +0 -3
  74. package/lib/module/package.json +0 -1
  75. package/lib/module/styles.css +0 -1111
  76. package/lib/module/types.js +0 -13
  77. package/lib/typescript/package.json +0 -1
  78. package/lib/typescript/src/Drawer/Drawer.d.ts +0 -100
  79. package/lib/typescript/src/Drawer/DrawerContext.d.ts +0 -3
  80. package/lib/typescript/src/Drawer/DrawerIcon.web.d.ts +0 -7
  81. package/lib/typescript/src/Drawer/RenderDrawer.native.d.ts +0 -8
  82. package/lib/typescript/src/Drawer/RenderDrawer.web.d.ts +0 -8
  83. package/lib/typescript/src/Drawer/useDrawer.d.ts +0 -2
  84. package/lib/typescript/src/Navigation.d.ts +0 -17
  85. package/lib/typescript/src/NavigationStack.d.ts +0 -46
  86. package/lib/typescript/src/Router.d.ts +0 -232
  87. package/lib/typescript/src/RouterContext.d.ts +0 -18
  88. package/lib/typescript/src/ScreenStack/ScreenStack.native.d.ts +0 -2
  89. package/lib/typescript/src/ScreenStack/ScreenStack.web.d.ts +0 -9
  90. package/lib/typescript/src/ScreenStack/ScreenStackContext.d.ts +0 -31
  91. package/lib/typescript/src/ScreenStack/animationHelpers.d.ts +0 -6
  92. package/lib/typescript/src/ScreenStack/index.d.ts +0 -2
  93. package/lib/typescript/src/ScreenStackItem/ScreenStackItem.d.ts +0 -3
  94. package/lib/typescript/src/ScreenStackItem/ScreenStackItem.types.d.ts +0 -14
  95. package/lib/typescript/src/ScreenStackItem/ScreenStackItem.web.d.ts +0 -3
  96. package/lib/typescript/src/ScreenStackItem/index.d.ts +0 -3
  97. package/lib/typescript/src/ScreenStackSheetItem/ScreenStackSheetItem.native.d.ts +0 -11
  98. package/lib/typescript/src/ScreenStackSheetItem/ScreenStackSheetItem.web.d.ts +0 -2
  99. package/lib/typescript/src/ScreenStackSheetItem/index.d.ts +0 -2
  100. package/lib/typescript/src/SplitView/RenderSplitView.native.d.ts +0 -19
  101. package/lib/typescript/src/SplitView/RenderSplitView.web.d.ts +0 -8
  102. package/lib/typescript/src/SplitView/SplitView.d.ts +0 -33
  103. package/lib/typescript/src/SplitView/SplitViewContext.d.ts +0 -3
  104. package/lib/typescript/src/SplitView/index.d.ts +0 -5
  105. package/lib/typescript/src/SplitView/useSplitView.d.ts +0 -2
  106. package/lib/typescript/src/StackRenderer.d.ts +0 -8
  107. package/lib/typescript/src/TabBar/RenderTabBar.native.d.ts +0 -8
  108. package/lib/typescript/src/TabBar/RenderTabBar.web.d.ts +0 -8
  109. package/lib/typescript/src/TabBar/TabBar.d.ts +0 -112
  110. package/lib/typescript/src/TabBar/TabBarContext.d.ts +0 -3
  111. package/lib/typescript/src/TabBar/TabIcon.web.d.ts +0 -7
  112. package/lib/typescript/src/TabBar/index.d.ts +0 -5
  113. package/lib/typescript/src/TabBar/useTabBar.d.ts +0 -2
  114. package/lib/typescript/src/TabBar/useTabBarHeight.d.ts +0 -18
  115. package/lib/typescript/src/createController.d.ts +0 -15
  116. package/lib/typescript/src/index.d.ts +0 -21
  117. package/lib/typescript/src/navigationNode.d.ts +0 -41
  118. package/lib/typescript/src/types.d.ts +0 -140
package/dist/index.js ADDED
@@ -0,0 +1,3696 @@
1
+ import { createContext, memo, useMemo, Suspense, useCallback, useLayoutEffect, createElement, useState, useRef, useEffect, useContext, Children, isValidElement, Fragment as Fragment$1 } from 'react';
2
+ import { useTransitionMap } from 'react-transition-state';
3
+ import { Router as Router$1, useLocation, useSearch, useRouter as useRouter$1, matchRoute } from 'wouter';
4
+ import { navigate, useHistoryState as useHistoryState$1 } from 'wouter/use-browser-location';
5
+ import { useMotionValue, animate, motion } from 'motion/react';
6
+ import { disableScroll, enableScroll } from '@fluejs/noscroll';
7
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
8
+ import './router.css';
9
+
10
+ // src/Router.tsx
11
+ var pageScrollLockCount = 0;
12
+ function getPageScrollElement() {
13
+ return document.querySelector(
14
+ ".screen-stack > .screen-stack-item.phase-active > .stack-screen-container"
15
+ ) ?? document.getElementById("root");
16
+ }
17
+ function usePageScrollLock(active) {
18
+ useEffect(() => {
19
+ const scrollEl = getPageScrollElement();
20
+ if (!scrollEl) return;
21
+ pageScrollLockCount += 1;
22
+ if (pageScrollLockCount === 1) {
23
+ disableScroll(scrollEl);
24
+ }
25
+ return () => {
26
+ pageScrollLockCount = Math.max(0, pageScrollLockCount - 1);
27
+ if (pageScrollLockCount === 0) {
28
+ enableScroll(scrollEl);
29
+ }
30
+ };
31
+ }, [active]);
32
+ }
33
+ var SHEET_DRAG_EPS = 0.5;
34
+ var SHEET_DRAG_START_DISTANCE = 2;
35
+ function sheetScrollAtTop(el) {
36
+ return !el || el.scrollTop <= SHEET_DRAG_EPS;
37
+ }
38
+ function sheetDragStartsInInteractiveControl(target) {
39
+ if (!(target instanceof Element)) return false;
40
+ return Boolean(
41
+ target.closest(
42
+ 'button,a,input,textarea,select,[role="button"],[data-sheet-drag-ignore="true"]'
43
+ )
44
+ );
45
+ }
46
+ function sheetDragStartsInTextInput(target) {
47
+ if (!(target instanceof Element)) return false;
48
+ return Boolean(
49
+ target.closest(
50
+ 'input,textarea,select,[contenteditable="true"],[data-sheet-drag-ignore="true"]'
51
+ )
52
+ );
53
+ }
54
+ function useSheetDragEngine({
55
+ targetRef,
56
+ enabled,
57
+ minDragDistance = SHEET_DRAG_START_DISTANCE,
58
+ ignoreStartTarget,
59
+ shouldStart,
60
+ onStart,
61
+ onMove,
62
+ onEnd,
63
+ suppressClickOnDrag = false
64
+ }) {
65
+ useEffect(() => {
66
+ if (!enabled) return;
67
+ const target = targetRef.current;
68
+ if (!target) return;
69
+ const drag = {
70
+ active: false,
71
+ tracking: false,
72
+ pointerId: -1,
73
+ startY: 0,
74
+ lastY: 0,
75
+ lastT: 0,
76
+ velocityY: 0,
77
+ lastClientY: 0,
78
+ lastTotalY: 0
79
+ };
80
+ let suppressNextClick = false;
81
+ let clearSuppressClickTimer = 0;
82
+ const clearSuppressNextClick = () => {
83
+ suppressNextClick = false;
84
+ if (clearSuppressClickTimer) {
85
+ window.clearTimeout(clearSuppressClickTimer);
86
+ clearSuppressClickTimer = 0;
87
+ }
88
+ };
89
+ const scheduleSuppressClickClear = () => {
90
+ if (!suppressClickOnDrag) return;
91
+ if (clearSuppressClickTimer) window.clearTimeout(clearSuppressClickTimer);
92
+ clearSuppressClickTimer = window.setTimeout(() => {
93
+ suppressNextClick = false;
94
+ clearSuppressClickTimer = 0;
95
+ }, 350);
96
+ };
97
+ const beginTracking = (clientY) => {
98
+ drag.active = false;
99
+ drag.tracking = true;
100
+ drag.pointerId = -1;
101
+ drag.startY = clientY;
102
+ drag.lastY = clientY;
103
+ drag.lastT = performance.now();
104
+ drag.velocityY = 0;
105
+ drag.lastClientY = clientY;
106
+ drag.lastTotalY = 0;
107
+ };
108
+ const resetTracking = () => {
109
+ drag.active = false;
110
+ drag.tracking = false;
111
+ drag.pointerId = -1;
112
+ drag.velocityY = 0;
113
+ drag.lastTotalY = 0;
114
+ };
115
+ const makeUpdate = (clientY, event, now) => {
116
+ const deltaY = clientY - drag.lastY;
117
+ const totalY = clientY - drag.startY;
118
+ const dt = Math.max(1, now - drag.lastT);
119
+ const velocityY = deltaY / dt * 1e3;
120
+ return { clientY, deltaY, totalY, velocityY, event };
121
+ };
122
+ const updateTracking = (clientY, event) => {
123
+ if (!drag.tracking) return;
124
+ const now = performance.now();
125
+ const update = makeUpdate(clientY, event, now);
126
+ drag.lastClientY = clientY;
127
+ drag.lastTotalY = update.totalY;
128
+ if (!drag.active) {
129
+ if (Math.abs(update.totalY) <= minDragDistance) {
130
+ drag.lastY = clientY;
131
+ drag.lastT = now;
132
+ return;
133
+ }
134
+ if (!shouldStart(update)) {
135
+ drag.lastY = clientY;
136
+ drag.lastT = now;
137
+ return;
138
+ }
139
+ drag.active = true;
140
+ if (suppressClickOnDrag) suppressNextClick = true;
141
+ if (event instanceof PointerEvent) {
142
+ try {
143
+ target.setPointerCapture(event.pointerId);
144
+ } catch {
145
+ }
146
+ }
147
+ onStart(update);
148
+ }
149
+ if (event.cancelable) event.preventDefault();
150
+ drag.velocityY = update.velocityY;
151
+ onMove(update);
152
+ drag.lastY = clientY;
153
+ drag.lastT = now;
154
+ };
155
+ const finishTracking = (event) => {
156
+ if (!drag.tracking) return;
157
+ const now = performance.now();
158
+ const update = makeUpdate(drag.lastClientY, event, now);
159
+ const wasActive = drag.active;
160
+ if (event instanceof PointerEvent) {
161
+ try {
162
+ target.releasePointerCapture(event.pointerId);
163
+ } catch {
164
+ }
165
+ }
166
+ const velocityY = drag.velocityY || update.velocityY;
167
+ resetTracking();
168
+ if (!wasActive) return;
169
+ if (event.cancelable) event.preventDefault();
170
+ scheduleSuppressClickClear();
171
+ onEnd({ ...update, velocityY });
172
+ };
173
+ const onTouchStart = (event) => {
174
+ if (event.touches.length !== 1 || ignoreStartTarget?.(event.target)) {
175
+ resetTracking();
176
+ return;
177
+ }
178
+ beginTracking(event.touches[0]?.clientY ?? 0);
179
+ };
180
+ const onTouchMove = (event) => {
181
+ if (event.touches.length !== 1) return;
182
+ updateTracking(event.touches[0]?.clientY ?? drag.lastY, event);
183
+ };
184
+ const onPointerDown = (event) => {
185
+ if (event.pointerType === "touch" || !event.isPrimary || event.button !== 0 || ignoreStartTarget?.(event.target)) {
186
+ resetTracking();
187
+ return;
188
+ }
189
+ beginTracking(event.clientY);
190
+ drag.pointerId = event.pointerId;
191
+ };
192
+ const onPointerMove = (event) => {
193
+ if (!drag.tracking || drag.pointerId !== event.pointerId) return;
194
+ updateTracking(event.clientY, event);
195
+ };
196
+ const onPointerEnd = (event) => {
197
+ if (!drag.tracking || drag.pointerId !== event.pointerId) return;
198
+ finishTracking(event);
199
+ };
200
+ const onClickCapture = (event) => {
201
+ if (!suppressNextClick) return;
202
+ clearSuppressNextClick();
203
+ event.preventDefault();
204
+ event.stopPropagation();
205
+ };
206
+ target.addEventListener("touchstart", onTouchStart, { passive: true });
207
+ target.addEventListener("touchmove", onTouchMove, { passive: false });
208
+ target.addEventListener("touchend", finishTracking, { passive: false });
209
+ target.addEventListener("touchcancel", finishTracking, { passive: false });
210
+ target.addEventListener("pointerdown", onPointerDown);
211
+ target.addEventListener("pointermove", onPointerMove, { passive: false });
212
+ target.addEventListener("pointerup", onPointerEnd);
213
+ target.addEventListener("pointercancel", onPointerEnd);
214
+ if (suppressClickOnDrag) target.addEventListener("click", onClickCapture, true);
215
+ return () => {
216
+ target.removeEventListener("touchstart", onTouchStart);
217
+ target.removeEventListener("touchmove", onTouchMove);
218
+ target.removeEventListener("touchend", finishTracking);
219
+ target.removeEventListener("touchcancel", finishTracking);
220
+ target.removeEventListener("pointerdown", onPointerDown);
221
+ target.removeEventListener("pointermove", onPointerMove);
222
+ target.removeEventListener("pointerup", onPointerEnd);
223
+ target.removeEventListener("pointercancel", onPointerEnd);
224
+ if (suppressClickOnDrag) target.removeEventListener("click", onClickCapture, true);
225
+ if (clearSuppressClickTimer) window.clearTimeout(clearSuppressClickTimer);
226
+ };
227
+ }, [
228
+ enabled,
229
+ ignoreStartTarget,
230
+ minDragDistance,
231
+ onEnd,
232
+ onMove,
233
+ onStart,
234
+ shouldStart,
235
+ suppressClickOnDrag,
236
+ targetRef
237
+ ]);
238
+ }
239
+ var SHEET_SPRING = { type: "spring", bounce: 0, duration: 0.38 };
240
+ var CLOSE_DISTANCE = 112;
241
+ var CLOSE_VELOCITY = 850;
242
+ var DRAG_START_DISTANCE = 6;
243
+ var PROJECT_VELOCITY_FACTOR = 0.18;
244
+ function closeTargetY(surface, target) {
245
+ if (target === "viewport") return window.innerHeight + 32;
246
+ return (surface?.getBoundingClientRect().height || window.innerHeight) + 32;
247
+ }
248
+ function SheetSurface({
249
+ children,
250
+ open,
251
+ onRequestClose,
252
+ onExitComplete,
253
+ zIndex,
254
+ mode,
255
+ canDismiss = true,
256
+ swipeToClose = true,
257
+ backdrop = false,
258
+ backdropClassName,
259
+ backdropStyle,
260
+ shellClassName,
261
+ surfaceClassName,
262
+ surfaceStyle,
263
+ scrollElement,
264
+ ariaLabel,
265
+ shell = "mobile-contained",
266
+ closeTarget = "surface"
267
+ }) {
268
+ const surfaceRef = useRef(null);
269
+ const removeRef = useRef(onExitComplete);
270
+ const requestCloseRef = useRef(onRequestClose);
271
+ const closingOffsetRef = useRef(0);
272
+ const closingRef = useRef(false);
273
+ const y = useMotionValue(window.innerHeight);
274
+ removeRef.current = onExitComplete;
275
+ requestCloseRef.current = onRequestClose;
276
+ usePageScrollLock(true);
277
+ const requestClose = useCallback(
278
+ (offset = 0) => {
279
+ if (!canDismiss || closingRef.current) return;
280
+ closingOffsetRef.current = offset;
281
+ requestCloseRef.current();
282
+ },
283
+ [canDismiss]
284
+ );
285
+ const closeWithAnimation = useCallback(
286
+ (offset = 0) => {
287
+ if (closingRef.current) return;
288
+ closingRef.current = true;
289
+ const target = closeTargetY(surfaceRef.current, closeTarget);
290
+ y.stop();
291
+ y.set(Math.max(0, offset));
292
+ const controls = animate(y, target, SHEET_SPRING);
293
+ void controls.finished.then(
294
+ () => {
295
+ removeRef.current();
296
+ },
297
+ () => {
298
+ closingRef.current = false;
299
+ }
300
+ );
301
+ },
302
+ [closeTarget, y]
303
+ );
304
+ useEffect(() => {
305
+ if (!open) {
306
+ closeWithAnimation(closingOffsetRef.current);
307
+ return;
308
+ }
309
+ y.set(window.innerHeight);
310
+ const controls = animate(y, 0, SHEET_SPRING);
311
+ return () => controls.stop();
312
+ }, [closeWithAnimation, open, y]);
313
+ useEffect(() => {
314
+ if (!canDismiss) return;
315
+ const handler = (event) => {
316
+ if (event.key === "Escape") requestClose();
317
+ };
318
+ document.addEventListener("keydown", handler);
319
+ return () => document.removeEventListener("keydown", handler);
320
+ }, [canDismiss, requestClose]);
321
+ const shouldStartDrag = useCallback(
322
+ (update) => update.totalY > 0 && sheetScrollAtTop(scrollElement ?? null),
323
+ [scrollElement]
324
+ );
325
+ const handleDragStart = useCallback(() => {
326
+ y.stop();
327
+ }, [y]);
328
+ const handleDragMove = useCallback(
329
+ (update) => {
330
+ y.set(Math.max(0, y.get() + update.deltaY));
331
+ },
332
+ [y]
333
+ );
334
+ const handleDragEnd = useCallback(
335
+ (update) => {
336
+ const offset = y.get();
337
+ const projected = offset + update.velocityY * PROJECT_VELOCITY_FACTOR;
338
+ if (projected > CLOSE_DISTANCE || update.velocityY > CLOSE_VELOCITY) {
339
+ requestClose(offset);
340
+ return;
341
+ }
342
+ y.stop();
343
+ void animate(y, 0, SHEET_SPRING).finished.catch(() => {
344
+ });
345
+ },
346
+ [requestClose, y]
347
+ );
348
+ useSheetDragEngine({
349
+ targetRef: surfaceRef,
350
+ enabled: swipeToClose && canDismiss,
351
+ minDragDistance: DRAG_START_DISTANCE,
352
+ ignoreStartTarget: sheetDragStartsInTextInput,
353
+ shouldStart: shouldStartDrag,
354
+ onStart: handleDragStart,
355
+ onMove: handleDragMove,
356
+ onEnd: handleDragEnd,
357
+ suppressClickOnDrag: true
358
+ });
359
+ const baseShellClassName = mode === "fullscreen" ? "router-overlay-shell router-overlay-shell--fullscreen" : "router-overlay-shell router-overlay-shell--content";
360
+ const overlayShellClassName = shellClassName ? `${baseShellClassName} ${shellClassName}` : baseShellClassName;
361
+ const shellStyle = useMemo(
362
+ () => ({
363
+ zIndex
364
+ }),
365
+ [zIndex]
366
+ );
367
+ const node = /* @__PURE__ */ jsxs("div", { className: overlayShellClassName, "data-router-shell": shell, style: shellStyle, children: [
368
+ backdrop ? /* @__PURE__ */ jsx(
369
+ motion.button,
370
+ {
371
+ type: "button",
372
+ "aria-label": "Close",
373
+ className: `pointer-events-auto absolute inset-0 border-0 p-0 ${backdropClassName ?? ""}`,
374
+ style: backdropStyle,
375
+ initial: { opacity: 0 },
376
+ animate: { opacity: open ? 1 : 0 },
377
+ transition: { duration: 0.2, ease: "easeOut" },
378
+ onClick: () => requestClose()
379
+ }
380
+ ) : null,
381
+ /* @__PURE__ */ jsx(
382
+ motion.div,
383
+ {
384
+ ref: surfaceRef,
385
+ className: surfaceClassName,
386
+ style: { ...surfaceStyle, y },
387
+ role: "dialog",
388
+ "aria-modal": "true",
389
+ "aria-label": ariaLabel,
390
+ children
391
+ }
392
+ )
393
+ ] });
394
+ return node;
395
+ }
396
+ function CloseIcon() {
397
+ return /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
398
+ "path",
399
+ {
400
+ d: "M4.75 4.75L13.25 13.25M13.25 4.75L4.75 13.25",
401
+ stroke: "currentColor",
402
+ strokeLinecap: "round",
403
+ strokeWidth: "2.2"
404
+ }
405
+ ) });
406
+ }
407
+ function RouterCloseButton({
408
+ onPress,
409
+ disabled,
410
+ tone = "default",
411
+ ariaLabel = "Close"
412
+ }) {
413
+ const toneClass = tone === "light" ? " router-close-button--light" : "";
414
+ return /* @__PURE__ */ jsxs(
415
+ "button",
416
+ {
417
+ type: "button",
418
+ "aria-label": ariaLabel,
419
+ className: `router-close-button router-close-button-circle${toneClass}`,
420
+ disabled,
421
+ onClick: onPress,
422
+ children: [
423
+ /* @__PURE__ */ jsx("div", { className: `router-close-button-inner router-close-button-circle${toneClass}`, children: /* @__PURE__ */ jsx(CloseIcon, {}) }),
424
+ /* @__PURE__ */ jsxs("div", { className: "router-close-button-blur router-close-button-circle", children: [
425
+ /* @__PURE__ */ jsx("div", { className: "router-close-mask-1", "aria-hidden": "true" }),
426
+ /* @__PURE__ */ jsx("div", { className: "router-close-mask-2", "aria-hidden": "true" }),
427
+ /* @__PURE__ */ jsx("div", { className: "router-close-mask-3", "aria-hidden": "true" })
428
+ ] })
429
+ ]
430
+ }
431
+ );
432
+ }
433
+ var DEFAULT_OVERLAY_CONTENT_KEY = "router-overlay-content:default";
434
+ var OverlayContentKeyContext = createContext(DEFAULT_OVERLAY_CONTENT_KEY);
435
+ function OverlayContentScope({
436
+ contentKey,
437
+ children
438
+ }) {
439
+ return /* @__PURE__ */ jsx(OverlayContentKeyContext.Provider, { value: contentKey, children });
440
+ }
441
+ function useOverlayContentKey() {
442
+ return useContext(OverlayContentKeyContext);
443
+ }
444
+ var ModalContext = createContext(null);
445
+ function useModalContext() {
446
+ const ctx = useContext(ModalContext);
447
+ if (!ctx) {
448
+ throw new Error("Modal sub-components must be rendered inside <Modal>");
449
+ }
450
+ return ctx;
451
+ }
452
+ function useModalSwipeTarget() {
453
+ const { setSwipeTarget } = useModalContext();
454
+ const contentKey = useOverlayContentKey();
455
+ return useCallback(
456
+ (el) => setSwipeTarget(contentKey, el),
457
+ [contentKey, setSwipeTarget]
458
+ );
459
+ }
460
+ function ModalFrame({
461
+ children,
462
+ open,
463
+ onRequestClose,
464
+ onExitComplete,
465
+ zIndex,
466
+ shell = "mobile-contained",
467
+ contentSized = false,
468
+ activeContentKey = DEFAULT_OVERLAY_CONTENT_KEY
469
+ }) {
470
+ const [registrations, setRegistrations] = useState(() => /* @__PURE__ */ new Map());
471
+ const setSwipeTarget = useCallback(
472
+ (contentKey, swipeTarget2) => {
473
+ setRegistrations((current) => {
474
+ const registration = current.get(contentKey);
475
+ if ((registration?.swipeTarget ?? null) === swipeTarget2) return current;
476
+ const next = new Map(current);
477
+ if (!swipeTarget2 && !registration?.options) {
478
+ next.delete(contentKey);
479
+ } else {
480
+ next.set(contentKey, { ...registration, swipeTarget: swipeTarget2 });
481
+ }
482
+ return next;
483
+ });
484
+ },
485
+ []
486
+ );
487
+ const setOptions = useCallback((contentKey, options2) => {
488
+ setRegistrations((current) => {
489
+ const registration = current.get(contentKey);
490
+ if (registration?.options === options2) return current;
491
+ const next = new Map(current);
492
+ if (!options2 && !registration?.swipeTarget) {
493
+ next.delete(contentKey);
494
+ } else {
495
+ next.set(contentKey, {
496
+ ...registration,
497
+ options: options2 ?? void 0
498
+ });
499
+ }
500
+ return next;
501
+ });
502
+ }, []);
503
+ const activeRegistration = registrations.get(activeContentKey);
504
+ const swipeTarget = activeRegistration?.swipeTarget ?? null;
505
+ const options = activeRegistration?.options;
506
+ const swipeToClose = options?.swipeToClose ?? true;
507
+ const ariaLabel = options?.ariaLabel;
508
+ const isContentSized = contentSized || options?.presentation === "content";
509
+ const requestClose = useCallback(() => {
510
+ onRequestClose();
511
+ }, [onRequestClose]);
512
+ const contextValue = useMemo(
513
+ () => ({ setSwipeTarget, requestClose, setOptions }),
514
+ [requestClose, setOptions, setSwipeTarget]
515
+ );
516
+ return /* @__PURE__ */ jsx(ModalContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
517
+ SheetSurface,
518
+ {
519
+ open,
520
+ onRequestClose: requestClose,
521
+ onExitComplete,
522
+ zIndex,
523
+ mode: "fullscreen",
524
+ swipeToClose,
525
+ shell,
526
+ backdrop: true,
527
+ backdropClassName: `router-modal-backdrop${isContentSized ? " router-modal-backdrop--transparent" : ""}`,
528
+ shellClassName: "router-modal-shell",
529
+ surfaceClassName: `router-modal-surface${isContentSized ? " router-modal-surface--content-sized" : ""}${options?.size === "wide" ? " router-modal-surface--wide" : ""}`,
530
+ scrollElement: swipeTarget,
531
+ ariaLabel,
532
+ closeTarget: isContentSized ? "surface" : "viewport",
533
+ children
534
+ }
535
+ ) });
536
+ }
537
+ function ModalRoot({
538
+ children,
539
+ swipeToClose = true,
540
+ ariaLabel,
541
+ size = "regular",
542
+ presentation = "fullscreen"
543
+ }) {
544
+ const { setOptions } = useModalContext();
545
+ const contentKey = useOverlayContentKey();
546
+ useLayoutEffect(() => {
547
+ setOptions(contentKey, { swipeToClose, ariaLabel, size, presentation });
548
+ return () => setOptions(contentKey, null);
549
+ }, [ariaLabel, contentKey, presentation, setOptions, size, swipeToClose]);
550
+ return /* @__PURE__ */ jsx(Fragment, { children });
551
+ }
552
+ function ModalHeader({
553
+ title,
554
+ leftAction,
555
+ rightAction,
556
+ hideCloseButton,
557
+ sticky = true
558
+ }) {
559
+ const { requestClose } = useModalContext();
560
+ const headerClassName = `router-modal-header${sticky ? " router-modal-header--sticky" : ""}`;
561
+ return /* @__PURE__ */ jsxs("div", { className: headerClassName, children: [
562
+ /* @__PURE__ */ jsx("div", { className: "router-modal-header-side", children: leftAction }),
563
+ /* @__PURE__ */ jsx("h2", { className: "router-modal-title", children: title }),
564
+ /* @__PURE__ */ jsx("div", { className: "router-modal-header-side router-modal-header-side--trailing", children: rightAction ?? (hideCloseButton ? null : /* @__PURE__ */ jsx(RouterCloseButton, { onPress: requestClose })) })
565
+ ] });
566
+ }
567
+ function ModalContent({ children, className }) {
568
+ return /* @__PURE__ */ jsx("div", { className: `router-modal-content ${className ?? ""}`, children });
569
+ }
570
+ function ModalScrollView({ children, className, style }) {
571
+ const setSwipeTarget = useModalSwipeTarget();
572
+ return /* @__PURE__ */ jsx(
573
+ "div",
574
+ {
575
+ ref: setSwipeTarget,
576
+ className: `router-scroll router-modal-scroll-view ${className ?? ""}`,
577
+ style,
578
+ children
579
+ }
580
+ );
581
+ }
582
+ function ModalFooter({ children, className }) {
583
+ return /* @__PURE__ */ jsx(
584
+ "div",
585
+ {
586
+ className: `router-modal-footer ${className ?? ""}`,
587
+ children: /* @__PURE__ */ jsx("div", { className: "router-modal-footer-content", children })
588
+ }
589
+ );
590
+ }
591
+ var Modal = Object.assign(ModalRoot, {
592
+ Header: ModalHeader,
593
+ Content: ModalContent,
594
+ ScrollView: ModalScrollView,
595
+ Footer: ModalFooter
596
+ });
597
+ var SheetContext = createContext(null);
598
+ function useSheetContext() {
599
+ const ctx = useContext(SheetContext);
600
+ if (!ctx) {
601
+ throw new Error("Sheet sub-components must be rendered inside <Sheet>");
602
+ }
603
+ return ctx;
604
+ }
605
+ function SheetFrame({
606
+ children,
607
+ open,
608
+ onRequestClose,
609
+ onExitComplete,
610
+ zIndex,
611
+ shell = "mobile-contained",
612
+ activeContentKey = DEFAULT_OVERLAY_CONTENT_KEY
613
+ }) {
614
+ const [frameScrollElement, setFrameScrollElement] = useState(null);
615
+ const [registrations, setRegistrations] = useState(() => /* @__PURE__ */ new Map());
616
+ const setScrollElement = useCallback(
617
+ (contentKey, scrollElement2) => {
618
+ setRegistrations((current) => {
619
+ const registration = current.get(contentKey);
620
+ if ((registration?.scrollElement ?? null) === scrollElement2) return current;
621
+ const next = new Map(current);
622
+ if (!scrollElement2 && !registration?.options) {
623
+ next.delete(contentKey);
624
+ } else {
625
+ next.set(contentKey, { ...registration, scrollElement: scrollElement2 });
626
+ }
627
+ return next;
628
+ });
629
+ },
630
+ []
631
+ );
632
+ const setOptions = useCallback((contentKey, options2) => {
633
+ setRegistrations((current) => {
634
+ const registration = current.get(contentKey);
635
+ if (registration?.options === options2) return current;
636
+ const next = new Map(current);
637
+ if (!options2 && !registration?.scrollElement) {
638
+ next.delete(contentKey);
639
+ } else {
640
+ next.set(contentKey, {
641
+ ...registration,
642
+ options: options2 ?? void 0
643
+ });
644
+ }
645
+ return next;
646
+ });
647
+ }, []);
648
+ const activeRegistration = registrations.get(activeContentKey);
649
+ const scrollElement = activeRegistration?.scrollElement ?? frameScrollElement;
650
+ const options = activeRegistration?.options;
651
+ const disableDismiss = options?.disableDismiss ?? false;
652
+ const customRequestClose = options?.onRequestClose;
653
+ const variant = options?.variant ?? "detached";
654
+ const presentation = options?.presentation ?? "content";
655
+ const fullscreen = presentation === "fullscreen";
656
+ const requestClose = useCallback(() => {
657
+ if (disableDismiss) return;
658
+ if (customRequestClose) {
659
+ customRequestClose();
660
+ return;
661
+ }
662
+ onRequestClose();
663
+ }, [customRequestClose, disableDismiss, onRequestClose]);
664
+ const contextValue = useMemo(
665
+ () => ({
666
+ setScrollElement,
667
+ close: requestClose,
668
+ canClose: !disableDismiss,
669
+ variant,
670
+ presentation,
671
+ setOptions
672
+ }),
673
+ [
674
+ requestClose,
675
+ disableDismiss,
676
+ presentation,
677
+ setScrollElement,
678
+ setOptions,
679
+ variant
680
+ ]
681
+ );
682
+ const surfaceStyle = useMemo(
683
+ () => fullscreen ? {
684
+ backgroundColor: "var(--router-bg)",
685
+ borderRadius: "33px 33px 0 0",
686
+ boxShadow: "var(--router-card-shadow)",
687
+ color: "var(--router-text)",
688
+ overflow: "hidden",
689
+ bottom: 0,
690
+ left: 0,
691
+ right: 0,
692
+ width: "100%",
693
+ maxWidth: "var(--router-container)",
694
+ height: "calc(100dvh - env(safe-area-inset-top, 0px))",
695
+ maxHeight: "none",
696
+ marginInline: "auto"
697
+ } : variant === "detached" ? {
698
+ backgroundColor: "var(--router-bg)",
699
+ borderRadius: "28px 28px 32px 32px",
700
+ boxShadow: "var(--router-card-shadow)",
701
+ color: "var(--router-text)",
702
+ overflow: "hidden",
703
+ // Centered onto the app column with the same 8px gutter on every detached edge.
704
+ bottom: 8,
705
+ left: 8,
706
+ right: 8,
707
+ width: "auto",
708
+ maxWidth: "calc(var(--router-container) - 16px)",
709
+ marginInline: "auto",
710
+ maxHeight: "calc(100dvh - 32px - env(safe-area-inset-top, 0px))"
711
+ } : {
712
+ backgroundColor: "var(--router-bg)",
713
+ borderTop: "1px solid var(--router-line)",
714
+ borderRadius: "28px 28px 0 0",
715
+ boxShadow: "var(--router-card-shadow)",
716
+ color: "var(--router-text)",
717
+ // Centered onto the app column, full column width.
718
+ left: 0,
719
+ right: 0,
720
+ maxWidth: "var(--router-container)",
721
+ marginInline: "auto",
722
+ bottom: 0,
723
+ maxHeight: "calc(100dvh - env(safe-area-inset-top, 0px))"
724
+ },
725
+ [fullscreen, variant]
726
+ );
727
+ const backdropStyle = useMemo(
728
+ () => shell === "desktop-fullscreen" ? {
729
+ background: "var(--router-scrim)",
730
+ left: 0,
731
+ right: 0
732
+ } : {
733
+ background: "var(--router-scrim)",
734
+ left: 0,
735
+ right: 0,
736
+ maxWidth: "var(--router-container)",
737
+ marginInline: "auto"
738
+ },
739
+ [shell]
740
+ );
741
+ return /* @__PURE__ */ jsx(SheetContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
742
+ SheetSurface,
743
+ {
744
+ open,
745
+ onRequestClose: requestClose,
746
+ onExitComplete,
747
+ zIndex,
748
+ mode: "content",
749
+ canDismiss: !disableDismiss,
750
+ shell,
751
+ backdrop: true,
752
+ backdropStyle,
753
+ surfaceClassName: "router-sheet-surface",
754
+ surfaceStyle,
755
+ scrollElement,
756
+ closeTarget: fullscreen ? "viewport" : "surface",
757
+ children: /* @__PURE__ */ jsx(
758
+ "div",
759
+ {
760
+ ref: setFrameScrollElement,
761
+ className: `router-scroll router-sheet-frame-scroll${fullscreen ? " router-sheet-frame-scroll--fullscreen" : ""}`,
762
+ children
763
+ }
764
+ )
765
+ }
766
+ ) });
767
+ }
768
+ function SheetRoot({
769
+ children,
770
+ disableDismiss,
771
+ onRequestClose,
772
+ variant = "detached",
773
+ presentation = "content"
774
+ }) {
775
+ const { setOptions } = useSheetContext();
776
+ const contentKey = useOverlayContentKey();
777
+ useLayoutEffect(() => {
778
+ setOptions(contentKey, {
779
+ disableDismiss,
780
+ onRequestClose,
781
+ variant,
782
+ presentation
783
+ });
784
+ return () => setOptions(contentKey, null);
785
+ }, [
786
+ contentKey,
787
+ disableDismiss,
788
+ onRequestClose,
789
+ presentation,
790
+ setOptions,
791
+ variant
792
+ ]);
793
+ return /* @__PURE__ */ jsx(Fragment, { children });
794
+ }
795
+ function SheetHeader({
796
+ title,
797
+ leftAction,
798
+ rightAction,
799
+ hideCloseButton,
800
+ sticky = true,
801
+ align = "start"
802
+ }) {
803
+ const { close, canClose, presentation } = useSheetContext();
804
+ const headerBaseClassName = presentation === "fullscreen" ? "router-modal-header" : "router-sheet-header";
805
+ const headerClassName = `${headerBaseClassName}${sticky ? ` ${headerBaseClassName}--sticky` : ""}`;
806
+ const titleClassName = `router-sheet-title${align === "center" ? " router-sheet-title--center" : ""}`;
807
+ return /* @__PURE__ */ jsxs("div", { className: headerClassName, children: [
808
+ leftAction != null && /* @__PURE__ */ jsx("div", { className: "router-sheet-header-side", children: leftAction }),
809
+ title && /* @__PURE__ */ jsx("h2", { className: titleClassName, children: title }),
810
+ !title && /* @__PURE__ */ jsx("div", { className: "router-sheet-header-spacer" }),
811
+ /* @__PURE__ */ jsx("div", { className: "router-sheet-header-side router-sheet-header-side--trailing", children: rightAction ?? (hideCloseButton ? null : /* @__PURE__ */ jsx(RouterCloseButton, { onPress: close, disabled: !canClose })) })
812
+ ] });
813
+ }
814
+ function SheetContent({ children, className }) {
815
+ return /* @__PURE__ */ jsx("div", { className: `router-sheet-content ${className ?? ""}`, children });
816
+ }
817
+ function SheetScrollView({ children, className, style }) {
818
+ const { setScrollElement } = useSheetContext();
819
+ const contentKey = useOverlayContentKey();
820
+ const setScopedScrollElement = useCallback(
821
+ (el) => setScrollElement(contentKey, el),
822
+ [contentKey, setScrollElement]
823
+ );
824
+ return /* @__PURE__ */ jsx(
825
+ "div",
826
+ {
827
+ ref: setScopedScrollElement,
828
+ className: `router-scroll router-sheet-scroll-view ${className ?? ""}`,
829
+ style,
830
+ children
831
+ }
832
+ );
833
+ }
834
+ function SheetFooter({ children, className }) {
835
+ const { variant } = useSheetContext();
836
+ const variantClassName = variant === "detached" ? "router-sheet-footer--detached" : "router-sheet-footer--attached";
837
+ return /* @__PURE__ */ jsx(
838
+ "div",
839
+ {
840
+ className: `router-sheet-footer ${variantClassName} ${className ?? ""}`,
841
+ children
842
+ }
843
+ );
844
+ }
845
+ var Sheet = Object.assign(SheetRoot, {
846
+ Header: SheetHeader,
847
+ Content: SheetContent,
848
+ ScrollView: SheetScrollView,
849
+ Footer: SheetFooter
850
+ });
851
+
852
+ // src/navigationHistory.ts
853
+ var MAX_HISTORY_LENGTH = 64;
854
+ function sameLocation(left, right) {
855
+ return left.stackKey === right.stackKey && left.pathname === right.pathname;
856
+ }
857
+ function sameScreenRoute(left, right) {
858
+ return left.stackKey === right.stackKey && left.route.id === right.route.id && left.presentation === right.presentation;
859
+ }
860
+ function latestSnapshots(history) {
861
+ return history.length > MAX_HISTORY_LENGTH ? history.slice(history.length - MAX_HISTORY_LENGTH) : history;
862
+ }
863
+ function updateSnapshotHistory(history, snapshot) {
864
+ const browserIndex = history.findIndex(
865
+ (entry) => entry.historyIndex === snapshot.historyIndex
866
+ );
867
+ const locationIndex = history.findIndex((entry) => sameLocation(entry, snapshot));
868
+ if (snapshot.historyCommit === "replace" && browserIndex !== -1) {
869
+ return latestSnapshots([...history.slice(0, browserIndex), snapshot]);
870
+ }
871
+ if (locationIndex !== -1 && locationIndex !== browserIndex) {
872
+ return latestSnapshots([...history.slice(0, locationIndex), snapshot]);
873
+ }
874
+ if (browserIndex !== -1) {
875
+ return latestSnapshots([...history.slice(0, browserIndex), snapshot]);
876
+ }
877
+ const active = history[history.length - 1];
878
+ if (active && sameScreenRoute(active, snapshot) && snapshot.splitNavigation?.operation !== "push") {
879
+ return latestSnapshots([...history.slice(0, -1), snapshot]);
880
+ }
881
+ const insertIndex = history.findIndex(
882
+ (entry) => entry.historyIndex > snapshot.historyIndex
883
+ );
884
+ const next = insertIndex === -1 ? [...history, snapshot] : [...history.slice(0, insertIndex), snapshot];
885
+ return latestSnapshots(next);
886
+ }
887
+ function previousSnapshotFromHistory(history, snapshot) {
888
+ if (!snapshot) return void 0;
889
+ const currentIndex = history.findIndex(
890
+ (entry) => entry.historyIndex === snapshot.historyIndex
891
+ );
892
+ if (currentIndex <= 0) return void 0;
893
+ return history[currentIndex - 1];
894
+ }
895
+
896
+ // src/utils/id.ts
897
+ function createRouterId() {
898
+ if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.randomUUID === "function") {
899
+ return globalThis.crypto.randomUUID();
900
+ }
901
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
902
+ const value = Math.floor(Math.random() * 16);
903
+ const nibble = char === "x" ? value : value & 3 | 8;
904
+ return nibble.toString(16);
905
+ });
906
+ }
907
+ var STACK_ROUTE_KIND = /* @__PURE__ */ Symbol.for("sigmela.stack.route.kind");
908
+ var HISTORY_INDEX_KEY = "__sigmela_idx";
909
+ var SPLIT_NAVIGATION_KEY = "__sigmela_split_navigation";
910
+ var SPLIT_NAVIGATION_ORIGIN_KEY = "__sigmela_split_navigation_origin";
911
+ var ROUTE_TRANSITION_MS = 300;
912
+ var OVERLAY_STACK_BASE_Z = 60;
913
+ var DEFAULT_SPLIT_MIN_WIDTH = 640;
914
+ var DEFAULT_SPLIT_PRIMARY_MAX_WIDTH = 390;
915
+ var DEFAULT_SPLIT_SECONDARY_MIN_WIDTH = 360;
916
+ var RouterRouteContext = createContext(null);
917
+ var RouterActionsContext = createContext(null);
918
+ var SplitPaneNavigationContext = createContext(null);
919
+ var RouterDrawerContext = createContext(null);
920
+ var CurrentModalContext = createContext(null);
921
+ var REACT_COMPONENT_TYPE_SYMBOLS = /* @__PURE__ */ new Set([
922
+ /* @__PURE__ */ Symbol.for("react.forward_ref"),
923
+ /* @__PURE__ */ Symbol.for("react.lazy"),
924
+ /* @__PURE__ */ Symbol.for("react.memo")
925
+ ]);
926
+ var HISTORY_NAVIGATION_KEY = "__sigmela_nav";
927
+ var HISTORY_COMMIT_KEY = "__sigmela_commit";
928
+ var DEFAULT_SHEET_FALLBACK = /* @__PURE__ */ jsx("div", { className: "min-h-[160px]" });
929
+ function splitViewWideMinWidth(minWidth, primaryMaxWidth, secondaryMinWidth = DEFAULT_SPLIT_SECONDARY_MIN_WIDTH) {
930
+ return Math.max(minWidth, primaryMaxWidth + secondaryMinWidth);
931
+ }
932
+ function isRecord(value) {
933
+ return value !== null && typeof value === "object" && !Array.isArray(value);
934
+ }
935
+ function mutableHistoryState(state) {
936
+ if (isRecord(state)) return { ...state };
937
+ if (state == null) return {};
938
+ return { value: state };
939
+ }
940
+ function splitNavigationFromState(state) {
941
+ if (!isRecord(state)) return void 0;
942
+ const value = state[SPLIT_NAVIGATION_KEY];
943
+ 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") {
945
+ return void 0;
946
+ }
947
+ return {
948
+ splitViewId: value.splitViewId,
949
+ detailPathId: value.detailPathId,
950
+ detailDepth: value.detailDepth,
951
+ operation: value.operation
952
+ };
953
+ }
954
+ function splitNavigationOriginFromState(state) {
955
+ if (!isRecord(state)) return void 0;
956
+ const value = state[SPLIT_NAVIGATION_ORIGIN_KEY];
957
+ if (!isRecord(value)) return void 0;
958
+ if (typeof value.splitViewId !== "string" || value.pane !== "primary" && value.pane !== "secondary" || typeof value.detailPathId !== "string" || typeof value.detailDepth !== "number") {
959
+ return void 0;
960
+ }
961
+ return {
962
+ splitViewId: value.splitViewId,
963
+ pane: value.pane,
964
+ detailPathId: value.detailPathId,
965
+ detailDepth: value.detailDepth
966
+ };
967
+ }
968
+ function stateWithSplitNavigationOrigin(state, origin) {
969
+ const next = mutableHistoryState(state);
970
+ next[SPLIT_NAVIGATION_ORIGIN_KEY] = origin;
971
+ return next;
972
+ }
973
+ function stateWithoutSplitNavigation(state) {
974
+ const next = mutableHistoryState(state);
975
+ delete next[SPLIT_NAVIGATION_KEY];
976
+ delete next[SPLIT_NAVIGATION_ORIGIN_KEY];
977
+ return next;
978
+ }
979
+ function historyIndexFromState(state) {
980
+ if (isRecord(state) && typeof state[HISTORY_INDEX_KEY] === "number") {
981
+ return state[HISTORY_INDEX_KEY];
982
+ }
983
+ if (isRecord(state) && typeof state.idx === "number") {
984
+ return state.idx;
985
+ }
986
+ return 0;
987
+ }
988
+ function historyNavigationKeyFromState(state) {
989
+ if (isRecord(state) && typeof state[HISTORY_NAVIGATION_KEY] === "string") {
990
+ return state[HISTORY_NAVIGATION_KEY];
991
+ }
992
+ return null;
993
+ }
994
+ function historyCommitFromState(state) {
995
+ if (isRecord(state)) {
996
+ const commit = state[HISTORY_COMMIT_KEY];
997
+ if (commit === "sync" || commit === "replace") return commit;
998
+ }
999
+ return "push";
1000
+ }
1001
+ function stateWithHistoryIndex(state, index, navigationKey = createRouterId(), commit = "push") {
1002
+ const next = isRecord(state) ? { ...state } : state == null ? {} : { value: state };
1003
+ next[HISTORY_INDEX_KEY] = index;
1004
+ next[HISTORY_NAVIGATION_KEY] = navigationKey;
1005
+ next[HISTORY_COMMIT_KEY] = commit;
1006
+ return next;
1007
+ }
1008
+ function navigateWithHistoryIndex(to, options = {}) {
1009
+ const { commit = "push", ...navigationOptions } = options;
1010
+ const currentState = window.history.state;
1011
+ const currentIndex = historyIndexFromState(currentState);
1012
+ const isSyncCommit = commit === "sync";
1013
+ const replacesBrowserEntry = commit !== "push";
1014
+ const nextIndex = replacesBrowserEntry ? currentIndex : currentIndex + 1;
1015
+ const currentNavigationKey = historyNavigationKeyFromState(currentState);
1016
+ const nextNavigationKey = isSyncCommit ? historyNavigationKeyFromState(options.state) ?? currentNavigationKey ?? createRouterId() : replacesBrowserEntry ? currentNavigationKey ?? createRouterId() : createRouterId();
1017
+ navigate(to, {
1018
+ ...navigationOptions,
1019
+ replace: replacesBrowserEntry,
1020
+ state: stateWithHistoryIndex(options.state, nextIndex, nextNavigationKey, commit)
1021
+ });
1022
+ }
1023
+ function ensureInitialHistoryIndex() {
1024
+ const currentState = window.history.state;
1025
+ const hasIndex = isRecord(currentState) && typeof currentState[HISTORY_INDEX_KEY] === "number";
1026
+ const currentNavigationKey = historyNavigationKeyFromState(currentState);
1027
+ if (hasIndex && currentNavigationKey) return;
1028
+ window.history.replaceState(
1029
+ stateWithHistoryIndex(
1030
+ currentState,
1031
+ historyIndexFromState(currentState),
1032
+ currentNavigationKey ?? createRouterId()
1033
+ ),
1034
+ "",
1035
+ window.location.href
1036
+ );
1037
+ }
1038
+ function normalizedPath(pathname) {
1039
+ if (pathname === "/") return "/";
1040
+ return pathname.replace(/\/+$/, "") || "/";
1041
+ }
1042
+ function routeDepth(pathname) {
1043
+ const path = normalizedPath(pathname);
1044
+ if (path === "/") return 0;
1045
+ return path.split("/").filter(Boolean).length;
1046
+ }
1047
+ function joinRoutePath(parentPath, childPath) {
1048
+ if (childPath == null || childPath === "") return parentPath;
1049
+ if (childPath === "*") return "*";
1050
+ if (childPath.startsWith("/")) return normalizedPath(childPath);
1051
+ if (!parentPath || parentPath === "/") return normalizedPath(`/${childPath}`);
1052
+ return normalizedPath(`${parentPath}/${childPath}`);
1053
+ }
1054
+ function routeComponent(presentation, container) {
1055
+ return Object.assign(
1056
+ function RouterRouteComponent(_props) {
1057
+ return null;
1058
+ },
1059
+ { [STACK_ROUTE_KIND]: { presentation, container } }
1060
+ );
1061
+ }
1062
+ function routeDescriptorFromElement(element) {
1063
+ const type = element.type;
1064
+ const descriptor = type[STACK_ROUTE_KIND];
1065
+ if (!descriptor) return null;
1066
+ if (typeof descriptor === "string") return { presentation: descriptor };
1067
+ return descriptor;
1068
+ }
1069
+ function isStackRouteElement(node) {
1070
+ return isValidElement(node) && routeDescriptorFromElement(node) != null;
1071
+ }
1072
+ function isTabBarItemElement(node) {
1073
+ return isValidElement(node) && routeDescriptorFromElement(node)?.container === "tabbar-item";
1074
+ }
1075
+ function isDrawerItemElement(node) {
1076
+ return isValidElement(node) && routeDescriptorFromElement(node)?.container === "drawer-item";
1077
+ }
1078
+ function isDrawerGroupElement(node) {
1079
+ return isValidElement(node) && routeDescriptorFromElement(node)?.container === "drawer-group";
1080
+ }
1081
+ function isDrawerSectionElement(node) {
1082
+ return isValidElement(node) && routeDescriptorFromElement(node)?.container === "drawer-section";
1083
+ }
1084
+ function isSplitPrimaryElement(node) {
1085
+ return isValidElement(node) && routeDescriptorFromElement(node)?.container === "split-primary";
1086
+ }
1087
+ function isSplitSecondaryElement(node) {
1088
+ return isValidElement(node) && routeDescriptorFromElement(node)?.container === "split-secondary";
1089
+ }
1090
+ function isProtectedElement(node) {
1091
+ return isValidElement(node) && routeDescriptorFromElement(node)?.container === "protected";
1092
+ }
1093
+ function isRedirectElement(node) {
1094
+ return isValidElement(node) && routeDescriptorFromElement(node)?.container === "redirect";
1095
+ }
1096
+ function isElementType(value) {
1097
+ if (isValidElement(value)) return false;
1098
+ if (typeof value === "function") return true;
1099
+ if (typeof value !== "object" || value === null) return false;
1100
+ const reactType = value.$$typeof;
1101
+ return reactType ? REACT_COMPONENT_TYPE_SYMBOLS.has(reactType) : false;
1102
+ }
1103
+ function flattenRouteNodes(children) {
1104
+ const result = [];
1105
+ Children.forEach(children, (child) => {
1106
+ if (isValidElement(child) && child.type === Fragment$1) {
1107
+ result.push(...flattenRouteNodes(child.props.children));
1108
+ return;
1109
+ }
1110
+ result.push(child);
1111
+ });
1112
+ return result;
1113
+ }
1114
+ function splitRouteChildren(children) {
1115
+ if (typeof children === "function") {
1116
+ return { routeChildren: [], renderChildren: children };
1117
+ }
1118
+ const childArray = flattenRouteNodes(children);
1119
+ const routeChildren = [];
1120
+ const nonRouteChildren = [];
1121
+ for (const child of childArray) {
1122
+ if (isStackRouteElement(child)) {
1123
+ routeChildren.push(child);
1124
+ } else {
1125
+ nonRouteChildren.push(child);
1126
+ }
1127
+ }
1128
+ if (routeChildren.length === 0) {
1129
+ return { routeChildren, renderChildren: children };
1130
+ }
1131
+ return {
1132
+ routeChildren,
1133
+ renderChildren: nonRouteChildren.length > 0 ? nonRouteChildren : void 0
1134
+ };
1135
+ }
1136
+ function drawerItemFromElement(element, parentPath) {
1137
+ const itemPath = joinRoutePath(parentPath, element.props.path) ?? "/";
1138
+ return {
1139
+ type: "item",
1140
+ id: element.props.id ?? normalizedPath(itemPath),
1141
+ path: itemPath,
1142
+ label: element.props.label,
1143
+ icon: element.props.icon,
1144
+ badge: element.props.badge,
1145
+ hidden: element.props.hidden
1146
+ };
1147
+ }
1148
+ function collectDrawerNavigation(routeChildren, parentPath, parentId) {
1149
+ const nodes = [];
1150
+ const entries = [];
1151
+ routeChildren.forEach((child, index) => {
1152
+ if (isDrawerSectionElement(child)) {
1153
+ nodes.push({
1154
+ type: "section",
1155
+ id: child.props.id ?? `${parentId}/drawer-section-${index}`,
1156
+ title: child.props.title,
1157
+ hidden: child.props.hidden
1158
+ });
1159
+ return;
1160
+ }
1161
+ if (isDrawerItemElement(child)) {
1162
+ const item = drawerItemFromElement(child, parentPath);
1163
+ nodes.push(item);
1164
+ entries.push({ element: child, item });
1165
+ return;
1166
+ }
1167
+ if (!isDrawerGroupElement(child)) return;
1168
+ const groupChildren = splitRouteChildren(child.props.children).routeChildren;
1169
+ const groupEntries = groupChildren.filter(isDrawerItemElement).map((element) => {
1170
+ const item = drawerItemFromElement(element, parentPath);
1171
+ return { element, item };
1172
+ });
1173
+ if (groupEntries.length === 0) return;
1174
+ nodes.push({
1175
+ type: "group",
1176
+ id: child.props.id ?? `${parentId}/drawer-group-${index}`,
1177
+ label: child.props.label,
1178
+ icon: child.props.icon,
1179
+ chevron: child.props.chevron,
1180
+ hidden: child.props.hidden,
1181
+ items: groupEntries.map((entry) => entry.item)
1182
+ });
1183
+ entries.push(...groupEntries);
1184
+ });
1185
+ return { nodes, entries };
1186
+ }
1187
+ function compiledRouteFromProps(props, path, routeId, presentation, animation, renderChildren, inheritedGuards) {
1188
+ const elementType = props.element !== void 0 && isElementType(props.element) ? props.element : void 0;
1189
+ const elementNode = props.element !== void 0 && !elementType ? props.element : void 0;
1190
+ const hasRenderable = props.element !== void 0 || props.component !== void 0 || renderChildren !== void 0;
1191
+ if (!hasRenderable) return null;
1192
+ return {
1193
+ id: routeId,
1194
+ path,
1195
+ presentation,
1196
+ animation,
1197
+ overlayLayout: props.overlayLayout,
1198
+ guards: inheritedGuards,
1199
+ stackKey: props.stackKey,
1200
+ fallback: props.fallback,
1201
+ element: elementNode,
1202
+ elementType,
1203
+ component: props.component,
1204
+ layout: props.layout,
1205
+ render: typeof renderChildren === "function" ? renderChildren : void 0,
1206
+ childrenNode: typeof renderChildren === "function" ? void 0 : renderChildren
1207
+ };
1208
+ }
1209
+ function compileStackRoutes(children, parentPath, parentId = "root", inheritedGuards = []) {
1210
+ const routes = [];
1211
+ flattenRouteNodes(children).forEach((child, index) => {
1212
+ if (!isStackRouteElement(child)) return;
1213
+ const descriptor = routeDescriptorFromElement(child) ?? { presentation: "push" };
1214
+ const presentation = descriptor.presentation;
1215
+ const props = child.props;
1216
+ const animation = props.animation ?? "push";
1217
+ const path = joinRoutePath(parentPath, props.path);
1218
+ const { routeChildren, renderChildren } = splitRouteChildren(props.children);
1219
+ const routeId = props.id ?? `${parentId}/${index}:${path ?? "*"}:${presentation}`;
1220
+ if (isRedirectElement(child)) {
1221
+ routes.push({
1222
+ id: routeId,
1223
+ path,
1224
+ presentation,
1225
+ animation,
1226
+ overlayLayout: props.overlayLayout,
1227
+ redirectTo: child.props.to,
1228
+ guards: inheritedGuards,
1229
+ stackKey: props.stackKey,
1230
+ fallback: props.fallback
1231
+ });
1232
+ return;
1233
+ }
1234
+ if (isProtectedElement(child)) {
1235
+ const guard = {
1236
+ id: props.id ?? routeId,
1237
+ when: child.props.when,
1238
+ redirectTo: child.props.redirectTo
1239
+ };
1240
+ if (routeChildren.length > 0) {
1241
+ routes.push(
1242
+ ...compileStackRoutes(routeChildren, path, routeId, [
1243
+ ...inheritedGuards,
1244
+ guard
1245
+ ])
1246
+ );
1247
+ }
1248
+ return;
1249
+ }
1250
+ const elementType = props.element !== void 0 && isElementType(props.element) ? props.element : void 0;
1251
+ const elementNode = props.element !== void 0 && !elementType ? props.element : void 0;
1252
+ const hasRenderable = props.element !== void 0 || props.component !== void 0 || renderChildren !== void 0;
1253
+ if (isModalLikePresentation(presentation) && routeChildren.length > 0) {
1254
+ const compiledChildren2 = compileStackRoutes(
1255
+ routeChildren,
1256
+ path,
1257
+ routeId,
1258
+ inheritedGuards
1259
+ );
1260
+ const nestedOverlays = compiledChildren2.filter(
1261
+ (route) => route.presentation !== "push"
1262
+ );
1263
+ const flowRoutes = [];
1264
+ const rootRoute = compiledRouteFromProps(
1265
+ props,
1266
+ path,
1267
+ `${routeId}/overlay-root`,
1268
+ "push",
1269
+ animation,
1270
+ renderChildren,
1271
+ inheritedGuards
1272
+ );
1273
+ if (rootRoute) flowRoutes.push(rootRoute);
1274
+ flowRoutes.push(
1275
+ ...compiledChildren2.filter((route) => route.presentation === "push")
1276
+ );
1277
+ routes.push(...nestedOverlays);
1278
+ if (flowRoutes.length === 0) return;
1279
+ const overlayFlow = {
1280
+ id: `${routeId}/overlay-flow`,
1281
+ kind: presentation,
1282
+ routes: flowRoutes
1283
+ };
1284
+ for (const flowRoute of flowRoutes) {
1285
+ routes.push({
1286
+ id: `${overlayFlow.id}:${flowRoute.id}`,
1287
+ path: flowRoute.path,
1288
+ presentation,
1289
+ animation,
1290
+ overlayLayout: props.overlayLayout,
1291
+ redirectTo: flowRoute.redirectTo,
1292
+ guards: flowRoute.guards,
1293
+ stackKey: props.stackKey ?? routeId,
1294
+ fallback: flowRoute.fallback ?? props.fallback,
1295
+ childRoute: flowRoute,
1296
+ overlayFlow
1297
+ });
1298
+ }
1299
+ return;
1300
+ }
1301
+ if (descriptor.container === "splitview") {
1302
+ const primarySlot = routeChildren.find(isSplitPrimaryElement);
1303
+ const secondarySlot = routeChildren.find(isSplitSecondaryElement);
1304
+ const compileSplitSlot = (slot, slotName) => {
1305
+ if (!slot) return [];
1306
+ const slotProps = slot.props;
1307
+ const slotPath = joinRoutePath(path, slotProps.path);
1308
+ const { routeChildren: slotRouteChildren, renderChildren: slotRenderChildren } = splitRouteChildren(slotProps.children);
1309
+ const slotRouteId = slotProps.id ?? `${routeId}/${slotName}`;
1310
+ const slotAnimation = slotProps.animation ?? "push";
1311
+ const slotRoutes = [];
1312
+ const slotOwnRoute = compiledRouteFromProps(
1313
+ slotProps,
1314
+ slotPath,
1315
+ slotRouteId,
1316
+ presentation,
1317
+ slotAnimation,
1318
+ slotRenderChildren,
1319
+ inheritedGuards
1320
+ );
1321
+ if (slotOwnRoute) {
1322
+ slotRoutes.push(slotOwnRoute);
1323
+ }
1324
+ if (slotRouteChildren.length > 0) {
1325
+ slotRoutes.push(
1326
+ ...compileStackRoutes(
1327
+ slotRouteChildren,
1328
+ slotPath,
1329
+ slotRouteId,
1330
+ inheritedGuards
1331
+ )
1332
+ );
1333
+ }
1334
+ return slotRoutes;
1335
+ };
1336
+ const primaryRoutes = compileSplitSlot(primarySlot, "primary");
1337
+ const secondaryRoutes = compileSplitSlot(secondarySlot, "secondary");
1338
+ for (const splitChildRoute of [...primaryRoutes, ...secondaryRoutes]) {
1339
+ if (splitChildRoute.presentation !== "push") {
1340
+ routes.push(splitChildRoute);
1341
+ }
1342
+ }
1343
+ const primaryPushRoutes = primaryRoutes.filter(
1344
+ (route) => route.presentation === "push"
1345
+ );
1346
+ const secondaryPushRoutes = secondaryRoutes.filter(
1347
+ (route) => route.presentation === "push"
1348
+ );
1349
+ const defaultPrimaryRoute = primaryPushRoutes[0];
1350
+ if (!defaultPrimaryRoute) {
1351
+ return;
1352
+ }
1353
+ const minWidth = props.minWidth ?? DEFAULT_SPLIT_MIN_WIDTH;
1354
+ const primaryMaxWidth = props.primaryMaxWidth ?? DEFAULT_SPLIT_PRIMARY_MAX_WIDTH;
1355
+ const secondaryMinWidth = props.secondaryMinWidth ?? DEFAULT_SPLIT_SECONDARY_MIN_WIDTH;
1356
+ const wideMinWidth = splitViewWideMinWidth(
1357
+ minWidth,
1358
+ primaryMaxWidth,
1359
+ secondaryMinWidth
1360
+ );
1361
+ const splitViewLayoutId = `${routeId}/splitview`;
1362
+ for (const primaryRoute of primaryPushRoutes) {
1363
+ routes.push({
1364
+ id: `${routeId}/splitview:primary:${primaryRoute.id}`,
1365
+ path: primaryRoute.path,
1366
+ presentation: primaryRoute.presentation,
1367
+ animation: primaryRoute.animation,
1368
+ overlayLayout: primaryRoute.overlayLayout,
1369
+ guards: primaryRoute.guards,
1370
+ stackKey: props.stackKey ?? routeId,
1371
+ fallback: primaryRoute.fallback ?? props.fallback,
1372
+ element: elementNode,
1373
+ elementType,
1374
+ component: props.component,
1375
+ layout: props.layout,
1376
+ render: typeof renderChildren === "function" ? renderChildren : void 0,
1377
+ childrenNode: typeof renderChildren === "function" ? void 0 : renderChildren,
1378
+ container: descriptor.container,
1379
+ childRoute: primaryRoute,
1380
+ splitView: {
1381
+ primaryRoute,
1382
+ secondaryRoutes: secondaryPushRoutes,
1383
+ activePane: "primary",
1384
+ minWidth,
1385
+ primaryMaxWidth,
1386
+ secondaryMinWidth,
1387
+ wideMinWidth,
1388
+ layoutId: splitViewLayoutId
1389
+ }
1390
+ });
1391
+ }
1392
+ for (const secondaryRoute of secondaryPushRoutes) {
1393
+ routes.push({
1394
+ id: `${routeId}/splitview:secondary:${secondaryRoute.id}`,
1395
+ path: secondaryRoute.path,
1396
+ presentation: secondaryRoute.presentation,
1397
+ animation: secondaryRoute.animation,
1398
+ overlayLayout: secondaryRoute.overlayLayout,
1399
+ guards: secondaryRoute.guards,
1400
+ stackKey: props.stackKey ?? routeId,
1401
+ fallback: secondaryRoute.fallback ?? props.fallback,
1402
+ element: elementNode,
1403
+ elementType,
1404
+ component: props.component,
1405
+ layout: props.layout,
1406
+ render: typeof renderChildren === "function" ? renderChildren : void 0,
1407
+ childrenNode: typeof renderChildren === "function" ? void 0 : renderChildren,
1408
+ container: descriptor.container,
1409
+ childRoute: secondaryRoute,
1410
+ splitView: {
1411
+ primaryRoute: defaultPrimaryRoute,
1412
+ secondaryRoute,
1413
+ secondaryRoutes: secondaryPushRoutes,
1414
+ activePane: "secondary",
1415
+ minWidth,
1416
+ primaryMaxWidth,
1417
+ secondaryMinWidth,
1418
+ wideMinWidth,
1419
+ layoutId: splitViewLayoutId
1420
+ }
1421
+ });
1422
+ }
1423
+ return;
1424
+ }
1425
+ if (descriptor.container === "tabbar" || descriptor.container === "drawer") {
1426
+ const isTabBar = descriptor.container === "tabbar";
1427
+ const drawerNavigation = isTabBar ? null : collectDrawerNavigation(routeChildren, path, routeId);
1428
+ const navigationItems = isTabBar ? routeChildren.filter(isTabBarItemElement) : drawerNavigation.entries.map((entry) => entry.element);
1429
+ if (navigationItems.length > 0) {
1430
+ const tabs = isTabBar ? navigationItems.map((item) => {
1431
+ const itemPath = joinRoutePath(path, item.props.path) ?? "/";
1432
+ return {
1433
+ id: item.props.id ?? normalizedPath(itemPath),
1434
+ path: itemPath,
1435
+ label: item.props.label,
1436
+ icon: item.props.icon
1437
+ };
1438
+ }) : void 0;
1439
+ const drawerItems = drawerNavigation?.nodes;
1440
+ navigationItems.forEach((item, itemIndex) => {
1441
+ const itemProps = item.props;
1442
+ const itemPath = joinRoutePath(path, itemProps.path);
1443
+ const navigationItem = isTabBar ? tabs?.[itemIndex] : drawerNavigation?.entries[itemIndex]?.item;
1444
+ if (!navigationItem) return;
1445
+ const { routeChildren: itemRouteChildren, renderChildren: itemRenderChildren } = splitRouteChildren(itemProps.children);
1446
+ const itemRouteId = itemProps.id ?? `${routeId}/${descriptor.container}-${itemIndex}:${itemPath ?? "*"}:push`;
1447
+ const itemAnimation = itemProps.animation ?? "push";
1448
+ const itemElementType = itemProps.element !== void 0 && isElementType(itemProps.element) ? itemProps.element : void 0;
1449
+ const itemElementNode = itemProps.element !== void 0 && !itemElementType ? itemProps.element : void 0;
1450
+ const itemHasRenderable = itemProps.element !== void 0 || itemProps.component !== void 0 || itemRenderChildren !== void 0;
1451
+ const itemChildRoutes = [];
1452
+ if (itemHasRenderable) {
1453
+ itemChildRoutes.push({
1454
+ id: itemRouteId,
1455
+ path: itemPath,
1456
+ presentation,
1457
+ animation: itemAnimation,
1458
+ overlayLayout: itemProps.overlayLayout,
1459
+ guards: inheritedGuards,
1460
+ stackKey: itemProps.stackKey,
1461
+ fallback: itemProps.fallback,
1462
+ element: itemElementNode,
1463
+ elementType: itemElementType,
1464
+ component: itemProps.component,
1465
+ layout: itemProps.layout,
1466
+ render: typeof itemRenderChildren === "function" ? itemRenderChildren : void 0,
1467
+ childrenNode: typeof itemRenderChildren === "function" ? void 0 : itemRenderChildren
1468
+ });
1469
+ }
1470
+ if (itemRouteChildren.length > 0) {
1471
+ itemChildRoutes.push(
1472
+ ...compileStackRoutes(
1473
+ itemRouteChildren,
1474
+ itemPath,
1475
+ itemRouteId,
1476
+ inheritedGuards
1477
+ )
1478
+ );
1479
+ }
1480
+ for (const childRoute of itemChildRoutes) {
1481
+ if (childRoute.presentation !== "push") {
1482
+ routes.push(childRoute);
1483
+ continue;
1484
+ }
1485
+ const containerRoute = {
1486
+ id: `${routeId}/${descriptor.container}:${navigationItem.id}:${childRoute.id}`,
1487
+ path: childRoute.path,
1488
+ presentation: childRoute.presentation,
1489
+ animation: childRoute.animation,
1490
+ overlayLayout: childRoute.overlayLayout,
1491
+ guards: childRoute.guards,
1492
+ stackKey: props.stackKey ?? routeId,
1493
+ fallback: childRoute.fallback ?? props.fallback,
1494
+ element: elementNode,
1495
+ elementType,
1496
+ component: props.component,
1497
+ layout: props.layout,
1498
+ render: typeof renderChildren === "function" ? renderChildren : void 0,
1499
+ childrenNode: typeof renderChildren === "function" ? void 0 : renderChildren,
1500
+ container: descriptor.container,
1501
+ childRoute,
1502
+ drawerAside: isTabBar ? void 0 : props.aside,
1503
+ drawerFooter: isTabBar ? void 0 : props.footer
1504
+ };
1505
+ if (isTabBar && tabs) {
1506
+ containerRoute.tabBar = {
1507
+ tabs,
1508
+ activeTab: navigationItem.id
1509
+ };
1510
+ } else if (!isTabBar && drawerItems) {
1511
+ containerRoute.drawer = {
1512
+ items: drawerItems,
1513
+ activeItem: navigationItem.id
1514
+ };
1515
+ }
1516
+ routes.push(containerRoute);
1517
+ }
1518
+ });
1519
+ return;
1520
+ }
1521
+ const compiledChildren2 = routeChildren.length > 0 ? compileStackRoutes(routeChildren, path, routeId, inheritedGuards) : [];
1522
+ for (const childRoute of compiledChildren2) {
1523
+ if (childRoute.presentation !== "push") {
1524
+ routes.push(childRoute);
1525
+ continue;
1526
+ }
1527
+ routes.push({
1528
+ id: routeId,
1529
+ path: childRoute.path,
1530
+ presentation,
1531
+ animation: childRoute.animation,
1532
+ overlayLayout: childRoute.overlayLayout,
1533
+ guards: childRoute.guards,
1534
+ stackKey: props.stackKey ?? routeId,
1535
+ fallback: childRoute.fallback ?? props.fallback,
1536
+ element: elementNode,
1537
+ elementType,
1538
+ component: props.component,
1539
+ layout: props.layout,
1540
+ render: typeof renderChildren === "function" ? renderChildren : void 0,
1541
+ childrenNode: typeof renderChildren === "function" ? void 0 : renderChildren,
1542
+ container: descriptor.container,
1543
+ childRoute
1544
+ });
1545
+ }
1546
+ return;
1547
+ }
1548
+ if (descriptor.container === "tabbar-item" || descriptor.container === "drawer-group" || descriptor.container === "drawer-section" || descriptor.container === "drawer-item" || descriptor.container === "split-primary" || descriptor.container === "split-secondary") {
1549
+ return;
1550
+ }
1551
+ const compiledChildren = routeChildren.length > 0 ? compileStackRoutes(routeChildren, path, routeId, inheritedGuards) : [];
1552
+ if (hasRenderable) {
1553
+ routes.push({
1554
+ id: routeId,
1555
+ path,
1556
+ presentation,
1557
+ animation,
1558
+ overlayLayout: props.overlayLayout,
1559
+ guards: inheritedGuards,
1560
+ stackKey: props.stackKey,
1561
+ fallback: props.fallback,
1562
+ element: elementNode,
1563
+ elementType,
1564
+ component: props.component,
1565
+ layout: props.layout,
1566
+ render: typeof renderChildren === "function" ? renderChildren : void 0,
1567
+ childrenNode: typeof renderChildren === "function" ? void 0 : renderChildren
1568
+ });
1569
+ }
1570
+ if (compiledChildren.length > 0) {
1571
+ routes.push(...compiledChildren);
1572
+ }
1573
+ });
1574
+ return routes;
1575
+ }
1576
+ function matchStackRouteByPresentation(routes, parser, pathname, presentations) {
1577
+ for (const route of routes) {
1578
+ if (!presentations.has(route.presentation)) continue;
1579
+ const [matches, params] = matchRoute(parser, route.path ?? "*", pathname);
1580
+ if (matches) {
1581
+ return {
1582
+ route,
1583
+ params: { ...params }
1584
+ };
1585
+ }
1586
+ }
1587
+ return null;
1588
+ }
1589
+ function splitViewRouteForCompiledRoute(route) {
1590
+ if (route?.container === "splitview" && route.splitView) return route;
1591
+ if (route?.childRoute?.container === "splitview" && route.childRoute.splitView) {
1592
+ return route.childRoute;
1593
+ }
1594
+ return void 0;
1595
+ }
1596
+ function stateForSplitNavigationTarget(state, targetMatch, action) {
1597
+ const origin = splitNavigationOriginFromState(state);
1598
+ if (!origin) return state;
1599
+ const next = mutableHistoryState(state);
1600
+ delete next[SPLIT_NAVIGATION_ORIGIN_KEY];
1601
+ const targetSplitRoute = splitViewRouteForCompiledRoute(targetMatch?.route);
1602
+ const targetSplit = targetSplitRoute?.splitView;
1603
+ if (!targetSplit || targetSplit.layoutId !== origin.splitViewId || targetSplit.activePane !== "secondary") {
1604
+ delete next[SPLIT_NAVIGATION_KEY];
1605
+ return next;
1606
+ }
1607
+ const metadata = (
1608
+ // A primary selection establishes a new detail root. Navigation initiated
1609
+ // inside secondary continues (or explicitly replaces) that detail path.
1610
+ origin.pane === "primary" ? {
1611
+ splitViewId: origin.splitViewId,
1612
+ detailPathId: createRouterId(),
1613
+ detailDepth: 0,
1614
+ operation: "selection"
1615
+ } : {
1616
+ splitViewId: origin.splitViewId,
1617
+ detailPathId: origin.detailPathId,
1618
+ detailDepth: action === "navigate" ? origin.detailDepth + 1 : origin.detailDepth,
1619
+ operation: action === "navigate" ? "push" : "replace"
1620
+ }
1621
+ );
1622
+ next[SPLIT_NAVIGATION_KEY] = metadata;
1623
+ return next;
1624
+ }
1625
+ function routeRenderPropsForMatch(pathname, search, historyState, match) {
1626
+ return {
1627
+ params: match.params,
1628
+ pathname: normalizedPath(pathname),
1629
+ search,
1630
+ historyState,
1631
+ presentation: match.route.presentation
1632
+ };
1633
+ }
1634
+ function resolveGuardValue(value, props) {
1635
+ return typeof value === "function" ? value(props) : value;
1636
+ }
1637
+ function guardRedirectForMatch(match, pathname, search, historyState) {
1638
+ const props = routeRenderPropsForMatch(pathname, search, historyState, match);
1639
+ for (const guard of match.route.guards) {
1640
+ if (resolveGuardValue(guard.when, props)) continue;
1641
+ return resolveGuardValue(guard.redirectTo, props);
1642
+ }
1643
+ if (match.route.redirectTo) {
1644
+ return resolveGuardValue(match.route.redirectTo, props);
1645
+ }
1646
+ return null;
1647
+ }
1648
+ var PUSH_PRESENTATIONS = /* @__PURE__ */ new Set(["push"]);
1649
+ var OVERLAY_PRESENTATIONS = /* @__PURE__ */ new Set(["modal", "sheet"]);
1650
+ function parseNavigationTarget(to, currentPathname) {
1651
+ const base = `${window.location.origin}${currentPathname.startsWith("/") ? currentPathname : `/${currentPathname}`}`;
1652
+ const url = new URL(to, base);
1653
+ return {
1654
+ pathname: normalizedPath(url.pathname),
1655
+ search: url.search
1656
+ };
1657
+ }
1658
+ function pathForRoutePattern(pattern, params) {
1659
+ if (!pattern || pattern === "*") return null;
1660
+ let missingParam = false;
1661
+ const pathname = pattern.replace(/:([A-Za-z0-9_]+)/g, (_match, key) => {
1662
+ const value = params[key];
1663
+ if (value == null) {
1664
+ missingParam = true;
1665
+ return "";
1666
+ }
1667
+ return encodeURIComponent(String(value));
1668
+ });
1669
+ if (missingParam || pathname.includes("*")) return null;
1670
+ return normalizedPath(pathname);
1671
+ }
1672
+ function resolveStackKey(match, props) {
1673
+ const { route } = match;
1674
+ if (typeof route.stackKey === "function") return route.stackKey(props);
1675
+ if (typeof route.stackKey === "string") return route.stackKey;
1676
+ return normalizedPath(props.pathname);
1677
+ }
1678
+ function stackSnapshot(pathname, search, historyState, match) {
1679
+ const normalized = normalizedPath(pathname);
1680
+ const historyIndex = historyIndexFromState(historyState);
1681
+ const historyNavigationKey = historyNavigationKeyFromState(historyState) ?? `navigation-${historyIndex}`;
1682
+ const props = routeRenderPropsForMatch(normalized, search, historyState, match);
1683
+ const stackKey = resolveStackKey(match, props);
1684
+ const splitNavigation = splitNavigationFromState(historyState);
1685
+ return {
1686
+ ...match,
1687
+ stackKey,
1688
+ locationKey: `${normalized}${search}:${historyIndex}:${historyNavigationKey}`,
1689
+ historyNavigationKey,
1690
+ pathname: normalized,
1691
+ search,
1692
+ historyState,
1693
+ historyIndex,
1694
+ historyCommit: historyCommitFromState(historyState),
1695
+ depth: routeDepth(normalized),
1696
+ presentation: match.route.presentation,
1697
+ splitNavigation
1698
+ };
1699
+ }
1700
+ function resolveDirection(previous, current) {
1701
+ if (!previous) return "forward";
1702
+ if (current.historyIndex < previous.historyIndex) return "back";
1703
+ if (current.historyIndex > previous.historyIndex) return "forward";
1704
+ if (current.depth < previous.depth) return "back";
1705
+ return "forward";
1706
+ }
1707
+ function createEntry(snapshot, enterDirection, animateEnter) {
1708
+ const transitionKey = animateEnter ? `${snapshot.stackKey}:${snapshot.locationKey}:${createRouterId()}` : `${snapshot.stackKey}:${snapshot.locationKey}`;
1709
+ return {
1710
+ ...snapshot,
1711
+ id: transitionKey,
1712
+ transitionKey,
1713
+ enterDirection,
1714
+ exitDirection: "back",
1715
+ animateEnter
1716
+ };
1717
+ }
1718
+ function markCommitted(entry) {
1719
+ return entry.animateEnter ? { ...entry, animateEnter: false } : entry;
1720
+ }
1721
+ function mergeEntrySnapshot(entry, snapshot) {
1722
+ return {
1723
+ ...entry,
1724
+ ...snapshot,
1725
+ id: entry.id,
1726
+ transitionKey: entry.transitionKey,
1727
+ animateEnter: entry.animateEnter
1728
+ };
1729
+ }
1730
+ function isModalLikePresentation(presentation) {
1731
+ return presentation === "modal" || presentation === "sheet";
1732
+ }
1733
+ function isTabBarStackEntry(entry) {
1734
+ return entry.route.container === "tabbar";
1735
+ }
1736
+ function isDrawerContainerUpdate(previous, current) {
1737
+ return previous.stackKey === current.stackKey && previous.route.container === "drawer" && current.route.container === "drawer";
1738
+ }
1739
+ function canNavigateBackWithinDrawer(previous, current) {
1740
+ if (!previous || !current) return false;
1741
+ return previous.stackKey === current.stackKey && previous.route.container === "drawer" && current.route.container === "drawer" && previous.route.drawer?.activeItem === current.route.drawer?.activeItem && previous.pathname !== current.pathname;
1742
+ }
1743
+ function isTabBarContainerUpdate(previous, current) {
1744
+ return previous.stackKey === current.stackKey && (isTabBarStackEntry(previous) || isTabBarStackEntry(current));
1745
+ }
1746
+ function stackEntryContentContainer(entry) {
1747
+ return entry.route.childRoute?.container ?? entry.route.container;
1748
+ }
1749
+ function splitViewRouteForSnapshot(snapshot) {
1750
+ return splitViewRouteForCompiledRoute(snapshot.route);
1751
+ }
1752
+ function activeSplitViewLayout(layoutId) {
1753
+ if (typeof document === "undefined") return null;
1754
+ const splitViews = document.querySelectorAll(
1755
+ ".sigmela-split-view[data-router-split-view-id]"
1756
+ );
1757
+ for (const splitView of splitViews) {
1758
+ if (splitView.dataset.routerSplitViewId !== layoutId) continue;
1759
+ const stackScreen = splitView.closest(
1760
+ '[data-route-stack-screen="true"]'
1761
+ );
1762
+ if (stackScreen?.dataset.routeStackPhase !== "active") continue;
1763
+ const layout = splitView.dataset.layout;
1764
+ if (layout === "wide") return true;
1765
+ if (layout === "narrow") return false;
1766
+ }
1767
+ return null;
1768
+ }
1769
+ function isWideSplitViewRoute(route) {
1770
+ const splitView = route?.splitView;
1771
+ if (!splitView || typeof window === "undefined" || typeof window.matchMedia !== "function") {
1772
+ return false;
1773
+ }
1774
+ const activeLayout = activeSplitViewLayout(splitView.layoutId);
1775
+ if (activeLayout !== null) return activeLayout;
1776
+ return window.matchMedia(`(min-width: ${splitView.wideMinWidth}px)`).matches;
1777
+ }
1778
+ function secondarySplitBackTarget(snapshot) {
1779
+ if (!snapshot) return null;
1780
+ const splitRoute = splitViewRouteForSnapshot(snapshot);
1781
+ const splitView = splitRoute?.splitView;
1782
+ if (!splitView || splitView.activePane !== "secondary") return null;
1783
+ if (!isWideSplitViewRoute(splitRoute)) return null;
1784
+ const currentPath = normalizedPath(snapshot.pathname);
1785
+ const parentSecondaryRoute = (splitView.secondaryRoutes ?? []).map((route) => pathForRoutePattern(route.path, snapshot.params)).filter(
1786
+ (path) => Boolean(path && path !== currentPath && currentPath.startsWith(`${path}/`))
1787
+ ).sort((a, b) => routeDepth(b) - routeDepth(a))[0];
1788
+ if (parentSecondaryRoute) return parentSecondaryRoute;
1789
+ const target = pathForRoutePattern(splitView.primaryRoute.path, snapshot.params);
1790
+ if (!target || target === snapshot.pathname) return null;
1791
+ return target;
1792
+ }
1793
+ function canNavigateBackWithinSplit(previous, current) {
1794
+ if (!previous || !current) return false;
1795
+ const currentRoute = splitViewRouteForSnapshot(current);
1796
+ const previousRoute = splitViewRouteForSnapshot(previous);
1797
+ const currentSplit = currentRoute?.splitView;
1798
+ const previousSplit = previousRoute?.splitView;
1799
+ if (!currentSplit || currentSplit.activePane !== "secondary") return false;
1800
+ if (!previousSplit) return false;
1801
+ if (!isWideSplitViewRoute(currentRoute)) return false;
1802
+ return previous.stackKey === current.stackKey && previousSplit.primaryRoute.id === currentSplit.primaryRoute.id;
1803
+ }
1804
+ function existingStackEntryIndexForSnapshot(entries, snapshot) {
1805
+ const locationIndex = entries.findIndex(
1806
+ (entry) => entry.locationKey === snapshot.locationKey
1807
+ );
1808
+ if (locationIndex !== -1) return locationIndex;
1809
+ for (let i = entries.length - 1; i >= 0; i--) {
1810
+ const entry = entries[i];
1811
+ if (entry.stackKey === snapshot.stackKey && entry.pathname === snapshot.pathname && entry.search === snapshot.search) {
1812
+ return i;
1813
+ }
1814
+ }
1815
+ return -1;
1816
+ }
1817
+ function syncTargetEntryIndex(entries, snapshot) {
1818
+ if (snapshot.historyCommit !== "sync") return -1;
1819
+ return entries.findIndex(
1820
+ (entry) => entry.historyNavigationKey === snapshot.historyNavigationKey
1821
+ );
1822
+ }
1823
+ function entriesRevealingSnapshot(entries, existingIndex, snapshot) {
1824
+ const revealed = entries[existingIndex];
1825
+ const base = entries.slice(0, existingIndex).map(markCommitted);
1826
+ return [
1827
+ ...base,
1828
+ {
1829
+ ...revealed,
1830
+ ...snapshot,
1831
+ id: revealed.id,
1832
+ animateEnter: false,
1833
+ enterDirection: "back"
1834
+ }
1835
+ ];
1836
+ }
1837
+ function previousStackEntry(entries, active) {
1838
+ if (!active) return void 0;
1839
+ const activeIndex = entries.findIndex((entry) => entry.id === active.id);
1840
+ if (activeIndex <= 0) return void 0;
1841
+ return entries[activeIndex - 1];
1842
+ }
1843
+ function previousSnapshotInDetailPath(history, current) {
1844
+ const navigation = current.splitNavigation;
1845
+ if (!navigation || navigation.detailDepth <= 0) return void 0;
1846
+ const currentIndex = history.findIndex(
1847
+ (entry) => entry.historyIndex === current.historyIndex
1848
+ );
1849
+ if (currentIndex <= 0) return void 0;
1850
+ for (let index = currentIndex - 1; index >= 0; index -= 1) {
1851
+ const candidate = history[index];
1852
+ const candidateNavigation = candidate.splitNavigation;
1853
+ if (candidateNavigation?.splitViewId === navigation.splitViewId && candidateNavigation.detailPathId === navigation.detailPathId && candidateNavigation.detailDepth === navigation.detailDepth - 1) {
1854
+ return candidate;
1855
+ }
1856
+ }
1857
+ return void 0;
1858
+ }
1859
+ function navigateToSnapshot(snapshot) {
1860
+ if (!snapshot) return false;
1861
+ navigateWithHistoryIndex(`${snapshot.pathname}${snapshot.search}`, {
1862
+ commit: "sync",
1863
+ state: snapshot.historyState
1864
+ });
1865
+ return true;
1866
+ }
1867
+ function isWideSplitInternalUpdate(previous, current) {
1868
+ const previousRoute = splitViewRouteForSnapshot(previous);
1869
+ const currentRoute = splitViewRouteForSnapshot(current);
1870
+ const previousSplit = previousRoute?.splitView;
1871
+ const currentSplit = currentRoute?.splitView;
1872
+ if (!previousSplit || !currentSplit) return false;
1873
+ return previous.stackKey === current.stackKey && (previousSplit.activePane === "secondary" || currentSplit.activePane === "secondary") && previousSplit.primaryRoute.id === currentSplit.primaryRoute.id && isWideSplitViewRoute(currentRoute);
1874
+ }
1875
+ function isSameScreenRouteUpdate(previous, current) {
1876
+ return previous.stackKey === current.stackKey && previous.route.id === current.route.id && previous.presentation === current.presentation;
1877
+ }
1878
+ function shouldReplaceActiveStackEntry(previous, current) {
1879
+ const isSecondaryPush = current.splitPane === "secondary" && current.splitNavigation?.operation === "push";
1880
+ return !current.forcePushRouteChanges && !isSecondaryPush && isSameScreenRouteUpdate(previous, current) || isWideSplitInternalUpdate(previous, current) || isDrawerContainerUpdate(previous, current) || isTabBarContainerUpdate(previous, current);
1881
+ }
1882
+ function isExplicitStackReplace(previous, current) {
1883
+ return current.historyCommit === "replace" && previous.historyIndex === current.historyIndex;
1884
+ }
1885
+ function replaceActiveStackEntry(entries, activeId, snapshot, direction, commitInactive) {
1886
+ return entries.map((entry) => {
1887
+ if (entry.id === activeId) {
1888
+ return {
1889
+ ...mergeEntrySnapshot(entry, snapshot),
1890
+ enterDirection: direction
1891
+ };
1892
+ }
1893
+ return commitInactive ? markCommitted(entry) : entry;
1894
+ });
1895
+ }
1896
+ function presentationClass(presentation) {
1897
+ return presentation;
1898
+ }
1899
+ function activeAnimationClass(entry) {
1900
+ if (entry.presentation !== "push") return "none";
1901
+ if (entry.route.animation === "slide_from_bottom" && entry.enterDirection !== "back") {
1902
+ return "slide-from-bottom-enter";
1903
+ }
1904
+ return entry.enterDirection === "back" ? "pop-enter" : "push-enter";
1905
+ }
1906
+ function exitingAnimationClass(entry) {
1907
+ if (entry.presentation !== "push") return "none";
1908
+ if (entry.route.animation === "slide_from_bottom" && entry.exitDirection === "back") {
1909
+ return "slide-from-bottom-exit";
1910
+ }
1911
+ return entry.exitDirection === "back" ? "pop-exit" : "push-exit";
1912
+ }
1913
+ function inactiveAnimationClass(entry, activeEntry, direction) {
1914
+ if (entry.presentation !== "push" || activeEntry?.presentation !== "push") {
1915
+ return "none";
1916
+ }
1917
+ if (activeEntry.route.animation === "slide_from_bottom") {
1918
+ return "none";
1919
+ }
1920
+ return direction === "back" ? "pop-background" : "push-background";
1921
+ }
1922
+ function transitionStatusForEntry(entry, status) {
1923
+ if (status) return status;
1924
+ return entry.animateEnter ? "preEnter" : "entered";
1925
+ }
1926
+ function phaseForEntry(entry, activeIds, activeId) {
1927
+ if (!activeIds.has(entry.id)) return "exiting";
1928
+ return entry.id === activeId ? "active" : "inactive";
1929
+ }
1930
+ function animationClassForEntry(entry, phase, activeEntry, direction) {
1931
+ if (phase === "exiting") return exitingAnimationClass(entry);
1932
+ if (phase === "active") return activeAnimationClass(entry);
1933
+ return inactiveAnimationClass(entry, activeEntry, direction);
1934
+ }
1935
+ function screenClassName(entry, phase, transitionStatus, animationType) {
1936
+ return [
1937
+ "screen-stack-item",
1938
+ presentationClass(entry.presentation),
1939
+ animationType,
1940
+ `transition-${transitionStatus}`,
1941
+ `phase-${phase}`
1942
+ ].join(" ");
1943
+ }
1944
+ function frozenLocationHook(pathname) {
1945
+ return () => [pathname, navigateWithHistoryIndex];
1946
+ }
1947
+ function searchParamsForSearch(search) {
1948
+ return new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
1949
+ }
1950
+ function routeContextValue(entry) {
1951
+ return {
1952
+ params: entry.params,
1953
+ pathname: entry.pathname,
1954
+ search: entry.search,
1955
+ searchParams: searchParamsForSearch(entry.search),
1956
+ historyState: entry.historyState,
1957
+ presentation: entry.presentation,
1958
+ splitPane: entry.splitPane
1959
+ };
1960
+ }
1961
+ function routeContextValueFromProps(props) {
1962
+ return {
1963
+ ...props,
1964
+ searchParams: searchParamsForSearch(props.search)
1965
+ };
1966
+ }
1967
+ function pathnameForRouteContext(route, fallbackPathname) {
1968
+ const routePath = route.path;
1969
+ if (!routePath || routePath.includes(":") || routePath.includes("*")) {
1970
+ return fallbackPathname;
1971
+ }
1972
+ return normalizedPath(routePath);
1973
+ }
1974
+ function useMinWidth(minWidth) {
1975
+ const [matches, setMatches] = useState(() => {
1976
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
1977
+ return false;
1978
+ }
1979
+ return window.matchMedia(`(min-width: ${minWidth}px)`).matches;
1980
+ });
1981
+ useEffect(() => {
1982
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
1983
+ return;
1984
+ }
1985
+ const mediaQuery = window.matchMedia(`(min-width: ${minWidth}px)`);
1986
+ const handleChange = (event) => setMatches(event.matches);
1987
+ setMatches(mediaQuery.matches);
1988
+ if (typeof mediaQuery.addEventListener === "function") {
1989
+ mediaQuery.addEventListener("change", handleChange);
1990
+ return () => mediaQuery.removeEventListener("change", handleChange);
1991
+ }
1992
+ const mediaQueryWithListener = mediaQuery;
1993
+ mediaQueryWithListener.addListener?.(handleChange);
1994
+ return () => mediaQueryWithListener.removeListener?.(handleChange);
1995
+ }, [minWidth]);
1996
+ return matches;
1997
+ }
1998
+ function measuredElementWidth(element) {
1999
+ return element.getBoundingClientRect().width || element.clientWidth || 0;
2000
+ }
2001
+ function widthMatchesMinWidth(width, minWidth) {
2002
+ if (width <= 0) return null;
2003
+ return width >= minWidth;
2004
+ }
2005
+ function useElementMinWidth(minWidth) {
2006
+ const ref = useRef(null);
2007
+ const viewportMatches = useMinWidth(minWidth);
2008
+ const [elementMatches, setElementMatches] = useState(null);
2009
+ useLayoutEffect(() => {
2010
+ const element = ref.current;
2011
+ if (!element) {
2012
+ setElementMatches(null);
2013
+ return;
2014
+ }
2015
+ const updateWidth = (width) => {
2016
+ const nextMatches = widthMatchesMinWidth(width, minWidth);
2017
+ setElementMatches(
2018
+ (currentMatches) => currentMatches === nextMatches ? currentMatches : nextMatches
2019
+ );
2020
+ };
2021
+ const measure = () => updateWidth(measuredElementWidth(element));
2022
+ measure();
2023
+ let observer = null;
2024
+ if (typeof window !== "undefined" && typeof window.ResizeObserver === "function") {
2025
+ observer = new window.ResizeObserver((entries) => {
2026
+ updateWidth(entries[0]?.contentRect.width ?? measuredElementWidth(element));
2027
+ });
2028
+ observer.observe(element);
2029
+ }
2030
+ if (typeof window !== "undefined") {
2031
+ window.addEventListener("resize", measure);
2032
+ }
2033
+ return () => {
2034
+ observer?.disconnect();
2035
+ if (typeof window !== "undefined") {
2036
+ window.removeEventListener("resize", measure);
2037
+ }
2038
+ };
2039
+ }, [minWidth]);
2040
+ return [ref, elementMatches ?? viewportMatches];
2041
+ }
2042
+ function useRoute() {
2043
+ const route = useContext(RouterRouteContext);
2044
+ if (!route) {
2045
+ throw new Error("Router route hooks must be used inside <Router>");
2046
+ }
2047
+ return route;
2048
+ }
2049
+ function useCurrentRoute() {
2050
+ return useRoute();
2051
+ }
2052
+ function useParams() {
2053
+ return useRoute().params;
2054
+ }
2055
+ function useSearchParams() {
2056
+ return useRoute().searchParams;
2057
+ }
2058
+ function useHistoryState() {
2059
+ return useRoute().historyState;
2060
+ }
2061
+ function useRouter() {
2062
+ const router = useContext(RouterActionsContext);
2063
+ const splitPaneOrigin = useContext(SplitPaneNavigationContext);
2064
+ if (!router) {
2065
+ throw new Error("useRouter must be used inside <Router>");
2066
+ }
2067
+ return useMemo(() => {
2068
+ if (!splitPaneOrigin) return router;
2069
+ return {
2070
+ ...router,
2071
+ navigate: (to, options = {}) => {
2072
+ if (typeof to === "number") {
2073
+ router.navigate(to, options);
2074
+ return;
2075
+ }
2076
+ router.navigate(to, {
2077
+ ...options,
2078
+ state: stateWithSplitNavigationOrigin(options.state, splitPaneOrigin)
2079
+ });
2080
+ },
2081
+ replace: (to, options = {}) => {
2082
+ router.replace(to, {
2083
+ ...options,
2084
+ state: stateWithSplitNavigationOrigin(options.state, splitPaneOrigin)
2085
+ });
2086
+ }
2087
+ };
2088
+ }, [router, splitPaneOrigin]);
2089
+ }
2090
+ function useDrawer() {
2091
+ const drawer = useContext(RouterDrawerContext);
2092
+ if (!drawer) {
2093
+ throw new Error("useDrawer must be used inside <Router.Drawer>");
2094
+ }
2095
+ return drawer;
2096
+ }
2097
+ function useCurrentModal() {
2098
+ const current = useContext(CurrentModalContext);
2099
+ if (!current) {
2100
+ throw new Error("useCurrentModal called outside a Router modal/sheet entry");
2101
+ }
2102
+ return current;
2103
+ }
2104
+ function DefaultSplitViewLayout({
2105
+ children,
2106
+ primary,
2107
+ secondary,
2108
+ isWide,
2109
+ primaryMaxWidth = DEFAULT_SPLIT_PRIMARY_MAX_WIDTH,
2110
+ splitViewRef,
2111
+ splitViewLayoutId
2112
+ }) {
2113
+ return /* @__PURE__ */ jsxs(
2114
+ "div",
2115
+ {
2116
+ ref: splitViewRef,
2117
+ className: "sigmela-split-view",
2118
+ "data-layout": isWide ? "wide" : "narrow",
2119
+ "data-router-split-view-id": splitViewLayoutId,
2120
+ style: { "--sigmela-split-primary-max-width": `${primaryMaxWidth}px` },
2121
+ children: [
2122
+ /* @__PURE__ */ jsx("div", { className: "sigmela-split-view__primary", children: isWide ? primary : children }),
2123
+ isWide ? /* @__PURE__ */ jsx("div", { className: "sigmela-split-view__secondary", children: secondary }) : null
2124
+ ]
2125
+ }
2126
+ );
2127
+ }
2128
+ function DefaultDrawerLayout({
2129
+ children,
2130
+ aside,
2131
+ footer,
2132
+ items = [],
2133
+ activeItem,
2134
+ onItemChange,
2135
+ drawer
2136
+ }) {
2137
+ const [expandedGroups, setExpandedGroups] = useState(
2138
+ () => /* @__PURE__ */ new Set()
2139
+ );
2140
+ const toggleGroup = useCallback((id) => {
2141
+ setExpandedGroups((current) => {
2142
+ const next = new Set(current);
2143
+ if (next.has(id)) next.delete(id);
2144
+ else next.add(id);
2145
+ return next;
2146
+ });
2147
+ }, []);
2148
+ return /* @__PURE__ */ jsxs("div", { className: "sigmela-drawer", "data-drawer-open": drawer?.isOpen ? "true" : "false", children: [
2149
+ /* @__PURE__ */ jsxs("aside", { className: "sigmela-drawer__sidebar", children: [
2150
+ /* @__PURE__ */ jsx("div", { className: "sigmela-drawer__sidebar-content", children: items.filter((node) => !node.hidden).map((node) => {
2151
+ if (node.type === "item") {
2152
+ return /* @__PURE__ */ jsxs(
2153
+ "button",
2154
+ {
2155
+ type: "button",
2156
+ className: "sigmela-drawer__item",
2157
+ "data-active": node.id === activeItem ? "true" : void 0,
2158
+ onClick: () => onItemChange?.(node.id),
2159
+ children: [
2160
+ node.icon ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-icon", children: node.icon }) : null,
2161
+ /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-label", children: node.label }),
2162
+ node.badge ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-badge", children: node.badge }) : null
2163
+ ]
2164
+ },
2165
+ node.id
2166
+ );
2167
+ }
2168
+ if (node.type === "section") {
2169
+ return /* @__PURE__ */ jsx("div", { className: "sigmela-drawer__section-label", children: node.title }, node.id);
2170
+ }
2171
+ const visibleItems = node.items.filter((item) => !item.hidden);
2172
+ if (visibleItems.length === 0) return null;
2173
+ const isExpanded = expandedGroups.has(node.id);
2174
+ const itemsId = `${node.id}-drawer-items`;
2175
+ return /* @__PURE__ */ jsxs("div", { className: "sigmela-drawer__group-container", children: [
2176
+ /* @__PURE__ */ jsxs(
2177
+ "button",
2178
+ {
2179
+ type: "button",
2180
+ className: "sigmela-drawer__item sigmela-drawer__group-label",
2181
+ "aria-controls": itemsId,
2182
+ "aria-expanded": isExpanded,
2183
+ onClick: () => toggleGroup(node.id),
2184
+ children: [
2185
+ node.icon ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-icon", children: node.icon }) : null,
2186
+ /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-label", children: node.label }),
2187
+ node.chevron ? /* @__PURE__ */ jsx(
2188
+ "span",
2189
+ {
2190
+ className: "sigmela-drawer__group-chevron",
2191
+ "data-expanded": isExpanded ? "true" : void 0,
2192
+ "aria-hidden": "true",
2193
+ children: node.chevron
2194
+ }
2195
+ ) : null
2196
+ ]
2197
+ }
2198
+ ),
2199
+ isExpanded ? /* @__PURE__ */ jsx("div", { id: itemsId, className: "sigmela-drawer__group-items", children: visibleItems.map((item) => /* @__PURE__ */ jsxs(
2200
+ "button",
2201
+ {
2202
+ type: "button",
2203
+ className: "sigmela-drawer__item sigmela-drawer__subitem",
2204
+ "data-active": item.id === activeItem ? "true" : void 0,
2205
+ onClick: () => onItemChange?.(item.id),
2206
+ children: [
2207
+ item.icon ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-icon", children: item.icon }) : null,
2208
+ /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-label", children: item.label }),
2209
+ item.badge ? /* @__PURE__ */ jsx("span", { className: "sigmela-drawer__item-badge", children: item.badge }) : null
2210
+ ]
2211
+ },
2212
+ item.id
2213
+ )) }) : null
2214
+ ] }, node.id);
2215
+ }) }),
2216
+ footer ? /* @__PURE__ */ jsx("div", { className: "sigmela-drawer__footer", children: footer }) : null
2217
+ ] }),
2218
+ /* @__PURE__ */ jsx(
2219
+ "button",
2220
+ {
2221
+ type: "button",
2222
+ className: "sigmela-drawer__overlay",
2223
+ "aria-label": "Close navigation",
2224
+ onClick: drawer?.close
2225
+ }
2226
+ ),
2227
+ /* @__PURE__ */ jsx("main", { className: "sigmela-drawer__main", children }),
2228
+ aside ? /* @__PURE__ */ jsx("aside", { className: "sigmela-drawer__assistant", children: aside }) : null
2229
+ ] });
2230
+ }
2231
+ function FrozenPaneRouteContent({
2232
+ route,
2233
+ props,
2234
+ fallback
2235
+ }) {
2236
+ const hook = useMemo(() => frozenLocationHook(props.pathname), [props.pathname]);
2237
+ const searchHook = useMemo(() => () => props.search, [props.search]);
2238
+ const routeContext = useMemo(() => routeContextValueFromProps(props), [props]);
2239
+ return /* @__PURE__ */ jsx(Router$1, { hook, searchHook, children: /* @__PURE__ */ jsx(RouterRouteContext.Provider, { value: routeContext, children: /* @__PURE__ */ jsx(Suspense, { fallback, children: renderRouteContent(route, props) }) }) });
2240
+ }
2241
+ function stackSnapshotForRouteProps(route, props, stackKey, forcePushRouteChanges = false) {
2242
+ const normalized = normalizedPath(props.pathname);
2243
+ const historyIndex = historyIndexFromState(props.historyState);
2244
+ const historyNavigationKey = historyNavigationKeyFromState(props.historyState) ?? `navigation-${historyIndex}`;
2245
+ const splitNavigation = splitNavigationFromState(props.historyState);
2246
+ return {
2247
+ route,
2248
+ params: props.params,
2249
+ stackKey,
2250
+ locationKey: `${normalized}${props.search}:${historyIndex}:${historyNavigationKey}`,
2251
+ historyNavigationKey,
2252
+ pathname: normalized,
2253
+ search: props.search,
2254
+ historyState: props.historyState,
2255
+ historyIndex,
2256
+ historyCommit: historyCommitFromState(props.historyState),
2257
+ depth: routeDepth(normalized),
2258
+ presentation: route.presentation,
2259
+ forcePushRouteChanges,
2260
+ splitPane: props.splitPane,
2261
+ splitNavigation
2262
+ };
2263
+ }
2264
+ function propsForStackEntry(entry) {
2265
+ return {
2266
+ params: entry.params,
2267
+ pathname: entry.pathname,
2268
+ search: entry.search,
2269
+ historyState: entry.historyState,
2270
+ presentation: entry.presentation,
2271
+ splitPane: entry.splitPane
2272
+ };
2273
+ }
2274
+ function RouteStackViewport({
2275
+ route,
2276
+ props,
2277
+ fallback,
2278
+ stackKey,
2279
+ className = "sigmela-split-pane-stack",
2280
+ contentClassName,
2281
+ scopeOverlayContent = false,
2282
+ forcePushRouteChanges = false
2283
+ }) {
2284
+ const snapshot = useMemo(
2285
+ () => stackSnapshotForRouteProps(
2286
+ route,
2287
+ props,
2288
+ stackKey,
2289
+ forcePushRouteChanges
2290
+ ),
2291
+ [
2292
+ props.historyState,
2293
+ props.params,
2294
+ props.pathname,
2295
+ props.search,
2296
+ props.splitPane,
2297
+ forcePushRouteChanges,
2298
+ route,
2299
+ stackKey
2300
+ ]
2301
+ );
2302
+ const [entries, setEntries] = useState(() => [
2303
+ createEntry(snapshot, "forward", false)
2304
+ ]);
2305
+ const [heldEnterIds, setHeldEnterIds] = useState(() => /* @__PURE__ */ new Set());
2306
+ const heldEnterIdsRef = useRef(heldEnterIds);
2307
+ const enterHoldCleanupRef = useRef(/* @__PURE__ */ new Map());
2308
+ const entryArchiveRef = useRef(
2309
+ new Map(entries.map((entry) => [entry.id, entry]))
2310
+ );
2311
+ const { stateMap, setItem, toggle, deleteItem } = useTransitionMap({
2312
+ timeout: ROUTE_TRANSITION_MS,
2313
+ preEnter: true,
2314
+ preExit: true,
2315
+ mountOnEnter: true,
2316
+ unmountOnExit: true,
2317
+ allowMultiple: true
2318
+ });
2319
+ for (const entry of entries) {
2320
+ entryArchiveRef.current.set(entry.id, entry);
2321
+ }
2322
+ heldEnterIdsRef.current = heldEnterIds;
2323
+ const releaseHeldEnter = useCallback((id) => {
2324
+ const cleanup = enterHoldCleanupRef.current.get(id);
2325
+ if (cleanup) cleanup();
2326
+ enterHoldCleanupRef.current.delete(id);
2327
+ setHeldEnterIds((prev) => {
2328
+ if (!prev.has(id)) return prev;
2329
+ const next = new Set(prev);
2330
+ next.delete(id);
2331
+ return next;
2332
+ });
2333
+ }, []);
2334
+ const holdEnterUntilPainted = useCallback((id, onRelease) => {
2335
+ if (heldEnterIdsRef.current.has(id) || enterHoldCleanupRef.current.has(id)) return;
2336
+ setHeldEnterIds((prev) => {
2337
+ if (prev.has(id)) return prev;
2338
+ const next = new Set(prev);
2339
+ next.add(id);
2340
+ return next;
2341
+ });
2342
+ let frame = 0;
2343
+ let timeout = 0;
2344
+ const cleanup = () => {
2345
+ if (frame) window.cancelAnimationFrame(frame);
2346
+ if (timeout) window.clearTimeout(timeout);
2347
+ };
2348
+ frame = window.requestAnimationFrame(() => {
2349
+ timeout = window.setTimeout(() => {
2350
+ enterHoldCleanupRef.current.delete(id);
2351
+ setHeldEnterIds((prev) => {
2352
+ if (!prev.has(id)) return prev;
2353
+ const next = new Set(prev);
2354
+ next.delete(id);
2355
+ return next;
2356
+ });
2357
+ onRelease();
2358
+ }, 0);
2359
+ });
2360
+ enterHoldCleanupRef.current.set(id, cleanup);
2361
+ }, []);
2362
+ useEffect(() => {
2363
+ return () => {
2364
+ for (const cleanup of enterHoldCleanupRef.current.values()) cleanup();
2365
+ enterHoldCleanupRef.current.clear();
2366
+ };
2367
+ }, []);
2368
+ useLayoutEffect(() => {
2369
+ setEntries((prev) => {
2370
+ const change = resolveStackChange(prev, snapshot);
2371
+ const nextIds = new Set(change.entries.map((entry) => entry.id));
2372
+ for (const entry of prev) {
2373
+ if (!nextIds.has(entry.id)) {
2374
+ entryArchiveRef.current.set(entry.id, {
2375
+ ...entry,
2376
+ animateEnter: false,
2377
+ exitDirection: change.direction
2378
+ });
2379
+ }
2380
+ }
2381
+ for (const entry of change.entries) {
2382
+ entryArchiveRef.current.set(entry.id, entry);
2383
+ }
2384
+ return change.entries;
2385
+ });
2386
+ }, [snapshot]);
2387
+ const visibleEntries = useMemo(
2388
+ () => visibleEntriesForSnapshot(entries, snapshot),
2389
+ [entries, snapshot]
2390
+ );
2391
+ const activeIds = useMemo(() => new Set(entries.map((entry) => entry.id)), [entries]);
2392
+ const visibleActiveIds = useMemo(
2393
+ () => new Set(visibleEntries.map((entry) => entry.id)),
2394
+ [visibleEntries]
2395
+ );
2396
+ const visibleActiveId = visibleEntries[visibleEntries.length - 1]?.id ?? null;
2397
+ const visibleActiveEntry = visibleEntries[visibleEntries.length - 1];
2398
+ const visibleStackDirection = visibleActiveEntry?.enterDirection ?? "forward";
2399
+ const activeIdKey = useMemo(
2400
+ () => entries.map((entry) => entry.id).join("|"),
2401
+ [entries]
2402
+ );
2403
+ const mountedTransitionEntries = useMemo(
2404
+ () => Array.from(stateMap.entries()).filter(([, state]) => state.isMounted),
2405
+ [stateMap]
2406
+ );
2407
+ const isTransitionSettled = mountedTransitionEntries.every(
2408
+ ([, state]) => state.isResolved
2409
+ );
2410
+ useLayoutEffect(() => {
2411
+ for (const entry of entries) {
2412
+ const state = stateMap.get(entry.id);
2413
+ if (!state) {
2414
+ setItem(entry.id, { initialEntered: !entry.animateEnter });
2415
+ if (entry.animateEnter) {
2416
+ holdEnterUntilPainted(entry.id, () => toggle(entry.id, true));
2417
+ }
2418
+ continue;
2419
+ }
2420
+ if (heldEnterIds.has(entry.id)) {
2421
+ continue;
2422
+ }
2423
+ if (!state.isEnter) {
2424
+ toggle(entry.id, true);
2425
+ }
2426
+ }
2427
+ for (const [id, state] of stateMap) {
2428
+ if (!activeIds.has(id) && state.isEnter) {
2429
+ releaseHeldEnter(id);
2430
+ toggle(id, false);
2431
+ }
2432
+ }
2433
+ }, [
2434
+ activeIdKey,
2435
+ activeIds,
2436
+ entries,
2437
+ heldEnterIds,
2438
+ holdEnterUntilPainted,
2439
+ releaseHeldEnter,
2440
+ setItem,
2441
+ stateMap,
2442
+ toggle
2443
+ ]);
2444
+ useEffect(() => {
2445
+ const unmountedIds = Array.from(stateMap.entries()).filter(([, state]) => !state.isMounted).map(([id]) => id);
2446
+ for (const id of unmountedIds) {
2447
+ if (activeIds.has(id)) continue;
2448
+ entryArchiveRef.current.delete(id);
2449
+ releaseHeldEnter(id);
2450
+ deleteItem(id);
2451
+ }
2452
+ }, [activeIds, deleteItem, releaseHeldEnter, stateMap]);
2453
+ const renderEntries = useMemo(() => {
2454
+ const result = [...visibleEntries];
2455
+ const visibleIds = new Set(visibleEntries.map((entry) => entry.id));
2456
+ for (const [id] of mountedTransitionEntries) {
2457
+ if (visibleIds.has(id)) continue;
2458
+ const entry = entryArchiveRef.current.get(id);
2459
+ if (!entry) continue;
2460
+ result.push(entry);
2461
+ }
2462
+ return result;
2463
+ }, [mountedTransitionEntries, visibleEntries]);
2464
+ return /* @__PURE__ */ jsx(
2465
+ "div",
2466
+ {
2467
+ className: `screen-stack ${className}`,
2468
+ "data-animation": !isTransitionSettled ? "true" : void 0,
2469
+ style: { "--stack-transition-time": `${ROUTE_TRANSITION_MS}ms` },
2470
+ children: renderEntries.map((entry, index) => {
2471
+ const phase = phaseForEntry(entry, visibleActiveIds, visibleActiveId);
2472
+ const transitionStatus = heldEnterIds.has(entry.id) ? "preEnter" : transitionStatusForEntry(entry, stateMap.get(entry.id)?.status);
2473
+ const animationType = animationClassForEntry(
2474
+ entry,
2475
+ phase,
2476
+ visibleActiveEntry,
2477
+ visibleStackDirection
2478
+ );
2479
+ const zIndex = phase === "exiting" ? renderEntries.length + index + 1 : index + 1;
2480
+ const routeContent = /* @__PURE__ */ jsx(
2481
+ FrozenPaneRouteContent,
2482
+ {
2483
+ route: entry.route,
2484
+ props: propsForStackEntry(entry),
2485
+ fallback: entry.route.fallback ?? fallback
2486
+ }
2487
+ );
2488
+ const scopedContent = scopeOverlayContent ? /* @__PURE__ */ jsx(OverlayContentScope, { contentKey: entry.historyNavigationKey, children: routeContent }) : routeContent;
2489
+ return /* @__PURE__ */ jsx(
2490
+ "div",
2491
+ {
2492
+ "data-route-stack-screen": "true",
2493
+ "data-route-stack-key": entry.stackKey,
2494
+ "data-route-stack-route-id": entry.route.id,
2495
+ "data-route-stack-container": entry.route.container,
2496
+ "data-route-stack-presentation": entry.presentation,
2497
+ "data-route-stack-phase": phase,
2498
+ "data-route-stack-transition": transitionStatus,
2499
+ className: screenClassName(entry, phase, transitionStatus, animationType),
2500
+ "aria-hidden": phase === "active" ? void 0 : true,
2501
+ inert: phase === "active" ? void 0 : true,
2502
+ style: { zIndex },
2503
+ children: /* @__PURE__ */ jsx("div", { className: "stack-screen-container", children: contentClassName ? /* @__PURE__ */ jsx("div", { className: contentClassName, children: scopedContent }) : scopedContent })
2504
+ },
2505
+ entry.id
2506
+ );
2507
+ })
2508
+ }
2509
+ );
2510
+ }
2511
+ function SplitViewRouteContent({
2512
+ route,
2513
+ props,
2514
+ fallback
2515
+ }) {
2516
+ const splitView = route.splitView;
2517
+ const [splitViewRef, isWide] = useElementMinWidth(
2518
+ splitView?.wideMinWidth ?? splitViewWideMinWidth(DEFAULT_SPLIT_MIN_WIDTH, DEFAULT_SPLIT_PRIMARY_MAX_WIDTH)
2519
+ );
2520
+ if (!splitView) {
2521
+ return route.childRoute ? renderRouteContent(route.childRoute, props) : null;
2522
+ }
2523
+ const storedSplitNavigation = splitNavigationFromState(props.historyState);
2524
+ const detailPathId = storedSplitNavigation?.splitViewId === splitView.layoutId ? storedSplitNavigation.detailPathId : `${splitView.layoutId}:default`;
2525
+ const detailDepth = storedSplitNavigation?.splitViewId === splitView.layoutId ? storedSplitNavigation.detailDepth : 0;
2526
+ const primaryNavigationOrigin = {
2527
+ splitViewId: splitView.layoutId,
2528
+ pane: "primary",
2529
+ detailPathId,
2530
+ detailDepth
2531
+ };
2532
+ const secondaryNavigationOrigin = {
2533
+ splitViewId: splitView.layoutId,
2534
+ pane: "secondary",
2535
+ detailPathId,
2536
+ detailDepth
2537
+ };
2538
+ const primaryProps = {
2539
+ ...props,
2540
+ splitPane: "primary"
2541
+ };
2542
+ const secondaryProps = splitView.secondaryRoute ? {
2543
+ ...props,
2544
+ pathname: pathnameForRouteContext(splitView.secondaryRoute, props.pathname),
2545
+ splitPane: "secondary"
2546
+ } : props;
2547
+ const primary = /* @__PURE__ */ jsx(SplitPaneNavigationContext.Provider, { value: primaryNavigationOrigin, children: /* @__PURE__ */ jsx(
2548
+ FrozenPaneRouteContent,
2549
+ {
2550
+ route: splitView.primaryRoute,
2551
+ props: primaryProps,
2552
+ fallback: splitView.primaryRoute.fallback ?? fallback
2553
+ }
2554
+ ) });
2555
+ const secondary = splitView.secondaryRoute ? /* @__PURE__ */ jsx(SplitPaneNavigationContext.Provider, { value: secondaryNavigationOrigin, children: isWide ? /* @__PURE__ */ jsx(
2556
+ RouteStackViewport,
2557
+ {
2558
+ route: splitView.secondaryRoute,
2559
+ props: secondaryProps,
2560
+ fallback: splitView.secondaryRoute.fallback ?? fallback,
2561
+ stackKey: `${route.id}:secondary`
2562
+ },
2563
+ detailPathId
2564
+ ) : /* @__PURE__ */ jsx(
2565
+ FrozenPaneRouteContent,
2566
+ {
2567
+ route: splitView.secondaryRoute,
2568
+ props: secondaryProps,
2569
+ fallback: splitView.secondaryRoute.fallback ?? fallback
2570
+ }
2571
+ ) }) : null;
2572
+ const activeContent = splitView.activePane === "secondary" && secondary ? secondary : primary;
2573
+ const layoutProps = {
2574
+ children: isWide ? primary : activeContent,
2575
+ primary,
2576
+ secondary,
2577
+ isWide,
2578
+ hasSecondary: Boolean(splitView.secondaryRoute),
2579
+ minWidth: splitView.minWidth,
2580
+ primaryMaxWidth: splitView.primaryMaxWidth,
2581
+ secondaryMinWidth: splitView.secondaryMinWidth,
2582
+ splitViewRef,
2583
+ splitViewLayoutId: splitView.layoutId
2584
+ };
2585
+ if (route.layout) {
2586
+ return createElement(route.layout, layoutProps);
2587
+ }
2588
+ if (route.component) {
2589
+ return createElement(route.component, { ...props, ...layoutProps });
2590
+ }
2591
+ if (route.elementType) {
2592
+ return createElement(route.elementType, { ...props, ...layoutProps });
2593
+ }
2594
+ if (route.render) return route.render(props);
2595
+ return /* @__PURE__ */ jsx(DefaultSplitViewLayout, { ...layoutProps });
2596
+ }
2597
+ function DrawerRouteContent({
2598
+ route,
2599
+ props
2600
+ }) {
2601
+ const [isOpen, setIsOpen] = useState(false);
2602
+ const open = useCallback(() => setIsOpen(true), []);
2603
+ const close = useCallback(() => setIsOpen(false), []);
2604
+ const toggle = useCallback(() => setIsOpen((value) => !value), []);
2605
+ const drawerApi = useMemo(
2606
+ () => ({ isOpen, open, close, toggle }),
2607
+ [close, isOpen, open, toggle]
2608
+ );
2609
+ const childFallback = route.childRoute?.fallback ?? route.fallback ?? null;
2610
+ const drawer = route.drawer;
2611
+ const activeItem = drawer?.activeItem;
2612
+ const content = route.childRoute ? /* @__PURE__ */ jsx(
2613
+ RouteStackViewport,
2614
+ {
2615
+ route: route.childRoute,
2616
+ props,
2617
+ fallback: childFallback,
2618
+ stackKey: `${route.stackKey ?? route.id}:drawer:${activeItem ?? "content"}`
2619
+ },
2620
+ activeItem
2621
+ ) : null;
2622
+ const onItemChange = drawer ? (id) => {
2623
+ const item = findDrawerItem(drawer.items, id);
2624
+ if (item && normalizedPath(item.path) !== props.pathname) {
2625
+ navigateWithHistoryIndex(item.path);
2626
+ }
2627
+ close();
2628
+ } : void 0;
2629
+ const layoutProps = {
2630
+ children: content,
2631
+ aside: route.drawerAside,
2632
+ footer: route.drawerFooter,
2633
+ portal: false,
2634
+ items: drawer?.items,
2635
+ activeItem: drawer?.activeItem,
2636
+ onItemChange,
2637
+ drawer: drawerApi
2638
+ };
2639
+ let rendered;
2640
+ if (route.layout) {
2641
+ rendered = createElement(route.layout, layoutProps);
2642
+ } else if (route.component) {
2643
+ rendered = createElement(route.component, { ...props, ...layoutProps });
2644
+ } else if (route.elementType) {
2645
+ rendered = createElement(route.elementType, { ...props, ...layoutProps });
2646
+ } else if (route.render) {
2647
+ rendered = route.render(props);
2648
+ } else {
2649
+ rendered = /* @__PURE__ */ jsx(DefaultDrawerLayout, { ...layoutProps });
2650
+ }
2651
+ return /* @__PURE__ */ jsx(RouterDrawerContext.Provider, { value: drawerApi, children: rendered });
2652
+ }
2653
+ function findDrawerItem(nodes, id) {
2654
+ for (const node of nodes) {
2655
+ if (node.type === "item") {
2656
+ if (node.id === id) return node;
2657
+ continue;
2658
+ }
2659
+ if (node.type === "section") continue;
2660
+ const item = node.items.find((entry) => entry.id === id);
2661
+ if (item) return item;
2662
+ }
2663
+ return void 0;
2664
+ }
2665
+ function renderRouteContent(route, props) {
2666
+ if (route.container === "splitview" && route.splitView) {
2667
+ return /* @__PURE__ */ jsx(
2668
+ SplitViewRouteContent,
2669
+ {
2670
+ route,
2671
+ props,
2672
+ fallback: route.fallback ?? null
2673
+ }
2674
+ );
2675
+ }
2676
+ if (route.component) {
2677
+ return createElement(route.component, props);
2678
+ }
2679
+ let content;
2680
+ if (route.elementType) {
2681
+ content = createElement(route.elementType, props);
2682
+ } else if (route.render) {
2683
+ content = route.render(props);
2684
+ } else if (route.childrenNode !== void 0) {
2685
+ content = route.childrenNode;
2686
+ } else {
2687
+ content = route.element;
2688
+ }
2689
+ if (!route.layout) return content;
2690
+ return createElement(route.layout, { children: content });
2691
+ }
2692
+ function renderCompiledRoute(entry) {
2693
+ const props = {
2694
+ params: entry.params,
2695
+ pathname: entry.pathname,
2696
+ search: entry.search,
2697
+ historyState: entry.historyState,
2698
+ presentation: entry.presentation
2699
+ };
2700
+ if (entry.route.container === "drawer" && entry.route.childRoute) {
2701
+ return /* @__PURE__ */ jsx(DrawerRouteContent, { route: entry.route, props });
2702
+ }
2703
+ if (entry.route.container === "tabbar" && entry.route.childRoute) {
2704
+ const childFallback = entry.route.childRoute.fallback ?? entry.route.fallback ?? null;
2705
+ const content = /* @__PURE__ */ jsx(Suspense, { fallback: childFallback, children: renderRouteContent(entry.route.childRoute, props) });
2706
+ const tabBar = entry.route.tabBar;
2707
+ const onTabChange = tabBar ? (id) => {
2708
+ const tab = tabBar.tabs.find((item) => item.id === id);
2709
+ if (tab && normalizedPath(tab.path) !== entry.pathname) {
2710
+ navigateWithHistoryIndex(tab.path);
2711
+ }
2712
+ } : void 0;
2713
+ const layoutProps = {
2714
+ children: content,
2715
+ portal: false,
2716
+ tabs: tabBar?.tabs,
2717
+ activeTab: tabBar?.activeTab,
2718
+ onTabChange
2719
+ };
2720
+ if (entry.route.layout) {
2721
+ return createElement(entry.route.layout, layoutProps);
2722
+ }
2723
+ if (entry.route.component) {
2724
+ return createElement(entry.route.component, { ...props, ...layoutProps });
2725
+ }
2726
+ if (entry.route.elementType) {
2727
+ return createElement(entry.route.elementType, { ...props, ...layoutProps });
2728
+ }
2729
+ if (entry.route.render) return entry.route.render(props);
2730
+ return content;
2731
+ }
2732
+ return renderRouteContent(entry.route, props);
2733
+ }
2734
+ var FrozenStackEntry = memo(function FrozenStackEntry2({
2735
+ entry,
2736
+ fallback
2737
+ }) {
2738
+ const hook = useMemo(() => frozenLocationHook(entry.pathname), [entry.pathname]);
2739
+ const searchHook = useMemo(() => () => entry.search, [entry.search]);
2740
+ const routeContext = useMemo(() => routeContextValue(entry), [entry]);
2741
+ const suspenseFallback = entry.route.fallback ?? fallback;
2742
+ return /* @__PURE__ */ jsx(Router$1, { hook, searchHook, children: /* @__PURE__ */ jsx(RouterRouteContext.Provider, { value: routeContext, children: /* @__PURE__ */ jsx(Suspense, { fallback: suspenseFallback, children: renderCompiledRoute(entry) }) }) });
2743
+ });
2744
+ var FrozenModalRouteEntry = memo(function FrozenModalRouteEntry2({
2745
+ entry,
2746
+ fallback
2747
+ }) {
2748
+ const activeScreen = entry.flowScreens?.[entry.flowScreens.length - 1];
2749
+ if (activeScreen) {
2750
+ const props = {
2751
+ params: activeScreen.params,
2752
+ pathname: activeScreen.pathname,
2753
+ search: activeScreen.search,
2754
+ historyState: activeScreen.historyState,
2755
+ presentation: "push"
2756
+ };
2757
+ return /* @__PURE__ */ jsx(
2758
+ RouteStackViewport,
2759
+ {
2760
+ route: activeScreen.route,
2761
+ props,
2762
+ fallback: activeScreen.route.fallback ?? fallback,
2763
+ stackKey: `${entry.id}:overlay-flow`,
2764
+ className: `router-overlay-flow-stack router-overlay-flow-stack--${entry.kind}`,
2765
+ contentClassName: "router-overlay-flow-screen",
2766
+ scopeOverlayContent: true,
2767
+ forcePushRouteChanges: true
2768
+ }
2769
+ );
2770
+ }
2771
+ return /* @__PURE__ */ jsx(
2772
+ FrozenModalCompiledRouteEntry,
2773
+ {
2774
+ entry,
2775
+ route: entry.route,
2776
+ fallback
2777
+ }
2778
+ );
2779
+ });
2780
+ var FrozenModalCompiledRouteEntry = memo(function FrozenModalCompiledRouteEntry2({
2781
+ entry,
2782
+ route,
2783
+ fallback
2784
+ }) {
2785
+ const hook = useMemo(() => frozenLocationHook(entry.pathname), [entry.pathname]);
2786
+ const searchHook = useMemo(() => () => entry.search, [entry.search]);
2787
+ const routeContext = useMemo(
2788
+ () => ({
2789
+ params: entry.params,
2790
+ pathname: entry.pathname,
2791
+ search: entry.search,
2792
+ searchParams: searchParamsForSearch(entry.search),
2793
+ historyState: entry.historyState,
2794
+ presentation: entry.kind
2795
+ }),
2796
+ [entry]
2797
+ );
2798
+ const props = useMemo(
2799
+ () => ({
2800
+ params: entry.params,
2801
+ pathname: entry.pathname,
2802
+ search: entry.search,
2803
+ historyState: entry.historyState,
2804
+ presentation: entry.kind
2805
+ }),
2806
+ [entry]
2807
+ );
2808
+ const suspenseFallback = route.fallback ?? fallback;
2809
+ const modalFallback = suspenseFallback ?? (entry.kind === "sheet" ? DEFAULT_SHEET_FALLBACK : null);
2810
+ return /* @__PURE__ */ jsx(Router$1, { hook, searchHook, children: /* @__PURE__ */ jsx(RouterRouteContext.Provider, { value: routeContext, children: /* @__PURE__ */ jsx(Suspense, { fallback: modalFallback, children: renderRouteContent(route, props) }) }) });
2811
+ });
2812
+ function RouterModalStackHost({
2813
+ entries,
2814
+ close,
2815
+ remove,
2816
+ fallback,
2817
+ shell
2818
+ }) {
2819
+ return /* @__PURE__ */ jsx(Fragment, { children: entries.map((entry, index) => /* @__PURE__ */ jsx(
2820
+ RouterModalEntryHost,
2821
+ {
2822
+ entry,
2823
+ index,
2824
+ close,
2825
+ remove,
2826
+ fallback,
2827
+ shell
2828
+ },
2829
+ entry.id
2830
+ )) });
2831
+ }
2832
+ var RouterModalEntryHost = memo(function RouterModalEntryHost2({
2833
+ entry,
2834
+ index,
2835
+ close,
2836
+ remove,
2837
+ fallback,
2838
+ shell
2839
+ }) {
2840
+ const closeSelf = useCallback(() => close(entry.id), [close, entry.id]);
2841
+ const removeSelf = useCallback(() => remove(entry.id), [remove, entry.id]);
2842
+ const current = useMemo(
2843
+ () => ({
2844
+ id: entry.id,
2845
+ index,
2846
+ exiting: entry.exiting,
2847
+ close: closeSelf,
2848
+ remove: removeSelf
2849
+ }),
2850
+ [entry.id, entry.exiting, index, closeSelf, removeSelf]
2851
+ );
2852
+ const zIndex = OVERLAY_STACK_BASE_Z + index;
2853
+ const overlayShell = entry.route.overlayLayout ?? shell;
2854
+ const activeContentKey = entry.flowScreens ? historyNavigationKeyFromState(entry.historyState) ?? void 0 : void 0;
2855
+ return /* @__PURE__ */ jsx(CurrentModalContext.Provider, { value: current, children: entry.kind === "sheet" ? /* @__PURE__ */ jsx(
2856
+ SheetFrame,
2857
+ {
2858
+ open: !entry.exiting,
2859
+ onRequestClose: closeSelf,
2860
+ onExitComplete: removeSelf,
2861
+ zIndex,
2862
+ shell: overlayShell,
2863
+ activeContentKey,
2864
+ children: /* @__PURE__ */ jsx(FrozenModalRouteEntry, { entry, fallback })
2865
+ }
2866
+ ) : /* @__PURE__ */ jsx(
2867
+ ModalFrame,
2868
+ {
2869
+ open: !entry.exiting,
2870
+ onRequestClose: closeSelf,
2871
+ onExitComplete: removeSelf,
2872
+ zIndex,
2873
+ shell: overlayShell,
2874
+ activeContentKey,
2875
+ children: /* @__PURE__ */ jsx(FrozenModalRouteEntry, { entry, fallback })
2876
+ }
2877
+ ) });
2878
+ });
2879
+ function resolveStackChange(prev, snapshot) {
2880
+ const top = prev[prev.length - 1];
2881
+ if (!top) {
2882
+ return {
2883
+ entries: [createEntry(snapshot, "forward", false)],
2884
+ direction: "forward"
2885
+ };
2886
+ }
2887
+ if (top.locationKey === snapshot.locationKey) {
2888
+ return {
2889
+ entries: prev.map(
2890
+ (entry) => entry.id === top.id ? mergeEntrySnapshot(entry, snapshot) : markCommitted(entry)
2891
+ ),
2892
+ direction: "forward"
2893
+ };
2894
+ }
2895
+ const direction = resolveDirection(top, snapshot);
2896
+ if (isExplicitStackReplace(top, snapshot)) {
2897
+ return {
2898
+ entries: replaceActiveStackEntry(prev, top.id, snapshot, direction, true),
2899
+ direction
2900
+ };
2901
+ }
2902
+ const syncTargetIndex = syncTargetEntryIndex(prev, snapshot);
2903
+ if (syncTargetIndex !== -1 && syncTargetIndex !== prev.length - 1) {
2904
+ return {
2905
+ entries: entriesRevealingSnapshot(prev, syncTargetIndex, snapshot),
2906
+ direction: "back"
2907
+ };
2908
+ }
2909
+ if (shouldReplaceActiveStackEntry(top, snapshot)) {
2910
+ return {
2911
+ entries: replaceActiveStackEntry(prev, top.id, snapshot, direction, true),
2912
+ direction
2913
+ };
2914
+ }
2915
+ const existingIndex = existingStackEntryIndexForSnapshot(prev, snapshot);
2916
+ const revealsExistingEntry = existingIndex !== -1;
2917
+ const isBack = direction === "back";
2918
+ if (isBack || revealsExistingEntry) {
2919
+ if (!revealsExistingEntry) {
2920
+ return {
2921
+ entries: [createEntry(snapshot, "back", false)],
2922
+ direction: "back"
2923
+ };
2924
+ }
2925
+ const revealed = prev[existingIndex];
2926
+ const base = prev.slice(0, existingIndex).map(markCommitted);
2927
+ return {
2928
+ entries: [
2929
+ ...base,
2930
+ {
2931
+ ...revealed,
2932
+ ...snapshot,
2933
+ id: revealed.id,
2934
+ animateEnter: false,
2935
+ enterDirection: "back"
2936
+ }
2937
+ ],
2938
+ direction: "back"
2939
+ };
2940
+ }
2941
+ return {
2942
+ entries: [...prev.map(markCommitted), createEntry(snapshot, "forward", true)],
2943
+ direction: "forward"
2944
+ };
2945
+ }
2946
+ function visibleEntriesForSnapshot(entries, snapshot) {
2947
+ if (!snapshot) return entries;
2948
+ const top = entries[entries.length - 1];
2949
+ if (!top) {
2950
+ return [createEntry(snapshot, "forward", false)];
2951
+ }
2952
+ if (top.locationKey === snapshot.locationKey) {
2953
+ return entries.map(
2954
+ (entry) => entry.id === top.id ? mergeEntrySnapshot(entry, snapshot) : entry
2955
+ );
2956
+ }
2957
+ if (isExplicitStackReplace(top, snapshot)) {
2958
+ return replaceActiveStackEntry(
2959
+ entries,
2960
+ top.id,
2961
+ snapshot,
2962
+ resolveDirection(top, snapshot),
2963
+ false
2964
+ );
2965
+ }
2966
+ const syncTargetIndex = syncTargetEntryIndex(entries, snapshot);
2967
+ if (syncTargetIndex !== -1 && syncTargetIndex !== entries.length - 1) {
2968
+ return entriesRevealingSnapshot(entries, syncTargetIndex, snapshot);
2969
+ }
2970
+ if (shouldReplaceActiveStackEntry(top, snapshot)) {
2971
+ return replaceActiveStackEntry(
2972
+ entries,
2973
+ top.id,
2974
+ snapshot,
2975
+ resolveDirection(top, snapshot),
2976
+ false
2977
+ );
2978
+ }
2979
+ const direction = resolveDirection(top, snapshot);
2980
+ const existingIndex = existingStackEntryIndexForSnapshot(entries, snapshot);
2981
+ if (direction === "back" || existingIndex !== -1) {
2982
+ if (existingIndex === -1) {
2983
+ return [createEntry(snapshot, "back", false)];
2984
+ }
2985
+ const revealed = entries[existingIndex];
2986
+ const base = entries.slice(0, existingIndex).map(markCommitted);
2987
+ return [
2988
+ ...base,
2989
+ {
2990
+ ...revealed,
2991
+ ...snapshot,
2992
+ id: revealed.id,
2993
+ animateEnter: false,
2994
+ enterDirection: "back"
2995
+ }
2996
+ ];
2997
+ }
2998
+ return entries;
2999
+ }
3000
+ function activeModalEntry(entries) {
3001
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
3002
+ if (!entries[index].exiting) return entries[index];
3003
+ }
3004
+ return void 0;
3005
+ }
3006
+ function modalFlowScreenFromMatch(match, pathname, search, state, historyIndex, commit) {
3007
+ const route = match.route.childRoute;
3008
+ if (!match.route.overlayFlow || !route) return null;
3009
+ return {
3010
+ overlayRoute: match.route,
3011
+ route,
3012
+ params: match.params,
3013
+ pathname,
3014
+ search,
3015
+ historyState: stateWithHistoryIndex(
3016
+ state,
3017
+ historyIndex,
3018
+ createRouterId(),
3019
+ commit
3020
+ )
3021
+ };
3022
+ }
3023
+ function RouterRuntime({
3024
+ children,
3025
+ fallback,
3026
+ shell = "mobile-contained"
3027
+ }) {
3028
+ const [pathname] = useLocation();
3029
+ const search = useSearch();
3030
+ const historyState = useHistoryState$1();
3031
+ const wouterRouter = useRouter$1();
3032
+ const compiledRoutes = useMemo(() => compileStackRoutes(children), [children]);
3033
+ const runtimeChildren = useMemo(
3034
+ () => flattenRouteNodes(children).filter((child) => !isStackRouteElement(child)),
3035
+ [children]
3036
+ );
3037
+ const rawMatch = useMemo(
3038
+ () => matchStackRouteByPresentation(
3039
+ compiledRoutes,
3040
+ wouterRouter.parser,
3041
+ pathname,
3042
+ PUSH_PRESENTATIONS
3043
+ ),
3044
+ [compiledRoutes, pathname, wouterRouter.parser]
3045
+ );
3046
+ const fallbackNode = fallback ?? null;
3047
+ const [modalEntries, setModalEntries] = useState([]);
3048
+ const modalEntriesRef = useRef(modalEntries);
3049
+ const modalSeqRef = useRef(0);
3050
+ modalEntriesRef.current = modalEntries;
3051
+ useLayoutEffect(() => {
3052
+ ensureInitialHistoryIndex();
3053
+ }, []);
3054
+ useEffect(() => {
3055
+ document.documentElement.setAttribute("data-router-shell", shell);
3056
+ return () => {
3057
+ document.documentElement.removeAttribute("data-router-shell");
3058
+ };
3059
+ }, [shell]);
3060
+ const searchPath = search ? `?${search}` : "";
3061
+ const guardRedirect = useMemo(
3062
+ () => rawMatch ? guardRedirectForMatch(rawMatch, pathname, searchPath, historyState) : null,
3063
+ [historyState, pathname, rawMatch, searchPath]
3064
+ );
3065
+ const match = guardRedirect ? null : rawMatch;
3066
+ useLayoutEffect(() => {
3067
+ if (!guardRedirect) return;
3068
+ const target = parseNavigationTarget(guardRedirect, pathname);
3069
+ if (target.pathname === normalizedPath(pathname) && target.search === searchPath) {
3070
+ return;
3071
+ }
3072
+ navigateWithHistoryIndex(`${target.pathname}${target.search}`, { commit: "sync" });
3073
+ }, [guardRedirect, pathname, searchPath]);
3074
+ const snapshot = useMemo(() => {
3075
+ if (!match) return null;
3076
+ return stackSnapshot(pathname, searchPath, historyState, match);
3077
+ }, [historyState, match, pathname, searchPath]);
3078
+ const snapshotHistoryRef = useRef([]);
3079
+ const [entries, setEntries] = useState(
3080
+ () => snapshot ? [createEntry(snapshot, "forward", false)] : []
3081
+ );
3082
+ const [heldEnterIds, setHeldEnterIds] = useState(() => /* @__PURE__ */ new Set());
3083
+ const heldEnterIdsRef = useRef(heldEnterIds);
3084
+ const enterHoldCleanupRef = useRef(/* @__PURE__ */ new Map());
3085
+ const entryArchiveRef = useRef(
3086
+ new Map(entries.map((entry) => [entry.id, entry]))
3087
+ );
3088
+ const { stateMap, setItem, toggle, deleteItem } = useTransitionMap({
3089
+ timeout: ROUTE_TRANSITION_MS,
3090
+ preEnter: true,
3091
+ preExit: true,
3092
+ mountOnEnter: true,
3093
+ unmountOnExit: true,
3094
+ allowMultiple: true
3095
+ });
3096
+ for (const entry of entries) {
3097
+ entryArchiveRef.current.set(entry.id, entry);
3098
+ }
3099
+ const entriesRef = useRef(entries);
3100
+ entriesRef.current = entries;
3101
+ heldEnterIdsRef.current = heldEnterIds;
3102
+ const releaseHeldEnter = useCallback((id) => {
3103
+ const cleanup = enterHoldCleanupRef.current.get(id);
3104
+ if (cleanup) cleanup();
3105
+ enterHoldCleanupRef.current.delete(id);
3106
+ setHeldEnterIds((prev) => {
3107
+ if (!prev.has(id)) return prev;
3108
+ const next = new Set(prev);
3109
+ next.delete(id);
3110
+ return next;
3111
+ });
3112
+ }, []);
3113
+ const holdEnterUntilPainted = useCallback((id, onRelease) => {
3114
+ if (heldEnterIdsRef.current.has(id) || enterHoldCleanupRef.current.has(id)) return;
3115
+ setHeldEnterIds((prev) => {
3116
+ if (prev.has(id)) return prev;
3117
+ const next = new Set(prev);
3118
+ next.add(id);
3119
+ return next;
3120
+ });
3121
+ let frame = 0;
3122
+ let timeout = 0;
3123
+ const cleanup = () => {
3124
+ if (frame) window.cancelAnimationFrame(frame);
3125
+ if (timeout) window.clearTimeout(timeout);
3126
+ };
3127
+ frame = window.requestAnimationFrame(() => {
3128
+ timeout = window.setTimeout(() => {
3129
+ enterHoldCleanupRef.current.delete(id);
3130
+ setHeldEnterIds((prev) => {
3131
+ if (!prev.has(id)) return prev;
3132
+ const next = new Set(prev);
3133
+ next.delete(id);
3134
+ return next;
3135
+ });
3136
+ onRelease();
3137
+ }, 0);
3138
+ });
3139
+ enterHoldCleanupRef.current.set(id, cleanup);
3140
+ }, []);
3141
+ useEffect(() => {
3142
+ return () => {
3143
+ for (const cleanup of enterHoldCleanupRef.current.values()) cleanup();
3144
+ enterHoldCleanupRef.current.clear();
3145
+ };
3146
+ }, []);
3147
+ useLayoutEffect(() => {
3148
+ if (!snapshot) return;
3149
+ snapshotHistoryRef.current = updateSnapshotHistory(
3150
+ snapshotHistoryRef.current,
3151
+ snapshot
3152
+ );
3153
+ setEntries((prev) => {
3154
+ const change = resolveStackChange(prev, snapshot);
3155
+ const nextIds = new Set(change.entries.map((entry) => entry.id));
3156
+ for (const entry of prev) {
3157
+ if (!nextIds.has(entry.id)) {
3158
+ entryArchiveRef.current.set(entry.id, {
3159
+ ...entry,
3160
+ animateEnter: false,
3161
+ exitDirection: change.direction
3162
+ });
3163
+ }
3164
+ }
3165
+ for (const entry of change.entries) {
3166
+ entryArchiveRef.current.set(entry.id, entry);
3167
+ }
3168
+ return change.entries;
3169
+ });
3170
+ }, [snapshot]);
3171
+ const visibleEntries = useMemo(
3172
+ () => visibleEntriesForSnapshot(entries, snapshot),
3173
+ [entries, snapshot]
3174
+ );
3175
+ const activeIds = useMemo(() => new Set(entries.map((entry) => entry.id)), [entries]);
3176
+ const visibleActiveIds = useMemo(
3177
+ () => new Set(visibleEntries.map((entry) => entry.id)),
3178
+ [visibleEntries]
3179
+ );
3180
+ const visibleActiveId = visibleEntries[visibleEntries.length - 1]?.id ?? null;
3181
+ const visibleActiveEntry = visibleEntries[visibleEntries.length - 1];
3182
+ const visibleActiveEntryRef = useRef(void 0);
3183
+ visibleActiveEntryRef.current = visibleActiveEntry;
3184
+ const visibleStackDirection = visibleActiveEntry?.enterDirection ?? "forward";
3185
+ const activeIdKey = useMemo(
3186
+ () => entries.map((entry) => entry.id).join("|"),
3187
+ [entries]
3188
+ );
3189
+ const mountedTransitionEntries = useMemo(
3190
+ () => Array.from(stateMap.entries()).filter(([, state]) => state.isMounted),
3191
+ [stateMap]
3192
+ );
3193
+ const isTransitionSettled = mountedTransitionEntries.every(
3194
+ ([, state]) => state.isResolved
3195
+ );
3196
+ useLayoutEffect(() => {
3197
+ for (const entry of entries) {
3198
+ const state = stateMap.get(entry.id);
3199
+ if (!state) {
3200
+ setItem(entry.id, { initialEntered: !entry.animateEnter });
3201
+ if (entry.animateEnter) {
3202
+ holdEnterUntilPainted(entry.id, () => toggle(entry.id, true));
3203
+ }
3204
+ continue;
3205
+ }
3206
+ if (heldEnterIds.has(entry.id)) {
3207
+ continue;
3208
+ }
3209
+ if (!state.isEnter) {
3210
+ toggle(entry.id, true);
3211
+ }
3212
+ }
3213
+ for (const [id, state] of stateMap) {
3214
+ if (!activeIds.has(id) && state.isEnter) {
3215
+ releaseHeldEnter(id);
3216
+ toggle(id, false);
3217
+ }
3218
+ }
3219
+ }, [
3220
+ activeIdKey,
3221
+ activeIds,
3222
+ entries,
3223
+ heldEnterIds,
3224
+ holdEnterUntilPainted,
3225
+ releaseHeldEnter,
3226
+ setItem,
3227
+ stateMap,
3228
+ toggle
3229
+ ]);
3230
+ useEffect(() => {
3231
+ const unmountedIds = Array.from(stateMap.entries()).filter(([, state]) => !state.isMounted).map(([id]) => id);
3232
+ for (const id of unmountedIds) {
3233
+ if (activeIds.has(id)) continue;
3234
+ entryArchiveRef.current.delete(id);
3235
+ releaseHeldEnter(id);
3236
+ deleteItem(id);
3237
+ }
3238
+ }, [activeIds, deleteItem, releaseHeldEnter, stateMap]);
3239
+ const renderEntries = useMemo(() => {
3240
+ const result = [...visibleEntries];
3241
+ for (const [id] of mountedTransitionEntries) {
3242
+ if (visibleActiveIds.has(id)) continue;
3243
+ const entry = entryArchiveRef.current.get(id);
3244
+ if (!entry) continue;
3245
+ result.push(entry);
3246
+ }
3247
+ return result;
3248
+ }, [visibleActiveIds, visibleEntries, mountedTransitionEntries]);
3249
+ const removeModal = useCallback((id) => {
3250
+ setModalEntries((prev) => prev.filter((entry) => entry.id !== id));
3251
+ }, []);
3252
+ const closeModal = useCallback((id) => {
3253
+ setModalEntries((prev) => {
3254
+ let changed = false;
3255
+ const next = prev.map((entry) => {
3256
+ if (entry.id !== id || entry.exiting) return entry;
3257
+ changed = true;
3258
+ return { ...entry, exiting: true };
3259
+ });
3260
+ return changed ? next : prev;
3261
+ });
3262
+ }, []);
3263
+ const closeAllModals = useCallback(() => {
3264
+ setModalEntries((prev) => {
3265
+ let changed = false;
3266
+ const next = prev.map((entry) => {
3267
+ if (entry.exiting) return entry;
3268
+ changed = true;
3269
+ return { ...entry, exiting: true };
3270
+ });
3271
+ return changed ? next : prev;
3272
+ });
3273
+ }, []);
3274
+ const goBackTopModal = useCallback(() => {
3275
+ const entry = activeModalEntry(modalEntriesRef.current);
3276
+ if (!entry) return false;
3277
+ if (entry.flowScreens && entry.flowScreens.length > 1) {
3278
+ setModalEntries(
3279
+ (current) => current.map((candidate) => {
3280
+ if (candidate.id !== entry.id || !candidate.flowScreens) return candidate;
3281
+ const flowScreens = candidate.flowScreens.slice(0, -1);
3282
+ const activeScreen = flowScreens[flowScreens.length - 1];
3283
+ if (!activeScreen) return candidate;
3284
+ return {
3285
+ ...candidate,
3286
+ route: activeScreen.overlayRoute,
3287
+ params: activeScreen.params,
3288
+ pathname: activeScreen.pathname,
3289
+ search: activeScreen.search,
3290
+ historyState: activeScreen.historyState,
3291
+ flowScreens
3292
+ };
3293
+ })
3294
+ );
3295
+ return true;
3296
+ }
3297
+ closeModal(entry.id);
3298
+ return true;
3299
+ }, [closeModal]);
3300
+ const updateActiveModalFlow = useCallback(
3301
+ (match2, target, state, operation) => {
3302
+ const entry = activeModalEntry(modalEntriesRef.current);
3303
+ const flow = match2.route.overlayFlow;
3304
+ if (!entry?.flowScreens || !flow || entry.route.overlayFlow?.id !== flow.id) {
3305
+ return false;
3306
+ }
3307
+ const historyIndex = operation === "replace" ? Math.max(0, entry.flowScreens.length - 1) : entry.flowScreens.length;
3308
+ const screen = modalFlowScreenFromMatch(
3309
+ match2,
3310
+ target.pathname,
3311
+ target.search,
3312
+ state,
3313
+ historyIndex,
3314
+ operation
3315
+ );
3316
+ if (!screen) return false;
3317
+ setModalEntries(
3318
+ (current) => current.map((candidate) => {
3319
+ if (candidate.id !== entry.id || !candidate.flowScreens) return candidate;
3320
+ const flowScreens = operation === "replace" ? [...candidate.flowScreens.slice(0, -1), screen] : [...candidate.flowScreens, screen];
3321
+ return {
3322
+ ...candidate,
3323
+ route: screen.overlayRoute,
3324
+ params: screen.params,
3325
+ pathname: screen.pathname,
3326
+ search: screen.search,
3327
+ historyState: screen.historyState,
3328
+ flowScreens
3329
+ };
3330
+ })
3331
+ );
3332
+ return true;
3333
+ },
3334
+ []
3335
+ );
3336
+ const pushModalEntry = useCallback(
3337
+ (entry, replace2 = false) => {
3338
+ modalSeqRef.current += 1;
3339
+ const id = `${entry.kind}:${modalSeqRef.current}-${createRouterId()}`;
3340
+ const nextEntry = { ...entry, id, exiting: false };
3341
+ setModalEntries((prev) => {
3342
+ if (replace2 && prev.length > 0) {
3343
+ return [...prev.slice(0, -1), nextEntry];
3344
+ }
3345
+ return [...prev, nextEntry];
3346
+ });
3347
+ return id;
3348
+ },
3349
+ []
3350
+ );
3351
+ const navigate = useCallback(
3352
+ (to, options = {}) => {
3353
+ if (typeof to === "number") {
3354
+ if (to < 0 && goBackTopModal()) return;
3355
+ window.history.go(to);
3356
+ return;
3357
+ }
3358
+ const target = parseNavigationTarget(to, pathname);
3359
+ const overlayMatch = matchStackRouteByPresentation(
3360
+ compiledRoutes,
3361
+ wouterRouter.parser,
3362
+ target.pathname,
3363
+ OVERLAY_PRESENTATIONS
3364
+ );
3365
+ if (overlayMatch && isModalLikePresentation(overlayMatch.route.presentation)) {
3366
+ const overlayState = stateForSplitNavigationTarget(
3367
+ options.state,
3368
+ null,
3369
+ "navigate"
3370
+ );
3371
+ const overlayRedirect = guardRedirectForMatch(
3372
+ overlayMatch,
3373
+ target.pathname,
3374
+ target.search,
3375
+ overlayState
3376
+ );
3377
+ if (overlayRedirect) {
3378
+ navigateWithHistoryIndex(overlayRedirect, { commit: "sync" });
3379
+ return;
3380
+ }
3381
+ if (updateActiveModalFlow(overlayMatch, target, overlayState, "push")) {
3382
+ return;
3383
+ }
3384
+ const initialFlowScreen = modalFlowScreenFromMatch(
3385
+ overlayMatch,
3386
+ target.pathname,
3387
+ target.search,
3388
+ overlayState,
3389
+ 0,
3390
+ "push"
3391
+ );
3392
+ pushModalEntry({
3393
+ kind: overlayMatch.route.presentation,
3394
+ route: overlayMatch.route,
3395
+ params: overlayMatch.params,
3396
+ pathname: target.pathname,
3397
+ search: target.search,
3398
+ historyState: initialFlowScreen?.historyState ?? overlayState,
3399
+ flowScreens: initialFlowScreen ? [initialFlowScreen] : void 0
3400
+ });
3401
+ return;
3402
+ }
3403
+ const pushMatch = matchStackRouteByPresentation(
3404
+ compiledRoutes,
3405
+ wouterRouter.parser,
3406
+ target.pathname,
3407
+ PUSH_PRESENTATIONS
3408
+ );
3409
+ navigateWithHistoryIndex(to, {
3410
+ ...options,
3411
+ state: stateForSplitNavigationTarget(options.state, pushMatch, "navigate")
3412
+ });
3413
+ },
3414
+ [
3415
+ compiledRoutes,
3416
+ goBackTopModal,
3417
+ pathname,
3418
+ pushModalEntry,
3419
+ updateActiveModalFlow,
3420
+ wouterRouter.parser
3421
+ ]
3422
+ );
3423
+ const replace = useCallback(
3424
+ (to, options = {}) => {
3425
+ const target = parseNavigationTarget(to, pathname);
3426
+ const overlayMatch = matchStackRouteByPresentation(
3427
+ compiledRoutes,
3428
+ wouterRouter.parser,
3429
+ target.pathname,
3430
+ OVERLAY_PRESENTATIONS
3431
+ );
3432
+ if (overlayMatch && isModalLikePresentation(overlayMatch.route.presentation)) {
3433
+ const overlayState = stateForSplitNavigationTarget(
3434
+ options.state,
3435
+ null,
3436
+ "replace"
3437
+ );
3438
+ const overlayRedirect = guardRedirectForMatch(
3439
+ overlayMatch,
3440
+ target.pathname,
3441
+ target.search,
3442
+ overlayState
3443
+ );
3444
+ if (overlayRedirect) {
3445
+ navigateWithHistoryIndex(overlayRedirect, {
3446
+ commit: "replace",
3447
+ state: options.state
3448
+ });
3449
+ return;
3450
+ }
3451
+ if (updateActiveModalFlow(overlayMatch, target, overlayState, "replace")) {
3452
+ return;
3453
+ }
3454
+ const initialFlowScreen = modalFlowScreenFromMatch(
3455
+ overlayMatch,
3456
+ target.pathname,
3457
+ target.search,
3458
+ overlayState,
3459
+ 0,
3460
+ "replace"
3461
+ );
3462
+ pushModalEntry(
3463
+ {
3464
+ kind: overlayMatch.route.presentation,
3465
+ route: overlayMatch.route,
3466
+ params: overlayMatch.params,
3467
+ pathname: target.pathname,
3468
+ search: target.search,
3469
+ historyState: initialFlowScreen?.historyState ?? overlayState,
3470
+ flowScreens: initialFlowScreen ? [initialFlowScreen] : void 0
3471
+ },
3472
+ true
3473
+ );
3474
+ return;
3475
+ }
3476
+ const pushMatch = matchStackRouteByPresentation(
3477
+ compiledRoutes,
3478
+ wouterRouter.parser,
3479
+ target.pathname,
3480
+ PUSH_PRESENTATIONS
3481
+ );
3482
+ navigateWithHistoryIndex(to, {
3483
+ commit: "replace",
3484
+ state: stateForSplitNavigationTarget(options.state, pushMatch, "replace")
3485
+ });
3486
+ },
3487
+ [
3488
+ compiledRoutes,
3489
+ pathname,
3490
+ pushModalEntry,
3491
+ updateActiveModalFlow,
3492
+ wouterRouter.parser
3493
+ ]
3494
+ );
3495
+ const goBack = useCallback(() => {
3496
+ if (goBackTopModal()) return true;
3497
+ const activeEntry = visibleActiveEntryRef.current;
3498
+ const activeSplitRoute = activeEntry ? splitViewRouteForSnapshot(activeEntry) : void 0;
3499
+ const activeSplitNavigation = activeEntry?.splitNavigation;
3500
+ if (activeEntry && activeSplitRoute?.splitView?.activePane === "secondary" && activeSplitNavigation) {
3501
+ const previousDetailSnapshot = previousSnapshotInDetailPath(
3502
+ snapshotHistoryRef.current,
3503
+ activeEntry
3504
+ );
3505
+ if (navigateToSnapshot(previousDetailSnapshot)) return true;
3506
+ const splitRootTarget = secondarySplitBackTarget(activeEntry);
3507
+ if (splitRootTarget) {
3508
+ navigateWithHistoryIndex(`${splitRootTarget}${activeEntry.search}`, {
3509
+ commit: "sync",
3510
+ state: stateWithoutSplitNavigation(activeEntry.historyState)
3511
+ });
3512
+ return true;
3513
+ }
3514
+ }
3515
+ const previousSnapshot = previousSnapshotFromHistory(
3516
+ snapshotHistoryRef.current,
3517
+ activeEntry
3518
+ );
3519
+ if (canNavigateBackWithinDrawer(previousSnapshot, activeEntry)) {
3520
+ return navigateToSnapshot(previousSnapshot);
3521
+ }
3522
+ const previousEntry = previousStackEntry(entriesRef.current, activeEntry);
3523
+ if (navigateToSnapshot(previousEntry)) return true;
3524
+ if (canNavigateBackWithinSplit(previousSnapshot, activeEntry)) {
3525
+ return navigateToSnapshot(previousSnapshot);
3526
+ }
3527
+ const splitBackTarget = secondarySplitBackTarget(activeEntry);
3528
+ if (splitBackTarget) {
3529
+ navigateWithHistoryIndex(`${splitBackTarget}${activeEntry?.search ?? ""}`, {
3530
+ commit: "sync",
3531
+ state: activeEntry?.historyState
3532
+ });
3533
+ return true;
3534
+ }
3535
+ return false;
3536
+ }, [goBackTopModal]);
3537
+ const router = useMemo(
3538
+ () => ({
3539
+ navigate,
3540
+ replace,
3541
+ close: closeModal,
3542
+ closeAll: closeAllModals,
3543
+ goBack
3544
+ }),
3545
+ [closeAllModals, closeModal, goBack, navigate, replace]
3546
+ );
3547
+ useEffect(() => {
3548
+ if (visibleActiveEntry) {
3549
+ document.documentElement.setAttribute(
3550
+ "data-route-stack-active-key",
3551
+ visibleActiveEntry.stackKey
3552
+ );
3553
+ document.documentElement.setAttribute(
3554
+ "data-route-stack-active-presentation",
3555
+ visibleActiveEntry.presentation
3556
+ );
3557
+ }
3558
+ return () => {
3559
+ document.documentElement.removeAttribute("data-route-stack-active-key");
3560
+ document.documentElement.removeAttribute("data-route-stack-active-presentation");
3561
+ };
3562
+ }, [visibleActiveEntry]);
3563
+ if (guardRedirect) {
3564
+ return /* @__PURE__ */ jsxs(RouterActionsContext.Provider, { value: router, children: [
3565
+ runtimeChildren,
3566
+ fallbackNode
3567
+ ] });
3568
+ }
3569
+ if (!snapshot && entries.length === 0) {
3570
+ return /* @__PURE__ */ jsxs(RouterActionsContext.Provider, { value: router, children: [
3571
+ runtimeChildren,
3572
+ fallbackNode,
3573
+ /* @__PURE__ */ jsx(
3574
+ RouterModalStackHost,
3575
+ {
3576
+ entries: modalEntries,
3577
+ close: closeModal,
3578
+ remove: removeModal,
3579
+ fallback: fallbackNode,
3580
+ shell
3581
+ }
3582
+ )
3583
+ ] });
3584
+ }
3585
+ return /* @__PURE__ */ jsxs(RouterActionsContext.Provider, { value: router, children: [
3586
+ runtimeChildren,
3587
+ /* @__PURE__ */ jsx(
3588
+ "div",
3589
+ {
3590
+ className: "screen-stack",
3591
+ "data-router-shell": shell,
3592
+ "data-animation": !isTransitionSettled ? "true" : void 0,
3593
+ style: { "--stack-transition-time": `${ROUTE_TRANSITION_MS}ms` },
3594
+ children: renderEntries.map((entry, index) => {
3595
+ const phase = phaseForEntry(entry, visibleActiveIds, visibleActiveId);
3596
+ const transitionStatus = heldEnterIds.has(entry.id) ? "preEnter" : transitionStatusForEntry(entry, stateMap.get(entry.id)?.status);
3597
+ const animationType = animationClassForEntry(
3598
+ entry,
3599
+ phase,
3600
+ visibleActiveEntry,
3601
+ visibleStackDirection
3602
+ );
3603
+ const zIndex = phase === "exiting" ? renderEntries.length + index + 1 : index + 1;
3604
+ const contentContainer = stackEntryContentContainer(entry);
3605
+ return /* @__PURE__ */ jsx(
3606
+ "div",
3607
+ {
3608
+ "data-route-stack-screen": "true",
3609
+ "data-route-stack-key": entry.stackKey,
3610
+ "data-route-stack-route-id": entry.route.id,
3611
+ "data-route-stack-container": entry.route.container,
3612
+ "data-route-stack-content-container": contentContainer,
3613
+ "data-route-stack-presentation": entry.presentation,
3614
+ "data-route-stack-phase": phase,
3615
+ "data-route-stack-transition": transitionStatus,
3616
+ className: screenClassName(entry, phase, transitionStatus, animationType),
3617
+ style: { zIndex },
3618
+ children: /* @__PURE__ */ jsx("div", { className: "stack-screen-container", children: /* @__PURE__ */ jsx(FrozenStackEntry, { entry, fallback: fallbackNode }) })
3619
+ },
3620
+ entry.id
3621
+ );
3622
+ })
3623
+ }
3624
+ ),
3625
+ /* @__PURE__ */ jsx(
3626
+ RouterModalStackHost,
3627
+ {
3628
+ entries: modalEntries,
3629
+ close: closeModal,
3630
+ remove: removeModal,
3631
+ fallback: fallbackNode,
3632
+ shell
3633
+ }
3634
+ )
3635
+ ] });
3636
+ }
3637
+ function RouterRoot(props) {
3638
+ return /* @__PURE__ */ jsx(
3639
+ Router$1,
3640
+ {
3641
+ aroundNav: (_navigate, to, options) => {
3642
+ navigateWithHistoryIndex(to, options);
3643
+ },
3644
+ children: /* @__PURE__ */ jsx(RouterRuntime, { ...props })
3645
+ }
3646
+ );
3647
+ }
3648
+ var RouterScreen = routeComponent("push");
3649
+ var RouterStack = routeComponent("push");
3650
+ var RouterModal = routeComponent("modal");
3651
+ var RouterSheet = routeComponent("sheet");
3652
+ var RouterTabBar = routeComponent("push", "tabbar");
3653
+ var RouterTabBarItem = routeComponent("push", "tabbar-item");
3654
+ var RouterDrawer = routeComponent("push", "drawer");
3655
+ var RouterDrawerGroup = routeComponent("push", "drawer-group");
3656
+ var RouterDrawerSection = routeComponent(
3657
+ "push",
3658
+ "drawer-section"
3659
+ );
3660
+ var RouterDrawerItem = routeComponent("push", "drawer-item");
3661
+ var RouterSplitViewPrimary = routeComponent(
3662
+ "push",
3663
+ "split-primary"
3664
+ );
3665
+ var RouterSplitViewSecondary = routeComponent(
3666
+ "push",
3667
+ "split-secondary"
3668
+ );
3669
+ var RouterSplitView = Object.assign(
3670
+ routeComponent("push", "splitview"),
3671
+ {
3672
+ Primary: RouterSplitViewPrimary,
3673
+ Secondary: RouterSplitViewSecondary
3674
+ }
3675
+ );
3676
+ var RouterProtected = routeComponent("push", "protected");
3677
+ var RouterRedirect = routeComponent("push", "redirect");
3678
+ var Router = Object.assign(RouterRoot, {
3679
+ Screen: RouterScreen,
3680
+ Stack: RouterStack,
3681
+ Redirect: RouterRedirect,
3682
+ Modal: RouterModal,
3683
+ Sheet: RouterSheet,
3684
+ TabBar: RouterTabBar,
3685
+ TabBarItem: RouterTabBarItem,
3686
+ Drawer: RouterDrawer,
3687
+ DrawerGroup: RouterDrawerGroup,
3688
+ DrawerSection: RouterDrawerSection,
3689
+ DrawerItem: RouterDrawerItem,
3690
+ SplitView: RouterSplitView,
3691
+ Protected: RouterProtected
3692
+ });
3693
+
3694
+ export { Modal, ModalFrame, Router, Sheet, SheetFrame, sheetDragStartsInInteractiveControl, sheetScrollAtTop, useCurrentModal, useCurrentRoute, useDrawer, useHistoryState, useModalSwipeTarget, useParams, useRoute, useRouter, useSearchParams, useSheetDragEngine };
3695
+ //# sourceMappingURL=index.js.map
3696
+ //# sourceMappingURL=index.js.map