@sigmela/router 0.3.3 → 0.3.5
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.
|
@@ -4,9 +4,10 @@ import { StackRenderer } from "../StackRenderer.js";
|
|
|
4
4
|
import { DrawerContext } from "./DrawerContext.js";
|
|
5
5
|
import { useRouter } from "../RouterContext.js";
|
|
6
6
|
import { ScreenStackItem } from 'react-native-screens';
|
|
7
|
-
import { Pressable, StyleSheet, View, Text } from 'react-native';
|
|
7
|
+
import { Platform, Pressable, StyleSheet, View, Text } from 'react-native';
|
|
8
8
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
9
9
|
import { useCallback, useSyncExternalStore, memo, useEffect, useState, useMemo, startTransition } from 'react';
|
|
10
|
+
const EMPTY_HISTORY = [];
|
|
10
11
|
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated';
|
|
11
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
13
|
const TIMING_CONFIG = {
|
|
@@ -19,7 +20,10 @@ const DrawerStackRenderer = /*#__PURE__*/memo(({
|
|
|
19
20
|
}) => {
|
|
20
21
|
const router = useRouter();
|
|
21
22
|
const stackId = stack.getId();
|
|
22
|
-
|
|
23
|
+
|
|
24
|
+
// On Android Fabric, start with empty history so the parent Screen's
|
|
25
|
+
// Fragment can attach before child ScreenStackItems mount.
|
|
26
|
+
const [history, setHistory] = useState(Platform.OS === 'android' ? EMPTY_HISTORY : () => router.getStackHistory(stackId));
|
|
23
27
|
useEffect(() => {
|
|
24
28
|
const update = () => {
|
|
25
29
|
startTransition(() => {
|
|
@@ -124,9 +128,11 @@ export const RenderDrawer = /*#__PURE__*/memo(({
|
|
|
124
128
|
useEffect(() => {
|
|
125
129
|
const key = tabs[index]?.tabKey;
|
|
126
130
|
if (key) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
131
|
+
startTransition(() => {
|
|
132
|
+
setVisited(prev => prev[key] ? prev : {
|
|
133
|
+
...prev,
|
|
134
|
+
[key]: true
|
|
135
|
+
});
|
|
130
136
|
});
|
|
131
137
|
}
|
|
132
138
|
}, [tabs, index]);
|
package/lib/module/Navigation.js
CHANGED
|
@@ -11,7 +11,9 @@ function useStackHistory(router, stackId) {
|
|
|
11
11
|
const [history, setHistory] = useState(() => stackId ? router.getStackHistory(stackId) : EMPTY_HISTORY);
|
|
12
12
|
useEffect(() => {
|
|
13
13
|
if (!stackId) {
|
|
14
|
-
|
|
14
|
+
startTransition(() => {
|
|
15
|
+
setHistory(EMPTY_HISTORY);
|
|
16
|
+
});
|
|
15
17
|
return;
|
|
16
18
|
}
|
|
17
19
|
const update = () => {
|
|
@@ -4,9 +4,10 @@ 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, useMemo, useState, useEffect, startTransition } from 'react';
|
|
8
|
-
import { StyleSheet } from 'react-native';
|
|
7
|
+
import { memo, useMemo, useRef, useState, useEffect, startTransition } from 'react';
|
|
8
|
+
import { Platform, StyleSheet } from 'react-native';
|
|
9
9
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
const EMPTY_HISTORY = [];
|
|
10
11
|
/**
|
|
11
12
|
* On native, SplitView renders primary and secondary screens in a SINGLE
|
|
12
13
|
* ScreenStack to get native push/pop animations.
|
|
@@ -38,12 +39,15 @@ export const RenderSplitView = /*#__PURE__*/memo(({
|
|
|
38
39
|
const primaryId = splitView.primary.getId();
|
|
39
40
|
const secondaryId = splitView.secondary.getId();
|
|
40
41
|
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
const
|
|
45
|
-
const
|
|
42
|
+
// On Android Fabric, start with empty history so the parent Screen's
|
|
43
|
+
// Fragment can attach before child ScreenStackItems mount. The useEffect
|
|
44
|
+
// below populates via startTransition on the next commit.
|
|
45
|
+
const isAndroid = Platform.OS === 'android';
|
|
46
|
+
const hydratedRef = useRef(!isAndroid);
|
|
47
|
+
const [primaryHistory, setPrimaryHistory] = useState(isAndroid ? EMPTY_HISTORY : () => router.getStackHistory(primaryId));
|
|
48
|
+
const [secondaryHistory, setSecondaryHistory] = useState(isAndroid ? EMPTY_HISTORY : () => router.getStackHistory(secondaryId));
|
|
46
49
|
useEffect(() => {
|
|
50
|
+
hydratedRef.current = true;
|
|
47
51
|
const updatePrimary = () => {
|
|
48
52
|
startTransition(() => {
|
|
49
53
|
setPrimaryHistory(router.getStackHistory(primaryId));
|
|
@@ -66,11 +70,14 @@ export const RenderSplitView = /*#__PURE__*/memo(({
|
|
|
66
70
|
};
|
|
67
71
|
}, [router, primaryId, secondaryId]);
|
|
68
72
|
|
|
69
|
-
// Fallback: if primary is empty, seed with first route
|
|
73
|
+
// Fallback: if primary is empty, seed with first route.
|
|
74
|
+
// On Android, skip the seed during the initial empty mount (before useEffect
|
|
75
|
+
// hydrates history) so no ScreenStackItems mount before the parent Fragment attaches.
|
|
70
76
|
const primaryHistoryToRender = useMemo(() => {
|
|
71
77
|
if (primaryHistory.length > 0) {
|
|
72
78
|
return primaryHistory;
|
|
73
79
|
}
|
|
80
|
+
if (!hydratedRef.current) return [];
|
|
74
81
|
const first = splitView.primary.getFirstRoute();
|
|
75
82
|
if (!first) return [];
|
|
76
83
|
const activePath = router.getActiveRoute()?.path;
|
|
@@ -7,6 +7,7 @@ import { Tabs } from 'react-native-screens';
|
|
|
7
7
|
import { Platform, StyleSheet, View } from 'react-native';
|
|
8
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
|
+
const EMPTY_HISTORY = [];
|
|
10
11
|
const isImageSource = value => {
|
|
11
12
|
if (value == null) return false;
|
|
12
13
|
const valueType = typeof value;
|
|
@@ -136,7 +137,10 @@ const TabStackRenderer = /*#__PURE__*/memo(({
|
|
|
136
137
|
}) => {
|
|
137
138
|
const router = useRouter();
|
|
138
139
|
const stackId = stack.getId();
|
|
139
|
-
|
|
140
|
+
|
|
141
|
+
// On Android Fabric, start with empty history so the parent Screen's
|
|
142
|
+
// Fragment can attach before child ScreenStackItems mount.
|
|
143
|
+
const [history, setHistory] = useState(Platform.OS === 'android' ? EMPTY_HISTORY : () => router.getStackHistory(stackId));
|
|
140
144
|
useEffect(() => {
|
|
141
145
|
const update = () => {
|
|
142
146
|
startTransition(() => {
|
|
@@ -176,6 +180,20 @@ export const RenderTabBar = /*#__PURE__*/memo(({
|
|
|
176
180
|
index,
|
|
177
181
|
config
|
|
178
182
|
} = snapshot;
|
|
183
|
+
|
|
184
|
+
// Local focus state driven by startTransition so that isFocused changes
|
|
185
|
+
// are deferred until the native Fragment lifecycle completes (Android Fabric).
|
|
186
|
+
const [focusedTabKey, setFocusedTabKey] = useState(() => tabs[index]?.tabKey);
|
|
187
|
+
|
|
188
|
+
// Sync focusedTabKey when index changes from external sources (programmatic navigation).
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
const key = tabs[index]?.tabKey;
|
|
191
|
+
if (key && key !== focusedTabKey) {
|
|
192
|
+
startTransition(() => {
|
|
193
|
+
setFocusedTabKey(key);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}, [tabs, index, focusedTabKey]);
|
|
179
197
|
const {
|
|
180
198
|
iconColor,
|
|
181
199
|
iconColorActive,
|
|
@@ -199,7 +217,10 @@ export const RenderTabBar = /*#__PURE__*/memo(({
|
|
|
199
217
|
const tabIndex = tabs.findIndex(route => route.tabKey === tabKey);
|
|
200
218
|
if (tabIndex === -1) return;
|
|
201
219
|
|
|
202
|
-
//
|
|
220
|
+
// Defer isFocused update so the native Fragment has time to attach.
|
|
221
|
+
startTransition(() => {
|
|
222
|
+
setFocusedTabKey(tabKey);
|
|
223
|
+
});
|
|
203
224
|
if (tabIndex !== index) {
|
|
204
225
|
tabBar.onIndexChange(tabIndex);
|
|
205
226
|
}
|
|
@@ -355,7 +376,7 @@ export const RenderTabBar = /*#__PURE__*/memo(({
|
|
|
355
376
|
experimentalControlNavigationStateInJS: config.experimentalControlNavigationStateInJS,
|
|
356
377
|
...containerProps,
|
|
357
378
|
children: tabs.map((tab, i) => {
|
|
358
|
-
const isFocused = tab.tabKey ===
|
|
379
|
+
const isFocused = tab.tabKey === focusedTabKey;
|
|
359
380
|
const stack = tabBar.stacks[tab.tabKey];
|
|
360
381
|
const node = tabBar.nodes[tab.tabKey];
|
|
361
382
|
const Screen = tabBar.screens[tab.tabKey];
|