mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-13 22:42:25 +08:00
Compare commits
38 Commits
@react-nav
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -87,6 +87,9 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Build packages in the monorepo
|
name: Build packages in the monorepo
|
||||||
command: yarn lerna run prepare
|
command: yarn lerna run prepare
|
||||||
|
- run:
|
||||||
|
name: Verify built type definitions are correct
|
||||||
|
command: yarn typescript
|
||||||
- run:
|
- run:
|
||||||
name: Verify paths for types
|
name: Verify paths for types
|
||||||
command: node scripts/check-types-path.js
|
command: node scripts/check-types-path.js
|
||||||
|
|||||||
1
.github/workflows/expo.yml
vendored
1
.github/workflows/expo.yml
vendored
@@ -3,6 +3,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"expo": {
|
"expo": {
|
||||||
"name": "React Navigation",
|
"name": "React Navigation",
|
||||||
"owner": "react-navigation",
|
"owner": "react-navigation",
|
||||||
"slug": "NavigationPlayground",
|
"slug": "react-navigation-example",
|
||||||
"description": "Demonstrates the functionality and various capabilities of React Navigation.",
|
"description": "Demonstrates the functionality and various capabilities of React Navigation.",
|
||||||
"privacy": "public",
|
"privacy": "public",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
|||||||
@@ -14,8 +14,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/vector-icons": "^10.0.0",
|
"@expo/vector-icons": "^10.0.0",
|
||||||
|
"@react-native-async-storage/async-storage": "^1.13.1",
|
||||||
"@react-native-community/masked-view": "0.1.10",
|
"@react-native-community/masked-view": "0.1.10",
|
||||||
"color": "^3.1.2",
|
"color": "^3.1.3",
|
||||||
"expo": "^39.0.0",
|
"expo": "^39.0.0",
|
||||||
"expo-asset": "~8.2.0",
|
"expo-asset": "~8.2.0",
|
||||||
"expo-blur": "~8.2.0",
|
"expo-blur": "~8.2.0",
|
||||||
@@ -36,25 +37,25 @@
|
|||||||
"react-native-web": "^0.13.16"
|
"react-native-web": "^0.13.16"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/node": "^7.10.1",
|
"@babel/node": "^7.12.1",
|
||||||
"@expo/webpack-config": "^0.12.38",
|
"@expo/webpack-config": "^0.12.40",
|
||||||
"@types/cheerio": "^0.22.22",
|
"@types/cheerio": "^0.22.22",
|
||||||
"@types/jest-dev-server": "^4.2.0",
|
"@types/jest-dev-server": "^4.2.0",
|
||||||
"@types/koa": "^2.11.4",
|
"@types/koa": "^2.11.6",
|
||||||
"@types/node-fetch": "^2.5.7",
|
"@types/node-fetch": "^2.5.7",
|
||||||
"@types/react": "~16.9.51",
|
"@types/react": "~16.9.53",
|
||||||
"@types/react-dom": "^16.9.8",
|
"@types/react-dom": "^16.9.8",
|
||||||
"@types/react-native": "~0.63.25",
|
"@types/react-native": "~0.63.30",
|
||||||
"babel-loader": "^8.1.0",
|
"babel-loader": "^8.1.0",
|
||||||
"babel-plugin-module-resolver": "^4.0.0",
|
"babel-plugin-module-resolver": "^4.0.0",
|
||||||
"babel-preset-expo": "^8.3.0",
|
"babel-preset-expo": "^8.3.0",
|
||||||
"cheerio": "^1.0.0-rc.3",
|
"cheerio": "^1.0.0-rc.3",
|
||||||
"expo-cli": "^3.27.14",
|
"expo-cli": "^3.28.2",
|
||||||
"jest": "^26.5.2",
|
"jest": "^26.6.1",
|
||||||
"jest-dev-server": "^4.4.0",
|
"jest-dev-server": "^4.4.0",
|
||||||
"mock-require-assets": "^0.0.1",
|
"mock-require-assets": "^0.0.1",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"nodemon": "^2.0.4",
|
"nodemon": "^2.0.6",
|
||||||
"playwright": "^0.14.0",
|
"playwright": "^0.14.0",
|
||||||
"serve": "^11.3.0",
|
"serve": "^11.3.0",
|
||||||
"typescript": "^4.0.3"
|
"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 SplashScreen = () => {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
<ActivityIndicator />
|
<ActivityIndicator color={colors.primary} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,18 +25,9 @@ const LinkButton = ({
|
|||||||
to,
|
to,
|
||||||
...rest
|
...rest
|
||||||
}: React.ComponentProps<typeof Button> & { to: string }) => {
|
}: React.ComponentProps<typeof Button> & { to: string }) => {
|
||||||
const { onPress, ...props } = useLinkProps({ to });
|
const props = useLinkProps({ to });
|
||||||
|
|
||||||
return (
|
return <Button {...props} {...rest} />;
|
||||||
<Button
|
|
||||||
{...props}
|
|
||||||
{...rest}
|
|
||||||
{...Platform.select({
|
|
||||||
web: { onClick: onPress } as any,
|
|
||||||
default: { onPress },
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ArticleScreen = ({
|
const ArticleScreen = ({
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {
|
||||||
ScrollView,
|
ScrollView,
|
||||||
YellowBox,
|
|
||||||
Platform,
|
Platform,
|
||||||
StatusBar,
|
StatusBar,
|
||||||
I18nManager,
|
I18nManager,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
ScaledSize,
|
ScaledSize,
|
||||||
Linking,
|
Linking,
|
||||||
|
LogBox,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { enableScreens } from 'react-native-screens';
|
import { enableScreens } from 'react-native-screens';
|
||||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||||
@@ -38,9 +38,9 @@ import {
|
|||||||
HeaderStyleInterpolators,
|
HeaderStyleInterpolators,
|
||||||
} from '@react-navigation/stack';
|
} from '@react-navigation/stack';
|
||||||
import { useReduxDevToolsExtension } from '@react-navigation/devtools';
|
import { useReduxDevToolsExtension } from '@react-navigation/devtools';
|
||||||
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
|
|
||||||
import { restartApp } from './Restart';
|
import { restartApp } from './Restart';
|
||||||
import AsyncStorage from './AsyncStorage';
|
|
||||||
import LinkingPrefixes from './LinkingPrefixes';
|
import LinkingPrefixes from './LinkingPrefixes';
|
||||||
import SettingsItem from './Shared/SettingsItem';
|
import SettingsItem from './Shared/SettingsItem';
|
||||||
import SimpleStack from './Screens/SimpleStack';
|
import SimpleStack from './Screens/SimpleStack';
|
||||||
@@ -58,7 +58,9 @@ import PreventRemove from './Screens/PreventRemove';
|
|||||||
import CompatAPI from './Screens/CompatAPI';
|
import CompatAPI from './Screens/CompatAPI';
|
||||||
import LinkComponent from './Screens/LinkComponent';
|
import LinkComponent from './Screens/LinkComponent';
|
||||||
|
|
||||||
YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']);
|
if (Platform.OS !== 'web') {
|
||||||
|
LogBox.ignoreLogs(['Require cycle:']);
|
||||||
|
}
|
||||||
|
|
||||||
enableScreens();
|
enableScreens();
|
||||||
|
|
||||||
|
|||||||
@@ -26,14 +26,14 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/config-conventional": "^11.0.0",
|
"@commitlint/config-conventional": "^11.0.0",
|
||||||
"@types/jest": "^26.0.14",
|
"@types/jest": "^26.0.15",
|
||||||
"babel-jest": "^26.5.2",
|
"babel-jest": "^26.6.1",
|
||||||
"codecov": "^3.8.0",
|
"codecov": "^3.8.0",
|
||||||
"commitlint": "^11.0.0",
|
"commitlint": "^11.0.0",
|
||||||
"eslint": "^7.10.0",
|
"eslint": "^7.12.0",
|
||||||
"eslint-config-satya164": "^3.1.8",
|
"eslint-config-satya164": "^3.1.8",
|
||||||
"husky": "^4.3.0",
|
"husky": "^4.3.0",
|
||||||
"jest": "^26.5.2",
|
"jest": "^26.6.1",
|
||||||
"lerna": "^3.22.1",
|
"lerna": "^3.22.1",
|
||||||
"metro-react-native-babel-preset": "^0.63.0",
|
"metro-react-native-babel-preset": "^0.63.0",
|
||||||
"prettier": "^2.1.2",
|
"prettier": "^2.1.2",
|
||||||
|
|||||||
@@ -3,6 +3,44 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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)
|
## [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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/bottom-tabs",
|
"name": "@react-navigation/bottom-tabs",
|
||||||
"description": "Bottom tab navigator following iOS design guidelines",
|
"description": "Bottom tab navigator following iOS design guidelines",
|
||||||
"version": "5.9.2",
|
"version": "5.10.3",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -36,16 +36,16 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^3.1.2",
|
"color": "^3.1.3",
|
||||||
"react-native-iphone-x-helper": "^1.2.1"
|
"react-native-iphone-x-helper": "^1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-native-community/bob": "^0.16.2",
|
||||||
"@react-navigation/native": "^5.7.6",
|
"@react-navigation/native": "^5.8.3",
|
||||||
"@testing-library/react-native": "^7.0.2",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/color": "^3.0.1",
|
"@types/color": "^3.0.1",
|
||||||
"@types/react": "^16.9.51",
|
"@types/react": "^16.9.53",
|
||||||
"@types/react-native": "^0.63.25",
|
"@types/react-native": "^0.63.30",
|
||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
"react-native": "~0.63.2",
|
"react-native": "~0.63.2",
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
TabRouter,
|
TabRouter,
|
||||||
TabRouterOptions,
|
TabRouterOptions,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
|
TabActionHelpers,
|
||||||
|
ParamListBase,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import BottomTabView from '../views/BottomTabView';
|
import BottomTabView from '../views/BottomTabView';
|
||||||
import type {
|
import type {
|
||||||
@@ -23,11 +25,13 @@ function BottomTabNavigator({
|
|||||||
backBehavior,
|
backBehavior,
|
||||||
children,
|
children,
|
||||||
screenOptions,
|
screenOptions,
|
||||||
|
sceneContainerStyle,
|
||||||
...rest
|
...rest
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { state, descriptors, navigation } = useNavigationBuilder<
|
const { state, descriptors, navigation } = useNavigationBuilder<
|
||||||
TabNavigationState,
|
TabNavigationState<ParamListBase>,
|
||||||
TabRouterOptions,
|
TabRouterOptions,
|
||||||
|
TabActionHelpers<ParamListBase>,
|
||||||
BottomTabNavigationOptions,
|
BottomTabNavigationOptions,
|
||||||
BottomTabNavigationEventMap
|
BottomTabNavigationEventMap
|
||||||
>(TabRouter, {
|
>(TabRouter, {
|
||||||
@@ -43,12 +47,13 @@ function BottomTabNavigator({
|
|||||||
state={state}
|
state={state}
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
descriptors={descriptors}
|
descriptors={descriptors}
|
||||||
|
sceneContainerStyle={sceneContainerStyle}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createNavigatorFactory<
|
export default createNavigatorFactory<
|
||||||
TabNavigationState,
|
TabNavigationState<ParamListBase>,
|
||||||
BottomTabNavigationOptions,
|
BottomTabNavigationOptions,
|
||||||
BottomTabNavigationEventMap,
|
BottomTabNavigationEventMap,
|
||||||
typeof BottomTabNavigator
|
typeof BottomTabNavigator
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ export type LabelPosition = 'beside-icon' | 'below-icon';
|
|||||||
export type BottomTabNavigationHelpers = NavigationHelpers<
|
export type BottomTabNavigationHelpers = NavigationHelpers<
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
BottomTabNavigationEventMap
|
BottomTabNavigationEventMap
|
||||||
>;
|
> &
|
||||||
|
TabActionHelpers<ParamListBase>;
|
||||||
|
|
||||||
export type BottomTabNavigationProp<
|
export type BottomTabNavigationProp<
|
||||||
ParamList extends ParamListBase,
|
ParamList extends ParamListBase,
|
||||||
@@ -41,7 +42,7 @@ export type BottomTabNavigationProp<
|
|||||||
> = NavigationProp<
|
> = NavigationProp<
|
||||||
ParamList,
|
ParamList,
|
||||||
RouteName,
|
RouteName,
|
||||||
TabNavigationState,
|
TabNavigationState<ParamList>,
|
||||||
BottomTabNavigationOptions,
|
BottomTabNavigationOptions,
|
||||||
BottomTabNavigationEventMap
|
BottomTabNavigationEventMap
|
||||||
> &
|
> &
|
||||||
@@ -148,7 +149,7 @@ export type BottomTabNavigationOptions = {
|
|||||||
export type BottomTabDescriptor = Descriptor<
|
export type BottomTabDescriptor = Descriptor<
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
string,
|
string,
|
||||||
TabNavigationState,
|
TabNavigationState<ParamListBase>,
|
||||||
BottomTabNavigationOptions
|
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.
|
* Options for the tab bar which will be passed as props to the tab bar component.
|
||||||
*/
|
*/
|
||||||
tabBarOptions?: T;
|
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`.
|
||||||
|
*/
|
||||||
|
detachInactiveScreens?: boolean;
|
||||||
|
/**
|
||||||
|
* Style object for the component wrapping the screen content.
|
||||||
|
*/
|
||||||
|
sceneContainerStyle?: StyleProp<ViewStyle>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BottomTabBarOptions = {
|
export type BottomTabBarOptions = {
|
||||||
@@ -240,7 +251,7 @@ export type BottomTabBarOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type BottomTabBarProps<T = BottomTabBarOptions> = T & {
|
export type BottomTabBarProps<T = BottomTabBarOptions> = T & {
|
||||||
state: TabNavigationState;
|
state: TabNavigationState<ParamListBase>;
|
||||||
descriptors: BottomTabDescriptorMap;
|
descriptors: BottomTabDescriptorMap;
|
||||||
navigation: NavigationHelpers<ParamListBase, BottomTabNavigationEventMap>;
|
navigation: NavigationHelpers<ParamListBase, BottomTabNavigationEventMap>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View, StyleSheet } from 'react-native';
|
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
NavigationHelpersContext,
|
NavigationHelpersContext,
|
||||||
|
ParamListBase,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
@@ -19,7 +20,7 @@ import type {
|
|||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
type Props = BottomTabNavigationConfig & {
|
type Props = BottomTabNavigationConfig & {
|
||||||
state: TabNavigationState;
|
state: TabNavigationState<ParamListBase>;
|
||||||
navigation: BottomTabNavigationHelpers;
|
navigation: BottomTabNavigationHelpers;
|
||||||
descriptors: BottomTabDescriptorMap;
|
descriptors: BottomTabDescriptorMap;
|
||||||
};
|
};
|
||||||
@@ -31,9 +32,11 @@ type State = {
|
|||||||
function SceneContent({
|
function SceneContent({
|
||||||
isFocused,
|
isFocused,
|
||||||
children,
|
children,
|
||||||
|
style,
|
||||||
}: {
|
}: {
|
||||||
isFocused: boolean;
|
isFocused: boolean;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
style?: StyleProp<ViewStyle>;
|
||||||
}) {
|
}) {
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
|
||||||
@@ -41,7 +44,7 @@ function SceneContent({
|
|||||||
<View
|
<View
|
||||||
accessibilityElementsHidden={!isFocused}
|
accessibilityElementsHidden={!isFocused}
|
||||||
importantForAccessibility={isFocused ? 'auto' : 'no-hide-descendants'}
|
importantForAccessibility={isFocused ? 'auto' : 'no-hide-descendants'}
|
||||||
style={[styles.content, { backgroundColor: colors.background }]}
|
style={[styles.content, { backgroundColor: colors.background }, style]}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
@@ -85,7 +88,14 @@ export default class BottomTabView extends React.Component<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { state, descriptors, navigation, lazy } = this.props;
|
const {
|
||||||
|
state,
|
||||||
|
descriptors,
|
||||||
|
navigation,
|
||||||
|
lazy,
|
||||||
|
detachInactiveScreens = true,
|
||||||
|
sceneContainerStyle,
|
||||||
|
} = this.props;
|
||||||
const { routes } = state;
|
const { routes } = state;
|
||||||
const { loaded } = this.state;
|
const { loaded } = this.state;
|
||||||
|
|
||||||
@@ -93,7 +103,11 @@ export default class BottomTabView extends React.Component<Props, State> {
|
|||||||
<NavigationHelpersContext.Provider value={navigation}>
|
<NavigationHelpersContext.Provider value={navigation}>
|
||||||
<SafeAreaProviderCompat>
|
<SafeAreaProviderCompat>
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<ScreenContainer style={styles.pages}>
|
<ScreenContainer
|
||||||
|
// @ts-ignore
|
||||||
|
enabled={detachInactiveScreens}
|
||||||
|
style={styles.pages}
|
||||||
|
>
|
||||||
{routes.map((route, index) => {
|
{routes.map((route, index) => {
|
||||||
const descriptor = descriptors[route.key];
|
const descriptor = descriptors[route.key];
|
||||||
const { unmountOnBlur } = descriptor.options;
|
const { unmountOnBlur } = descriptor.options;
|
||||||
@@ -113,8 +127,12 @@ export default class BottomTabView extends React.Component<Props, State> {
|
|||||||
key={route.key}
|
key={route.key}
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
isVisible={isFocused}
|
isVisible={isFocused}
|
||||||
|
enabled={detachInactiveScreens}
|
||||||
>
|
>
|
||||||
<SceneContent isFocused={isFocused}>
|
<SceneContent
|
||||||
|
isFocused={isFocused}
|
||||||
|
style={sceneContainerStyle}
|
||||||
|
>
|
||||||
{descriptor.render()}
|
{descriptor.render()}
|
||||||
</SceneContent>
|
</SceneContent>
|
||||||
</ResourceSavingScene>
|
</ResourceSavingScene>
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Platform, StyleSheet, View } from 'react-native';
|
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 = {
|
type Props = {
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
enabled: boolean;
|
||||||
style?: any;
|
style?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -16,8 +22,17 @@ export default class ResourceSavingScene extends React.Component<Props> {
|
|||||||
if (screensEnabled?.() && Platform.OS !== 'web') {
|
if (screensEnabled?.() && Platform.OS !== 'web') {
|
||||||
const { isVisible, ...rest } = this.props;
|
const { isVisible, ...rest } = this.props;
|
||||||
|
|
||||||
// @ts-expect-error: stackPresentation is incorrectly marked as required
|
if (shouldUseActivityState) {
|
||||||
return <Screen active={isVisible ? 1 : 0} {...rest} />;
|
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;
|
const { isVisible, children, style, ...rest } = this.props;
|
||||||
|
|||||||
@@ -3,6 +3,42 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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)
|
## [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
|
**Note:** Version bump only for package @react-navigation/compat
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/compat",
|
"name": "@react-navigation/compat",
|
||||||
"description": "Compatibility layer to write navigator definitions in static configuration format",
|
"description": "Compatibility layer to write navigator definitions in static configuration format",
|
||||||
"version": "5.2.8",
|
"version": "5.3.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -32,8 +32,8 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-native-community/bob": "^0.16.2",
|
||||||
"@react-navigation/native": "^5.7.6",
|
"@react-navigation/native": "^5.8.3",
|
||||||
"@types/react": "^16.9.51",
|
"@types/react": "^16.9.53",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
"typescript": "^4.0.3"
|
"typescript": "^4.0.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ type EventName =
|
|||||||
export default function createCompatNavigationProp<
|
export default function createCompatNavigationProp<
|
||||||
NavigationPropType extends NavigationProp<ParamListBase>,
|
NavigationPropType extends NavigationProp<ParamListBase>,
|
||||||
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
||||||
infer P
|
infer P,
|
||||||
|
any,
|
||||||
|
any,
|
||||||
|
any,
|
||||||
|
any
|
||||||
>
|
>
|
||||||
? P
|
? P
|
||||||
: ParamListBase
|
: ParamListBase
|
||||||
|
|||||||
@@ -32,7 +32,11 @@ export default function createCompatNavigatorFactory<
|
|||||||
const createCompatNavigator = <
|
const createCompatNavigator = <
|
||||||
NavigationPropType extends NavigationProp<any, any, any, any, any>,
|
NavigationPropType extends NavigationProp<any, any, any, any, any>,
|
||||||
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
||||||
infer P
|
infer P,
|
||||||
|
any,
|
||||||
|
any,
|
||||||
|
any,
|
||||||
|
any
|
||||||
>
|
>
|
||||||
? P
|
? P
|
||||||
: ParamListBase,
|
: ParamListBase,
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import {
|
|||||||
TabRouter,
|
TabRouter,
|
||||||
TabRouterOptions,
|
TabRouterOptions,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
|
TabActionHelpers,
|
||||||
|
ParamListBase,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import createCompatNavigatorFactory from './createCompatNavigatorFactory';
|
import createCompatNavigatorFactory from './createCompatNavigatorFactory';
|
||||||
|
|
||||||
@@ -12,17 +14,21 @@ type Props = DefaultNavigatorOptions<{}> & TabRouterOptions;
|
|||||||
|
|
||||||
function SwitchNavigator(props: Props) {
|
function SwitchNavigator(props: Props) {
|
||||||
const { state, descriptors } = useNavigationBuilder<
|
const { state, descriptors } = useNavigationBuilder<
|
||||||
TabNavigationState,
|
TabNavigationState<ParamListBase>,
|
||||||
TabRouterOptions,
|
TabRouterOptions,
|
||||||
{},
|
{},
|
||||||
{}
|
{},
|
||||||
|
TabActionHelpers<ParamListBase>
|
||||||
>(TabRouter, props);
|
>(TabRouter, props);
|
||||||
|
|
||||||
return descriptors[state.routes[state.index].key].render();
|
return descriptors[state.routes[state.index].key].render();
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createCompatNavigatorFactory(
|
export default createCompatNavigatorFactory(
|
||||||
createNavigatorFactory<TabNavigationState, {}, {}, typeof SwitchNavigator>(
|
createNavigatorFactory<
|
||||||
SwitchNavigator
|
TabNavigationState<ParamListBase>,
|
||||||
)
|
{},
|
||||||
|
{},
|
||||||
|
typeof SwitchNavigator
|
||||||
|
>(SwitchNavigator)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ import type * as helpers from './helpers';
|
|||||||
export type CompatNavigationProp<
|
export type CompatNavigationProp<
|
||||||
NavigationPropType extends NavigationProp<ParamListBase>,
|
NavigationPropType extends NavigationProp<ParamListBase>,
|
||||||
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
||||||
infer P
|
infer P,
|
||||||
|
any,
|
||||||
|
any,
|
||||||
|
any,
|
||||||
|
any
|
||||||
>
|
>
|
||||||
? P
|
? P
|
||||||
: ParamListBase,
|
: ParamListBase,
|
||||||
@@ -67,7 +71,11 @@ export type CompatScreenType<
|
|||||||
export type CompatRouteConfig<
|
export type CompatRouteConfig<
|
||||||
NavigationPropType extends NavigationProp<ParamListBase>,
|
NavigationPropType extends NavigationProp<ParamListBase>,
|
||||||
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
||||||
infer P
|
infer P,
|
||||||
|
any,
|
||||||
|
any,
|
||||||
|
any,
|
||||||
|
any
|
||||||
>
|
>
|
||||||
? P
|
? P
|
||||||
: ParamListBase
|
: ParamListBase
|
||||||
|
|||||||
@@ -3,6 +3,59 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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)
|
## [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
|
**Note:** Version bump only for package @react-navigation/core
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/core",
|
"name": "@react-navigation/core",
|
||||||
"description": "Core utilities for building navigators",
|
"description": "Core utilities for building navigators",
|
||||||
"version": "5.12.5",
|
"version": "5.13.3",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
"react-native",
|
"react-native",
|
||||||
@@ -35,17 +35,17 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-navigation/routers": "^5.4.12",
|
"@react-navigation/routers": "^5.5.1",
|
||||||
"escape-string-regexp": "^4.0.0",
|
"escape-string-regexp": "^4.0.0",
|
||||||
"nanoid": "^3.1.12",
|
"nanoid": "^3.1.15",
|
||||||
"query-string": "^6.13.5",
|
"query-string": "^6.13.6",
|
||||||
"react-is": "^16.13.0",
|
"react-is": "^16.13.0",
|
||||||
"use-subscription": "^1.4.0"
|
"use-subscription": "^1.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-native-community/bob": "^0.16.2",
|
||||||
"@testing-library/react-native": "^7.0.2",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/react": "^16.9.51",
|
"@types/react": "^16.9.53",
|
||||||
"@types/react-is": "^16.7.1",
|
"@types/react-is": "^16.7.1",
|
||||||
"@types/use-subscription": "^1.0.0",
|
"@types/use-subscription": "^1.0.0",
|
||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ const BaseNavigationContainer = React.forwardRef(
|
|||||||
{
|
{
|
||||||
initialState,
|
initialState,
|
||||||
onStateChange,
|
onStateChange,
|
||||||
|
onUnhandledAction,
|
||||||
independent,
|
independent,
|
||||||
children,
|
children,
|
||||||
}: NavigationContainerProps,
|
}: NavigationContainerProps,
|
||||||
@@ -342,51 +343,56 @@ const BaseNavigationContainer = React.forwardRef(
|
|||||||
isFirstMountRef.current = false;
|
isFirstMountRef.current = false;
|
||||||
}, [getRootState, emitter, state]);
|
}, [getRootState, emitter, state]);
|
||||||
|
|
||||||
const onUnhandledAction = React.useCallback((action: NavigationAction) => {
|
const defaultOnUnhandledAction = React.useCallback(
|
||||||
if (process.env.NODE_ENV === 'production') {
|
(action: NavigationAction) => {
|
||||||
return;
|
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}'${
|
let message = `The action '${action.type}'${
|
||||||
payload ? ` with payload ${JSON.stringify(action.payload)}` : ''
|
payload ? ` with payload ${JSON.stringify(action.payload)}` : ''
|
||||||
} was not handled by any navigator.`;
|
} was not handled by any navigator.`;
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'NAVIGATE':
|
case 'NAVIGATE':
|
||||||
case 'PUSH':
|
case 'PUSH':
|
||||||
case 'REPLACE':
|
case 'REPLACE':
|
||||||
case 'JUMP_TO':
|
case 'JUMP_TO':
|
||||||
if (payload?.name) {
|
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.`;
|
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 {
|
} 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.`;
|
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;
|
break;
|
||||||
case 'GO_BACK':
|
case 'GO_BACK':
|
||||||
case 'POP':
|
case 'POP':
|
||||||
case 'POP_TO_TOP':
|
case 'POP_TO_TOP':
|
||||||
message += `\n\nIs there any screen to go back to?`;
|
message += `\n\nIs there any screen to go back to?`;
|
||||||
break;
|
break;
|
||||||
case 'OPEN_DRAWER':
|
case 'OPEN_DRAWER':
|
||||||
case 'CLOSE_DRAWER':
|
case 'CLOSE_DRAWER':
|
||||||
case 'TOGGLE_DRAWER':
|
case 'TOGGLE_DRAWER':
|
||||||
message += `\n\nIs your screen inside a Drawer navigator?`;
|
message += `\n\nIs your screen inside a Drawer navigator?`;
|
||||||
break;
|
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 (
|
return (
|
||||||
<ScheduleUpdateContext.Provider value={scheduleContext}>
|
<ScheduleUpdateContext.Provider value={scheduleContext}>
|
||||||
<NavigationBuilderContext.Provider value={builderContext}>
|
<NavigationBuilderContext.Provider value={builderContext}>
|
||||||
<NavigationStateContext.Provider value={context}>
|
<NavigationStateContext.Provider value={context}>
|
||||||
<UnhandledActionContext.Provider value={onUnhandledAction}>
|
<UnhandledActionContext.Provider
|
||||||
|
value={onUnhandledAction ?? defaultOnUnhandledAction}
|
||||||
|
>
|
||||||
<EnsureSingleNavigator>{children}</EnsureSingleNavigator>
|
<EnsureSingleNavigator>{children}</EnsureSingleNavigator>
|
||||||
</UnhandledActionContext.Provider>
|
</UnhandledActionContext.Provider>
|
||||||
</NavigationStateContext.Provider>
|
</NavigationStateContext.Provider>
|
||||||
|
|||||||
@@ -721,3 +721,39 @@ it("throws if the ref hasn't finished initializing", () => {
|
|||||||
|
|
||||||
render(element);
|
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:
|
default:
|
||||||
return BaseRouter.getStateForAction(state, action);
|
return BaseRouter.getStateForAction(state, action);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,32 +43,435 @@ it('gets navigate action from state', () => {
|
|||||||
},
|
},
|
||||||
type: 'NAVIGATE',
|
type: 'NAVIGATE',
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
expect(
|
it('gets navigate action from state for top-level screen', () => {
|
||||||
getActionFromState({
|
const state = {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: 'foo',
|
||||||
|
params: { answer: 42 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getActionFromState(state)).toEqual({
|
||||||
|
payload: {
|
||||||
|
name: 'foo',
|
||||||
|
params: { answer: 42 },
|
||||||
|
},
|
||||||
|
type: 'NAVIGATE',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets navigate 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: [
|
routes: [
|
||||||
{
|
{
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
|
params: { answer: 42 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'bar',
|
||||||
|
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', () => {
|
||||||
|
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 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: {
|
state: {
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
name: 'bar',
|
name: 'qux',
|
||||||
state: {
|
params: {
|
||||||
routes: [
|
author: 'jane',
|
||||||
{
|
|
||||||
name: 'qux',
|
|
||||||
params: { author: 'jane' },
|
|
||||||
},
|
|
||||||
{ name: 'quz' },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{ name: 'quz' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
})
|
},
|
||||||
).toEqual({
|
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: {
|
payload: {
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
params: {
|
params: {
|
||||||
@@ -84,6 +487,262 @@ it('gets navigate 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 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 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 reset action from state', () => {
|
it('gets reset action from state', () => {
|
||||||
const state = {
|
const state = {
|
||||||
routes: [
|
routes: [
|
||||||
|
|||||||
@@ -735,6 +735,20 @@ it('navigates to nested child in a navigator', () => {
|
|||||||
expect(element).toMatchInlineSnapshot(
|
expect(element).toMatchInlineSnapshot(
|
||||||
`"[bar-a, {\\"lol\\":\\"why\\",\\"whoa\\":\\"test\\"}]"`
|
`"[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}]"`
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('navigates to nested child in a navigator with initial: false', () => {
|
it('navigates to nested child in a navigator with initial: false', () => {
|
||||||
@@ -1093,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', () => {
|
it('gives access to internal state', () => {
|
||||||
const TestNavigator = (props: any): any => {
|
const TestNavigator = (props: any): any => {
|
||||||
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ it('sets options with options prop as an object', () => {
|
|||||||
const { state, descriptors } = useNavigationBuilder<
|
const { state, descriptors } = useNavigationBuilder<
|
||||||
NavigationState,
|
NavigationState,
|
||||||
any,
|
any,
|
||||||
|
{},
|
||||||
{ title?: string },
|
{ title?: string },
|
||||||
any
|
any
|
||||||
>(MockRouter, props);
|
>(MockRouter, props);
|
||||||
@@ -67,6 +68,7 @@ it('sets options with options prop as a fuction', () => {
|
|||||||
const { state, descriptors } = useNavigationBuilder<
|
const { state, descriptors } = useNavigationBuilder<
|
||||||
NavigationState,
|
NavigationState,
|
||||||
any,
|
any,
|
||||||
|
{},
|
||||||
{ title?: string },
|
{ title?: string },
|
||||||
any
|
any
|
||||||
>(MockRouter, props);
|
>(MockRouter, props);
|
||||||
@@ -113,6 +115,7 @@ it('sets options with screenOptions prop as an object', () => {
|
|||||||
const { state, descriptors } = useNavigationBuilder<
|
const { state, descriptors } = useNavigationBuilder<
|
||||||
NavigationState,
|
NavigationState,
|
||||||
any,
|
any,
|
||||||
|
{},
|
||||||
{ title?: string },
|
{ title?: string },
|
||||||
any
|
any
|
||||||
>(MockRouter, props);
|
>(MockRouter, props);
|
||||||
@@ -173,6 +176,7 @@ it('sets options with screenOptions prop as a fuction', () => {
|
|||||||
const { state, descriptors } = useNavigationBuilder<
|
const { state, descriptors } = useNavigationBuilder<
|
||||||
NavigationState,
|
NavigationState,
|
||||||
any,
|
any,
|
||||||
|
{},
|
||||||
{ title?: string },
|
{ title?: string },
|
||||||
any
|
any
|
||||||
>(MockRouter, props);
|
>(MockRouter, props);
|
||||||
@@ -245,6 +249,7 @@ it('sets initial options with setOptions', () => {
|
|||||||
const { state, descriptors } = useNavigationBuilder<
|
const { state, descriptors } = useNavigationBuilder<
|
||||||
NavigationState,
|
NavigationState,
|
||||||
any,
|
any,
|
||||||
|
{},
|
||||||
{
|
{
|
||||||
title?: string;
|
title?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
@@ -302,6 +307,7 @@ it('updates options with setOptions', () => {
|
|||||||
NavigationState,
|
NavigationState,
|
||||||
any,
|
any,
|
||||||
any,
|
any,
|
||||||
|
any,
|
||||||
any
|
any
|
||||||
>(MockRouter, props);
|
>(MockRouter, props);
|
||||||
const { render, options } = descriptors[state.routes[state.index].key];
|
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<
|
const { state, descriptors } = useNavigationBuilder<
|
||||||
NavigationState,
|
NavigationState,
|
||||||
any,
|
any,
|
||||||
|
{},
|
||||||
{ title?: string },
|
{ title?: string },
|
||||||
any
|
any
|
||||||
>(MockRouter, props);
|
>(MockRouter, props);
|
||||||
@@ -441,6 +448,7 @@ it(`returns false for canGoBack when current router doesn't handle GO_BACK`, ()
|
|||||||
NavigationState,
|
NavigationState,
|
||||||
any,
|
any,
|
||||||
any,
|
any,
|
||||||
|
any,
|
||||||
any
|
any
|
||||||
>(TestRouter, props);
|
>(TestRouter, props);
|
||||||
|
|
||||||
@@ -491,6 +499,7 @@ it('returns true for canGoBack when current router handles GO_BACK', () => {
|
|||||||
const { state, descriptors } = useNavigationBuilder<
|
const { state, descriptors } = useNavigationBuilder<
|
||||||
NavigationState,
|
NavigationState,
|
||||||
any,
|
any,
|
||||||
|
{},
|
||||||
{ title?: string },
|
{ title?: string },
|
||||||
any
|
any
|
||||||
>(ParentRouter, props);
|
>(ParentRouter, props);
|
||||||
@@ -501,6 +510,7 @@ it('returns true for canGoBack when current router handles GO_BACK', () => {
|
|||||||
const { state, descriptors } = useNavigationBuilder<
|
const { state, descriptors } = useNavigationBuilder<
|
||||||
NavigationState,
|
NavigationState,
|
||||||
any,
|
any,
|
||||||
|
{},
|
||||||
{ title?: string },
|
{ title?: string },
|
||||||
any
|
any
|
||||||
>(MockRouter, props);
|
>(MockRouter, props);
|
||||||
@@ -558,6 +568,7 @@ it('returns true for canGoBack when parent router handles GO_BACK', () => {
|
|||||||
const { state, descriptors } = useNavigationBuilder<
|
const { state, descriptors } = useNavigationBuilder<
|
||||||
NavigationState,
|
NavigationState,
|
||||||
any,
|
any,
|
||||||
|
{},
|
||||||
{ title?: string },
|
{ title?: string },
|
||||||
any
|
any
|
||||||
>(OverrodeRouter, props);
|
>(OverrodeRouter, props);
|
||||||
@@ -568,6 +579,7 @@ it('returns true for canGoBack when parent router handles GO_BACK', () => {
|
|||||||
const { state, descriptors } = useNavigationBuilder<
|
const { state, descriptors } = useNavigationBuilder<
|
||||||
NavigationState,
|
NavigationState,
|
||||||
any,
|
any,
|
||||||
|
{},
|
||||||
{ title?: string },
|
{ title?: string },
|
||||||
any
|
any
|
||||||
>(MockRouter, props);
|
>(MockRouter, props);
|
||||||
|
|||||||
@@ -1,43 +1,94 @@
|
|||||||
import type { PartialState, NavigationState } from '@react-navigation/routers';
|
import type {
|
||||||
|
Route,
|
||||||
|
PartialRoute,
|
||||||
|
NavigationState,
|
||||||
|
PartialState,
|
||||||
|
CommonActions,
|
||||||
|
} from '@react-navigation/routers';
|
||||||
|
import type { PathConfig, PathConfigMap, NestedNavigateParams } from './types';
|
||||||
|
|
||||||
type NavigateParams = {
|
type ConfigItem = {
|
||||||
screen?: string;
|
initialRouteName?: string;
|
||||||
params?: NavigateParams;
|
screens?: Record<string, ConfigItem>;
|
||||||
initial?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type NavigateAction = {
|
type Options = { initialRouteName?: string; screens: PathConfigMap };
|
||||||
|
|
||||||
|
type NavigateAction<State extends NavigationState> = {
|
||||||
type: 'NAVIGATE';
|
type: 'NAVIGATE';
|
||||||
payload: { name: string; params: NavigateParams };
|
payload: {
|
||||||
|
name: string;
|
||||||
|
params?: NestedNavigateParams<State>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function getActionFromState(
|
export default function getActionFromState(
|
||||||
state: PartialState<NavigationState>
|
state: PartialState<NavigationState>,
|
||||||
): NavigateAction | undefined {
|
options?: Options
|
||||||
if (state.routes.length === 0) {
|
): 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;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to construct payload for a `NAVIGATE` action from the state
|
if (
|
||||||
// This lets us preserve the navigation state and not lose it
|
!(
|
||||||
let route = state.routes[state.routes.length - 1];
|
routes.length === 1 ||
|
||||||
|
(routes.length === 2 &&
|
||||||
|
routes[0].name === normalizedConfig?.initialRouteName)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
type: 'RESET',
|
||||||
|
payload: state,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let payload: { name: string; params: NavigateParams } = {
|
const route = state.routes[state.index ?? state.routes.length - 1];
|
||||||
name: route.name,
|
|
||||||
params: { ...route.params },
|
|
||||||
};
|
|
||||||
|
|
||||||
let current = route.state;
|
let current: PartialState<NavigationState> | undefined = route?.state;
|
||||||
let params = payload.params;
|
let config: ConfigItem | undefined = normalizedConfig?.screens?.[route?.name];
|
||||||
|
let params: NestedNavigateParams<NavigationState> = { ...route.params };
|
||||||
|
|
||||||
|
let payload = route ? { name: route.name, params } : undefined;
|
||||||
|
|
||||||
while (current) {
|
while (current) {
|
||||||
if (current.routes.length === 0) {
|
if (current.routes.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
route = current.routes[current.routes.length - 1];
|
const routes =
|
||||||
params.initial = current.routes.length === 1;
|
current.index != null
|
||||||
params.screen = route.name;
|
? current.routes.slice(0, current.index + 1)
|
||||||
|
: current.routes;
|
||||||
|
|
||||||
|
const route: Route<string> | PartialRoute<Route<string>> =
|
||||||
|
routes[routes.length - 1];
|
||||||
|
|
||||||
|
if (routes.length === 1) {
|
||||||
|
params.initial = true;
|
||||||
|
params.screen = route.name;
|
||||||
|
params.state = undefined; // Explicitly set to override existing value when merging params
|
||||||
|
} else if (
|
||||||
|
routes.length === 2 &&
|
||||||
|
routes[0].key === undefined &&
|
||||||
|
routes[0].name === config?.initialRouteName
|
||||||
|
) {
|
||||||
|
params.initial = false;
|
||||||
|
params.screen = route.name;
|
||||||
|
params.state = undefined;
|
||||||
|
} else {
|
||||||
|
params.initial = undefined;
|
||||||
|
params.screen = undefined;
|
||||||
|
params.params = undefined;
|
||||||
|
params.state = current;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (route.state) {
|
if (route.state) {
|
||||||
params.params = { ...route.params };
|
params.params = { ...route.params };
|
||||||
@@ -47,10 +98,34 @@ export default function getActionFromState(
|
|||||||
}
|
}
|
||||||
|
|
||||||
current = route.state;
|
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 {
|
return {
|
||||||
type: 'NAVIGATE',
|
type: 'NAVIGATE',
|
||||||
payload,
|
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;
|
||||||
|
}, {});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import queryString from 'query-string';
|
import * as queryString from 'query-string';
|
||||||
import type {
|
import type {
|
||||||
NavigationState,
|
NavigationState,
|
||||||
PartialState,
|
PartialState,
|
||||||
@@ -35,7 +35,7 @@ const getActiveRoute = (state: State): { name: string; params?: object } => {
|
|||||||
/**
|
/**
|
||||||
* Utility to serialize a navigation state object to a path string.
|
* Utility to serialize a navigation state object to a path string.
|
||||||
*
|
*
|
||||||
* Example:
|
* @example
|
||||||
* ```js
|
* ```js
|
||||||
* getPathFromState(
|
* getPathFromState(
|
||||||
* {
|
* {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import escape from 'escape-string-regexp';
|
import escape from 'escape-string-regexp';
|
||||||
import queryString from 'query-string';
|
import * as queryString from 'query-string';
|
||||||
import type {
|
import type {
|
||||||
NavigationState,
|
NavigationState,
|
||||||
PartialState,
|
PartialState,
|
||||||
@@ -37,7 +37,7 @@ type ResultState = PartialState<NavigationState> & {
|
|||||||
* Utility to parse a path string to initial state object accepted by the container.
|
* 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.
|
* This is useful for deep linking when we need to handle the incoming URL.
|
||||||
*
|
*
|
||||||
* Example:
|
* @example
|
||||||
* ```js
|
* ```js
|
||||||
* getStateFromPath(
|
* getStateFromPath(
|
||||||
* '/chat/jane/42',
|
* '/chat/jane/42',
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ import type {
|
|||||||
} from '@react-navigation/routers';
|
} from '@react-navigation/routers';
|
||||||
|
|
||||||
export type DefaultNavigatorOptions<
|
export type DefaultNavigatorOptions<
|
||||||
ScreenOptions extends {}
|
ScreenOptions extends {},
|
||||||
> = DefaultRouterOptions & {
|
ParamList extends ParamListBase = ParamListBase
|
||||||
|
> = DefaultRouterOptions<Extract<keyof ParamList, string>> & {
|
||||||
/**
|
/**
|
||||||
* Children React Elements to extract the route configuration from.
|
* Children React Elements to extract the route configuration from.
|
||||||
* Only `Screen` components are supported as children.
|
* Only `Screen` components are supported as children.
|
||||||
@@ -23,7 +24,7 @@ export type DefaultNavigatorOptions<
|
|||||||
screenOptions?:
|
screenOptions?:
|
||||||
| ScreenOptions
|
| ScreenOptions
|
||||||
| ((props: {
|
| ((props: {
|
||||||
route: RouteProp<ParamListBase, string>;
|
route: RouteProp<ParamList, keyof ParamList>;
|
||||||
navigation: any;
|
navigation: any;
|
||||||
}) => ScreenOptions);
|
}) => ScreenOptions);
|
||||||
};
|
};
|
||||||
@@ -237,6 +238,10 @@ export type NavigationContainerProps = {
|
|||||||
* Callback which is called with the latest navigation state when it changes.
|
* Callback which is called with the latest navigation state when it changes.
|
||||||
*/
|
*/
|
||||||
onStateChange?: (state: NavigationState | undefined) => void;
|
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.
|
* 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.
|
* 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<
|
export type NavigationProp<
|
||||||
ParamList extends ParamListBase,
|
ParamList extends ParamListBase,
|
||||||
RouteName extends keyof ParamList = string,
|
RouteName extends keyof ParamList = string,
|
||||||
State extends NavigationState = NavigationState,
|
State extends NavigationState = NavigationState<ParamList>,
|
||||||
ScreenOptions extends {} = {},
|
ScreenOptions extends {} = {},
|
||||||
EventMap extends EventMapBase = {}
|
EventMap extends EventMapBase = {}
|
||||||
> = NavigationHelpersCommon<ParamList, State> & {
|
> = NavigationHelpersCommon<ParamList, State> & {
|
||||||
@@ -277,20 +282,7 @@ export type NavigationProp<
|
|||||||
export type RouteProp<
|
export type RouteProp<
|
||||||
ParamList extends ParamListBase,
|
ParamList extends ParamListBase,
|
||||||
RouteName extends keyof ParamList
|
RouteName extends keyof ParamList
|
||||||
> = Omit<Route<Extract<RouteName, string>>, 'params'> &
|
> = Route<Extract<RouteName, string>, ParamList[RouteName]>;
|
||||||
(undefined extends ParamList[RouteName]
|
|
||||||
? Readonly<{
|
|
||||||
/**
|
|
||||||
* Params for this route
|
|
||||||
*/
|
|
||||||
params?: Readonly<ParamList[RouteName]>;
|
|
||||||
}>
|
|
||||||
: Readonly<{
|
|
||||||
/**
|
|
||||||
* Params for this route
|
|
||||||
*/
|
|
||||||
params: Readonly<ParamList[RouteName]>;
|
|
||||||
}>);
|
|
||||||
|
|
||||||
export type CompositeNavigationProp<
|
export type CompositeNavigationProp<
|
||||||
A extends NavigationProp<ParamListBase, string, any, any>,
|
A extends NavigationProp<ParamListBase, string, any, any>,
|
||||||
@@ -498,14 +490,11 @@ export type TypedNavigator<
|
|||||||
* Navigator component which manages the child screens.
|
* Navigator component which manages the child screens.
|
||||||
*/
|
*/
|
||||||
Navigator: React.ComponentType<
|
Navigator: React.ComponentType<
|
||||||
Omit<React.ComponentProps<Navigator>, keyof DefaultNavigatorOptions<any>> &
|
Omit<
|
||||||
Omit<DefaultNavigatorOptions<ScreenOptions>, 'initialRouteName'> & {
|
React.ComponentProps<Navigator>,
|
||||||
/**
|
keyof DefaultNavigatorOptions<any, any>
|
||||||
* Name of the route to focus by on initial render.
|
> &
|
||||||
* If not specified, usually the first route is used.
|
DefaultNavigatorOptions<ScreenOptions, ParamList>
|
||||||
*/
|
|
||||||
initialRouteName?: keyof ParamList;
|
|
||||||
}
|
|
||||||
>;
|
>;
|
||||||
/**
|
/**
|
||||||
* Component used for specifying route configuration.
|
* Component used for specifying route configuration.
|
||||||
@@ -515,6 +504,20 @@ export type TypedNavigator<
|
|||||||
) => null;
|
) => null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NestedNavigateParams<State extends NavigationState> =
|
||||||
|
| {
|
||||||
|
screen?: string;
|
||||||
|
params?: object;
|
||||||
|
initial?: boolean;
|
||||||
|
state?: never;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
screen?: never;
|
||||||
|
params?: never;
|
||||||
|
initial?: never;
|
||||||
|
state?: PartialState<State> | State;
|
||||||
|
};
|
||||||
|
|
||||||
export type PathConfig = {
|
export type PathConfig = {
|
||||||
path?: string;
|
path?: string;
|
||||||
exact?: boolean;
|
exact?: boolean;
|
||||||
|
|||||||
@@ -23,30 +23,27 @@ import useFocusEvents from './useFocusEvents';
|
|||||||
import useOnRouteFocus from './useOnRouteFocus';
|
import useOnRouteFocus from './useOnRouteFocus';
|
||||||
import useChildListeners from './useChildListeners';
|
import useChildListeners from './useChildListeners';
|
||||||
import useFocusedListenersChildrenAdapter from './useFocusedListenersChildrenAdapter';
|
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 {
|
import {
|
||||||
DefaultNavigatorOptions,
|
DefaultNavigatorOptions,
|
||||||
RouteConfig,
|
RouteConfig,
|
||||||
PrivateValueStore,
|
PrivateValueStore,
|
||||||
EventMapBase,
|
EventMapBase,
|
||||||
EventMapCore,
|
EventMapCore,
|
||||||
|
NestedNavigateParams,
|
||||||
} from './types';
|
} 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
|
// This is to make TypeScript compiler happy
|
||||||
// eslint-disable-next-line babel/no-unused-expressions
|
// eslint-disable-next-line babel/no-unused-expressions
|
||||||
PrivateValueStore;
|
PrivateValueStore;
|
||||||
|
|
||||||
type NavigatorRoute = {
|
type NavigatorRoute<State extends NavigationState> = {
|
||||||
key: string;
|
key: string;
|
||||||
params?: {
|
params?: NestedNavigateParams<State>;
|
||||||
screen?: string;
|
|
||||||
params?: object;
|
|
||||||
initial?: boolean;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,6 +179,7 @@ const getRouteConfigsFromChildren = <
|
|||||||
export default function useNavigationBuilder<
|
export default function useNavigationBuilder<
|
||||||
State extends NavigationState,
|
State extends NavigationState,
|
||||||
RouterOptions extends DefaultRouterOptions,
|
RouterOptions extends DefaultRouterOptions,
|
||||||
|
ActionHelpers extends Record<string, () => void>,
|
||||||
ScreenOptions extends {},
|
ScreenOptions extends {},
|
||||||
EventMap extends Record<string, any>
|
EventMap extends Record<string, any>
|
||||||
>(
|
>(
|
||||||
@@ -191,20 +189,15 @@ export default function useNavigationBuilder<
|
|||||||
const navigatorKey = useRegisterNavigator();
|
const navigatorKey = useRegisterNavigator();
|
||||||
|
|
||||||
const route = React.useContext(NavigationRouteContext) as
|
const route = React.useContext(NavigationRouteContext) as
|
||||||
| NavigatorRoute
|
| NavigatorRoute<State>
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
const previousNestedParamsRef = React.useRef(route?.params);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
previousNestedParamsRef.current = route?.params;
|
|
||||||
}, [route]);
|
|
||||||
|
|
||||||
const { children, ...rest } = options;
|
const { children, ...rest } = options;
|
||||||
const { current: router } = React.useRef<Router<State, any>>(
|
const { current: router } = React.useRef<Router<State, any>>(
|
||||||
createRouter({
|
createRouter({
|
||||||
...((rest as unknown) as RouterOptions),
|
...((rest as unknown) as RouterOptions),
|
||||||
...(route?.params &&
|
...(route?.params &&
|
||||||
|
route.params.state == null &&
|
||||||
route.params.initial !== false &&
|
route.params.initial !== false &&
|
||||||
typeof route.params.screen === 'string'
|
typeof route.params.screen === 'string'
|
||||||
? { initialRouteName: route.params.screen }
|
? { initialRouteName: route.params.screen }
|
||||||
@@ -239,7 +232,9 @@ export default function useNavigationBuilder<
|
|||||||
(acc, curr) => {
|
(acc, curr) => {
|
||||||
const { initialParams } = screens[curr];
|
const { initialParams } = screens[curr];
|
||||||
const initialParamsFromParams =
|
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
|
? route.params.params
|
||||||
: undefined;
|
: 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)
|
// 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
|
// Otherwise assume that the state was provided as initial state
|
||||||
// So we need to rehydrate it to make it usable
|
// 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 [
|
return [
|
||||||
router.getInitialState({
|
router.getInitialState({
|
||||||
routeNames,
|
routeNames,
|
||||||
@@ -297,10 +295,13 @@ export default function useNavigationBuilder<
|
|||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
router.getRehydratedState(currentState as PartialState<State>, {
|
router.getRehydratedState(
|
||||||
routeNames,
|
route?.params?.state ?? (currentState as PartialState<State>),
|
||||||
routeParamList,
|
{
|
||||||
}),
|
routeNames,
|
||||||
|
routeParamList,
|
||||||
|
}
|
||||||
|
),
|
||||||
false,
|
false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -331,21 +332,54 @@ export default function useNavigationBuilder<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const previousNestedParamsRef = React.useRef(route?.params);
|
||||||
typeof route?.params?.screen === 'string' &&
|
|
||||||
(route.params !== previousNestedParamsRef.current ||
|
React.useEffect(() => {
|
||||||
(route.params.initial === false && isFirstStateInitialization))
|
previousNestedParamsRef.current = route?.params;
|
||||||
) {
|
}, [route?.params]);
|
||||||
// If the route was updated with new name and/or params, we should navigate there
|
|
||||||
|
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
|
||||||
|
// So we need to make sure to reuse it only if:
|
||||||
|
// - The screen is the same, so navigation happened with same params
|
||||||
|
// - Params have actually changed
|
||||||
|
// - It's the first navigation during initialization
|
||||||
|
const params = (
|
||||||
|
route.params.screen === nextState.routes[nextState.index].name
|
||||||
|
? route.params.screen === previousParams?.screen
|
||||||
|
: route.params.params !== previousParams?.params ||
|
||||||
|
(route.params.initial === false && isFirstStateInitialization)
|
||||||
|
)
|
||||||
|
? route.params.params
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
// If the route was updated with new screen name and/or params, we should navigate there
|
||||||
|
action = CommonActions.navigate(route.params.screen, params);
|
||||||
|
}
|
||||||
|
|
||||||
// The update should be limited to current navigator only, so we call the router manually
|
// The update should be limited to current navigator only, so we call the router manually
|
||||||
const updatedState = router.getStateForAction(
|
const updatedState = action
|
||||||
nextState,
|
? router.getStateForAction(nextState, action, {
|
||||||
CommonActions.navigate(route.params.screen, route.params.params),
|
routeNames,
|
||||||
{
|
routeParamList,
|
||||||
routeNames,
|
})
|
||||||
routeParamList,
|
: null;
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
nextState =
|
nextState =
|
||||||
updatedState !== null
|
updatedState !== null
|
||||||
@@ -484,7 +518,12 @@ export default function useNavigationBuilder<
|
|||||||
setState,
|
setState,
|
||||||
});
|
});
|
||||||
|
|
||||||
const navigation = useNavigationHelpers<State, NavigationAction, EventMap>({
|
const navigation = useNavigationHelpers<
|
||||||
|
State,
|
||||||
|
ActionHelpers,
|
||||||
|
NavigationAction,
|
||||||
|
EventMap
|
||||||
|
>({
|
||||||
onAction,
|
onAction,
|
||||||
getState,
|
getState,
|
||||||
emitter,
|
emitter,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ type Options<State extends NavigationState, Action extends NavigationAction> = {
|
|||||||
*/
|
*/
|
||||||
export default function useNavigationHelpers<
|
export default function useNavigationHelpers<
|
||||||
State extends NavigationState,
|
State extends NavigationState,
|
||||||
|
ActionHelpers extends Record<string, () => void>,
|
||||||
Action extends NavigationAction,
|
Action extends NavigationAction,
|
||||||
EventMap extends Record<string, any>
|
EventMap extends Record<string, any>
|
||||||
>({ onAction, getState, emitter, router }: Options<State, Action>) {
|
>({ onAction, getState, emitter, router }: Options<State, Action>) {
|
||||||
@@ -85,7 +86,8 @@ export default function useNavigationHelpers<
|
|||||||
dangerouslyGetParent: () => parentNavigationHelpers as any,
|
dangerouslyGetParent: () => parentNavigationHelpers as any,
|
||||||
dangerouslyGetState: getState,
|
dangerouslyGetState: getState,
|
||||||
} as NavigationHelpers<ParamListBase, EventMap> &
|
} as NavigationHelpers<ParamListBase, EventMap> &
|
||||||
(NavigationProp<ParamListBase, string, any, any, any> | undefined);
|
(NavigationProp<ParamListBase, string, any, any, any> | undefined) &
|
||||||
|
ActionHelpers;
|
||||||
}, [
|
}, [
|
||||||
emitter.emit,
|
emitter.emit,
|
||||||
getState,
|
getState,
|
||||||
|
|||||||
@@ -3,6 +3,38 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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)
|
## [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
|
**Note:** Version bump only for package @react-navigation/devtools
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/devtools",
|
"name": "@react-navigation/devtools",
|
||||||
"description": "Developer tools for React Navigation",
|
"description": "Developer tools for React Navigation",
|
||||||
"version": "5.1.7",
|
"version": "5.1.11",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
"react-native",
|
"react-native",
|
||||||
@@ -36,14 +36,14 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-navigation/core": "^5.12.5",
|
"@react-navigation/core": "^5.13.3",
|
||||||
"deep-equal": "^2.0.4"
|
"deep-equal": "^2.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-native-community/bob": "^0.16.2",
|
||||||
"@testing-library/react-native": "^7.0.2",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/deep-equal": "^1.0.1",
|
"@types/deep-equal": "^1.0.1",
|
||||||
"@types/react": "^16.9.51",
|
"@types/react": "^16.9.53",
|
||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
"typescript": "^4.0.3"
|
"typescript": "^4.0.3"
|
||||||
|
|||||||
@@ -3,6 +3,43 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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)
|
## [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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/drawer",
|
"name": "@react-navigation/drawer",
|
||||||
"description": "Drawer navigator component with animated transitions and gesturess",
|
"description": "Drawer navigator component with animated transitions and gesturess",
|
||||||
"version": "5.9.3",
|
"version": "5.10.3",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -41,15 +41,15 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^3.1.2",
|
"color": "^3.1.3",
|
||||||
"react-native-iphone-x-helper": "^1.2.1"
|
"react-native-iphone-x-helper": "^1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-native-community/bob": "^0.16.2",
|
||||||
"@react-navigation/native": "^5.7.6",
|
"@react-navigation/native": "^5.8.3",
|
||||||
"@testing-library/react-native": "^7.0.2",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/react": "^16.9.51",
|
"@types/react": "^16.9.53",
|
||||||
"@types/react-native": "^0.63.25",
|
"@types/react-native": "^0.63.30",
|
||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
"react-native": "~0.63.2",
|
"react-native": "~0.63.2",
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
DrawerNavigationState,
|
DrawerNavigationState,
|
||||||
DrawerRouterOptions,
|
DrawerRouterOptions,
|
||||||
DrawerRouter,
|
DrawerRouter,
|
||||||
|
DrawerActionHelpers,
|
||||||
|
ParamListBase,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
import DrawerView from '../views/DrawerView';
|
import DrawerView from '../views/DrawerView';
|
||||||
@@ -28,8 +30,9 @@ function DrawerNavigator({
|
|||||||
...rest
|
...rest
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { state, descriptors, navigation } = useNavigationBuilder<
|
const { state, descriptors, navigation } = useNavigationBuilder<
|
||||||
DrawerNavigationState,
|
DrawerNavigationState<ParamListBase>,
|
||||||
DrawerRouterOptions,
|
DrawerRouterOptions,
|
||||||
|
DrawerActionHelpers<ParamListBase>,
|
||||||
DrawerNavigationOptions,
|
DrawerNavigationOptions,
|
||||||
DrawerNavigationEventMap
|
DrawerNavigationEventMap
|
||||||
>(DrawerRouter, {
|
>(DrawerRouter, {
|
||||||
@@ -51,7 +54,7 @@ function DrawerNavigator({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default createNavigatorFactory<
|
export default createNavigatorFactory<
|
||||||
DrawerNavigationState,
|
DrawerNavigationState<ParamListBase>,
|
||||||
DrawerNavigationOptions,
|
DrawerNavigationOptions,
|
||||||
DrawerNavigationEventMap,
|
DrawerNavigationEventMap,
|
||||||
typeof DrawerNavigator
|
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.
|
* You can pass a custom background color for a drawer or a custom width here.
|
||||||
*/
|
*/
|
||||||
drawerStyle?: StyleProp<ViewStyle>;
|
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 = {
|
export type DrawerNavigationOptions = {
|
||||||
@@ -136,7 +142,7 @@ export type DrawerNavigationOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type DrawerContentComponentProps<T = DrawerContentOptions> = T & {
|
export type DrawerContentComponentProps<T = DrawerContentOptions> = T & {
|
||||||
state: DrawerNavigationState;
|
state: DrawerNavigationState<ParamListBase>;
|
||||||
navigation: DrawerNavigationHelpers;
|
navigation: DrawerNavigationHelpers;
|
||||||
descriptors: DrawerDescriptorMap;
|
descriptors: DrawerDescriptorMap;
|
||||||
/**
|
/**
|
||||||
@@ -195,7 +201,8 @@ export type DrawerNavigationEventMap = {
|
|||||||
export type DrawerNavigationHelpers = NavigationHelpers<
|
export type DrawerNavigationHelpers = NavigationHelpers<
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
DrawerNavigationEventMap
|
DrawerNavigationEventMap
|
||||||
>;
|
> &
|
||||||
|
DrawerActionHelpers<ParamListBase>;
|
||||||
|
|
||||||
export type DrawerNavigationProp<
|
export type DrawerNavigationProp<
|
||||||
ParamList extends ParamListBase,
|
ParamList extends ParamListBase,
|
||||||
@@ -203,7 +210,7 @@ export type DrawerNavigationProp<
|
|||||||
> = NavigationProp<
|
> = NavigationProp<
|
||||||
ParamList,
|
ParamList,
|
||||||
RouteName,
|
RouteName,
|
||||||
DrawerNavigationState,
|
DrawerNavigationState<ParamList>,
|
||||||
DrawerNavigationOptions,
|
DrawerNavigationOptions,
|
||||||
DrawerNavigationEventMap
|
DrawerNavigationEventMap
|
||||||
> &
|
> &
|
||||||
@@ -220,7 +227,7 @@ export type DrawerScreenProps<
|
|||||||
export type DrawerDescriptor = Descriptor<
|
export type DrawerDescriptor = Descriptor<
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
string,
|
string,
|
||||||
DrawerNavigationState,
|
DrawerNavigationState<ParamListBase>,
|
||||||
DrawerNavigationOptions
|
DrawerNavigationOptions
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
CommonActions,
|
CommonActions,
|
||||||
DrawerActions,
|
DrawerActions,
|
||||||
DrawerNavigationState,
|
DrawerNavigationState,
|
||||||
|
ParamListBase,
|
||||||
useLinkBuilder,
|
useLinkBuilder,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import DrawerItem from './DrawerItem';
|
import DrawerItem from './DrawerItem';
|
||||||
@@ -13,7 +14,7 @@ import type {
|
|||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
type Props = Omit<DrawerContentOptions, 'contentContainerStyle' | 'style'> & {
|
type Props = Omit<DrawerContentOptions, 'contentContainerStyle' | 'style'> & {
|
||||||
state: DrawerNavigationState;
|
state: DrawerNavigationState<ParamListBase>;
|
||||||
navigation: DrawerNavigationHelpers;
|
navigation: DrawerNavigationHelpers;
|
||||||
descriptors: DrawerDescriptorMap;
|
descriptors: DrawerDescriptorMap;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
DrawerNavigationState,
|
DrawerNavigationState,
|
||||||
DrawerActions,
|
DrawerActions,
|
||||||
useTheme,
|
useTheme,
|
||||||
|
ParamListBase,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
import { GestureHandlerRootView } from './GestureHandler';
|
import { GestureHandlerRootView } from './GestureHandler';
|
||||||
@@ -31,7 +32,7 @@ import type {
|
|||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
type Props = DrawerNavigationConfig & {
|
type Props = DrawerNavigationConfig & {
|
||||||
state: DrawerNavigationState;
|
state: DrawerNavigationState<ParamListBase>;
|
||||||
navigation: DrawerNavigationHelpers;
|
navigation: DrawerNavigationHelpers;
|
||||||
descriptors: DrawerDescriptorMap;
|
descriptors: DrawerDescriptorMap;
|
||||||
};
|
};
|
||||||
@@ -82,6 +83,7 @@ export default function DrawerView({
|
|||||||
gestureHandlerProps,
|
gestureHandlerProps,
|
||||||
minSwipeDistance,
|
minSwipeDistance,
|
||||||
sceneContainerStyle,
|
sceneContainerStyle,
|
||||||
|
detachInactiveScreens = true,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const [loaded, setLoaded] = React.useState([state.routes[state.index].key]);
|
const [loaded, setLoaded] = React.useState([state.routes[state.index].key]);
|
||||||
const dimensions = useWindowDimensions();
|
const dimensions = useWindowDimensions();
|
||||||
@@ -151,7 +153,8 @@ export default function DrawerView({
|
|||||||
|
|
||||||
const renderContent = () => {
|
const renderContent = () => {
|
||||||
return (
|
return (
|
||||||
<ScreenContainer style={styles.content}>
|
// @ts-ignore
|
||||||
|
<ScreenContainer enabled={detachInactiveScreens} style={styles.content}>
|
||||||
{state.routes.map((route, index) => {
|
{state.routes.map((route, index) => {
|
||||||
const descriptor = descriptors[route.key];
|
const descriptor = descriptors[route.key];
|
||||||
const { unmountOnBlur } = descriptor.options;
|
const { unmountOnBlur } = descriptor.options;
|
||||||
@@ -171,6 +174,7 @@ export default function DrawerView({
|
|||||||
key={route.key}
|
key={route.key}
|
||||||
style={[StyleSheet.absoluteFill, { opacity: isFocused ? 1 : 0 }]}
|
style={[StyleSheet.absoluteFill, { opacity: isFocused ? 1 : 0 }]}
|
||||||
isVisible={isFocused}
|
isVisible={isFocused}
|
||||||
|
enabled={detachInactiveScreens}
|
||||||
>
|
>
|
||||||
{descriptor.render()}
|
{descriptor.render()}
|
||||||
</ResourceSavingScene>
|
</ResourceSavingScene>
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Platform, StyleSheet, View } from 'react-native';
|
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 = {
|
type Props = {
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
enabled: boolean;
|
||||||
style?: any;
|
style?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -16,8 +22,17 @@ export default class ResourceSavingScene extends React.Component<Props> {
|
|||||||
if (screensEnabled?.() && Platform.OS !== 'web') {
|
if (screensEnabled?.() && Platform.OS !== 'web') {
|
||||||
const { isVisible, ...rest } = this.props;
|
const { isVisible, ...rest } = this.props;
|
||||||
|
|
||||||
// @ts-expect-error: stackPresentation is incorrectly marked as required
|
if (shouldUseActivityState) {
|
||||||
return <Screen active={isVisible ? 1 : 0} {...rest} />;
|
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;
|
const { isVisible, children, style, ...rest } = this.props;
|
||||||
|
|||||||
@@ -3,6 +3,43 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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)
|
## [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
|
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/material-bottom-tabs",
|
"name": "@react-navigation/material-bottom-tabs",
|
||||||
"description": "Integration for bottom navigation component from react-native-paper",
|
"description": "Integration for bottom navigation component from react-native-paper",
|
||||||
"version": "5.2.19",
|
"version": "5.3.3",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -42,10 +42,10 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-native-community/bob": "^0.16.2",
|
||||||
"@react-navigation/native": "^5.7.6",
|
"@react-navigation/native": "^5.8.3",
|
||||||
"@testing-library/react-native": "^7.0.2",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/react": "^16.9.51",
|
"@types/react": "^16.9.53",
|
||||||
"@types/react-native": "^0.63.25",
|
"@types/react-native": "^0.63.30",
|
||||||
"@types/react-native-vector-icons": "^6.4.6",
|
"@types/react-native-vector-icons": "^6.4.6",
|
||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
TabRouter,
|
TabRouter,
|
||||||
TabRouterOptions,
|
TabRouterOptions,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
|
TabActionHelpers,
|
||||||
|
ParamListBase,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
import MaterialBottomTabView from '../views/MaterialBottomTabView';
|
import MaterialBottomTabView from '../views/MaterialBottomTabView';
|
||||||
@@ -27,8 +29,9 @@ function MaterialBottomTabNavigator({
|
|||||||
...rest
|
...rest
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { state, descriptors, navigation } = useNavigationBuilder<
|
const { state, descriptors, navigation } = useNavigationBuilder<
|
||||||
TabNavigationState,
|
TabNavigationState<ParamListBase>,
|
||||||
TabRouterOptions,
|
TabRouterOptions,
|
||||||
|
TabActionHelpers<ParamListBase>,
|
||||||
MaterialBottomTabNavigationOptions,
|
MaterialBottomTabNavigationOptions,
|
||||||
MaterialBottomTabNavigationEventMap
|
MaterialBottomTabNavigationEventMap
|
||||||
>(TabRouter, {
|
>(TabRouter, {
|
||||||
@@ -49,7 +52,7 @@ function MaterialBottomTabNavigator({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default createNavigatorFactory<
|
export default createNavigatorFactory<
|
||||||
TabNavigationState,
|
TabNavigationState<ParamListBase>,
|
||||||
MaterialBottomTabNavigationOptions,
|
MaterialBottomTabNavigationOptions,
|
||||||
MaterialBottomTabNavigationEventMap,
|
MaterialBottomTabNavigationEventMap,
|
||||||
typeof MaterialBottomTabNavigator
|
typeof MaterialBottomTabNavigator
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ export type MaterialBottomTabNavigationEventMap = {
|
|||||||
export type MaterialBottomTabNavigationHelpers = NavigationHelpers<
|
export type MaterialBottomTabNavigationHelpers = NavigationHelpers<
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
MaterialBottomTabNavigationEventMap
|
MaterialBottomTabNavigationEventMap
|
||||||
>;
|
> &
|
||||||
|
TabActionHelpers<ParamListBase>;
|
||||||
|
|
||||||
export type MaterialBottomTabNavigationProp<
|
export type MaterialBottomTabNavigationProp<
|
||||||
ParamList extends ParamListBase,
|
ParamList extends ParamListBase,
|
||||||
@@ -27,7 +28,7 @@ export type MaterialBottomTabNavigationProp<
|
|||||||
> = NavigationProp<
|
> = NavigationProp<
|
||||||
ParamList,
|
ParamList,
|
||||||
RouteName,
|
RouteName,
|
||||||
TabNavigationState,
|
TabNavigationState<ParamList>,
|
||||||
MaterialBottomTabNavigationOptions,
|
MaterialBottomTabNavigationOptions,
|
||||||
MaterialBottomTabNavigationEventMap
|
MaterialBottomTabNavigationEventMap
|
||||||
> &
|
> &
|
||||||
@@ -84,7 +85,7 @@ export type MaterialBottomTabNavigationOptions = {
|
|||||||
export type MaterialBottomTabDescriptor = Descriptor<
|
export type MaterialBottomTabDescriptor = Descriptor<
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
string,
|
string,
|
||||||
TabNavigationState,
|
TabNavigationState<ParamListBase>,
|
||||||
MaterialBottomTabNavigationOptions
|
MaterialBottomTabNavigationOptions
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { StyleSheet, Platform } from 'react-native';
|
import { StyleSheet, Platform } from 'react-native';
|
||||||
import { BottomNavigation, DefaultTheme, DarkTheme } from 'react-native-paper';
|
import { BottomNavigation, DefaultTheme, DarkTheme } from 'react-native-paper';
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
||||||
import {
|
import {
|
||||||
NavigationHelpersContext,
|
NavigationHelpersContext,
|
||||||
Route,
|
Route,
|
||||||
@@ -10,6 +9,7 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
useLinkBuilder,
|
useLinkBuilder,
|
||||||
Link,
|
Link,
|
||||||
|
ParamListBase,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
@@ -19,13 +19,55 @@ import type {
|
|||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
type Props = MaterialBottomTabNavigationConfig & {
|
type Props = MaterialBottomTabNavigationConfig & {
|
||||||
state: TabNavigationState;
|
state: TabNavigationState<ParamListBase>;
|
||||||
navigation: MaterialBottomTabNavigationHelpers;
|
navigation: MaterialBottomTabNavigationHelpers;
|
||||||
descriptors: MaterialBottomTabDescriptorMap;
|
descriptors: MaterialBottomTabDescriptorMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Scene = { route: { key: string } };
|
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({
|
function MaterialBottomTabViewInner({
|
||||||
state,
|
state,
|
||||||
navigation,
|
navigation,
|
||||||
|
|||||||
@@ -3,6 +3,42 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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)
|
## [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
|
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/material-top-tabs",
|
"name": "@react-navigation/material-top-tabs",
|
||||||
"description": "Integration for the animated tab view component from react-native-tab-view",
|
"description": "Integration for the animated tab view component from react-native-tab-view",
|
||||||
"version": "5.2.19",
|
"version": "5.3.3",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -41,14 +41,14 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^3.1.2"
|
"color": "^3.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-native-community/bob": "^0.16.2",
|
||||||
"@react-navigation/native": "^5.7.6",
|
"@react-navigation/native": "^5.8.3",
|
||||||
"@testing-library/react-native": "^7.0.2",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/react": "^16.9.51",
|
"@types/react": "^16.9.53",
|
||||||
"@types/react-native": "^0.63.25",
|
"@types/react-native": "^0.63.30",
|
||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
"react-native": "~0.63.2",
|
"react-native": "~0.63.2",
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
TabRouter,
|
TabRouter,
|
||||||
TabRouterOptions,
|
TabRouterOptions,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
|
TabActionHelpers,
|
||||||
|
ParamListBase,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import MaterialTopTabView from '../views/MaterialTopTabView';
|
import MaterialTopTabView from '../views/MaterialTopTabView';
|
||||||
import type {
|
import type {
|
||||||
@@ -26,8 +28,9 @@ function MaterialTopTabNavigator({
|
|||||||
...rest
|
...rest
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { state, descriptors, navigation } = useNavigationBuilder<
|
const { state, descriptors, navigation } = useNavigationBuilder<
|
||||||
TabNavigationState,
|
TabNavigationState<ParamListBase>,
|
||||||
TabRouterOptions,
|
TabRouterOptions,
|
||||||
|
TabActionHelpers<ParamListBase>,
|
||||||
MaterialTopTabNavigationOptions,
|
MaterialTopTabNavigationOptions,
|
||||||
MaterialTopTabNavigationEventMap
|
MaterialTopTabNavigationEventMap
|
||||||
>(TabRouter, {
|
>(TabRouter, {
|
||||||
@@ -48,7 +51,7 @@ function MaterialTopTabNavigator({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default createNavigatorFactory<
|
export default createNavigatorFactory<
|
||||||
TabNavigationState,
|
TabNavigationState<ParamListBase>,
|
||||||
MaterialTopTabNavigationOptions,
|
MaterialTopTabNavigationOptions,
|
||||||
MaterialTopTabNavigationEventMap,
|
MaterialTopTabNavigationEventMap,
|
||||||
typeof MaterialTopTabNavigator
|
typeof MaterialTopTabNavigator
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ export type MaterialTopTabNavigationEventMap = {
|
|||||||
export type MaterialTopTabNavigationHelpers = NavigationHelpers<
|
export type MaterialTopTabNavigationHelpers = NavigationHelpers<
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
MaterialTopTabNavigationEventMap
|
MaterialTopTabNavigationEventMap
|
||||||
>;
|
> &
|
||||||
|
TabActionHelpers<ParamListBase>;
|
||||||
|
|
||||||
export type MaterialTopTabNavigationProp<
|
export type MaterialTopTabNavigationProp<
|
||||||
ParamList extends ParamListBase,
|
ParamList extends ParamListBase,
|
||||||
@@ -45,7 +46,7 @@ export type MaterialTopTabNavigationProp<
|
|||||||
> = NavigationProp<
|
> = NavigationProp<
|
||||||
ParamList,
|
ParamList,
|
||||||
RouteName,
|
RouteName,
|
||||||
TabNavigationState,
|
TabNavigationState<ParamList>,
|
||||||
MaterialTopTabNavigationOptions,
|
MaterialTopTabNavigationOptions,
|
||||||
MaterialTopTabNavigationEventMap
|
MaterialTopTabNavigationEventMap
|
||||||
> &
|
> &
|
||||||
@@ -94,7 +95,7 @@ export type MaterialTopTabNavigationOptions = {
|
|||||||
export type MaterialTopTabDescriptor = Descriptor<
|
export type MaterialTopTabDescriptor = Descriptor<
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
string,
|
string,
|
||||||
TabNavigationState,
|
TabNavigationState<ParamListBase>,
|
||||||
MaterialTopTabNavigationOptions
|
MaterialTopTabNavigationOptions
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@@ -192,7 +193,7 @@ export type MaterialTopTabBarOptions = Partial<
|
|||||||
|
|
||||||
export type MaterialTopTabBarProps = MaterialTopTabBarOptions &
|
export type MaterialTopTabBarProps = MaterialTopTabBarOptions &
|
||||||
SceneRendererProps & {
|
SceneRendererProps & {
|
||||||
state: TabNavigationState;
|
state: TabNavigationState<ParamListBase>;
|
||||||
navigation: NavigationHelpers<
|
navigation: NavigationHelpers<
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
MaterialTopTabNavigationEventMap
|
MaterialTopTabNavigationEventMap
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
NavigationHelpersContext,
|
NavigationHelpersContext,
|
||||||
TabNavigationState,
|
TabNavigationState,
|
||||||
TabActions,
|
TabActions,
|
||||||
|
ParamListBase,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ import type {
|
|||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
type Props = MaterialTopTabNavigationConfig & {
|
type Props = MaterialTopTabNavigationConfig & {
|
||||||
state: TabNavigationState;
|
state: TabNavigationState<ParamListBase>;
|
||||||
navigation: MaterialTopTabNavigationHelpers;
|
navigation: MaterialTopTabNavigationHelpers;
|
||||||
descriptors: MaterialTopTabDescriptorMap;
|
descriptors: MaterialTopTabDescriptorMap;
|
||||||
tabBarPosition?: 'top' | 'bottom';
|
tabBarPosition?: 'top' | 'bottom';
|
||||||
|
|||||||
@@ -3,6 +3,46 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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)
|
## [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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/native",
|
"name": "@react-navigation/native",
|
||||||
"description": "React Native integration for React Navigation",
|
"description": "React Native integration for React Navigation",
|
||||||
"version": "5.7.6",
|
"version": "5.8.3",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native",
|
"react-native",
|
||||||
"react-navigation",
|
"react-navigation",
|
||||||
@@ -37,15 +37,16 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-navigation/core": "^5.12.5",
|
"@react-navigation/core": "^5.13.3",
|
||||||
"nanoid": "^3.1.12"
|
"escape-string-regexp": "^4.0.0",
|
||||||
|
"nanoid": "^3.1.15"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-native-community/bob": "^0.16.2",
|
||||||
"@testing-library/react-native": "^7.0.2",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/react": "^16.9.51",
|
"@types/react": "^16.9.53",
|
||||||
"@types/react-dom": "^16.9.8",
|
"@types/react-dom": "^16.9.8",
|
||||||
"@types/react-native": "^0.63.25",
|
"@types/react-native": "^0.63.30",
|
||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
|
|||||||
@@ -27,12 +27,23 @@ export type LinkingOptions = {
|
|||||||
* The prefixes are stripped from the URL before parsing them.
|
* The prefixes are stripped from the URL before parsing them.
|
||||||
* Usually they are the `scheme` + `host` (e.g. `myapp://chat?user=jane`)
|
* Usually they are the `scheme` + `host` (e.g. `myapp://chat?user=jane`)
|
||||||
* Only applicable on Android and iOS.
|
* 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[];
|
prefixes: string[];
|
||||||
/**
|
/**
|
||||||
* Config to fine-tune how to parse the path.
|
* Config to fine-tune how to parse the path.
|
||||||
*
|
*
|
||||||
* Example:
|
* @example
|
||||||
* ```js
|
* ```js
|
||||||
* {
|
* {
|
||||||
* Chat: {
|
* Chat: {
|
||||||
@@ -43,13 +54,47 @@ export type LinkingOptions = {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
config?: { initialRouteName?: string; screens: PathConfigMap };
|
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).
|
* Custom function to parse the URL to a valid navigation state (advanced).
|
||||||
* Only applicable on Web.
|
|
||||||
*/
|
*/
|
||||||
getStateFromPath?: typeof getStateFromPathDefault;
|
getStateFromPath?: typeof getStateFromPathDefault;
|
||||||
/**
|
/**
|
||||||
* Custom function to convert the state object to a valid URL (advanced).
|
* Custom function to convert the state object to a valid URL (advanced).
|
||||||
|
* Only applicable on Web.
|
||||||
*/
|
*/
|
||||||
getPathFromState?: typeof getPathFromStateDefault;
|
getPathFromState?: typeof getPathFromStateDefault;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default function useLinkProps({ to, action }: Props) {
|
|||||||
const linkTo = useLinkTo();
|
const linkTo = useLinkTo();
|
||||||
|
|
||||||
const onPress = (
|
const onPress = (
|
||||||
e: React.MouseEvent<HTMLAnchorElement, MouseEvent> | GestureResponderEvent
|
e?: React.MouseEvent<HTMLAnchorElement, MouseEvent> | GestureResponderEvent
|
||||||
) => {
|
) => {
|
||||||
let shouldHandle = false;
|
let shouldHandle = false;
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default function useLinkTo() {
|
|||||||
root = current;
|
root = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
const action = getActionFromState(state);
|
const action = getActionFromState(state, options?.config);
|
||||||
|
|
||||||
if (action !== undefined) {
|
if (action !== undefined) {
|
||||||
root.dispatch(action);
|
root.dispatch(action);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
NavigationContainerRef,
|
NavigationContainerRef,
|
||||||
} from '@react-navigation/core';
|
} from '@react-navigation/core';
|
||||||
import type { LinkingOptions } from './types';
|
import type { LinkingOptions } from './types';
|
||||||
|
import escapeStringRegexp from 'escape-string-regexp';
|
||||||
|
|
||||||
let isUsingLinking = false;
|
let isUsingLinking = false;
|
||||||
|
|
||||||
@@ -15,6 +16,22 @@ export default function useLinking(
|
|||||||
enabled = true,
|
enabled = true,
|
||||||
prefixes,
|
prefixes,
|
||||||
config,
|
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,
|
getStateFromPath = getStateFromPathDefault,
|
||||||
}: LinkingOptions
|
}: LinkingOptions
|
||||||
) {
|
) {
|
||||||
@@ -47,19 +64,29 @@ export default function useLinking(
|
|||||||
const enabledRef = React.useRef(enabled);
|
const enabledRef = React.useRef(enabled);
|
||||||
const prefixesRef = React.useRef(prefixes);
|
const prefixesRef = React.useRef(prefixes);
|
||||||
const configRef = React.useRef(config);
|
const configRef = React.useRef(config);
|
||||||
|
const getInitialURLRef = React.useRef(getInitialURL);
|
||||||
const getStateFromPathRef = React.useRef(getStateFromPath);
|
const getStateFromPathRef = React.useRef(getStateFromPath);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
enabledRef.current = enabled;
|
enabledRef.current = enabled;
|
||||||
prefixesRef.current = prefixes;
|
prefixesRef.current = prefixes;
|
||||||
configRef.current = config;
|
configRef.current = config;
|
||||||
|
getInitialURLRef.current = getInitialURL;
|
||||||
getStateFromPathRef.current = getStateFromPath;
|
getStateFromPathRef.current = getStateFromPath;
|
||||||
}, [config, enabled, getStateFromPath, prefixes]);
|
}, [config, enabled, prefixes, getInitialURL, getStateFromPath]);
|
||||||
|
|
||||||
const extractPathFromURL = React.useCallback((url: string) => {
|
const extractPathFromURL = React.useCallback((url: string) => {
|
||||||
for (const prefix of prefixesRef.current) {
|
for (const prefix of prefixesRef.current) {
|
||||||
if (url.startsWith(prefix)) {
|
const protocol = prefix.match(/^[^:]+:\/\//)?.[0] ?? '';
|
||||||
return url.replace(prefix, '');
|
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;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = await (Promise.race([
|
const url = await getInitialURLRef.current();
|
||||||
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 path = url ? extractPathFromURL(url) : null;
|
const path = url ? extractPathFromURL(url) : null;
|
||||||
|
|
||||||
if (path) {
|
if (path) {
|
||||||
@@ -90,7 +109,7 @@ export default function useLinking(
|
|||||||
}, [extractPathFromURL]);
|
}, [extractPathFromURL]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const listener = ({ url }: { url: string }) => {
|
const listener = (url: string) => {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -102,7 +121,20 @@ export default function useLinking(
|
|||||||
const state = getStateFromPathRef.current(path, configRef.current);
|
const state = getStateFromPathRef.current(path, configRef.current);
|
||||||
|
|
||||||
if (state) {
|
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) {
|
if (action !== undefined) {
|
||||||
navigation.dispatch(action);
|
navigation.dispatch(action);
|
||||||
@@ -113,10 +145,8 @@ export default function useLinking(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Linking.addEventListener('url', listener);
|
return subscribe(listener);
|
||||||
|
}, [enabled, ref, subscribe, extractPathFromURL]);
|
||||||
return () => Linking.removeEventListener('url', listener);
|
|
||||||
}, [enabled, extractPathFromURL, ref]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getInitialState,
|
getInitialState,
|
||||||
|
|||||||
@@ -399,11 +399,26 @@ export default function useLinking(
|
|||||||
|
|
||||||
// We should only dispatch an action when going forward
|
// We should only dispatch an action when going forward
|
||||||
// Otherwise the action will likely add items to history, which would mess things up
|
// Otherwise the action will likely add items to history, which would mess things up
|
||||||
if (state && index > previousIndex) {
|
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 (action !== undefined) {
|
if (state.routes.some((r) => !rootState?.routeNames.includes(r.name))) {
|
||||||
navigation.dispatch(action);
|
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) {
|
||||||
|
navigation.dispatch(action);
|
||||||
|
} else {
|
||||||
|
navigation.resetRoot(state);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
navigation.resetRoot(state);
|
navigation.resetRoot(state);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,34 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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)
|
## [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
|
**Note:** Version bump only for package @react-navigation/routers
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/routers",
|
"name": "@react-navigation/routers",
|
||||||
"description": "Routers to help build custom navigators",
|
"description": "Routers to help build custom navigators",
|
||||||
"version": "5.4.12",
|
"version": "5.5.1",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
"react-native",
|
"react-native",
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.1.12"
|
"nanoid": "^3.1.15"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-native-community/bob": "^0.16.2",
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ export type DrawerRouterOptions = TabRouterOptions & {
|
|||||||
openByDefault?: boolean;
|
openByDefault?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DrawerNavigationState = Omit<
|
export type DrawerNavigationState<ParamList extends ParamListBase> = Omit<
|
||||||
TabNavigationState,
|
TabNavigationState<ParamList>,
|
||||||
'type' | 'history'
|
'type' | 'history'
|
||||||
> & {
|
> & {
|
||||||
/**
|
/**
|
||||||
@@ -72,10 +72,14 @@ export const DrawerActions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isDrawerOpen = (
|
const isDrawerOpen = (
|
||||||
state: DrawerNavigationState | PartialState<DrawerNavigationState>
|
state:
|
||||||
|
| DrawerNavigationState<ParamListBase>
|
||||||
|
| PartialState<DrawerNavigationState<ParamListBase>>
|
||||||
) => Boolean(state.history?.find((it) => it.type === 'drawer'));
|
) => Boolean(state.history?.find((it) => it.type === 'drawer'));
|
||||||
|
|
||||||
const openDrawer = (state: DrawerNavigationState): DrawerNavigationState => {
|
const openDrawer = (
|
||||||
|
state: DrawerNavigationState<ParamListBase>
|
||||||
|
): DrawerNavigationState<ParamListBase> => {
|
||||||
if (isDrawerOpen(state)) {
|
if (isDrawerOpen(state)) {
|
||||||
return 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)) {
|
if (!isDrawerOpen(state)) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -101,11 +107,11 @@ export default function DrawerRouter({
|
|||||||
openByDefault,
|
openByDefault,
|
||||||
...rest
|
...rest
|
||||||
}: DrawerRouterOptions): Router<
|
}: DrawerRouterOptions): Router<
|
||||||
DrawerNavigationState,
|
DrawerNavigationState<ParamListBase>,
|
||||||
DrawerActionType | CommonNavigationAction
|
DrawerActionType | CommonNavigationAction
|
||||||
> {
|
> {
|
||||||
const router = (TabRouter(rest) as unknown) as Router<
|
const router = (TabRouter(rest) as unknown) as Router<
|
||||||
DrawerNavigationState,
|
DrawerNavigationState<ParamListBase>,
|
||||||
TabActionType | CommonNavigationAction
|
TabActionType | CommonNavigationAction
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ export type StackActionType =
|
|||||||
|
|
||||||
export type StackRouterOptions = DefaultRouterOptions;
|
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.
|
* Type of the router, in this case, it's stack.
|
||||||
*/
|
*/
|
||||||
@@ -96,7 +98,7 @@ export const StackActions = {
|
|||||||
|
|
||||||
export default function StackRouter(options: StackRouterOptions) {
|
export default function StackRouter(options: StackRouterOptions) {
|
||||||
const router: Router<
|
const router: Router<
|
||||||
StackNavigationState,
|
StackNavigationState<ParamListBase>,
|
||||||
CommonNavigationAction | StackActionType
|
CommonNavigationAction | StackActionType
|
||||||
> = {
|
> = {
|
||||||
...BaseRouter,
|
...BaseRouter,
|
||||||
@@ -256,10 +258,35 @@ export default function StackRouter(options: StackRouterOptions) {
|
|||||||
|
|
||||||
case 'PUSH':
|
case 'PUSH':
|
||||||
if (state.routeNames.includes(action.payload.name)) {
|
if (state.routeNames.includes(action.payload.name)) {
|
||||||
return {
|
const route =
|
||||||
...state,
|
action.payload.name && action.payload.key
|
||||||
index: state.index + 1,
|
? state.routes.find(
|
||||||
routes: [
|
(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,
|
...state.routes,
|
||||||
{
|
{
|
||||||
key:
|
key:
|
||||||
@@ -275,7 +302,13 @@ export default function StackRouter(options: StackRouterOptions) {
|
|||||||
}
|
}
|
||||||
: action.payload.params,
|
: action.payload.params,
|
||||||
},
|
},
|
||||||
],
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
index: routes.length - 1,
|
||||||
|
routes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ export type TabRouterOptions = DefaultRouterOptions & {
|
|||||||
backBehavior?: BackBehavior;
|
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.
|
* Type of the router, in this case, it's tab.
|
||||||
*/
|
*/
|
||||||
@@ -93,7 +96,7 @@ const getRouteHistory = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const changeIndex = (
|
const changeIndex = (
|
||||||
state: TabNavigationState,
|
state: TabNavigationState<ParamListBase>,
|
||||||
index: number,
|
index: number,
|
||||||
backBehavior: BackBehavior,
|
backBehavior: BackBehavior,
|
||||||
initialRouteName: string | undefined
|
initialRouteName: string | undefined
|
||||||
@@ -127,7 +130,7 @@ export default function TabRouter({
|
|||||||
backBehavior = 'history',
|
backBehavior = 'history',
|
||||||
}: TabRouterOptions) {
|
}: TabRouterOptions) {
|
||||||
const router: Router<
|
const router: Router<
|
||||||
TabNavigationState,
|
TabNavigationState<ParamListBase>,
|
||||||
TabActionType | CommonNavigationAction
|
TabActionType | CommonNavigationAction
|
||||||
> = {
|
> = {
|
||||||
...BaseRouter,
|
...BaseRouter,
|
||||||
@@ -172,9 +175,9 @@ export default function TabRouter({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const routes = routeNames.map((name) => {
|
const routes = routeNames.map((name) => {
|
||||||
const route = (state as PartialState<TabNavigationState>).routes.find(
|
const route = (state as PartialState<
|
||||||
(r) => r.name === name
|
TabNavigationState<ParamListBase>
|
||||||
);
|
>).routes.find((r) => r.name === name);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...route,
|
...route,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
DrawerRouter,
|
DrawerRouter,
|
||||||
DrawerActions,
|
DrawerActions,
|
||||||
DrawerNavigationState,
|
DrawerNavigationState,
|
||||||
|
ParamListBase,
|
||||||
} from '..';
|
} from '..';
|
||||||
|
|
||||||
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
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", () => {
|
it("doesn't rehydrate state if it's not stale", () => {
|
||||||
const router = DrawerRouter({});
|
const router = DrawerRouter({});
|
||||||
|
|
||||||
const state: DrawerNavigationState = {
|
const state: DrawerNavigationState<ParamListBase> = {
|
||||||
index: 0,
|
index: 0,
|
||||||
key: 'drawer-test',
|
key: 'drawer-test',
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
@@ -340,7 +341,7 @@ it('handles open drawer action', () => {
|
|||||||
history: [{ type: 'route', key: 'bar' }, { type: 'drawer' }],
|
history: [{ type: 'route', key: 'bar' }, { type: 'drawer' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
const state: DrawerNavigationState = {
|
const state: DrawerNavigationState<ParamListBase> = {
|
||||||
stale: false as const,
|
stale: false as const,
|
||||||
type: 'drawer' as const,
|
type: 'drawer' as const,
|
||||||
key: 'root',
|
key: 'root',
|
||||||
@@ -395,7 +396,7 @@ it('handles close drawer action', () => {
|
|||||||
history: [{ type: 'route', key: 'bar' }],
|
history: [{ type: 'route', key: 'bar' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
const state: DrawerNavigationState = {
|
const state: DrawerNavigationState<ParamListBase> = {
|
||||||
stale: false as const,
|
stale: false as const,
|
||||||
type: 'drawer' as const,
|
type: 'drawer' as const,
|
||||||
key: 'root',
|
key: 'root',
|
||||||
@@ -487,7 +488,7 @@ it('handles toggle drawer action', () => {
|
|||||||
it('updates history on focus change', () => {
|
it('updates history on focus change', () => {
|
||||||
const router = DrawerRouter({ backBehavior: 'history' });
|
const router = DrawerRouter({ backBehavior: 'history' });
|
||||||
|
|
||||||
const state: DrawerNavigationState = {
|
const state: DrawerNavigationState<ParamListBase> = {
|
||||||
index: 0,
|
index: 0,
|
||||||
key: 'drawer-test',
|
key: 'drawer-test',
|
||||||
routeNames: ['baz', 'bar'],
|
routeNames: ['baz', 'bar'],
|
||||||
|
|||||||
@@ -848,7 +848,7 @@ it('handles push action', () => {
|
|||||||
stale: false,
|
stale: false,
|
||||||
type: 'stack',
|
type: 'stack',
|
||||||
key: 'root',
|
key: 'root',
|
||||||
index: 3,
|
index: 1,
|
||||||
routeNames: ['baz', 'bar', 'qux'],
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
routes: [
|
routes: [
|
||||||
{ key: 'bar', name: 'bar' },
|
{ key: 'bar', name: 'bar' },
|
||||||
@@ -873,7 +873,7 @@ it('handles push action', () => {
|
|||||||
stale: false,
|
stale: false,
|
||||||
type: 'stack',
|
type: 'stack',
|
||||||
key: 'root',
|
key: 'root',
|
||||||
index: 3,
|
index: 1,
|
||||||
routeNames: ['baz', 'bar', 'qux'],
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
routes: [
|
routes: [
|
||||||
{ key: 'bar', name: 'bar' },
|
{ key: 'bar', name: 'bar' },
|
||||||
@@ -895,6 +895,73 @@ it('handles push action', () => {
|
|||||||
options
|
options
|
||||||
)
|
)
|
||||||
).toBe(null);
|
).toBe(null);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 2,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar-3', name: 'bar' },
|
||||||
|
{ key: 'bar-4', name: 'bar', params: { foo: 21 } },
|
||||||
|
{ key: 'baz-5', name: 'baz' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PUSH',
|
||||||
|
payload: { name: 'bar', key: 'bar-4', params: { bar: 29 } },
|
||||||
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 2,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar-3', name: 'bar' },
|
||||||
|
{ key: 'baz-5', name: 'baz' },
|
||||||
|
{ key: 'bar-4', name: 'bar', params: { foo: 21, bar: 29 } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
router.getStateForAction(
|
||||||
|
{
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 2,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar-3', name: 'bar' },
|
||||||
|
{ key: 'bar-4', name: 'bar' },
|
||||||
|
{ key: 'baz-5', name: 'baz' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'PUSH',
|
||||||
|
payload: { name: 'bar', key: 'bar-6', params: { bar: 29 } },
|
||||||
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
stale: false,
|
||||||
|
type: 'stack',
|
||||||
|
key: 'root',
|
||||||
|
index: 3,
|
||||||
|
routeNames: ['baz', 'bar', 'qux'],
|
||||||
|
routes: [
|
||||||
|
{ key: 'bar-3', name: 'bar' },
|
||||||
|
{ key: 'bar-4', name: 'bar' },
|
||||||
|
{ key: 'baz-5', name: 'baz' },
|
||||||
|
{ key: 'bar-6', name: 'bar', params: { bar: 29 } },
|
||||||
|
],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changes index on focus change', () => {
|
it('changes index on focus change', () => {
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import { CommonActions, TabRouter, TabActions, TabNavigationState } from '..';
|
import {
|
||||||
|
CommonActions,
|
||||||
|
TabRouter,
|
||||||
|
TabActions,
|
||||||
|
TabNavigationState,
|
||||||
|
ParamListBase,
|
||||||
|
} from '..';
|
||||||
|
|
||||||
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
jest.mock('nanoid/non-secure', () => ({ nanoid: () => 'test' }));
|
||||||
|
|
||||||
@@ -217,7 +223,7 @@ it('gets rehydrated state from partial state', () => {
|
|||||||
it("doesn't rehydrate state if it's not stale", () => {
|
it("doesn't rehydrate state if it's not stale", () => {
|
||||||
const router = TabRouter({});
|
const router = TabRouter({});
|
||||||
|
|
||||||
const state: TabNavigationState = {
|
const state: TabNavigationState<ParamListBase> = {
|
||||||
index: 0,
|
index: 0,
|
||||||
key: 'tab-test',
|
key: 'tab-test',
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
@@ -699,7 +705,7 @@ it('handles back action with backBehavior: history', () => {
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('qux'),
|
TabActions.jumpTo('qux'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(state, CommonActions.goBack(), options)
|
router.getStateForAction(state, CommonActions.goBack(), options)
|
||||||
@@ -721,7 +727,7 @@ it('handles back action with backBehavior: history', () => {
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('baz'),
|
TabActions.jumpTo('baz'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(state, CommonActions.goBack(), options)
|
router.getStateForAction(state, CommonActions.goBack(), options)
|
||||||
@@ -746,7 +752,7 @@ it('handles back action with backBehavior: history', () => {
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('bar'),
|
TabActions.jumpTo('bar'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(state, CommonActions.goBack(), options)
|
router.getStateForAction(state, CommonActions.goBack(), options)
|
||||||
@@ -785,7 +791,7 @@ it('handles back action with backBehavior: order', () => {
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('qux'),
|
TabActions.jumpTo('qux'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(state, CommonActions.goBack(), options)
|
router.getStateForAction(state, CommonActions.goBack(), options)
|
||||||
@@ -810,7 +816,7 @@ it('handles back action with backBehavior: order', () => {
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('baz'),
|
TabActions.jumpTo('baz'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(state, CommonActions.goBack(), options)
|
router.getStateForAction(state, CommonActions.goBack(), options)
|
||||||
@@ -832,7 +838,7 @@ it('handles back action with backBehavior: order', () => {
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('bar'),
|
TabActions.jumpTo('bar'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(state, CommonActions.goBack(), options)
|
router.getStateForAction(state, CommonActions.goBack(), options)
|
||||||
@@ -856,7 +862,7 @@ it('handles back action with backBehavior: initialRoute', () => {
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('qux'),
|
TabActions.jumpTo('qux'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(state, CommonActions.goBack(), options)
|
router.getStateForAction(state, CommonActions.goBack(), options)
|
||||||
@@ -878,7 +884,7 @@ it('handles back action with backBehavior: initialRoute', () => {
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('baz'),
|
TabActions.jumpTo('baz'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(state, CommonActions.goBack(), options)
|
router.getStateForAction(state, CommonActions.goBack(), options)
|
||||||
@@ -900,7 +906,7 @@ it('handles back action with backBehavior: initialRoute', () => {
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('bar'),
|
TabActions.jumpTo('bar'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(state, CommonActions.goBack(), options)
|
router.getStateForAction(state, CommonActions.goBack(), options)
|
||||||
@@ -928,7 +934,7 @@ it('handles back action with backBehavior: initialRoute and initialRouteName', (
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('qux'),
|
TabActions.jumpTo('qux'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(state, CommonActions.goBack(), options)
|
router.getStateForAction(state, CommonActions.goBack(), options)
|
||||||
@@ -950,7 +956,7 @@ it('handles back action with backBehavior: initialRoute and initialRouteName', (
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('bar'),
|
TabActions.jumpTo('bar'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(state, CommonActions.goBack(), options)
|
router.getStateForAction(state, CommonActions.goBack(), options)
|
||||||
@@ -972,7 +978,7 @@ it('handles back action with backBehavior: initialRoute and initialRouteName', (
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('baz'),
|
TabActions.jumpTo('baz'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(state, CommonActions.goBack(), options)
|
router.getStateForAction(state, CommonActions.goBack(), options)
|
||||||
@@ -992,7 +998,7 @@ it('handles back action with backBehavior: none', () => {
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('baz'),
|
TabActions.jumpTo('baz'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
router.getStateForAction(state, CommonActions.goBack(), options)
|
router.getStateForAction(state, CommonActions.goBack(), options)
|
||||||
@@ -1006,7 +1012,7 @@ it('updates route key history on navigate and jump to', () => {
|
|||||||
routeParamList: {},
|
routeParamList: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
let state: TabNavigationState = {
|
let state: TabNavigationState<ParamListBase> = {
|
||||||
index: 1,
|
index: 1,
|
||||||
key: 'tab-test',
|
key: 'tab-test',
|
||||||
routeNames: ['bar', 'baz', 'qux'],
|
routeNames: ['bar', 'baz', 'qux'],
|
||||||
@@ -1024,7 +1030,7 @@ it('updates route key history on navigate and jump to', () => {
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('qux'),
|
TabActions.jumpTo('qux'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(state.history).toEqual([
|
expect(state.history).toEqual([
|
||||||
{ type: 'route', key: 'baz-0' },
|
{ type: 'route', key: 'baz-0' },
|
||||||
@@ -1035,7 +1041,7 @@ it('updates route key history on navigate and jump to', () => {
|
|||||||
state,
|
state,
|
||||||
CommonActions.navigate('bar'),
|
CommonActions.navigate('bar'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(state.history).toEqual([
|
expect(state.history).toEqual([
|
||||||
{ type: 'route', key: 'baz-0' },
|
{ type: 'route', key: 'baz-0' },
|
||||||
@@ -1047,7 +1053,7 @@ it('updates route key history on navigate and jump to', () => {
|
|||||||
state,
|
state,
|
||||||
TabActions.jumpTo('baz'),
|
TabActions.jumpTo('baz'),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(state.history).toEqual([
|
expect(state.history).toEqual([
|
||||||
{ type: 'route', key: 'qux-0' },
|
{ type: 'route', key: 'qux-0' },
|
||||||
@@ -1059,7 +1065,7 @@ it('updates route key history on navigate and jump to', () => {
|
|||||||
state,
|
state,
|
||||||
CommonActions.goBack(),
|
CommonActions.goBack(),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(state.history).toEqual([
|
expect(state.history).toEqual([
|
||||||
{ type: 'route', key: 'qux-0' },
|
{ type: 'route', key: 'qux-0' },
|
||||||
@@ -1070,7 +1076,7 @@ it('updates route key history on navigate and jump to', () => {
|
|||||||
state,
|
state,
|
||||||
CommonActions.goBack(),
|
CommonActions.goBack(),
|
||||||
options
|
options
|
||||||
) as TabNavigationState;
|
) as TabNavigationState<ParamListBase>;
|
||||||
|
|
||||||
expect(state.history).toEqual([{ type: 'route', key: 'qux-0' }]);
|
expect(state.history).toEqual([{ type: 'route', key: 'qux-0' }]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,16 @@ import type * as CommonActions from './CommonActions';
|
|||||||
|
|
||||||
export type CommonNavigationAction = CommonActions.Action;
|
export type CommonNavigationAction = CommonActions.Action;
|
||||||
|
|
||||||
export type NavigationState = Readonly<{
|
type NavigationRoute<
|
||||||
|
ParamList extends ParamListBase,
|
||||||
|
RouteName extends keyof ParamList
|
||||||
|
> = Route<Extract<RouteName, string>, ParamList[RouteName]> & {
|
||||||
|
state?: NavigationState | PartialState<NavigationState>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type NavigationState<
|
||||||
|
ParamList extends ParamListBase = ParamListBase
|
||||||
|
> = Readonly<{
|
||||||
/**
|
/**
|
||||||
* Unique key for the navigation state.
|
* Unique key for the navigation state.
|
||||||
*/
|
*/
|
||||||
@@ -14,7 +23,7 @@ export type NavigationState = Readonly<{
|
|||||||
/**
|
/**
|
||||||
* List of valid route names as defined in the screen components.
|
* List of valid route names as defined in the screen components.
|
||||||
*/
|
*/
|
||||||
routeNames: string[];
|
routeNames: Extract<keyof ParamList, string>[];
|
||||||
/**
|
/**
|
||||||
* Alternative entries for history.
|
* Alternative entries for history.
|
||||||
*/
|
*/
|
||||||
@@ -22,9 +31,7 @@ export type NavigationState = Readonly<{
|
|||||||
/**
|
/**
|
||||||
* List of rendered routes.
|
* List of rendered routes.
|
||||||
*/
|
*/
|
||||||
routes: (Route<string> & {
|
routes: NavigationRoute<ParamList, keyof ParamList>[];
|
||||||
state?: NavigationState | PartialState<NavigationState>;
|
|
||||||
})[];
|
|
||||||
/**
|
/**
|
||||||
* Custom type for the state, whether it's for tab, stack, drawer etc.
|
* Custom type for the state, whether it's for tab, stack, drawer etc.
|
||||||
* During rehydration, the state will be discarded if type doesn't match with router type.
|
* During rehydration, the state will be discarded if type doesn't match with router type.
|
||||||
@@ -43,19 +50,24 @@ export type InitialState = Readonly<
|
|||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export type PartialRoute<R extends Route<string>> = Omit<R, 'key'> & {
|
||||||
|
key?: string;
|
||||||
|
state?: PartialState<NavigationState>;
|
||||||
|
};
|
||||||
|
|
||||||
export type PartialState<State extends NavigationState> = Partial<
|
export type PartialState<State extends NavigationState> = Partial<
|
||||||
Omit<State, 'stale' | 'type' | 'key' | 'routes' | 'routeNames'>
|
Omit<State, 'stale' | 'type' | 'key' | 'routes' | 'routeNames'>
|
||||||
> &
|
> &
|
||||||
Readonly<{
|
Readonly<{
|
||||||
stale?: true;
|
stale?: true;
|
||||||
type?: string;
|
type?: string;
|
||||||
routes: (Omit<Route<string>, 'key'> & {
|
routes: PartialRoute<Route<string>>[];
|
||||||
key?: string;
|
|
||||||
state?: InitialState;
|
|
||||||
})[];
|
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type Route<RouteName extends string> = Readonly<{
|
export type Route<
|
||||||
|
RouteName extends string,
|
||||||
|
Params extends object | undefined = object | undefined
|
||||||
|
> = Readonly<{
|
||||||
/**
|
/**
|
||||||
* Unique key for the route.
|
* Unique key for the route.
|
||||||
*/
|
*/
|
||||||
@@ -64,11 +76,20 @@ export type Route<RouteName extends string> = Readonly<{
|
|||||||
* User-provided name for the route.
|
* User-provided name for the route.
|
||||||
*/
|
*/
|
||||||
name: RouteName;
|
name: RouteName;
|
||||||
/**
|
}> &
|
||||||
* Params for the route.
|
(undefined extends Params
|
||||||
*/
|
? Readonly<{
|
||||||
params?: object;
|
/**
|
||||||
}>;
|
* Params for this route
|
||||||
|
*/
|
||||||
|
params?: Readonly<Params>;
|
||||||
|
}>
|
||||||
|
: Readonly<{
|
||||||
|
/**
|
||||||
|
* Params for this route
|
||||||
|
*/
|
||||||
|
params: Readonly<Params>;
|
||||||
|
}>);
|
||||||
|
|
||||||
export type ParamListBase = Record<string, object | undefined>;
|
export type ParamListBase = Record<string, object | undefined>;
|
||||||
|
|
||||||
@@ -95,12 +116,12 @@ export type ActionCreators<Action extends NavigationAction> = {
|
|||||||
[key: string]: (...args: any) => Action;
|
[key: string]: (...args: any) => Action;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DefaultRouterOptions = {
|
export type DefaultRouterOptions<RouteName extends string = string> = {
|
||||||
/**
|
/**
|
||||||
* Name of the route to focus by on initial render.
|
* Name of the route to focus by on initial render.
|
||||||
* If not specified, usually the first route is used.
|
* If not specified, usually the first route is used.
|
||||||
*/
|
*/
|
||||||
initialRouteName?: string;
|
initialRouteName?: RouteName;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RouterFactory<
|
export type RouterFactory<
|
||||||
|
|||||||
@@ -3,6 +3,57 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [5.12.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.11.1...@react-navigation/stack@5.12.0) (2020-11-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add a headerBackAccessibilityLabel option in stack ([c326c10](https://github.com/react-navigation/react-navigation/commit/c326c106f9a2492ff45bdc8da9bfbc404e48786a)), closes [#9016](https://github.com/react-navigation/react-navigation/issues/9016)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [5.11.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.11.0...@react-navigation/stack@5.11.1) (2020-10-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.11.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.10.0...@react-navigation/stack@5.11.0) (2020-10-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* enable react-native-screens in Stack by default on iOS ([45dbe5c](https://github.com/react-navigation/react-navigation/commit/45dbe5c40ebc66c62593b3fad35cff3048b888a4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [5.10.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.9.3...@react-navigation/stack@5.10.0) (2020-10-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add missing check for parent header when calculating height ([da91cec](https://github.com/react-navigation/react-navigation/commit/da91cec941e7dec352ba1910901904d769c9f3a3))
|
||||||
|
* don't set statusbar height on nested header by default ([38e17aa](https://github.com/react-navigation/react-navigation/commit/38e17aae939974b47fe5c77d8670b9a4544250f2))
|
||||||
|
* fix header buttons not pressable when headerTransparent=true & headerMode=float ([#8804](https://github.com/react-navigation/react-navigation/issues/8804)) ([d6cac67](https://github.com/react-navigation/react-navigation/commit/d6cac6713a51e4de320fc1c7ece72a2b92241574)), closes [#8731](https://github.com/react-navigation/react-navigation/issues/8731)
|
||||||
|
* set needsOffscreenAlphaCompositing and update default android animation ([8ee0dda](https://github.com/react-navigation/react-navigation/commit/8ee0dda155b4cde43be1e58170e44823b54e7d4f)), closes [#8696](https://github.com/react-navigation/react-navigation/issues/8696)
|
||||||
|
|
||||||
|
|
||||||
|
### 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/stack@5.9.2...@react-navigation/stack@5.9.3) (2020-10-07)
|
## [5.9.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.9.2...@react-navigation/stack@5.9.3) (2020-10-07)
|
||||||
|
|
||||||
**Note:** Version bump only for package @react-navigation/stack
|
**Note:** Version bump only for package @react-navigation/stack
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@react-navigation/stack",
|
"name": "@react-navigation/stack",
|
||||||
"description": "Stack navigator component for iOS and Android with animated transitions and gestures",
|
"description": "Stack navigator component for iOS and Android with animated transitions and gestures",
|
||||||
"version": "5.9.3",
|
"version": "5.12.0",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native-component",
|
"react-native-component",
|
||||||
"react-component",
|
"react-component",
|
||||||
@@ -40,17 +40,17 @@
|
|||||||
"clean": "del lib"
|
"clean": "del lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^3.1.2",
|
"color": "^3.1.3",
|
||||||
"react-native-iphone-x-helper": "^1.2.1"
|
"react-native-iphone-x-helper": "^1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@react-native-community/bob": "^0.16.2",
|
"@react-native-community/bob": "^0.16.2",
|
||||||
"@react-native-community/masked-view": "^0.1.10",
|
"@react-native-community/masked-view": "^0.1.10",
|
||||||
"@react-navigation/native": "^5.7.6",
|
"@react-navigation/native": "^5.8.3",
|
||||||
"@testing-library/react-native": "^7.0.2",
|
"@testing-library/react-native": "^7.1.0",
|
||||||
"@types/color": "^3.0.1",
|
"@types/color": "^3.0.1",
|
||||||
"@types/react": "^16.9.51",
|
"@types/react": "^16.9.53",
|
||||||
"@types/react-native": "^0.63.25",
|
"@types/react-native": "^0.63.30",
|
||||||
"del-cli": "^3.0.1",
|
"del-cli": "^3.0.1",
|
||||||
"react": "~16.13.1",
|
"react": "~16.13.1",
|
||||||
"react-native": "~0.63.2",
|
"react-native": "~0.63.2",
|
||||||
|
|||||||
@@ -289,8 +289,8 @@ export function forScaleFromCenterAndroid({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const opacity = progress.interpolate({
|
const opacity = progress.interpolate({
|
||||||
inputRange: [0, 0.8, 1, 1.2, 2],
|
inputRange: [0, 0.75, 0.875, 1, 1.0825, 1.2075, 2],
|
||||||
outputRange: [0, 0.5, 1, 0.33, 0],
|
outputRange: [0, 0, 1, 1, 1, 1, 0],
|
||||||
});
|
});
|
||||||
|
|
||||||
const scale = conditional(
|
const scale = conditional(
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
import type { TransitionPreset } from '../types';
|
import type { TransitionPreset } from '../types';
|
||||||
|
|
||||||
const ANDROID_VERSION_PIE = 28;
|
const ANDROID_VERSION_PIE = 28;
|
||||||
|
const ANDROID_VERSION_10 = 29;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard iOS navigation transition.
|
* Standard iOS navigation transition.
|
||||||
@@ -102,10 +103,13 @@ export const ScaleFromCenterAndroid: TransitionPreset = {
|
|||||||
*/
|
*/
|
||||||
export const DefaultTransition = Platform.select({
|
export const DefaultTransition = Platform.select({
|
||||||
ios: SlideFromRightIOS,
|
ios: SlideFromRightIOS,
|
||||||
default:
|
android:
|
||||||
Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_PIE
|
Platform.Version >= ANDROID_VERSION_10
|
||||||
|
? ScaleFromCenterAndroid
|
||||||
|
: Platform.Version >= ANDROID_VERSION_PIE
|
||||||
? RevealFromBottomAndroid
|
? RevealFromBottomAndroid
|
||||||
: FadeFromBottomAndroid,
|
: FadeFromBottomAndroid,
|
||||||
|
default: ScaleFromCenterAndroid,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import {
|
|||||||
StackRouterOptions,
|
StackRouterOptions,
|
||||||
StackNavigationState,
|
StackNavigationState,
|
||||||
StackActions,
|
StackActions,
|
||||||
|
ParamListBase,
|
||||||
|
StackActionHelpers,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import StackView from '../views/Stack/StackView';
|
import StackView from '../views/Stack/StackView';
|
||||||
import type {
|
import type {
|
||||||
@@ -36,8 +38,9 @@ function StackNavigator({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { state, descriptors, navigation } = useNavigationBuilder<
|
const { state, descriptors, navigation } = useNavigationBuilder<
|
||||||
StackNavigationState,
|
StackNavigationState<ParamListBase>,
|
||||||
StackRouterOptions,
|
StackRouterOptions,
|
||||||
|
StackActionHelpers<ParamListBase>,
|
||||||
StackNavigationOptions,
|
StackNavigationOptions,
|
||||||
StackNavigationEventMap
|
StackNavigationEventMap
|
||||||
>(StackRouter, {
|
>(StackRouter, {
|
||||||
@@ -91,7 +94,7 @@ function StackNavigator({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default createNavigatorFactory<
|
export default createNavigatorFactory<
|
||||||
StackNavigationState,
|
StackNavigationState<ParamListBase>,
|
||||||
StackNavigationOptions,
|
StackNavigationOptions,
|
||||||
StackNavigationEventMap,
|
StackNavigationEventMap,
|
||||||
typeof StackNavigator
|
typeof StackNavigator
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ export type StackNavigationEventMap = {
|
|||||||
export type StackNavigationHelpers = NavigationHelpers<
|
export type StackNavigationHelpers = NavigationHelpers<
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
StackNavigationEventMap
|
StackNavigationEventMap
|
||||||
>;
|
> &
|
||||||
|
StackActionHelpers<ParamListBase>;
|
||||||
|
|
||||||
export type StackNavigationProp<
|
export type StackNavigationProp<
|
||||||
ParamList extends ParamListBase,
|
ParamList extends ParamListBase,
|
||||||
@@ -52,7 +53,7 @@ export type StackNavigationProp<
|
|||||||
> = NavigationProp<
|
> = NavigationProp<
|
||||||
ParamList,
|
ParamList,
|
||||||
RouteName,
|
RouteName,
|
||||||
StackNavigationState,
|
StackNavigationState<ParamList>,
|
||||||
StackNavigationOptions,
|
StackNavigationOptions,
|
||||||
StackNavigationEventMap
|
StackNavigationEventMap
|
||||||
> &
|
> &
|
||||||
@@ -144,6 +145,10 @@ export type StackHeaderOptions = {
|
|||||||
* Whether back button title font should scale to respect Text Size accessibility settings. Defaults to `false`.
|
* Whether back button title font should scale to respect Text Size accessibility settings. Defaults to `false`.
|
||||||
*/
|
*/
|
||||||
headerBackAllowFontScaling?: boolean;
|
headerBackAllowFontScaling?: boolean;
|
||||||
|
/**
|
||||||
|
* Accessibility label for the header back button.
|
||||||
|
*/
|
||||||
|
headerBackAccessibilityLabel?: string;
|
||||||
/**
|
/**
|
||||||
* Title string used by the back button on iOS. Defaults to the previous scene's `headerTitle`.
|
* Title string used by the back button on iOS. Defaults to the previous scene's `headerTitle`.
|
||||||
* Use `headerBackTitleVisible: false` to hide it.
|
* Use `headerBackTitleVisible: false` to hide it.
|
||||||
@@ -250,7 +255,7 @@ export type StackHeaderProps = {
|
|||||||
export type StackDescriptor = Descriptor<
|
export type StackDescriptor = Descriptor<
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
string,
|
string,
|
||||||
StackNavigationState,
|
StackNavigationState<ParamListBase>,
|
||||||
StackNavigationOptions
|
StackNavigationOptions
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@@ -320,11 +325,11 @@ export type StackNavigationOptions = StackHeaderOptions &
|
|||||||
*/
|
*/
|
||||||
gestureResponseDistance?: {
|
gestureResponseDistance?: {
|
||||||
/**
|
/**
|
||||||
* Distance for horizontal direction. Defaults to 25.
|
* Distance for vertical direction. Defaults to 135.
|
||||||
*/
|
*/
|
||||||
vertical?: number;
|
vertical?: number;
|
||||||
/**
|
/**
|
||||||
* Distance for vertical direction. Defaults to 135.
|
* Distance for horizontal direction. Defaults to 25.
|
||||||
*/
|
*/
|
||||||
horizontal?: number;
|
horizontal?: number;
|
||||||
};
|
};
|
||||||
@@ -344,6 +349,13 @@ export type StackNavigationOptions = StackHeaderOptions &
|
|||||||
bottom?: number;
|
bottom?: number;
|
||||||
left?: number;
|
left?: number;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* Whether to detach the previous screen from the view hierarchy to save memory.
|
||||||
|
* Set it to `false` if you need the previous screen to be seen through the active screen.
|
||||||
|
* Only applicable if `detachInactiveScreens` isn't set to `false`.
|
||||||
|
* Defaults to `false` for the last screen when mode='modal', otherwise `true`.
|
||||||
|
*/
|
||||||
|
detachPreviousScreen?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StackNavigationConfig = {
|
export type StackNavigationConfig = {
|
||||||
@@ -354,6 +366,12 @@ export type StackNavigationConfig = {
|
|||||||
* Defaults to `true`.
|
* Defaults to `true`.
|
||||||
*/
|
*/
|
||||||
keyboardHandlingEnabled?: boolean;
|
keyboardHandlingEnabled?: boolean;
|
||||||
|
/**
|
||||||
|
* 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 StackHeaderLeftButtonProps = {
|
export type StackHeaderLeftButtonProps = {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
import { Animated, View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
||||||
import {
|
import {
|
||||||
NavigationContext,
|
NavigationContext,
|
||||||
NavigationRouteContext,
|
NavigationRouteContext,
|
||||||
@@ -60,7 +60,7 @@ export default function HeaderContainer({
|
|||||||
const parentPreviousScene = React.useContext(PreviousSceneContext);
|
const parentPreviousScene = React.useContext(PreviousSceneContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View pointerEvents="box-none" style={style}>
|
<Animated.View pointerEvents="box-none" style={style}>
|
||||||
{scenes.slice(-3).map((scene, i, self) => {
|
{scenes.slice(-3).map((scene, i, self) => {
|
||||||
if ((mode === 'screen' && i !== self.length - 1) || !scene) {
|
if ((mode === 'screen' && i !== self.length - 1) || !scene) {
|
||||||
return null;
|
return null;
|
||||||
@@ -130,11 +130,14 @@ export default function HeaderContainer({
|
|||||||
<View
|
<View
|
||||||
onLayout={
|
onLayout={
|
||||||
onContentHeightChange
|
onContentHeightChange
|
||||||
? (e) =>
|
? (e) => {
|
||||||
|
const { height } = e.nativeEvent.layout;
|
||||||
|
|
||||||
onContentHeightChange({
|
onContentHeightChange({
|
||||||
route: scene.route,
|
route: scene.route,
|
||||||
height: e.nativeEvent.layout.height,
|
height,
|
||||||
})
|
});
|
||||||
|
}
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
pointerEvents={isFocused ? 'box-none' : 'none'}
|
pointerEvents={isFocused ? 'box-none' : 'none'}
|
||||||
@@ -156,7 +159,7 @@ export default function HeaderContainer({
|
|||||||
</NavigationContext.Provider>
|
</NavigationContext.Provider>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</View>
|
</Animated.View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import type { EdgeInsets } from 'react-native-safe-area-context';
|
|||||||
import type { Route } from '@react-navigation/native';
|
import type { Route } from '@react-navigation/native';
|
||||||
import HeaderBackButton from './HeaderBackButton';
|
import HeaderBackButton from './HeaderBackButton';
|
||||||
import HeaderBackground from './HeaderBackground';
|
import HeaderBackground from './HeaderBackground';
|
||||||
|
import HeaderShownContext from '../../utils/HeaderShownContext';
|
||||||
import memoize from '../../utils/memoize';
|
import memoize from '../../utils/memoize';
|
||||||
import type {
|
import type {
|
||||||
Layout,
|
Layout,
|
||||||
@@ -32,11 +33,6 @@ type Props = StackHeaderOptions & {
|
|||||||
styleInterpolator: StackHeaderStyleInterpolator;
|
styleInterpolator: StackHeaderStyleInterpolator;
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
|
||||||
titleLayout?: Layout;
|
|
||||||
leftLabelLayout?: Layout;
|
|
||||||
};
|
|
||||||
|
|
||||||
const warnIfHeaderStylesDefined = (styles: Record<string, any>) => {
|
const warnIfHeaderStylesDefined = (styles: Record<string, any>) => {
|
||||||
Object.keys(styles).forEach((styleProp) => {
|
Object.keys(styles).forEach((styleProp) => {
|
||||||
const value = styles[styleProp];
|
const value = styles[styleProp];
|
||||||
@@ -76,30 +72,35 @@ export const getDefaultHeaderHeight = (
|
|||||||
return headerHeight + statusBarHeight;
|
return headerHeight + statusBarHeight;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class HeaderSegment extends React.Component<Props, State> {
|
export default function HeaderSegment(props: Props) {
|
||||||
state: State = {};
|
const [leftLabelLayout, setLeftLabelLayout] = React.useState<
|
||||||
|
Layout | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
private handleTitleLayout = (e: LayoutChangeEvent) => {
|
const [titleLayout, setTitleLayout] = React.useState<Layout | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
const isParentHeaderShown = React.useContext(HeaderShownContext);
|
||||||
|
|
||||||
|
const handleTitleLayout = (e: LayoutChangeEvent) => {
|
||||||
const { height, width } = e.nativeEvent.layout;
|
const { height, width } = e.nativeEvent.layout;
|
||||||
|
|
||||||
this.setState(({ titleLayout }) => {
|
setTitleLayout((titleLayout) => {
|
||||||
if (
|
if (
|
||||||
titleLayout &&
|
titleLayout &&
|
||||||
height === titleLayout.height &&
|
height === titleLayout.height &&
|
||||||
width === titleLayout.width
|
width === titleLayout.width
|
||||||
) {
|
) {
|
||||||
return null;
|
return titleLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return { height, width };
|
||||||
titleLayout: { height, width },
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleLeftLabelLayout = (e: LayoutChangeEvent) => {
|
const handleLeftLabelLayout = (e: LayoutChangeEvent) => {
|
||||||
const { height, width } = e.nativeEvent.layout;
|
const { height, width } = e.nativeEvent.layout;
|
||||||
const { leftLabelLayout } = this.state;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
leftLabelLayout &&
|
leftLabelLayout &&
|
||||||
@@ -109,10 +110,10 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ leftLabelLayout: { height, width } });
|
setLeftLabelLayout({ height, width });
|
||||||
};
|
};
|
||||||
|
|
||||||
private getInterpolatedStyle = memoize(
|
const getInterpolatedStyle = memoize(
|
||||||
(
|
(
|
||||||
styleInterpolator: StackHeaderStyleInterpolator,
|
styleInterpolator: StackHeaderStyleInterpolator,
|
||||||
layout: Layout,
|
layout: Layout,
|
||||||
@@ -137,258 +138,253 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
render() {
|
const {
|
||||||
const {
|
scene,
|
||||||
scene,
|
layout,
|
||||||
layout,
|
insets,
|
||||||
insets,
|
title: currentTitle,
|
||||||
title: currentTitle,
|
leftLabel: previousTitle,
|
||||||
leftLabel: previousTitle,
|
onGoBack,
|
||||||
onGoBack,
|
headerTitle,
|
||||||
headerTitle,
|
headerTitleAlign = Platform.select({
|
||||||
headerTitleAlign = Platform.select({
|
ios: 'center',
|
||||||
ios: 'center',
|
default: 'left',
|
||||||
default: 'left',
|
}),
|
||||||
}),
|
headerLeft: left = onGoBack
|
||||||
headerLeft: left = onGoBack
|
? (props: StackHeaderLeftButtonProps) => <HeaderBackButton {...props} />
|
||||||
? (props: StackHeaderLeftButtonProps) => <HeaderBackButton {...props} />
|
: undefined,
|
||||||
: undefined,
|
headerTransparent,
|
||||||
headerTransparent,
|
headerTintColor,
|
||||||
headerTintColor,
|
headerBackground,
|
||||||
headerBackground,
|
headerRight: right,
|
||||||
headerRight: right,
|
headerBackImage: backImage,
|
||||||
headerBackImage: backImage,
|
headerBackTitle: leftLabel,
|
||||||
headerBackTitle: leftLabel,
|
headerBackTitleVisible,
|
||||||
headerBackTitleVisible,
|
headerTruncatedBackTitle: truncatedLabel,
|
||||||
headerTruncatedBackTitle: truncatedLabel,
|
headerPressColorAndroid: pressColorAndroid,
|
||||||
headerPressColorAndroid: pressColorAndroid,
|
headerBackAccessibilityLabel: backAccessibilityLabel,
|
||||||
headerBackAllowFontScaling: backAllowFontScaling,
|
headerBackAllowFontScaling: backAllowFontScaling,
|
||||||
headerTitleAllowFontScaling: titleAllowFontScaling,
|
headerTitleAllowFontScaling: titleAllowFontScaling,
|
||||||
headerTitleStyle: customTitleStyle,
|
headerTitleStyle: customTitleStyle,
|
||||||
headerBackTitleStyle: customLeftLabelStyle,
|
headerBackTitleStyle: customLeftLabelStyle,
|
||||||
headerLeftContainerStyle: leftContainerStyle,
|
headerLeftContainerStyle: leftContainerStyle,
|
||||||
headerRightContainerStyle: rightContainerStyle,
|
headerRightContainerStyle: rightContainerStyle,
|
||||||
headerTitleContainerStyle: titleContainerStyle,
|
headerTitleContainerStyle: titleContainerStyle,
|
||||||
headerStyle: customHeaderStyle,
|
headerStyle: customHeaderStyle,
|
||||||
headerStatusBarHeight = insets.top,
|
headerStatusBarHeight = isParentHeaderShown ? 0 : insets.top,
|
||||||
styleInterpolator,
|
styleInterpolator,
|
||||||
} = this.props;
|
} = props;
|
||||||
|
|
||||||
const { leftLabelLayout, titleLayout } = this.state;
|
const defaultHeight = getDefaultHeaderHeight(layout, headerStatusBarHeight);
|
||||||
|
|
||||||
const defaultHeight = getDefaultHeaderHeight(layout, headerStatusBarHeight);
|
const {
|
||||||
|
height = defaultHeight,
|
||||||
|
minHeight,
|
||||||
|
maxHeight,
|
||||||
|
backgroundColor,
|
||||||
|
borderBottomColor,
|
||||||
|
borderBottomEndRadius,
|
||||||
|
borderBottomLeftRadius,
|
||||||
|
borderBottomRightRadius,
|
||||||
|
borderBottomStartRadius,
|
||||||
|
borderBottomWidth,
|
||||||
|
borderColor,
|
||||||
|
borderEndColor,
|
||||||
|
borderEndWidth,
|
||||||
|
borderLeftColor,
|
||||||
|
borderLeftWidth,
|
||||||
|
borderRadius,
|
||||||
|
borderRightColor,
|
||||||
|
borderRightWidth,
|
||||||
|
borderStartColor,
|
||||||
|
borderStartWidth,
|
||||||
|
borderStyle,
|
||||||
|
borderTopColor,
|
||||||
|
borderTopEndRadius,
|
||||||
|
borderTopLeftRadius,
|
||||||
|
borderTopRightRadius,
|
||||||
|
borderTopStartRadius,
|
||||||
|
borderTopWidth,
|
||||||
|
borderWidth,
|
||||||
|
// @ts-expect-error: web support for shadow
|
||||||
|
boxShadow,
|
||||||
|
elevation,
|
||||||
|
shadowColor,
|
||||||
|
shadowOffset,
|
||||||
|
shadowOpacity,
|
||||||
|
shadowRadius,
|
||||||
|
opacity,
|
||||||
|
transform,
|
||||||
|
...unsafeStyles
|
||||||
|
} = StyleSheet.flatten(customHeaderStyle || {}) as ViewStyle;
|
||||||
|
|
||||||
const {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
height = defaultHeight,
|
warnIfHeaderStylesDefined(unsafeStyles);
|
||||||
minHeight,
|
}
|
||||||
maxHeight,
|
|
||||||
backgroundColor,
|
|
||||||
borderBottomColor,
|
|
||||||
borderBottomEndRadius,
|
|
||||||
borderBottomLeftRadius,
|
|
||||||
borderBottomRightRadius,
|
|
||||||
borderBottomStartRadius,
|
|
||||||
borderBottomWidth,
|
|
||||||
borderColor,
|
|
||||||
borderEndColor,
|
|
||||||
borderEndWidth,
|
|
||||||
borderLeftColor,
|
|
||||||
borderLeftWidth,
|
|
||||||
borderRadius,
|
|
||||||
borderRightColor,
|
|
||||||
borderRightWidth,
|
|
||||||
borderStartColor,
|
|
||||||
borderStartWidth,
|
|
||||||
borderStyle,
|
|
||||||
borderTopColor,
|
|
||||||
borderTopEndRadius,
|
|
||||||
borderTopLeftRadius,
|
|
||||||
borderTopRightRadius,
|
|
||||||
borderTopStartRadius,
|
|
||||||
borderTopWidth,
|
|
||||||
borderWidth,
|
|
||||||
// @ts-expect-error: web support for shadow
|
|
||||||
boxShadow,
|
|
||||||
elevation,
|
|
||||||
shadowColor,
|
|
||||||
shadowOffset,
|
|
||||||
shadowOpacity,
|
|
||||||
shadowRadius,
|
|
||||||
opacity,
|
|
||||||
transform,
|
|
||||||
...unsafeStyles
|
|
||||||
} = StyleSheet.flatten(customHeaderStyle || {}) as ViewStyle;
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
const safeStyles: ViewStyle = {
|
||||||
warnIfHeaderStylesDefined(unsafeStyles);
|
backgroundColor,
|
||||||
|
borderBottomColor,
|
||||||
|
borderBottomEndRadius,
|
||||||
|
borderBottomLeftRadius,
|
||||||
|
borderBottomRightRadius,
|
||||||
|
borderBottomStartRadius,
|
||||||
|
borderBottomWidth,
|
||||||
|
borderColor,
|
||||||
|
borderEndColor,
|
||||||
|
borderEndWidth,
|
||||||
|
borderLeftColor,
|
||||||
|
borderLeftWidth,
|
||||||
|
borderRadius,
|
||||||
|
borderRightColor,
|
||||||
|
borderRightWidth,
|
||||||
|
borderStartColor,
|
||||||
|
borderStartWidth,
|
||||||
|
borderStyle,
|
||||||
|
borderTopColor,
|
||||||
|
borderTopEndRadius,
|
||||||
|
borderTopLeftRadius,
|
||||||
|
borderTopRightRadius,
|
||||||
|
borderTopStartRadius,
|
||||||
|
borderTopWidth,
|
||||||
|
borderWidth,
|
||||||
|
// @ts-expect-error: boxShadow is only for Web
|
||||||
|
boxShadow,
|
||||||
|
elevation,
|
||||||
|
shadowColor,
|
||||||
|
shadowOffset,
|
||||||
|
shadowOpacity,
|
||||||
|
shadowRadius,
|
||||||
|
opacity,
|
||||||
|
transform,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Setting a property to undefined triggers default style
|
||||||
|
// So we need to filter them out
|
||||||
|
// Users can use `null` instead
|
||||||
|
for (const styleProp in safeStyles) {
|
||||||
|
// @ts-expect-error: typescript wrongly complains that styleProp cannot be used to index safeStyles
|
||||||
|
if (safeStyles[styleProp] === undefined) {
|
||||||
|
// @ts-expect-error
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
|
delete safeStyles[styleProp];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const safeStyles: ViewStyle = {
|
const {
|
||||||
backgroundColor,
|
titleStyle,
|
||||||
borderBottomColor,
|
leftButtonStyle,
|
||||||
borderBottomEndRadius,
|
leftLabelStyle,
|
||||||
borderBottomLeftRadius,
|
rightButtonStyle,
|
||||||
borderBottomRightRadius,
|
backgroundStyle,
|
||||||
borderBottomStartRadius,
|
} = getInterpolatedStyle(
|
||||||
borderBottomWidth,
|
styleInterpolator,
|
||||||
borderColor,
|
layout,
|
||||||
borderEndColor,
|
scene.progress.current,
|
||||||
borderEndWidth,
|
scene.progress.next,
|
||||||
borderLeftColor,
|
titleLayout,
|
||||||
borderLeftWidth,
|
previousTitle ? leftLabelLayout : undefined,
|
||||||
borderRadius,
|
typeof height === 'number' ? height : defaultHeight
|
||||||
borderRightColor,
|
);
|
||||||
borderRightWidth,
|
|
||||||
borderStartColor,
|
|
||||||
borderStartWidth,
|
|
||||||
borderStyle,
|
|
||||||
borderTopColor,
|
|
||||||
borderTopEndRadius,
|
|
||||||
borderTopLeftRadius,
|
|
||||||
borderTopRightRadius,
|
|
||||||
borderTopStartRadius,
|
|
||||||
borderTopWidth,
|
|
||||||
borderWidth,
|
|
||||||
// @ts-expect-error: boxShadow is only for Web
|
|
||||||
boxShadow,
|
|
||||||
elevation,
|
|
||||||
shadowColor,
|
|
||||||
shadowOffset,
|
|
||||||
shadowOpacity,
|
|
||||||
shadowRadius,
|
|
||||||
opacity,
|
|
||||||
transform,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Setting a property to undefined triggers default style
|
const leftButton = left
|
||||||
// So we need to filter them out
|
? left({
|
||||||
// Users can use `null` instead
|
backImage,
|
||||||
for (const styleProp in safeStyles) {
|
pressColorAndroid,
|
||||||
// @ts-expect-error: typescript wrongly complains that styleProp cannot be used to index safeStyles
|
accessibilityLabel: backAccessibilityLabel,
|
||||||
if (safeStyles[styleProp] === undefined) {
|
allowFontScaling: backAllowFontScaling,
|
||||||
// @ts-expect-error
|
onPress: onGoBack,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
labelVisible: headerBackTitleVisible,
|
||||||
delete safeStyles[styleProp];
|
label: leftLabel !== undefined ? leftLabel : previousTitle,
|
||||||
}
|
truncatedLabel,
|
||||||
}
|
labelStyle: [leftLabelStyle, customLeftLabelStyle],
|
||||||
|
onLabelLayout: handleLeftLabelLayout,
|
||||||
|
screenLayout: layout,
|
||||||
|
titleLayout,
|
||||||
|
tintColor: headerTintColor,
|
||||||
|
canGoBack: Boolean(onGoBack),
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
const {
|
const rightButton = right ? right({ tintColor: headerTintColor }) : null;
|
||||||
titleStyle,
|
|
||||||
leftButtonStyle,
|
|
||||||
leftLabelStyle,
|
|
||||||
rightButtonStyle,
|
|
||||||
backgroundStyle,
|
|
||||||
} = this.getInterpolatedStyle(
|
|
||||||
styleInterpolator,
|
|
||||||
layout,
|
|
||||||
scene.progress.current,
|
|
||||||
scene.progress.next,
|
|
||||||
titleLayout,
|
|
||||||
previousTitle ? leftLabelLayout : undefined,
|
|
||||||
typeof height === 'number' ? height : defaultHeight
|
|
||||||
);
|
|
||||||
|
|
||||||
const leftButton = left
|
return (
|
||||||
? left({
|
<React.Fragment>
|
||||||
backImage,
|
<Animated.View
|
||||||
pressColorAndroid,
|
pointerEvents="box-none"
|
||||||
allowFontScaling: backAllowFontScaling,
|
style={[StyleSheet.absoluteFill, { zIndex: 0 }, backgroundStyle]}
|
||||||
onPress: onGoBack,
|
>
|
||||||
labelVisible: headerBackTitleVisible,
|
{headerBackground ? (
|
||||||
label: leftLabel !== undefined ? leftLabel : previousTitle,
|
headerBackground({ style: safeStyles })
|
||||||
truncatedLabel,
|
) : headerTransparent ? null : (
|
||||||
labelStyle: [leftLabelStyle, customLeftLabelStyle],
|
<HeaderBackground style={safeStyles} />
|
||||||
onLabelLayout: this.handleLeftLabelLayout,
|
)}
|
||||||
screenLayout: layout,
|
</Animated.View>
|
||||||
titleLayout,
|
<Animated.View
|
||||||
tintColor: headerTintColor,
|
pointerEvents="box-none"
|
||||||
canGoBack: Boolean(onGoBack),
|
style={[{ height, minHeight, maxHeight, opacity, transform }]}
|
||||||
})
|
>
|
||||||
: null;
|
<View pointerEvents="none" style={{ height: headerStatusBarHeight }} />
|
||||||
|
<View pointerEvents="box-none" style={styles.content}>
|
||||||
const rightButton = right ? right({ tintColor: headerTintColor }) : null;
|
{leftButton ? (
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<Animated.View
|
|
||||||
pointerEvents="box-none"
|
|
||||||
style={[StyleSheet.absoluteFill, { zIndex: 0 }, backgroundStyle]}
|
|
||||||
>
|
|
||||||
{headerBackground ? (
|
|
||||||
headerBackground({ style: safeStyles })
|
|
||||||
) : headerTransparent ? null : (
|
|
||||||
<HeaderBackground style={safeStyles} />
|
|
||||||
)}
|
|
||||||
</Animated.View>
|
|
||||||
<Animated.View
|
|
||||||
pointerEvents="box-none"
|
|
||||||
style={[{ height, minHeight, maxHeight, opacity, transform }]}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
pointerEvents="none"
|
|
||||||
style={{ height: headerStatusBarHeight }}
|
|
||||||
/>
|
|
||||||
<View pointerEvents="box-none" style={styles.content}>
|
|
||||||
{leftButton ? (
|
|
||||||
<Animated.View
|
|
||||||
pointerEvents="box-none"
|
|
||||||
style={[
|
|
||||||
styles.left,
|
|
||||||
{ left: insets.left },
|
|
||||||
leftButtonStyle,
|
|
||||||
leftContainerStyle,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{leftButton}
|
|
||||||
</Animated.View>
|
|
||||||
) : null}
|
|
||||||
<Animated.View
|
<Animated.View
|
||||||
pointerEvents="box-none"
|
pointerEvents="box-none"
|
||||||
style={[
|
style={[
|
||||||
headerTitleAlign === 'left'
|
styles.left,
|
||||||
? {
|
{ left: insets.left },
|
||||||
position: 'absolute',
|
leftButtonStyle,
|
||||||
left: (leftButton ? 72 : 16) + insets.left,
|
leftContainerStyle,
|
||||||
right: (rightButton ? 72 : 16) + insets.right,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
marginHorizontal:
|
|
||||||
(leftButton ? 32 : 16) +
|
|
||||||
(leftButton && headerBackTitleVisible !== false
|
|
||||||
? 40
|
|
||||||
: 0) +
|
|
||||||
Math.max(insets.left, insets.right),
|
|
||||||
},
|
|
||||||
titleStyle,
|
|
||||||
titleContainerStyle,
|
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{headerTitle({
|
{leftButton}
|
||||||
children: currentTitle,
|
|
||||||
onLayout: this.handleTitleLayout,
|
|
||||||
allowFontScaling: titleAllowFontScaling,
|
|
||||||
tintColor: headerTintColor,
|
|
||||||
style: customTitleStyle,
|
|
||||||
})}
|
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
{rightButton ? (
|
) : null}
|
||||||
<Animated.View
|
<Animated.View
|
||||||
pointerEvents="box-none"
|
pointerEvents="box-none"
|
||||||
style={[
|
style={[
|
||||||
styles.right,
|
headerTitleAlign === 'left'
|
||||||
{ right: insets.right },
|
? {
|
||||||
rightButtonStyle,
|
position: 'absolute',
|
||||||
rightContainerStyle,
|
left: (leftButton ? 72 : 16) + insets.left,
|
||||||
]}
|
right: (rightButton ? 72 : 16) + insets.right,
|
||||||
>
|
}
|
||||||
{rightButton}
|
: {
|
||||||
</Animated.View>
|
marginHorizontal:
|
||||||
) : null}
|
(leftButton ? 32 : 16) +
|
||||||
</View>
|
(leftButton && headerBackTitleVisible !== false
|
||||||
</Animated.View>
|
? 40
|
||||||
</React.Fragment>
|
: 0) +
|
||||||
);
|
Math.max(insets.left, insets.right),
|
||||||
}
|
},
|
||||||
|
titleStyle,
|
||||||
|
titleContainerStyle,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{headerTitle({
|
||||||
|
children: currentTitle,
|
||||||
|
onLayout: handleTitleLayout,
|
||||||
|
allowFontScaling: titleAllowFontScaling,
|
||||||
|
tintColor: headerTintColor,
|
||||||
|
style: customTitleStyle,
|
||||||
|
})}
|
||||||
|
</Animated.View>
|
||||||
|
{rightButton ? (
|
||||||
|
<Animated.View
|
||||||
|
pointerEvents="box-none"
|
||||||
|
style={[
|
||||||
|
styles.right,
|
||||||
|
{ right: insets.right },
|
||||||
|
rightButtonStyle,
|
||||||
|
rightContainerStyle,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{rightButton}
|
||||||
|
</Animated.View>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
</Animated.View>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ class WebScreen extends React.Component<
|
|||||||
|
|
||||||
const AnimatedWebScreen = Animated.createAnimatedComponent(WebScreen);
|
const AnimatedWebScreen = Animated.createAnimatedComponent(WebScreen);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
export const shouldUseActivityState = Screens?.shouldUseActivityState;
|
||||||
|
|
||||||
export const MaybeScreenContainer = ({
|
export const MaybeScreenContainer = ({
|
||||||
enabled,
|
enabled,
|
||||||
...rest
|
...rest
|
||||||
@@ -41,8 +44,11 @@ export const MaybeScreenContainer = ({
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) => {
|
}) => {
|
||||||
if (enabled && Platform.OS !== 'web' && Screens && Screens.screensEnabled()) {
|
if (enabled && Platform.OS !== 'web' && Screens?.screensEnabled()) {
|
||||||
return <Screens.ScreenContainer {...rest} />;
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<Screens.ScreenContainer enabled={enabled} {...rest} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <View {...rest} />;
|
return <View {...rest} />;
|
||||||
@@ -54,16 +60,25 @@ export const MaybeScreen = ({
|
|||||||
...rest
|
...rest
|
||||||
}: ViewProps & {
|
}: ViewProps & {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
active: 0 | 1 | Animated.AnimatedInterpolation;
|
active: 0 | 1 | 2 | Animated.AnimatedInterpolation;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) => {
|
}) => {
|
||||||
if (enabled && Platform.OS === 'web') {
|
if (enabled && Platform.OS === 'web') {
|
||||||
return <AnimatedWebScreen active={active} {...rest} />;
|
return <AnimatedWebScreen active={active} {...rest} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enabled && Screens && Screens.screensEnabled()) {
|
if (enabled && Screens?.screensEnabled()) {
|
||||||
// @ts-expect-error: stackPresentation is incorrectly marked as required
|
if (shouldUseActivityState) {
|
||||||
return <Screens.Screen active={active} {...rest} />;
|
return (
|
||||||
|
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
||||||
|
<Screens.Screen enabled={enabled} activityState={active} {...rest} />
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
// @ts-expect-error: there was an `active` prop and no `activityState` in older version and stackPresentation was required
|
||||||
|
<Screens.Screen enabled={enabled} active={active} {...rest} />
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <View {...rest} />;
|
return <View {...rest} />;
|
||||||
|
|||||||
@@ -79,6 +79,15 @@ const GESTURE_RESPONSE_DISTANCE_VERTICAL = 135;
|
|||||||
|
|
||||||
const useNativeDriver = Platform.OS !== 'web';
|
const useNativeDriver = Platform.OS !== 'web';
|
||||||
|
|
||||||
|
const hasOpacityStyle = (style: any) => {
|
||||||
|
if (style) {
|
||||||
|
const flattenedStyle = StyleSheet.flatten(style);
|
||||||
|
return flattenedStyle.opacity != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
export default class Card extends React.Component<Props> {
|
export default class Card extends React.Component<Props> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
overlayEnabled: Platform.OS !== 'ios',
|
overlayEnabled: Platform.OS !== 'ios',
|
||||||
@@ -533,6 +542,7 @@ export default class Card extends React.Component<Props> {
|
|||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
<Animated.View
|
<Animated.View
|
||||||
|
needsOffscreenAlphaCompositing={hasOpacityStyle(containerStyle)}
|
||||||
style={[styles.container, containerStyle, customContainerStyle]}
|
style={[styles.container, containerStyle, customContainerStyle]}
|
||||||
pointerEvents="box-none"
|
pointerEvents="box-none"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ type Props = TransitionPreset & {
|
|||||||
route: Route<string>;
|
route: Route<string>;
|
||||||
height: number;
|
height: number;
|
||||||
}) => void;
|
}) => void;
|
||||||
|
isParentHeaderShown: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const EPSILON = 0.1;
|
const EPSILON = 0.1;
|
||||||
@@ -93,6 +94,7 @@ function CardContainer({
|
|||||||
hasAbsoluteHeader,
|
hasAbsoluteHeader,
|
||||||
headerHeight,
|
headerHeight,
|
||||||
onHeaderHeightChange,
|
onHeaderHeightChange,
|
||||||
|
isParentHeaderShown,
|
||||||
index,
|
index,
|
||||||
layout,
|
layout,
|
||||||
onCloseRoute,
|
onCloseRoute,
|
||||||
@@ -181,7 +183,6 @@ function CardContainer({
|
|||||||
};
|
};
|
||||||
}, [pointerEvents, scene.progress.next]);
|
}, [pointerEvents, scene.progress.next]);
|
||||||
|
|
||||||
const isParentHeaderShown = React.useContext(HeaderShownContext);
|
|
||||||
const isCurrentHeaderShown = headerMode !== 'none' && headerShown !== false;
|
const isCurrentHeaderShown = headerMode !== 'none' && headerShown !== false;
|
||||||
const previousScene = getPreviousScene({ route: scene.route });
|
const previousScene = getPreviousScene({ route: scene.route });
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,19 @@ import {
|
|||||||
StyleSheet,
|
StyleSheet,
|
||||||
LayoutChangeEvent,
|
LayoutChangeEvent,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
Platform,
|
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import type { EdgeInsets } from 'react-native-safe-area-context';
|
import type { EdgeInsets } from 'react-native-safe-area-context';
|
||||||
import type { Route, StackNavigationState } from '@react-navigation/native';
|
import type {
|
||||||
|
ParamListBase,
|
||||||
|
Route,
|
||||||
|
StackNavigationState,
|
||||||
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
import { MaybeScreenContainer, MaybeScreen } from '../Screens';
|
import {
|
||||||
|
MaybeScreenContainer,
|
||||||
|
MaybeScreen,
|
||||||
|
shouldUseActivityState,
|
||||||
|
} from '../Screens';
|
||||||
import { getDefaultHeaderHeight } from '../Header/HeaderSegment';
|
import { getDefaultHeaderHeight } from '../Header/HeaderSegment';
|
||||||
import type { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
import type { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
||||||
import CardContainer from './CardContainer';
|
import CardContainer from './CardContainer';
|
||||||
@@ -19,7 +26,6 @@ import {
|
|||||||
} from '../../TransitionConfigs/TransitionPresets';
|
} from '../../TransitionConfigs/TransitionPresets';
|
||||||
import { forNoAnimation as forNoAnimationHeader } from '../../TransitionConfigs/HeaderStyleInterpolators';
|
import { forNoAnimation as forNoAnimationHeader } from '../../TransitionConfigs/HeaderStyleInterpolators';
|
||||||
import { forNoAnimation as forNoAnimationCard } from '../../TransitionConfigs/CardStyleInterpolators';
|
import { forNoAnimation as forNoAnimationCard } from '../../TransitionConfigs/CardStyleInterpolators';
|
||||||
import HeaderShownContext from '../../utils/HeaderShownContext';
|
|
||||||
import getDistanceForDirection from '../../utils/getDistanceForDirection';
|
import getDistanceForDirection from '../../utils/getDistanceForDirection';
|
||||||
import type {
|
import type {
|
||||||
Layout,
|
Layout,
|
||||||
@@ -38,7 +44,7 @@ type GestureValues = {
|
|||||||
type Props = {
|
type Props = {
|
||||||
mode: StackCardMode;
|
mode: StackCardMode;
|
||||||
insets: EdgeInsets;
|
insets: EdgeInsets;
|
||||||
state: StackNavigationState;
|
state: StackNavigationState<ParamListBase>;
|
||||||
descriptors: StackDescriptorMap;
|
descriptors: StackDescriptorMap;
|
||||||
routes: Route<string>[];
|
routes: Route<string>[];
|
||||||
openingRouteKeys: string[];
|
openingRouteKeys: string[];
|
||||||
@@ -52,6 +58,7 @@ type Props = {
|
|||||||
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
|
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
|
||||||
renderScene: (props: { route: Route<string> }) => React.ReactNode;
|
renderScene: (props: { route: Route<string> }) => React.ReactNode;
|
||||||
headerMode: StackHeaderMode;
|
headerMode: StackHeaderMode;
|
||||||
|
isParentHeaderShown: boolean;
|
||||||
onTransitionStart: (
|
onTransitionStart: (
|
||||||
props: { route: Route<string> },
|
props: { route: Route<string> },
|
||||||
closing: boolean
|
closing: boolean
|
||||||
@@ -63,6 +70,7 @@ type Props = {
|
|||||||
onGestureStart?: (props: { route: Route<string> }) => void;
|
onGestureStart?: (props: { route: Route<string> }) => void;
|
||||||
onGestureEnd?: (props: { route: Route<string> }) => void;
|
onGestureEnd?: (props: { route: Route<string> }) => void;
|
||||||
onGestureCancel?: (props: { route: Route<string> }) => void;
|
onGestureCancel?: (props: { route: Route<string> }) => void;
|
||||||
|
detachInactiveScreens?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
@@ -76,11 +84,16 @@ type State = {
|
|||||||
|
|
||||||
const EPSILON = 0.01;
|
const EPSILON = 0.01;
|
||||||
|
|
||||||
|
const STATE_INACTIVE = 0;
|
||||||
|
const STATE_TRANSITIONING_OR_BELOW_TOP = 1;
|
||||||
|
const STATE_ON_TOP = 2;
|
||||||
|
|
||||||
const FALLBACK_DESCRIPTOR = Object.freeze({ options: {} });
|
const FALLBACK_DESCRIPTOR = Object.freeze({ options: {} });
|
||||||
|
|
||||||
const getHeaderHeights = (
|
const getHeaderHeights = (
|
||||||
routes: Route<string>[],
|
routes: Route<string>[],
|
||||||
insets: EdgeInsets,
|
insets: EdgeInsets,
|
||||||
|
isParentHeaderShown: boolean,
|
||||||
descriptors: StackDescriptorMap,
|
descriptors: StackDescriptorMap,
|
||||||
layout: Layout,
|
layout: Layout,
|
||||||
previous: Record<string, number>
|
previous: Record<string, number>
|
||||||
@@ -97,7 +110,9 @@ const getHeaderHeights = (
|
|||||||
...options.safeAreaInsets,
|
...options.safeAreaInsets,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { headerStatusBarHeight = safeAreaInsets.top } = options;
|
const {
|
||||||
|
headerStatusBarHeight = isParentHeaderShown ? 0 : safeAreaInsets.top,
|
||||||
|
} = options;
|
||||||
|
|
||||||
acc[curr.key] =
|
acc[curr.key] =
|
||||||
typeof height === 'number'
|
typeof height === 'number'
|
||||||
@@ -260,6 +275,7 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
headerHeights: getHeaderHeights(
|
headerHeights: getHeaderHeights(
|
||||||
props.routes,
|
props.routes,
|
||||||
props.insets,
|
props.insets,
|
||||||
|
props.isParentHeaderShown,
|
||||||
state.descriptors,
|
state.descriptors,
|
||||||
state.layout,
|
state.layout,
|
||||||
state.headerHeights
|
state.headerHeights
|
||||||
@@ -302,6 +318,7 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
headerHeights: getHeaderHeights(
|
headerHeights: getHeaderHeights(
|
||||||
props.routes,
|
props.routes,
|
||||||
props.insets,
|
props.insets,
|
||||||
|
props.isParentHeaderShown,
|
||||||
state.descriptors,
|
state.descriptors,
|
||||||
layout,
|
layout,
|
||||||
state.headerHeights
|
state.headerHeights
|
||||||
@@ -370,6 +387,7 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
renderHeader,
|
renderHeader,
|
||||||
renderScene,
|
renderScene,
|
||||||
headerMode,
|
headerMode,
|
||||||
|
isParentHeaderShown,
|
||||||
onTransitionStart,
|
onTransitionStart,
|
||||||
onTransitionEnd,
|
onTransitionEnd,
|
||||||
onPageChangeStart,
|
onPageChangeStart,
|
||||||
@@ -378,6 +396,8 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
onGestureStart,
|
onGestureStart,
|
||||||
onGestureEnd,
|
onGestureEnd,
|
||||||
onGestureCancel,
|
onGestureCancel,
|
||||||
|
// Enable on new versions of screens or for non modals on older versions
|
||||||
|
detachInactiveScreens = shouldUseActivityState || mode !== 'modal',
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { scenes, layout, gestures, headerHeights } = this.state;
|
const { scenes, layout, gestures, headerHeights } = this.state;
|
||||||
@@ -385,6 +405,7 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
const focusedRoute = state.routes[state.index];
|
const focusedRoute = state.routes[state.index];
|
||||||
const focusedDescriptor = descriptors[focusedRoute.key];
|
const focusedDescriptor = descriptors[focusedRoute.key];
|
||||||
const focusedOptions = focusedDescriptor ? focusedDescriptor.options : {};
|
const focusedOptions = focusedDescriptor ? focusedDescriptor.options : {};
|
||||||
|
const focusedHeaderHeight = headerHeights[focusedRoute.key];
|
||||||
|
|
||||||
let defaultTransitionPreset =
|
let defaultTransitionPreset =
|
||||||
mode === 'modal' ? ModalTransition : DefaultTransition;
|
mode === 'modal' ? ModalTransition : DefaultTransition;
|
||||||
@@ -403,212 +424,250 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
left = insets.left,
|
left = insets.left,
|
||||||
} = focusedOptions.safeAreaInsets || {};
|
} = focusedOptions.safeAreaInsets || {};
|
||||||
|
|
||||||
// Screens is buggy on iOS and web, so we only enable it on Android
|
let activeScreensLimit = 1;
|
||||||
// For modals, usually we want the screen underneath to be visible, so also disable it there
|
|
||||||
const isScreensEnabled = Platform.OS !== 'ios' && mode !== 'modal';
|
for (let i = scenes.length - 1; i >= 0; i--) {
|
||||||
|
const {
|
||||||
|
// By default, we don't want to detach the previous screen of the active one for modals
|
||||||
|
detachPreviousScreen = mode === 'modal'
|
||||||
|
? i !== scenes.length - 1
|
||||||
|
: true,
|
||||||
|
} = scenes[i].descriptor.options;
|
||||||
|
|
||||||
|
if (detachPreviousScreen === false) {
|
||||||
|
activeScreensLimit++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isFloatHeaderAbsolute =
|
||||||
|
headerMode === 'float'
|
||||||
|
? this.state.scenes.slice(-2).some((scene) => {
|
||||||
|
const { descriptor } = scene;
|
||||||
|
const options = descriptor ? descriptor.options : {};
|
||||||
|
const {
|
||||||
|
headerTransparent,
|
||||||
|
headerShown = isParentHeaderShown === false,
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
if (headerTransparent || headerShown === false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
: false;
|
||||||
|
|
||||||
|
const floatingHeader =
|
||||||
|
headerMode === 'float' ? (
|
||||||
|
<React.Fragment key="header">
|
||||||
|
{renderHeader({
|
||||||
|
mode: 'float',
|
||||||
|
layout,
|
||||||
|
insets: { top, right, bottom, left },
|
||||||
|
scenes,
|
||||||
|
getPreviousScene: this.getPreviousScene,
|
||||||
|
getFocusedRoute: this.getFocusedRoute,
|
||||||
|
onContentHeightChange: this.handleHeaderLayout,
|
||||||
|
gestureDirection:
|
||||||
|
focusedOptions.gestureDirection !== undefined
|
||||||
|
? focusedOptions.gestureDirection
|
||||||
|
: defaultTransitionPreset.gestureDirection,
|
||||||
|
styleInterpolator:
|
||||||
|
focusedOptions.headerStyleInterpolator !== undefined
|
||||||
|
? focusedOptions.headerStyleInterpolator
|
||||||
|
: defaultTransitionPreset.headerStyleInterpolator,
|
||||||
|
style: [
|
||||||
|
styles.floating,
|
||||||
|
isFloatHeaderAbsolute && [
|
||||||
|
// Without this, the header buttons won't be touchable on Android when headerTransparent: true
|
||||||
|
{ height: focusedHeaderHeight },
|
||||||
|
styles.absolute,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HeaderShownContext.Consumer>
|
<React.Fragment>
|
||||||
{(isParentHeaderShown) => {
|
{isFloatHeaderAbsolute ? null : floatingHeader}
|
||||||
const isFloatHeaderAbsolute =
|
<MaybeScreenContainer
|
||||||
headerMode === 'float'
|
enabled={detachInactiveScreens}
|
||||||
? this.state.scenes.slice(-2).some((scene) => {
|
style={styles.container}
|
||||||
const { descriptor } = scene;
|
onLayout={this.handleLayout}
|
||||||
const options = descriptor ? descriptor.options : {};
|
>
|
||||||
const {
|
{routes.map((route, index, self) => {
|
||||||
headerTransparent,
|
const focused = focusedRoute.key === route.key;
|
||||||
headerShown = isParentHeaderShown === false,
|
const gesture = gestures[route.key];
|
||||||
} = options;
|
const scene = scenes[index];
|
||||||
|
|
||||||
if (headerTransparent || headerShown === false) {
|
// For the screens that shouldn't be active, the value is 0
|
||||||
return true;
|
// For those that should be active, but are not the top screen, the value is 1
|
||||||
}
|
// For those on top of the stack and with interaction enabled, the value is 2
|
||||||
|
// For the old implementation, it stays the same it was
|
||||||
|
let isScreenActive: Animated.AnimatedInterpolation | 2 | 1 | 0 = 1;
|
||||||
|
|
||||||
return false;
|
if (shouldUseActivityState) {
|
||||||
})
|
if (index < self.length - activeScreensLimit - 1) {
|
||||||
: false;
|
// screen should be inactive because it is too deep in the stack
|
||||||
|
isScreenActive = STATE_INACTIVE;
|
||||||
|
} else {
|
||||||
|
const sceneForActivity = scenes[self.length - 1];
|
||||||
|
const outputValue =
|
||||||
|
index === self.length - 1
|
||||||
|
? STATE_ON_TOP // the screen is on top after the transition
|
||||||
|
: index >= self.length - activeScreensLimit
|
||||||
|
? STATE_TRANSITIONING_OR_BELOW_TOP // the screen should stay active after the transition, it is not on top but is in activeLimit
|
||||||
|
: STATE_INACTIVE; // the screen should be active only during the transition, it is at the edge of activeLimit
|
||||||
|
isScreenActive = sceneForActivity
|
||||||
|
? sceneForActivity.progress.current.interpolate({
|
||||||
|
inputRange: [0, 1 - EPSILON, 1],
|
||||||
|
outputRange: [1, 1, outputValue],
|
||||||
|
extrapolate: 'clamp',
|
||||||
|
})
|
||||||
|
: STATE_TRANSITIONING_OR_BELOW_TOP;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isScreenActive = scene.progress.next
|
||||||
|
? scene.progress.next.interpolate({
|
||||||
|
inputRange: [0, 1 - EPSILON, 1],
|
||||||
|
outputRange: [1, 1, 0],
|
||||||
|
extrapolate: 'clamp',
|
||||||
|
})
|
||||||
|
: 1;
|
||||||
|
}
|
||||||
|
|
||||||
const floatingHeader =
|
const {
|
||||||
headerMode === 'float' ? (
|
safeAreaInsets,
|
||||||
<React.Fragment key="header">
|
headerShown = isParentHeaderShown === false,
|
||||||
{renderHeader({
|
headerTransparent,
|
||||||
mode: 'float',
|
cardShadowEnabled,
|
||||||
layout,
|
cardOverlayEnabled,
|
||||||
insets: { top, right, bottom, left },
|
cardOverlay,
|
||||||
scenes,
|
cardStyle,
|
||||||
getPreviousScene: this.getPreviousScene,
|
animationEnabled,
|
||||||
getFocusedRoute: this.getFocusedRoute,
|
gestureResponseDistance,
|
||||||
onContentHeightChange: this.handleHeaderLayout,
|
gestureVelocityImpact,
|
||||||
gestureDirection:
|
gestureDirection = defaultTransitionPreset.gestureDirection,
|
||||||
focusedOptions.gestureDirection !== undefined
|
transitionSpec = defaultTransitionPreset.transitionSpec,
|
||||||
? focusedOptions.gestureDirection
|
cardStyleInterpolator = animationEnabled === false
|
||||||
: defaultTransitionPreset.gestureDirection,
|
? forNoAnimationCard
|
||||||
styleInterpolator:
|
: defaultTransitionPreset.cardStyleInterpolator,
|
||||||
focusedOptions.headerStyleInterpolator !== undefined
|
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
|
||||||
? focusedOptions.headerStyleInterpolator
|
} = scene.descriptor
|
||||||
: defaultTransitionPreset.headerStyleInterpolator,
|
? scene.descriptor.options
|
||||||
style: [
|
: ({} as StackNavigationOptions);
|
||||||
styles.floating,
|
|
||||||
isFloatHeaderAbsolute && styles.absolute,
|
|
||||||
],
|
|
||||||
})}
|
|
||||||
</React.Fragment>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
return (
|
let transitionConfig = {
|
||||||
<React.Fragment>
|
gestureDirection,
|
||||||
{isFloatHeaderAbsolute ? null : floatingHeader}
|
transitionSpec,
|
||||||
<MaybeScreenContainer
|
cardStyleInterpolator,
|
||||||
enabled={isScreensEnabled}
|
headerStyleInterpolator,
|
||||||
style={styles.container}
|
};
|
||||||
onLayout={this.handleLayout}
|
|
||||||
|
// When a screen is not the last, it should use next screen's transition config
|
||||||
|
// Many transitions also animate the previous screen, so using 2 different transitions doesn't look right
|
||||||
|
// For example combining a slide and a modal transition would look wrong otherwise
|
||||||
|
// With this approach, combining different transition styles in the same navigator mostly looks right
|
||||||
|
// This will still be broken when 2 transitions have different idle state (e.g. modal presentation),
|
||||||
|
// but majority of the transitions look alright
|
||||||
|
if (index !== self.length - 1) {
|
||||||
|
const nextScene = scenes[index + 1];
|
||||||
|
|
||||||
|
if (nextScene) {
|
||||||
|
const {
|
||||||
|
animationEnabled,
|
||||||
|
gestureDirection = defaultTransitionPreset.gestureDirection,
|
||||||
|
transitionSpec = defaultTransitionPreset.transitionSpec,
|
||||||
|
cardStyleInterpolator = animationEnabled === false
|
||||||
|
? forNoAnimationCard
|
||||||
|
: defaultTransitionPreset.cardStyleInterpolator,
|
||||||
|
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
|
||||||
|
} = nextScene.descriptor
|
||||||
|
? nextScene.descriptor.options
|
||||||
|
: ({} as StackNavigationOptions);
|
||||||
|
|
||||||
|
transitionConfig = {
|
||||||
|
gestureDirection,
|
||||||
|
transitionSpec,
|
||||||
|
cardStyleInterpolator,
|
||||||
|
headerStyleInterpolator,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
top: safeAreaInsetTop = insets.top,
|
||||||
|
right: safeAreaInsetRight = insets.right,
|
||||||
|
bottom: safeAreaInsetBottom = insets.bottom,
|
||||||
|
left: safeAreaInsetLeft = insets.left,
|
||||||
|
} = safeAreaInsets || {};
|
||||||
|
|
||||||
|
const headerHeight =
|
||||||
|
headerMode !== 'none' && headerShown !== false
|
||||||
|
? headerHeights[route.key]
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MaybeScreen
|
||||||
|
key={route.key}
|
||||||
|
style={StyleSheet.absoluteFill}
|
||||||
|
enabled={detachInactiveScreens}
|
||||||
|
active={isScreenActive}
|
||||||
|
pointerEvents="box-none"
|
||||||
>
|
>
|
||||||
{routes.map((route, index, self) => {
|
<CardContainer
|
||||||
const focused = focusedRoute.key === route.key;
|
index={index}
|
||||||
const gesture = gestures[route.key];
|
active={index === self.length - 1}
|
||||||
const scene = scenes[index];
|
focused={focused}
|
||||||
|
closing={closingRouteKeys.includes(route.key)}
|
||||||
const isScreenActive = scene.progress.next
|
layout={layout}
|
||||||
? scene.progress.next.interpolate({
|
gesture={gesture}
|
||||||
inputRange: [0, 1 - EPSILON, 1],
|
scene={scene}
|
||||||
outputRange: [1, 1, 0],
|
safeAreaInsetTop={safeAreaInsetTop}
|
||||||
extrapolate: 'clamp',
|
safeAreaInsetRight={safeAreaInsetRight}
|
||||||
})
|
safeAreaInsetBottom={safeAreaInsetBottom}
|
||||||
: 1;
|
safeAreaInsetLeft={safeAreaInsetLeft}
|
||||||
|
cardOverlay={cardOverlay}
|
||||||
const {
|
cardOverlayEnabled={cardOverlayEnabled}
|
||||||
safeAreaInsets,
|
cardShadowEnabled={cardShadowEnabled}
|
||||||
headerShown = isParentHeaderShown === false,
|
cardStyle={cardStyle}
|
||||||
headerTransparent,
|
onPageChangeStart={onPageChangeStart}
|
||||||
cardShadowEnabled,
|
onPageChangeConfirm={onPageChangeConfirm}
|
||||||
cardOverlayEnabled,
|
onPageChangeCancel={onPageChangeCancel}
|
||||||
cardOverlay,
|
onGestureStart={onGestureStart}
|
||||||
cardStyle,
|
onGestureCancel={onGestureCancel}
|
||||||
animationEnabled,
|
onGestureEnd={onGestureEnd}
|
||||||
gestureResponseDistance,
|
gestureResponseDistance={gestureResponseDistance}
|
||||||
gestureVelocityImpact,
|
headerHeight={headerHeight}
|
||||||
gestureDirection = defaultTransitionPreset.gestureDirection,
|
isParentHeaderShown={isParentHeaderShown}
|
||||||
transitionSpec = defaultTransitionPreset.transitionSpec,
|
onHeaderHeightChange={this.handleHeaderLayout}
|
||||||
cardStyleInterpolator = animationEnabled === false
|
getPreviousScene={this.getPreviousScene}
|
||||||
? forNoAnimationCard
|
getFocusedRoute={this.getFocusedRoute}
|
||||||
: defaultTransitionPreset.cardStyleInterpolator,
|
mode={mode}
|
||||||
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
|
headerMode={headerMode}
|
||||||
} = scene.descriptor
|
headerShown={headerShown}
|
||||||
? scene.descriptor.options
|
hasAbsoluteHeader={
|
||||||
: ({} as StackNavigationOptions);
|
isFloatHeaderAbsolute && !headerTransparent
|
||||||
|
|
||||||
let transitionConfig = {
|
|
||||||
gestureDirection,
|
|
||||||
transitionSpec,
|
|
||||||
cardStyleInterpolator,
|
|
||||||
headerStyleInterpolator,
|
|
||||||
};
|
|
||||||
|
|
||||||
// When a screen is not the last, it should use next screen's transition config
|
|
||||||
// Many transitions also animate the previous screen, so using 2 different transitions doesn't look right
|
|
||||||
// For example combining a slide and a modal transition would look wrong otherwise
|
|
||||||
// With this approach, combining different transition styles in the same navigator mostly looks right
|
|
||||||
// This will still be broken when 2 transitions have different idle state (e.g. modal presentation),
|
|
||||||
// but majority of the transitions look alright
|
|
||||||
if (index !== self.length - 1) {
|
|
||||||
const nextScene = scenes[index + 1];
|
|
||||||
|
|
||||||
if (nextScene) {
|
|
||||||
const {
|
|
||||||
animationEnabled,
|
|
||||||
gestureDirection = defaultTransitionPreset.gestureDirection,
|
|
||||||
transitionSpec = defaultTransitionPreset.transitionSpec,
|
|
||||||
cardStyleInterpolator = animationEnabled === false
|
|
||||||
? forNoAnimationCard
|
|
||||||
: defaultTransitionPreset.cardStyleInterpolator,
|
|
||||||
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
|
|
||||||
} = nextScene.descriptor
|
|
||||||
? nextScene.descriptor.options
|
|
||||||
: ({} as StackNavigationOptions);
|
|
||||||
|
|
||||||
transitionConfig = {
|
|
||||||
gestureDirection,
|
|
||||||
transitionSpec,
|
|
||||||
cardStyleInterpolator,
|
|
||||||
headerStyleInterpolator,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
renderHeader={renderHeader}
|
||||||
const {
|
renderScene={renderScene}
|
||||||
top: safeAreaInsetTop = insets.top,
|
onOpenRoute={onOpenRoute}
|
||||||
right: safeAreaInsetRight = insets.right,
|
onCloseRoute={onCloseRoute}
|
||||||
bottom: safeAreaInsetBottom = insets.bottom,
|
onTransitionStart={onTransitionStart}
|
||||||
left: safeAreaInsetLeft = insets.left,
|
onTransitionEnd={onTransitionEnd}
|
||||||
} = safeAreaInsets || {};
|
gestureEnabled={index !== 0 && getGesturesEnabled({ route })}
|
||||||
|
gestureVelocityImpact={gestureVelocityImpact}
|
||||||
const headerHeight =
|
{...transitionConfig}
|
||||||
headerMode !== 'none' && headerShown !== false
|
/>
|
||||||
? headerHeights[route.key]
|
</MaybeScreen>
|
||||||
: 0;
|
);
|
||||||
|
})}
|
||||||
return (
|
</MaybeScreenContainer>
|
||||||
<MaybeScreen
|
{isFloatHeaderAbsolute ? floatingHeader : null}
|
||||||
key={route.key}
|
</React.Fragment>
|
||||||
style={StyleSheet.absoluteFill}
|
|
||||||
enabled={isScreensEnabled}
|
|
||||||
active={isScreenActive}
|
|
||||||
pointerEvents="box-none"
|
|
||||||
>
|
|
||||||
<CardContainer
|
|
||||||
index={index}
|
|
||||||
active={index === self.length - 1}
|
|
||||||
focused={focused}
|
|
||||||
closing={closingRouteKeys.includes(route.key)}
|
|
||||||
layout={layout}
|
|
||||||
gesture={gesture}
|
|
||||||
scene={scene}
|
|
||||||
safeAreaInsetTop={safeAreaInsetTop}
|
|
||||||
safeAreaInsetRight={safeAreaInsetRight}
|
|
||||||
safeAreaInsetBottom={safeAreaInsetBottom}
|
|
||||||
safeAreaInsetLeft={safeAreaInsetLeft}
|
|
||||||
cardOverlay={cardOverlay}
|
|
||||||
cardOverlayEnabled={cardOverlayEnabled}
|
|
||||||
cardShadowEnabled={cardShadowEnabled}
|
|
||||||
cardStyle={cardStyle}
|
|
||||||
onPageChangeStart={onPageChangeStart}
|
|
||||||
onPageChangeConfirm={onPageChangeConfirm}
|
|
||||||
onPageChangeCancel={onPageChangeCancel}
|
|
||||||
onGestureStart={onGestureStart}
|
|
||||||
onGestureCancel={onGestureCancel}
|
|
||||||
onGestureEnd={onGestureEnd}
|
|
||||||
gestureResponseDistance={gestureResponseDistance}
|
|
||||||
headerHeight={headerHeight}
|
|
||||||
onHeaderHeightChange={this.handleHeaderLayout}
|
|
||||||
getPreviousScene={this.getPreviousScene}
|
|
||||||
getFocusedRoute={this.getFocusedRoute}
|
|
||||||
mode={mode}
|
|
||||||
headerMode={headerMode}
|
|
||||||
headerShown={headerShown}
|
|
||||||
hasAbsoluteHeader={
|
|
||||||
isFloatHeaderAbsolute && !headerTransparent
|
|
||||||
}
|
|
||||||
renderHeader={renderHeader}
|
|
||||||
renderScene={renderScene}
|
|
||||||
onOpenRoute={onOpenRoute}
|
|
||||||
onCloseRoute={onCloseRoute}
|
|
||||||
onTransitionStart={onTransitionStart}
|
|
||||||
onTransitionEnd={onTransitionEnd}
|
|
||||||
gestureEnabled={
|
|
||||||
index !== 0 && getGesturesEnabled({ route })
|
|
||||||
}
|
|
||||||
gestureVelocityImpact={gestureVelocityImpact}
|
|
||||||
{...transitionConfig}
|
|
||||||
/>
|
|
||||||
</MaybeScreen>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</MaybeScreenContainer>
|
|
||||||
{isFloatHeaderAbsolute ? floatingHeader : null}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</HeaderShownContext.Consumer>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
StackActions,
|
StackActions,
|
||||||
StackNavigationState,
|
StackNavigationState,
|
||||||
Route,
|
Route,
|
||||||
|
ParamListBase,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
|
|
||||||
import { GestureHandlerRootView } from '../GestureHandler';
|
import { GestureHandlerRootView } from '../GestureHandler';
|
||||||
@@ -20,9 +21,10 @@ import type {
|
|||||||
StackNavigationConfig,
|
StackNavigationConfig,
|
||||||
StackDescriptorMap,
|
StackDescriptorMap,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
import HeaderShownContext from '../../utils/HeaderShownContext';
|
||||||
|
|
||||||
type Props = StackNavigationConfig & {
|
type Props = StackNavigationConfig & {
|
||||||
state: StackNavigationState;
|
state: StackNavigationState<ParamListBase>;
|
||||||
navigation: StackNavigationHelpers;
|
navigation: StackNavigationHelpers;
|
||||||
descriptors: StackDescriptorMap;
|
descriptors: StackDescriptorMap;
|
||||||
};
|
};
|
||||||
@@ -455,29 +457,34 @@ export default class StackView extends React.Component<Props, State> {
|
|||||||
{(insets) => (
|
{(insets) => (
|
||||||
<KeyboardManager enabled={keyboardHandlingEnabled !== false}>
|
<KeyboardManager enabled={keyboardHandlingEnabled !== false}>
|
||||||
{(props) => (
|
{(props) => (
|
||||||
<CardStack
|
<HeaderShownContext.Consumer>
|
||||||
mode={mode}
|
{(isParentHeaderShown) => (
|
||||||
insets={insets as EdgeInsets}
|
<CardStack
|
||||||
getPreviousRoute={this.getPreviousRoute}
|
mode={mode}
|
||||||
getGesturesEnabled={this.getGesturesEnabled}
|
insets={insets as EdgeInsets}
|
||||||
routes={routes}
|
isParentHeaderShown={isParentHeaderShown}
|
||||||
openingRouteKeys={openingRouteKeys}
|
getPreviousRoute={this.getPreviousRoute}
|
||||||
closingRouteKeys={closingRouteKeys}
|
getGesturesEnabled={this.getGesturesEnabled}
|
||||||
onOpenRoute={this.handleOpenRoute}
|
routes={routes}
|
||||||
onCloseRoute={this.handleCloseRoute}
|
openingRouteKeys={openingRouteKeys}
|
||||||
onTransitionStart={this.handleTransitionStart}
|
closingRouteKeys={closingRouteKeys}
|
||||||
onTransitionEnd={this.handleTransitionEnd}
|
onOpenRoute={this.handleOpenRoute}
|
||||||
renderHeader={this.renderHeader}
|
onCloseRoute={this.handleCloseRoute}
|
||||||
renderScene={this.renderScene}
|
onTransitionStart={this.handleTransitionStart}
|
||||||
headerMode={headerMode}
|
onTransitionEnd={this.handleTransitionEnd}
|
||||||
state={state}
|
renderHeader={this.renderHeader}
|
||||||
descriptors={descriptors}
|
renderScene={this.renderScene}
|
||||||
onGestureStart={this.handleGestureStart}
|
headerMode={headerMode}
|
||||||
onGestureEnd={this.handleGestureEnd}
|
state={state}
|
||||||
onGestureCancel={this.handleGestureCancel}
|
descriptors={descriptors}
|
||||||
{...rest}
|
onGestureStart={this.handleGestureStart}
|
||||||
{...props}
|
onGestureEnd={this.handleGestureEnd}
|
||||||
/>
|
onGestureCancel={this.handleGestureCancel}
|
||||||
|
{...rest}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</HeaderShownContext.Consumer>
|
||||||
)}
|
)}
|
||||||
</KeyboardManager>
|
</KeyboardManager>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user