@sigmela/router 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/Drawer/RenderDrawer.native.js +12 -4
- package/lib/module/Navigation.js +21 -6
- package/lib/module/SplitView/RenderSplitView.native.js +47 -16
- package/lib/module/StackRenderer.js +13 -9
- package/lib/module/TabBar/RenderTabBar.native.js +93 -128
- package/lib/typescript/src/SplitView/RenderSplitView.native.d.ts +18 -2
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ import { useRouter } from "../RouterContext.js";
|
|
|
6
6
|
import { ScreenStackItem } from 'react-native-screens';
|
|
7
7
|
import { Pressable, StyleSheet, View, Text } from 'react-native';
|
|
8
8
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
9
|
-
import { useCallback, useSyncExternalStore, memo, useEffect, useState, useMemo } from 'react';
|
|
9
|
+
import { useCallback, useSyncExternalStore, memo, useEffect, useState, useMemo, startTransition } from 'react';
|
|
10
10
|
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated';
|
|
11
11
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
12
|
const TIMING_CONFIG = {
|
|
@@ -19,9 +19,17 @@ const DrawerStackRenderer = /*#__PURE__*/memo(({
|
|
|
19
19
|
}) => {
|
|
20
20
|
const router = useRouter();
|
|
21
21
|
const stackId = stack.getId();
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const [history, setHistory] = useState(() => router.getStackHistory(stackId));
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const update = () => {
|
|
25
|
+
startTransition(() => {
|
|
26
|
+
setHistory(router.getStackHistory(stackId));
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
const unsub = router.subscribeStack(stackId, update);
|
|
30
|
+
update();
|
|
31
|
+
return unsub;
|
|
32
|
+
}, [router, stackId]);
|
|
25
33
|
return /*#__PURE__*/_jsx(StackRenderer, {
|
|
26
34
|
appearance: appearance,
|
|
27
35
|
stackId: stackId,
|
package/lib/module/Navigation.js
CHANGED
|
@@ -4,13 +4,26 @@ import { ScreenStackItem } from "./ScreenStackItem/index.js";
|
|
|
4
4
|
import { RouterContext } from "./RouterContext.js";
|
|
5
5
|
import { ScreenStack } from "./ScreenStack/index.js";
|
|
6
6
|
import { StyleSheet } from 'react-native';
|
|
7
|
-
import {
|
|
7
|
+
import { memo, useEffect, useRef, useState, startTransition } from 'react';
|
|
8
8
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
9
|
const EMPTY_HISTORY = [];
|
|
10
10
|
function useStackHistory(router, stackId) {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const [history, setHistory] = useState(() => stackId ? router.getStackHistory(stackId) : EMPTY_HISTORY);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!stackId) {
|
|
14
|
+
setHistory(EMPTY_HISTORY);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const update = () => {
|
|
18
|
+
startTransition(() => {
|
|
19
|
+
setHistory(router.getStackHistory(stackId));
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
const unsub = router.subscribeStack(stackId, update);
|
|
23
|
+
update();
|
|
24
|
+
return unsub;
|
|
25
|
+
}, [router, stackId]);
|
|
26
|
+
return history;
|
|
14
27
|
}
|
|
15
28
|
|
|
16
29
|
/**
|
|
@@ -30,8 +43,10 @@ export const Navigation = /*#__PURE__*/memo(({
|
|
|
30
43
|
}));
|
|
31
44
|
useEffect(() => {
|
|
32
45
|
return router.subscribeRoot(() => {
|
|
33
|
-
|
|
34
|
-
|
|
46
|
+
startTransition(() => {
|
|
47
|
+
setRoot({
|
|
48
|
+
rootId: router.getRootStackId()
|
|
49
|
+
});
|
|
35
50
|
});
|
|
36
51
|
});
|
|
37
52
|
}, [router]);
|
|
@@ -4,33 +4,67 @@ import { ScreenStackItem } from "../ScreenStackItem/index.js";
|
|
|
4
4
|
import { ScreenStack } from "../ScreenStack/index.js";
|
|
5
5
|
import { SplitViewContext } from "./SplitViewContext.js";
|
|
6
6
|
import { useRouter } from "../RouterContext.js";
|
|
7
|
-
import { memo,
|
|
7
|
+
import { memo, useMemo, useState, useEffect, startTransition } from 'react';
|
|
8
8
|
import { StyleSheet } from 'react-native';
|
|
9
9
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
10
|
/**
|
|
11
|
-
* On native
|
|
12
|
-
*
|
|
11
|
+
* On native, SplitView renders primary and secondary screens in a SINGLE
|
|
12
|
+
* ScreenStack to get native push/pop animations.
|
|
13
13
|
*
|
|
14
14
|
* The combined history is: [...primaryHistory, ...secondaryHistory]
|
|
15
15
|
* This way, navigating from primary to secondary is a native push.
|
|
16
|
+
*
|
|
17
|
+
* On Android (Fabric) react-native-screens manages each ScreenStackItem via
|
|
18
|
+
* an Android Fragment. Fragment lifecycle (onCreateView) is asynchronous,
|
|
19
|
+
* but Fabric's layout pass is synchronous. If a new commit is dispatched
|
|
20
|
+
* while the previous Fragment transaction is still in flight, Fabric tries
|
|
21
|
+
* to insert child views into a Screen whose Fragment isn't attached yet:
|
|
22
|
+
*
|
|
23
|
+
* "addViewAt: Parent Screen does not have its Fragment attached"
|
|
24
|
+
*
|
|
25
|
+
* The fix follows the same pattern used in react-native-screens' own
|
|
26
|
+
* BottomTabsContainer example: subscribe via useEffect and apply updates
|
|
27
|
+
* inside `startTransition`. This marks the update as non-urgent, letting
|
|
28
|
+
* React defer the Fabric commit until pending Fragment transactions complete.
|
|
29
|
+
*
|
|
30
|
+
* See: https://github.com/software-mansion/react-native-screens/blob/ddd1a9e/
|
|
31
|
+
* apps/src/shared/gamma/containers/bottom-tabs/BottomTabsContainer.tsx
|
|
16
32
|
*/
|
|
17
33
|
export const RenderSplitView = /*#__PURE__*/memo(({
|
|
18
34
|
splitView,
|
|
19
35
|
appearance
|
|
20
36
|
}) => {
|
|
21
37
|
const router = useRouter();
|
|
22
|
-
|
|
23
|
-
// Subscribe to primary stack
|
|
24
38
|
const primaryId = splitView.primary.getId();
|
|
25
|
-
const subscribePrimary = useCallback(cb => router.subscribeStack(primaryId, cb), [router, primaryId]);
|
|
26
|
-
const getPrimary = useCallback(() => router.getStackHistory(primaryId), [router, primaryId]);
|
|
27
|
-
const primaryHistory = useSyncExternalStore(subscribePrimary, getPrimary, getPrimary);
|
|
28
|
-
|
|
29
|
-
// Subscribe to secondary stack
|
|
30
39
|
const secondaryId = splitView.secondary.getId();
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
|
|
41
|
+
// Subscribe to both stacks via useEffect + startTransition.
|
|
42
|
+
// startTransition defers the Fabric commit so the FragmentManager has
|
|
43
|
+
// time to finish pending transactions between commits.
|
|
44
|
+
const [primaryHistory, setPrimaryHistory] = useState(() => router.getStackHistory(primaryId));
|
|
45
|
+
const [secondaryHistory, setSecondaryHistory] = useState(() => router.getStackHistory(secondaryId));
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const updatePrimary = () => {
|
|
48
|
+
startTransition(() => {
|
|
49
|
+
setPrimaryHistory(router.getStackHistory(primaryId));
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
const updateSecondary = () => {
|
|
53
|
+
startTransition(() => {
|
|
54
|
+
setSecondaryHistory(router.getStackHistory(secondaryId));
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
const unsub1 = router.subscribeStack(primaryId, updatePrimary);
|
|
58
|
+
const unsub2 = router.subscribeStack(secondaryId, updateSecondary);
|
|
59
|
+
|
|
60
|
+
// Sync in case the store changed between useState init and useEffect
|
|
61
|
+
updatePrimary();
|
|
62
|
+
updateSecondary();
|
|
63
|
+
return () => {
|
|
64
|
+
unsub1();
|
|
65
|
+
unsub2();
|
|
66
|
+
};
|
|
67
|
+
}, [router, primaryId, secondaryId]);
|
|
34
68
|
|
|
35
69
|
// Fallback: if primary is empty, seed with first route
|
|
36
70
|
const primaryHistoryToRender = useMemo(() => {
|
|
@@ -56,9 +90,6 @@ export const RenderSplitView = /*#__PURE__*/memo(({
|
|
|
56
90
|
const combinedHistory = useMemo(() => {
|
|
57
91
|
return [...primaryHistoryToRender, ...secondaryHistory];
|
|
58
92
|
}, [primaryHistoryToRender, secondaryHistory]);
|
|
59
|
-
|
|
60
|
-
// Use primary stack ID for the combined ScreenStack
|
|
61
|
-
// (secondary items will animate as if pushed onto this stack)
|
|
62
93
|
return /*#__PURE__*/_jsx(SplitViewContext.Provider, {
|
|
63
94
|
value: splitView,
|
|
64
95
|
children: /*#__PURE__*/_jsx(ScreenStack, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { memo,
|
|
3
|
+
import { memo, useEffect, useState, startTransition } from 'react';
|
|
4
4
|
import { ScreenStackItem } from "./ScreenStackItem/index.js";
|
|
5
5
|
import { ScreenStack } from "./ScreenStack/index.js";
|
|
6
6
|
import { useRouter } from "./RouterContext.js";
|
|
@@ -13,15 +13,19 @@ export const StackRenderer = /*#__PURE__*/memo(({
|
|
|
13
13
|
}) => {
|
|
14
14
|
const router = useRouter();
|
|
15
15
|
const hasHistoryProp = history != null;
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
const [internalHistory, setInternalHistory] = useState(() => hasHistoryProp ? history : router.getStackHistory(stackId));
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (hasHistoryProp) return;
|
|
19
|
+
const update = () => {
|
|
20
|
+
startTransition(() => {
|
|
21
|
+
setInternalHistory(router.getStackHistory(stackId));
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
const unsub = router.subscribeStack(stackId, update);
|
|
25
|
+
update();
|
|
26
|
+
return unsub;
|
|
19
27
|
}, [router, stackId, hasHistoryProp]);
|
|
20
|
-
const
|
|
21
|
-
if (hasHistoryProp) return history;
|
|
22
|
-
return router.getStackHistory(stackId);
|
|
23
|
-
}, [router, stackId, hasHistoryProp, history]);
|
|
24
|
-
const historyForThisStack = useSyncExternalStore(subscribe, get, get);
|
|
28
|
+
const historyForThisStack = hasHistoryProp ? history : internalHistory;
|
|
25
29
|
return /*#__PURE__*/_jsx(ScreenStack, {
|
|
26
30
|
style: [styles.flex, appearance?.screen],
|
|
27
31
|
children: historyForThisStack.map(item => /*#__PURE__*/_jsx(ScreenStackItem, {
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import { StackRenderer } from "../StackRenderer.js";
|
|
4
4
|
import { TabBarContext } from "./TabBarContext.js";
|
|
5
5
|
import { useRouter } from "../RouterContext.js";
|
|
6
|
-
import { Tabs
|
|
6
|
+
import { Tabs } from 'react-native-screens';
|
|
7
7
|
import { Platform, StyleSheet, View } from 'react-native';
|
|
8
|
-
import { useCallback, useSyncExternalStore, memo, useEffect, useState, useMemo } from 'react';
|
|
8
|
+
import { useCallback, useSyncExternalStore, memo, useEffect, useState, useMemo, startTransition } from 'react';
|
|
9
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
10
|
const isImageSource = value => {
|
|
11
11
|
if (value == null) return false;
|
|
@@ -136,9 +136,17 @@ const TabStackRenderer = /*#__PURE__*/memo(({
|
|
|
136
136
|
}) => {
|
|
137
137
|
const router = useRouter();
|
|
138
138
|
const stackId = stack.getId();
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
const [history, setHistory] = useState(() => router.getStackHistory(stackId));
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
const update = () => {
|
|
142
|
+
startTransition(() => {
|
|
143
|
+
setHistory(router.getStackHistory(stackId));
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
const unsub = router.subscribeStack(stackId, update);
|
|
147
|
+
update();
|
|
148
|
+
return () => unsub();
|
|
149
|
+
}, [router, stackId]);
|
|
142
150
|
return /*#__PURE__*/_jsx(StackRenderer, {
|
|
143
151
|
appearance: appearance,
|
|
144
152
|
stackId: stackId,
|
|
@@ -190,81 +198,54 @@ export const RenderTabBar = /*#__PURE__*/memo(({
|
|
|
190
198
|
const tabKey = event.nativeEvent.tabKey;
|
|
191
199
|
const tabIndex = tabs.findIndex(route => route.tabKey === tabKey);
|
|
192
200
|
if (tabIndex === -1) return;
|
|
193
|
-
const targetTab = tabs[tabIndex];
|
|
194
|
-
if (!targetTab) return;
|
|
195
|
-
const targetStack = tabBar.stacks[targetTab.tabKey];
|
|
196
|
-
const targetNode = tabBar.nodes[targetTab.tabKey];
|
|
197
201
|
|
|
198
|
-
//
|
|
202
|
+
// Keep native Tabs.Host focus state as the source of truth.
|
|
199
203
|
if (tabIndex !== index) {
|
|
200
204
|
tabBar.onIndexChange(tabIndex);
|
|
201
205
|
}
|
|
202
|
-
|
|
203
|
-
|
|
206
|
+
}, [tabs, tabBar, index]);
|
|
207
|
+
const seedTabIfNeeded = useCallback(targetTab => {
|
|
208
|
+
if (!targetTab) return;
|
|
209
|
+
const targetStack = tabBar.stacks[targetTab.tabKey];
|
|
210
|
+
const targetNode = tabBar.nodes[targetTab.tabKey];
|
|
204
211
|
if (targetStack) {
|
|
205
212
|
const stackId = targetStack.getId();
|
|
206
213
|
const stackHistory = router.getStackHistory(stackId);
|
|
207
|
-
// Only navigate if stack is empty (first visit)
|
|
208
214
|
if (stackHistory.length === 0) {
|
|
209
215
|
const firstRoute = targetStack.getFirstRoute();
|
|
210
216
|
if (firstRoute?.path) {
|
|
211
217
|
router.navigate(firstRoute.path);
|
|
212
218
|
}
|
|
213
219
|
}
|
|
214
|
-
|
|
215
|
-
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (targetNode) {
|
|
216
223
|
const nodeId = targetNode.getId?.();
|
|
217
|
-
if (nodeId)
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
224
|
+
if (!nodeId) return;
|
|
225
|
+
const nodeHistory = router.getStackHistory(nodeId);
|
|
226
|
+
if (nodeHistory.length === 0) {
|
|
227
|
+
const seed = targetNode.seed?.();
|
|
228
|
+
if (seed?.path) {
|
|
229
|
+
const prefix = targetTab.tabPrefix ?? '';
|
|
230
|
+
const fullPath = prefix && !seed.path.startsWith(prefix) ? `${prefix}${seed.path.startsWith('/') ? '' : '/'}${seed.path}` : seed.path;
|
|
231
|
+
router.navigate(fullPath);
|
|
226
232
|
}
|
|
227
233
|
}
|
|
228
234
|
}
|
|
229
|
-
}, [
|
|
235
|
+
}, [tabBar.nodes, tabBar.stacks, router]);
|
|
236
|
+
useEffect(() => {
|
|
237
|
+
seedTabIfNeeded(tabs[index]);
|
|
238
|
+
}, [tabs, index, seedTabIfNeeded]);
|
|
230
239
|
const onTabPress = useCallback(nextIndex => {
|
|
231
240
|
const targetTab = tabs[nextIndex];
|
|
232
241
|
if (!targetTab) return;
|
|
233
|
-
const targetStack = tabBar.stacks[targetTab.tabKey];
|
|
234
|
-
const targetNode = tabBar.nodes[targetTab.tabKey];
|
|
235
242
|
|
|
236
243
|
// Update TabBar UI state
|
|
237
244
|
if (nextIndex !== index) {
|
|
238
245
|
tabBar.onIndexChange(nextIndex);
|
|
239
246
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (targetStack) {
|
|
243
|
-
const stackId = targetStack.getId();
|
|
244
|
-
const stackHistory = router.getStackHistory(stackId);
|
|
245
|
-
// Only navigate if stack is empty (first visit)
|
|
246
|
-
if (stackHistory.length === 0) {
|
|
247
|
-
const firstRoute = targetStack.getFirstRoute();
|
|
248
|
-
if (firstRoute?.path) {
|
|
249
|
-
router.navigate(firstRoute.path);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
} else if (targetNode) {
|
|
253
|
-
// For nodes like SplitView, check if we need to seed it
|
|
254
|
-
const nodeId = targetNode.getId?.();
|
|
255
|
-
if (nodeId) {
|
|
256
|
-
const nodeHistory = router.getStackHistory(nodeId);
|
|
257
|
-
if (nodeHistory.length === 0) {
|
|
258
|
-
const seed = targetNode.seed?.();
|
|
259
|
-
if (seed?.path) {
|
|
260
|
-
const prefix = targetTab.tabPrefix ?? '';
|
|
261
|
-
const fullPath = prefix && !seed.path.startsWith(prefix) ? `${prefix}${seed.path.startsWith('/') ? '' : '/'}${seed.path}` : seed.path;
|
|
262
|
-
router.navigate(fullPath);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}, [tabs, tabBar, index, router]);
|
|
247
|
+
seedTabIfNeeded(targetTab);
|
|
248
|
+
}, [tabs, tabBar, index, seedTabIfNeeded]);
|
|
268
249
|
const containerProps = useMemo(() => ({
|
|
269
250
|
tabBarBackgroundColor: backgroundColor,
|
|
270
251
|
tabBarItemTitleFontFamily: title?.fontFamily,
|
|
@@ -339,83 +320,67 @@ export const RenderTabBar = /*#__PURE__*/memo(({
|
|
|
339
320
|
}
|
|
340
321
|
}, [tabs, index]);
|
|
341
322
|
if (CustomTabBar) {
|
|
342
|
-
return /*#__PURE__*/
|
|
343
|
-
screenId: "root-tabbar",
|
|
344
|
-
headerConfig: {
|
|
345
|
-
hidden: true
|
|
346
|
-
},
|
|
347
|
-
style: StyleSheet.absoluteFill,
|
|
348
|
-
stackAnimation: "slide_from_right",
|
|
349
|
-
children: /*#__PURE__*/_jsxs(TabBarContext.Provider, {
|
|
350
|
-
value: tabBar,
|
|
351
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
352
|
-
style: styles.flex,
|
|
353
|
-
children: tabs.filter(t => visited[t.tabKey]).map(tab => {
|
|
354
|
-
const isActive = tab.tabKey === tabs[index]?.tabKey;
|
|
355
|
-
const stackForTab = tabBar.stacks[tab.tabKey];
|
|
356
|
-
const nodeForTab = tabBar.nodes[tab.tabKey];
|
|
357
|
-
const ScreenForTab = tabBar.screens[tab.tabKey];
|
|
358
|
-
return /*#__PURE__*/_jsx(View, {
|
|
359
|
-
style: [styles.flex, !isActive && styles.hidden],
|
|
360
|
-
children: stackForTab ? /*#__PURE__*/_jsx(TabStackRenderer, {
|
|
361
|
-
appearance: appearance,
|
|
362
|
-
stack: stackForTab
|
|
363
|
-
}) : nodeForTab ? /*#__PURE__*/_jsx(TabNodeRenderer, {
|
|
364
|
-
appearance: appearance,
|
|
365
|
-
node: nodeForTab
|
|
366
|
-
}) : ScreenForTab ? /*#__PURE__*/_jsx(ScreenForTab, {}) : null
|
|
367
|
-
}, `tab-content-${tab.tabKey}`);
|
|
368
|
-
})
|
|
369
|
-
}), /*#__PURE__*/_jsx(CustomTabBar, {
|
|
370
|
-
onTabPress: onTabPress,
|
|
371
|
-
activeIndex: index,
|
|
372
|
-
tabs: tabs
|
|
373
|
-
})]
|
|
374
|
-
})
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
return /*#__PURE__*/_jsx(ScreenStackItem, {
|
|
378
|
-
screenId: "root-tabbar",
|
|
379
|
-
headerConfig: {
|
|
380
|
-
hidden: true
|
|
381
|
-
},
|
|
382
|
-
style: StyleSheet.absoluteFill,
|
|
383
|
-
stackAnimation: "slide_from_right",
|
|
384
|
-
children: /*#__PURE__*/_jsx(TabBarContext.Provider, {
|
|
323
|
+
return /*#__PURE__*/_jsxs(TabBarContext.Provider, {
|
|
385
324
|
value: tabBar,
|
|
386
|
-
children: /*#__PURE__*/_jsx(
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const convertedIcon = tabIcons[i];
|
|
397
|
-
const {
|
|
398
|
-
icon: _icon,
|
|
399
|
-
selectedIcon: _selectedIcon,
|
|
400
|
-
tabPrefix: _prefix,
|
|
401
|
-
...tabScreenProps
|
|
402
|
-
} = tab;
|
|
403
|
-
return /*#__PURE__*/_jsx(Tabs.Screen, {
|
|
404
|
-
...tabScreenProps,
|
|
405
|
-
scrollEdgeAppearance: iosAppearance,
|
|
406
|
-
standardAppearance: iosAppearance,
|
|
407
|
-
isFocused: isFocused,
|
|
408
|
-
icon: convertedIcon?.icon,
|
|
409
|
-
selectedIcon: convertedIcon?.selectedIcon,
|
|
410
|
-
children: stack ? /*#__PURE__*/_jsx(TabStackRenderer, {
|
|
325
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
326
|
+
style: styles.flex,
|
|
327
|
+
children: tabs.filter(t => visited[t.tabKey]).map(tab => {
|
|
328
|
+
const isActive = tab.tabKey === tabs[index]?.tabKey;
|
|
329
|
+
const stackForTab = tabBar.stacks[tab.tabKey];
|
|
330
|
+
const nodeForTab = tabBar.nodes[tab.tabKey];
|
|
331
|
+
const ScreenForTab = tabBar.screens[tab.tabKey];
|
|
332
|
+
return /*#__PURE__*/_jsx(View, {
|
|
333
|
+
style: [styles.flex, !isActive && styles.hidden],
|
|
334
|
+
children: stackForTab ? /*#__PURE__*/_jsx(TabStackRenderer, {
|
|
411
335
|
appearance: appearance,
|
|
412
|
-
stack:
|
|
413
|
-
}) :
|
|
336
|
+
stack: stackForTab
|
|
337
|
+
}) : nodeForTab ? /*#__PURE__*/_jsx(TabNodeRenderer, {
|
|
414
338
|
appearance: appearance,
|
|
415
|
-
node:
|
|
416
|
-
}) :
|
|
417
|
-
}, tab.tabKey);
|
|
339
|
+
node: nodeForTab
|
|
340
|
+
}) : ScreenForTab ? /*#__PURE__*/_jsx(ScreenForTab, {}) : null
|
|
341
|
+
}, `tab-content-${tab.tabKey}`);
|
|
418
342
|
})
|
|
343
|
+
}), /*#__PURE__*/_jsx(CustomTabBar, {
|
|
344
|
+
onTabPress: onTabPress,
|
|
345
|
+
activeIndex: index,
|
|
346
|
+
tabs: tabs
|
|
347
|
+
})]
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
return /*#__PURE__*/_jsx(TabBarContext.Provider, {
|
|
351
|
+
value: tabBar,
|
|
352
|
+
children: /*#__PURE__*/_jsx(Tabs.Host, {
|
|
353
|
+
onNativeFocusChange: onNativeFocusChange,
|
|
354
|
+
bottomAccessory: config.bottomAccessory,
|
|
355
|
+
experimentalControlNavigationStateInJS: config.experimentalControlNavigationStateInJS,
|
|
356
|
+
...containerProps,
|
|
357
|
+
children: tabs.map((tab, i) => {
|
|
358
|
+
const isFocused = tab.tabKey === tabs[index]?.tabKey;
|
|
359
|
+
const stack = tabBar.stacks[tab.tabKey];
|
|
360
|
+
const node = tabBar.nodes[tab.tabKey];
|
|
361
|
+
const Screen = tabBar.screens[tab.tabKey];
|
|
362
|
+
const convertedIcon = tabIcons[i];
|
|
363
|
+
const {
|
|
364
|
+
icon: _icon,
|
|
365
|
+
selectedIcon: _selectedIcon,
|
|
366
|
+
tabPrefix: _prefix,
|
|
367
|
+
...tabScreenProps
|
|
368
|
+
} = tab;
|
|
369
|
+
return /*#__PURE__*/_jsx(Tabs.Screen, {
|
|
370
|
+
...tabScreenProps,
|
|
371
|
+
scrollEdgeAppearance: iosAppearance,
|
|
372
|
+
standardAppearance: iosAppearance,
|
|
373
|
+
isFocused: isFocused,
|
|
374
|
+
icon: convertedIcon?.icon,
|
|
375
|
+
selectedIcon: convertedIcon?.selectedIcon,
|
|
376
|
+
children: stack ? /*#__PURE__*/_jsx(TabStackRenderer, {
|
|
377
|
+
appearance: appearance,
|
|
378
|
+
stack: stack
|
|
379
|
+
}) : node ? /*#__PURE__*/_jsx(TabNodeRenderer, {
|
|
380
|
+
appearance: appearance,
|
|
381
|
+
node: node
|
|
382
|
+
}) : Screen ? /*#__PURE__*/_jsx(Screen, {}) : null
|
|
383
|
+
}, tab.tabKey);
|
|
419
384
|
})
|
|
420
385
|
})
|
|
421
386
|
});
|
|
@@ -5,11 +5,27 @@ export interface RenderSplitViewProps {
|
|
|
5
5
|
appearance?: NavigationAppearance;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
* On native
|
|
9
|
-
*
|
|
8
|
+
* On native, SplitView renders primary and secondary screens in a SINGLE
|
|
9
|
+
* ScreenStack to get native push/pop animations.
|
|
10
10
|
*
|
|
11
11
|
* The combined history is: [...primaryHistory, ...secondaryHistory]
|
|
12
12
|
* This way, navigating from primary to secondary is a native push.
|
|
13
|
+
*
|
|
14
|
+
* On Android (Fabric) react-native-screens manages each ScreenStackItem via
|
|
15
|
+
* an Android Fragment. Fragment lifecycle (onCreateView) is asynchronous,
|
|
16
|
+
* but Fabric's layout pass is synchronous. If a new commit is dispatched
|
|
17
|
+
* while the previous Fragment transaction is still in flight, Fabric tries
|
|
18
|
+
* to insert child views into a Screen whose Fragment isn't attached yet:
|
|
19
|
+
*
|
|
20
|
+
* "addViewAt: Parent Screen does not have its Fragment attached"
|
|
21
|
+
*
|
|
22
|
+
* The fix follows the same pattern used in react-native-screens' own
|
|
23
|
+
* BottomTabsContainer example: subscribe via useEffect and apply updates
|
|
24
|
+
* inside `startTransition`. This marks the update as non-urgent, letting
|
|
25
|
+
* React defer the Fabric commit until pending Fragment transactions complete.
|
|
26
|
+
*
|
|
27
|
+
* See: https://github.com/software-mansion/react-native-screens/blob/ddd1a9e/
|
|
28
|
+
* apps/src/shared/gamma/containers/bottom-tabs/BottomTabsContainer.tsx
|
|
13
29
|
*/
|
|
14
30
|
export declare const RenderSplitView: import("react").NamedExoticComponent<RenderSplitViewProps>;
|
|
15
31
|
//# sourceMappingURL=RenderSplitView.native.d.ts.map
|