@buoy-gg/route-events 7.0.40 → 7.0.42

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 (104) hide show
  1. package/README.md +11 -60
  2. package/lib/commonjs/RouteObserver.js +1 -54
  3. package/lib/commonjs/RouteParser.js +1 -314
  4. package/lib/commonjs/RouteTracker.js +1 -112
  5. package/lib/commonjs/RouteTracker.web.js +1 -0
  6. package/lib/commonjs/browserRouteRegistry.js +1 -0
  7. package/lib/commonjs/browserRoutes.js +1 -0
  8. package/lib/commonjs/components/NavigationStack.js +2 -572
  9. package/lib/commonjs/components/ReactNavigationRoutes.js +1 -836
  10. package/lib/commonjs/components/ReactNavigationRoutes.web.js +1 -0
  11. package/lib/commonjs/components/RouteEventDetail.js +1 -97
  12. package/lib/commonjs/components/RouteEventExpandedContent.js +1 -204
  13. package/lib/commonjs/components/RouteEventItemCompact.js +1 -191
  14. package/lib/commonjs/components/RouteEventsModalWithTabs.js +1 -717
  15. package/lib/commonjs/components/RouteEventsTimeline.js +1 -107
  16. package/lib/commonjs/components/RouteFilterViewV2.js +1 -42
  17. package/lib/commonjs/components/RoutesSitemap.js +2 -1035
  18. package/lib/commonjs/expoRouterStore.js +1 -121
  19. package/lib/commonjs/expoRouterStore.web.js +1 -0
  20. package/lib/commonjs/hooks/useRouteEvents.js +1 -57
  21. package/lib/commonjs/index.js +1 -161
  22. package/lib/commonjs/preset.js +1 -69
  23. package/lib/commonjs/stores/navigationStackStore.js +1 -45
  24. package/lib/commonjs/stores/routeEventStore.js +1 -136
  25. package/lib/commonjs/sync/routeEventsSyncAdapter.js +1 -275
  26. package/lib/commonjs/useNavigationStack.js +1 -408
  27. package/lib/commonjs/useNavigationStack.web.js +1 -0
  28. package/lib/commonjs/useRouteObserver.js +1 -73
  29. package/lib/commonjs/useRouteObserverReactNavigation.js +1 -109
  30. package/lib/commonjs/useRouteSitemap.js +1 -255
  31. package/lib/commonjs/utils/routeTemplate.js +1 -28
  32. package/lib/commonjs/utils/safeReactNavigation.js +1 -194
  33. package/lib/commonjs/utils/safeReactNavigation.web.js +1 -0
  34. package/lib/commonjs/utils/testIDs.js +1 -63
  35. package/lib/module/RouteObserver.js +1 -49
  36. package/lib/module/RouteParser.js +1 -309
  37. package/lib/module/RouteTracker.js +1 -109
  38. package/lib/module/RouteTracker.web.js +1 -0
  39. package/lib/module/browserRouteRegistry.js +1 -0
  40. package/lib/module/browserRoutes.js +1 -0
  41. package/lib/module/components/NavigationStack.js +2 -568
  42. package/lib/module/components/ReactNavigationRoutes.js +1 -832
  43. package/lib/module/components/ReactNavigationRoutes.web.js +1 -0
  44. package/lib/module/components/RouteEventDetail.js +1 -93
  45. package/lib/module/components/RouteEventExpandedContent.js +1 -200
  46. package/lib/module/components/RouteEventItemCompact.js +1 -187
  47. package/lib/module/components/RouteEventsModalWithTabs.js +1 -714
  48. package/lib/module/components/RouteEventsTimeline.js +1 -103
  49. package/lib/module/components/RouteFilterViewV2.js +1 -38
  50. package/lib/module/components/RoutesSitemap.js +2 -1033
  51. package/lib/module/expoRouterStore.js +1 -114
  52. package/lib/module/expoRouterStore.web.js +1 -0
  53. package/lib/module/hooks/useRouteEvents.js +1 -54
  54. package/lib/module/index.js +1 -69
  55. package/lib/module/preset.js +1 -66
  56. package/lib/module/stores/navigationStackStore.js +1 -41
  57. package/lib/module/stores/routeEventStore.js +1 -128
  58. package/lib/module/sync/routeEventsSyncAdapter.js +1 -273
  59. package/lib/module/useNavigationStack.js +1 -404
  60. package/lib/module/useNavigationStack.web.js +1 -0
  61. package/lib/module/useRouteObserver.js +1 -70
  62. package/lib/module/useRouteObserverReactNavigation.js +1 -106
  63. package/lib/module/useRouteSitemap.js +1 -250
  64. package/lib/module/utils/routeTemplate.js +1 -24
  65. package/lib/module/utils/safeReactNavigation.js +1 -185
  66. package/lib/module/utils/safeReactNavigation.web.js +1 -0
  67. package/lib/module/utils/testIDs.js +1 -55
  68. package/lib/typescript/RouteTracker.web.d.ts +2 -0
  69. package/lib/typescript/browserRouteRegistry.d.ts +4 -0
  70. package/lib/typescript/browserRoutes.d.ts +2 -0
  71. package/lib/typescript/components/ReactNavigationRoutes.web.d.ts +2 -0
  72. package/lib/typescript/expoRouterStore.web.d.ts +13 -0
  73. package/lib/typescript/index.d.ts +1 -0
  74. package/lib/typescript/useNavigationStack.web.d.ts +4 -0
  75. package/lib/typescript/utils/routeTemplate.d.ts +13 -0
  76. package/lib/typescript/utils/safeReactNavigation.web.d.ts +22 -0
  77. package/lib/web/index.mjs +6 -0
  78. package/package.json +33 -13
  79. package/lib/typescript/RouteObserver.d.ts.map +0 -1
  80. package/lib/typescript/RouteParser.d.ts.map +0 -1
  81. package/lib/typescript/RouteTracker.d.ts.map +0 -1
  82. package/lib/typescript/components/NavigationStack.d.ts.map +0 -1
  83. package/lib/typescript/components/ReactNavigationRoutes.d.ts.map +0 -1
  84. package/lib/typescript/components/RouteEventDetail.d.ts.map +0 -1
  85. package/lib/typescript/components/RouteEventExpandedContent.d.ts.map +0 -1
  86. package/lib/typescript/components/RouteEventItemCompact.d.ts.map +0 -1
  87. package/lib/typescript/components/RouteEventsModalWithTabs.d.ts.map +0 -1
  88. package/lib/typescript/components/RouteEventsTimeline.d.ts.map +0 -1
  89. package/lib/typescript/components/RouteFilterViewV2.d.ts.map +0 -1
  90. package/lib/typescript/components/RoutesSitemap.d.ts.map +0 -1
  91. package/lib/typescript/expoRouterStore.d.ts.map +0 -1
  92. package/lib/typescript/hooks/useRouteEvents.d.ts.map +0 -1
  93. package/lib/typescript/index.d.ts.map +0 -1
  94. package/lib/typescript/preset.d.ts.map +0 -1
  95. package/lib/typescript/stores/navigationStackStore.d.ts.map +0 -1
  96. package/lib/typescript/stores/routeEventStore.d.ts.map +0 -1
  97. package/lib/typescript/sync/routeEventsSyncAdapter.d.ts.map +0 -1
  98. package/lib/typescript/useNavigationStack.d.ts.map +0 -1
  99. package/lib/typescript/useRouteObserver.d.ts.map +0 -1
  100. package/lib/typescript/useRouteObserverReactNavigation.d.ts.map +0 -1
  101. package/lib/typescript/useRouteSitemap.d.ts.map +0 -1
  102. package/lib/typescript/utils/routeTemplate.d.ts.map +0 -1
  103. package/lib/typescript/utils/safeReactNavigation.d.ts.map +0 -1
  104. package/lib/typescript/utils/testIDs.d.ts.map +0 -1
@@ -1,573 +1,3 @@
1
- "use strict";
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.NavigationStack=NavigationStack;var _react=require("react");var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _license=require("@buoy-gg/license");var _dataViewer=require("@buoy-gg/shared-ui/dataViewer");var _useNavigationStack=require("../useNavigationStack");var _testIDs=require("../utils/testIDs");var _jsxRuntime=require("react/jsx-runtime");function NavigationStack({style:h,injectedStack:f,onAction:r,onCopyValueChange:b}){const c=(0,_useNavigationStack.useNavigationStack)();const y=f!=null;const F=(0,_sharedUi.useSafeAreaInsets)({minBottom:8});const t=y?f:c.stack;const v=y?true:c.isLoaded;const T=y?null:c.error;const k=(0,_react.useMemo)(()=>t.find(e=>e.isFocused)??null,[t]);const l=t.length;const p=l<=1;const S=e=>r?r("navigateToIndex",{index:e}):c.navigateToIndex(e);const w=e=>r?r("popToIndex",{index:e}):c.popToIndex(e);const B=()=>r?r("goBack"):c.goBack();const D=()=>r?r("popToTop"):c.popToTop();const[a,C]=(0,_react.useState)(null);const[m,d]=(0,_react.useState)(false);const u=(0,_license.useIsPro)();(0,_react.useEffect)(()=>{if(m&&u){d(false)}},[m,u]);const I=(0,_react.useMemo)(()=>{const e=t.map(o=>({pathname:o.pathname,name:o.name,params:o.params,isFocused:o.isFocused}));return JSON.stringify(e,null,2)},[t]);(0,_react.useEffect)(()=>{b?.(I)},[I,b]);const i=(0,_react.useMemo)(()=>{if(a!==null&&t[a]){return t[a]}return k},[a,t,k]);const z=(0,_react.useMemo)(()=>{return a!==null&&t[a]&&!t[a].isFocused},[a,t]);if(!v){return(0,_jsxRuntime.jsx)(_reactNative.View,{style:[styles.container,h],children:(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.loadingContainer,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.loadingText,testID:"buoy-routes-stack-loading",children:"Loading navigation stack..."})})})}if(T){return(0,_jsxRuntime.jsx)(_reactNative.View,{style:[styles.container,h],children:(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.errorContainer,testID:"buoy-routes-stack-error",children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.errorText,children:"Error loading stack"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.errorDetail,testID:"buoy-routes-stack-error-detail",children:T.message})]})})}if(t.length===0){return(0,_jsxRuntime.jsx)(_reactNative.View,{style:[styles.container,h],children:(0,_jsxRuntime.jsx)(_sharedUi.ToolEmptyState,{testID:"buoy-routes-stack-empty",otto:"walking",title:"No navigation stack",message:"Navigate to a route to see the stack"})})}const j=(e,o,n,x)=>{if(r){const g=globalThis.window?.confirm;if(!g||g(`${e}
2
2
 
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.NavigationStack = NavigationStack;
7
- var _react = require("react");
8
- var _reactNative = require("react-native");
9
- var _sharedUi = require("@buoy-gg/shared-ui");
10
- var _license = require("@buoy-gg/license");
11
- var _dataViewer = require("@buoy-gg/shared-ui/dataViewer");
12
- var _useNavigationStack = require("../useNavigationStack");
13
- var _testIDs = require("../utils/testIDs");
14
- var _jsxRuntime = require("react/jsx-runtime");
15
- /**
16
- * NavigationStack - Visual representation of current navigation stack
17
- *
18
- * Shows all screens currently mounted in memory, which one is visible,
19
- * and provides controls to manipulate the stack.
20
- */
21
-
22
- // ============================================================================
23
- // Types
24
- // ============================================================================
25
-
26
- /** Stack manipulation a host can be asked to perform on the device. */
27
-
28
- // ============================================================================
29
- // Main Component
30
- // ============================================================================
31
-
32
- function NavigationStack({
33
- style,
34
- injectedStack,
35
- onAction,
36
- onCopyValueChange
37
- }) {
38
- const hookResult = (0, _useNavigationStack.useNavigationStack)();
39
- const isInjected = injectedStack != null;
40
- const insets = (0, _sharedUi.useSafeAreaInsets)({
41
- minBottom: 8
42
- });
43
-
44
- // Data source: injected (dashboard) or the local navigation container.
45
- const stack = isInjected ? injectedStack : hookResult.stack;
46
- const isLoaded = isInjected ? true : hookResult.isLoaded;
47
- const error = isInjected ? null : hookResult.error;
48
- const focusedRoute = (0, _react.useMemo)(() => stack.find(item => item.isFocused) ?? null, [stack]);
49
- const stackDepth = stack.length;
50
- const isAtRoot = stackDepth <= 1;
51
-
52
- // Action wrappers: delegate to the host when provided, else act locally.
53
- const navigateToIndex = index => onAction ? onAction("navigateToIndex", {
54
- index
55
- }) : hookResult.navigateToIndex(index);
56
- const popToIndex = index => onAction ? onAction("popToIndex", {
57
- index
58
- }) : hookResult.popToIndex(index);
59
- const goBack = () => onAction ? onAction("goBack") : hookResult.goBack();
60
- const popToTop = () => onAction ? onAction("popToTop") : hookResult.popToTop();
61
- const [expandedIndex, setExpandedIndex] = (0, _react.useState)(null);
62
- const [showUpgradeModal, setShowUpgradeModal] = (0, _react.useState)(false);
63
-
64
- // Check Pro status internally
65
- const isPro = (0, _license.useIsPro)();
66
-
67
- // Auto-close upgrade modal when user becomes Pro
68
- (0, _react.useEffect)(() => {
69
- if (showUpgradeModal && isPro) {
70
- setShowUpgradeModal(false);
71
- }
72
- }, [showUpgradeModal, isPro]);
73
-
74
- // ✅ All hooks must be called BEFORE any conditional returns
75
- // Prepare stack data for copying
76
- const stackDataForCopy = (0, _react.useMemo)(() => {
77
- const stackData = stack.map(item => ({
78
- pathname: item.pathname,
79
- name: item.name,
80
- params: item.params,
81
- isFocused: item.isFocused
82
- }));
83
- return JSON.stringify(stackData, null, 2);
84
- }, [stack]);
85
-
86
- // Report the copy payload up so the host can render the copy button in the
87
- // shared navbar.
88
- (0, _react.useEffect)(() => {
89
- onCopyValueChange?.(stackDataForCopy);
90
- }, [stackDataForCopy, onCopyValueChange]);
91
-
92
- // Determine which route actions should operate on
93
- // If a stack item is expanded, actions target that route
94
- // Otherwise, actions target the focused (visible) route
95
- const selectedRoute = (0, _react.useMemo)(() => {
96
- if (expandedIndex !== null && stack[expandedIndex]) {
97
- return stack[expandedIndex];
98
- }
99
- return focusedRoute;
100
- }, [expandedIndex, stack, focusedRoute]);
101
- const isNonFocusedSelected = (0, _react.useMemo)(() => {
102
- return expandedIndex !== null && stack[expandedIndex] && !stack[expandedIndex].isFocused;
103
- }, [expandedIndex, stack]);
104
-
105
- // Loading state
106
- if (!isLoaded) {
107
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
108
- style: [styles.container, style],
109
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
110
- style: styles.loadingContainer,
111
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
112
- style: styles.loadingText,
113
- testID: "buoy-routes-stack-loading",
114
- children: "Loading navigation stack..."
115
- })
116
- })
117
- });
118
- }
119
-
120
- // Error state
121
- if (error) {
122
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
123
- style: [styles.container, style],
124
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
125
- style: styles.errorContainer,
126
- testID: "buoy-routes-stack-error",
127
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
128
- style: styles.errorText,
129
- children: "Error loading stack"
130
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
131
- style: styles.errorDetail,
132
- testID: "buoy-routes-stack-error-detail",
133
- children: error.message
134
- })]
135
- })
136
- });
137
- }
138
-
139
- // Empty state
140
- if (stack.length === 0) {
141
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
142
- style: [styles.container, style],
143
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolEmptyState, {
144
- testID: "buoy-routes-stack-empty",
145
- otto: "walking",
146
- title: "No navigation stack",
147
- message: "Navigate to a route to see the stack"
148
- })
149
- });
150
- }
151
-
152
- // Handlers
153
- // Confirm a destructive action. On device we use the native Alert; when an
154
- // action delegate is set (dashboard, react-native-web) Alert button presses
155
- // don't fire, so fall back to window.confirm.
156
- const confirmDestructive = (title, message, confirmLabel, onConfirm) => {
157
- if (onAction) {
158
- const confirmFn = globalThis.window?.confirm;
159
- if (!confirmFn || confirmFn(`${title}\n\n${message}`)) {
160
- onConfirm();
161
- }
162
- return;
163
- }
164
- _reactNative.Alert.alert(title, message, [{
165
- text: "Cancel",
166
- style: "cancel"
167
- }, {
168
- text: confirmLabel,
169
- style: "destructive",
170
- onPress: onConfirm
171
- }]);
172
- };
173
- const handleGoBack = () => {
174
- // Gate behind Pro
175
- if (!isPro) {
176
- setShowUpgradeModal(true);
177
- return;
178
- }
179
- if (isAtRoot) {
180
- _reactNative.Alert.alert("Cannot Go Back", "Already at the root of the stack");
181
- return;
182
- }
183
- goBack();
184
- };
185
- const handlePopToTop = () => {
186
- // Gate behind Pro
187
- if (!isPro) {
188
- setShowUpgradeModal(true);
189
- return;
190
- }
191
- if (isAtRoot) {
192
- _reactNative.Alert.alert("Already at Top", "Stack only has one screen");
193
- return;
194
- }
195
- confirmDestructive("Pop to Top", "This will remove all screens except the root screen.", "Pop to Top", popToTop);
196
- };
197
- const toggleExpand = index => {
198
- setExpandedIndex(expandedIndex === index ? null : index);
199
- };
200
- const handleGo = () => {
201
- // Gate behind Pro
202
- if (!isPro) {
203
- setShowUpgradeModal(true);
204
- return;
205
- }
206
- if (!selectedRoute) return;
207
-
208
- // If the selected route is already focused, do nothing
209
- if (selectedRoute.isFocused) {
210
- _reactNative.Alert.alert("Already There", "This route is already visible");
211
- return;
212
- }
213
-
214
- // Navigate to the selected route
215
- navigateToIndex(selectedRoute.index);
216
- };
217
- const handlePopTo = () => {
218
- // Gate behind Pro
219
- if (!isPro) {
220
- setShowUpgradeModal(true);
221
- return;
222
- }
223
- if (!selectedRoute) return;
224
-
225
- // If selected route is the focused one, we can't pop to it
226
- if (selectedRoute.isFocused) {
227
- _reactNative.Alert.alert("Cannot Pop", "Selected route is already visible");
228
- return;
229
- }
230
-
231
- // If selected route is at the top of stack, nothing to pop
232
- if (selectedRoute.index === stackDepth - 1) {
233
- _reactNative.Alert.alert("Cannot Pop", "No screens above selected route");
234
- return;
235
- }
236
- const screensToRemove = stackDepth - 1 - selectedRoute.index;
237
- const targetIndex = selectedRoute.index;
238
- confirmDestructive("Pop to Route", `Remove ${screensToRemove} screen${screensToRemove !== 1 ? "s" : ""} above ${selectedRoute.pathname}?`, "Pop",
239
- // POP, not navigate. `navigate` only returns to a mounted screen when
240
- // the name AND the params match, so on any dynamic route — the common
241
- // case for a stack you would want to unwind — it pushed a fresh copy
242
- // instead: the dialog promised to remove two screens and the stack came
243
- // back one deeper, with the route in it twice. `popToIndex` is the
244
- // action the hook has always exported for this, and the same one the
245
- // desktop host already dispatches over the socket.
246
- () => popToIndex(targetIndex));
247
- };
248
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
249
- style: [styles.container, style],
250
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
251
- testID: "buoy-routes-stack-scroll",
252
- style: styles.stackScroll,
253
- contentContainerStyle: [styles.stackContent, {
254
- paddingBottom: styles.stackContent.padding + insets.bottom
255
- }],
256
- children: [...stack].reverse().map((item, reverseIndex) => {
257
- const actualIndex = stack.length - 1 - reverseIndex;
258
- const isExpanded = expandedIndex === actualIndex;
259
- const hasParams = Object.keys(item.params).length > 0;
260
- const itemTestID = (0, _testIDs.stackItemTestID)(actualIndex);
261
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
262
- style: styles.stackItemWrapper,
263
- testID: itemTestID,
264
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
265
- style: [styles.stackItem, isExpanded && styles.stackItemSelected],
266
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
267
- style: styles.stackItemHeader,
268
- onPress: () => toggleExpand(actualIndex),
269
- activeOpacity: 0.7,
270
- testID: `${itemTestID}-toggle`,
271
- accessibilityRole: "button",
272
- accessibilityLabel: `${item.pathname}, ${item.isFocused ? "focused" : "mounted"}, position ${actualIndex} of ${stackDepth - 1}`,
273
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
274
- style: styles.expandIndicator,
275
- children: isExpanded ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronDown, {
276
- size: 14,
277
- color: _sharedUi.night.color.textSecondary
278
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronRight, {
279
- size: 14,
280
- color: _sharedUi.night.color.textSecondary
281
- })
282
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
283
- style: styles.stackItemPath,
284
- numberOfLines: 1,
285
- testID: `${itemTestID}-path`,
286
- children: item.pathname
287
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.InlineCopyButton, {
288
- value: item.pathname,
289
- buttonStyle: styles.copyRouteButton,
290
- testID: `${itemTestID}-copy`
291
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
292
- style: [styles.statusBadge, {
293
- backgroundColor: item.isFocused ? `${_sharedUi.buoyColors.success}15` : "#3B82F615",
294
- borderColor: item.isFocused ? `${_sharedUi.buoyColors.success}40` : "#3B82F640"
295
- }],
296
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
297
- style: [styles.statusBadgeText, {
298
- color: item.isFocused ? _sharedUi.buoyColors.success : "#3B82F6"
299
- }],
300
- testID: `${itemTestID}-status`,
301
- children: item.isFocused ? "FOCUSED" : "MOUNTED"
302
- })
303
- })]
304
- }), isExpanded && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
305
- style: styles.expandedContent,
306
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
307
- style: styles.detailRow,
308
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
309
- style: styles.detailLabel,
310
- children: "Route Name:"
311
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
312
- style: styles.detailValue,
313
- testID: `${itemTestID}-name`,
314
- children: item.name
315
- })]
316
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
317
- style: styles.detailRow,
318
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
319
- style: styles.detailLabel,
320
- children: "Position:"
321
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
322
- style: styles.detailValue,
323
- testID: `${itemTestID}-position`,
324
- children: [actualIndex, " / ", stackDepth - 1]
325
- })]
326
- }), hasParams && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
327
- style: styles.dataViewerContainer,
328
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.DataViewer, {
329
- title: "Parameters",
330
- data: item.params,
331
- showTypeFilter: false
332
- })
333
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
334
- style: styles.actionsRow,
335
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
336
- style: styles.actionWrapper,
337
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
338
- style: [styles.actionButton, isAtRoot && styles.actionButtonDisabled],
339
- onPress: handleGoBack,
340
- disabled: isAtRoot,
341
- testID: `${itemTestID}-back`,
342
- accessibilityRole: "button",
343
- accessibilityLabel: "Go back one screen",
344
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
345
- style: [styles.actionButtonText, isAtRoot && styles.actionButtonTextDisabled],
346
- children: "Back"
347
- })
348
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
349
- style: styles.helpText,
350
- children: "Go back one screen"
351
- })]
352
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
353
- style: styles.actionWrapper,
354
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
355
- style: [styles.actionButton, item.isFocused && styles.actionButtonDisabled],
356
- onPress: handleGo,
357
- disabled: item.isFocused,
358
- testID: `${itemTestID}-go`,
359
- accessibilityRole: "button",
360
- accessibilityLabel: `Navigate to ${item.pathname}`,
361
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
362
- style: [styles.actionButtonText, item.isFocused && styles.actionButtonTextDisabled],
363
- children: "Go"
364
- })
365
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
366
- style: styles.helpText,
367
- children: "Navigate to this route"
368
- })]
369
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
370
- style: styles.actionWrapper,
371
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
372
- style: [styles.actionButton, (item.isFocused || actualIndex === stackDepth - 1) && styles.actionButtonDisabled],
373
- onPress: handlePopTo,
374
- disabled: item.isFocused || actualIndex === stackDepth - 1,
375
- testID: `${itemTestID}-pop-to`,
376
- accessibilityRole: "button",
377
- accessibilityLabel: `Remove screens above ${item.pathname}`,
378
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
379
- style: [styles.actionButtonText, (item.isFocused || actualIndex === stackDepth - 1) && styles.actionButtonTextDisabled],
380
- children: "Pop To"
381
- })
382
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
383
- style: styles.helpText,
384
- children: "Remove screens above this"
385
- })]
386
- })]
387
- })]
388
- })]
389
- })
390
- }, `stack-${actualIndex}-${item.key}`);
391
- })
392
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ProUpgradeModal, {
393
- visible: showUpgradeModal,
394
- onClose: () => setShowUpgradeModal(false),
395
- featureName: "Stack Navigation"
396
- })]
397
- });
398
- }
399
-
400
- // ============================================================================
401
- // Styles
402
- // ============================================================================
403
-
404
- const styles = _reactNative.StyleSheet.create({
405
- container: {
406
- flex: 1,
407
- // Transparent so JsModal's ToolBackground shows through (the modal paints opaque base beneath it).
408
- backgroundColor: "transparent"
409
- },
410
- loadingContainer: {
411
- flex: 1,
412
- justifyContent: "center",
413
- alignItems: "center",
414
- padding: 32
415
- },
416
- loadingText: {
417
- color: _sharedUi.night.color.textSecondary,
418
- fontSize: 14,
419
- fontFamily: _sharedUi.monoFont
420
- },
421
- errorContainer: {
422
- flex: 1,
423
- justifyContent: "center",
424
- alignItems: "center",
425
- padding: 32
426
- },
427
- errorText: {
428
- color: _sharedUi.buoyColors.error,
429
- fontSize: 16,
430
- fontWeight: "600",
431
- fontFamily: _sharedUi.monoFont,
432
- marginBottom: 8
433
- },
434
- errorDetail: {
435
- color: _sharedUi.night.color.textSecondary,
436
- fontSize: 12,
437
- fontFamily: _sharedUi.monoFont,
438
- textAlign: "center"
439
- },
440
- emptyContainer: {
441
- flex: 1,
442
- justifyContent: "center",
443
- alignItems: "center",
444
- padding: 32
445
- },
446
- emptyText: {
447
- color: _sharedUi.night.color.text,
448
- fontSize: 16,
449
- fontWeight: "600",
450
- fontFamily: _sharedUi.monoFont,
451
- marginBottom: 8
452
- },
453
- emptySubtext: {
454
- color: _sharedUi.night.color.textSecondary,
455
- fontSize: 14,
456
- fontFamily: _sharedUi.monoFont,
457
- textAlign: "center"
458
- },
459
- stackScroll: {
460
- flex: 1
461
- },
462
- stackContent: {
463
- padding: 8
464
- },
465
- stackItemWrapper: {
466
- marginBottom: 6
467
- },
468
- stackItem: {
469
- backgroundColor: _sharedUi.night.color.surface,
470
- borderRadius: 6,
471
- borderWidth: 1,
472
- borderColor: _sharedUi.night.color.border,
473
- overflow: "hidden"
474
- },
475
- stackItemSelected: {
476
- borderColor: _sharedUi.night.color.accent,
477
- borderWidth: 2
478
- },
479
- stackItemHeader: {
480
- flexDirection: "row",
481
- alignItems: "center",
482
- paddingVertical: 10,
483
- paddingHorizontal: 12,
484
- gap: 8
485
- },
486
- expandIndicator: {
487
- width: 20,
488
- alignItems: "center"
489
- },
490
- stackItemPath: {
491
- fontSize: 13,
492
- fontWeight: "600",
493
- color: _sharedUi.night.color.text,
494
- fontFamily: _sharedUi.monoFont,
495
- flex: 1
496
- },
497
- copyRouteButton: {
498
- padding: 4,
499
- marginLeft: 4
500
- },
501
- statusBadge: {
502
- borderRadius: 4,
503
- paddingHorizontal: 6,
504
- paddingVertical: 2,
505
- borderWidth: 1
506
- },
507
- statusBadgeText: {
508
- fontSize: 10,
509
- fontWeight: "600",
510
- fontFamily: _sharedUi.monoFont
511
- },
512
- expandedContent: {
513
- paddingHorizontal: 12,
514
- paddingTop: 8,
515
- paddingBottom: 12,
516
- borderTopWidth: 1,
517
- borderTopColor: _sharedUi.night.color.border
518
- },
519
- detailRow: {
520
- marginBottom: 8
521
- },
522
- detailLabel: {
523
- fontSize: 10,
524
- color: _sharedUi.night.color.textSecondary,
525
- fontFamily: _sharedUi.monoFont,
526
- marginBottom: 2
527
- },
528
- detailValue: {
529
- fontSize: 12,
530
- color: _sharedUi.night.color.text,
531
- fontFamily: _sharedUi.monoFont
532
- },
533
- dataViewerContainer: {
534
- marginTop: 4,
535
- marginHorizontal: -12,
536
- marginBottom: 8
537
- },
538
- actionsRow: {
539
- flexDirection: "row",
540
- gap: 6,
541
- marginTop: 12
542
- },
543
- actionWrapper: {
544
- flex: 1
545
- },
546
- actionButton: {
547
- backgroundColor: _sharedUi.night.color.surfaceElevated,
548
- paddingVertical: 8,
549
- paddingHorizontal: 8,
550
- borderRadius: 6,
551
- alignItems: "center"
552
- },
553
- actionButtonDisabled: {
554
- opacity: 0.5
555
- },
556
- actionButtonText: {
557
- fontSize: 12,
558
- fontWeight: "600",
559
- color: _sharedUi.night.color.text,
560
- fontFamily: _sharedUi.monoFont
561
- },
562
- actionButtonTextDisabled: {
563
- color: _sharedUi.night.color.textTertiary
564
- },
565
- helpText: {
566
- fontSize: 9,
567
- color: _sharedUi.night.color.textSecondary,
568
- fontFamily: _sharedUi.monoFont,
569
- textAlign: "center",
570
- marginTop: 4,
571
- lineHeight: 12
572
- }
573
- });
3
+ ${o}`)){x()}return}_reactNative.Alert.alert(e,o,[{text:"Cancel",style:"cancel"},{text:n,style:"destructive",onPress:x}])};const V=()=>{if(!u){d(true);return}if(p){_reactNative.Alert.alert("Cannot Go Back","Already at the root of the stack");return}B()};const N=()=>{if(!u){d(true);return}if(p){_reactNative.Alert.alert("Already at Top","Stack only has one screen");return}j("Pop to Top","This will remove all screens except the root screen.","Pop to Top",D)};const R=e=>{C(a===e?null:e)};const $=()=>{if(!u){d(true);return}if(!i)return;if(i.isFocused){_reactNative.Alert.alert("Already There","This route is already visible");return}S(i.index)};const P=()=>{if(!u){d(true);return}if(!i)return;if(i.isFocused){_reactNative.Alert.alert("Cannot Pop","Selected route is already visible");return}if(i.index===l-1){_reactNative.Alert.alert("Cannot Pop","No screens above selected route");return}const e=l-1-i.index;const o=i.index;j("Pop to Route",`Remove ${e} screen${e!==1?"s":""} above ${i.pathname}?`,"Pop",()=>w(o))};return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:[styles.container,h],children:[(0,_jsxRuntime.jsx)(_reactNative.ScrollView,{testID:"buoy-routes-stack-scroll",style:styles.stackScroll,contentContainerStyle:[styles.stackContent,{paddingBottom:styles.stackContent.padding+F.bottom}],children:[...t].reverse().map((e,o)=>{const n=t.length-1-o;const x=a===n;const g=Object.keys(e.params).length>0;const s=(0,_testIDs.stackItemTestID)(n);return(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.stackItemWrapper,testID:s,children:(0,_jsxRuntime.jsxs)(_reactNative.View,{style:[styles.stackItem,x&&styles.stackItemSelected],children:[(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{style:styles.stackItemHeader,onPress:()=>R(n),activeOpacity:.7,testID:`${s}-toggle`,accessibilityRole:"button",accessibilityLabel:`${e.pathname}, ${e.isFocused?"focused":"mounted"}, position ${n} of ${l-1}`,children:[(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.expandIndicator,children:x?(0,_jsxRuntime.jsx)(_sharedUi.ChevronDown,{size:14,color:_sharedUi.night.color.textSecondary}):(0,_jsxRuntime.jsx)(_sharedUi.ChevronRight,{size:14,color:_sharedUi.night.color.textSecondary})}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.stackItemPath,numberOfLines:1,testID:`${s}-path`,children:e.pathname}),(0,_jsxRuntime.jsx)(_sharedUi.InlineCopyButton,{value:e.pathname,buttonStyle:styles.copyRouteButton,testID:`${s}-copy`}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:[styles.statusBadge,{backgroundColor:e.isFocused?`${_sharedUi.buoyColors.success}15`:"#3B82F615",borderColor:e.isFocused?`${_sharedUi.buoyColors.success}40`:"#3B82F640"}],children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.statusBadgeText,{color:e.isFocused?_sharedUi.buoyColors.success:"#3B82F6"}],testID:`${s}-status`,children:e.isFocused?"FOCUSED":"MOUNTED"})})]}),x&&(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.expandedContent,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.detailRow,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.detailLabel,children:"Route Name:"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.detailValue,testID:`${s}-name`,children:e.name})]}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.detailRow,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.detailLabel,children:"Position:"}),(0,_jsxRuntime.jsxs)(_reactNative.Text,{style:styles.detailValue,testID:`${s}-position`,children:[n," / ",l-1]})]}),g&&(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.dataViewerContainer,children:(0,_jsxRuntime.jsx)(_dataViewer.DataViewer,{title:"Parameters",data:e.params,showTypeFilter:false})}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.actionsRow,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.actionWrapper,children:[(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{style:[styles.actionButton,p&&styles.actionButtonDisabled],onPress:V,disabled:p,testID:`${s}-back`,accessibilityRole:"button",accessibilityLabel:"Go back one screen",children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.actionButtonText,p&&styles.actionButtonTextDisabled],children:"Back"})}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.helpText,children:"Go back one screen"})]}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.actionWrapper,children:[(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{style:[styles.actionButton,e.isFocused&&styles.actionButtonDisabled],onPress:$,disabled:e.isFocused,testID:`${s}-go`,accessibilityRole:"button",accessibilityLabel:`Navigate to ${e.pathname}`,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.actionButtonText,e.isFocused&&styles.actionButtonTextDisabled],children:"Go"})}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.helpText,children:"Navigate to this route"})]}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.actionWrapper,children:[(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{style:[styles.actionButton,(e.isFocused||n===l-1)&&styles.actionButtonDisabled],onPress:P,disabled:e.isFocused||n===l-1,testID:`${s}-pop-to`,accessibilityRole:"button",accessibilityLabel:`Remove screens above ${e.pathname}`,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.actionButtonText,(e.isFocused||n===l-1)&&styles.actionButtonTextDisabled],children:"Pop To"})}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.helpText,children:"Remove screens above this"})]})]})]})]})},`stack-${n}-${e.key}`)})}),(0,_jsxRuntime.jsx)(_sharedUi.ProUpgradeModal,{visible:m,onClose:()=>d(false),featureName:"Stack Navigation"})]})}const styles=_reactNative.StyleSheet.create({container:{flex:1,backgroundColor:"transparent"},loadingContainer:{flex:1,justifyContent:"center",alignItems:"center",padding:32},loadingText:{color:_sharedUi.night.color.textSecondary,fontSize:14,fontFamily:_sharedUi.monoFont},errorContainer:{flex:1,justifyContent:"center",alignItems:"center",padding:32},errorText:{color:_sharedUi.buoyColors.error,fontSize:16,fontWeight:"600",fontFamily:_sharedUi.monoFont,marginBottom:8},errorDetail:{color:_sharedUi.night.color.textSecondary,fontSize:12,fontFamily:_sharedUi.monoFont,textAlign:"center"},emptyContainer:{flex:1,justifyContent:"center",alignItems:"center",padding:32},emptyText:{color:_sharedUi.night.color.text,fontSize:16,fontWeight:"600",fontFamily:_sharedUi.monoFont,marginBottom:8},emptySubtext:{color:_sharedUi.night.color.textSecondary,fontSize:14,fontFamily:_sharedUi.monoFont,textAlign:"center"},stackScroll:{flex:1},stackContent:{padding:8},stackItemWrapper:{marginBottom:6},stackItem:{backgroundColor:_sharedUi.night.color.surface,borderRadius:6,borderWidth:1,borderColor:_sharedUi.night.color.border,overflow:"hidden"},stackItemSelected:{borderColor:_sharedUi.night.color.accent,borderWidth:2},stackItemHeader:{flexDirection:"row",alignItems:"center",paddingVertical:10,paddingHorizontal:12,gap:8},expandIndicator:{width:20,alignItems:"center"},stackItemPath:{fontSize:13,fontWeight:"600",color:_sharedUi.night.color.text,fontFamily:_sharedUi.monoFont,flex:1},copyRouteButton:{padding:4,marginLeft:4},statusBadge:{borderRadius:4,paddingHorizontal:6,paddingVertical:2,borderWidth:1},statusBadgeText:{fontSize:10,fontWeight:"600",fontFamily:_sharedUi.monoFont},expandedContent:{paddingHorizontal:12,paddingTop:8,paddingBottom:12,borderTopWidth:1,borderTopColor:_sharedUi.night.color.border},detailRow:{marginBottom:8},detailLabel:{fontSize:10,color:_sharedUi.night.color.textSecondary,fontFamily:_sharedUi.monoFont,marginBottom:2},detailValue:{fontSize:12,color:_sharedUi.night.color.text,fontFamily:_sharedUi.monoFont},dataViewerContainer:{marginTop:4,marginHorizontal:-12,marginBottom:8},actionsRow:{flexDirection:"row",gap:6,marginTop:12},actionWrapper:{flex:1},actionButton:{backgroundColor:_sharedUi.night.color.surfaceElevated,paddingVertical:8,paddingHorizontal:8,borderRadius:6,alignItems:"center"},actionButtonDisabled:{opacity:.5},actionButtonText:{fontSize:12,fontWeight:"600",color:_sharedUi.night.color.text,fontFamily:_sharedUi.monoFont},actionButtonTextDisabled:{color:_sharedUi.night.color.textTertiary},helpText:{fontSize:9,color:_sharedUi.night.color.textSecondary,fontFamily:_sharedUi.monoFont,textAlign:"center",marginTop:4,lineHeight:12}});