mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-13 22:42:25 +08:00
Compare commits
74 Commits
@react-nav
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50a161dc3d | ||
|
|
360b0e9958 | ||
|
|
e50c8aa942 | ||
|
|
8f0efc8db5 | ||
|
|
7de6677e72 | ||
|
|
1dad338b7a | ||
|
|
ce7d20e336 | ||
|
|
e3e58c2d89 | ||
|
|
cb2e744dce | ||
|
|
9beca3a802 | ||
|
|
ec7b02af2c | ||
|
|
4c2379cec1 | ||
|
|
1169ed0946 | ||
|
|
bf464a8378 | ||
|
|
a495506e20 | ||
|
|
b20f2d1f7c | ||
|
|
66f3a4a0bb | ||
|
|
84cc0d758a | ||
|
|
ebc7f9ea75 | ||
|
|
bd9f0ad5f6 | ||
|
|
c326c106f9 | ||
|
|
52451d1109 | ||
|
|
0945689b70 | ||
|
|
37b9454f3e | ||
|
|
fb7ac960c8 | ||
|
|
e8515f9cd9 | ||
|
|
5eee804e7f | ||
|
|
45dbe5c40e | ||
|
|
d26bcc057e | ||
|
|
836ca4482e | ||
|
|
fdd549a536 | ||
|
|
128bbbe62a | ||
|
|
a186b445b4 | ||
|
|
ac11a3bded | ||
|
|
55d635f53e | ||
|
|
95600500a4 | ||
|
|
6cf124a190 | ||
|
|
bfd0d94985 | ||
|
|
748e92f120 | ||
|
|
7f3b27a9ec | ||
|
|
f51086edea | ||
|
|
7196889bf1 | ||
|
|
7dc2f5832e | ||
|
|
8ec6c1a603 | ||
|
|
960f0a5281 | ||
|
|
da91cec941 | ||
|
|
38e17aae93 | ||
|
|
0f60b4617f | ||
|
|
f01bb4834b | ||
|
|
261a33a0d0 | ||
|
|
d6cac6713a | ||
|
|
8ee0dda155 | ||
|
|
8585f97226 | ||
|
|
23ab350492 | ||
|
|
80ff5a9c54 | ||
|
|
90ebfc40b3 | ||
|
|
091b2a2038 | ||
|
|
01f86d2ac6 | ||
|
|
c49dab31b2 | ||
|
|
16e7ac131f | ||
|
|
9e3650831c | ||
|
|
e523138321 | ||
|
|
cc8f1f4205 | ||
|
|
e18578f83f | ||
|
|
a6179b75ad | ||
|
|
9b03c8e3a4 | ||
|
|
2bb6603aa3 | ||
|
|
dcbfe52667 | ||
|
|
97c215d2f2 | ||
|
|
050447b9ac | ||
|
|
1c4bd6813b | ||
|
|
2d1da7ef2f | ||
|
|
70691acdfc | ||
|
|
ee71a05570 |
@@ -87,6 +87,9 @@ jobs:
|
||||
- run:
|
||||
name: Build packages in the monorepo
|
||||
command: yarn lerna run prepare
|
||||
- run:
|
||||
name: Verify built type definitions are correct
|
||||
command: yarn typescript
|
||||
- run:
|
||||
name: Verify paths for types
|
||||
command: node scripts/check-types-path.js
|
||||
|
||||
2
.github/workflows/expo-preview.yml
vendored
2
.github/workflows/expo-preview.yml
vendored
@@ -37,6 +37,8 @@ jobs:
|
||||
- name: Publish Expo app
|
||||
working-directory: ./example
|
||||
run: expo publish --release-channel=pr-${{ github.event.number }}
|
||||
env:
|
||||
EXPO_USE_DEV_SERVER: true
|
||||
|
||||
- name: Get expo link
|
||||
id: expo
|
||||
|
||||
1
.github/workflows/expo.yml
vendored
1
.github/workflows/expo.yml
vendored
@@ -3,6 +3,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
|
||||
10
.github/workflows/triage.yml
vendored
10
.github/workflows/triage.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "Hey! Thanks for opening the issue. Can you provide more information about the issue? Please fill the issue template when opening the issue without deleting any section. We need all the information we can to be able to help.\n\nMake sure to at least provide - Current behaviour, Expected behaviour, A way to [reproduce the issue with minimal code](https://stackoverflow.com/help/minimal-reproducible-example) (link to [snack.expo.io](https://snack.expo.io)) or a repo on GitHub, and the information about your environment (such as the platform of the device, exact versions of all the packages mentioned in the template etc.)."
|
||||
body: "Hey! Thanks for opening the issue. Can you provide more information about the issue? Please fill the issue template when opening the issue without deleting any section. We need all the information we can to be able to help.\n\nMake sure to at least provide - Current behaviour, Expected behaviour, A way to [reproduce the issue with minimal code](https://stackoverflow.com/help/minimal-reproducible-example) (link to [snack.expo.io](https://snack.expo.io)) or a repo on GitHub, and the information about your environment (such as the platform of the device, exact versions of all the packages mentioned in the template etc.). In addition, if you can provide a video or GIF demonstrating the issue, it will also be very helpful."
|
||||
})
|
||||
|
||||
needs-repro:
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports in React Navigation. Seems you have an issue related to `native-stack` navigator or `react-native-screens` library. Please post your issue in [this repo](https://github.com/software-mansion/react-native-screens) so that it's notified to the maintainers of that library."
|
||||
body: "Hey! Thanks for opening the issue. Seems that this issue issue related to `react-native-screens` library which is a dependency of React Navigation. Can you also post your issue in [this repo](https://github.com/software-mansion/react-native-screens) so that it's notified to the maintainers of that library? This will help us fix the issue faster since it's upto the maintainers of that library to investigate it."
|
||||
})
|
||||
|
||||
react-native-reanimated:
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports in React Navigation. Seems you have an issue related to `react-native-reanimated` library. Please post your issue in [this repo](https://github.com/software-mansion/react-native-reanimated) so that it's notified to the maintainers of that library."
|
||||
body: "Hey! Thanks for opening the issue. Seems that this issue issue related to `react-native-reanimated` library which is a dependency of React Navigation. Can you also post your issue in [this repo](https://github.com/software-mansion/react-native-reanimated) so that it's notified to the maintainers of that library? This will help us fix the issue faster since it's upto the maintainers of that library to investigate it."
|
||||
})
|
||||
|
||||
react-native-gesture-handler:
|
||||
@@ -106,7 +106,7 @@ jobs:
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports in React Navigation. Seems you have an issue related to `react-native-gesture-handler` library. Please post your issue in [this repo](https://github.com/software-mansion/react-native-gesture-handler) so that it's notified to the maintainers of that library."
|
||||
body: "Hey! Thanks for opening the issue. Seems that this issue issue related to `react-native-gesture-handler` library which is a dependency of React Navigation. Can you also post your issue in [this repo](https://github.com/software-mansion/react-native-gesture-handler) so that it's notified to the maintainers of that library? This will help us fix the issue faster since it's upto the maintainers of that library to investigate it."
|
||||
})
|
||||
|
||||
react-native-safe-area-context:
|
||||
@@ -121,5 +121,5 @@ jobs:
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports in React Navigation. Seems you have an issue related to `react-native-safe-area-context` library. Please post your issue in [this repo](https://github.com/th3rdwave/react-native-safe-area-context) so that it's notified to the maintainers of that library."
|
||||
body: "Hey! Thanks for opening the issue. Seems that this issue issue related to `react-native-safe-area-context` library which is a dependency of React Navigation. Can you also post your issue in [this repo](https://github.com/th3rdwave/react-native-safe-area-context) so that it's notified to the maintainers of that library? This will help us fix the issue faster since it's upto the maintainers of that library to investigate it."
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"expo": {
|
||||
"name": "React Navigation",
|
||||
"owner": "react-navigation",
|
||||
"slug": "NavigationPlayground",
|
||||
"slug": "react-navigation-example",
|
||||
"description": "Demonstrates the functionality and various capabilities of React Navigation.",
|
||||
"privacy": "public",
|
||||
"version": "1.0.0",
|
||||
|
||||
@@ -13,49 +13,51 @@
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^10.2.0",
|
||||
"@react-native-community/masked-view": "^0.1.10",
|
||||
"color": "^3.1.2",
|
||||
"expo": "^38.0.9",
|
||||
"expo-asset": "~8.1.7",
|
||||
"expo-blur": "~8.1.2",
|
||||
"expo-linking": "^1.0.3",
|
||||
"expo-updates": "~0.2.14",
|
||||
"@expo/vector-icons": "^10.0.0",
|
||||
"@react-native-async-storage/async-storage": "^1.13.1",
|
||||
"@react-native-community/masked-view": "0.1.10",
|
||||
"color": "^3.1.3",
|
||||
"expo": "^39.0.0",
|
||||
"expo-asset": "~8.2.0",
|
||||
"expo-blur": "~8.2.0",
|
||||
"expo-linking": "^1.0.4",
|
||||
"expo-updates": "~0.3.5",
|
||||
"koa": "^2.13.0",
|
||||
"react": "~16.9.0",
|
||||
"react-dom": "~16.9.0",
|
||||
"react-native": "~0.62.2",
|
||||
"react": "~16.13.1",
|
||||
"react-dom": "~16.13.1",
|
||||
"react-native": "~0.63.2",
|
||||
"react-native-appearance": "~0.3.3",
|
||||
"react-native-gesture-handler": "^1.7.0",
|
||||
"react-native-paper": "^4.0.1",
|
||||
"react-native-reanimated": "^1.10.1",
|
||||
"react-native-safe-area-context": "~3.1.1",
|
||||
"react-native-screens": "^2.9.0",
|
||||
"react-native-tab-view": "^2.15.1",
|
||||
"react-native-gesture-handler": "~1.7.0",
|
||||
"react-native-paper": "^4.2.0",
|
||||
"react-native-reanimated": "~1.13.0",
|
||||
"react-native-safe-area-context": "3.1.4",
|
||||
"react-native-screens": "~2.10.1",
|
||||
"react-native-tab-view": "^2.15.2",
|
||||
"react-native-vector-icons": "^7.0.0",
|
||||
"react-native-web": "^0.11.7"
|
||||
"react-native-web": "^0.13.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/node": "^7.10.1",
|
||||
"@expo/webpack-config": "^0.12.21",
|
||||
"@types/cheerio": "^0.22.21",
|
||||
"@babel/node": "^7.12.1",
|
||||
"@expo/webpack-config": "^0.12.40",
|
||||
"@types/cheerio": "^0.22.22",
|
||||
"@types/jest-dev-server": "^4.2.0",
|
||||
"@types/koa": "^2.11.3",
|
||||
"@types/koa": "^2.11.6",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/react": "~16.9.44",
|
||||
"@types/react": "~16.9.53",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/react-native": "~0.63.4",
|
||||
"@types/react-native": "~0.63.30",
|
||||
"babel-loader": "^8.1.0",
|
||||
"babel-plugin-module-resolver": "^4.0.0",
|
||||
"babel-preset-expo": "^8.2.3",
|
||||
"babel-preset-expo": "^8.3.0",
|
||||
"cheerio": "^1.0.0-rc.3",
|
||||
"expo-cli": "^3.23.1",
|
||||
"jest": "^26.2.2",
|
||||
"expo-cli": "^3.28.2",
|
||||
"jest": "^26.6.1",
|
||||
"jest-dev-server": "^4.4.0",
|
||||
"mock-require-assets": "^0.0.1",
|
||||
"node-fetch": "^2.6.0",
|
||||
"nodemon": "^2.0.4",
|
||||
"node-fetch": "^2.6.1",
|
||||
"nodemon": "^2.0.6",
|
||||
"playwright": "^0.14.0",
|
||||
"serve": "^11.3.0",
|
||||
"typescript": "~3.9.5"
|
||||
"typescript": "^4.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import { AsyncStorage } from 'react-native';
|
||||
|
||||
export default AsyncStorage;
|
||||
@@ -1,14 +0,0 @@
|
||||
export default {
|
||||
getItem(key: string) {
|
||||
return Promise.resolve(localStorage.getItem(key));
|
||||
},
|
||||
setItem(key: string, value: string) {
|
||||
return Promise.resolve(localStorage.setItem(key, value));
|
||||
},
|
||||
removeItem(key: string) {
|
||||
return Promise.resolve(localStorage.removeItem(key));
|
||||
},
|
||||
clear() {
|
||||
return Promise.resolve(localStorage.clear());
|
||||
},
|
||||
};
|
||||
@@ -31,9 +31,11 @@ const AuthContext = React.createContext<{
|
||||
});
|
||||
|
||||
const SplashScreen = () => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<View style={styles.content}>
|
||||
<ActivityIndicator />
|
||||
<ActivityIndicator color={colors.primary} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityI
|
||||
import {
|
||||
getFocusedRouteNameFromRoute,
|
||||
ParamListBase,
|
||||
NavigatorScreenParams,
|
||||
} from '@react-navigation/native';
|
||||
import type { StackScreenProps } from '@react-navigation/stack';
|
||||
import {
|
||||
@@ -15,7 +16,7 @@ import TouchableBounce from '../Shared/TouchableBounce';
|
||||
import Albums from '../Shared/Albums';
|
||||
import Contacts from '../Shared/Contacts';
|
||||
import Chat from '../Shared/Chat';
|
||||
import SimpleStackScreen from './SimpleStack';
|
||||
import SimpleStackScreen, { SimpleStackParams } from './SimpleStack';
|
||||
|
||||
const getTabBarIcon = (name: string) => ({
|
||||
color,
|
||||
@@ -26,7 +27,7 @@ const getTabBarIcon = (name: string) => ({
|
||||
}) => <MaterialCommunityIcons name={name} color={color} size={size} />;
|
||||
|
||||
type BottomTabParams = {
|
||||
Article: undefined;
|
||||
Article: NavigatorScreenParams<SimpleStackParams>;
|
||||
Albums: undefined;
|
||||
Contacts: undefined;
|
||||
Chat: undefined;
|
||||
@@ -85,12 +86,18 @@ export default function BottomTabsScreen({
|
||||
>
|
||||
<BottomTabs.Screen
|
||||
name="Article"
|
||||
component={SimpleStackScreen}
|
||||
options={{
|
||||
title: 'Article',
|
||||
tabBarIcon: getTabBarIcon('file-document-box'),
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{(props) => (
|
||||
<SimpleStackScreen
|
||||
{...props}
|
||||
screenOptions={{ headerShown: false }}
|
||||
/>
|
||||
)}
|
||||
</BottomTabs.Screen>
|
||||
<BottomTabs.Screen
|
||||
name="Chat"
|
||||
component={Chat}
|
||||
|
||||
@@ -25,18 +25,9 @@ const LinkButton = ({
|
||||
to,
|
||||
...rest
|
||||
}: React.ComponentProps<typeof Button> & { to: string }) => {
|
||||
const { onPress, ...props } = useLinkProps({ to });
|
||||
const props = useLinkProps({ to });
|
||||
|
||||
return (
|
||||
<Button
|
||||
{...props}
|
||||
{...rest}
|
||||
{...Platform.select({
|
||||
web: { onClick: onPress } as any,
|
||||
default: { onPress },
|
||||
})}
|
||||
/>
|
||||
);
|
||||
return <Button {...props} {...rest} />;
|
||||
};
|
||||
|
||||
const ArticleScreen = ({
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import type { NavigatorScreenParams } from '@react-navigation/native';
|
||||
import { createMaterialBottomTabNavigator } from '@react-navigation/material-bottom-tabs';
|
||||
import Albums from '../Shared/Albums';
|
||||
import Contacts from '../Shared/Contacts';
|
||||
import Chat from '../Shared/Chat';
|
||||
import SimpleStackScreen from './SimpleStack';
|
||||
import SimpleStackScreen, { SimpleStackParams } from './SimpleStack';
|
||||
|
||||
type MaterialBottomTabParams = {
|
||||
Article: undefined;
|
||||
Article: NavigatorScreenParams<SimpleStackParams>;
|
||||
Albums: undefined;
|
||||
Contacts: undefined;
|
||||
Chat: undefined;
|
||||
@@ -22,13 +23,19 @@ export default function MaterialBottomTabsScreen() {
|
||||
<MaterialBottomTabs.Navigator barStyle={styles.tabBar}>
|
||||
<MaterialBottomTabs.Screen
|
||||
name="Article"
|
||||
component={SimpleStackScreen}
|
||||
options={{
|
||||
tabBarLabel: 'Article',
|
||||
tabBarIcon: 'file-document-box',
|
||||
tabBarColor: '#C9E7F8',
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{(props) => (
|
||||
<SimpleStackScreen
|
||||
{...props}
|
||||
screenOptions={{ headerShown: false }}
|
||||
/>
|
||||
)}
|
||||
</MaterialBottomTabs.Screen>
|
||||
<MaterialBottomTabs.Screen
|
||||
name="Chat"
|
||||
component={Chat}
|
||||
|
||||
@@ -93,7 +93,9 @@ export default function SimpleStackScreen({ navigation, options }: Props) {
|
||||
cardOverlayEnabled: true,
|
||||
gestureEnabled: true,
|
||||
headerStatusBarHeight:
|
||||
navigation.dangerouslyGetState().routes.indexOf(route) > 0
|
||||
navigation
|
||||
.dangerouslyGetState()
|
||||
.routes.findIndex((r: any) => r.key === route.key) > 0
|
||||
? 0
|
||||
: undefined,
|
||||
})}
|
||||
|
||||
@@ -77,18 +77,28 @@ const InputScreen = ({
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
Alert.alert(
|
||||
'Discard changes?',
|
||||
'You have unsaved changes. Are you sure to discard them and leave the screen?',
|
||||
[
|
||||
{ text: "Don't leave", style: 'cancel', onPress: () => {} },
|
||||
{
|
||||
text: 'Discard',
|
||||
style: 'destructive',
|
||||
onPress: () => navigation.dispatch(action),
|
||||
},
|
||||
]
|
||||
);
|
||||
if (Platform.OS === 'web') {
|
||||
const discard = confirm(
|
||||
'You have unsaved changes. Discard them and leave the screen?'
|
||||
);
|
||||
|
||||
if (discard) {
|
||||
navigation.dispatch(action);
|
||||
}
|
||||
} else {
|
||||
Alert.alert(
|
||||
'Discard changes?',
|
||||
'You have unsaved changes. Discard them and leave the screen?',
|
||||
[
|
||||
{ text: "Don't leave", style: 'cancel', onPress: () => {} },
|
||||
{
|
||||
text: 'Discard',
|
||||
style: 'destructive',
|
||||
onPress: () => navigation.dispatch(action),
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}),
|
||||
[hasUnsavedChanges, navigation]
|
||||
);
|
||||
|
||||
@@ -4,13 +4,14 @@ import { Button } from 'react-native-paper';
|
||||
import type { ParamListBase } from '@react-navigation/native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
StackNavigationOptions,
|
||||
StackScreenProps,
|
||||
} from '@react-navigation/stack';
|
||||
import Article from '../Shared/Article';
|
||||
import Albums from '../Shared/Albums';
|
||||
import NewsFeed from '../Shared/NewsFeed';
|
||||
|
||||
type SimpleStackParams = {
|
||||
export type SimpleStackParams = {
|
||||
Article: { author: string } | undefined;
|
||||
NewsFeed: { date: number };
|
||||
Albums: undefined;
|
||||
@@ -105,7 +106,10 @@ const SimpleStack = createStackNavigator<SimpleStackParams>();
|
||||
|
||||
export default function SimpleStackScreen({
|
||||
navigation,
|
||||
}: StackScreenProps<ParamListBase>) {
|
||||
screenOptions,
|
||||
}: StackScreenProps<ParamListBase> & {
|
||||
screenOptions?: StackNavigationOptions;
|
||||
}) {
|
||||
React.useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerShown: false,
|
||||
@@ -113,7 +117,7 @@ export default function SimpleStackScreen({
|
||||
}, [navigation]);
|
||||
|
||||
return (
|
||||
<SimpleStack.Navigator>
|
||||
<SimpleStack.Navigator screenOptions={screenOptions}>
|
||||
<SimpleStack.Screen
|
||||
name="Article"
|
||||
component={ArticleScreen}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
ScrollView,
|
||||
YellowBox,
|
||||
Platform,
|
||||
StatusBar,
|
||||
I18nManager,
|
||||
Dimensions,
|
||||
ScaledSize,
|
||||
Linking,
|
||||
LogBox,
|
||||
} from 'react-native';
|
||||
import { enableScreens } from 'react-native-screens';
|
||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||
@@ -38,9 +38,9 @@ import {
|
||||
HeaderStyleInterpolators,
|
||||
} from '@react-navigation/stack';
|
||||
import { useReduxDevToolsExtension } from '@react-navigation/devtools';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
import { restartApp } from './Restart';
|
||||
import AsyncStorage from './AsyncStorage';
|
||||
import LinkingPrefixes from './LinkingPrefixes';
|
||||
import SettingsItem from './Shared/SettingsItem';
|
||||
import SimpleStack from './Screens/SimpleStack';
|
||||
@@ -58,7 +58,9 @@ import PreventRemove from './Screens/PreventRemove';
|
||||
import CompatAPI from './Screens/CompatAPI';
|
||||
import LinkComponent from './Screens/LinkComponent';
|
||||
|
||||
YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']);
|
||||
if (Platform.OS !== 'web') {
|
||||
LogBox.ignoreLogs(['Require cycle:']);
|
||||
}
|
||||
|
||||
enableScreens();
|
||||
|
||||
@@ -67,13 +69,6 @@ type RootDrawerParamList = {
|
||||
Another: undefined;
|
||||
};
|
||||
|
||||
type RootStackParamList = {
|
||||
Home: undefined;
|
||||
NotFound: undefined;
|
||||
} & {
|
||||
[P in keyof typeof SCREENS]: undefined;
|
||||
};
|
||||
|
||||
const SCREENS = {
|
||||
SimpleStack: { title: 'Simple Stack', component: SimpleStack },
|
||||
ModalPresentationStack: {
|
||||
@@ -123,6 +118,13 @@ const SCREENS = {
|
||||
},
|
||||
};
|
||||
|
||||
type RootStackParamList = {
|
||||
Home: undefined;
|
||||
NotFound: undefined;
|
||||
} & {
|
||||
[P in keyof typeof SCREENS]: undefined;
|
||||
};
|
||||
|
||||
const Drawer = createDrawerNavigator<RootDrawerParamList>();
|
||||
const Stack = createStackNavigator<RootStackParamList>();
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const createExpoWebpackConfigAsync = require('@expo/webpack-config');
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
|
||||
const node_modules = path.resolve(__dirname, '..', 'node_modules');
|
||||
const packages = path.resolve(__dirname, '..', 'packages');
|
||||
@@ -19,10 +17,6 @@ module.exports = async function (env, argv) {
|
||||
use: 'babel-loader',
|
||||
});
|
||||
|
||||
config.resolve.plugins = config.resolve.plugins.filter(
|
||||
(p) => !(p instanceof ModuleScopePlugin)
|
||||
);
|
||||
|
||||
Object.assign(config.resolve.alias, {
|
||||
'react': path.resolve(node_modules, 'react'),
|
||||
'react-native': path.resolve(node_modules, 'react-native-web'),
|
||||
|
||||
28
package.json
28
package.json
@@ -25,23 +25,23 @@
|
||||
"example": "yarn --cwd example"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/config-conventional": "^9.1.1",
|
||||
"@types/jest": "^26.0.8",
|
||||
"babel-jest": "^26.2.2",
|
||||
"codecov": "^3.7.2",
|
||||
"commitlint": "^9.1.0",
|
||||
"eslint": "^7.6.0",
|
||||
"eslint-config-satya164": "^3.1.7",
|
||||
"husky": "^4.2.5",
|
||||
"jest": "^26.2.2",
|
||||
"@commitlint/config-conventional": "^11.0.0",
|
||||
"@types/jest": "^26.0.15",
|
||||
"babel-jest": "^26.6.1",
|
||||
"codecov": "^3.8.0",
|
||||
"commitlint": "^11.0.0",
|
||||
"eslint": "^7.12.0",
|
||||
"eslint-config-satya164": "^3.1.8",
|
||||
"husky": "^4.3.0",
|
||||
"jest": "^26.6.1",
|
||||
"lerna": "^3.22.1",
|
||||
"metro-react-native-babel-preset": "^0.61.0",
|
||||
"prettier": "^2.0.5",
|
||||
"typescript": "^3.9.7"
|
||||
"metro-react-native-babel-preset": "^0.63.0",
|
||||
"prettier": "^2.1.2",
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.62.2"
|
||||
"react": "~16.13.1",
|
||||
"react-native": "~0.63.2"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
|
||||
@@ -3,6 +3,115 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.10.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.10.6...@react-navigation/bottom-tabs@5.10.7) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.10.5...@react-navigation/bottom-tabs@5.10.6) (2020-11-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disable react-native-screens on iOS for older versions ([ce7d20e](https://github.com/react-navigation/react-navigation/commit/ce7d20e3366415b07a537e01ee0b17ce7e72cad6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.10.4...@react-navigation/bottom-tabs@5.10.5) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.10.3...@react-navigation/bottom-tabs@5.10.4) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.10.2...@react-navigation/bottom-tabs@5.10.3) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.10.1...@react-navigation/bottom-tabs@5.10.2) (2020-10-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.10.0...@react-navigation/bottom-tabs@5.10.1) (2020-10-28)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.10.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.9.2...@react-navigation/bottom-tabs@5.10.0) (2020-10-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add optional screens per navigator ([#8805](https://github.com/react-navigation/react-navigation/issues/8805)) ([7196889](https://github.com/react-navigation/react-navigation/commit/7196889bf1218eb6a736d9475e33a909c2248c3b))
|
||||
* add sceneContainerStyle prop to bottom-tabs navigator ([#8947](https://github.com/react-navigation/react-navigation/issues/8947)) ([f01bb48](https://github.com/react-navigation/react-navigation/commit/f01bb4834b01e13ab9a6b220328349f77ca49428))
|
||||
* improve types for navigation state ([#8980](https://github.com/react-navigation/react-navigation/issues/8980)) ([7dc2f58](https://github.com/react-navigation/react-navigation/commit/7dc2f5832e371473f3263c01ab39824eb9e2057d))
|
||||
* update helper types to have navigator specific methods ([f51086e](https://github.com/react-navigation/react-navigation/commit/f51086edea42f2382dac8c6914aac8574132114b))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.9.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.9.1...@react-navigation/bottom-tabs@5.9.2) (2020-10-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use route keys instead of index for lazy load ([c49dab3](https://github.com/react-navigation/react-navigation/commit/c49dab31b2c63a1735f0ed0a1936ecf7bbcd8b13))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.9.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.9.0...@react-navigation/bottom-tabs@5.9.1) (2020-09-28)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.9.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.8.0...@react-navigation/bottom-tabs@5.9.0) (2020-09-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* cleanly removing event listeners in useWindowDimensions ([#8866](https://github.com/react-navigation/react-navigation/issues/8866)) ([dcbfe52](https://github.com/react-navigation/react-navigation/commit/dcbfe52667d14b0dbed6a353675d02189f7f7b5b))
|
||||
* fix border showing on hidden tab bar. closes [#8869](https://github.com/react-navigation/react-navigation/issues/8869) ([9b03c8e](https://github.com/react-navigation/react-navigation/commit/9b03c8e3a4e1552f9abaa2732a64e2136821e38d))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add compact size for bottom tabs ([#8728](https://github.com/react-navigation/react-navigation/issues/8728)) ([a6179b7](https://github.com/react-navigation/react-navigation/commit/a6179b75adba2282a9c12751bb8db3751c39d8e4))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.8.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.7.3...@react-navigation/bottom-tabs@5.8.0) (2020-08-04)
|
||||
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
Bottom tab navigator for React Navigation following iOS design guidelines.
|
||||
|
||||
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/bottom-tab-navigator.html).
|
||||
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/bottom-tab-navigator/).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/bottom-tabs",
|
||||
"description": "Bottom tab navigator following iOS design guidelines",
|
||||
"version": "5.8.0",
|
||||
"version": "5.10.7",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -36,22 +36,22 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"color": "^3.1.2",
|
||||
"react-native-iphone-x-helper": "^1.2.1"
|
||||
"color": "^3.1.3",
|
||||
"react-native-iphone-x-helper": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@react-navigation/native": "^5.7.3",
|
||||
"@testing-library/react-native": "^7.0.1",
|
||||
"@react-navigation/native": "^5.8.7",
|
||||
"@testing-library/react-native": "^7.1.0",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/react": "^16.9.44",
|
||||
"@types/react-native": "^0.63.4",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-native": "^0.63.30",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.62.2",
|
||||
"react-native-safe-area-context": "~3.1.1",
|
||||
"react-native-screens": "^2.9.0",
|
||||
"typescript": "^3.9.7"
|
||||
"react": "~16.13.1",
|
||||
"react-native": "~0.63.2",
|
||||
"react-native-safe-area-context": "3.1.4",
|
||||
"react-native-screens": "~2.10.1",
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.5",
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
TabRouter,
|
||||
TabRouterOptions,
|
||||
TabNavigationState,
|
||||
TabActionHelpers,
|
||||
ParamListBase,
|
||||
} from '@react-navigation/native';
|
||||
import BottomTabView from '../views/BottomTabView';
|
||||
import type {
|
||||
@@ -23,11 +25,13 @@ function BottomTabNavigator({
|
||||
backBehavior,
|
||||
children,
|
||||
screenOptions,
|
||||
sceneContainerStyle,
|
||||
...rest
|
||||
}: Props) {
|
||||
const { state, descriptors, navigation } = useNavigationBuilder<
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamListBase>,
|
||||
TabRouterOptions,
|
||||
TabActionHelpers<ParamListBase>,
|
||||
BottomTabNavigationOptions,
|
||||
BottomTabNavigationEventMap
|
||||
>(TabRouter, {
|
||||
@@ -43,12 +47,13 @@ function BottomTabNavigator({
|
||||
state={state}
|
||||
navigation={navigation}
|
||||
descriptors={descriptors}
|
||||
sceneContainerStyle={sceneContainerStyle}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default createNavigatorFactory<
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamListBase>,
|
||||
BottomTabNavigationOptions,
|
||||
BottomTabNavigationEventMap,
|
||||
typeof BottomTabNavigator
|
||||
|
||||
@@ -33,7 +33,8 @@ export type LabelPosition = 'beside-icon' | 'below-icon';
|
||||
export type BottomTabNavigationHelpers = NavigationHelpers<
|
||||
ParamListBase,
|
||||
BottomTabNavigationEventMap
|
||||
>;
|
||||
> &
|
||||
TabActionHelpers<ParamListBase>;
|
||||
|
||||
export type BottomTabNavigationProp<
|
||||
ParamList extends ParamListBase,
|
||||
@@ -41,7 +42,7 @@ export type BottomTabNavigationProp<
|
||||
> = NavigationProp<
|
||||
ParamList,
|
||||
RouteName,
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamList>,
|
||||
BottomTabNavigationOptions,
|
||||
BottomTabNavigationEventMap
|
||||
> &
|
||||
@@ -148,7 +149,7 @@ export type BottomTabNavigationOptions = {
|
||||
export type BottomTabDescriptor = Descriptor<
|
||||
ParamListBase,
|
||||
string,
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamListBase>,
|
||||
BottomTabNavigationOptions
|
||||
>;
|
||||
|
||||
@@ -170,6 +171,16 @@ export type BottomTabNavigationConfig<T = BottomTabBarOptions> = {
|
||||
* Options for the tab bar which will be passed as props to the tab bar component.
|
||||
*/
|
||||
tabBarOptions?: T;
|
||||
/**
|
||||
* Whether inactive screens should be detached from the view hierarchy to save memory.
|
||||
* Make sure to call `enableScreens` from `react-native-screens` to make it work.
|
||||
* Defaults to `true` on Android.
|
||||
*/
|
||||
detachInactiveScreens?: boolean;
|
||||
/**
|
||||
* Style object for the component wrapping the screen content.
|
||||
*/
|
||||
sceneContainerStyle?: StyleProp<ViewStyle>;
|
||||
};
|
||||
|
||||
export type BottomTabBarOptions = {
|
||||
@@ -240,7 +251,7 @@ export type BottomTabBarOptions = {
|
||||
};
|
||||
|
||||
export type BottomTabBarProps<T = BottomTabBarOptions> = T & {
|
||||
state: TabNavigationState;
|
||||
state: TabNavigationState<ParamListBase>;
|
||||
descriptors: BottomTabDescriptorMap;
|
||||
navigation: NavigationHelpers<ParamListBase, BottomTabNavigationEventMap>;
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ export default function useWindowDimensions() {
|
||||
|
||||
Dimensions.addEventListener('change', onChange);
|
||||
|
||||
return () => Dimensions.addEventListener('change', onChange);
|
||||
return () => Dimensions.removeEventListener('change', onChange);
|
||||
}, []);
|
||||
|
||||
return dimensions;
|
||||
|
||||
@@ -26,6 +26,7 @@ type Props = BottomTabBarProps & {
|
||||
};
|
||||
|
||||
const DEFAULT_TABBAR_HEIGHT = 49;
|
||||
const COMPACT_TABBAR_HEIGHT = 32;
|
||||
const DEFAULT_MAX_TAB_ITEM_WIDTH = 125;
|
||||
|
||||
const useNativeDriver = Platform.OS !== 'web';
|
||||
@@ -119,6 +120,8 @@ export default function BottomTabBar({
|
||||
width: dimensions.width,
|
||||
});
|
||||
|
||||
const isLandscape = () => dimensions.width > dimensions.height;
|
||||
|
||||
const handleLayout = (e: LayoutChangeEvent) => {
|
||||
const { height, width } = e.nativeEvent.layout;
|
||||
|
||||
@@ -160,9 +163,7 @@ export default function BottomTabBar({
|
||||
|
||||
return routes.length * maxTabItemWidth <= layout.width;
|
||||
} else {
|
||||
const isLandscape = dimensions.width > dimensions.height;
|
||||
|
||||
return isLandscape;
|
||||
return isLandscape();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -180,6 +181,18 @@ export default function BottomTabBar({
|
||||
0
|
||||
);
|
||||
|
||||
const getDefaultTabBarHeight = () => {
|
||||
if (
|
||||
Platform.OS === 'ios' &&
|
||||
!Platform.isPad &&
|
||||
isLandscape() &&
|
||||
shouldUseHorizontalLabels()
|
||||
) {
|
||||
return COMPACT_TABBAR_HEIGHT;
|
||||
}
|
||||
return DEFAULT_TABBAR_HEIGHT;
|
||||
};
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
style={[
|
||||
@@ -193,7 +206,10 @@ export default function BottomTabBar({
|
||||
{
|
||||
translateY: visible.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [layout.height + paddingBottom, 0],
|
||||
outputRange: [
|
||||
layout.height + paddingBottom + StyleSheet.hairlineWidth,
|
||||
0,
|
||||
],
|
||||
}),
|
||||
},
|
||||
],
|
||||
@@ -202,7 +218,7 @@ export default function BottomTabBar({
|
||||
position: isTabBarHidden ? 'absolute' : (null as any),
|
||||
},
|
||||
{
|
||||
height: DEFAULT_TABBAR_HEIGHT + paddingBottom,
|
||||
height: getDefaultTabBarHeight() + paddingBottom,
|
||||
paddingBottom,
|
||||
paddingHorizontal: Math.max(insets.left, insets.right),
|
||||
},
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
||||
|
||||
import {
|
||||
NavigationHelpersContext,
|
||||
ParamListBase,
|
||||
TabNavigationState,
|
||||
useTheme,
|
||||
} from '@react-navigation/native';
|
||||
@@ -19,21 +20,23 @@ import type {
|
||||
} from '../types';
|
||||
|
||||
type Props = BottomTabNavigationConfig & {
|
||||
state: TabNavigationState;
|
||||
state: TabNavigationState<ParamListBase>;
|
||||
navigation: BottomTabNavigationHelpers;
|
||||
descriptors: BottomTabDescriptorMap;
|
||||
};
|
||||
|
||||
type State = {
|
||||
loaded: number[];
|
||||
loaded: string[];
|
||||
};
|
||||
|
||||
function SceneContent({
|
||||
isFocused,
|
||||
children,
|
||||
style,
|
||||
}: {
|
||||
isFocused: boolean;
|
||||
children: React.ReactNode;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}) {
|
||||
const { colors } = useTheme();
|
||||
|
||||
@@ -41,7 +44,7 @@ function SceneContent({
|
||||
<View
|
||||
accessibilityElementsHidden={!isFocused}
|
||||
importantForAccessibility={isFocused ? 'auto' : 'no-hide-descendants'}
|
||||
style={[styles.content, { backgroundColor: colors.background }]}
|
||||
style={[styles.content, { backgroundColor: colors.background }, style]}
|
||||
>
|
||||
{children}
|
||||
</View>
|
||||
@@ -54,18 +57,18 @@ export default class BottomTabView extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
const { index } = nextProps.state;
|
||||
const focusedRouteKey = nextProps.state.routes[nextProps.state.index].key;
|
||||
|
||||
return {
|
||||
// Set the current tab to be loaded if it was not loaded before
|
||||
loaded: prevState.loaded.includes(index)
|
||||
loaded: prevState.loaded.includes(focusedRouteKey)
|
||||
? prevState.loaded
|
||||
: [...prevState.loaded, index],
|
||||
: [...prevState.loaded, focusedRouteKey],
|
||||
};
|
||||
}
|
||||
|
||||
state = {
|
||||
loaded: [this.props.state.index],
|
||||
state: State = {
|
||||
loaded: [this.props.state.routes[this.props.state.index].key],
|
||||
};
|
||||
|
||||
private renderTabBar = () => {
|
||||
@@ -85,7 +88,14 @@ export default class BottomTabView extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { state, descriptors, navigation, lazy } = this.props;
|
||||
const {
|
||||
state,
|
||||
descriptors,
|
||||
navigation,
|
||||
lazy,
|
||||
detachInactiveScreens = true,
|
||||
sceneContainerStyle,
|
||||
} = this.props;
|
||||
const { routes } = state;
|
||||
const { loaded } = this.state;
|
||||
|
||||
@@ -93,7 +103,11 @@ export default class BottomTabView extends React.Component<Props, State> {
|
||||
<NavigationHelpersContext.Provider value={navigation}>
|
||||
<SafeAreaProviderCompat>
|
||||
<View style={styles.container}>
|
||||
<ScreenContainer style={styles.pages}>
|
||||
<ScreenContainer
|
||||
// @ts-ignore
|
||||
enabled={detachInactiveScreens}
|
||||
style={styles.pages}
|
||||
>
|
||||
{routes.map((route, index) => {
|
||||
const descriptor = descriptors[route.key];
|
||||
const { unmountOnBlur } = descriptor.options;
|
||||
@@ -103,7 +117,7 @@ export default class BottomTabView extends React.Component<Props, State> {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (lazy && !loaded.includes(index) && !isFocused) {
|
||||
if (lazy && !loaded.includes(route.key) && !isFocused) {
|
||||
// Don't render a screen if we've never navigated to it
|
||||
return null;
|
||||
}
|
||||
@@ -113,8 +127,12 @@ export default class BottomTabView extends React.Component<Props, State> {
|
||||
key={route.key}
|
||||
style={StyleSheet.absoluteFill}
|
||||
isVisible={isFocused}
|
||||
enabled={detachInactiveScreens}
|
||||
>
|
||||
<SceneContent isFocused={isFocused}>
|
||||
<SceneContent
|
||||
isFocused={isFocused}
|
||||
style={sceneContainerStyle}
|
||||
>
|
||||
{descriptor.render()}
|
||||
</SceneContent>
|
||||
</ResourceSavingScene>
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import * as React from 'react';
|
||||
import { Platform, StyleSheet, View } from 'react-native';
|
||||
import { Screen, screensEnabled } from 'react-native-screens';
|
||||
import {
|
||||
Screen,
|
||||
screensEnabled,
|
||||
// @ts-ignore
|
||||
shouldUseActivityState,
|
||||
} from 'react-native-screens';
|
||||
|
||||
type Props = {
|
||||
isVisible: boolean;
|
||||
children: React.ReactNode;
|
||||
enabled: boolean;
|
||||
style?: any;
|
||||
};
|
||||
|
||||
@@ -16,8 +22,17 @@ export default class ResourceSavingScene extends React.Component<Props> {
|
||||
if (screensEnabled?.() && Platform.OS !== 'web') {
|
||||
const { isVisible, ...rest } = this.props;
|
||||
|
||||
// @ts-expect-error: stackPresentation is incorrectly marked as required
|
||||
return <Screen active={isVisible ? 1 : 0} {...rest} />;
|
||||
if (shouldUseActivityState) {
|
||||
return (
|
||||
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
||||
<Screen activityState={isVisible ? 2 : 0} {...rest} />
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
||||
<Screen active={isVisible ? 1 : 0} {...rest} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const { isVisible, children, style, ...rest } = this.props;
|
||||
|
||||
@@ -3,6 +3,98 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.3.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.6...@react-navigation/compat@5.3.7) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.5...@react-navigation/compat@5.3.6) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.4...@react-navigation/compat@5.3.5) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.3...@react-navigation/compat@5.3.4) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.2...@react-navigation/compat@5.3.3) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.1...@react-navigation/compat@5.3.2) (2020-10-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.0...@react-navigation/compat@5.3.1) (2020-10-28)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.3.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.2.8...@react-navigation/compat@5.3.0) (2020-10-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* improve types for navigation state ([#8980](https://github.com/react-navigation/react-navigation/issues/8980)) ([7dc2f58](https://github.com/react-navigation/react-navigation/commit/7dc2f5832e371473f3263c01ab39824eb9e2057d))
|
||||
* update helper types to have navigator specific methods ([f51086e](https://github.com/react-navigation/react-navigation/commit/f51086edea42f2382dac8c6914aac8574132114b))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.8](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.2.7...@react-navigation/compat@5.2.8) (2020-10-07)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.2.6...@react-navigation/compat@5.2.7) (2020-09-28)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.2.5...@react-navigation/compat@5.2.6) (2020-09-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.2.4...@react-navigation/compat@5.2.5) (2020-08-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
Compatibility layer to write navigator definitions in static configuration format.
|
||||
|
||||
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/compatibility.html).
|
||||
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/compatibility/).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/compat",
|
||||
"description": "Compatibility layer to write navigator definitions in static configuration format",
|
||||
"version": "5.2.5",
|
||||
"version": "5.3.7",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -11,7 +11,7 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/react-navigation/react-navigation/issues"
|
||||
},
|
||||
"homepage": "https://reactnavigation.org/docs/compatibility.html",
|
||||
"homepage": "https://reactnavigation.org/docs/compatibility/",
|
||||
"main": "lib/commonjs/index.js",
|
||||
"react-native": "src/index.tsx",
|
||||
"source": "src/index.tsx",
|
||||
@@ -32,10 +32,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@react-navigation/native": "^5.7.3",
|
||||
"@types/react": "^16.9.44",
|
||||
"react": "~16.9.0",
|
||||
"typescript": "^3.9.7"
|
||||
"@react-navigation/native": "^5.8.7",
|
||||
"@types/react": "^16.9.53",
|
||||
"react": "~16.13.1",
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.5",
|
||||
|
||||
@@ -19,7 +19,11 @@ type EventName =
|
||||
export default function createCompatNavigationProp<
|
||||
NavigationPropType extends NavigationProp<ParamListBase>,
|
||||
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
||||
infer P
|
||||
infer P,
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any
|
||||
>
|
||||
? P
|
||||
: ParamListBase
|
||||
@@ -143,7 +147,6 @@ export default function createCompatNavigationProp<
|
||||
}
|
||||
},
|
||||
state: {
|
||||
// @ts-expect-error: these properties may actually exist
|
||||
key: state.key,
|
||||
// @ts-expect-error
|
||||
routeName: state.name,
|
||||
@@ -198,7 +201,6 @@ export default function createCompatNavigationProp<
|
||||
|
||||
const { routes } = navigation.dangerouslyGetState();
|
||||
|
||||
// @ts-expect-error
|
||||
return routes[0].key === state.key;
|
||||
},
|
||||
dangerouslyGetParent() {
|
||||
|
||||
@@ -32,7 +32,11 @@ export default function createCompatNavigatorFactory<
|
||||
const createCompatNavigator = <
|
||||
NavigationPropType extends NavigationProp<any, any, any, any, any>,
|
||||
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
||||
infer P
|
||||
infer P,
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any
|
||||
>
|
||||
? P
|
||||
: ParamListBase,
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
TabRouter,
|
||||
TabRouterOptions,
|
||||
TabNavigationState,
|
||||
TabActionHelpers,
|
||||
ParamListBase,
|
||||
} from '@react-navigation/native';
|
||||
import createCompatNavigatorFactory from './createCompatNavigatorFactory';
|
||||
|
||||
@@ -12,17 +14,21 @@ type Props = DefaultNavigatorOptions<{}> & TabRouterOptions;
|
||||
|
||||
function SwitchNavigator(props: Props) {
|
||||
const { state, descriptors } = useNavigationBuilder<
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamListBase>,
|
||||
TabRouterOptions,
|
||||
{},
|
||||
{}
|
||||
{},
|
||||
TabActionHelpers<ParamListBase>
|
||||
>(TabRouter, props);
|
||||
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
}
|
||||
|
||||
export default createCompatNavigatorFactory(
|
||||
createNavigatorFactory<TabNavigationState, {}, {}, typeof SwitchNavigator>(
|
||||
SwitchNavigator
|
||||
)
|
||||
createNavigatorFactory<
|
||||
TabNavigationState<ParamListBase>,
|
||||
{},
|
||||
{},
|
||||
typeof SwitchNavigator
|
||||
>(SwitchNavigator)
|
||||
);
|
||||
|
||||
@@ -12,7 +12,9 @@ export function navigate(
|
||||
params?: object,
|
||||
action?: never
|
||||
): NavigateActionType;
|
||||
// eslint-disable-next-line no-redeclare
|
||||
export function navigate(options: NavigateActionPayload): NavigateActionType;
|
||||
// eslint-disable-next-line no-redeclare
|
||||
export function navigate(
|
||||
options: string | NavigateActionPayload,
|
||||
params?: object,
|
||||
|
||||
@@ -8,7 +8,11 @@ import type * as helpers from './helpers';
|
||||
export type CompatNavigationProp<
|
||||
NavigationPropType extends NavigationProp<ParamListBase>,
|
||||
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
||||
infer P
|
||||
infer P,
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any
|
||||
>
|
||||
? P
|
||||
: ParamListBase,
|
||||
@@ -67,7 +71,11 @@ export type CompatScreenType<
|
||||
export type CompatRouteConfig<
|
||||
NavigationPropType extends NavigationProp<ParamListBase>,
|
||||
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
||||
infer P
|
||||
infer P,
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any
|
||||
>
|
||||
? P
|
||||
: ParamListBase
|
||||
|
||||
@@ -3,6 +3,129 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.14.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.14.0...@react-navigation/core@5.14.1) (2020-11-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* tweak error message when navigator has non-screen children ([360b0e9](https://github.com/react-navigation/react-navigation/commit/360b0e995835990c55b75898757ebdd120d52446))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.14.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.13.5...@react-navigation/core@5.14.0) (2020-11-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* always respect key in the route object when generating action ([cb2e744](https://github.com/react-navigation/react-navigation/commit/cb2e744dcebf7f71ddaa5462d393a6dbfd971fcd))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add a NavigatorScreenParams type. closes [#6931](https://github.com/react-navigation/react-navigation/issues/6931) ([e3e58c2](https://github.com/react-navigation/react-navigation/commit/e3e58c2d890e7fab75d78371e349aea55a402fcd))
|
||||
* add warning on accessing the state object on route prop ([ec7b02a](https://github.com/react-navigation/react-navigation/commit/ec7b02af2ca835122b9000799e2366d7009da6e3))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.13.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.13.4...@react-navigation/core@5.13.5) (2020-11-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don't use use-subscription to avoid peer dep related errors ([66f3a4a](https://github.com/react-navigation/react-navigation/commit/66f3a4a0bb39475434668bc94fb1750dbe618ee0)), closes [/github.com/react-navigation/react-navigation/issues/9021#issuecomment-721679760](https://github.com//github.com/react-navigation/react-navigation/issues/9021/issues/issuecomment-721679760)
|
||||
* use useDebugValue in more places ([b20f2d1](https://github.com/react-navigation/react-navigation/commit/b20f2d1f7ccb82db70df9cddf5746557912daa99))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.13.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.13.3...@react-navigation/core@5.13.4) (2020-11-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix nested navigation not working the first time ([ebc7f9e](https://github.com/react-navigation/react-navigation/commit/ebc7f9ea75bbf6e3b6303027cfa023d7c97342ff))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.13.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.13.2...@react-navigation/core@5.13.3) (2020-11-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle navigating to same screen again for nested screens ([0945689](https://github.com/react-navigation/react-navigation/commit/0945689b70d71a4b5d766c61d57009761c460bf6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.13.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.13.1...@react-navigation/core@5.13.2) (2020-10-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix params from for the root screen when creating action ([e8515f9](https://github.com/react-navigation/react-navigation/commit/e8515f9cd94a912c107a407dea3d953c4172393f)), closes [#9006](https://github.com/react-navigation/react-navigation/issues/9006)
|
||||
* trim routes if an index is specified in state ([fb7ac96](https://github.com/react-navigation/react-navigation/commit/fb7ac960c8e1ffca200ecb12696ce5531a139e50))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.13.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.13.0...@react-navigation/core@5.13.1) (2020-10-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve types for route prop in screenOptions ([d26bcc0](https://github.com/react-navigation/react-navigation/commit/d26bcc057ef31f8950f909adf83e263171a42d74))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.13.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.12.5...@react-navigation/core@5.13.0) (2020-10-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix imports from query-string. closes [#8971](https://github.com/react-navigation/react-navigation/issues/8971) ([#8976](https://github.com/react-navigation/react-navigation/issues/8976)) ([261a33a](https://github.com/react-navigation/react-navigation/commit/261a33a0d03150c87b06f01aeace4926b1c03eb6))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add an unhandled action listener ([#8895](https://github.com/react-navigation/react-navigation/issues/8895)) ([80ff5a9](https://github.com/react-navigation/react-navigation/commit/80ff5a9c543a44fa2fd7ba7fda0598f1b0d52a64))
|
||||
* allow deep linking to reset state ([#8973](https://github.com/react-navigation/react-navigation/issues/8973)) ([7f3b27a](https://github.com/react-navigation/react-navigation/commit/7f3b27a9ec8edd9604ac19774baa1f60963ccdc9)), closes [#8952](https://github.com/react-navigation/react-navigation/issues/8952)
|
||||
* improve types for navigation state ([#8980](https://github.com/react-navigation/react-navigation/issues/8980)) ([7dc2f58](https://github.com/react-navigation/react-navigation/commit/7dc2f5832e371473f3263c01ab39824eb9e2057d))
|
||||
* update helper types to have navigator specific methods ([f51086e](https://github.com/react-navigation/react-navigation/commit/f51086edea42f2382dac8c6914aac8574132114b))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.12.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.12.4...@react-navigation/core@5.12.5) (2020-10-07)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.12.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.12.3...@react-navigation/core@5.12.4) (2020-09-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* typo in logic of getStateFromPath ([#8868](https://github.com/react-navigation/react-navigation/issues/8868)) ([97c215d](https://github.com/react-navigation/react-navigation/commit/97c215d2f2ea9f6bbade7503348827c5b6dc4186))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.12.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.12.2...@react-navigation/core@5.12.3) (2020-08-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/core
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/core",
|
||||
"description": "Core utilities for building navigators",
|
||||
"version": "5.12.3",
|
||||
"version": "5.14.1",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native",
|
||||
@@ -35,23 +35,21 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.4.11",
|
||||
"@react-navigation/routers": "^5.6.1",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"nanoid": "^3.1.12",
|
||||
"query-string": "^6.13.1",
|
||||
"react-is": "^16.13.0",
|
||||
"use-subscription": "^1.4.0"
|
||||
"nanoid": "^3.1.15",
|
||||
"query-string": "^6.13.6",
|
||||
"react-is": "^16.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@testing-library/react-native": "^7.0.1",
|
||||
"@types/react": "^16.9.44",
|
||||
"@testing-library/react-native": "^7.1.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-is": "^16.7.1",
|
||||
"@types/use-subscription": "^1.0.0",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.9.0",
|
||||
"react": "~16.13.1",
|
||||
"react-test-renderer": "~16.13.1",
|
||||
"typescript": "^3.9.7"
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
|
||||
@@ -94,6 +94,7 @@ const BaseNavigationContainer = React.forwardRef(
|
||||
{
|
||||
initialState,
|
||||
onStateChange,
|
||||
onUnhandledAction,
|
||||
independent,
|
||||
children,
|
||||
}: NavigationContainerProps,
|
||||
@@ -159,9 +160,20 @@ const BaseNavigationContainer = React.forwardRef(
|
||||
|
||||
const resetRoot = React.useCallback(
|
||||
(state?: PartialState<NavigationState> | NavigationState) => {
|
||||
setState(state);
|
||||
const target = state?.key ?? keyedListeners.getState.root?.().key;
|
||||
|
||||
if (target == null) {
|
||||
throw new Error(NOT_INITIALIZED_ERROR);
|
||||
}
|
||||
|
||||
listeners.focus[0]((navigation) =>
|
||||
navigation.dispatch({
|
||||
...CommonActions.reset(state),
|
||||
target,
|
||||
})
|
||||
);
|
||||
},
|
||||
[setState]
|
||||
[keyedListeners.getState, listeners.focus]
|
||||
);
|
||||
|
||||
const getRootState = React.useCallback(() => {
|
||||
@@ -342,51 +354,56 @@ const BaseNavigationContainer = React.forwardRef(
|
||||
isFirstMountRef.current = false;
|
||||
}, [getRootState, emitter, state]);
|
||||
|
||||
const onUnhandledAction = React.useCallback((action: NavigationAction) => {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
return;
|
||||
}
|
||||
const defaultOnUnhandledAction = React.useCallback(
|
||||
(action: NavigationAction) => {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: Record<string, any> | undefined = action.payload;
|
||||
const payload: Record<string, any> | undefined = action.payload;
|
||||
|
||||
let message = `The action '${action.type}'${
|
||||
payload ? ` with payload ${JSON.stringify(action.payload)}` : ''
|
||||
} was not handled by any navigator.`;
|
||||
let message = `The action '${action.type}'${
|
||||
payload ? ` with payload ${JSON.stringify(action.payload)}` : ''
|
||||
} was not handled by any navigator.`;
|
||||
|
||||
switch (action.type) {
|
||||
case 'NAVIGATE':
|
||||
case 'PUSH':
|
||||
case 'REPLACE':
|
||||
case 'JUMP_TO':
|
||||
if (payload?.name) {
|
||||
message += `\n\nDo you have a screen named '${payload.name}'?\n\nIf you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.`;
|
||||
} else {
|
||||
message += `\n\nYou need to pass the name of the screen to navigate to.\n\nSee https://reactnavigation.org/docs/navigation-actions for usage.`;
|
||||
}
|
||||
switch (action.type) {
|
||||
case 'NAVIGATE':
|
||||
case 'PUSH':
|
||||
case 'REPLACE':
|
||||
case 'JUMP_TO':
|
||||
if (payload?.name) {
|
||||
message += `\n\nDo you have a screen named '${payload.name}'?\n\nIf you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.`;
|
||||
} else {
|
||||
message += `\n\nYou need to pass the name of the screen to navigate to.\n\nSee https://reactnavigation.org/docs/navigation-actions for usage.`;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'GO_BACK':
|
||||
case 'POP':
|
||||
case 'POP_TO_TOP':
|
||||
message += `\n\nIs there any screen to go back to?`;
|
||||
break;
|
||||
case 'OPEN_DRAWER':
|
||||
case 'CLOSE_DRAWER':
|
||||
case 'TOGGLE_DRAWER':
|
||||
message += `\n\nIs your screen inside a Drawer navigator?`;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'GO_BACK':
|
||||
case 'POP':
|
||||
case 'POP_TO_TOP':
|
||||
message += `\n\nIs there any screen to go back to?`;
|
||||
break;
|
||||
case 'OPEN_DRAWER':
|
||||
case 'CLOSE_DRAWER':
|
||||
case 'TOGGLE_DRAWER':
|
||||
message += `\n\nIs your screen inside a Drawer navigator?`;
|
||||
break;
|
||||
}
|
||||
|
||||
message += `\n\nThis is a development-only warning and won't be shown in production.`;
|
||||
message += `\n\nThis is a development-only warning and won't be shown in production.`;
|
||||
|
||||
console.error(message);
|
||||
}, []);
|
||||
console.error(message);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<ScheduleUpdateContext.Provider value={scheduleContext}>
|
||||
<NavigationBuilderContext.Provider value={builderContext}>
|
||||
<NavigationStateContext.Provider value={context}>
|
||||
<UnhandledActionContext.Provider value={onUnhandledAction}>
|
||||
<UnhandledActionContext.Provider
|
||||
value={onUnhandledAction ?? defaultOnUnhandledAction}
|
||||
>
|
||||
<EnsureSingleNavigator>{children}</EnsureSingleNavigator>
|
||||
</UnhandledActionContext.Provider>
|
||||
</NavigationStateContext.Provider>
|
||||
|
||||
@@ -18,9 +18,8 @@ type Props<
|
||||
> = {
|
||||
screen: RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>;
|
||||
navigation: NavigationProp<ParamListBase, string, State, ScreenOptions>;
|
||||
route: Route<string> & {
|
||||
state?: NavigationState | PartialState<NavigationState>;
|
||||
};
|
||||
route: Route<string>;
|
||||
routeState: NavigationState | PartialState<NavigationState> | undefined;
|
||||
getState: () => State;
|
||||
setState: (state: State) => void;
|
||||
options: object;
|
||||
@@ -38,6 +37,7 @@ export default function SceneView<
|
||||
screen,
|
||||
route,
|
||||
navigation,
|
||||
routeState,
|
||||
getState,
|
||||
setState,
|
||||
options,
|
||||
@@ -86,7 +86,7 @@ export default function SceneView<
|
||||
|
||||
const context = React.useMemo(
|
||||
() => ({
|
||||
state: route.state,
|
||||
state: routeState,
|
||||
getState: getCurrentState,
|
||||
setState: setCurrentState,
|
||||
getKey,
|
||||
@@ -95,7 +95,7 @@ export default function SceneView<
|
||||
addOptionsGetter,
|
||||
}),
|
||||
[
|
||||
route.state,
|
||||
routeState,
|
||||
getCurrentState,
|
||||
setCurrentState,
|
||||
getKey,
|
||||
|
||||
@@ -721,3 +721,39 @@ it("throws if the ref hasn't finished initializing", () => {
|
||||
|
||||
render(element);
|
||||
});
|
||||
|
||||
it('invokes the unhandled action listener with the unhandled action', () => {
|
||||
const ref = React.createRef<NavigationContainerRef>();
|
||||
const fn = jest.fn();
|
||||
|
||||
const TestNavigator = (props: any) => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{state.routes.map((route) => descriptors[route.key].render())}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const TestScreen = () => <></>;
|
||||
|
||||
render(
|
||||
<BaseNavigationContainer ref={ref} onUnhandledAction={fn}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo" component={TestScreen} />
|
||||
<Screen name="bar" component={TestScreen} />
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
act(() => ref.current!.navigate('bar'));
|
||||
act(() => ref.current!.navigate('baz'));
|
||||
|
||||
expect(fn).toHaveBeenCalledWith({
|
||||
payload: {
|
||||
name: 'baz',
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -132,6 +132,17 @@ export default function MockRouter(options: DefaultRouterOptions) {
|
||||
};
|
||||
}
|
||||
|
||||
case 'GO_BACK': {
|
||||
if (state.index === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
index: state.index - 1,
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return BaseRouter.getStateForAction(state, action);
|
||||
}
|
||||
|
||||
@@ -43,11 +43,58 @@ it('gets navigate action from state', () => {
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
expect(
|
||||
getActionFromState({
|
||||
it('gets navigate action from state for top-level screen', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getActionFromState(state)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets reset action from state with 1 route with key at root', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
key: 'test',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
key: 'test',
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getActionFromState(state)).toEqual({
|
||||
payload: {
|
||||
routes: [
|
||||
{
|
||||
key: 'test',
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
@@ -55,11 +102,7 @@ it('gets navigate action from state', () => {
|
||||
name: 'bar',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{ name: 'quz' },
|
||||
{ key: 'test', name: 'qux', params: { author: 'jane' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -67,8 +110,459 @@ it('gets navigate action from state', () => {
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
).toEqual({
|
||||
},
|
||||
type: 'RESET',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets reset action from state for top-level screen with 2 screens', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getActionFromState(state)).toEqual({
|
||||
payload: {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'RESET',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets reset action from state for top-level screen with more than 2 screens with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{ name: 'baz' },
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
initialRouteName: 'foo',
|
||||
screens: {
|
||||
bar: 'bar',
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{ name: 'baz' },
|
||||
],
|
||||
},
|
||||
type: 'RESET',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets reset action from state for top-level screen with 2 screens with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
key: 'test',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
initialRouteName: 'foo',
|
||||
screens: {
|
||||
bar: 'bar',
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
key: 'test',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'RESET',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state for top-level screen with 2 screens with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
initialRouteName: 'foo',
|
||||
screens: {
|
||||
bar: 'bar',
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
name: 'bar',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state for top-level screen with more than 2 screens with config with lower index', () => {
|
||||
const state = {
|
||||
index: 1,
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{ name: 'baz' },
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
initialRouteName: 'foo',
|
||||
screens: {
|
||||
bar: 'bar',
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
name: 'bar',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state with 2 screens', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{ name: 'quz' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getActionFromState(state)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: {
|
||||
screen: 'bar',
|
||||
initial: true,
|
||||
params: {
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: {
|
||||
author: 'jane',
|
||||
},
|
||||
},
|
||||
{ name: 'quz' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state with 2 screens with lower index', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
index: 0,
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{ name: 'quz' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getActionFromState(state)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: {
|
||||
screen: 'bar',
|
||||
initial: true,
|
||||
params: {
|
||||
screen: 'qux',
|
||||
initial: true,
|
||||
params: {
|
||||
author: 'jane',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state with more than 2 screens', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{ name: 'quz' },
|
||||
{ name: 'qua' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getActionFromState(state)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: {
|
||||
screen: 'bar',
|
||||
initial: true,
|
||||
params: {
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: {
|
||||
author: 'jane',
|
||||
},
|
||||
},
|
||||
{ name: 'quz' },
|
||||
{ name: 'qua' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
params: { answer: 42 },
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
screens: {
|
||||
foo: {
|
||||
initialRouteName: 'bar',
|
||||
screens: {
|
||||
bar: {
|
||||
initialRouteName: 'qux',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: {
|
||||
params: {
|
||||
answer: 42,
|
||||
params: {
|
||||
author: 'jane',
|
||||
},
|
||||
screen: 'qux',
|
||||
initial: true,
|
||||
},
|
||||
screen: 'bar',
|
||||
initial: true,
|
||||
},
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state for top-level screen with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
screens: {
|
||||
initialRouteName: 'bar',
|
||||
foo: {
|
||||
path: 'some-path/:answer',
|
||||
parse: {
|
||||
answer: Number,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state with 2 screens including initial route and with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{ name: 'quz' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
screens: {
|
||||
foo: {
|
||||
initialRouteName: 'bar',
|
||||
screens: {
|
||||
bar: {
|
||||
initialRouteName: 'qux',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: {
|
||||
@@ -84,7 +578,332 @@ it('gets navigate action from state', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('gets reset action from state', () => {
|
||||
it('gets navigate action from state with 2 screens without initial route and with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{ name: 'quz' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
screens: {
|
||||
foo: {
|
||||
initialRouteName: 'bar',
|
||||
screens: {
|
||||
bar: {
|
||||
initialRouteName: 'quz',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: {
|
||||
initial: true,
|
||||
screen: 'bar',
|
||||
params: {
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: {
|
||||
author: 'jane',
|
||||
},
|
||||
},
|
||||
{ name: 'quz' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state with 2 screens including route with key on initial route and with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
key: 'test',
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{ name: 'quz' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
screens: {
|
||||
foo: {
|
||||
initialRouteName: 'bar',
|
||||
screens: {
|
||||
bar: {
|
||||
initialRouteName: 'qux',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: {
|
||||
initial: true,
|
||||
screen: 'bar',
|
||||
params: {
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
key: 'test',
|
||||
name: 'qux',
|
||||
params: {
|
||||
author: 'jane',
|
||||
},
|
||||
},
|
||||
{ name: 'quz' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state with 2 screens including route with key on 2nd route and with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{
|
||||
key: 'test',
|
||||
name: 'quz',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
screens: {
|
||||
foo: {
|
||||
initialRouteName: 'bar',
|
||||
screens: {
|
||||
bar: {
|
||||
initialRouteName: 'qux',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: {
|
||||
initial: true,
|
||||
screen: 'bar',
|
||||
params: {
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: {
|
||||
author: 'jane',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'test',
|
||||
name: 'quz',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state with more than 2 screens and with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{ name: 'quz' },
|
||||
{ name: 'qua' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
screens: {
|
||||
foo: {
|
||||
initialRouteName: 'bar',
|
||||
screens: {
|
||||
bar: {
|
||||
initialRouteName: 'qux',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: {
|
||||
initial: true,
|
||||
screen: 'bar',
|
||||
params: {
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: {
|
||||
author: 'jane',
|
||||
},
|
||||
},
|
||||
{ name: 'quz' },
|
||||
{ name: 'qua' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state with more than 2 screens with lower index', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
index: 1,
|
||||
routes: [
|
||||
{ name: 'quu' },
|
||||
{
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{ name: 'quz' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
screens: {
|
||||
foo: {
|
||||
initialRouteName: 'bar',
|
||||
screens: {
|
||||
bar: {
|
||||
initialRouteName: 'quu',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: {
|
||||
screen: 'bar',
|
||||
initial: true,
|
||||
params: {
|
||||
screen: 'qux',
|
||||
initial: false,
|
||||
params: {
|
||||
author: 'jane',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't return action if no routes are provided'", () => {
|
||||
expect(getActionFromState({ routes: [] })).toBe(undefined);
|
||||
});
|
||||
|
||||
it('gets undefined action from state', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
|
||||
@@ -714,31 +714,41 @@ it('navigates to nested child in a navigator', () => {
|
||||
|
||||
expect(element).toMatchInlineSnapshot(`"[foo-a, undefined]"`);
|
||||
|
||||
act(
|
||||
() =>
|
||||
navigation.current &&
|
||||
navigation.current.navigate('bar', {
|
||||
screen: 'bar-b',
|
||||
params: { test: 42 },
|
||||
})
|
||||
act(() =>
|
||||
navigation.current?.navigate('bar', {
|
||||
screen: 'bar-b',
|
||||
params: { test: 42 },
|
||||
})
|
||||
);
|
||||
|
||||
expect(element).toMatchInlineSnapshot(
|
||||
`"[bar-b, {\\"some\\":\\"stuff\\",\\"test\\":42}]"`
|
||||
);
|
||||
|
||||
act(
|
||||
() =>
|
||||
navigation.current &&
|
||||
navigation.current.navigate('bar', {
|
||||
screen: 'bar-a',
|
||||
params: { whoa: 'test' },
|
||||
})
|
||||
act(() =>
|
||||
navigation.current?.navigate('bar', {
|
||||
screen: 'bar-a',
|
||||
params: { whoa: 'test' },
|
||||
})
|
||||
);
|
||||
|
||||
expect(element).toMatchInlineSnapshot(
|
||||
`"[bar-a, {\\"lol\\":\\"why\\",\\"whoa\\":\\"test\\"}]"`
|
||||
);
|
||||
|
||||
act(() => navigation.current?.navigate('bar', { screen: 'bar-b' }));
|
||||
|
||||
act(() => navigation.current?.goBack());
|
||||
|
||||
expect(element).toMatchInlineSnapshot(
|
||||
`"[bar-a, {\\"lol\\":\\"why\\",\\"whoa\\":\\"test\\"}]"`
|
||||
);
|
||||
|
||||
act(() => navigation.current?.navigate('bar', { screen: 'bar-b' }));
|
||||
|
||||
expect(element).toMatchInlineSnapshot(
|
||||
`"[bar-b, {\\"some\\":\\"stuff\\",\\"test\\":42,\\"whoa\\":\\"test\\"}]"`
|
||||
);
|
||||
});
|
||||
|
||||
it('navigates to nested child in a navigator with initial: false', () => {
|
||||
@@ -855,13 +865,11 @@ it('navigates to nested child in a navigator with initial: false', () => {
|
||||
type: 'test',
|
||||
});
|
||||
|
||||
act(
|
||||
() =>
|
||||
navigation.current &&
|
||||
navigation.current.navigate('bar', {
|
||||
screen: 'bar-b',
|
||||
params: { test: 42 },
|
||||
})
|
||||
act(() =>
|
||||
navigation.current?.navigate('bar', {
|
||||
screen: 'bar-b',
|
||||
params: { test: 42 },
|
||||
})
|
||||
);
|
||||
|
||||
expect(first).toMatchInlineSnapshot(
|
||||
@@ -962,14 +970,12 @@ it('navigates to nested child in a navigator with initial: false', () => {
|
||||
type: 'test',
|
||||
});
|
||||
|
||||
act(
|
||||
() =>
|
||||
navigation.current &&
|
||||
navigation.current.navigate('bar', {
|
||||
screen: 'bar-b',
|
||||
params: { test: 42 },
|
||||
initial: false,
|
||||
})
|
||||
act(() =>
|
||||
navigation.current?.navigate('bar', {
|
||||
screen: 'bar-b',
|
||||
params: { test: 42 },
|
||||
initial: false,
|
||||
})
|
||||
);
|
||||
|
||||
expect(second).toMatchInlineSnapshot(`"[bar-b, {\\"test\\":42}]"`);
|
||||
@@ -1101,6 +1107,194 @@ it('navigates to nested child in a navigator with initial: false', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('resets state of a nested child in a navigator', () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[state.routes[state.index].key].render();
|
||||
};
|
||||
|
||||
const TestComponent = ({ route }: any): any =>
|
||||
`[${route.name}, ${JSON.stringify(route.params)}]`;
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const navigation = React.createRef<NavigationContainerRef>();
|
||||
|
||||
const first = render(
|
||||
<BaseNavigationContainer ref={navigation} onStateChange={onStateChange}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo">
|
||||
{() => (
|
||||
<TestNavigator>
|
||||
<Screen name="foo-a" component={TestComponent} />
|
||||
<Screen name="foo-b" component={TestComponent} />
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
<Screen name="bar">
|
||||
{() => (
|
||||
<TestNavigator initialRouteName="bar-a">
|
||||
<Screen name="bar-a" component={TestComponent} />
|
||||
<Screen
|
||||
name="bar-b"
|
||||
component={TestComponent}
|
||||
initialParams={{ some: 'stuff' }}
|
||||
/>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
expect(first).toMatchInlineSnapshot(`"[foo-a, undefined]"`);
|
||||
|
||||
expect(navigation.current?.getRootState()).toEqual({
|
||||
index: 0,
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [
|
||||
{
|
||||
key: 'foo',
|
||||
name: 'foo',
|
||||
state: {
|
||||
index: 0,
|
||||
key: '1',
|
||||
routeNames: ['foo-a', 'foo-b'],
|
||||
routes: [
|
||||
{
|
||||
key: 'foo-a',
|
||||
name: 'foo-a',
|
||||
},
|
||||
{
|
||||
key: 'foo-b',
|
||||
name: 'foo-b',
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
},
|
||||
},
|
||||
{ key: 'bar', name: 'bar' },
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
});
|
||||
|
||||
act(() =>
|
||||
navigation.current?.navigate('bar', {
|
||||
state: {
|
||||
routes: [{ name: 'bar-a' }, { name: 'bar-b' }],
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(first).toMatchInlineSnapshot(`"[bar-a, undefined]"`);
|
||||
|
||||
expect(navigation.current?.getRootState()).toEqual({
|
||||
index: 1,
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo' },
|
||||
{
|
||||
key: 'bar',
|
||||
name: 'bar',
|
||||
params: {
|
||||
state: {
|
||||
routes: [{ name: 'bar-a' }, { name: 'bar-b' }],
|
||||
},
|
||||
},
|
||||
state: {
|
||||
index: 0,
|
||||
key: '4',
|
||||
routeNames: ['bar-a', 'bar-b'],
|
||||
routes: [
|
||||
{
|
||||
key: 'bar-a-2',
|
||||
name: 'bar-a',
|
||||
},
|
||||
{
|
||||
key: 'bar-b-3',
|
||||
name: 'bar-b',
|
||||
params: { some: 'stuff' },
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
},
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
});
|
||||
|
||||
act(() =>
|
||||
navigation.current?.navigate('bar', {
|
||||
state: {
|
||||
index: 2,
|
||||
routes: [
|
||||
{ key: '37', name: 'bar-b' },
|
||||
{ name: 'bar-b' },
|
||||
{ name: 'bar-a', params: { test: 18 } },
|
||||
],
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(first).toMatchInlineSnapshot(`"[bar-a, {\\"test\\":18}]"`);
|
||||
|
||||
expect(navigation.current?.getRootState()).toEqual({
|
||||
index: 1,
|
||||
key: '0',
|
||||
routeNames: ['foo', 'bar'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo' },
|
||||
{
|
||||
key: 'bar',
|
||||
name: 'bar',
|
||||
params: {
|
||||
state: {
|
||||
index: 2,
|
||||
routes: [
|
||||
{ key: '37', name: 'bar-b' },
|
||||
{ name: 'bar-b' },
|
||||
{ name: 'bar-a', params: { test: 18 } },
|
||||
],
|
||||
},
|
||||
},
|
||||
state: {
|
||||
index: 2,
|
||||
key: '7',
|
||||
routeNames: ['bar-a', 'bar-b'],
|
||||
routes: [
|
||||
{
|
||||
key: '37',
|
||||
name: 'bar-b',
|
||||
params: { some: 'stuff' },
|
||||
},
|
||||
{
|
||||
key: 'bar-b-5',
|
||||
name: 'bar-b',
|
||||
params: { some: 'stuff' },
|
||||
},
|
||||
{
|
||||
key: 'bar-a-6',
|
||||
name: 'bar-a',
|
||||
params: { test: 18 },
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
},
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
it('gives access to internal state', () => {
|
||||
const TestNavigator = (props: any): any => {
|
||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
@@ -22,6 +22,7 @@ it('sets options with options prop as an object', () => {
|
||||
const { state, descriptors } = useNavigationBuilder<
|
||||
NavigationState,
|
||||
any,
|
||||
{},
|
||||
{ title?: string },
|
||||
any
|
||||
>(MockRouter, props);
|
||||
@@ -67,6 +68,7 @@ it('sets options with options prop as a fuction', () => {
|
||||
const { state, descriptors } = useNavigationBuilder<
|
||||
NavigationState,
|
||||
any,
|
||||
{},
|
||||
{ title?: string },
|
||||
any
|
||||
>(MockRouter, props);
|
||||
@@ -113,6 +115,7 @@ it('sets options with screenOptions prop as an object', () => {
|
||||
const { state, descriptors } = useNavigationBuilder<
|
||||
NavigationState,
|
||||
any,
|
||||
{},
|
||||
{ title?: string },
|
||||
any
|
||||
>(MockRouter, props);
|
||||
@@ -173,6 +176,7 @@ it('sets options with screenOptions prop as a fuction', () => {
|
||||
const { state, descriptors } = useNavigationBuilder<
|
||||
NavigationState,
|
||||
any,
|
||||
{},
|
||||
{ title?: string },
|
||||
any
|
||||
>(MockRouter, props);
|
||||
@@ -245,6 +249,7 @@ it('sets initial options with setOptions', () => {
|
||||
const { state, descriptors } = useNavigationBuilder<
|
||||
NavigationState,
|
||||
any,
|
||||
{},
|
||||
{
|
||||
title?: string;
|
||||
color?: string;
|
||||
@@ -302,6 +307,7 @@ it('updates options with setOptions', () => {
|
||||
NavigationState,
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any
|
||||
>(MockRouter, props);
|
||||
const { render, options } = descriptors[state.routes[state.index].key];
|
||||
@@ -378,6 +384,7 @@ it("returns correct value for canGoBack when it's not overridden", () => {
|
||||
const { state, descriptors } = useNavigationBuilder<
|
||||
NavigationState,
|
||||
any,
|
||||
{},
|
||||
{ title?: string },
|
||||
any
|
||||
>(MockRouter, props);
|
||||
@@ -441,6 +448,7 @@ it(`returns false for canGoBack when current router doesn't handle GO_BACK`, ()
|
||||
NavigationState,
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any
|
||||
>(TestRouter, props);
|
||||
|
||||
@@ -491,6 +499,7 @@ it('returns true for canGoBack when current router handles GO_BACK', () => {
|
||||
const { state, descriptors } = useNavigationBuilder<
|
||||
NavigationState,
|
||||
any,
|
||||
{},
|
||||
{ title?: string },
|
||||
any
|
||||
>(ParentRouter, props);
|
||||
@@ -501,6 +510,7 @@ it('returns true for canGoBack when current router handles GO_BACK', () => {
|
||||
const { state, descriptors } = useNavigationBuilder<
|
||||
NavigationState,
|
||||
any,
|
||||
{},
|
||||
{ title?: string },
|
||||
any
|
||||
>(MockRouter, props);
|
||||
@@ -558,6 +568,7 @@ it('returns true for canGoBack when parent router handles GO_BACK', () => {
|
||||
const { state, descriptors } = useNavigationBuilder<
|
||||
NavigationState,
|
||||
any,
|
||||
{},
|
||||
{ title?: string },
|
||||
any
|
||||
>(OverrodeRouter, props);
|
||||
@@ -568,6 +579,7 @@ it('returns true for canGoBack when parent router handles GO_BACK', () => {
|
||||
const { state, descriptors } = useNavigationBuilder<
|
||||
NavigationState,
|
||||
any,
|
||||
{},
|
||||
{ title?: string },
|
||||
any
|
||||
>(MockRouter, props);
|
||||
|
||||
@@ -1178,3 +1178,149 @@ it("prevents removing by multiple screens with 'beforeRemove' event", () => {
|
||||
type: 'stack',
|
||||
});
|
||||
});
|
||||
|
||||
it("prevents removing a child screen with 'beforeRemove' event with 'resetRoot'", () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { state, descriptors } = useNavigationBuilder(StackRouter, props);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{state.routes.map((route) => descriptors[route.key].render())}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const onBeforeRemove = jest.fn();
|
||||
|
||||
let shouldPrevent = true;
|
||||
let shouldContinue = false;
|
||||
|
||||
const TestScreen = (props: any) => {
|
||||
React.useEffect(
|
||||
() =>
|
||||
props.navigation.addListener('beforeRemove', (e: any) => {
|
||||
onBeforeRemove();
|
||||
|
||||
if (shouldPrevent) {
|
||||
e.preventDefault();
|
||||
|
||||
if (shouldContinue) {
|
||||
props.navigation.dispatch(e.data.action);
|
||||
}
|
||||
}
|
||||
}),
|
||||
[props.navigation]
|
||||
);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const ref = React.createRef<NavigationContainerRef>();
|
||||
|
||||
const element = (
|
||||
<BaseNavigationContainer ref={ref} onStateChange={onStateChange}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo">{() => null}</Screen>
|
||||
<Screen name="bar">{() => null}</Screen>
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
<TestNavigator>
|
||||
<Screen name="qux" component={TestScreen} />
|
||||
<Screen name="lex">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
|
||||
act(() => ref.current?.navigate('baz'));
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).toBeCalledWith({
|
||||
index: 1,
|
||||
key: 'stack-2',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'foo-3', name: 'foo' },
|
||||
{
|
||||
key: 'baz-4',
|
||||
name: 'baz',
|
||||
state: {
|
||||
index: 0,
|
||||
key: 'stack-6',
|
||||
routeNames: ['qux', 'lex'],
|
||||
routes: [{ key: 'qux-7', name: 'qux' }],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
},
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
});
|
||||
|
||||
act(() =>
|
||||
ref.current?.resetRoot({
|
||||
index: 0,
|
||||
key: 'stack-2',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [{ key: 'foo-3', name: 'foo' }],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
})
|
||||
);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onBeforeRemove).toBeCalledTimes(1);
|
||||
|
||||
expect(ref.current?.getRootState()).toEqual({
|
||||
index: 1,
|
||||
key: 'stack-2',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'foo-3', name: 'foo' },
|
||||
{
|
||||
key: 'baz-4',
|
||||
name: 'baz',
|
||||
state: {
|
||||
index: 0,
|
||||
key: 'stack-6',
|
||||
routeNames: ['qux', 'lex'],
|
||||
routes: [{ key: 'qux-7', name: 'qux' }],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
},
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
});
|
||||
|
||||
shouldPrevent = false;
|
||||
|
||||
act(() =>
|
||||
ref.current?.resetRoot({
|
||||
index: 0,
|
||||
key: 'stack-2',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [{ key: 'foo-3', name: 'foo' }],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
})
|
||||
);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(2);
|
||||
expect(onStateChange).toBeCalledWith({
|
||||
index: 0,
|
||||
key: 'stack-2',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [{ key: 'foo-3', name: 'foo' }],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,56 +1,144 @@
|
||||
import type { PartialState, NavigationState } from '@react-navigation/routers';
|
||||
import type {
|
||||
Route,
|
||||
PartialRoute,
|
||||
ParamListBase,
|
||||
NavigationState,
|
||||
PartialState,
|
||||
CommonActions,
|
||||
} from '@react-navigation/routers';
|
||||
import type { PathConfig, PathConfigMap, NavigatorScreenParams } from './types';
|
||||
|
||||
type NavigateParams = {
|
||||
screen?: string;
|
||||
params?: NavigateParams;
|
||||
initial?: boolean;
|
||||
type ConfigItem = {
|
||||
initialRouteName?: string;
|
||||
screens?: Record<string, ConfigItem>;
|
||||
};
|
||||
|
||||
type NavigateAction = {
|
||||
type Options = { initialRouteName?: string; screens: PathConfigMap };
|
||||
|
||||
type NavigateAction<State extends NavigationState> = {
|
||||
type: 'NAVIGATE';
|
||||
payload: { name: string; params: NavigateParams };
|
||||
payload: {
|
||||
name: string;
|
||||
params?: NavigatorScreenParams<State>;
|
||||
};
|
||||
};
|
||||
|
||||
export default function getActionFromState(
|
||||
state: PartialState<NavigationState>
|
||||
): NavigateAction | undefined {
|
||||
if (state.routes.length === 0) {
|
||||
state: PartialState<NavigationState>,
|
||||
options?: Options
|
||||
): NavigateAction<NavigationState> | CommonActions.Action | undefined {
|
||||
// Create a normalized configs object which will be easier to use
|
||||
const normalizedConfig = options ? createNormalizedConfigItem(options) : {};
|
||||
|
||||
const routes =
|
||||
state.index != null ? state.routes.slice(0, state.index + 1) : state.routes;
|
||||
|
||||
if (routes.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Try to construct payload for a `NAVIGATE` action from the state
|
||||
// This lets us preserve the navigation state and not lose it
|
||||
let route = state.routes[state.routes.length - 1];
|
||||
if (
|
||||
!(
|
||||
(routes.length === 1 && routes[0].key === undefined) ||
|
||||
(routes.length === 2 &&
|
||||
routes[0].key === undefined &&
|
||||
routes[0].name === normalizedConfig?.initialRouteName &&
|
||||
routes[1].key === undefined)
|
||||
)
|
||||
) {
|
||||
return {
|
||||
type: 'RESET',
|
||||
payload: state,
|
||||
};
|
||||
}
|
||||
|
||||
let payload: { name: string; params: NavigateParams } = {
|
||||
name: route.name,
|
||||
params: { ...route.params },
|
||||
};
|
||||
const route = state.routes[state.index ?? state.routes.length - 1];
|
||||
|
||||
let current = route.state;
|
||||
let params = payload.params;
|
||||
let current: PartialState<NavigationState> | undefined = route?.state;
|
||||
let config: ConfigItem | undefined = normalizedConfig?.screens?.[route?.name];
|
||||
let params = { ...route.params } as NavigatorScreenParams<
|
||||
ParamListBase,
|
||||
NavigationState
|
||||
>;
|
||||
|
||||
let payload = route ? { name: route.name, params } : undefined;
|
||||
|
||||
while (current) {
|
||||
if (current.routes.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
route = current.routes[current.routes.length - 1];
|
||||
params.initial = current.routes.length === 1;
|
||||
params.screen = route.name;
|
||||
const routes =
|
||||
current.index != null
|
||||
? current.routes.slice(0, current.index + 1)
|
||||
: current.routes;
|
||||
|
||||
const route: Route<string> | PartialRoute<Route<string>> =
|
||||
routes[routes.length - 1];
|
||||
|
||||
// Explicitly set to override existing value when merging params
|
||||
Object.assign(params, {
|
||||
initial: undefined,
|
||||
screen: undefined,
|
||||
params: undefined,
|
||||
state: undefined,
|
||||
});
|
||||
|
||||
if (routes.length === 1 && routes[0].key === undefined) {
|
||||
params.initial = true;
|
||||
params.screen = route.name;
|
||||
} else if (
|
||||
routes.length === 2 &&
|
||||
routes[0].key === undefined &&
|
||||
routes[0].name === config?.initialRouteName &&
|
||||
routes[1].key === undefined
|
||||
) {
|
||||
params.initial = false;
|
||||
params.screen = route.name;
|
||||
} else {
|
||||
params.state = current;
|
||||
break;
|
||||
}
|
||||
|
||||
if (route.state) {
|
||||
params.params = { ...route.params };
|
||||
params = params.params;
|
||||
params = params.params as NavigatorScreenParams<
|
||||
ParamListBase,
|
||||
NavigationState
|
||||
>;
|
||||
} else {
|
||||
params.params = route.params;
|
||||
}
|
||||
|
||||
current = route.state;
|
||||
config = config?.screens?.[route.name];
|
||||
}
|
||||
|
||||
if (!payload) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to construct payload for a `NAVIGATE` action from the state
|
||||
// This lets us preserve the navigation state and not lose it
|
||||
return {
|
||||
type: 'NAVIGATE',
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
const createNormalizedConfigItem = (config: PathConfig | string) =>
|
||||
typeof config === 'object' && config != null
|
||||
? {
|
||||
initialRouteName: config.initialRouteName,
|
||||
screens:
|
||||
config.screens != null
|
||||
? createNormalizedConfigs(config.screens)
|
||||
: undefined,
|
||||
}
|
||||
: {};
|
||||
|
||||
const createNormalizedConfigs = (options: PathConfigMap) =>
|
||||
Object.entries(options).reduce<Record<string, ConfigItem>>((acc, [k, v]) => {
|
||||
acc[k] = createNormalizedConfigItem(v);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
@@ -3,11 +3,17 @@ import type {
|
||||
PartialState,
|
||||
NavigationState,
|
||||
} from '@react-navigation/routers';
|
||||
import { SUPPRESS_STATE_ACCESS_WARNING } from './useRouteCache';
|
||||
|
||||
export default function getFocusedRouteNameFromRoute(
|
||||
route: Partial<Route<string>> & { state?: PartialState<NavigationState> }
|
||||
): string | undefined {
|
||||
SUPPRESS_STATE_ACCESS_WARNING.value = true;
|
||||
|
||||
const state = route.state;
|
||||
|
||||
SUPPRESS_STATE_ACCESS_WARNING.value = false;
|
||||
|
||||
const params = route.params as { screen?: unknown } | undefined;
|
||||
|
||||
const routeName = state
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import queryString from 'query-string';
|
||||
import * as queryString from 'query-string';
|
||||
import type {
|
||||
NavigationState,
|
||||
PartialState,
|
||||
@@ -35,7 +35,7 @@ const getActiveRoute = (state: State): { name: string; params?: object } => {
|
||||
/**
|
||||
* Utility to serialize a navigation state object to a path string.
|
||||
*
|
||||
* Example:
|
||||
* @example
|
||||
* ```js
|
||||
* getPathFromState(
|
||||
* {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import escape from 'escape-string-regexp';
|
||||
import queryString from 'query-string';
|
||||
import * as queryString from 'query-string';
|
||||
import type {
|
||||
NavigationState,
|
||||
PartialState,
|
||||
@@ -37,7 +37,7 @@ type ResultState = PartialState<NavigationState> & {
|
||||
* Utility to parse a path string to initial state object accepted by the container.
|
||||
* This is useful for deep linking when we need to handle the incoming URL.
|
||||
*
|
||||
* Example:
|
||||
* @example
|
||||
* ```js
|
||||
* getStateFromPath(
|
||||
* '/chat/jane/42',
|
||||
@@ -122,7 +122,7 @@ export default function getStateFromPath(
|
||||
// If one of the patterns starts with the other, it's more exhaustive
|
||||
// So move it up
|
||||
if (a.pattern.startsWith(b.pattern)) {
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (b.pattern.startsWith(a.pattern)) {
|
||||
@@ -570,10 +570,7 @@ const createRouteObjects = (
|
||||
const value = allParams![p];
|
||||
|
||||
if (value) {
|
||||
acc[key] =
|
||||
config.parse && config.parse[key]
|
||||
? config.parse[key](value)
|
||||
: value;
|
||||
acc[key] = config.parse?.[key] ? config.parse[key](value) : value;
|
||||
}
|
||||
|
||||
return acc;
|
||||
|
||||
@@ -10,8 +10,9 @@ import type {
|
||||
} from '@react-navigation/routers';
|
||||
|
||||
export type DefaultNavigatorOptions<
|
||||
ScreenOptions extends {}
|
||||
> = DefaultRouterOptions & {
|
||||
ScreenOptions extends {},
|
||||
ParamList extends ParamListBase = ParamListBase
|
||||
> = DefaultRouterOptions<Extract<keyof ParamList, string>> & {
|
||||
/**
|
||||
* Children React Elements to extract the route configuration from.
|
||||
* Only `Screen` components are supported as children.
|
||||
@@ -23,7 +24,7 @@ export type DefaultNavigatorOptions<
|
||||
screenOptions?:
|
||||
| ScreenOptions
|
||||
| ((props: {
|
||||
route: RouteProp<ParamListBase, string>;
|
||||
route: RouteProp<ParamList, keyof ParamList>;
|
||||
navigation: any;
|
||||
}) => ScreenOptions);
|
||||
};
|
||||
@@ -237,6 +238,10 @@ export type NavigationContainerProps = {
|
||||
* Callback which is called with the latest navigation state when it changes.
|
||||
*/
|
||||
onStateChange?: (state: NavigationState | undefined) => void;
|
||||
/**
|
||||
* Callback which is called when an action is not handled.
|
||||
*/
|
||||
onUnhandledAction?: (action: NavigationAction) => void;
|
||||
/**
|
||||
* Whether this navigation container should be independent of parent containers.
|
||||
* If this is not set to `true`, this container cannot be nested inside another container.
|
||||
@@ -252,7 +257,7 @@ export type NavigationContainerProps = {
|
||||
export type NavigationProp<
|
||||
ParamList extends ParamListBase,
|
||||
RouteName extends keyof ParamList = string,
|
||||
State extends NavigationState = NavigationState,
|
||||
State extends NavigationState = NavigationState<ParamList>,
|
||||
ScreenOptions extends {} = {},
|
||||
EventMap extends EventMapBase = {}
|
||||
> = NavigationHelpersCommon<ParamList, State> & {
|
||||
@@ -277,20 +282,7 @@ export type NavigationProp<
|
||||
export type RouteProp<
|
||||
ParamList extends ParamListBase,
|
||||
RouteName extends keyof ParamList
|
||||
> = Omit<Route<Extract<RouteName, string>>, 'params'> &
|
||||
(undefined extends ParamList[RouteName]
|
||||
? Readonly<{
|
||||
/**
|
||||
* Params for this route
|
||||
*/
|
||||
params?: Readonly<ParamList[RouteName]>;
|
||||
}>
|
||||
: Readonly<{
|
||||
/**
|
||||
* Params for this route
|
||||
*/
|
||||
params: Readonly<ParamList[RouteName]>;
|
||||
}>);
|
||||
> = Route<Extract<RouteName, string>, ParamList[RouteName]>;
|
||||
|
||||
export type CompositeNavigationProp<
|
||||
A extends NavigationProp<ParamListBase, string, any, any>,
|
||||
@@ -498,14 +490,11 @@ export type TypedNavigator<
|
||||
* Navigator component which manages the child screens.
|
||||
*/
|
||||
Navigator: React.ComponentType<
|
||||
Omit<React.ComponentProps<Navigator>, keyof DefaultNavigatorOptions<any>> &
|
||||
Omit<DefaultNavigatorOptions<ScreenOptions>, 'initialRouteName'> & {
|
||||
/**
|
||||
* Name of the route to focus by on initial render.
|
||||
* If not specified, usually the first route is used.
|
||||
*/
|
||||
initialRouteName?: keyof ParamList;
|
||||
}
|
||||
Omit<
|
||||
React.ComponentProps<Navigator>,
|
||||
keyof DefaultNavigatorOptions<any, any>
|
||||
> &
|
||||
DefaultNavigatorOptions<ScreenOptions, ParamList>
|
||||
>;
|
||||
/**
|
||||
* Component used for specifying route configuration.
|
||||
@@ -515,6 +504,32 @@ export type TypedNavigator<
|
||||
) => null;
|
||||
};
|
||||
|
||||
export type NavigatorScreenParams<
|
||||
ParamList,
|
||||
State extends NavigationState = NavigationState
|
||||
> =
|
||||
| {
|
||||
screen?: never;
|
||||
params?: never;
|
||||
initial?: never;
|
||||
state: PartialState<State> | State | undefined;
|
||||
}
|
||||
| {
|
||||
[RouteName in keyof ParamList]: undefined extends ParamList[RouteName]
|
||||
? {
|
||||
screen: RouteName;
|
||||
params?: ParamList[RouteName];
|
||||
initial?: boolean;
|
||||
state?: never;
|
||||
}
|
||||
: {
|
||||
screen: RouteName;
|
||||
params: ParamList[RouteName];
|
||||
initial?: boolean;
|
||||
state?: never;
|
||||
};
|
||||
}[keyof ParamList];
|
||||
|
||||
export type PathConfig = {
|
||||
path?: string;
|
||||
exact?: boolean;
|
||||
|
||||
@@ -12,6 +12,7 @@ import NavigationBuilderContext, {
|
||||
} from './NavigationBuilderContext';
|
||||
import type { NavigationEventEmitter } from './useEventEmitter';
|
||||
import useNavigationCache from './useNavigationCache';
|
||||
import useRouteCache from './useRouteCache';
|
||||
import NavigationContext from './NavigationContext';
|
||||
import NavigationRouteContext from './NavigationRouteContext';
|
||||
import type {
|
||||
@@ -113,9 +114,11 @@ export default function useDescriptors<
|
||||
emitter,
|
||||
});
|
||||
|
||||
return state.routes.reduce<
|
||||
const routes = useRouteCache(state.routes);
|
||||
|
||||
return routes.reduce<
|
||||
Record<string, Descriptor<ParamListBase, string, State, ScreenOptions>>
|
||||
>((acc, route) => {
|
||||
>((acc, route, i) => {
|
||||
const screen = screens[route.name];
|
||||
const navigation = navigations[route.key];
|
||||
|
||||
@@ -151,6 +154,7 @@ export default function useDescriptors<
|
||||
navigation={navigation}
|
||||
route={route}
|
||||
screen={screen}
|
||||
routeState={state.routes[i].state}
|
||||
getState={getState}
|
||||
setState={setState}
|
||||
options={routeOptions}
|
||||
|
||||
@@ -69,7 +69,7 @@ export default function useEventEmitter<T extends Record<string, any>>(
|
||||
// Copy the current list of callbacks in case they are mutated during execution
|
||||
const callbacks =
|
||||
target !== undefined
|
||||
? items[target] && items[target].slice()
|
||||
? items[target]?.slice()
|
||||
: ([] as Listeners)
|
||||
.concat(...Object.keys(items).map((t) => items[t]))
|
||||
.filter((cb, i, self) => self.lastIndexOf(cb) === i);
|
||||
|
||||
@@ -1,32 +1,42 @@
|
||||
import * as React from 'react';
|
||||
import { useSubscription } from 'use-subscription';
|
||||
import { useState } from 'react';
|
||||
import useNavigation from './useNavigation';
|
||||
|
||||
/**
|
||||
* Hook to get the current focus state of the screen. Returns a `true` if screen is focused, otherwise `false`.
|
||||
* This can be used if a component needs to render something based on the focus state.
|
||||
* It uses `use-subscription` under the hood for safer use in concurrent mode.
|
||||
*/
|
||||
export default function useIsFocused(): boolean {
|
||||
const navigation = useNavigation();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const getCurrentValue = React.useCallback(navigation.isFocused, [navigation]);
|
||||
const subscribe = React.useCallback(
|
||||
(callback: () => void) => {
|
||||
const unsubscribeFocus = navigation.addListener('focus', callback);
|
||||
const [isFocused, setIsFocused] = useState(navigation.isFocused);
|
||||
|
||||
const unsubscribeBlur = navigation.addListener('blur', callback);
|
||||
const valueToReturn = navigation.isFocused();
|
||||
|
||||
return () => {
|
||||
unsubscribeFocus();
|
||||
unsubscribeBlur();
|
||||
};
|
||||
},
|
||||
[navigation]
|
||||
);
|
||||
if (isFocused !== valueToReturn) {
|
||||
// If the value has changed since the last render, we need to update it.
|
||||
// This could happen if we missed an update from the event listeners during re-render.
|
||||
// React will process this update immediately, so the old subscription value won't be committed.
|
||||
// It is still nice to avoid returning a mismatched value though, so let's override the return value.
|
||||
// This is the same logic as in https://github.com/facebook/react/tree/master/packages/use-subscription
|
||||
setIsFocused(valueToReturn);
|
||||
}
|
||||
|
||||
return useSubscription({
|
||||
getCurrentValue,
|
||||
subscribe,
|
||||
});
|
||||
React.useEffect(() => {
|
||||
const unsubscribeFocus = navigation.addListener('focus', () =>
|
||||
setIsFocused(true)
|
||||
);
|
||||
|
||||
const unsubscribeBlur = navigation.addListener('blur', () =>
|
||||
setIsFocused(false)
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribeFocus();
|
||||
unsubscribeBlur();
|
||||
};
|
||||
}, [navigation]);
|
||||
|
||||
React.useDebugValue(valueToReturn);
|
||||
|
||||
return valueToReturn;
|
||||
}
|
||||
|
||||
@@ -23,30 +23,27 @@ import useFocusEvents from './useFocusEvents';
|
||||
import useOnRouteFocus from './useOnRouteFocus';
|
||||
import useChildListeners from './useChildListeners';
|
||||
import useFocusedListenersChildrenAdapter from './useFocusedListenersChildrenAdapter';
|
||||
import useKeyedChildListeners from './useKeyedChildListeners';
|
||||
import useOnGetState from './useOnGetState';
|
||||
import useScheduleUpdate from './useScheduleUpdate';
|
||||
import useCurrentRender from './useCurrentRender';
|
||||
import isArrayEqual from './isArrayEqual';
|
||||
import {
|
||||
DefaultNavigatorOptions,
|
||||
RouteConfig,
|
||||
PrivateValueStore,
|
||||
EventMapBase,
|
||||
EventMapCore,
|
||||
NavigatorScreenParams,
|
||||
} from './types';
|
||||
import useKeyedChildListeners from './useKeyedChildListeners';
|
||||
import useOnGetState from './useOnGetState';
|
||||
import useScheduleUpdate from './useScheduleUpdate';
|
||||
import useCurrentRender from './useCurrentRender';
|
||||
import isArrayEqual from './isArrayEqual';
|
||||
|
||||
// This is to make TypeScript compiler happy
|
||||
// eslint-disable-next-line babel/no-unused-expressions
|
||||
PrivateValueStore;
|
||||
|
||||
type NavigatorRoute = {
|
||||
type NavigatorRoute<State extends NavigationState> = {
|
||||
key: string;
|
||||
params?: {
|
||||
screen?: string;
|
||||
params?: object;
|
||||
initial?: boolean;
|
||||
};
|
||||
params?: NavigatorScreenParams<ParamListBase, State>;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -95,8 +92,8 @@ const getRouteConfigsFromChildren = <
|
||||
throw new Error(
|
||||
`A navigator can only contain 'Screen' components as its direct children (found '${
|
||||
// @ts-expect-error: child can be any type and we're accessing it safely, but TS doesn't understand it
|
||||
child.type && child.type.name ? child.type.name : String(child)
|
||||
}')`
|
||||
child.type?.name ? child.type.name : String(child)
|
||||
}'). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.`
|
||||
);
|
||||
}, []);
|
||||
|
||||
@@ -182,6 +179,7 @@ const getRouteConfigsFromChildren = <
|
||||
export default function useNavigationBuilder<
|
||||
State extends NavigationState,
|
||||
RouterOptions extends DefaultRouterOptions,
|
||||
ActionHelpers extends Record<string, () => void>,
|
||||
ScreenOptions extends {},
|
||||
EventMap extends Record<string, any>
|
||||
>(
|
||||
@@ -191,20 +189,15 @@ export default function useNavigationBuilder<
|
||||
const navigatorKey = useRegisterNavigator();
|
||||
|
||||
const route = React.useContext(NavigationRouteContext) as
|
||||
| NavigatorRoute
|
||||
| NavigatorRoute<State>
|
||||
| undefined;
|
||||
|
||||
const previousNestedParamsRef = React.useRef(route?.params);
|
||||
|
||||
React.useEffect(() => {
|
||||
previousNestedParamsRef.current = route?.params;
|
||||
}, [route]);
|
||||
|
||||
const { children, ...rest } = options;
|
||||
const { current: router } = React.useRef<Router<State, any>>(
|
||||
createRouter({
|
||||
...((rest as unknown) as RouterOptions),
|
||||
...(route?.params &&
|
||||
route.params.state == null &&
|
||||
route.params.initial !== false &&
|
||||
typeof route.params.screen === 'string'
|
||||
? { initialRouteName: route.params.screen }
|
||||
@@ -239,7 +232,9 @@ export default function useNavigationBuilder<
|
||||
(acc, curr) => {
|
||||
const { initialParams } = screens[curr];
|
||||
const initialParamsFromParams =
|
||||
route?.params?.initial !== false && route?.params?.screen === curr
|
||||
route?.params?.state == null &&
|
||||
route?.params?.initial !== false &&
|
||||
route?.params?.screen === curr
|
||||
? route.params.params
|
||||
: undefined;
|
||||
|
||||
@@ -287,7 +282,10 @@ export default function useNavigationBuilder<
|
||||
// We also need to re-initialize it if the state passed from parent was changed (maybe due to reset)
|
||||
// Otherwise assume that the state was provided as initial state
|
||||
// So we need to rehydrate it to make it usable
|
||||
if (currentState === undefined || !isStateValid(currentState)) {
|
||||
if (
|
||||
(currentState === undefined || !isStateValid(currentState)) &&
|
||||
route?.params?.state == null
|
||||
) {
|
||||
return [
|
||||
router.getInitialState({
|
||||
routeNames,
|
||||
@@ -297,10 +295,13 @@ export default function useNavigationBuilder<
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
router.getRehydratedState(currentState as PartialState<State>, {
|
||||
routeNames,
|
||||
routeParamList,
|
||||
}),
|
||||
router.getRehydratedState(
|
||||
route?.params?.state ?? (currentState as PartialState<State>),
|
||||
{
|
||||
routeNames,
|
||||
routeParamList,
|
||||
}
|
||||
),
|
||||
false,
|
||||
];
|
||||
}
|
||||
@@ -331,21 +332,41 @@ export default function useNavigationBuilder<
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
typeof route?.params?.screen === 'string' &&
|
||||
(route.params !== previousNestedParamsRef.current ||
|
||||
(route.params.initial === false && isFirstStateInitialization))
|
||||
) {
|
||||
// If the route was updated with new name and/or params, we should navigate there
|
||||
const previousNestedParamsRef = React.useRef(route?.params);
|
||||
|
||||
React.useEffect(() => {
|
||||
previousNestedParamsRef.current = route?.params;
|
||||
}, [route?.params]);
|
||||
|
||||
if (route?.params) {
|
||||
const previousParams = previousNestedParamsRef.current;
|
||||
|
||||
let action: CommonActions.Action | undefined;
|
||||
|
||||
if (
|
||||
typeof route.params.state === 'object' &&
|
||||
route.params.state != null &&
|
||||
route.params.state !== previousParams?.state
|
||||
) {
|
||||
// If the route was updated with new state, we should reset to it
|
||||
action = CommonActions.reset(route.params.state);
|
||||
} else if (
|
||||
typeof route.params.screen === 'string' &&
|
||||
((route.params.initial === false && isFirstStateInitialization) ||
|
||||
route.params !== previousParams)
|
||||
) {
|
||||
// FIXME: Since params are merged, `route.params.params` might contain params from an older route
|
||||
// If the route was updated with new screen name and/or params, we should navigate there
|
||||
action = CommonActions.navigate(route.params.screen, route.params.params);
|
||||
}
|
||||
|
||||
// The update should be limited to current navigator only, so we call the router manually
|
||||
const updatedState = router.getStateForAction(
|
||||
nextState,
|
||||
CommonActions.navigate(route.params.screen, route.params.params),
|
||||
{
|
||||
routeNames,
|
||||
routeParamList,
|
||||
}
|
||||
);
|
||||
const updatedState = action
|
||||
? router.getStateForAction(nextState, action, {
|
||||
routeNames,
|
||||
routeParamList,
|
||||
})
|
||||
: null;
|
||||
|
||||
nextState =
|
||||
updatedState !== null
|
||||
@@ -484,7 +505,12 @@ export default function useNavigationBuilder<
|
||||
setState,
|
||||
});
|
||||
|
||||
const navigation = useNavigationHelpers<State, NavigationAction, EventMap>({
|
||||
const navigation = useNavigationHelpers<
|
||||
State,
|
||||
ActionHelpers,
|
||||
NavigationAction,
|
||||
EventMap
|
||||
>({
|
||||
onAction,
|
||||
getState,
|
||||
emitter,
|
||||
|
||||
@@ -31,6 +31,7 @@ type Options<State extends NavigationState, Action extends NavigationAction> = {
|
||||
*/
|
||||
export default function useNavigationHelpers<
|
||||
State extends NavigationState,
|
||||
ActionHelpers extends Record<string, () => void>,
|
||||
Action extends NavigationAction,
|
||||
EventMap extends Record<string, any>
|
||||
>({ onAction, getState, emitter, router }: Options<State, Action>) {
|
||||
@@ -85,7 +86,8 @@ export default function useNavigationHelpers<
|
||||
dangerouslyGetParent: () => parentNavigationHelpers as any,
|
||||
dangerouslyGetState: getState,
|
||||
} as NavigationHelpers<ParamListBase, EventMap> &
|
||||
(NavigationProp<ParamListBase, string, any, any, any> | undefined);
|
||||
(NavigationProp<ParamListBase, string, any, any, any> | undefined) &
|
||||
ActionHelpers;
|
||||
}, [
|
||||
emitter.emit,
|
||||
getState,
|
||||
|
||||
@@ -90,18 +90,11 @@ export default function useOnAction({
|
||||
onDispatchAction(action, state === result);
|
||||
|
||||
if (state !== result) {
|
||||
const nextRouteKeys = (result.routes as any[]).map(
|
||||
(route: { key?: string }) => route.key
|
||||
);
|
||||
|
||||
const removedRoutes = state.routes.filter(
|
||||
(route) => !nextRouteKeys.includes(route.key)
|
||||
);
|
||||
|
||||
const isPrevented = shouldPreventRemove(
|
||||
emitter,
|
||||
beforeRemoveListeners,
|
||||
removedRoutes,
|
||||
state.routes,
|
||||
result.routes,
|
||||
action
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import type {
|
||||
NavigationState,
|
||||
Route,
|
||||
NavigationAction,
|
||||
} from '@react-navigation/routers';
|
||||
import NavigationBuilderContext, {
|
||||
@@ -22,11 +21,16 @@ const VISITED_ROUTE_KEYS = Symbol('VISITED_ROUTE_KEYS');
|
||||
export const shouldPreventRemove = (
|
||||
emitter: NavigationEventEmitter<EventMapCore<any>>,
|
||||
beforeRemoveListeners: Record<string, ChildBeforeRemoveListener | undefined>,
|
||||
routes: Route<string>[],
|
||||
currentRoutes: { key: string }[],
|
||||
nextRoutes: { key?: string | undefined }[],
|
||||
action: NavigationAction
|
||||
) => {
|
||||
const nextRouteKeys = nextRoutes.map((route) => route.key);
|
||||
|
||||
// Call these in reverse order so last screens handle the event first
|
||||
const reversedRoutes = [...routes].reverse();
|
||||
const removedRoutes = currentRoutes
|
||||
.filter((route) => !nextRouteKeys.includes(route.key))
|
||||
.reverse();
|
||||
|
||||
const visitedRouteKeys: Set<string> =
|
||||
// @ts-expect-error: add this property to mark that we've already emitted this action
|
||||
@@ -37,7 +41,7 @@ export const shouldPreventRemove = (
|
||||
[VISITED_ROUTE_KEYS]: visitedRouteKeys,
|
||||
};
|
||||
|
||||
for (const route of reversedRoutes) {
|
||||
for (const route of removedRoutes) {
|
||||
if (visitedRouteKeys.has(route.key)) {
|
||||
// Skip if we've already emitted this action for this screen
|
||||
continue;
|
||||
@@ -85,6 +89,7 @@ export default function useOnPreventRemove({
|
||||
emitter,
|
||||
beforeRemoveListeners,
|
||||
state.routes,
|
||||
[],
|
||||
action
|
||||
);
|
||||
});
|
||||
|
||||
61
packages/core/src/useRouteCache.tsx
Normal file
61
packages/core/src/useRouteCache.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import * as React from 'react';
|
||||
import type {
|
||||
ParamListBase,
|
||||
NavigationState,
|
||||
Route,
|
||||
} from '@react-navigation/routers';
|
||||
import type { RouteProp } from './types';
|
||||
|
||||
type RouteCache = Map<Route<string>, RouteProp<ParamListBase, string>>;
|
||||
|
||||
/**
|
||||
* Utilites such as `getFocusedRouteNameFromRoute` need to access state.
|
||||
* So we need a way to suppress the warning for those use cases.
|
||||
* This is fine since they are internal utilities and this is not public API.
|
||||
*/
|
||||
export const SUPPRESS_STATE_ACCESS_WARNING = { value: false };
|
||||
|
||||
/**
|
||||
* Hook to cache route props for each screen in the navigator.
|
||||
* This lets add warnings and modifications to the route object but keep references between renders.
|
||||
*/
|
||||
export default function useRouteCache<State extends NavigationState>(
|
||||
routes: State['routes']
|
||||
) {
|
||||
// Cache object which holds route objects for each screen
|
||||
const cache = React.useMemo(() => ({ current: new Map() as RouteCache }), []);
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
// We don't want the overhead of creating extra maps every render in prod
|
||||
return routes;
|
||||
}
|
||||
|
||||
cache.current = routes.reduce((acc, route) => {
|
||||
const previous = cache.current.get(route);
|
||||
|
||||
if (previous) {
|
||||
// If a cached route object already exists, reuse it
|
||||
acc.set(route, previous);
|
||||
} else {
|
||||
const proxy = { ...route };
|
||||
|
||||
Object.defineProperty(proxy, 'state', {
|
||||
get() {
|
||||
if (!SUPPRESS_STATE_ACCESS_WARNING.value) {
|
||||
console.warn(
|
||||
"Accessing the 'state' property of the 'route' object is not supported. If you want to get the focused route name, use the 'getFocusedRouteNameFromRoute' helper instead: https://reactnavigation.org/docs/screen-options-resolution/#setting-parent-screen-options-based-on-child-navigators-state"
|
||||
);
|
||||
}
|
||||
|
||||
return route.state;
|
||||
},
|
||||
});
|
||||
|
||||
acc.set(route, proxy);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, new Map() as RouteCache);
|
||||
|
||||
return Array.from(cache.current.values());
|
||||
}
|
||||
@@ -68,5 +68,7 @@ export default function useSyncState<T>(initialState?: (() => T) | T) {
|
||||
|
||||
const state = stateRef.current;
|
||||
|
||||
React.useDebugValue(state);
|
||||
|
||||
return [state, getState, setState, scheduleUpdate, flushUpdates] as const;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,86 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.1.15](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.14...@react-navigation/devtools@5.1.15) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.14](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.13...@react-navigation/devtools@5.1.14) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.13](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.12...@react-navigation/devtools@5.1.13) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.12](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.11...@react-navigation/devtools@5.1.12) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.11](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.10...@react-navigation/devtools@5.1.11) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.10](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.9...@react-navigation/devtools@5.1.10) (2020-10-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.9](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.8...@react-navigation/devtools@5.1.9) (2020-10-28)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.8](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.7...@react-navigation/devtools@5.1.8) (2020-10-24)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.6...@react-navigation/devtools@5.1.7) (2020-10-07)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.5...@react-navigation/devtools@5.1.6) (2020-09-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.4...@react-navigation/devtools@5.1.5) (2020-08-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/devtools",
|
||||
"description": "Developer tools for React Navigation",
|
||||
"version": "5.1.5",
|
||||
"version": "5.1.15",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native",
|
||||
@@ -36,17 +36,17 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/core": "^5.12.3",
|
||||
"deep-equal": "^2.0.3"
|
||||
"@react-navigation/core": "^5.14.1",
|
||||
"deep-equal": "^2.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@testing-library/react-native": "^7.0.1",
|
||||
"@testing-library/react-native": "^7.1.0",
|
||||
"@types/deep-equal": "^1.0.1",
|
||||
"@types/react": "^16.9.44",
|
||||
"@types/react": "^16.9.53",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.9.0",
|
||||
"typescript": "^3.9.7"
|
||||
"react": "~16.13.1",
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
|
||||
@@ -3,6 +3,106 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.10.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.10.6...@react-navigation/drawer@5.10.7) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.10.5...@react-navigation/drawer@5.10.6) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.10.4...@react-navigation/drawer@5.10.5) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.10.3...@react-navigation/drawer@5.10.4) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.10.2...@react-navigation/drawer@5.10.3) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.10.1...@react-navigation/drawer@5.10.2) (2020-10-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.10.0...@react-navigation/drawer@5.10.1) (2020-10-28)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.10.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.9.3...@react-navigation/drawer@5.10.0) (2020-10-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add optional screens per navigator ([#8805](https://github.com/react-navigation/react-navigation/issues/8805)) ([7196889](https://github.com/react-navigation/react-navigation/commit/7196889bf1218eb6a736d9475e33a909c2248c3b))
|
||||
* improve types for navigation state ([#8980](https://github.com/react-navigation/react-navigation/issues/8980)) ([7dc2f58](https://github.com/react-navigation/react-navigation/commit/7dc2f5832e371473f3263c01ab39824eb9e2057d))
|
||||
* update helper types to have navigator specific methods ([f51086e](https://github.com/react-navigation/react-navigation/commit/f51086edea42f2382dac8c6914aac8574132114b))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.9.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.9.2...@react-navigation/drawer@5.9.3) (2020-10-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use route keys instead of index for lazy load ([c49dab3](https://github.com/react-navigation/react-navigation/commit/c49dab31b2c63a1735f0ed0a1936ecf7bbcd8b13))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.9.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.9.1...@react-navigation/drawer@5.9.2) (2020-09-28)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.9.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.9.0...@react-navigation/drawer@5.9.1) (2020-09-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add flex: 1 to DrawerItem style ([#8701](https://github.com/react-navigation/react-navigation/issues/8701)) ([1c4bd68](https://github.com/react-navigation/react-navigation/commit/1c4bd6813bc6f5151fd2d99a0245331ff5631c38)), closes [/github.com/react-navigation/react-navigation/blob/main/packages/drawer/src/views/DrawerItem.tsx#L167](https://github.com//github.com/react-navigation/react-navigation/blob/main/packages/drawer/src/views/DrawerItem.tsx/issues/L167)
|
||||
* cleanly removing event listeners in useWindowDimensions ([#8866](https://github.com/react-navigation/react-navigation/issues/8866)) ([dcbfe52](https://github.com/react-navigation/react-navigation/commit/dcbfe52667d14b0dbed6a353675d02189f7f7b5b))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.9.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.8.7...@react-navigation/drawer@5.9.0) (2020-08-04)
|
||||
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
Drawer navigator for React Navigation following Material Design guidelines.
|
||||
|
||||
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/drawer-navigator.html).
|
||||
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/drawer-navigator/).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/drawer",
|
||||
"description": "Drawer navigator component with animated transitions and gesturess",
|
||||
"version": "5.9.0",
|
||||
"version": "5.10.7",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -21,7 +21,7 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/react-navigation/react-navigation/issues"
|
||||
},
|
||||
"homepage": "https://reactnavigation.org/docs/drawer-navigator.html",
|
||||
"homepage": "https://reactnavigation.org/docs/drawer-navigator/",
|
||||
"main": "lib/commonjs/index.js",
|
||||
"react-native": "src/index.tsx",
|
||||
"source": "src/index.tsx",
|
||||
@@ -41,23 +41,23 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"color": "^3.1.2",
|
||||
"react-native-iphone-x-helper": "^1.2.1"
|
||||
"color": "^3.1.3",
|
||||
"react-native-iphone-x-helper": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@react-navigation/native": "^5.7.3",
|
||||
"@testing-library/react-native": "^7.0.1",
|
||||
"@types/react": "^16.9.44",
|
||||
"@types/react-native": "^0.63.4",
|
||||
"@react-navigation/native": "^5.8.7",
|
||||
"@testing-library/react-native": "^7.1.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-native": "^0.63.30",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.62.2",
|
||||
"react-native-gesture-handler": "^1.7.0",
|
||||
"react-native-reanimated": "^1.10.1",
|
||||
"react-native-safe-area-context": "~3.1.1",
|
||||
"react-native-screens": "^2.9.0",
|
||||
"typescript": "^3.9.7"
|
||||
"react": "~16.13.1",
|
||||
"react-native": "~0.63.2",
|
||||
"react-native-gesture-handler": "~1.7.0",
|
||||
"react-native-reanimated": "~1.13.0",
|
||||
"react-native-safe-area-context": "3.1.4",
|
||||
"react-native-screens": "~2.10.1",
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.5",
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
DrawerNavigationState,
|
||||
DrawerRouterOptions,
|
||||
DrawerRouter,
|
||||
DrawerActionHelpers,
|
||||
ParamListBase,
|
||||
} from '@react-navigation/native';
|
||||
|
||||
import DrawerView from '../views/DrawerView';
|
||||
@@ -28,8 +30,9 @@ function DrawerNavigator({
|
||||
...rest
|
||||
}: Props) {
|
||||
const { state, descriptors, navigation } = useNavigationBuilder<
|
||||
DrawerNavigationState,
|
||||
DrawerNavigationState<ParamListBase>,
|
||||
DrawerRouterOptions,
|
||||
DrawerActionHelpers<ParamListBase>,
|
||||
DrawerNavigationOptions,
|
||||
DrawerNavigationEventMap
|
||||
>(DrawerRouter, {
|
||||
@@ -51,7 +54,7 @@ function DrawerNavigator({
|
||||
}
|
||||
|
||||
export default createNavigatorFactory<
|
||||
DrawerNavigationState,
|
||||
DrawerNavigationState<ParamListBase>,
|
||||
DrawerNavigationOptions,
|
||||
DrawerNavigationEventMap,
|
||||
typeof DrawerNavigator
|
||||
|
||||
@@ -86,6 +86,12 @@ export type DrawerNavigationConfig<T = DrawerContentOptions> = {
|
||||
* You can pass a custom background color for a drawer or a custom width here.
|
||||
*/
|
||||
drawerStyle?: StyleProp<ViewStyle>;
|
||||
/**
|
||||
* Whether inactive screens should be detached from the view hierarchy to save memory.
|
||||
* Make sure to call `enableScreens` from `react-native-screens` to make it work.
|
||||
* Defaults to `true`.
|
||||
*/
|
||||
detachInactiveScreens?: boolean;
|
||||
};
|
||||
|
||||
export type DrawerNavigationOptions = {
|
||||
@@ -136,7 +142,7 @@ export type DrawerNavigationOptions = {
|
||||
};
|
||||
|
||||
export type DrawerContentComponentProps<T = DrawerContentOptions> = T & {
|
||||
state: DrawerNavigationState;
|
||||
state: DrawerNavigationState<ParamListBase>;
|
||||
navigation: DrawerNavigationHelpers;
|
||||
descriptors: DrawerDescriptorMap;
|
||||
/**
|
||||
@@ -195,7 +201,8 @@ export type DrawerNavigationEventMap = {
|
||||
export type DrawerNavigationHelpers = NavigationHelpers<
|
||||
ParamListBase,
|
||||
DrawerNavigationEventMap
|
||||
>;
|
||||
> &
|
||||
DrawerActionHelpers<ParamListBase>;
|
||||
|
||||
export type DrawerNavigationProp<
|
||||
ParamList extends ParamListBase,
|
||||
@@ -203,7 +210,7 @@ export type DrawerNavigationProp<
|
||||
> = NavigationProp<
|
||||
ParamList,
|
||||
RouteName,
|
||||
DrawerNavigationState,
|
||||
DrawerNavigationState<ParamList>,
|
||||
DrawerNavigationOptions,
|
||||
DrawerNavigationEventMap
|
||||
> &
|
||||
@@ -220,7 +227,7 @@ export type DrawerScreenProps<
|
||||
export type DrawerDescriptor = Descriptor<
|
||||
ParamListBase,
|
||||
string,
|
||||
DrawerNavigationState,
|
||||
DrawerNavigationState<ParamListBase>,
|
||||
DrawerNavigationOptions
|
||||
>;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ export default function useWindowDimensions() {
|
||||
|
||||
Dimensions.addEventListener('change', onChange);
|
||||
|
||||
return () => Dimensions.addEventListener('change', onChange);
|
||||
return () => Dimensions.removeEventListener('change', onChange);
|
||||
}, []);
|
||||
|
||||
return dimensions;
|
||||
|
||||
@@ -205,6 +205,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
label: {
|
||||
marginRight: 32,
|
||||
flex: 1,
|
||||
},
|
||||
button: {
|
||||
display: 'flex',
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
CommonActions,
|
||||
DrawerActions,
|
||||
DrawerNavigationState,
|
||||
ParamListBase,
|
||||
useLinkBuilder,
|
||||
} from '@react-navigation/native';
|
||||
import DrawerItem from './DrawerItem';
|
||||
@@ -13,7 +14,7 @@ import type {
|
||||
} from '../types';
|
||||
|
||||
type Props = Omit<DrawerContentOptions, 'contentContainerStyle' | 'style'> & {
|
||||
state: DrawerNavigationState;
|
||||
state: DrawerNavigationState<ParamListBase>;
|
||||
navigation: DrawerNavigationHelpers;
|
||||
descriptors: DrawerDescriptorMap;
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
DrawerNavigationState,
|
||||
DrawerActions,
|
||||
useTheme,
|
||||
ParamListBase,
|
||||
} from '@react-navigation/native';
|
||||
|
||||
import { GestureHandlerRootView } from './GestureHandler';
|
||||
@@ -31,7 +32,7 @@ import type {
|
||||
} from '../types';
|
||||
|
||||
type Props = DrawerNavigationConfig & {
|
||||
state: DrawerNavigationState;
|
||||
state: DrawerNavigationState<ParamListBase>;
|
||||
navigation: DrawerNavigationHelpers;
|
||||
descriptors: DrawerDescriptorMap;
|
||||
};
|
||||
@@ -46,7 +47,7 @@ const getDefaultDrawerWidth = ({
|
||||
/*
|
||||
* Default drawer width is screen width - header height
|
||||
* with a max width of 280 on mobile and 320 on tablet
|
||||
* https://material.io/guidelines/patterns/navigation-drawer.html
|
||||
* https://material.io/components/navigation-drawer
|
||||
*/
|
||||
const smallerAxisSize = Math.min(height, width);
|
||||
const isLandscape = width > height;
|
||||
@@ -82,8 +83,9 @@ export default function DrawerView({
|
||||
gestureHandlerProps,
|
||||
minSwipeDistance,
|
||||
sceneContainerStyle,
|
||||
detachInactiveScreens = true,
|
||||
}: Props) {
|
||||
const [loaded, setLoaded] = React.useState([state.index]);
|
||||
const [loaded, setLoaded] = React.useState([state.routes[state.index].key]);
|
||||
const dimensions = useWindowDimensions();
|
||||
|
||||
const { colors } = useTheme();
|
||||
@@ -129,8 +131,10 @@ export default function DrawerView({
|
||||
return () => subscription?.remove();
|
||||
}, [handleDrawerClose, isDrawerOpen, navigation, state.key]);
|
||||
|
||||
if (!loaded.includes(state.index)) {
|
||||
setLoaded([...loaded, state.index]);
|
||||
const focusedRouteKey = state.routes[state.index].key;
|
||||
|
||||
if (!loaded.includes(focusedRouteKey)) {
|
||||
setLoaded([...loaded, focusedRouteKey]);
|
||||
}
|
||||
|
||||
const renderNavigationView = ({ progress }: any) => {
|
||||
@@ -149,7 +153,8 @@ export default function DrawerView({
|
||||
|
||||
const renderContent = () => {
|
||||
return (
|
||||
<ScreenContainer style={styles.content}>
|
||||
// @ts-ignore
|
||||
<ScreenContainer enabled={detachInactiveScreens} style={styles.content}>
|
||||
{state.routes.map((route, index) => {
|
||||
const descriptor = descriptors[route.key];
|
||||
const { unmountOnBlur } = descriptor.options;
|
||||
@@ -159,7 +164,7 @@ export default function DrawerView({
|
||||
return null;
|
||||
}
|
||||
|
||||
if (lazy && !loaded.includes(index) && !isFocused) {
|
||||
if (lazy && !loaded.includes(route.key) && !isFocused) {
|
||||
// Don't render a screen if we've never navigated to it
|
||||
return null;
|
||||
}
|
||||
@@ -169,6 +174,7 @@ export default function DrawerView({
|
||||
key={route.key}
|
||||
style={[StyleSheet.absoluteFill, { opacity: isFocused ? 1 : 0 }]}
|
||||
isVisible={isFocused}
|
||||
enabled={detachInactiveScreens}
|
||||
>
|
||||
{descriptor.render()}
|
||||
</ResourceSavingScene>
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import * as React from 'react';
|
||||
import { Platform, StyleSheet, View } from 'react-native';
|
||||
import { Screen, screensEnabled } from 'react-native-screens';
|
||||
import {
|
||||
Screen,
|
||||
screensEnabled,
|
||||
// @ts-ignore
|
||||
shouldUseActivityState,
|
||||
} from 'react-native-screens';
|
||||
|
||||
type Props = {
|
||||
isVisible: boolean;
|
||||
children: React.ReactNode;
|
||||
enabled: boolean;
|
||||
style?: any;
|
||||
};
|
||||
|
||||
@@ -16,8 +22,17 @@ export default class ResourceSavingScene extends React.Component<Props> {
|
||||
if (screensEnabled?.() && Platform.OS !== 'web') {
|
||||
const { isVisible, ...rest } = this.props;
|
||||
|
||||
// @ts-expect-error: stackPresentation is incorrectly marked as required
|
||||
return <Screen active={isVisible ? 1 : 0} {...rest} />;
|
||||
if (shouldUseActivityState) {
|
||||
return (
|
||||
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
||||
<Screen activityState={isVisible ? 2 : 0} {...rest} />
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
||||
<Screen active={isVisible ? 1 : 0} {...rest} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const { isVisible, children, style, ...rest } = this.props;
|
||||
|
||||
@@ -3,6 +3,99 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.3.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.6...@react-navigation/material-bottom-tabs@5.3.7) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.5...@react-navigation/material-bottom-tabs@5.3.6) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.4...@react-navigation/material-bottom-tabs@5.3.5) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.3...@react-navigation/material-bottom-tabs@5.3.4) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.2...@react-navigation/material-bottom-tabs@5.3.3) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.1...@react-navigation/material-bottom-tabs@5.3.2) (2020-10-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.0...@react-navigation/material-bottom-tabs@5.3.1) (2020-10-28)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.3.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.2.19...@react-navigation/material-bottom-tabs@5.3.0) (2020-10-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* improve types for navigation state ([#8980](https://github.com/react-navigation/react-navigation/issues/8980)) ([7dc2f58](https://github.com/react-navigation/react-navigation/commit/7dc2f5832e371473f3263c01ab39824eb9e2057d))
|
||||
* make react-native-vector-icons optional ([#8936](https://github.com/react-navigation/react-navigation/issues/8936)) ([90ebfc4](https://github.com/react-navigation/react-navigation/commit/90ebfc40b387b209031e6275aaa0be95192f7d04)), closes [/github.com/callstack/react-native-paper/blob/4b26429c49053eaa4c3e0fae208639e01093fa87/src/components/MaterialCommunityIcon.tsx#L14](https://github.com//github.com/callstack/react-native-paper/blob/4b26429c49053eaa4c3e0fae208639e01093fa87/src/components/MaterialCommunityIcon.tsx/issues/L14) [#8821](https://github.com/react-navigation/react-navigation/issues/8821)
|
||||
* update helper types to have navigator specific methods ([f51086e](https://github.com/react-navigation/react-navigation/commit/f51086edea42f2382dac8c6914aac8574132114b))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.19](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.2.18...@react-navigation/material-bottom-tabs@5.2.19) (2020-10-07)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.18](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.2.17...@react-navigation/material-bottom-tabs@5.2.18) (2020-09-28)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.17](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.2.16...@react-navigation/material-bottom-tabs@5.2.17) (2020-09-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.16](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.2.15...@react-navigation/material-bottom-tabs@5.2.16) (2020-08-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# `@react-navigation/material-bottom-tabs`
|
||||
|
||||
React Navigation integration for [bottom navigation](https://material.io/design/components/bottom-navigation.html) component from [`react-native-paper`](https://callstack.github.io/react-native-paper/bottom-navigation.html).
|
||||
React Navigation integration for [bottom navigation](https://material.io/components/bottom-navigation) component from [`react-native-paper`](https://callstack.github.io/react-native-paper/bottom-navigation.html).
|
||||
|
||||
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/material-bottom-tab-navigator.html).
|
||||
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/material-bottom-tab-navigator/).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/material-bottom-tabs",
|
||||
"description": "Integration for bottom navigation component from react-native-paper",
|
||||
"version": "5.2.16",
|
||||
"version": "5.3.7",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -21,7 +21,7 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/react-navigation/react-navigation/issues"
|
||||
},
|
||||
"homepage": "https://reactnavigation.org/docs/material-bottom-tab-navigator.html",
|
||||
"homepage": "https://reactnavigation.org/docs/material-bottom-tab-navigator/",
|
||||
"main": "lib/commonjs/index.js",
|
||||
"react-native": "src/index.tsx",
|
||||
"source": "src/index.tsx",
|
||||
@@ -42,17 +42,17 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@react-navigation/native": "^5.7.3",
|
||||
"@testing-library/react-native": "^7.0.1",
|
||||
"@types/react": "^16.9.44",
|
||||
"@types/react-native": "^0.63.4",
|
||||
"@types/react-native-vector-icons": "^6.4.5",
|
||||
"@react-navigation/native": "^5.8.7",
|
||||
"@testing-library/react-native": "^7.1.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-native": "^0.63.30",
|
||||
"@types/react-native-vector-icons": "^6.4.6",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.62.2",
|
||||
"react-native-paper": "^4.0.1",
|
||||
"react": "~16.13.1",
|
||||
"react-native": "~0.63.2",
|
||||
"react-native-paper": "^4.2.0",
|
||||
"react-native-vector-icons": "^7.0.0",
|
||||
"typescript": "^3.9.7"
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.5",
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
TabRouter,
|
||||
TabRouterOptions,
|
||||
TabNavigationState,
|
||||
TabActionHelpers,
|
||||
ParamListBase,
|
||||
} from '@react-navigation/native';
|
||||
|
||||
import MaterialBottomTabView from '../views/MaterialBottomTabView';
|
||||
@@ -27,8 +29,9 @@ function MaterialBottomTabNavigator({
|
||||
...rest
|
||||
}: Props) {
|
||||
const { state, descriptors, navigation } = useNavigationBuilder<
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamListBase>,
|
||||
TabRouterOptions,
|
||||
TabActionHelpers<ParamListBase>,
|
||||
MaterialBottomTabNavigationOptions,
|
||||
MaterialBottomTabNavigationEventMap
|
||||
>(TabRouter, {
|
||||
@@ -49,7 +52,7 @@ function MaterialBottomTabNavigator({
|
||||
}
|
||||
|
||||
export default createNavigatorFactory<
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamListBase>,
|
||||
MaterialBottomTabNavigationOptions,
|
||||
MaterialBottomTabNavigationEventMap,
|
||||
typeof MaterialBottomTabNavigator
|
||||
|
||||
@@ -19,7 +19,8 @@ export type MaterialBottomTabNavigationEventMap = {
|
||||
export type MaterialBottomTabNavigationHelpers = NavigationHelpers<
|
||||
ParamListBase,
|
||||
MaterialBottomTabNavigationEventMap
|
||||
>;
|
||||
> &
|
||||
TabActionHelpers<ParamListBase>;
|
||||
|
||||
export type MaterialBottomTabNavigationProp<
|
||||
ParamList extends ParamListBase,
|
||||
@@ -27,7 +28,7 @@ export type MaterialBottomTabNavigationProp<
|
||||
> = NavigationProp<
|
||||
ParamList,
|
||||
RouteName,
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamList>,
|
||||
MaterialBottomTabNavigationOptions,
|
||||
MaterialBottomTabNavigationEventMap
|
||||
> &
|
||||
@@ -84,7 +85,7 @@ export type MaterialBottomTabNavigationOptions = {
|
||||
export type MaterialBottomTabDescriptor = Descriptor<
|
||||
ParamListBase,
|
||||
string,
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamListBase>,
|
||||
MaterialBottomTabNavigationOptions
|
||||
>;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet, Platform } from 'react-native';
|
||||
import { BottomNavigation, DefaultTheme, DarkTheme } from 'react-native-paper';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import {
|
||||
NavigationHelpersContext,
|
||||
Route,
|
||||
@@ -10,6 +9,7 @@ import {
|
||||
useTheme,
|
||||
useLinkBuilder,
|
||||
Link,
|
||||
ParamListBase,
|
||||
} from '@react-navigation/native';
|
||||
|
||||
import type {
|
||||
@@ -19,13 +19,55 @@ import type {
|
||||
} from '../types';
|
||||
|
||||
type Props = MaterialBottomTabNavigationConfig & {
|
||||
state: TabNavigationState;
|
||||
state: TabNavigationState<ParamListBase>;
|
||||
navigation: MaterialBottomTabNavigationHelpers;
|
||||
descriptors: MaterialBottomTabDescriptorMap;
|
||||
};
|
||||
|
||||
type Scene = { route: { key: string } };
|
||||
|
||||
// Optionally require vector-icons referenced from react-native-paper:
|
||||
// https://github.com/callstack/react-native-paper/blob/4b26429c49053eaa4c3e0fae208639e01093fa87/src/components/MaterialCommunityIcon.tsx#L14
|
||||
let MaterialCommunityIcons: any;
|
||||
|
||||
try {
|
||||
// Optionally require vector-icons
|
||||
MaterialCommunityIcons = require('react-native-vector-icons/MaterialCommunityIcons')
|
||||
.default;
|
||||
} catch (e) {
|
||||
// @ts-expect-error
|
||||
if (global.__expo?.Icon?.MaterialCommunityIcons) {
|
||||
// Snack doesn't properly bundle vector icons from sub-path
|
||||
// Use icons from the __expo global if available
|
||||
// @ts-expect-error
|
||||
MaterialCommunityIcons = global.__expo.Icon.MaterialCommunityIcons;
|
||||
} else {
|
||||
let isErrorLogged = false;
|
||||
|
||||
// Fallback component for icons
|
||||
MaterialCommunityIcons = () => {
|
||||
if (!isErrorLogged) {
|
||||
if (
|
||||
!/(Cannot find module|Module not found|Cannot resolve module)/.test(
|
||||
e.message
|
||||
)
|
||||
) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
console.warn(
|
||||
`Tried to use the icon '${name}' in a component from '@react-navigation/material-bottom-tabs', but 'react-native-vector-icons' could not be loaded.`,
|
||||
`To remove this warning, try installing 'react-native-vector-icons' or use another method.`
|
||||
);
|
||||
|
||||
isErrorLogged = true;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function MaterialBottomTabViewInner({
|
||||
state,
|
||||
navigation,
|
||||
|
||||
@@ -3,6 +3,98 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.3.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.6...@react-navigation/material-top-tabs@5.3.7) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.5...@react-navigation/material-top-tabs@5.3.6) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.4...@react-navigation/material-top-tabs@5.3.5) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.3...@react-navigation/material-top-tabs@5.3.4) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.2...@react-navigation/material-top-tabs@5.3.3) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.1...@react-navigation/material-top-tabs@5.3.2) (2020-10-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.0...@react-navigation/material-top-tabs@5.3.1) (2020-10-28)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.3.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.2.19...@react-navigation/material-top-tabs@5.3.0) (2020-10-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* improve types for navigation state ([#8980](https://github.com/react-navigation/react-navigation/issues/8980)) ([7dc2f58](https://github.com/react-navigation/react-navigation/commit/7dc2f5832e371473f3263c01ab39824eb9e2057d))
|
||||
* update helper types to have navigator specific methods ([f51086e](https://github.com/react-navigation/react-navigation/commit/f51086edea42f2382dac8c6914aac8574132114b))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.19](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.2.18...@react-navigation/material-top-tabs@5.2.19) (2020-10-07)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.18](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.2.17...@react-navigation/material-top-tabs@5.2.18) (2020-09-28)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.17](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.2.16...@react-navigation/material-top-tabs@5.2.17) (2020-09-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.2.16](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.2.15...@react-navigation/material-top-tabs@5.2.16) (2020-08-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
React Navigation integration for animated tab view component from [`react-native-tab-view`](https://github.com/react-native-community/react-native-tab-view).
|
||||
|
||||
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/material-top-tab-navigator.html).
|
||||
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/material-top-tab-navigator/).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/material-top-tabs",
|
||||
"description": "Integration for the animated tab view component from react-native-tab-view",
|
||||
"version": "5.2.16",
|
||||
"version": "5.3.7",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -21,7 +21,7 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/react-navigation/react-navigation/issues"
|
||||
},
|
||||
"homepage": "https://reactnavigation.org/docs/material-top-tab-navigator.html",
|
||||
"homepage": "https://reactnavigation.org/docs/material-top-tab-navigator/",
|
||||
"main": "lib/commonjs/index.js",
|
||||
"react-native": "src/index.tsx",
|
||||
"source": "src/index.tsx",
|
||||
@@ -41,21 +41,21 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"color": "^3.1.2"
|
||||
"color": "^3.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@react-navigation/native": "^5.7.3",
|
||||
"@testing-library/react-native": "^7.0.1",
|
||||
"@types/react": "^16.9.44",
|
||||
"@types/react-native": "^0.63.4",
|
||||
"@react-navigation/native": "^5.8.7",
|
||||
"@testing-library/react-native": "^7.1.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-native": "^0.63.30",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.62.2",
|
||||
"react-native-gesture-handler": "^1.7.0",
|
||||
"react-native-reanimated": "^1.10.1",
|
||||
"react-native-tab-view": "^2.15.1",
|
||||
"typescript": "^3.9.7"
|
||||
"react": "~16.13.1",
|
||||
"react-native": "~0.63.2",
|
||||
"react-native-gesture-handler": "~1.7.0",
|
||||
"react-native-reanimated": "~1.13.0",
|
||||
"react-native-tab-view": "^2.15.2",
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.5",
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
TabRouter,
|
||||
TabRouterOptions,
|
||||
TabNavigationState,
|
||||
TabActionHelpers,
|
||||
ParamListBase,
|
||||
} from '@react-navigation/native';
|
||||
import MaterialTopTabView from '../views/MaterialTopTabView';
|
||||
import type {
|
||||
@@ -26,8 +28,9 @@ function MaterialTopTabNavigator({
|
||||
...rest
|
||||
}: Props) {
|
||||
const { state, descriptors, navigation } = useNavigationBuilder<
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamListBase>,
|
||||
TabRouterOptions,
|
||||
TabActionHelpers<ParamListBase>,
|
||||
MaterialTopTabNavigationOptions,
|
||||
MaterialTopTabNavigationEventMap
|
||||
>(TabRouter, {
|
||||
@@ -48,7 +51,7 @@ function MaterialTopTabNavigator({
|
||||
}
|
||||
|
||||
export default createNavigatorFactory<
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamListBase>,
|
||||
MaterialTopTabNavigationOptions,
|
||||
MaterialTopTabNavigationEventMap,
|
||||
typeof MaterialTopTabNavigator
|
||||
|
||||
@@ -37,7 +37,8 @@ export type MaterialTopTabNavigationEventMap = {
|
||||
export type MaterialTopTabNavigationHelpers = NavigationHelpers<
|
||||
ParamListBase,
|
||||
MaterialTopTabNavigationEventMap
|
||||
>;
|
||||
> &
|
||||
TabActionHelpers<ParamListBase>;
|
||||
|
||||
export type MaterialTopTabNavigationProp<
|
||||
ParamList extends ParamListBase,
|
||||
@@ -45,7 +46,7 @@ export type MaterialTopTabNavigationProp<
|
||||
> = NavigationProp<
|
||||
ParamList,
|
||||
RouteName,
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamList>,
|
||||
MaterialTopTabNavigationOptions,
|
||||
MaterialTopTabNavigationEventMap
|
||||
> &
|
||||
@@ -94,7 +95,7 @@ export type MaterialTopTabNavigationOptions = {
|
||||
export type MaterialTopTabDescriptor = Descriptor<
|
||||
ParamListBase,
|
||||
string,
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamListBase>,
|
||||
MaterialTopTabNavigationOptions
|
||||
>;
|
||||
|
||||
@@ -192,7 +193,7 @@ export type MaterialTopTabBarOptions = Partial<
|
||||
|
||||
export type MaterialTopTabBarProps = MaterialTopTabBarOptions &
|
||||
SceneRendererProps & {
|
||||
state: TabNavigationState;
|
||||
state: TabNavigationState<ParamListBase>;
|
||||
navigation: NavigationHelpers<
|
||||
ParamListBase,
|
||||
MaterialTopTabNavigationEventMap
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
NavigationHelpersContext,
|
||||
TabNavigationState,
|
||||
TabActions,
|
||||
ParamListBase,
|
||||
useTheme,
|
||||
} from '@react-navigation/native';
|
||||
|
||||
@@ -16,7 +17,7 @@ import type {
|
||||
} from '../types';
|
||||
|
||||
type Props = MaterialTopTabNavigationConfig & {
|
||||
state: TabNavigationState;
|
||||
state: TabNavigationState<ParamListBase>;
|
||||
navigation: MaterialTopTabNavigationHelpers;
|
||||
descriptors: MaterialTopTabDescriptorMap;
|
||||
tabBarPosition?: 'top' | 'bottom';
|
||||
|
||||
@@ -3,6 +3,111 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.8.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.6...@react-navigation/native@5.8.7) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.8.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.5...@react-navigation/native@5.8.6) (2020-11-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ignore any errors from deep linking ([4c2379c](https://github.com/react-navigation/react-navigation/commit/4c2379cec1e661aa132002fd1c50909ea64cb983))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.8.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.4...@react-navigation/native@5.8.5) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.8.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.3...@react-navigation/native@5.8.4) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.8.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.2...@react-navigation/native@5.8.3) (2020-11-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* make sure that invalid linking config doesn't work if app is open ([52451d1](https://github.com/react-navigation/react-navigation/commit/52451d11094b8551e3c6950b3e005d68225c7da9))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.8.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.1...@react-navigation/native@5.8.2) (2020-10-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.8.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.0...@react-navigation/native@5.8.1) (2020-10-28)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.8.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.7.6...@react-navigation/native@5.8.0) (2020-10-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add `getInitialURL` and `subscribe` options to linking config ([748e92f](https://github.com/react-navigation/react-navigation/commit/748e92f120b9ff73c6b1e14515f60c76701081db))
|
||||
* allow deep linking to reset state ([#8973](https://github.com/react-navigation/react-navigation/issues/8973)) ([7f3b27a](https://github.com/react-navigation/react-navigation/commit/7f3b27a9ec8edd9604ac19774baa1f60963ccdc9)), closes [#8952](https://github.com/react-navigation/react-navigation/issues/8952)
|
||||
* support wildcard string prefixes ([#8942](https://github.com/react-navigation/react-navigation/issues/8942)) ([23ab350](https://github.com/react-navigation/react-navigation/commit/23ab3504921b7e741a48d66c6a953905206df4b7)), closes [#8941](https://github.com/react-navigation/react-navigation/issues/8941)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.7.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.7.5...@react-navigation/native@5.7.6) (2020-10-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add missing check for initial state on web ([9e36508](https://github.com/react-navigation/react-navigation/commit/9e3650831c22b47130d2b388390f7eb7910fe91d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.7.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.7.4...@react-navigation/native@5.7.5) (2020-09-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* check for correct resolved value in useThenable. fixes [#8798](https://github.com/react-navigation/react-navigation/issues/8798) ([cc8f1f4](https://github.com/react-navigation/react-navigation/commit/cc8f1f4205373f605fc457b40666305b3e117772))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.7.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.7.3...@react-navigation/native@5.7.4) (2020-09-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.7.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.7.2...@react-navigation/native@5.7.3) (2020-08-04)
|
||||
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
React Native integration for React Navigation.
|
||||
|
||||
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/getting-started.html).
|
||||
Installation instructions and documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/getting-started/).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/native",
|
||||
"description": "React Native integration for React Navigation",
|
||||
"version": "5.7.3",
|
||||
"version": "5.8.7",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
"react-navigation",
|
||||
@@ -37,20 +37,21 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/core": "^5.12.3",
|
||||
"nanoid": "^3.1.12"
|
||||
"@react-navigation/core": "^5.14.1",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"nanoid": "^3.1.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@testing-library/react-native": "^7.0.1",
|
||||
"@types/react": "^16.9.44",
|
||||
"@testing-library/react-native": "^7.1.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/react-native": "^0.63.4",
|
||||
"@types/react-native": "^0.63.30",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.9.0",
|
||||
"react": "~16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-native": "~0.62.2",
|
||||
"typescript": "^3.9.7"
|
||||
"react-native": "~0.63.2",
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
|
||||
@@ -27,12 +27,23 @@ export type LinkingOptions = {
|
||||
* The prefixes are stripped from the URL before parsing them.
|
||||
* Usually they are the `scheme` + `host` (e.g. `myapp://chat?user=jane`)
|
||||
* Only applicable on Android and iOS.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* {
|
||||
* prefixes: [
|
||||
* "myapp://", // App-specific scheme
|
||||
* "https://example.com", // Prefix for universal links
|
||||
* "https://*.example.com" // Prefix which matches any subdomain
|
||||
* ]
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
prefixes: string[];
|
||||
/**
|
||||
* Config to fine-tune how to parse the path.
|
||||
*
|
||||
* Example:
|
||||
* @example
|
||||
* ```js
|
||||
* {
|
||||
* Chat: {
|
||||
@@ -43,13 +54,47 @@ export type LinkingOptions = {
|
||||
* ```
|
||||
*/
|
||||
config?: { initialRouteName?: string; screens: PathConfigMap };
|
||||
/**
|
||||
* Custom function to get the initial URL used for linking.
|
||||
* Uses `Linking.getInitialURL()` by default.
|
||||
* Not supported on Web.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* {
|
||||
* getInitialURL () => Linking.getInitialURL(),
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
getInitialURL?: () => Promise<string | null | undefined>;
|
||||
/**
|
||||
* Custom function to get subscribe to URL updates.
|
||||
* Uses `Linking.addEventListener('url', callback)` by default.
|
||||
* Not supported on Web.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* {
|
||||
* subscribe: (listener) => {
|
||||
* const onReceiveURL = ({ url }) => listener(url);
|
||||
*
|
||||
* Linking.addEventListener('url', onReceiveURL);
|
||||
*
|
||||
* return () => Linking.removeEventListener('url', onReceiveURL);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
subscribe?: (
|
||||
listener: (url: string) => void
|
||||
) => undefined | void | (() => void);
|
||||
/**
|
||||
* Custom function to parse the URL to a valid navigation state (advanced).
|
||||
* Only applicable on Web.
|
||||
*/
|
||||
getStateFromPath?: typeof getStateFromPathDefault;
|
||||
/**
|
||||
* Custom function to convert the state object to a valid URL (advanced).
|
||||
* Only applicable on Web.
|
||||
*/
|
||||
getPathFromState?: typeof getPathFromStateDefault;
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function useLinkProps({ to, action }: Props) {
|
||||
const linkTo = useLinkTo();
|
||||
|
||||
const onPress = (
|
||||
e: React.MouseEvent<HTMLAnchorElement, MouseEvent> | GestureResponderEvent
|
||||
e?: React.MouseEvent<HTMLAnchorElement, MouseEvent> | GestureResponderEvent
|
||||
) => {
|
||||
let shouldHandle = false;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function useLinkTo() {
|
||||
root = current;
|
||||
}
|
||||
|
||||
const action = getActionFromState(state);
|
||||
const action = getActionFromState(state, options?.config);
|
||||
|
||||
if (action !== undefined) {
|
||||
root.dispatch(action);
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
NavigationContainerRef,
|
||||
} from '@react-navigation/core';
|
||||
import type { LinkingOptions } from './types';
|
||||
import escapeStringRegexp from 'escape-string-regexp';
|
||||
|
||||
let isUsingLinking = false;
|
||||
|
||||
@@ -15,6 +16,22 @@ export default function useLinking(
|
||||
enabled = true,
|
||||
prefixes,
|
||||
config,
|
||||
getInitialURL = () =>
|
||||
Promise.race([
|
||||
Linking.getInitialURL(),
|
||||
new Promise<undefined>((resolve) =>
|
||||
// Timeout in 150ms if `getInitialState` doesn't resolve
|
||||
// Workaround for https://github.com/facebook/react-native/issues/25675
|
||||
setTimeout(resolve, 150)
|
||||
),
|
||||
]),
|
||||
subscribe = (listener) => {
|
||||
const callback = ({ url }: { url: string }) => listener(url);
|
||||
|
||||
Linking.addEventListener('url', callback);
|
||||
|
||||
return () => Linking.removeEventListener('url', callback);
|
||||
},
|
||||
getStateFromPath = getStateFromPathDefault,
|
||||
}: LinkingOptions
|
||||
) {
|
||||
@@ -47,19 +64,29 @@ export default function useLinking(
|
||||
const enabledRef = React.useRef(enabled);
|
||||
const prefixesRef = React.useRef(prefixes);
|
||||
const configRef = React.useRef(config);
|
||||
const getInitialURLRef = React.useRef(getInitialURL);
|
||||
const getStateFromPathRef = React.useRef(getStateFromPath);
|
||||
|
||||
React.useEffect(() => {
|
||||
enabledRef.current = enabled;
|
||||
prefixesRef.current = prefixes;
|
||||
configRef.current = config;
|
||||
getInitialURLRef.current = getInitialURL;
|
||||
getStateFromPathRef.current = getStateFromPath;
|
||||
}, [config, enabled, getStateFromPath, prefixes]);
|
||||
}, [config, enabled, prefixes, getInitialURL, getStateFromPath]);
|
||||
|
||||
const extractPathFromURL = React.useCallback((url: string) => {
|
||||
for (const prefix of prefixesRef.current) {
|
||||
if (url.startsWith(prefix)) {
|
||||
return url.replace(prefix, '');
|
||||
const protocol = prefix.match(/^[^:]+:\/\//)?.[0] ?? '';
|
||||
const host = prefix.replace(protocol, '');
|
||||
const prefixRegex = new RegExp(
|
||||
`^${escapeStringRegexp(protocol)}${host
|
||||
.split('.')
|
||||
.map((it) => (it === '*' ? '[^/]+' : escapeStringRegexp(it)))
|
||||
.join('\\.')}`
|
||||
);
|
||||
if (prefixRegex.test(url)) {
|
||||
return url.replace(prefixRegex, '');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,15 +98,7 @@ export default function useLinking(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const url = await (Promise.race([
|
||||
Linking.getInitialURL(),
|
||||
new Promise((resolve) =>
|
||||
// Timeout in 150ms if `getInitialState` doesn't resolve
|
||||
// Workaround for https://github.com/facebook/react-native/issues/25675
|
||||
setTimeout(resolve, 150)
|
||||
),
|
||||
]) as Promise<string | null | undefined>);
|
||||
|
||||
const url = await getInitialURLRef.current();
|
||||
const path = url ? extractPathFromURL(url) : null;
|
||||
|
||||
if (path) {
|
||||
@@ -90,7 +109,7 @@ export default function useLinking(
|
||||
}, [extractPathFromURL]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const listener = ({ url }: { url: string }) => {
|
||||
const listener = (url: string) => {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
@@ -102,10 +121,31 @@ export default function useLinking(
|
||||
const state = getStateFromPathRef.current(path, configRef.current);
|
||||
|
||||
if (state) {
|
||||
const action = getActionFromState(state);
|
||||
// Make sure that the routes in the state exist in the root navigator
|
||||
// Otherwise there's an error in the linking configuration
|
||||
const rootState = navigation.getRootState();
|
||||
|
||||
if (
|
||||
state.routes.some((r) => !rootState?.routeNames.includes(r.name))
|
||||
) {
|
||||
console.warn(
|
||||
"The navigation state parsed from the URL contains routes not present in the root navigator. This usually means that the linking configuration doesn't match the navigation structure. See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const action = getActionFromState(state, configRef.current);
|
||||
|
||||
if (action !== undefined) {
|
||||
navigation.dispatch(action);
|
||||
try {
|
||||
navigation.dispatch(action);
|
||||
} catch (e) {
|
||||
// Ignore any errors from deep linking.
|
||||
// This could happen in case of malformed links, navigation object not being initialized etc.
|
||||
console.warn(
|
||||
`An error occurred when trying to handle the link '${path}': ${e.message}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
navigation.resetRoot(state);
|
||||
}
|
||||
@@ -113,10 +153,8 @@ export default function useLinking(
|
||||
}
|
||||
};
|
||||
|
||||
Linking.addEventListener('url', listener);
|
||||
|
||||
return () => Linking.removeEventListener('url', listener);
|
||||
}, [enabled, extractPathFromURL, ref]);
|
||||
return subscribe(listener);
|
||||
}, [enabled, ref, subscribe, extractPathFromURL]);
|
||||
|
||||
return {
|
||||
getInitialState,
|
||||
|
||||
@@ -399,11 +399,34 @@ export default function useLinking(
|
||||
|
||||
// We should only dispatch an action when going forward
|
||||
// Otherwise the action will likely add items to history, which would mess things up
|
||||
if (state && index > previousIndex) {
|
||||
const action = getActionFromState(state);
|
||||
if (state) {
|
||||
// Make sure that the routes in the state exist in the root navigator
|
||||
// Otherwise there's an error in the linking configuration
|
||||
const rootState = navigation.getRootState();
|
||||
|
||||
if (action !== undefined) {
|
||||
navigation.dispatch(action);
|
||||
if (state.routes.some((r) => !rootState?.routeNames.includes(r.name))) {
|
||||
console.warn(
|
||||
"The navigation state parsed from the URL contains routes not present in the root navigator. This usually means that the linking configuration doesn't match the navigation structure. See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (index > previousIndex) {
|
||||
const action = getActionFromState(state, configRef.current);
|
||||
|
||||
if (action !== undefined) {
|
||||
try {
|
||||
navigation.dispatch(action);
|
||||
} catch (e) {
|
||||
// Ignore any errors from deep linking.
|
||||
// This could happen in case of malformed links, navigation object not being initialized etc.
|
||||
console.warn(
|
||||
`An error occurred when trying to handle the link '${path}': ${e.message}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
navigation.resetRoot(state);
|
||||
}
|
||||
} else {
|
||||
navigation.resetRoot(state);
|
||||
}
|
||||
@@ -423,13 +446,16 @@ export default function useLinking(
|
||||
// We need to record the current metadata on the first render if they aren't set
|
||||
// This will allow the initial state to be in the history entry
|
||||
const state = ref.current.getRootState();
|
||||
const path = getPathFromStateRef.current(state, configRef.current);
|
||||
|
||||
if (previousStateRef.current === undefined) {
|
||||
previousStateRef.current = state;
|
||||
if (state) {
|
||||
const path = getPathFromStateRef.current(state, configRef.current);
|
||||
|
||||
if (previousStateRef.current === undefined) {
|
||||
previousStateRef.current = state;
|
||||
}
|
||||
|
||||
history.replace({ path, state });
|
||||
}
|
||||
|
||||
history.replace({ path, state });
|
||||
}
|
||||
|
||||
const onStateChange = async () => {
|
||||
|
||||
@@ -3,19 +3,15 @@ import * as React from 'react';
|
||||
export default function useThenable<T>(create: () => PromiseLike<T>) {
|
||||
const [promise] = React.useState(create);
|
||||
|
||||
// Check if our thenable is synchronous
|
||||
let resolved = false;
|
||||
let value: T | undefined;
|
||||
let initialState: [boolean, T | undefined] = [false, undefined];
|
||||
|
||||
// Check if our thenable is synchronous
|
||||
promise.then((result) => {
|
||||
resolved = true;
|
||||
value = result;
|
||||
initialState = [true, result];
|
||||
});
|
||||
|
||||
const [state, setState] = React.useState<[boolean, T | undefined]>([
|
||||
resolved,
|
||||
value,
|
||||
]);
|
||||
const [state, setState] = React.useState(initialState);
|
||||
const [resolved] = state;
|
||||
|
||||
React.useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
@@ -3,6 +3,61 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.6.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.6.0...@react-navigation/routers@5.6.1) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/routers
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.6.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.5.1...@react-navigation/routers@5.6.0) (2020-11-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add a merge option to navigate to control merging params ([9beca3a](https://github.com/react-navigation/react-navigation/commit/9beca3a8027c6e2135dbef2abb8eede6b0b4bc44))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.5.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.5.0...@react-navigation/routers@5.5.1) (2020-10-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve types for route prop in screenOptions ([d26bcc0](https://github.com/react-navigation/react-navigation/commit/d26bcc057ef31f8950f909adf83e263171a42d74))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.5.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.4.12...@react-navigation/routers@5.5.0) (2020-10-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle pushing a route with duplicate key ([091b2a2](https://github.com/react-navigation/react-navigation/commit/091b2a2038af1097be57a1bb451623d64a1efc92))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* allow deep linking to reset state ([#8973](https://github.com/react-navigation/react-navigation/issues/8973)) ([7f3b27a](https://github.com/react-navigation/react-navigation/commit/7f3b27a9ec8edd9604ac19774baa1f60963ccdc9)), closes [#8952](https://github.com/react-navigation/react-navigation/issues/8952)
|
||||
* improve types for navigation state ([#8980](https://github.com/react-navigation/react-navigation/issues/8980)) ([7dc2f58](https://github.com/react-navigation/react-navigation/commit/7dc2f5832e371473f3263c01ab39824eb9e2057d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.4.12](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.4.11...@react-navigation/routers@5.4.12) (2020-09-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/routers
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.4.11](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.4.10...@react-navigation/routers@5.4.11) (2020-08-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/routers
|
||||
|
||||
@@ -14,4 +14,4 @@ yarn add @react-navigation/routers
|
||||
|
||||
## Usage
|
||||
|
||||
Documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/custom-routers.html).
|
||||
Documentation can be found on the [React Navigation website](https://reactnavigation.org/docs/custom-routers/).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/routers",
|
||||
"description": "Routers to help build custom navigators",
|
||||
"version": "5.4.11",
|
||||
"version": "5.6.1",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native",
|
||||
@@ -16,7 +16,7 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/react-navigation/react-navigation/issues"
|
||||
},
|
||||
"homepage": "https://reactnavigation.org/docs/custom-routers.html",
|
||||
"homepage": "https://reactnavigation.org/docs/custom-routers/",
|
||||
"main": "lib/commonjs/index.js",
|
||||
"react-native": "src/index.tsx",
|
||||
"source": "src/index.tsx",
|
||||
@@ -36,12 +36,12 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"nanoid": "^3.1.12"
|
||||
"nanoid": "^3.1.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"del-cli": "^3.0.1",
|
||||
"typescript": "^3.9.7"
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"@react-native-community/bob": {
|
||||
"source": "src",
|
||||
|
||||
@@ -16,14 +16,14 @@ export type Action =
|
||||
| {
|
||||
type: 'NAVIGATE';
|
||||
payload:
|
||||
| { key: string; name?: undefined; params?: object }
|
||||
| { name: string; key?: string; params?: object };
|
||||
| { key: string; name?: undefined; params?: object; merge?: boolean }
|
||||
| { name: string; key?: string; params?: object; merge?: boolean };
|
||||
source?: string;
|
||||
target?: string;
|
||||
}
|
||||
| {
|
||||
type: 'RESET';
|
||||
payload: ResetState;
|
||||
payload: ResetState | undefined;
|
||||
source?: string;
|
||||
target?: string;
|
||||
}
|
||||
@@ -43,7 +43,9 @@ export function navigate(
|
||||
| { key: string; params?: object }
|
||||
| { name: string; key?: string; params?: object }
|
||||
): Action;
|
||||
// eslint-disable-next-line no-redeclare
|
||||
export function navigate(name: string, params?: object): Action;
|
||||
// eslint-disable-next-line no-redeclare
|
||||
export function navigate(...args: any): Action {
|
||||
if (typeof args[0] === 'string') {
|
||||
return { type: 'NAVIGATE', payload: { name: args[0], params: args[1] } };
|
||||
@@ -60,7 +62,7 @@ export function navigate(...args: any): Action {
|
||||
}
|
||||
}
|
||||
|
||||
export function reset(state: ResetState): Action {
|
||||
export function reset(state: ResetState | undefined): Action {
|
||||
return { type: 'RESET', payload: state };
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ export type DrawerRouterOptions = TabRouterOptions & {
|
||||
openByDefault?: boolean;
|
||||
};
|
||||
|
||||
export type DrawerNavigationState = Omit<
|
||||
TabNavigationState,
|
||||
export type DrawerNavigationState<ParamList extends ParamListBase> = Omit<
|
||||
TabNavigationState<ParamList>,
|
||||
'type' | 'history'
|
||||
> & {
|
||||
/**
|
||||
@@ -72,10 +72,14 @@ export const DrawerActions = {
|
||||
};
|
||||
|
||||
const isDrawerOpen = (
|
||||
state: DrawerNavigationState | PartialState<DrawerNavigationState>
|
||||
state:
|
||||
| DrawerNavigationState<ParamListBase>
|
||||
| PartialState<DrawerNavigationState<ParamListBase>>
|
||||
) => Boolean(state.history?.find((it) => it.type === 'drawer'));
|
||||
|
||||
const openDrawer = (state: DrawerNavigationState): DrawerNavigationState => {
|
||||
const openDrawer = (
|
||||
state: DrawerNavigationState<ParamListBase>
|
||||
): DrawerNavigationState<ParamListBase> => {
|
||||
if (isDrawerOpen(state)) {
|
||||
return state;
|
||||
}
|
||||
@@ -86,7 +90,9 @@ const openDrawer = (state: DrawerNavigationState): DrawerNavigationState => {
|
||||
};
|
||||
};
|
||||
|
||||
const closeDrawer = (state: DrawerNavigationState): DrawerNavigationState => {
|
||||
const closeDrawer = (
|
||||
state: DrawerNavigationState<ParamListBase>
|
||||
): DrawerNavigationState<ParamListBase> => {
|
||||
if (!isDrawerOpen(state)) {
|
||||
return state;
|
||||
}
|
||||
@@ -101,11 +107,11 @@ export default function DrawerRouter({
|
||||
openByDefault,
|
||||
...rest
|
||||
}: DrawerRouterOptions): Router<
|
||||
DrawerNavigationState,
|
||||
DrawerNavigationState<ParamListBase>,
|
||||
DrawerActionType | CommonNavigationAction
|
||||
> {
|
||||
const router = (TabRouter(rest) as unknown) as Router<
|
||||
DrawerNavigationState,
|
||||
DrawerNavigationState<ParamListBase>,
|
||||
TabActionType | CommonNavigationAction
|
||||
>;
|
||||
|
||||
|
||||
@@ -36,7 +36,9 @@ export type StackActionType =
|
||||
|
||||
export type StackRouterOptions = DefaultRouterOptions;
|
||||
|
||||
export type StackNavigationState = NavigationState & {
|
||||
export type StackNavigationState<
|
||||
ParamList extends ParamListBase
|
||||
> = NavigationState<ParamList> & {
|
||||
/**
|
||||
* Type of the router, in this case, it's stack.
|
||||
*/
|
||||
@@ -96,7 +98,7 @@ export const StackActions = {
|
||||
|
||||
export default function StackRouter(options: StackRouterOptions) {
|
||||
const router: Router<
|
||||
StackNavigationState,
|
||||
StackNavigationState<ParamListBase>,
|
||||
CommonNavigationAction | StackActionType
|
||||
> = {
|
||||
...BaseRouter,
|
||||
@@ -256,10 +258,35 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
|
||||
case 'PUSH':
|
||||
if (state.routeNames.includes(action.payload.name)) {
|
||||
return {
|
||||
...state,
|
||||
index: state.index + 1,
|
||||
routes: [
|
||||
const route =
|
||||
action.payload.name && action.payload.key
|
||||
? state.routes.find(
|
||||
(route) =>
|
||||
route.name === action.payload.name &&
|
||||
route.key === action.payload.key
|
||||
)
|
||||
: undefined;
|
||||
|
||||
let routes: Route<string>[];
|
||||
|
||||
if (route) {
|
||||
routes = state.routes.filter((r) => r.key !== route.key);
|
||||
routes.push(
|
||||
action.payload.params
|
||||
? {
|
||||
...route,
|
||||
params:
|
||||
action.payload.params !== undefined
|
||||
? {
|
||||
...route.params,
|
||||
...action.payload.params,
|
||||
}
|
||||
: route.params,
|
||||
}
|
||||
: route
|
||||
);
|
||||
} else {
|
||||
routes = [
|
||||
...state.routes,
|
||||
{
|
||||
key:
|
||||
@@ -275,7 +302,13 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
}
|
||||
: action.payload.params,
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
index: routes.length - 1,
|
||||
routes,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -364,19 +397,34 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
);
|
||||
}
|
||||
|
||||
const route = state.routes[index];
|
||||
|
||||
let params;
|
||||
|
||||
if (action.payload.merge === false) {
|
||||
params =
|
||||
routeParamList[route.name] !== undefined
|
||||
? {
|
||||
...routeParamList[route.name],
|
||||
...action.payload.params,
|
||||
}
|
||||
: action.payload.params;
|
||||
} else {
|
||||
params = action.payload.params
|
||||
? {
|
||||
...route.params,
|
||||
...action.payload.params,
|
||||
}
|
||||
: route.params;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
index,
|
||||
routes: [
|
||||
...state.routes.slice(0, index),
|
||||
action.payload.params !== undefined
|
||||
? {
|
||||
...state.routes[index],
|
||||
params: {
|
||||
...state.routes[index].params,
|
||||
...action.payload.params,
|
||||
},
|
||||
}
|
||||
params !== route.params
|
||||
? { ...route, params }
|
||||
: state.routes[index],
|
||||
],
|
||||
};
|
||||
|
||||
@@ -23,7 +23,10 @@ export type TabRouterOptions = DefaultRouterOptions & {
|
||||
backBehavior?: BackBehavior;
|
||||
};
|
||||
|
||||
export type TabNavigationState = Omit<NavigationState, 'history'> & {
|
||||
export type TabNavigationState<ParamList extends ParamListBase> = Omit<
|
||||
NavigationState<ParamList>,
|
||||
'history'
|
||||
> & {
|
||||
/**
|
||||
* Type of the router, in this case, it's tab.
|
||||
*/
|
||||
@@ -93,7 +96,7 @@ const getRouteHistory = (
|
||||
};
|
||||
|
||||
const changeIndex = (
|
||||
state: TabNavigationState,
|
||||
state: TabNavigationState<ParamListBase>,
|
||||
index: number,
|
||||
backBehavior: BackBehavior,
|
||||
initialRouteName: string | undefined
|
||||
@@ -127,7 +130,7 @@ export default function TabRouter({
|
||||
backBehavior = 'history',
|
||||
}: TabRouterOptions) {
|
||||
const router: Router<
|
||||
TabNavigationState,
|
||||
TabNavigationState<ParamListBase>,
|
||||
TabActionType | CommonNavigationAction
|
||||
> = {
|
||||
...BaseRouter,
|
||||
@@ -172,9 +175,9 @@ export default function TabRouter({
|
||||
}
|
||||
|
||||
const routes = routeNames.map((name) => {
|
||||
const route = (state as PartialState<TabNavigationState>).routes.find(
|
||||
(r) => r.name === name
|
||||
);
|
||||
const route = (state as PartialState<
|
||||
TabNavigationState<ParamListBase>
|
||||
>).routes.find((r) => r.name === name);
|
||||
|
||||
return {
|
||||
...route,
|
||||
@@ -268,7 +271,7 @@ export default function TabRouter({
|
||||
return changeIndex(state, index, backBehavior, initialRouteName);
|
||||
},
|
||||
|
||||
getStateForAction(state, action) {
|
||||
getStateForAction(state, action, { routeParamList }) {
|
||||
switch (action.type) {
|
||||
case 'JUMP_TO':
|
||||
case 'NAVIGATE': {
|
||||
@@ -293,17 +296,37 @@ export default function TabRouter({
|
||||
...state,
|
||||
routes:
|
||||
action.payload.params !== undefined
|
||||
? state.routes.map((route, i) =>
|
||||
i === index
|
||||
? {
|
||||
...route,
|
||||
params: {
|
||||
? state.routes.map((route, i) => {
|
||||
if (i !== index) {
|
||||
return route;
|
||||
}
|
||||
|
||||
let params;
|
||||
|
||||
if (
|
||||
action.type === 'NAVIGATE' &&
|
||||
action.payload.merge === false
|
||||
) {
|
||||
params =
|
||||
routeParamList[route.name] !== undefined
|
||||
? {
|
||||
...routeParamList[route.name],
|
||||
...action.payload.params,
|
||||
}
|
||||
: action.payload.params;
|
||||
} else {
|
||||
params = action.payload.params
|
||||
? {
|
||||
...route.params,
|
||||
...action.payload.params,
|
||||
},
|
||||
}
|
||||
: route
|
||||
)
|
||||
}
|
||||
: route.params;
|
||||
}
|
||||
|
||||
return params !== route.params
|
||||
? { ...route, params }
|
||||
: route;
|
||||
})
|
||||
: state.routes,
|
||||
},
|
||||
index,
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
DrawerRouter,
|
||||
DrawerActions,
|
||||
DrawerNavigationState,
|
||||
ParamListBase,
|
||||
} from '..';
|
||||
|
||||
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
||||
@@ -199,7 +200,7 @@ it('gets rehydrated state from partial state', () => {
|
||||
it("doesn't rehydrate state if it's not stale", () => {
|
||||
const router = DrawerRouter({});
|
||||
|
||||
const state: DrawerNavigationState = {
|
||||
const state: DrawerNavigationState<ParamListBase> = {
|
||||
index: 0,
|
||||
key: 'drawer-test',
|
||||
routeNames: ['bar', 'baz', 'qux'],
|
||||
@@ -340,7 +341,7 @@ it('handles open drawer action', () => {
|
||||
history: [{ type: 'route', key: 'bar' }, { type: 'drawer' }],
|
||||
});
|
||||
|
||||
const state: DrawerNavigationState = {
|
||||
const state: DrawerNavigationState<ParamListBase> = {
|
||||
stale: false as const,
|
||||
type: 'drawer' as const,
|
||||
key: 'root',
|
||||
@@ -395,7 +396,7 @@ it('handles close drawer action', () => {
|
||||
history: [{ type: 'route', key: 'bar' }],
|
||||
});
|
||||
|
||||
const state: DrawerNavigationState = {
|
||||
const state: DrawerNavigationState<ParamListBase> = {
|
||||
stale: false as const,
|
||||
type: 'drawer' as const,
|
||||
key: 'root',
|
||||
@@ -487,7 +488,7 @@ it('handles toggle drawer action', () => {
|
||||
it('updates history on focus change', () => {
|
||||
const router = DrawerRouter({ backBehavior: 'history' });
|
||||
|
||||
const state: DrawerNavigationState = {
|
||||
const state: DrawerNavigationState<ParamListBase> = {
|
||||
index: 0,
|
||||
key: 'drawer-test',
|
||||
routeNames: ['baz', 'bar'],
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user