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