Compare commits

...

39 Commits

Author SHA1 Message Date
Satyajit Sahoo
2ca8c34b2d chore: publish
- react-navigation-stack@2.6.0
2020-06-06 22:38:25 +02:00
Satyajit Sahoo
03f9f8cd8e feat: sync latest stack 2020-06-06 02:28:20 +02:00
Satyajit Sahoo
1ee8c76df3 chore: publish
- react-navigation-stack@2.5.1
2020-05-15 22:02:17 +02:00
Satyajit Sahoo
bc1313b61d fix: sync latest stack 2020-05-15 22:00:38 +02:00
Satyajit Sahoo
28982ffca0 chore: publish
- react-navigation-stack@2.5.0
2020-05-12 00:57:02 +02:00
Satyajit Sahoo
bb82d5000f feat: sync latest stack 2020-05-11 18:34:46 +02:00
Satyajit Sahoo
1f30227049 chore: update eslint and prettier 2020-05-11 18:17:15 +02:00
Satyajit Sahoo
f34ec46111 chore: publish
- react-navigation-animated-switch@0.5.12
 - @react-navigation/core@3.7.6
 - react-navigation-drawer@2.4.13
 - react-navigation-material-bottom-tabs@2.2.12
 - @react-navigation/native@3.7.13
 - react-navigation@4.3.9
 - react-navigation-stack@2.4.0
 - react-navigation-tabs@2.8.13
2020-05-01 01:45:52 +02:00
Satyajit Sahoo
8b79e9bee6 feat: sync latest stack 2020-05-01 01:45:10 +02:00
linzera
e09906a423 fix: change old docUrl to new docUrl due v5 2020-04-30 16:57:36 +02:00
Satyajit Sahoo
ed7ffb98f4 chore: publish
- react-navigation-animated-switch@0.5.11
 - react-navigation-drawer@2.4.12
 - react-navigation-material-bottom-tabs@2.2.11
 - @react-navigation/native@3.7.12
 - react-navigation@4.3.8
 - react-navigation-stack@2.3.13
 - react-navigation-tabs@2.8.12
2020-04-22 16:47:35 +02:00
Satyajit Sahoo
90eff6a5e2 chore: sync latest stack 2020-04-22 16:45:32 +02:00
Satyajit Sahoo
b4691d7de2 chore: update react-native-safe-area-view 2020-04-22 16:31:49 +02:00
Satyajit Sahoo
f99f523a37 chore: publish
- react-navigation-stack@2.3.12
2020-04-18 01:49:20 +02:00
Satyajit Sahoo
05b47dbb09 chore: sync latest stack 2020-04-18 01:48:06 +02:00
Alejandro
4c4e66f05c fix: stack peer warnings 2020-04-18 01:15:41 +02:00
Satyajit Sahoo
720d1f970d chore: publish
- react-navigation-animated-switch@0.5.10
 - @react-navigation/core@3.7.5
 - react-navigation-drawer@2.4.11
 - react-navigation-material-bottom-tabs@2.2.10
 - @react-navigation/native@3.7.11
 - react-navigation@4.3.7
 - react-navigation-stack@2.3.11
 - react-navigation-tabs@2.8.11
2020-04-02 12:30:56 +02:00
Satyajit Sahoo
66a4dbccd8 fix: emit refocus for child navigators 2020-04-02 12:29:54 +02:00
Satyajit Sahoo
7925f0c8a3 chore: publish
- react-navigation-animated-switch@0.5.9
 - @react-navigation/core@3.7.4
 - react-navigation-drawer@2.4.10
 - react-navigation-material-bottom-tabs@2.2.9
 - @react-navigation/native@3.7.10
 - react-navigation@4.3.6
 - react-navigation-stack@2.3.10
 - react-navigation-tabs@2.8.10
2020-03-31 18:19:11 +02:00
Satyajit Sahoo
b3d77b79b7 chore: update typescript 2020-03-31 18:15:29 +02:00
Satyajit Sahoo
1d527ce16a chore: sync latest stack 2020-03-31 18:07:52 +02:00
Satyajit Sahoo
3bec1c964a fix: remove isTransitioning from SwitchRouter state 2020-03-31 17:38:42 +02:00
osdnk
a7e0c193cd chore: publish
- react-navigation-animated-switch@0.5.8
 - @react-navigation/core@3.7.3
 - react-navigation-drawer@2.4.9
 - react-navigation-material-bottom-tabs@2.2.8
 - @react-navigation/native@3.7.9
 - react-navigation@4.3.5
 - react-navigation-stack@2.3.9
 - react-navigation-tabs@2.8.9
2020-03-28 22:34:14 +01:00
Satyajit Sahoo
ac98c0a668 fix: always emit didFocus/didBlur for root navigator 2020-03-28 17:20:33 +01:00
Satyajit Sahoo
14a6538cc8 fix: emit didFocus and didBlur events based on parent's transition 2020-03-28 16:04:06 +01:00
Satyajit Sahoo
c9313a1419 chore: publish
- react-navigation-animated-switch@0.5.7
 - @react-navigation/core@3.7.2
 - react-navigation-drawer@2.4.8
 - react-navigation-material-bottom-tabs@2.2.7
 - @react-navigation/native@3.7.8
 - react-navigation@4.3.4
 - react-navigation-stack@2.3.8
 - react-navigation-tabs@2.8.8
2020-03-28 00:45:14 +01:00
Satyajit Sahoo
ec39721a74 chore: update yarn.lock 2020-03-27 21:55:06 +01:00
Satyajit Sahoo
a54d48b650 chore: add babel-jest 2020-03-27 21:44:39 +01:00
Satyajit Sahoo
8a9a9cd7d1 chore: sync latest stack 2020-03-27 21:34:40 +01:00
Satyajit Sahoo
cd08338186 fix: rework focus and blur events to make them more reliable
closes #4867, #6187, #6451, #7628, #7749
2020-03-27 21:28:30 +01:00
Satyajit Sahoo
d2433f0ab8 fix: don't handle prune if there's only one route
This was preventing the action from bubbling up which would prevent the screen from getting removed
2020-03-27 20:09:52 +01:00
osdnk
66374db859 chore: publish
- react-navigation-stack@2.3.7
2020-03-27 10:25:42 +01:00
osdnk
8dbe0299a8 chore: sync latest stack 2020-03-27 10:24:28 +01:00
Satyajit Sahoo
e4bc6dd506 chore: publish
- react-navigation-stack@2.3.6
2020-03-23 11:45:26 +01:00
Satyajit Sahoo
4bac3bfc1a chore: sync latest stack 2020-03-23 11:44:28 +01:00
Satyajit Sahoo
7656b35ee8 chore: publish
- react-navigation-animated-switch@0.5.6
 - @react-navigation/core@3.7.1
 - react-navigation-drawer@2.4.7
 - react-navigation-material-bottom-tabs@2.2.6
 - @react-navigation/native@3.7.7
 - react-navigation@4.3.3
 - react-navigation-stack@2.3.5
 - react-navigation-tabs@2.8.7
2020-03-23 00:09:22 +01:00
Satyajit Sahoo
597aa51fad chore: sync latest stack 2020-03-23 00:08:30 +01:00
Satyajit Sahoo
a929933bde chore: update prettier 2020-03-23 00:02:47 +01:00
Satyajit Sahoo
911d6bb2f4 chore: explicitely set tag for core and native packages 2020-03-22 17:08:01 +01:00
110 changed files with 4853 additions and 3745 deletions

View File

@@ -41,7 +41,7 @@ import StackWithTranslucentHeader from './src/StackWithTranslucentHeader';
import SwitchWithStacks from './src/SwitchWithStacks';
import TabsInDrawer from './src/TabsInDrawer';
import DragLimitedToModal from './src/DragLimitedToModal';
import EventsStack from './src/EventsStack';
import FocusEvents from './src/FocusEvents';
import FullScreen from './src/FullScreen';
import GestureInteraction from './src/GestureInteraction';
import {
@@ -144,7 +144,7 @@ const ExampleInfo = {
name: 'Drag limited to modal',
screen: DragLimitedToModal,
},
EventsStack: { name: 'Events Stack', screen: EventsStack },
EventsStack: { name: 'Focus Events', screen: FocusEvents },
FullScreen: { name: 'Fullscreen Stack', screen: FullScreen },
GestureInteraction: {
name: 'Gesture interaction',
@@ -335,7 +335,7 @@ class MainScreen extends React.Component<any, State> {
}}
>
{(Object.keys(ExampleInfo) as (keyof typeof ExampleInfo)[]).map(
routeName => (
(routeName) => (
<RectButton
key={routeName}
underlayColor="#ccc"

View File

@@ -1,4 +1,4 @@
module.exports = function(api) {
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],

View File

@@ -11,8 +11,8 @@ const modules = ['@expo/vector-icons']
// List all packages under `packages/`
.readdirSync(packages)
// Ignore hidden files such as .DS_Store
.filter(p => !p.startsWith('.'))
.map(p => {
.filter((p) => !p.startsWith('.'))
.map((p) => {
const pak = JSON.parse(
fs.readFileSync(path.join(packages, p, 'package.json'), 'utf8')
);
@@ -50,7 +50,7 @@ module.exports = {
},
server: {
enhanceMiddleware: middleware => {
enhanceMiddleware: (middleware) => {
return (req, res, next) => {
// When an asset is imported outside the project root, it has wrong path on Android
// This happens for the back button in stack, so we fix the path to correct one

View File

@@ -62,7 +62,7 @@ const CustomTabBar = ({
const { routes } = navigation.state;
return (
<SafeAreaView style={styles.tabContainer}>
{routes.map(route => (
{routes.map((route) => (
<BorderlessButton
onPress={() => navigation.navigate(route.routeName)}
style={styles.tab}

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { Button, ScrollView, View, Text } from 'react-native';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import {
createStackNavigator,
NavigationStackProp,
@@ -22,24 +23,36 @@ class FocusTag extends React.Component<NavigationStackScreenProps> {
state = { mode: 'didBlur' };
componentDidMount() {
this.props.navigation.addListener('willFocus', () => {
this.setMode('willFocus');
this._willFocusSub = this.props.navigation.addListener('willFocus', () => {
this._handleEvent('willFocus');
});
this.props.navigation.addListener('willBlur', () => {
this.setMode('willBlur');
this._willBlurSub = this.props.navigation.addListener('willBlur', () => {
this._handleEvent('willBlur');
});
this.props.navigation.addListener('didFocus', () => {
this.setMode('didFocus');
this._didFocusSub = this.props.navigation.addListener('didFocus', () => {
this._handleEvent('didFocus');
});
this.props.navigation.addListener('didBlur', () => {
this.setMode('didBlur');
this._didBlurSub = this.props.navigation.addListener('didBlur', () => {
this._handleEvent('didBlur');
});
}
setMode = (mode: string) => {
componentWillUnmount() {
this._willFocusSub?.remove();
this._willBlurSub?.remove();
this._didFocusSub?.remove();
this._didBlurSub?.remove();
}
_willFocusSub: { remove: () => void } | undefined;
_willBlurSub: { remove: () => void } | undefined;
_didFocusSub: { remove: () => void } | undefined;
_didBlurSub: { remove: () => void } | undefined;
_handleEvent = (mode: string) => {
this.setState({ mode });
};
@@ -119,18 +132,19 @@ class SampleScreen extends React.Component<NavigationStackScreenProps> {
}
}
const SimpleStack = createStackNavigator(
const Stack = createStackNavigator(
{
PageOne: {
screen: SampleScreen,
},
PageTwo: {
screen: SampleScreen,
},
PageOne: SampleScreen,
PageTwo: SampleScreen,
},
{
initialRouteName: 'PageOne',
}
);
export default SimpleStack;
const Tabs = createBottomTabNavigator({
A: Stack,
B: Stack,
});
export default Tabs;

View File

@@ -44,7 +44,7 @@ const ContainerWithButtons = withNavigation(
const MapScreen = () => (
<ContainerWithButtons>
<DrawerGestureContext.Consumer>
{ref => (
{(ref) => (
<NativeViewGestureHandler waitFor={ref}>
<MapView style={{ flex: 1 }} />
</NativeViewGestureHandler>
@@ -60,7 +60,7 @@ MapScreen.navigationOptions = {
const WebViewScreen = () => (
<ContainerWithButtons>
<DrawerGestureContext.Consumer>
{ref => (
{(ref) => (
<NativeViewGestureHandler waitFor={ref}>
<WebView
style={{ flex: 1 }}

View File

@@ -33,7 +33,7 @@ const BarCodeScreenBase = (
<BarCodeScanner
onBarCodeScanned={
props.isFocused
? data => {
? (data) => {
console.log('scanned...');
props.navigation.navigate('Info', { data });
}
@@ -51,7 +51,7 @@ BarCodeScreenBase.navigationOptions = {
const BarCodeScreen = withNavigationFocus(BarCodeScreenBase);
const InfoScreen: NavigationStackScreenComponent = props => {
const InfoScreen: NavigationStackScreenComponent = (props) => {
return (
<View style={{ flex: 1 }}>
<Text>{JSON.stringify(props.navigation.getParam('data'))}</Text>

View File

@@ -82,7 +82,7 @@ class ListScreen extends React.Component<
state = { isInverted: false };
onSwitch = () =>
this.setState(prevState => ({ isInverted: !prevState.isInverted }));
this.setState((prevState) => ({ isInverted: !prevState.isInverted }));
render() {
return (

View File

@@ -114,7 +114,7 @@ const MyNavScreen = ({
title="Set unlocked"
/>
),
unlocked: (
'unlocked': (
<Button
onPress={() =>
navigation.setParams({ drawerLockMode: 'locked-open' })
@@ -158,7 +158,7 @@ const DrawerContents = ({
<Animated.View style={{ transform: [{ translateX }] }}>
<ScrollView>
<SafeAreaView forceInset={{ top: 'always' }}>
{navigation.state.routes.map(route => (
{navigation.state.routes.map((route) => (
<DrawerItem
key={route.key}
navigation={descriptors[route.key].navigation}

View File

@@ -32,7 +32,7 @@ class RightDrawer extends Component<DrawerContentComponentProps> {
<ScrollView
style={{ height: '100%', width: '100%', backgroundColor: '#333333' }}
>
{this.state.categories.map(key => {
{this.state.categories.map((key) => {
return (
<TouchableOpacity
key={key.n}

View File

@@ -59,7 +59,7 @@ export default function PhotoGrid({ id }: { id: string }) {
return (
<NavigationAwareScrollView contentContainerStyle={styles.content}>
{PHOTOS.map(uri => (
{PHOTOS.map((uri) => (
<View key={uri} style={styles.item}>
<Image source={{ uri }} style={styles.photo} />
</View>

View File

@@ -106,7 +106,7 @@ const MyNavScreen = ({
title="Set unlocked"
/>
),
unlocked: (
'unlocked': (
<Button
onPress={() =>
navigation.setParams({ drawerLockMode: 'locked-open' })

View File

@@ -25,7 +25,7 @@ const ListScreen = (props: NavigationStackScreenProps) => (
const AnotherScreen = () => (
<CardAnimationContext.Consumer>
{value => {
{(value) => {
const scale = value
? value.current.progress.interpolate({
inputRange: [0, 1],
@@ -67,7 +67,7 @@ const YetAnotherScreen = () => (
}}
>
<CardAnimationContext.Consumer>
{value => (
{(value) => (
<Animated.Text
style={{
fontSize: 24,
@@ -84,7 +84,7 @@ const YetAnotherScreen = () => (
)}
</CardAnimationContext.Consumer>
<CardAnimationContext.Consumer>
{value => (
{(value) => (
<Animated.Text
style={{
fontSize: 24,

View File

@@ -59,7 +59,7 @@ const Drawer = createDrawerNavigator(
},
},
{
contentComponent: props => <Menu {...props} />,
contentComponent: (props) => <Menu {...props} />,
navigationOptions: { title: 'Example' },
}
);

View File

@@ -5,7 +5,7 @@ import {
NavigationStackScreenComponent,
} from 'react-navigation-stack';
const ListScreen: NavigationStackScreenComponent = function(props) {
const ListScreen: NavigationStackScreenComponent = function (props) {
return (
<View
style={{
@@ -29,7 +29,7 @@ const ListScreen: NavigationStackScreenComponent = function(props) {
);
};
const ModalDialogScreen: NavigationStackScreenComponent = function(props) {
const ModalDialogScreen: NavigationStackScreenComponent = function (props) {
return (
<View
style={{

View File

@@ -18,7 +18,7 @@
"author": "Adam Miskiewicz <adam@sk3vy.com>, Eric Vicenti <ericvicenti@gmail.com>, Brent Vatne <brent@expo.io>, Satyajit Sahoo <satyajit.happy@gmail.com>",
"scripts": {
"lint": "eslint --ext '.js,.ts,.tsx' .",
"typescript": "tsc --noEmit",
"typescript": "tsc --noEmit --composite false",
"test": "jest",
"prerelease": "yarn lerna run clean",
"release": "yarn lerna publish",
@@ -33,15 +33,16 @@
"@babel/runtime": "^7.8.7",
"@commitlint/config-conventional": "^8.3.4",
"@types/jest": "^25.1.4",
"babel-jest": "^25.2.3",
"commitlint": "^8.3.5",
"core-js": "^3.6.4",
"eslint": "^6.8.0",
"eslint-config-satya164": "^3.1.5",
"eslint": "^7.0.0",
"eslint-config-satya164": "^3.1.7",
"husky": "^4.2.1",
"jest": "^25.1.0",
"lerna": "^3.20.2",
"prettier": "^1.19.1",
"typescript": "~3.7.5"
"prettier": "^2.0.5",
"typescript": "~3.8.3"
},
"resolutions": {
"react": "~16.9.0",

View File

@@ -3,6 +3,62 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.5.12](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.11...react-navigation-animated-switch@0.5.12) (2020-04-30)
**Note:** Version bump only for package react-navigation-animated-switch
## [0.5.11](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.10...react-navigation-animated-switch@0.5.11) (2020-04-22)
**Note:** Version bump only for package react-navigation-animated-switch
## [0.5.10](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.9...react-navigation-animated-switch@0.5.10) (2020-04-02)
**Note:** Version bump only for package react-navigation-animated-switch
## [0.5.9](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.8...react-navigation-animated-switch@0.5.9) (2020-03-31)
**Note:** Version bump only for package react-navigation-animated-switch
## [0.5.8](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.6...react-navigation-animated-switch@0.5.8) (2020-03-28)
**Note:** Version bump only for package react-navigation-animated-switch
## [0.5.7](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.6...react-navigation-animated-switch@0.5.7) (2020-03-27)
**Note:** Version bump only for package react-navigation-animated-switch
## [0.5.6](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.5...react-navigation-animated-switch@0.5.6) (2020-03-22)
**Note:** Version bump only for package react-navigation-animated-switch
## [0.5.5](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.4...react-navigation-animated-switch@0.5.5) (2020-03-20)
**Note:** Version bump only for package react-navigation-animated-switch

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-animated-switch",
"version": "0.5.5",
"version": "0.5.12",
"description": "Animated switch for React Navigation",
"main": "lib/commonjs/index.js",
"react-native": "lib/module/index.js",
@@ -28,8 +28,8 @@
"react": "~16.9.0",
"react-native": "~0.61.5",
"react-native-reanimated": "~1.7.0",
"react-navigation": "^4.3.2",
"typescript": "~3.7.5"
"react-navigation": "^4.3.9",
"typescript": "~3.8.3"
},
"peerDependencies": {
"react": "*",

View File

@@ -3,6 +3,73 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.7.6](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.5...@react-navigation/core@3.7.6) (2020-04-30)
### Bug Fixes
* change old docUrl to new docUrl due v5 ([e09906a](https://github.com/react-navigation/react-navigation-core/commit/e09906a4235a0fca09140923ebe7af34b50b491a))
## [3.7.5](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.4...@react-navigation/core@3.7.5) (2020-04-02)
### Bug Fixes
* emit refocus for child navigators ([66a4dbc](https://github.com/react-navigation/react-navigation-core/commit/66a4dbccd8e7b3cda51a1d9c7e9397dfc58d6b9e))
## [3.7.4](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.3...@react-navigation/core@3.7.4) (2020-03-31)
### Bug Fixes
* remove isTransitioning from SwitchRouter state ([3bec1c9](https://github.com/react-navigation/react-navigation-core/commit/3bec1c964a49136c0ead8e8ba8a8c66c556bbcba))
## [3.7.3](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.1...@react-navigation/core@3.7.3) (2020-03-28)
### Bug Fixes
* always emit didFocus/didBlur for root navigator ([ac98c0a](https://github.com/react-navigation/react-navigation-core/commit/ac98c0a668fe21200d0c6f62ae3043f92cc5aa7e))
* don't handle prune if there's only one route ([d2433f0](https://github.com/react-navigation/react-navigation-core/commit/d2433f0ab8f9791df8169de4ddfdeed9bc699e3e))
* emit didFocus and didBlur events based on parent's transition ([14a6538](https://github.com/react-navigation/react-navigation-core/commit/14a6538cc8e12c50d5d10722d75c9395a0a281ec))
* rework focus and blur events to make them more reliable ([cd08338](https://github.com/react-navigation/react-navigation-core/commit/cd083381866506a192f1ec842ac169f2b4277ca5)), closes [#4867](https://github.com/react-navigation/react-navigation-core/issues/4867) [#6187](https://github.com/react-navigation/react-navigation-core/issues/6187) [#6451](https://github.com/react-navigation/react-navigation-core/issues/6451) [#7628](https://github.com/react-navigation/react-navigation-core/issues/7628) [#7749](https://github.com/react-navigation/react-navigation-core/issues/7749)
## [3.7.2](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.1...@react-navigation/core@3.7.2) (2020-03-27)
### Bug Fixes
* don't handle prune if there's only one route ([d2433f0](https://github.com/react-navigation/react-navigation-core/commit/d2433f0ab8f9791df8169de4ddfdeed9bc699e3e))
* rework focus and blur events to make them more reliable ([cd08338](https://github.com/react-navigation/react-navigation-core/commit/cd083381866506a192f1ec842ac169f2b4277ca5)), closes [#4867](https://github.com/react-navigation/react-navigation-core/issues/4867) [#6187](https://github.com/react-navigation/react-navigation-core/issues/6187) [#6451](https://github.com/react-navigation/react-navigation-core/issues/6451) [#7628](https://github.com/react-navigation/react-navigation-core/issues/7628) [#7749](https://github.com/react-navigation/react-navigation-core/issues/7749)
## [3.7.1](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.0...@react-navigation/core@3.7.1) (2020-03-22)
**Note:** Version bump only for package @react-navigation/core
# [3.7.0](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.6.1...@react-navigation/core@3.7.0) (2020-03-16)

View File

@@ -1,6 +1,6 @@
{
"name": "@react-navigation/core",
"version": "3.7.0",
"version": "3.7.6",
"description": "Core utilities for the react-navigation framework",
"main": "lib/commonjs/index.js",
"react-native": "lib/module/index.js",
@@ -15,7 +15,8 @@
},
"sideEffects": false,
"publishConfig": {
"access": "public"
"access": "public",
"tag": "4.x"
},
"keywords": [
"react-navigation",

View File

@@ -13,7 +13,7 @@ const StateUtils = {
* Gets a route by key. If the route isn't found, returns `null`.
*/
get(state, key) {
return state.routes.find(route => route.key === key) || null;
return state.routes.find((route) => route.key === key) || null;
},
/**
@@ -21,7 +21,7 @@ const StateUtils = {
* routes of the navigation state, or -1 if it is not present.
*/
indexOf(state, key) {
return state.routes.findIndex(route => route.key === key);
return state.routes.findIndex((route) => route.key === key);
},
/**
@@ -29,7 +29,7 @@ const StateUtils = {
* routes of the navigation state.
*/
has(state, key) {
return !!state.routes.some(route => route.key === key);
return !!state.routes.some((route) => route.key === key);
},
/**

View File

@@ -0,0 +1,148 @@
/* eslint-disable react/sort-comp */
import React from 'react';
import { NavigationActions, getNavigation, NavigationProvider } from '../index';
export default function createNavigationContainer(Component) {
class NavigationContainer extends React.Component {
static router = Component.router;
static navigationOptions = null;
constructor(props) {
super(props);
this._initialAction = NavigationActions.init();
this.state = {
nav: !props.loadNavigationState
? Component.router.getStateForAction(this._initialAction)
: null,
};
}
_actionEventSubscribers = new Set();
_onNavigationStateChange(prevNav, nav, action) {
if (typeof this.props.onNavigationStateChange === 'function') {
this.props.onNavigationStateChange(prevNav, nav, action);
}
}
componentDidUpdate() {
// Clear cached _navState every tick
if (this._navState === this.state.nav) {
this._navState = null;
}
}
async componentDidMount() {
// Initialize state. This must be done *after* any async code
// so we don't end up with a different value for this.state.nav
// due to changes while async function was resolving
let action = this._initialAction;
// eslint-disable-next-line react/no-access-state-in-setstate
let startupState = this.state.nav;
if (!startupState) {
startupState = Component.router.getStateForAction(action);
}
const dispatchActions = () =>
this._actionEventSubscribers.forEach((subscriber) =>
subscriber({
type: 'action',
action,
state: this.state.nav,
lastState: null,
})
);
if (startupState === this.state.nav) {
dispatchActions();
return;
}
// eslint-disable-next-line react/no-did-mount-set-state
this.setState({ nav: startupState }, () => {
dispatchActions();
});
}
dispatch = (action) => {
if (this.props.navigation) {
return this.props.navigation.dispatch(action);
}
// navState will have the most up-to-date value, because setState sometimes behaves asyncronously
this._navState = this._navState || this.state.nav;
const lastNavState = this._navState;
const reducedState = Component.router.getStateForAction(
action,
lastNavState
);
const navState = reducedState === null ? lastNavState : reducedState;
const dispatchActionEvents = () => {
this._actionEventSubscribers.forEach((subscriber) =>
subscriber({
type: 'action',
action,
state: navState,
lastState: lastNavState,
})
);
};
if (reducedState === null) {
// The router will return null when action has been handled and the state hasn't changed.
// dispatch returns true when something has been handled.
dispatchActionEvents();
return true;
}
if (navState !== lastNavState) {
// Cache updates to state.nav during the tick to ensure that subsequent calls will not discard this change
this._navState = navState;
this.setState({ nav: navState }, () => {
this._onNavigationStateChange(lastNavState, navState, action);
dispatchActionEvents();
});
return true;
}
dispatchActionEvents();
return false;
};
render() {
let navigation = this.props.navigation;
const navState = this.state.nav;
if (!navState) {
return null;
}
if (!this._navigation || this._navigation.state !== navState) {
this._navigation = getNavigation(
Component.router,
navState,
this.dispatch,
this._actionEventSubscribers,
this._getScreenProps,
() => this._navigation
);
}
navigation = this._navigation;
return (
<NavigationProvider value={navigation}>
<Component {...this.props} navigation={navigation} />
</NavigationProvider>
);
}
}
return NavigationContainer;
}

View File

@@ -0,0 +1,241 @@
import * as React from 'react';
import { render, act } from 'react-native-testing-library';
import { navigate } from '../NavigationActions';
import TabRouter from '../routers/TabRouter';
import createNavigator from '../navigators/createNavigator';
import createNavigationContainer from '../__fixtures__/createNavigationContainer';
it('fires focus and blur events in root navigator', async () => {
function createTestNavigator(routeConfigMap, config = {}) {
const router = TabRouter(routeConfigMap, config);
return createNavigator(
({ descriptors, navigation }) =>
navigation.state.routes.map((route) => {
const Comp = descriptors[route.key].getComponent();
return (
<Comp
key={route.key}
navigation={descriptors[route.key].navigation}
/>
);
}),
router,
config
);
}
const firstFocusCallback = jest.fn();
const firstBlurCallback = jest.fn();
const secondFocusCallback = jest.fn();
const secondBlurCallback = jest.fn();
const thirdFocusCallback = jest.fn();
const thirdBlurCallback = jest.fn();
const fourthFocusCallback = jest.fn();
const fourthBlurCallback = jest.fn();
const createComponent = (focusCallback, blurCallback) =>
class TestComponent extends React.Component {
componentDidMount() {
const { navigation } = this.props;
this.focusSub = navigation.addListener('willFocus', focusCallback);
this.blurSub = navigation.addListener('willBlur', blurCallback);
}
componentWillUnmount() {
this.focusSub?.remove();
this.blurSub?.remove();
}
render() {
return null;
}
};
const navigation = React.createRef();
const Navigator = createNavigationContainer(
createTestNavigator({
first: createComponent(firstFocusCallback, firstBlurCallback),
second: createComponent(secondFocusCallback, secondBlurCallback),
third: createComponent(thirdFocusCallback, thirdBlurCallback),
fourth: createComponent(fourthFocusCallback, fourthBlurCallback),
})
);
const element = <Navigator ref={navigation} />;
render(element);
expect(firstFocusCallback).toBeCalledTimes(1);
expect(firstBlurCallback).toBeCalledTimes(0);
expect(secondFocusCallback).toBeCalledTimes(0);
expect(secondBlurCallback).toBeCalledTimes(0);
expect(thirdFocusCallback).toBeCalledTimes(0);
expect(thirdBlurCallback).toBeCalledTimes(0);
expect(fourthFocusCallback).toBeCalledTimes(0);
expect(fourthBlurCallback).toBeCalledTimes(0);
act(() => {
navigation.current.dispatch(navigate({ routeName: 'second' }));
});
expect(firstBlurCallback).toBeCalledTimes(1);
expect(secondFocusCallback).toBeCalledTimes(1);
act(() => {
navigation.current.dispatch(navigate({ routeName: 'fourth' }));
});
expect(firstFocusCallback).toBeCalledTimes(1);
expect(firstBlurCallback).toBeCalledTimes(1);
expect(secondFocusCallback).toBeCalledTimes(1);
expect(secondBlurCallback).toBeCalledTimes(1);
expect(thirdFocusCallback).toBeCalledTimes(0);
expect(thirdBlurCallback).toBeCalledTimes(0);
expect(fourthFocusCallback).toBeCalledTimes(1);
expect(fourthBlurCallback).toBeCalledTimes(0);
});
it('fires focus and blur events in nested navigator', () => {
function createTestNavigator(routeConfigMap, config = {}) {
const router = TabRouter(routeConfigMap, config);
return createNavigator(
({ descriptors, navigation }) =>
navigation.state.routes.map((route) => {
const Comp = descriptors[route.key].getComponent();
return (
<Comp
key={route.key}
navigation={descriptors[route.key].navigation}
/>
);
}),
router,
config
);
}
const firstFocusCallback = jest.fn();
const firstBlurCallback = jest.fn();
const secondFocusCallback = jest.fn();
const secondBlurCallback = jest.fn();
const thirdFocusCallback = jest.fn();
const thirdBlurCallback = jest.fn();
const fourthFocusCallback = jest.fn();
const fourthBlurCallback = jest.fn();
const createComponent = (focusCallback, blurCallback) =>
class TestComponent extends React.Component {
componentDidMount() {
const { navigation } = this.props;
this.focusSub = navigation.addListener('willFocus', focusCallback);
this.blurSub = navigation.addListener('willBlur', blurCallback);
}
componentWillUnmount() {
this.focusSub?.remove();
this.blurSub?.remove();
}
render() {
return null;
}
};
const Navigator = createNavigationContainer(
createTestNavigator({
first: createComponent(firstFocusCallback, firstBlurCallback),
second: createComponent(secondFocusCallback, secondBlurCallback),
nested: createTestNavigator({
third: createComponent(thirdFocusCallback, thirdBlurCallback),
fourth: createComponent(fourthFocusCallback, fourthBlurCallback),
}),
})
);
const navigation = React.createRef();
const element = <Navigator ref={navigation} />;
render(element);
expect(thirdFocusCallback).toBeCalledTimes(0);
expect(firstFocusCallback).toBeCalledTimes(1);
act(() => {
navigation.current.dispatch(navigate({ routeName: 'nested' }));
});
expect(firstFocusCallback).toBeCalledTimes(1);
expect(fourthFocusCallback).toBeCalledTimes(0);
expect(thirdFocusCallback).toBeCalledTimes(1);
act(() => {
navigation.current.dispatch(navigate({ routeName: 'second' }));
});
expect(thirdFocusCallback).toBeCalledTimes(1);
expect(secondFocusCallback).toBeCalledTimes(1);
expect(fourthBlurCallback).toBeCalledTimes(0);
act(() => {
navigation.current.dispatch(navigate({ routeName: 'nested' }));
});
expect(firstBlurCallback).toBeCalledTimes(1);
expect(secondBlurCallback).toBeCalledTimes(1);
expect(thirdFocusCallback).toBeCalledTimes(2);
expect(fourthFocusCallback).toBeCalledTimes(0);
act(() => {
navigation.current.dispatch(navigate({ routeName: 'third' }));
});
expect(fourthBlurCallback).toBeCalledTimes(0);
expect(thirdFocusCallback).toBeCalledTimes(2);
act(() => {
navigation.current.dispatch(navigate({ routeName: 'first' }));
});
expect(firstFocusCallback).toBeCalledTimes(2);
expect(thirdBlurCallback).toBeCalledTimes(2);
act(() => {
navigation.current.dispatch(navigate({ routeName: 'fourth' }));
});
expect(fourthFocusCallback).toBeCalledTimes(1);
expect(thirdBlurCallback).toBeCalledTimes(2);
expect(firstBlurCallback).toBeCalledTimes(2);
act(() => {
navigation.current.dispatch(navigate({ routeName: 'third' }));
});
expect(thirdFocusCallback).toBeCalledTimes(3);
expect(fourthBlurCallback).toBeCalledTimes(1);
// Make sure nothing else has changed
expect(firstFocusCallback).toBeCalledTimes(2);
expect(firstBlurCallback).toBeCalledTimes(2);
expect(secondFocusCallback).toBeCalledTimes(1);
expect(secondBlurCallback).toBeCalledTimes(1);
expect(thirdFocusCallback).toBeCalledTimes(3);
expect(thirdBlurCallback).toBeCalledTimes(2);
expect(fourthFocusCallback).toBeCalledTimes(1);
expect(fourthBlurCallback).toBeCalledTimes(1);
});

View File

@@ -1,543 +0,0 @@
import getChildEventSubscriber from '../getChildEventSubscriber';
it('child action events only flow when focused', () => {
const parentSubscriber = jest.fn();
const emitParentAction = payload => {
parentSubscriber.mock.calls.forEach(subs => {
if (subs[0] === payload.type) {
subs[1](payload);
}
});
};
const subscriptionRemove = () => {};
parentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
.addListener;
const testState = {
key: 'foo',
routeName: 'FooRoute',
routes: [{ key: 'key0' }, { key: 'key1' }],
index: 0,
isTransitioning: false,
};
const focusedTestState = {
...testState,
index: 1,
};
const childActionHandler = jest.fn();
const childWillFocusHandler = jest.fn();
const childDidFocusHandler = jest.fn();
childEventSubscriber('action', childActionHandler);
childEventSubscriber('willFocus', childWillFocusHandler);
childEventSubscriber('didFocus', childDidFocusHandler);
emitParentAction({
type: 'action',
state: focusedTestState,
lastState: testState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
emitParentAction({
type: 'action',
state: focusedTestState,
lastState: focusedTestState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(1);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
});
it('grandchildren subscription', () => {
const grandParentSubscriber = jest.fn();
const emitGrandParentAction = payload => {
grandParentSubscriber.mock.calls.forEach(subs => {
if (subs[0] === payload.type) {
subs[1](payload);
}
});
};
const subscriptionRemove = () => {};
grandParentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
const parentSubscriber = getChildEventSubscriber(
grandParentSubscriber,
'parent'
).addListener;
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
.addListener;
const parentBlurState = {
key: 'foo',
routeName: 'FooRoute',
routes: [
{ key: 'aunt' },
{
key: 'parent',
routes: [{ key: 'key0' }, { key: 'key1' }],
index: 1,
isTransitioning: false,
},
],
index: 0,
isTransitioning: false,
};
const parentTransitionState = {
...parentBlurState,
index: 1,
isTransitioning: true,
};
const parentFocusState = {
...parentTransitionState,
isTransitioning: false,
};
const childActionHandler = jest.fn();
const childWillFocusHandler = jest.fn();
const childDidFocusHandler = jest.fn();
childEventSubscriber('action', childActionHandler);
childEventSubscriber('willFocus', childWillFocusHandler);
childEventSubscriber('didFocus', childDidFocusHandler);
emitGrandParentAction({
type: 'action',
state: parentTransitionState,
lastState: parentBlurState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(0);
emitGrandParentAction({
type: 'action',
state: parentFocusState,
lastState: parentTransitionState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
});
it('grandchildren transitions', () => {
const grandParentSubscriber = jest.fn();
const emitGrandParentAction = payload => {
grandParentSubscriber.mock.calls.forEach(subs => {
if (subs[0] === payload.type) {
subs[1](payload);
}
});
};
const subscriptionRemove = () => {};
grandParentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
const parentSubscriber = getChildEventSubscriber(
grandParentSubscriber,
'parent'
).addListener;
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
.addListener;
const makeFakeState = (childIndex, childIsTransitioning) => ({
index: 1,
isTransitioning: false,
routes: [
{ key: 'nothing' },
{
key: 'parent',
index: childIndex,
isTransitioning: childIsTransitioning,
routes: [{ key: 'key0' }, { key: 'key1' }, { key: 'key2' }],
},
],
});
const blurredState = makeFakeState(0, false);
const transitionState = makeFakeState(1, true);
const focusState = makeFakeState(1, false);
const transition2State = makeFakeState(2, true);
const blurred2State = makeFakeState(2, false);
const childActionHandler = jest.fn();
const childWillFocusHandler = jest.fn();
const childDidFocusHandler = jest.fn();
const childWillBlurHandler = jest.fn();
const childDidBlurHandler = jest.fn();
childEventSubscriber('action', childActionHandler);
childEventSubscriber('willFocus', childWillFocusHandler);
childEventSubscriber('didFocus', childDidFocusHandler);
childEventSubscriber('willBlur', childWillBlurHandler);
childEventSubscriber('didBlur', childDidBlurHandler);
emitGrandParentAction({
type: 'action',
state: transitionState,
lastState: blurredState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(0);
emitGrandParentAction({
type: 'action',
state: focusState,
lastState: transitionState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
emitGrandParentAction({
type: 'action',
state: focusState,
lastState: focusState,
action: { type: 'TestAction' },
});
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
expect(childActionHandler.mock.calls.length).toBe(1);
emitGrandParentAction({
type: 'action',
state: transition2State,
lastState: focusState,
action: { type: 'CauseWillBlurAction' },
});
expect(childWillBlurHandler.mock.calls.length).toBe(1);
expect(childDidBlurHandler.mock.calls.length).toBe(0);
expect(childActionHandler.mock.calls.length).toBe(1);
emitGrandParentAction({
type: 'action',
state: blurred2State,
lastState: transition2State,
action: { type: 'CauseDidBlurAction' },
});
expect(childWillBlurHandler.mock.calls.length).toBe(1);
expect(childDidBlurHandler.mock.calls.length).toBe(1);
expect(childActionHandler.mock.calls.length).toBe(1);
});
it('grandchildren pass through transitions', () => {
const grandParentSubscriber = jest.fn();
const emitGrandParentAction = payload => {
grandParentSubscriber.mock.calls.forEach(subs => {
if (subs[0] === payload.type) {
subs[1](payload);
}
});
};
const subscriptionRemove = () => {};
grandParentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
const parentSubscriber = getChildEventSubscriber(
grandParentSubscriber,
'parent'
).addListener;
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
.addListener;
const makeFakeState = (childIndex, childIsTransitioning) => ({
index: childIndex,
isTransitioning: childIsTransitioning,
routes: [
{ key: 'nothing' },
{
key: 'parent',
index: 1,
isTransitioning: false,
routes: [{ key: 'key0' }, { key: 'key1' }, { key: 'key2' }],
},
].slice(0, childIndex + 1),
});
const blurredState = makeFakeState(0, false);
const transitionState = makeFakeState(1, true);
const focusState = makeFakeState(1, false);
const transition2State = makeFakeState(0, true);
const blurred2State = makeFakeState(0, false);
const childActionHandler = jest.fn();
const childWillFocusHandler = jest.fn();
const childDidFocusHandler = jest.fn();
const childWillBlurHandler = jest.fn();
const childDidBlurHandler = jest.fn();
childEventSubscriber('action', childActionHandler);
childEventSubscriber('willFocus', childWillFocusHandler);
childEventSubscriber('didFocus', childDidFocusHandler);
childEventSubscriber('willBlur', childWillBlurHandler);
childEventSubscriber('didBlur', childDidBlurHandler);
emitGrandParentAction({
type: 'action',
state: transitionState,
lastState: blurredState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(0);
emitGrandParentAction({
type: 'action',
state: focusState,
lastState: transitionState,
action: { type: 'FooAction' },
});
expect(childActionHandler.mock.calls.length).toBe(0);
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
emitGrandParentAction({
type: 'action',
state: focusState,
lastState: focusState,
action: { type: 'TestAction' },
});
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
expect(childActionHandler.mock.calls.length).toBe(1);
emitGrandParentAction({
type: 'action',
state: transition2State,
lastState: focusState,
action: { type: 'CauseWillBlurAction' },
});
expect(childWillBlurHandler.mock.calls.length).toBe(1);
expect(childDidBlurHandler.mock.calls.length).toBe(0);
expect(childActionHandler.mock.calls.length).toBe(1);
emitGrandParentAction({
type: 'action',
state: blurred2State,
lastState: transition2State,
action: { type: 'CauseDidBlurAction' },
});
expect(childWillBlurHandler.mock.calls.length).toBe(1);
expect(childDidBlurHandler.mock.calls.length).toBe(1);
expect(childActionHandler.mock.calls.length).toBe(1);
});
it('child focus with transition', () => {
const parentSubscriber = jest.fn();
const emitParentAction = payload => {
parentSubscriber.mock.calls.forEach(subs => {
if (subs[0] === payload.type) {
subs[1](payload);
}
});
};
const subscriptionRemove = () => {};
parentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
.addListener;
const randomAction = { type: 'FooAction' };
const testState = {
key: 'foo',
routeName: 'FooRoute',
routes: [{ key: 'key0' }, { key: 'key1' }],
index: 0,
isTransitioning: false,
};
const childWillFocusHandler = jest.fn();
const childDidFocusHandler = jest.fn();
const childWillBlurHandler = jest.fn();
const childDidBlurHandler = jest.fn();
childEventSubscriber('willFocus', childWillFocusHandler);
childEventSubscriber('didFocus', childDidFocusHandler);
childEventSubscriber('willBlur', childWillBlurHandler);
childEventSubscriber('didBlur', childDidBlurHandler);
emitParentAction({
type: 'didFocus',
action: randomAction,
lastState: testState,
state: testState,
});
emitParentAction({
type: 'action',
action: randomAction,
lastState: testState,
state: {
...testState,
index: 1,
isTransitioning: true,
},
});
expect(childWillFocusHandler.mock.calls.length).toBe(1);
emitParentAction({
type: 'action',
action: randomAction,
lastState: {
...testState,
index: 1,
isTransitioning: true,
},
state: {
...testState,
index: 1,
isTransitioning: false,
},
});
expect(childDidFocusHandler.mock.calls.length).toBe(1);
emitParentAction({
type: 'action',
action: randomAction,
lastState: {
...testState,
index: 1,
isTransitioning: false,
},
state: {
...testState,
index: 0,
isTransitioning: true,
},
});
expect(childWillBlurHandler.mock.calls.length).toBe(1);
emitParentAction({
type: 'action',
action: randomAction,
lastState: {
...testState,
index: 0,
isTransitioning: true,
},
state: {
...testState,
index: 0,
isTransitioning: false,
},
});
expect(childDidBlurHandler.mock.calls.length).toBe(1);
});
it('child focus with immediate transition', () => {
const parentSubscriber = jest.fn();
const emitParentAction = payload => {
parentSubscriber.mock.calls.forEach(subs => {
if (subs[0] === payload.type) {
subs[1](payload);
}
});
};
const subscriptionRemove = () => {};
parentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
.addListener;
const randomAction = { type: 'FooAction' };
const testState = {
key: 'foo',
routeName: 'FooRoute',
routes: [{ key: 'key0' }, { key: 'key1' }],
index: 0,
isTransitioning: false,
};
const childWillFocusHandler = jest.fn();
const childDidFocusHandler = jest.fn();
const childWillBlurHandler = jest.fn();
const childDidBlurHandler = jest.fn();
childEventSubscriber('willFocus', childWillFocusHandler);
childEventSubscriber('didFocus', childDidFocusHandler);
childEventSubscriber('willBlur', childWillBlurHandler);
childEventSubscriber('didBlur', childDidBlurHandler);
emitParentAction({
type: 'didFocus',
action: randomAction,
lastState: testState,
state: testState,
});
emitParentAction({
type: 'action',
action: randomAction,
lastState: testState,
state: {
...testState,
index: 1,
},
});
expect(childWillFocusHandler.mock.calls.length).toBe(1);
expect(childDidFocusHandler.mock.calls.length).toBe(1);
emitParentAction({
type: 'action',
action: randomAction,
lastState: {
...testState,
index: 1,
},
state: {
...testState,
index: 0,
},
});
expect(childWillBlurHandler.mock.calls.length).toBe(1);
expect(childDidBlurHandler.mock.calls.length).toBe(1);
});
const setupEventTest = (subscriptionKey, initialLastFocusEvent) => {
const parentSubscriber = jest.fn();
const emitEvent = payload => {
parentSubscriber.mock.calls.forEach(subs => {
if (subs[0] === payload.type) {
subs[1](payload);
}
});
};
const subscriptionRemove = () => {};
parentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
const evtProvider = getChildEventSubscriber(
parentSubscriber,
subscriptionKey,
initialLastFocusEvent
);
const handlers = {};
evtProvider.addListener('action', (handlers.action = jest.fn()));
evtProvider.addListener('willFocus', (handlers.willFocus = jest.fn()));
evtProvider.addListener('didFocus', (handlers.didFocus = jest.fn()));
evtProvider.addListener('willBlur', (handlers.willBlur = jest.fn()));
evtProvider.addListener('didBlur', (handlers.didBlur = jest.fn()));
return { emitEvent, handlers, evtProvider };
};
it('immediate back with uncompleted transition will focus first screen again', () => {
const { handlers, emitEvent } = setupEventTest('key0', 'didFocus');
emitEvent({
type: 'action',
state: {
index: 1,
routes: [{ key: 'key0' }, { key: 'key1' }],
isTransitioning: true,
},
lastState: {
index: 0,
routes: [{ key: 'key0' }],
isTransitioning: false,
},
action: { type: 'Any action, does not matter here' },
});
expect(handlers.willFocus.mock.calls.length).toBe(0);
expect(handlers.didFocus.mock.calls.length).toBe(0);
expect(handlers.willBlur.mock.calls.length).toBe(1);
expect(handlers.didBlur.mock.calls.length).toBe(0);
emitEvent({
type: 'action',
state: {
index: 0,
routes: [{ key: 'key0' }],
isTransitioning: true,
},
lastState: {
index: 1,
routes: [{ key: 'key0' }, { key: 'key1' }],
isTransitioning: true,
},
action: { type: 'Any action, does not matter here' },
});
expect(handlers.willFocus.mock.calls.length).toBe(1);
expect(handlers.didFocus.mock.calls.length).toBe(0);
expect(handlers.willBlur.mock.calls.length).toBe(1);
expect(handlers.didBlur.mock.calls.length).toBe(0);
emitEvent({
type: 'action',
state: {
index: 0,
routes: [{ key: 'key0' }],
isTransitioning: false,
},
lastState: {
index: 0,
routes: [{ key: 'key0' }],
isTransitioning: true,
},
action: { type: 'Any action, does not matter here' },
});
expect(handlers.willFocus.mock.calls.length).toBe(1);
expect(handlers.didFocus.mock.calls.length).toBe(1);
expect(handlers.willBlur.mock.calls.length).toBe(1);
expect(handlers.didBlur.mock.calls.length).toBe(0);
});

View File

@@ -30,7 +30,7 @@ it('getNavigation provides default action helpers', () => {
it.skip('getNavigation provides router action helpers', () => {
const router = {
getActionCreators: () => ({
foo: bar => ({ type: 'FooBarAction', bar }),
foo: (bar) => ({ type: 'FooBarAction', bar }),
}),
getStateForAction(action, lastState = {}) {
return lastState;

View File

@@ -1,196 +0,0 @@
/*
* This is used to extract one children's worth of events from a stream of navigation action events
*
* Based on the 'action' events that get fired for this navigation state, this utility will fire
* focus and blur events for this child
*/
export default function getChildEventSubscriber(
addListener,
key,
initialLastFocusEvent = 'didBlur'
) {
const actionSubscribers = new Set();
const willFocusSubscribers = new Set();
const didFocusSubscribers = new Set();
const willBlurSubscribers = new Set();
const didBlurSubscribers = new Set();
const refocusSubscribers = new Set();
const removeAll = () => {
[
actionSubscribers,
willFocusSubscribers,
didFocusSubscribers,
willBlurSubscribers,
didBlurSubscribers,
refocusSubscribers,
].forEach(set => set.clear());
upstreamSubscribers.forEach(subs => subs && subs.remove());
};
const getChildSubscribers = evtName => {
switch (evtName) {
case 'action':
return actionSubscribers;
case 'willFocus':
return willFocusSubscribers;
case 'didFocus':
return didFocusSubscribers;
case 'willBlur':
return willBlurSubscribers;
case 'didBlur':
return didBlurSubscribers;
case 'refocus':
return refocusSubscribers;
default:
return null;
}
};
const emit = (type, payload) => {
const payloadWithType = { ...payload, type };
const subscribers = getChildSubscribers(type);
subscribers &&
subscribers.forEach(subs => {
subs(payloadWithType);
});
};
// lastFocusEvent keeps track of focus state for one route. First we assume
// we are blurred. If we are focused on initialization, the first 'action'
// event will cause onFocus+willFocus events because we had previously been
// considered blurred
let lastFocusEvent = initialLastFocusEvent;
const upstreamEvents = [
'willFocus',
'didFocus',
'willBlur',
'didBlur',
'refocus',
'action',
];
const upstreamSubscribers = upstreamEvents.map(eventName =>
addListener(eventName, payload => {
if (eventName === 'refocus') {
emit(eventName, payload);
return;
}
const { state, lastState, action } = payload;
const lastRoutes = lastState && lastState.routes;
const routes = state && state.routes;
// const lastFocusKey =
// lastState && lastState.routes && lastState.routes[lastState.index].key;
const focusKey = routes && routes[state.index].key;
const isChildFocused = focusKey === key;
const lastRoute =
lastRoutes && lastRoutes.find(route => route.key === key);
const newRoute = routes && routes.find(route => route.key === key);
const childPayload = {
context: `${key}:${action.type}_${payload.context || 'Root'}`,
state: newRoute,
lastState: lastRoute,
action,
type: eventName,
};
const isTransitioning = !!state && state.isTransitioning;
const previouslylastFocusEvent = lastFocusEvent;
if (lastFocusEvent === 'didBlur') {
// The child is currently blurred. Look for willFocus conditions
if (eventName === 'willFocus' && isChildFocused) {
emit((lastFocusEvent = 'willFocus'), childPayload);
} else if (eventName === 'action' && isChildFocused) {
emit((lastFocusEvent = 'willFocus'), childPayload);
}
}
if (lastFocusEvent === 'willFocus') {
// We are currently mid-focus. Look for didFocus conditions.
// If state.isTransitioning is false, this child event happens immediately after willFocus
if (eventName === 'didFocus' && isChildFocused && !isTransitioning) {
emit((lastFocusEvent = 'didFocus'), childPayload);
} else if (
eventName === 'action' &&
isChildFocused &&
!isTransitioning
) {
emit((lastFocusEvent = 'didFocus'), childPayload);
}
}
if (lastFocusEvent === 'didFocus') {
// The child is currently focused. Look for blurring events
if (!isChildFocused) {
// The child is no longer focused within this navigation state
emit((lastFocusEvent = 'willBlur'), childPayload);
} else if (eventName === 'willBlur') {
// The parent is getting a willBlur event
emit((lastFocusEvent = 'willBlur'), childPayload);
} else if (
eventName === 'action' &&
previouslylastFocusEvent === 'didFocus'
) {
// While focused, pass action events to children for grandchildren focus
emit('action', childPayload);
}
}
if (lastFocusEvent === 'willBlur') {
// The child is mid-blur. Wait for transition to end
if (eventName === 'action' && !isChildFocused && !isTransitioning) {
// The child is done blurring because transitioning is over, or isTransitioning
// never began and didBlur fires immediately after willBlur
emit((lastFocusEvent = 'didBlur'), childPayload);
} else if (eventName === 'didBlur') {
// Pass through the parent didBlur event if it happens
emit((lastFocusEvent = 'didBlur'), childPayload);
} else if (
eventName === 'action' &&
isChildFocused &&
!isTransitioning
) {
emit((lastFocusEvent = 'didFocus'), childPayload);
} else if (
eventName === 'action' &&
isChildFocused &&
isTransitioning
) {
emit((lastFocusEvent = 'willFocus'), childPayload);
}
}
if (lastFocusEvent === 'didBlur' && !newRoute) {
removeAll();
}
})
);
return {
addListener(eventName, eventHandler) {
const subscribers = getChildSubscribers(eventName);
if (!subscribers) {
throw new Error(`Invalid event name "${eventName}"`);
}
subscribers.add(eventHandler);
const remove = () => {
subscribers.delete(eventHandler);
};
return { remove };
},
emit(eventName, payload) {
if (eventName !== 'refocus') {
console.error(
`navigation.emit only supports the 'refocus' event currently.`
);
return;
}
emit(eventName, payload);
},
};
}

View File

@@ -1,9 +1,9 @@
import getChildEventSubscriber from './getChildEventSubscriber';
import getChildRouter from './getChildRouter';
import getNavigationActionCreators from './routers/getNavigationActionCreators';
import getChildrenNavigationCache from './getChildrenNavigationCache';
import getEventManager from './getEventManager';
const createParamGetter = route => (paramName, defaultValue) => {
const createParamGetter = (route) => (paramName, defaultValue) => {
const params = route.params;
if (params && paramName in params) {
@@ -15,7 +15,7 @@ const createParamGetter = route => (paramName, defaultValue) => {
function getChildNavigation(navigation, childKey, getCurrentParentNavigation) {
const children = getChildrenNavigationCache(navigation);
const childRoute = navigation.state.routes.find(r => r.key === childKey);
const childRoute = navigation.state.routes.find((r) => r.key === childKey);
if (!childRoute) {
return null;
@@ -47,7 +47,7 @@ function getChildNavigation(navigation, childKey, getCurrentParentNavigation) {
};
const actionHelpers = {};
Object.keys(actionCreators).forEach(actionName => {
Object.keys(actionCreators).forEach((actionName) => {
actionHelpers[actionName] = (...args) => {
const actionCreator = actionCreators[actionName];
const action = actionCreator(...args);
@@ -78,10 +78,7 @@ function getChildNavigation(navigation, childKey, getCurrentParentNavigation) {
};
return children[childKey];
} else {
const childSubscriber = getChildEventSubscriber(
navigation.addListener,
childKey
);
const { addListener, emit } = getEventManager(childKey);
children[childKey] = {
...actionHelpers,
@@ -91,7 +88,7 @@ function getChildNavigation(navigation, childKey, getCurrentParentNavigation) {
actions: actionCreators,
getParam: createParamGetter(childRoute),
getChildNavigation: grandChildKey =>
getChildNavigation: (grandChildKey) =>
getChildNavigation(children[childKey], grandChildKey, () => {
const nav = getCurrentParentNavigation();
return nav && nav.getChildNavigation(childKey);
@@ -115,9 +112,10 @@ function getChildNavigation(navigation, childKey, getCurrentParentNavigation) {
dispatch: navigation.dispatch,
getScreenProps: navigation.getScreenProps,
dangerouslyGetParent: getCurrentParentNavigation,
addListener: childSubscriber.addListener,
emit: childSubscriber.emit,
addListener,
emit,
};
return children[childKey];
}
}

View File

@@ -5,8 +5,8 @@ export default function getChildrenNavigationCache(navigation) {
let childrenNavigationCache =
navigation._childrenNavigation || (navigation._childrenNavigation = {});
let childKeys = navigation.state.routes.map(route => route.key);
Object.keys(childrenNavigationCache).forEach(cacheKey => {
let childKeys = navigation.state.routes.map((route) => route.key);
Object.keys(childrenNavigationCache).forEach((cacheKey) => {
if (!childKeys.includes(cacheKey) && !navigation.state.isTransitioning) {
delete childrenNavigationCache[cacheKey];
}

View File

@@ -0,0 +1,61 @@
// @ts-check
/**
* @param {string} target
*/
export default function getEventManager(target) {
/**
* @type {Record<string, Record<string, ((e: any) => void)[]>>}
*/
const listeners = {};
/**
* @param {string} type
* @param {() => void} callback
*/
const removeListener = (type, callback) => {
const callbacks = listeners[type] ? listeners[type][target] : undefined;
if (!callbacks) {
return;
}
const index = callbacks.indexOf(callback);
callbacks.splice(index, 1);
};
/**
* @param {string} type
* @param {() => void} callback
*/
const addListener = (type, callback) => {
listeners[type] = listeners[type] || {};
listeners[type][target] = listeners[type][target] || [];
listeners[type][target].push(callback);
return {
remove: () => removeListener(type, callback),
};
};
return {
addListener,
/**
* @param {string} type
* @param {any} [data]
*/
emit: (type, data) => {
const items = listeners[type] || {};
/**
* Copy the current list of callbacks in case they are mutated during execution
* @type {((data: any) => void)[] | undefined}
*/
const callbacks = items[target] && items[target].slice();
callbacks?.forEach((cb) => cb(data));
},
};
}

View File

@@ -18,9 +18,9 @@ export default function getNavigation(
state,
dispatch,
getScreenProps,
getChildNavigation: childKey =>
getChildNavigation: (childKey) =>
getChildNavigation(navigation, childKey, getCurrentNavigation),
isFocused: childKey => {
isFocused: (childKey) => {
const { routes, index } = getCurrentNavigation().state;
if (childKey == null || routes[index].key === childKey) {
return true;
@@ -48,7 +48,7 @@ export default function getNavigation(
...actions,
};
Object.keys(actionCreators).forEach(actionName => {
Object.keys(actionCreators).forEach((actionName) => {
navigation[actionName] = (...args) =>
navigation.dispatch(actionCreators[actionName](...args));
});

View File

@@ -4,8 +4,8 @@ import { render, fireEvent } from 'react-native-testing-library';
import { createAppContainer } from '@react-navigation/native';
import createSwitchNavigator from '../createSwitchNavigator';
const getActiveRouteName = state => state.routes[state.index].routeName;
const createScreen = (routeName, nextRouteName) => props => (
const getActiveRouteName = (state) => state.routes[state.index].routeName;
const createScreen = (routeName, nextRouteName) => (props) => (
<View>
<Text testID="title">{routeName}</Text>
<Text testID={routeName + '-isFirstRoute'}>

View File

@@ -1,6 +1,7 @@
import React from 'react';
import * as React from 'react';
import invariant from '../utils/invariant';
import ThemeContext from '../views/ThemeContext';
import NavigationFocusEvents from '../views/NavigationFocusEvents';
function createNavigator(NavigatorView, router, navigationConfig) {
class Navigator extends React.Component {
@@ -78,13 +79,21 @@ function createNavigator(NavigatorView, router, navigationConfig) {
render() {
return (
<NavigatorView
{...this.props}
screenProps={this.state.screenProps}
navigation={this.props.navigation}
navigationConfig={navigationConfig}
descriptors={this.state.descriptors}
/>
<React.Fragment>
<NavigationFocusEvents
navigation={this.props.navigation}
onEvent={(target, type, data) => {
this.state.descriptors[target]?.navigation.emit(type, data);
}}
/>
<NavigatorView
{...this.props}
screenProps={this.state.screenProps}
navigation={this.props.navigation}
navigationConfig={navigationConfig}
descriptors={this.state.descriptors}
/>
</React.Fragment>
);
}
}

View File

@@ -5,33 +5,33 @@ export const RESET = 'Navigation/RESET';
export const REPLACE = 'Navigation/REPLACE';
export const COMPLETE_TRANSITION = 'Navigation/COMPLETE_TRANSITION';
export const pop = payload => ({
export const pop = (payload) => ({
type: POP,
...payload,
});
export const popToTop = payload => ({
export const popToTop = (payload) => ({
type: POP_TO_TOP,
...payload,
});
export const push = payload => ({
export const push = (payload) => ({
type: PUSH,
...payload,
});
export const reset = payload => ({
export const reset = (payload) => ({
type: RESET,
key: null,
...payload,
});
export const replace = payload => ({
export const replace = (payload) => ({
type: REPLACE,
...payload,
});
export const completeTransition = payload => ({
export const completeTransition = (payload) => ({
type: COMPLETE_TRANSITION,
preserveFocus: true,
...payload,

View File

@@ -29,7 +29,7 @@ export default (routeConfigs, stackConfig = {}) => {
const routeNames = Object.keys(routeConfigs);
// Loop through routes and find child routers
routeNames.forEach(routeName => {
routeNames.forEach((routeName) => {
// We're not using `getScreenForRouteName` here to preserve the lazy loading
// behaviour of routes. This means that routes with child routers must be
// defined using a component directly or with an object with a screen prop.
@@ -152,7 +152,7 @@ export default (routeConfigs, stackConfig = {}) => {
n,
...params,
}),
popToTop: params => StackActions.popToTop(params),
popToTop: (params) => StackActions.popToTop(params),
push: (routeName, params, action) =>
StackActions.push({
routeName,
@@ -280,7 +280,7 @@ export default (routeConfigs, stackConfig = {}) => {
// Before pushing a new route we first try to find one in the existing route stack
// More information on this: https://github.com/react-navigation/rfcs/blob/master/text/0004-less-pushy-navigate.md
const lastRouteIndex = state.routes.findIndex(r => {
const lastRouteIndex = state.routes.findIndex((r) => {
if (action.key) {
return r.key === action.key;
} else {
@@ -425,7 +425,7 @@ export default (routeConfigs, stackConfig = {}) => {
if (action.key === undefined && state.routes.length) {
routeIndex = state.routes.length - 1;
} else {
routeIndex = state.routes.findIndex(r => r.key === action.key);
routeIndex = state.routes.findIndex((r) => r.key === action.key);
}
// Only replace if the key matches one of our routes
@@ -467,7 +467,7 @@ export default (routeConfigs, stackConfig = {}) => {
if (action.type === NavigationActions.SET_PARAMS) {
const key = action.key;
const lastRoute = state.routes.find(route => route.key === key);
const lastRoute = state.routes.find((route) => route.key === key);
if (lastRoute) {
const params = {
...lastRoute.params,
@@ -496,7 +496,7 @@ export default (routeConfigs, stackConfig = {}) => {
return {
...state,
routes: newStackActions.map(newStackAction => {
routes: newStackActions.map((newStackAction) => {
const router = childRouters[newStackAction.routeName];
let childState = {};
@@ -535,19 +535,22 @@ export default (routeConfigs, stackConfig = {}) => {
const { key, n, immediate, prune } = action;
if (action.type === StackActions.POP && prune === false && key) {
const index = state.routes.findIndex(r => r.key === key);
const count = Math.max(index - (n == null ? 1 : n) + 1, 1);
const routes = state.routes
.slice(0, count)
.concat(state.routes.slice(index + 1));
const index = state.routes.findIndex((r) => r.key === key);
if (routes.length) {
return {
...state,
routes,
index: routes.length - 1,
isTransitioning: immediate !== true,
};
if (index > 0) {
const count = Math.max(index - (n ?? 1) + 1, 1);
const routes = state.routes
.slice(0, count)
.concat(state.routes.slice(index + 1));
if (routes.length) {
return {
...state,
routes,
index: routes.length - 1,
isTransitioning: immediate !== true,
};
}
}
} else {
let backRouteIndex = state.index;
@@ -557,7 +560,7 @@ export default (routeConfigs, stackConfig = {}) => {
// back from state.index, as if it were a normal "BACK" action
backRouteIndex = Math.max(1, state.index - n + 1);
} else if (key) {
const backRoute = state.routes.find(route => route.key === key);
const backRoute = state.routes.find((route) => route.key === key);
backRouteIndex = state.routes.indexOf(backRoute);
}

View File

@@ -30,12 +30,12 @@ export default (routeConfigs, config = {}) => {
if (initialRouteIndex === -1) {
throw new Error(
`Invalid initialRouteName '${initialRouteName}'.` +
`Should be one of ${order.map(n => `"${n}"`).join(', ')}`
`Should be one of ${order.map((n) => `"${n}"`).join(', ')}`
);
}
const childRouters = {};
order.forEach(routeName => {
order.forEach((routeName) => {
childRouters[routeName] = null;
const screen = getScreenForRouteName(routeConfigs, routeName);
if (screen.router) {
@@ -93,7 +93,7 @@ export default (routeConfigs, config = {}) => {
if (action.type === NavigationActions.NAVIGATE) {
nextRouteKeyHistory = [...nextRouteKeyHistory]; // copy
const keyToAdd = nextState.routes[nextState.index].key;
nextRouteKeyHistory = nextRouteKeyHistory.filter(k => k !== keyToAdd); // dedup
nextRouteKeyHistory = nextRouteKeyHistory.filter((k) => k !== keyToAdd); // dedup
nextRouteKeyHistory.push(keyToAdd);
} else if (action.type === NavigationActions.BACK) {
nextRouteKeyHistory = [...nextRouteKeyHistory]; // copy
@@ -128,8 +128,8 @@ export default (routeConfigs, config = {}) => {
const initialState = {
routes,
index: initialRouteIndex,
isTransitioning: false,
};
if (backBehavior === 'history') {
const initialKey = routes[initialRouteIndex].key;
initialState['routeKeyHistory'] = [initialKey];
@@ -156,7 +156,7 @@ export default (routeConfigs, config = {}) => {
// Merge any params from the action into all the child routes
const { params } = action;
if (params) {
state.routes = state.routes.map(route => ({
state.routes = state.routes.map((route) => ({
...route,
params: {
...route.params,
@@ -175,14 +175,14 @@ export default (routeConfigs, config = {}) => {
) {
const { params } = action;
const index = state.routes.findIndex(
route => route.routeName === action.routeName
(route) => route.routeName === action.routeName
);
if (index === -1) {
throw new Error(
`There is no route named '${action.routeName}' in the navigator with the key '${action.key}'.\n` +
`Must be one of: ${state.routes
.map(route => `'${route.routeName}'`)
.map((route) => `'${route.routeName}'`)
.join(',')}`
);
}
@@ -305,7 +305,7 @@ export default (routeConfigs, config = {}) => {
if (action.type === NavigationActions.SET_PARAMS) {
const key = action.key;
const lastRoute = state.routes.find(route => route.key === key);
const lastRoute = state.routes.find((route) => route.key === key);
if (lastRoute) {
const params = {
...lastRoute.params,

View File

@@ -12,7 +12,7 @@ beforeEach(() => {
_TESTING_ONLY_normalize_keys();
});
const performRouterTest = createTestRouter => {
const performRouterTest = (createTestRouter) => {
const ListScreen = () => <div />;
const ProfileNavigator = () => <div />;
@@ -584,7 +584,7 @@ it('Handles nested switch routers', () => {
expect(action.action.routeName).toEqual('B');
});
const performRouteNameAsPathDisabledTest = createTestRouter => {
const performRouteNameAsPathDisabledTest = (createTestRouter) => {
const BScreen = () => <div />;
const NestedNavigator = () => <div />;
NestedNavigator.router = createTestRouter({

View File

@@ -24,7 +24,7 @@ const dummyEventSubscriber = () => ({
remove: () => {},
});
Object.keys(ROUTERS).forEach(routerName => {
Object.keys(ROUTERS).forEach((routerName) => {
const Router = ROUTERS[routerName];
describe(`General router features - ${routerName}`, () => {
@@ -441,7 +441,7 @@ it('Inner actions are only unpacked if the current tab matches', () => {
routes: [screenApreState],
};
const comparable = state => {
const comparable = (state) => {
let result = {};
if (typeof state.routeName === 'string') {
result = { ...result, routeName: state.routeName };

View File

@@ -26,7 +26,6 @@ describe('TabRouter', () => {
{ key: 'Foo', routeName: 'Foo' },
{ key: 'Bar', routeName: 'Bar' },
],
isTransitioning: false,
};
expect(state).toEqual(expectedState);
const state2 = router.getStateForAction(
@@ -39,7 +38,6 @@ describe('TabRouter', () => {
{ key: 'Foo', routeName: 'Foo' },
{ key: 'Bar', routeName: 'Bar' },
],
isTransitioning: false,
};
expect(state2).toEqual(expectedState2);
expect(router.getComponentForState(expectedState)).toEqual(ScreenA);
@@ -65,7 +63,6 @@ describe('TabRouter', () => {
{ key: 'Foo', routeName: 'Foo' },
{ key: 'Bar', routeName: 'Bar' },
],
isTransitioning: false,
};
expect(state).toEqual(expectedState);
const state2 = router.getStateForAction(
@@ -78,7 +75,6 @@ describe('TabRouter', () => {
{ key: 'Foo', routeName: 'Foo' },
{ key: 'Bar', routeName: 'Bar' },
],
isTransitioning: false,
};
expect(state2).toEqual(expectedState2);
expect(router.getComponentForState(expectedState)).toEqual(ScreenA);
@@ -102,7 +98,6 @@ describe('TabRouter', () => {
{ key: 'Foo', routeName: 'Foo' },
{ key: 'Bar', routeName: 'Bar' },
],
isTransitioning: false,
});
});
@@ -118,7 +113,6 @@ describe('TabRouter', () => {
{ key: 'Foo', routeName: 'Foo' },
{ key: 'Bar', routeName: 'Bar', params: { name: 'Qux' } },
],
isTransitioning: false,
});
});
@@ -230,7 +224,7 @@ describe('TabRouter', () => {
const state = router.getStateForAction(navAction);
expect(state).toEqual({
index: 1,
isTransitioning: false,
routes: [
{
key: 'Foo',
@@ -238,7 +232,7 @@ describe('TabRouter', () => {
},
{
index: 1,
isTransitioning: false,
key: 'Baz',
routeName: 'Baz',
params: { foo: '42' },
@@ -281,14 +275,14 @@ describe('TabRouter', () => {
let state = router.getStateForAction(navAction);
expect(state).toEqual({
index: 1,
isTransitioning: false,
routes: [
{ key: 'Foo', routeName: 'Foo' },
{
index: 0,
key: 'Baz',
routeName: 'Baz',
isTransitioning: false,
routes: [
{ key: 'Boo', routeName: 'Boo' },
{ key: 'Bar', routeName: 'Bar' },
@@ -308,7 +302,7 @@ describe('TabRouter', () => {
);
expect(state && state.routes[1]).toEqual({
index: 0,
isTransitioning: false,
key: 'Baz',
routeName: 'Baz',
routes: [
@@ -335,14 +329,14 @@ describe('TabRouter', () => {
});
expect(state).toEqual({
index: 1,
isTransitioning: false,
routes: [
{ key: 'Foo', routeName: 'Foo' },
{
index: 1,
key: 'Baz',
routeName: 'Baz',
isTransitioning: false,
routes: [
{ key: 'Foo', routeName: 'Foo' },
{ key: 'Bar', routeName: 'Bar' },
@@ -357,14 +351,14 @@ describe('TabRouter', () => {
);
expect(state2).toEqual({
index: 1,
isTransitioning: false,
routes: [
{ key: 'Foo', routeName: 'Foo' },
{
index: 0,
key: 'Baz',
routeName: 'Baz',
isTransitioning: false,
routes: [
{ key: 'Foo', routeName: 'Foo' },
{ key: 'Bar', routeName: 'Bar' },
@@ -403,19 +397,19 @@ describe('TabRouter', () => {
const state = router.getStateForAction(INIT_ACTION);
expect(state).toEqual({
index: 0,
isTransitioning: false,
routes: [
{
index: 0,
key: 'Foo',
routeName: 'Foo',
isTransitioning: false,
routes: [
{
index: 0,
key: 'Fee',
routeName: 'Fee',
isTransitioning: false,
routes: [
{ key: 'Boo', routeName: 'Boo' },
{ key: 'Baz', routeName: 'Baz' },
@@ -425,7 +419,7 @@ describe('TabRouter', () => {
index: 0,
key: 'Bar',
routeName: 'Bar',
isTransitioning: false,
routes: [
{ key: 'Zoo', routeName: 'Zoo' },
{ key: 'Zap', routeName: 'Zap' },
@@ -442,19 +436,19 @@ describe('TabRouter', () => {
);
expect(state2).toEqual({
index: 0,
isTransitioning: false,
routes: [
{
index: 1,
key: 'Foo',
routeName: 'Foo',
isTransitioning: false,
routes: [
{
index: 0,
key: 'Fee',
routeName: 'Fee',
isTransitioning: false,
routes: [
{ key: 'Boo', routeName: 'Boo' },
{ key: 'Baz', routeName: 'Baz' },
@@ -464,7 +458,7 @@ describe('TabRouter', () => {
index: 1,
key: 'Bar',
routeName: 'Bar',
isTransitioning: false,
routes: [
{ key: 'Zoo', routeName: 'Zoo' },
{ key: 'Zap', routeName: 'Zap' },
@@ -494,19 +488,19 @@ describe('TabRouter', () => {
});
expect(state4).toEqual({
index: 0,
isTransitioning: false,
routes: [
{
index: 1,
key: 'Foo',
routeName: 'Foo',
isTransitioning: false,
routes: [
{
index: 0,
key: 'Fee',
routeName: 'Fee',
isTransitioning: false,
routes: [
{ key: 'Boo', routeName: 'Boo' },
{ key: 'Baz', routeName: 'Baz' },
@@ -516,7 +510,7 @@ describe('TabRouter', () => {
index: 1,
key: 'Bar',
routeName: 'Bar',
isTransitioning: false,
routes: [
{ key: 'Zoo', routeName: 'Zoo' },
{ key: 'Zap', routeName: 'Zap' },
@@ -557,7 +551,7 @@ describe('TabRouter', () => {
const state = router.getStateForAction({ type: NavigationActions.INIT });
const expectedState = {
index: 0,
isTransitioning: false,
routes: [
{ key: 'Foo', routeName: 'Foo' },
{ key: 'Bar', routeName: 'Bar' },
@@ -567,7 +561,7 @@ describe('TabRouter', () => {
const state2 = router.getStateForAction(expectedAction, state);
const expectedState2 = {
index: 1,
isTransitioning: false,
routes: [
{ key: 'Foo', routeName: 'Foo', params: undefined },
{
@@ -628,13 +622,13 @@ describe('TabRouter', () => {
const state = {
index: 0,
isTransitioning: false,
routes: [
{
index: 1,
key: 'Foo',
routeName: 'Foo',
isTransitioning: false,
routes: [
{ key: 'Boo', routeName: 'Boo' },
{ key: 'Baz', routeName: 'Baz' },
@@ -660,7 +654,7 @@ describe('TabRouter', () => {
expect(state0).toEqual({
index: 0,
isTransitioning: false,
routes: [
{ key: 'a', routeName: 'a' },
{ key: 'b', routeName: 'b' },
@@ -676,7 +670,7 @@ describe('TabRouter', () => {
expect(state1).toEqual({
index: 1,
isTransitioning: false,
routes: [
{ key: 'a', routeName: 'a' },
{ key: 'b', routeName: 'b', params },
@@ -765,17 +759,17 @@ describe('TabRouter', () => {
const screenApreState = {
index: 0,
key: 'Foo',
isTransitioning: false,
routeName: 'Foo',
routes: [{ key: 'Bar', routeName: 'Bar' }],
};
const preState = {
index: 0,
isTransitioning: false,
routes: [screenApreState],
};
const comparable = state => {
const comparable = (state) => {
let result = {};
if (typeof state.routeName === 'string') {
result = { ...result, routeName: state.routeName };

View File

@@ -17,7 +17,7 @@ export const getRouterTestHelper = (router, options = defaultOptions) => {
? undefined
: router.getStateForAction({ type: NavigationActions.INIT });
const applyAction = action => {
const applyAction = (action) => {
state = router.getStateForAction(action, state);
};
@@ -35,7 +35,7 @@ export const getRouterTestHelper = (router, options = defaultOptions) => {
...otherActionAttributes,
});
const back = key =>
const back = (key) =>
applyAction({
type: NavigationActions.BACK,
key,

View File

@@ -13,7 +13,7 @@ export default function getScreenForRouteName(routeConfigs, routeName) {
throw new Error(
`There is no route defined for key ${routeName}.\n` +
`Must be one of: ${Object.keys(routeConfigs)
.map(a => `'${a}'`)
.map((a) => `'${a}'`)
.join(',')}`
);
}

View File

@@ -36,7 +36,7 @@ export const getParamsFromPath = (inputParams, pathMatch, pathMatchKeys) => {
return params;
};
const getRestOfPath = (pathMatch, pathMatchKeys) => {
const rest = pathMatch[pathMatchKeys.findIndex(k => k.asterisk) + 1];
const rest = pathMatch[pathMatchKeys.findIndex((k) => k.asterisk) + 1];
return rest;
};
@@ -81,7 +81,7 @@ export const createPathParser = (
let paths = [];
// Build pathsByRouteNames, which includes a regex to match paths for each route. Keep in mind, the regex will pass for the route and all child routes. The code that uses pathsByRouteNames will need to also verify that the child router produces an action, in the case of isPathMatchable false (a null path).
Object.keys(childRouters).forEach(routeName => {
Object.keys(childRouters).forEach((routeName) => {
let pathPattern;
// First check for paths on the router, then check the route config
@@ -189,7 +189,7 @@ export const createPathParser = (
return null;
};
const getPathAndParamsForRoute = route => {
const getPathAndParamsForRoute = (route) => {
const { routeName, params } = route;
const childRouter = childRouters[routeName];
const { toPath, exactReKeys } = pathsByRouteNames[routeName];
@@ -197,8 +197,8 @@ export const createPathParser = (
const nonPathParams = {};
if (params) {
Object.keys(params)
.filter(paramName => !exactReKeys.find(k => k.name === paramName))
.forEach(paramName => {
.filter((paramName) => !exactReKeys.find((k) => k.name === paramName))
.forEach((paramName) => {
nonPathParams[paramName] = params[paramName];
});
}

View File

@@ -13,7 +13,7 @@ function validateRouteConfigMap(routeConfigs) {
'Please specify at least one route when configuring a navigator.'
);
routeNames.forEach(routeName => {
routeNames.forEach((routeName) => {
const routeConfig = routeConfigs[routeName];
const screenComponent = getScreenComponent(routeConfig);

View File

@@ -7,7 +7,7 @@ const deprecatedKeys = ['tabBar'];
export default (screenOptions, route) => {
const keys = Object.keys(screenOptions);
const deprecatedKey = keys.find(key => deprecatedKeys.includes(key));
const deprecatedKey = keys.find((key) => deprecatedKeys.includes(key));
if (typeof screenOptions.title === 'function') {
throw new Error(
@@ -54,7 +54,7 @@ export default (screenOptions, route) => {
'{',
` ${deprecatedKey}: {`,
...Object.keys(screenOptions[deprecatedKey]).map(
key => ` ${key}: ...,`
(key) => ` ${key}: ...,`
),
' },',
'}',
@@ -62,7 +62,7 @@ export default (screenOptions, route) => {
'with:',
'{',
...Object.keys(screenOptions[deprecatedKey]).map(
key =>
(key) =>
` ${deprecatedKey + key[0].toUpperCase() + key.slice(1)}: ...,`
),
'}',

View File

@@ -1,3 +1,3 @@
export default function docsUrl(path: string) {
return `https://reactnavigation.org/docs/${path}`;
return `https://reactnavigation.org/docs/4.x/${path}`;
}

View File

@@ -5,8 +5,10 @@ function getSceneIndicesForInterpolationInputRange(props) {
const isBack = !scenes[lastSceneIndexInScenes].isActive;
if (isBack) {
const currentSceneIndexInScenes = scenes.findIndex(item => item === scene);
const targetSceneIndexInScenes = scenes.findIndex(item => item.isActive);
const currentSceneIndexInScenes = scenes.findIndex(
(item) => item === scene
);
const targetSceneIndexInScenes = scenes.findIndex((item) => item.isActive);
const targetSceneIndex = scenes[targetSceneIndexInScenes].index;
const lastSceneIndex = scenes[lastSceneIndexInScenes].index;

View File

@@ -9,10 +9,10 @@
* will remain to ensure logic does not differ in production.
*/
let validateFormat: (format?: string) => void = function() {};
let validateFormat: (format?: string) => void = function () {};
if (process.env.NODE_ENV !== 'production') {
validateFormat = function(format?: string) {
validateFormat = function (format?: string) {
if (format === undefined) {
throw new Error('invariant requires an error message argument');
}

View File

@@ -17,7 +17,7 @@ class NavigationEvents extends React.Component {
// We register all navigation listeners on mount to ensure listener stability across re-render
// A former implementation was replacing (removing/adding) listeners on all update (if prop provided)
// but there were issues (see https://github.com/react-navigation/react-navigation/issues/5058)
EventNames.forEach(eventName => {
EventNames.forEach((eventName) => {
this.subscriptions[eventName] = this.props.navigation.addListener(
eventName,
(...args) => {
@@ -29,12 +29,12 @@ class NavigationEvents extends React.Component {
}
componentWillUnmount() {
EventNames.forEach(eventName => {
EventNames.forEach((eventName) => {
this.subscriptions[eventName].remove();
});
}
getPropListener = eventName => this.props[EventNameToPropName[eventName]];
getPropListener = (eventName) => this.props[EventNameToPropName[eventName]];
render() {
return null;

View File

@@ -0,0 +1,360 @@
// @ts-check
import * as React from 'react';
/**
* @typedef {object} State
* @prop {number} index
* @prop {({ key: string } & (State | {}))[]} routes
* @prop {boolean} [isTransitioning]
*
* @typedef {object} ParentPayload
* @prop {string} type
* @prop {object} action
* @prop {State} state
* @prop {State | {key: string, routes?: undefined, index?: undefined, isTransitioning?: undefined} | undefined | null} lastState
* @prop {string} [context]
*
* @typedef {object} Payload
* @prop {string} type
* @prop {object} action
* @prop {State | {key: string}} state
* @prop {State | {key: string} | undefined | null} lastState
* @prop {string} [context]
*
* @typedef {object} Props
* @prop {object} navigation
* @prop {Function} navigation.addListener
* @prop {Function} navigation.removeListener
* @prop {() => boolean} navigation.isFocused
* @prop {() => object | undefined} navigation.dangerouslyGetParent
* @prop {State} navigation.state
* @prop {(target: string, type: string, data: any) => void} onEvent
*
* @extends {React.Component<Props>}
*/
export default class NavigationEventManager extends React.Component {
componentDidMount() {
const { navigation } = this.props;
this._actionSubscription = navigation.addListener(
'action',
this._handleAction
);
this._willFocusSubscription = navigation.addListener(
'willFocus',
this._handleWillFocus
);
this._willBlurSubscription = navigation.addListener(
'willBlur',
this._handleWillBlur
);
this._didFocusSubscription = navigation.addListener(
'didFocus',
this._handleDidFocus
);
this._didBlurSubscription = navigation.addListener(
'didBlur',
this._handleDidBlur
);
this._refocusSubscription = navigation.addListener(
'refocus',
this._handleRefocus
);
}
componentWillUnmount() {
this._actionSubscription?.remove();
this._willFocusSubscription?.remove();
this._willBlurSubscription?.remove();
this._didFocusSubscription?.remove();
this._didBlurSubscription?.remove();
this._refocusSubscription?.remove();
}
/**
* @type {{ remove(): void } | undefined}
*/
_actionSubscription;
/**
* @type {{ remove(): void } | undefined}
*/
_willFocusSubscription;
/**
* @type {{ remove(): void } | undefined}
*/
_willBlurSubscription;
/**
* @type {{ remove(): void } | undefined}
*/
_didFocusSubscription;
/**
* @type {{ remove(): void } | undefined}
*/
_didBlurSubscription;
/**
* @type {{ remove(): void } | undefined}
*/
_refocusSubscription;
/**
* @type {string | undefined}
*/
_lastWillBlurKey;
/**
* @type {string | undefined}
*/
_lastWillFocusKey;
/**
* @type {string | undefined}
*/
_lastDidBlurKey;
/**
* @type {string | undefined}
*/
_lastDidFocusKey;
/**
* The 'action' event will fire when navigation state changes.
* Detect if the focused route changed here and emit appropriate events.
*
* @param {ParentPayload} payload
*/
_handleAction = ({ state, lastState, action, type, context }) => {
const { navigation, onEvent } = this.props;
// We should only emit events when the navigator is focused
// When navigator is not focused, screens inside shouldn't receive focused status either
if (!navigation.isFocused()) {
return;
}
const previous = lastState
? lastState.routes?.[lastState.index]
: undefined;
const current = state.routes[state.index];
const payload = {
context: `${current.key}:${action.type}_${context || 'Root'}`,
state: current,
lastState: previous,
action,
type,
};
if (previous?.key !== current.key) {
this._emitWillFocus(current.key, payload);
if (previous?.key) {
this._emitWillBlur(previous.key, payload);
}
}
if (
lastState?.isTransitioning !== state.isTransitioning &&
state.isTransitioning === false
) {
if (this._lastWillBlurKey) {
this._emitDidBlur(this._lastWillBlurKey, payload);
}
if (this._lastWillFocusKey) {
this._emitDidFocus(this._lastWillFocusKey, payload);
}
}
onEvent(current.key, 'action', payload);
};
/**
* @param {ParentPayload} payload
*/
_handleWillFocus = ({ lastState, action, context, type }) => {
const { navigation } = this.props;
const route = navigation.state.routes[navigation.state.index];
this._emitWillFocus(route.key, {
context: `${route.key}:${action.type}_${context || 'Root'}`,
state: route,
lastState: lastState?.routes?.find((r) => r.key === route.key),
action,
type,
});
};
/**
* @param {ParentPayload} payload
*/
_handleWillBlur = ({ lastState, action, context, type }) => {
const { navigation } = this.props;
const route = navigation.state.routes[navigation.state.index];
this._emitWillBlur(route.key, {
context: `${route.key}:${action.type}_${context || 'Root'}`,
state: route,
lastState: lastState?.routes?.find((r) => r.key === route.key),
action,
type,
});
};
/**
* @param {ParentPayload} payload
*/
_handleDidFocus = ({ lastState, action, context, type }) => {
const { navigation } = this.props;
if (this._lastWillFocusKey) {
const route = navigation.state.routes.find(
(r) => r.key === this._lastWillFocusKey
);
if (route) {
this._emitDidFocus(route.key, {
context: `${route.key}:${action.type}_${context || 'Root'}`,
state: route,
lastState: lastState?.routes?.find((r) => r.key === route.key),
action,
type,
});
}
}
};
/**
* @param {ParentPayload} payload
*/
_handleDidBlur = ({ lastState, action, context, type }) => {
const { navigation } = this.props;
if (this._lastWillBlurKey) {
const route = navigation.state.routes.find(
(r) => r.key === this._lastWillBlurKey
);
if (route) {
this._emitDidBlur(route.key, {
context: `${route.key}:${action.type}_${context || 'Root'}`,
state: route,
lastState: lastState?.routes?.find((r) => r.key === route.key),
action,
type,
});
}
}
};
_handleRefocus = () => {
const { onEvent, navigation } = this.props;
const route = navigation.state.routes[navigation.state.index];
onEvent(route.key, 'refocus');
};
/**
* @param {string} target
* @param {Payload} payload
*/
_emitWillFocus = (target, payload) => {
if (this._lastWillBlurKey === target) {
this._lastWillBlurKey = undefined;
}
if (this._lastWillFocusKey === target) {
return;
}
this._lastDidFocusKey = undefined;
this._lastWillFocusKey = target;
const { navigation, onEvent } = this.props;
onEvent(target, 'willFocus', payload);
if (
typeof navigation.state.isTransitioning !== 'boolean' ||
(navigation.state.isTransitioning !== true &&
!navigation.dangerouslyGetParent())
) {
this._emitDidFocus(target, payload);
}
};
/**
* @param {string} target
* @param {Payload} payload
*/
_emitWillBlur = (target, payload) => {
if (this._lastWillFocusKey === target) {
this._lastWillFocusKey = undefined;
}
if (this._lastWillBlurKey === target) {
return;
}
this._lastDidBlurKey = undefined;
this._lastWillBlurKey = target;
const { navigation, onEvent } = this.props;
onEvent(target, 'willBlur', payload);
if (
typeof navigation.state.isTransitioning !== 'boolean' ||
(navigation.state.isTransitioning !== true &&
!navigation.dangerouslyGetParent())
) {
this._emitDidBlur(target, payload);
}
};
/**
* @param {string} target
* @param {Payload} payload
*/
_emitDidFocus = (target, payload) => {
if (this._lastWillFocusKey !== target || this._lastDidFocusKey === target) {
return;
}
this._lastDidFocusKey = target;
const { onEvent } = this.props;
onEvent(target, 'didFocus', payload);
};
/**
* @param {string} target
* @param {Payload} payload
*/
_emitDidBlur = (target, payload) => {
if (this._lastWillBlurKey !== target || this._lastDidBlurKey === target) {
return;
}
this._lastDidBlurKey = target;
const { onEvent } = this.props;
onEvent(target, 'didBlur', payload);
};
render() {
return null;
}
}

View File

@@ -26,13 +26,15 @@ const createTestNavigationAndHelpers = () => {
listeners[eventName].push(handler);
},
remove: (eventName, handler) => {
listeners[eventName] = listeners[eventName].filter(h => h !== handler);
listeners[eventName] = listeners[eventName].filter(
(h) => h !== handler
);
},
get: eventName => {
get: (eventName) => {
return listeners[eventName];
},
call: eventName => {
listeners[eventName].forEach(listener => listener());
call: (eventName) => {
listeners[eventName].forEach((listener) => listener());
},
};
})();

View File

@@ -8,14 +8,15 @@ export default function withNavigation(
config = { forwardRef: true }
) {
class ComponentWithNavigation extends React.Component {
static displayName = `withNavigation(${Component.displayName ||
Component.name})`;
static displayName = `withNavigation(${
Component.displayName || Component.name
})`;
render() {
const navigationProp = this.props.navigation;
return (
<NavigationContext.Consumer>
{navigationContext => {
{(navigationContext) => {
const navigation = navigationProp || navigationContext;
invariant(
!!navigation,

View File

@@ -4,28 +4,29 @@ import withNavigation from './withNavigation';
export default function withNavigationFocus(Component) {
class ComponentWithNavigationFocus extends React.Component {
static displayName = `withNavigationFocus(${Component.displayName ||
Component.name})`;
static displayName = `withNavigationFocus(${
Component.displayName || Component.name
})`;
constructor(props) {
super(props);
state = {
isFocused: this.props.navigation.isFocused(),
};
componentDidMount() {
const { navigation } = this.props;
this.subscriptions = [
props.navigation.addListener('didFocus', () =>
navigation.addListener('willFocus', () =>
this.setState({ isFocused: true })
),
props.navigation.addListener('willBlur', () =>
navigation.addListener('willBlur', () =>
this.setState({ isFocused: false })
),
];
this.state = {
isFocused: props.navigation ? props.navigation.isFocused() : false,
};
}
componentWillUnmount() {
this.subscriptions.forEach(sub => sub.remove());
this.subscriptions?.forEach((sub) => sub.remove());
}
render() {

View File

@@ -0,0 +1,6 @@
{
"extends": "../../tsconfig",
"compilerOptions": {
"allowJs": true
}
}

View File

@@ -3,6 +3,65 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.4.13](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.12...react-navigation-drawer@2.4.13) (2020-04-30)
**Note:** Version bump only for package react-navigation-drawer
## [2.4.12](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.11...react-navigation-drawer@2.4.12) (2020-04-22)
**Note:** Version bump only for package react-navigation-drawer
## [2.4.11](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.10...react-navigation-drawer@2.4.11) (2020-04-02)
**Note:** Version bump only for package react-navigation-drawer
## [2.4.10](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.9...react-navigation-drawer@2.4.10) (2020-03-31)
### Bug Fixes
* remove isTransitioning from SwitchRouter state ([3bec1c9](https://github.com/react-navigation/drawer/commit/3bec1c964a49136c0ead8e8ba8a8c66c556bbcba))
## [2.4.9](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.7...react-navigation-drawer@2.4.9) (2020-03-28)
**Note:** Version bump only for package react-navigation-drawer
## [2.4.8](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.7...react-navigation-drawer@2.4.8) (2020-03-27)
**Note:** Version bump only for package react-navigation-drawer
## [2.4.7](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.6...react-navigation-drawer@2.4.7) (2020-03-22)
**Note:** Version bump only for package react-navigation-drawer
## [2.4.6](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.5...react-navigation-drawer@2.4.6) (2020-03-20)
**Note:** Version bump only for package react-navigation-drawer

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-drawer",
"version": "2.4.6",
"version": "2.4.13",
"description": "Drawer navigator component for React Navigation",
"main": "lib/commonjs/index.js",
"react-native": "lib/module/index.js",
@@ -49,8 +49,8 @@
"react-native-reanimated": "^1.2.0",
"react-native-screens": "^2.3.0",
"react-native-testing-library": "^1.12.0",
"react-navigation": "^4.3.2",
"typescript": "~3.7.5"
"react-navigation": "^4.3.9",
"typescript": "~3.8.3"
},
"peerDependencies": {
"react": "*",

View File

@@ -18,7 +18,7 @@ it('handles basic drawer logic and fires close on switch', () => {
const state = router.getStateForAction(INIT_ACTION);
const expectedState = {
index: 0,
isTransitioning: false,
routes: [
{ key: 'Foo', routeName: 'Foo', params: undefined },
{ key: 'Bar', routeName: 'Bar', params: undefined },
@@ -32,7 +32,7 @@ it('handles basic drawer logic and fires close on switch', () => {
);
const expectedState2 = {
index: 1,
isTransitioning: false,
routes: [
{ key: 'Foo', routeName: 'Foo', params: undefined },
{ key: 'Bar', routeName: 'Bar', params: undefined },
@@ -65,7 +65,7 @@ it('handles initial route navigation', () => {
expect(state).toEqual({
index: 0,
isDrawerOpen: false,
isTransitioning: false,
routes: [
{
key: 'Foo',

View File

@@ -3,6 +3,62 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.2.12](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.11...react-navigation-material-bottom-tabs@2.2.12) (2020-04-30)
**Note:** Version bump only for package react-navigation-material-bottom-tabs
## [2.2.11](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.10...react-navigation-material-bottom-tabs@2.2.11) (2020-04-22)
**Note:** Version bump only for package react-navigation-material-bottom-tabs
## [2.2.10](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.9...react-navigation-material-bottom-tabs@2.2.10) (2020-04-02)
**Note:** Version bump only for package react-navigation-material-bottom-tabs
## [2.2.9](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.8...react-navigation-material-bottom-tabs@2.2.9) (2020-03-31)
**Note:** Version bump only for package react-navigation-material-bottom-tabs
## [2.2.8](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.6...react-navigation-material-bottom-tabs@2.2.8) (2020-03-28)
**Note:** Version bump only for package react-navigation-material-bottom-tabs
## [2.2.7](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.6...react-navigation-material-bottom-tabs@2.2.7) (2020-03-27)
**Note:** Version bump only for package react-navigation-material-bottom-tabs
## [2.2.6](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.5...react-navigation-material-bottom-tabs@2.2.6) (2020-03-22)
**Note:** Version bump only for package react-navigation-material-bottom-tabs
## [2.2.5](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.4...react-navigation-material-bottom-tabs@2.2.5) (2020-03-20)
**Note:** Version bump only for package react-navigation-material-bottom-tabs

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-material-bottom-tabs",
"version": "2.2.5",
"version": "2.2.12",
"description": "Material Bottom Tab Navigation component for React Navigation",
"main": "lib/commonjs/index.js",
"module": "lib/module/index.js",
@@ -48,8 +48,8 @@
"react-native-gesture-handler": "^1.6.0",
"react-native-paper": "^3.1.1",
"react-native-reanimated": "^1.2.0",
"react-navigation": "^4.3.2",
"typescript": "~3.7.5"
"react-navigation": "^4.3.9",
"typescript": "~3.8.3"
},
"peerDependencies": {
"react": "*",

View File

@@ -3,6 +3,68 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.7.13](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.12...@react-navigation/native@3.7.13) (2020-04-30)
**Note:** Version bump only for package @react-navigation/native
## [3.7.12](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.11...@react-navigation/native@3.7.12) (2020-04-22)
**Note:** Version bump only for package @react-navigation/native
## [3.7.11](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.10...@react-navigation/native@3.7.11) (2020-04-02)
**Note:** Version bump only for package @react-navigation/native
## [3.7.10](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.9...@react-navigation/native@3.7.10) (2020-03-31)
**Note:** Version bump only for package @react-navigation/native
## [3.7.9](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.7...@react-navigation/native@3.7.9) (2020-03-28)
### Bug Fixes
* rework focus and blur events to make them more reliable ([cd08338](https://github.com/react-navigation/react-navigation-native/commit/cd083381866506a192f1ec842ac169f2b4277ca5)), closes [#4867](https://github.com/react-navigation/react-navigation-native/issues/4867) [#6187](https://github.com/react-navigation/react-navigation-native/issues/6187) [#6451](https://github.com/react-navigation/react-navigation-native/issues/6451) [#7628](https://github.com/react-navigation/react-navigation-native/issues/7628) [#7749](https://github.com/react-navigation/react-navigation-native/issues/7749)
## [3.7.8](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.7...@react-navigation/native@3.7.8) (2020-03-27)
### Bug Fixes
* rework focus and blur events to make them more reliable ([cd08338](https://github.com/react-navigation/react-navigation-native/commit/cd083381866506a192f1ec842ac169f2b4277ca5)), closes [#4867](https://github.com/react-navigation/react-navigation-native/issues/4867) [#6187](https://github.com/react-navigation/react-navigation-native/issues/6187) [#6451](https://github.com/react-navigation/react-navigation-native/issues/6451) [#7628](https://github.com/react-navigation/react-navigation-native/issues/7628) [#7749](https://github.com/react-navigation/react-navigation-native/issues/7749)
## [3.7.7](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.6...@react-navigation/native@3.7.7) (2020-03-22)
**Note:** Version bump only for package @react-navigation/native
## [3.7.6](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.5...@react-navigation/native@3.7.6) (2020-03-20)

View File

@@ -1,6 +1,6 @@
{
"name": "@react-navigation/native",
"version": "3.7.6",
"version": "3.7.13",
"description": "React Native support for React Navigation",
"main": "lib/commonjs/index.js",
"react-native": "lib/module/index.js",
@@ -15,7 +15,8 @@
},
"sideEffects": false,
"publishConfig": {
"access": "public"
"access": "public",
"tag": "4.x"
},
"keywords": [
"react-native",
@@ -35,11 +36,11 @@
"homepage": "https://github.com/react-navigation/react-navigation-native#readme",
"dependencies": {
"hoist-non-react-statics": "^3.3.2",
"react-native-safe-area-view": "^0.14.8"
"react-native-safe-area-view": "^0.14.9"
},
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"@react-navigation/core": "^3.7.0",
"@react-navigation/core": "^3.7.6",
"@types/react-test-renderer": "^16.9.2",
"del-cli": "^3.0.0",
"react": "~16.9.0",

View File

@@ -52,7 +52,7 @@ const WrappedFlatList = React.forwardRef((props, ref) => (
<FlatList
ref={ref}
{...props}
renderScrollComponent={props => (
renderScrollComponent={(props) => (
<WrappedScrollView {...propsMaybeWithRefreshControl(props)} />
)}
/>
@@ -62,7 +62,7 @@ const WrappedSectionList = React.forwardRef((props, ref) => (
<SectionList
ref={ref}
{...props}
renderScrollComponent={props => (
renderScrollComponent={(props) => (
<WrappedScrollView {...propsMaybeWithRefreshControl(props)} />
)}
/>

View File

@@ -274,7 +274,7 @@ describe('NavigationContainer', () => {
});
// https://github.com/facebook/jest/issues/2157#issuecomment-279171856
const flushPromises = () => new Promise(resolve => setImmediate(resolve));
const flushPromises = () => new Promise((resolve) => setImmediate(resolve));
describe('state persistence', () => {
async function createPersistenceEnabledContainer(

View File

@@ -266,7 +266,7 @@ export default function createNavigationContainer(Component) {
}
const dispatchActions = () =>
this._actionEventSubscribers.forEach(subscriber =>
this._actionEventSubscribers.forEach((subscriber) =>
subscriber({
type: 'action',
action,
@@ -316,7 +316,7 @@ export default function createNavigationContainer(Component) {
}
}
_persistNavigationState = async nav => {
_persistNavigationState = async (nav) => {
const { persistNavigationState } = this.props;
if (persistNavigationState) {
try {
@@ -341,7 +341,7 @@ export default function createNavigationContainer(Component) {
// Per-tick temporary storage for state.nav
dispatch = action => {
dispatch = (action) => {
if (this.props.navigation) {
return this.props.navigation.dispatch(action);
}
@@ -357,7 +357,7 @@ export default function createNavigationContainer(Component) {
const navState = reducedState === null ? lastNavState : reducedState;
const dispatchActionEvents = () => {
this._actionEventSubscribers.forEach(subscriber =>
this._actionEventSubscribers.forEach((subscriber) =>
subscriber({
type: 'action',
action,

View File

@@ -4,9 +4,10 @@ import { withNavigation } from '@react-navigation/core';
export default function createNavigationAwareScrollable(Component) {
const ComponentWithNavigationScrolling = withNavigation(
class extends React.PureComponent<any> {
static displayName = `withNavigationScrolling(${Component.displayName ||
Component.name})`;
class extends React.PureComponent {
static displayName = `withNavigationScrolling(${
Component.displayName || Component.name
})`;
componentDidMount() {
this._subscription = this.props.navigation.addListener(
@@ -49,7 +50,7 @@ export default function createNavigationAwareScrollable(Component) {
render() {
return (
<Component
ref={view => {
ref={(view) => {
this._scrollRef = view;
}}
{...this.props}
@@ -59,11 +60,12 @@ export default function createNavigationAwareScrollable(Component) {
}
);
class NavigationAwareScrollable extends React.PureComponent<any> {
static displayName = `NavigationAwareScrollable(${Component.displayName ||
Component.name})`;
class NavigationAwareScrollable extends React.PureComponent {
static displayName = `NavigationAwareScrollable(${
Component.displayName || Component.name
})`;
_captureRef = view => {
_captureRef = (view) => {
this._innerRef = view;
this.props.onRef && this.props.onRef(view);
};

View File

@@ -1,4 +1,4 @@
export default function() {
export default function () {
throw new Error(
"This version of '@react-navigation/native' is not compatible with React Navigation 5. Make sure you install 5.x version of '@react-navigation/native'."
);

View File

@@ -9,10 +9,10 @@
* will remain to ensure logic does not differ in production.
*/
var validateFormat = function() {};
var validateFormat = function () {};
if (process.env.NODE_ENV !== 'production') {
validateFormat = function(format) {
validateFormat = function (format) {
if (format === undefined) {
throw new Error('invariant requires an error message argument');
}
@@ -32,7 +32,7 @@ export default function invariant(condition, format, a, b, c, d, e, f) {
var args = [a, b, c, d, e, f];
var argIndex = 0;
error = new Error(
format.replace(/%s/g, function() {
format.replace(/%s/g, function () {
return args[argIndex++];
})
);

View File

@@ -4,7 +4,7 @@ import hoistNonReactStatic from 'hoist-non-react-statics';
export const isOrientationLandscape = ({ width, height }) => width > height;
export default function(WrappedComponent) {
export default function (WrappedComponent) {
class withOrientation extends React.Component {
constructor() {
super();

View File

@@ -3,6 +3,65 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [4.3.9](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.8...react-navigation@4.3.9) (2020-04-30)
### Bug Fixes
* change old docUrl to new docUrl due v5 ([e09906a](https://github.com/react-navigation/react-navigation/commit/e09906a4235a0fca09140923ebe7af34b50b491a))
## [4.3.8](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.7...react-navigation@4.3.8) (2020-04-22)
**Note:** Version bump only for package react-navigation
## [4.3.7](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.6...react-navigation@4.3.7) (2020-04-02)
**Note:** Version bump only for package react-navigation
## [4.3.6](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.5...react-navigation@4.3.6) (2020-03-31)
**Note:** Version bump only for package react-navigation
## [4.3.5](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.3...react-navigation@4.3.5) (2020-03-28)
**Note:** Version bump only for package react-navigation
## [4.3.4](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.3...react-navigation@4.3.4) (2020-03-27)
**Note:** Version bump only for package react-navigation
## [4.3.3](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.2...react-navigation@4.3.3) (2020-03-22)
**Note:** Version bump only for package react-navigation
## [4.3.2](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.1...react-navigation@4.3.2) (2020-03-20)
**Note:** Version bump only for package react-navigation

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation",
"version": "4.3.2",
"version": "4.3.9",
"description": "Routing and navigation for your React Native apps",
"main": "src/index.js",
"types": "typescript/react-navigation.d.ts",
@@ -24,8 +24,8 @@
"react-native": "*"
},
"dependencies": {
"@react-navigation/core": "^3.7.0",
"@react-navigation/native": "^3.7.6"
"@react-navigation/core": "^3.7.6",
"@react-navigation/native": "^3.7.13"
},
"devDependencies": {
"@types/react": "^16.9.23",
@@ -34,6 +34,6 @@
"react": "~16.9.0",
"react-native": "~0.61.5",
"react-test-renderer": "^16.9.2",
"typescript": "~3.7.5"
"typescript": "~3.8.3"
}
}

View File

@@ -1,3 +1,3 @@
export default function docsUrl(path) {
return `https://reactnavigation.org/docs/${path}`;
return `https://reactnavigation.org/docs/4.x/${path}`;
}

View File

@@ -3,6 +3,132 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.6.0](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.5.1...react-navigation-stack@2.6.0) (2020-06-06)
### Features
* sync latest stack ([03f9f8c](https://github.com/react-navigation/react-navigation-stack/commit/03f9f8cd8ec3b5fe9dc2ed1ed2024e79dd06f854))
## [2.5.1](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.5.0...react-navigation-stack@2.5.1) (2020-05-15)
### Bug Fixes
* sync latest stack ([bc1313b](https://github.com/react-navigation/react-navigation-stack/commit/bc1313b61da6134255adff477ded2ed8f632bf4a))
# [2.5.0](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.4.0...react-navigation-stack@2.5.0) (2020-05-11)
### Features
* sync latest stack ([bb82d50](https://github.com/react-navigation/react-navigation-stack/commit/bb82d5000f6577f421cc7f72df8064eb4c2a0650))
# [2.4.0](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.13...react-navigation-stack@2.4.0) (2020-04-30)
### Features
* sync latest stack ([8b79e9b](https://github.com/react-navigation/react-navigation-stack/commit/8b79e9bee65c3ba6a788ca76771379319168f117))
## [2.3.13](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.12...react-navigation-stack@2.3.13) (2020-04-22)
**Note:** Version bump only for package react-navigation-stack
## [2.3.12](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.11...react-navigation-stack@2.3.12) (2020-04-17)
### Bug Fixes
* stack peer warnings ([4c4e66f](https://github.com/react-navigation/react-navigation-stack/commit/4c4e66f05cd4d694f512f4cc38827f3fbf0f70de))
## [2.3.11](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.10...react-navigation-stack@2.3.11) (2020-04-02)
**Note:** Version bump only for package react-navigation-stack
## [2.3.10](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.9...react-navigation-stack@2.3.10) (2020-03-31)
**Note:** Version bump only for package react-navigation-stack
## [2.3.9](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.7...react-navigation-stack@2.3.9) (2020-03-28)
### Bug Fixes
* emit didFocus and didBlur events based on parent's transition ([14a6538](https://github.com/react-navigation/react-navigation-stack/commit/14a6538cc8e12c50d5d10722d75c9395a0a281ec))
* rework focus and blur events to make them more reliable ([cd08338](https://github.com/react-navigation/react-navigation-stack/commit/cd083381866506a192f1ec842ac169f2b4277ca5)), closes [#4867](https://github.com/react-navigation/react-navigation-stack/issues/4867) [#6187](https://github.com/react-navigation/react-navigation-stack/issues/6187) [#6451](https://github.com/react-navigation/react-navigation-stack/issues/6451) [#7628](https://github.com/react-navigation/react-navigation-stack/issues/7628) [#7749](https://github.com/react-navigation/react-navigation-stack/issues/7749)
## [2.3.8](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.7...react-navigation-stack@2.3.8) (2020-03-27)
### Bug Fixes
* rework focus and blur events to make them more reliable ([cd08338](https://github.com/react-navigation/react-navigation-stack/commit/cd083381866506a192f1ec842ac169f2b4277ca5)), closes [#4867](https://github.com/react-navigation/react-navigation-stack/issues/4867) [#6187](https://github.com/react-navigation/react-navigation-stack/issues/6187) [#6451](https://github.com/react-navigation/react-navigation-stack/issues/6451) [#7628](https://github.com/react-navigation/react-navigation-stack/issues/7628) [#7749](https://github.com/react-navigation/react-navigation-stack/issues/7749)
## [2.3.7](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.6...react-navigation-stack@2.3.7) (2020-03-27)
**Note:** Version bump only for package react-navigation-stack
## [2.3.6](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.5...react-navigation-stack@2.3.6) (2020-03-23)
**Note:** Version bump only for package react-navigation-stack
## [2.3.5](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.4...react-navigation-stack@2.3.5) (2020-03-22)
**Note:** Version bump only for package react-navigation-stack
## [2.3.4](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.3...react-navigation-stack@2.3.4) (2020-03-20)
**Note:** Version bump only for package react-navigation-stack

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-stack",
"version": "2.3.4",
"version": "2.6.0",
"description": "Stack navigator component for React Navigation",
"main": "lib/commonjs/index.js",
"module": "lib/module/index.js",
@@ -45,7 +45,7 @@
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"@react-native-community/masked-view": "^0.1.7",
"@react-navigation/stack": "^5.2.3",
"@react-navigation/stack": "^5.4.2",
"@types/color": "^3.0.1",
"@types/react": "^16.9.23",
"@types/react-native": "^0.61.22",
@@ -56,16 +56,16 @@
"react-native-gesture-handler": "^1.6.0",
"react-native-safe-area-context": "^0.7.3",
"react-native-screens": "^2.3.0",
"react-navigation": "^4.3.2",
"react-navigation": "^4.3.9",
"react-test-renderer": "~16.9.0",
"typescript": "~3.7.5"
"typescript": "~3.8.3"
},
"peerDependencies": {
"@react-native-community/masked-view": "^0.1.1",
"@react-native-community/masked-view": ">=0.1.0",
"react": "*",
"react-native": "*",
"react-native-gesture-handler": "^1.5.0",
"react-native-safe-area-context": "^0.6.0",
"react-native-gesture-handler": ">= 1.5.0",
"react-native-safe-area-context": ">= 0.6.0",
"react-native-screens": ">=1.0.0 || >= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0",
"react-navigation": "^4.1.1"
},

View File

@@ -1,6 +1,6 @@
diff -Naur ../../node_modules/@react-navigation/stack/src/index.tsx src/vendor/index.tsx
--- ../../node_modules/@react-navigation/stack/src/index.tsx 2020-03-19 20:09:01.000000000 +0100
+++ src/vendor/index.tsx 2020-03-19 20:09:16.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/index.tsx 2020-06-06 02:18:25.000000000 +0200
+++ src/vendor/index.tsx 2020-06-06 02:22:30.000000000 +0200
@@ -3,11 +3,6 @@
import * as TransitionSpecs from './TransitionConfigs/TransitionSpecs';
import * as TransitionPresets from './TransitionConfigs/TransitionPresets';
@@ -16,22 +16,23 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/index.tsx src/vendor/i
@@ -49,9 +44,10 @@
* Types
*/
export {
export type {
- StackNavigationOptions,
- StackNavigationProp,
- StackHeaderProps,
- StackScreenProps,
+ NavigationStackState,
+ StackNavigationProp as NavigationStackProp,
+ StackNavigationOptions as NavigationStackOptions,
+ StackNavigationConfig as NavigationStackConfig,
StackHeaderProps,
StackHeaderLeftButtonProps,
StackHeaderTitleProps,
StackCardInterpolatedStyle,
diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStackNavigator.tsx src/vendor/navigators/createStackNavigator.tsx
--- ../../node_modules/@react-navigation/stack/src/navigators/createStackNavigator.tsx 2020-03-19 20:09:01.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/navigators/createStackNavigator.tsx 2020-06-06 02:18:25.000000000 +0200
+++ src/vendor/navigators/createStackNavigator.tsx 1970-01-01 01:00:00.000000000 +0100
@@ -1,81 +0,0 @@
@@ -1,96 +0,0 @@
-import * as React from 'react';
-import { Platform } from 'react-native';
-import {
- useNavigationBuilder,
- createNavigatorFactory,
@@ -59,6 +60,11 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStack
- screenOptions,
- ...rest
-}: Props) {
- const defaultOptions = {
- gestureEnabled: Platform.OS === 'ios',
- animationEnabled: Platform.OS !== 'web',
- };
-
- const { state, descriptors, navigation } = useNavigationBuilder<
- StackNavigationState,
- StackRouterOptions,
@@ -67,13 +73,22 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStack
- >(StackRouter, {
- initialRouteName,
- children,
- screenOptions,
- screenOptions:
- typeof screenOptions === 'function'
- ? (...args) => ({
- ...defaultOptions,
- ...screenOptions(...args),
- })
- : {
- ...defaultOptions,
- ...screenOptions,
- },
- });
-
- React.useEffect(
- () =>
- navigation.addListener &&
- navigation.addListener('tabPress', e => {
- navigation.addListener('tabPress', (e) => {
- const isFocused = navigation.isFocused();
-
- // Run the operation in the next frame so we're sure all listeners have been run
@@ -113,9 +128,9 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStack
- typeof StackNavigator
->(StackNavigator);
diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/types.tsx
--- ../../node_modules/@react-navigation/stack/src/types.tsx 2020-03-19 20:09:01.000000000 +0100
+++ src/vendor/types.tsx 2020-03-19 20:09:16.000000000 +0100
@@ -8,14 +8,28 @@
--- ../../node_modules/@react-navigation/stack/src/types.tsx 2020-06-06 02:18:25.000000000 +0200
+++ src/vendor/types.tsx 2020-06-06 02:22:30.000000000 +0200
@@ -8,15 +8,28 @@
} from 'react-native';
import { EdgeInsets } from 'react-native-safe-area-context';
import {
@@ -129,6 +144,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
- NavigationHelpers,
- StackNavigationState,
- StackActionHelpers,
- RouteProp,
-} from '@react-navigation/native';
+ NavigationParams,
+ NavigationNavigateAction,
@@ -151,7 +167,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
export type StackNavigationEventMap = {
/**
@@ -28,22 +42,29 @@
@@ -29,29 +42,28 @@
transitionEnd: { data: { closing: boolean } };
};
@@ -172,6 +188,13 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
- StackNavigationEventMap
-> &
- StackActionHelpers<ParamList>;
-
-export type StackScreenProps<
- ParamList extends ParamListBase,
- RouteName extends keyof ParamList = string
-> = {
- navigation: StackNavigationProp<ParamList, RouteName>;
- route: RouteProp<ParamList, RouteName>;
+ State = NavigationRoute,
+ Params = NavigationParams
+> = NavigationScreenProp<State, Params> & {
@@ -192,11 +215,10 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
+ event: NavigationStackEventName,
+ callback: NavigationEventCallback
+ ) => NavigationEventSubscription;
+};
};
export type Layout = { width: number; height: number };
@@ -218,24 +239,27 @@
@@ -227,24 +239,27 @@
/**
* Navigation prop for the header.
*/
@@ -230,7 +252,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
export type StackNavigationOptions = StackHeaderOptions &
Partial<TransitionPreset> & {
/**
@@ -317,6 +341,8 @@
@@ -329,6 +344,8 @@
bottom?: number;
left?: number;
};
@@ -240,8 +262,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
export type StackNavigationConfig = {
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.tsx src/vendor/views/Header/Header.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Header/Header.tsx 2020-03-19 20:09:01.000000000 +0100
+++ src/vendor/views/Header/Header.tsx 2020-03-19 20:09:16.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/views/Header/Header.tsx 2020-06-06 02:18:25.000000000 +0200
+++ src/vendor/views/Header/Header.tsx 2020-06-06 02:22:30.000000000 +0200
@@ -1,12 +1,14 @@
import * as React from 'react';
-import { StackActions } from '@react-navigation/native';
@@ -269,7 +291,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
let leftLabel;
@@ -38,17 +40,12 @@
@@ -38,18 +40,13 @@
? o.headerTitle
: o.title !== undefined
? o.title
@@ -277,6 +299,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
+ : previous.route.routeName;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
const goBack = React.useCallback(
debounce(() => {
- if (navigation.isFocused() && navigation.canGoBack()) {
@@ -289,7 +312,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
}, 50),
[navigation, scene.route.key]
);
@@ -63,7 +60,10 @@
@@ -64,7 +61,10 @@
leftLabel={leftLabel}
headerTitle={
typeof options.headerTitle !== 'function'
@@ -301,7 +324,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
: options.headerTitle
}
onGoBack={previous ? goBack : undefined}
@@ -71,3 +71,18 @@
@@ -72,3 +72,18 @@
/>
);
});
@@ -321,33 +344,34 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
+
+export default Header;
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackButton.tsx src/vendor/views/Header/HeaderBackButton.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackButton.tsx 2020-03-19 20:09:01.000000000 +0100
+++ src/vendor/views/Header/HeaderBackButton.tsx 2020-03-19 20:09:16.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackButton.tsx 2020-06-06 02:18:25.000000000 +0200
+++ src/vendor/views/Header/HeaderBackButton.tsx 2020-06-06 02:22:30.000000000 +0200
@@ -8,9 +8,9 @@
StyleSheet,
LayoutChangeEvent,
} from 'react-native';
-import { useTheme } from '@react-navigation/native';
import MaskedView from '../MaskedView';
import TouchableItem from '../TouchableItem';
import { TouchableItem } from '../TouchableItem';
+import useTheme from '../../../utils/useTheme';
import { StackHeaderLeftButtonProps } from '../../types';
type Props = StackHeaderLeftButtonProps;
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackground.tsx src/vendor/views/Header/HeaderBackground.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackground.tsx 2020-03-19 20:09:01.000000000 +0100
+++ src/vendor/views/Header/HeaderBackground.tsx 2020-03-19 20:09:16.000000000 +0100
@@ -1,6 +1,6 @@
import * as React from 'react';
import { Animated, StyleSheet, Platform, ViewProps } from 'react-native';
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackground.tsx 2020-06-06 02:18:25.000000000 +0200
+++ src/vendor/views/Header/HeaderBackground.tsx 2020-06-06 02:23:41.000000000 +0200
@@ -7,7 +7,7 @@
StyleProp,
ViewStyle,
} from 'react-native';
-import { useTheme } from '@react-navigation/native';
+import useTheme from '../../../utils/useTheme';
type Props = ViewProps & {
children?: React.ReactNode;
style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx src/vendor/views/Header/HeaderContainer.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx 2020-03-19 20:09:01.000000000 +0100
+++ src/vendor/views/Header/HeaderContainer.tsx 2020-03-19 20:09:16.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx 2020-06-06 02:18:25.000000000 +0200
+++ src/vendor/views/Header/HeaderContainer.tsx 2020-06-06 02:22:30.000000000 +0200
@@ -1,11 +1,6 @@
import * as React from 'react';
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
@@ -399,8 +423,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderCon
);
})}
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSegment.tsx src/vendor/views/Header/HeaderSegment.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSegment.tsx 2020-03-19 20:09:01.000000000 +0100
+++ src/vendor/views/Header/HeaderSegment.tsx 2020-03-19 20:09:16.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSegment.tsx 2020-06-06 02:18:25.000000000 +0200
+++ src/vendor/views/Header/HeaderSegment.tsx 2020-06-06 02:22:30.000000000 +0200
@@ -8,7 +8,7 @@
ViewStyle,
} from 'react-native';
@@ -420,20 +444,20 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSeg
};
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderTitle.tsx src/vendor/views/Header/HeaderTitle.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderTitle.tsx 2020-03-19 20:09:01.000000000 +0100
+++ src/vendor/views/Header/HeaderTitle.tsx 2020-03-19 20:09:16.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderTitle.tsx 2020-06-06 02:18:25.000000000 +0200
+++ src/vendor/views/Header/HeaderTitle.tsx 2020-06-06 02:22:30.000000000 +0200
@@ -1,6 +1,6 @@
import * as React from 'react';
import { Animated, StyleSheet, Platform } from 'react-native';
-import { useTheme } from '@react-navigation/native';
+import useTheme from '../../../utils/useTheme';
type Props = React.ComponentProps<typeof Animated.Text> & {
type Props = Omit<React.ComponentProps<typeof Animated.Text>, 'key'> & {
tintColor?: string;
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx src/vendor/views/Stack/Card.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx 2020-03-19 20:09:01.000000000 +0100
+++ src/vendor/views/Stack/Card.tsx 2020-03-19 20:09:16.000000000 +0100
@@ -138,7 +138,7 @@
--- ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx 2020-06-06 02:18:25.000000000 +0200
+++ src/vendor/views/Stack/Card.tsx 2020-06-06 02:22:30.000000000 +0200
@@ -146,7 +146,7 @@
private interactionHandle: number | undefined;
@@ -443,77 +467,34 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx s
private animate = ({
closing,
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardContainer.tsx src/vendor/views/Stack/CardContainer.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardContainer.tsx 2020-03-19 20:09:01.000000000 +0100
+++ src/vendor/views/Stack/CardContainer.tsx 2020-03-19 20:09:16.000000000 +0100
@@ -1,10 +1,16 @@
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardContainer.tsx 2020-06-06 02:18:25.000000000 +0200
+++ src/vendor/views/Stack/CardContainer.tsx 2020-06-06 02:22:30.000000000 +0200
@@ -1,10 +1,11 @@
import * as React from 'react';
import { Animated, View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
-import { Route, useTheme } from '@react-navigation/native';
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
import Card from './Card';
import HeaderHeightContext from '../../utils/HeaderHeightContext';
-import { Scene, Layout, StackHeaderMode, TransitionPreset } from '../../types';
+import useTheme from '../../../utils/useTheme';
+import {
import {
+ Route,
+ Scene,
+ Layout,
+ StackHeaderMode,
+ TransitionPreset,
+} from '../../types';
type Props = TransitionPreset & {
index: number;
@@ -36,6 +42,7 @@
closing: boolean
) => void;
onTransitionEnd?: (props: { route: Route<string> }, closing: boolean) => void;
+ onTransitionComplete: (props: { route: Route<string> }) => void;
onPageChangeStart?: () => void;
onPageChangeConfirm?: () => void;
onPageChangeCancel?: () => void;
@@ -83,6 +90,7 @@
layout,
onCloseRoute,
onOpenRoute,
+ onTransitionComplete,
onPageChangeCancel,
onPageChangeConfirm,
onPageChangeStart,
@@ -152,6 +160,9 @@
};
}, [pointerEvents, scene.progress.next]);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ React.useEffect(() => onTransitionComplete({ route: scene.route }), []);
+
return (
<Card
index={index}
@@ -183,7 +194,7 @@
? { marginTop: headerHeight }
: null
}
- contentStyle={[{ backgroundColor: colors.background }, cardStyle]}
+ contentStyle={[{ backgroundColor: colors.background }, cardStyle] as any}
style={StyleSheet.absoluteFill}
>
<View style={styles.container}>
Scene,
Layout,
StackHeaderMode,
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.tsx src/vendor/views/Stack/CardStack.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.tsx 2020-03-19 20:09:01.000000000 +0100
+++ src/vendor/views/Stack/CardStack.tsx 2020-03-19 20:09:16.000000000 +0100
@@ -9,9 +9,8 @@
ViewProps,
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.tsx 2020-06-06 02:18:25.000000000 +0200
+++ src/vendor/views/Stack/CardStack.tsx 2020-06-06 02:22:30.000000000 +0200
@@ -7,7 +7,7 @@
Platform,
} from 'react-native';
import { EdgeInsets } from 'react-native-safe-area-context';
-// eslint-disable-next-line import/no-unresolved
import { ScreenContainer, Screen, screensEnabled } from 'react-native-screens'; // Import with * as to prevent getters being called
-import { Route, StackNavigationState } from '@react-navigation/native';
+import { NavigationState as StackNavigationState } from 'react-navigation';
import { MaybeScreenContainer, MaybeScreen } from '../Screens';
import { getDefaultHeaderHeight } from '../Header/HeaderSegment';
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
@@ -24,6 +23,7 @@
@@ -21,6 +21,7 @@
import { forNoAnimation as forNoAnimationCard } from '../../TransitionConfigs/CardStyleInterpolators';
import getDistanceForDirection from '../../utils/getDistanceForDirection';
import {
@@ -521,47 +502,26 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.
Layout,
StackHeaderMode,
StackCardMode,
@@ -54,6 +54,7 @@
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
renderScene: (props: { route: Route<string> }) => React.ReactNode;
headerMode: StackHeaderMode;
+ onTransitionComplete: (props: { route: Route<string> }) => void;
onTransitionStart: (
props: { route: Route<string> },
closing: boolean
@@ -383,6 +384,7 @@
renderHeader,
renderScene,
headerMode,
+ onTransitionComplete,
onTransitionStart,
onTransitionEnd,
onPageChangeStart,
@@ -560,6 +562,7 @@
renderScene={renderScene}
onOpenRoute={onOpenRoute}
onCloseRoute={onCloseRoute}
+ onTransitionComplete={onTransitionComplete}
onTransitionStart={onTransitionStart}
onTransitionEnd={onTransitionEnd}
gestureEnabled={index !== 0 && getGesturesEnabled({ route })}
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.tsx src/vendor/views/Stack/StackView.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.tsx 2020-03-19 20:09:01.000000000 +0100
+++ src/vendor/views/Stack/StackView.tsx 2020-03-19 20:09:16.000000000 +0100
@@ -4,9 +4,9 @@
import { GestureHandlerRootView } from 'react-native-gesture-handler';
--- ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.tsx 2020-06-06 02:18:25.000000000 +0200
+++ src/vendor/views/Stack/StackView.tsx 2020-06-06 02:22:30.000000000 +0200
@@ -2,11 +2,11 @@
import { View, Platform, StyleSheet } from 'react-native';
import { SafeAreaConsumer, EdgeInsets } from 'react-native-safe-area-context';
import {
- NavigationHelpersContext,
StackActions,
- StackNavigationState,
- Route,
-} from '@react-navigation/native';
+ NavigationState as StackNavigationState,
+ NavigationActions,
+ SceneView,
+} from 'react-navigation';
import { GestureHandlerRootView } from '../GestureHandler';
import CardStack from './CardStack';
import KeyboardManager from '../KeyboardManager';
@@ -15,6 +15,7 @@
@@ -16,6 +16,7 @@
} from '../Header/HeaderContainer';
import SafeAreaProviderCompat from '../SafeAreaProviderCompat';
import {
@@ -569,7 +529,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.
StackNavigationHelpers,
StackNavigationConfig,
StackDescriptorMap,
@@ -24,6 +25,7 @@
@@ -25,6 +26,7 @@
state: StackNavigationState;
navigation: StackNavigationHelpers;
descriptors: StackDescriptorMap;
@@ -577,7 +537,18 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.
};
type State = {
@@ -290,14 +292,37 @@
@@ -293,7 +295,9 @@
return false;
}
- return gestureEnabled !== false;
+ return gestureEnabled !== undefined
+ ? gestureEnabled
+ : Platform.OS !== 'android';
}
return false;
@@ -321,24 +325,47 @@
return null;
}
@@ -598,25 +569,37 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.
return <HeaderContainer {...props} />;
};
+ private handleTransitionComplete = ({ route }: { route: Route<string> }) => {
+ private handleTransitionComplete = () => {
+ const { state, navigation } = this.props;
+
+ if (state.isTransitioning) {
+ navigation.dispatch(
+ StackActions.completeTransition({
+ key: navigation.state.key,
+ toChildKey: route.key,
+ toChildKey: state.routes[state.index].key,
+ })
+ );
+ }
+ };
+
private handleOpenRoute = ({ route }: { route: Route<string> }) => {
+ this.handleTransitionComplete({ route });
this.setState(state => ({
routes: state.replacingRouteKeys.length
? state.routes.filter(r => !state.replacingRouteKeys.includes(r.key))
@@ -315,12 +340,17 @@
const { state, navigation } = this.props;
+ this.handleTransitionComplete();
+
if (
this.state.replacingRouteKeys.every((key) => key !== route.key) &&
- state.routeNames.includes(route.name) &&
!state.routes.some((r) => r.key === route.key)
) {
// If route isn't present in current state, assume that a close animation was cancelled
// So we need to add this route back to the state
- navigation.navigate(route);
+ navigation.dispatch(NavigationActions.navigate(route));
} else {
this.setState((state) => ({
routes: state.replacingRouteKeys.length
@@ -364,12 +391,11 @@
// If a route exists in state, trigger a pop
// This will happen in when the route was closed from the card component
// e.g. When the close animation triggered from a gesture ends
@@ -628,18 +611,12 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.
+ // @ts-ignore
+ navigation.dispatch(StackActions.pop({ key: route.key, prune: false }));
} else {
+ // While closing route we need to point to the previous one assuming that
+ // this previous one in routes array
+ const index = this.state.routes.findIndex(r => r.key === route.key);
+
+ this.handleTransitionComplete({
+ route: this.state.routes[Math.max(index - 1, 0)],
+ });
+ this.handleTransitionComplete();
+
// We need to clean up any state tracking the route and pop it immediately
this.setState(state => ({
routes: state.routes.filter(r => r.key !== route.key),
@@ -337,22 +367,24 @@
this.setState((state) => ({
routes: state.routes.filter((r) => r.key !== route.key),
@@ -386,26 +412,29 @@
private handleTransitionStart = (
{ route }: { route: Route<string> },
closing: boolean
@@ -676,11 +653,26 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.
render() {
const {
@@ -391,6 +423,7 @@
closingRouteKeys={closingRouteKeys}
onOpenRoute={this.handleOpenRoute}
onCloseRoute={this.handleCloseRoute}
+ onTransitionComplete={this.handleTransitionComplete}
onTransitionStart={this.handleTransitionStart}
onTransitionEnd={this.handleTransitionEnd}
renderHeader={this.renderHeader}
state,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
navigation,
keyboardHandlingEnabled,
mode = 'card',
@@ -423,7 +452,7 @@
mode === 'card' && Platform.OS === 'ios' ? 'float' : 'screen';
return (
- <NavigationHelpersContext.Provider value={navigation}>
+ <>
<GestureHandlerWrapper style={styles.container}>
<SafeAreaProviderCompat>
<SafeAreaConsumer>
@@ -456,7 +485,7 @@
</SafeAreaConsumer>
</SafeAreaProviderCompat>
</GestureHandlerWrapper>
- </NavigationHelpersContext.Provider>
+ </>
);
}
}

View File

@@ -8,7 +8,7 @@ export { default as createStackNavigator } from './navigators/createStackNavigat
/**
* Types
*/
export {
export type {
NavigationStackScreenComponent,
NavigationStackScreenProps,
} from './types';

View File

@@ -8,456 +8,8 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
}
}
>
<View
onLayout={[Function]}
style={
Object {
"flex": 1,
"overflow": "hidden",
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View
accessibilityElementsHidden={false}
closing={false}
gestureVelocityImpact={0.3}
importantForAccessibility="auto"
onClose={[Function]}
onGestureBegin={[Function]}
onGestureCanceled={[Function]}
onOpen={[Function]}
onTransitionStart={[Function]}
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
transitionSpec={
Object {
"close": Object {
"animation": "spring",
"config": Object {
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 0.01,
"restSpeedThreshold": 0.01,
"stiffness": 1000,
},
},
"open": Object {
"animation": "spring",
"config": Object {
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 0.01,
"restSpeedThreshold": 0.01,
"stiffness": 1000,
},
},
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"flex": 1,
"marginTop": 44,
}
}
>
<PanGestureHandler
enabled={false}
hitSlop={
Object {
"right": -700,
}
}
maxDeltaY={20}
minOffsetX={5}
onHandlerStateChange={[Function]}
>
<View
style={
Object {
"flex": 1,
"transform": Array [
Object {
"translateX": 0,
},
Object {
"translateX": 0,
},
],
}
}
>
<View
pointerEvents="none"
style={
Object {
"backgroundColor": "#fff",
"bottom": 0,
"left": 0,
"position": "absolute",
"shadowColor": "#000",
"shadowOffset": Object {
"height": 1,
"width": -1,
},
"shadowOpacity": 0.3,
"shadowRadius": 5,
"top": 0,
"width": 3,
}
}
/>
<View
style={
Array [
Object {
"flex": 1,
"overflow": "hidden",
},
Array [
Object {
"backgroundColor": "rgb(242, 242, 242)",
},
undefined,
],
]
}
>
<View
style={
Object {
"flex": 1,
"flexDirection": "column-reverse",
}
}
>
<View
style={
Object {
"flex": 1,
}
}
>
<View
style={
Object {
"flex": 1,
}
}
>
<View
onLayout={[Function]}
style={
Object {
"flex": 1,
"overflow": "hidden",
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View
accessibilityElementsHidden={false}
closing={false}
gestureVelocityImpact={0.3}
importantForAccessibility="auto"
onClose={[Function]}
onGestureBegin={[Function]}
onGestureCanceled={[Function]}
onOpen={[Function]}
onTransitionStart={[Function]}
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
transitionSpec={
Object {
"close": Object {
"animation": "spring",
"config": Object {
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 0.01,
"restSpeedThreshold": 0.01,
"stiffness": 1000,
},
},
"open": Object {
"animation": "spring",
"config": Object {
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 0.01,
"restSpeedThreshold": 0.01,
"stiffness": 1000,
},
},
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"flex": 1,
"marginTop": 44,
}
}
>
<PanGestureHandler
enabled={false}
hitSlop={
Object {
"right": -700,
}
}
maxDeltaY={20}
minOffsetX={5}
onHandlerStateChange={[Function]}
>
<View
style={
Object {
"flex": 1,
"transform": Array [
Object {
"translateX": 0,
},
Object {
"translateX": 0,
},
],
}
}
>
<View
pointerEvents="none"
style={
Object {
"backgroundColor": "#fff",
"bottom": 0,
"left": 0,
"position": "absolute",
"shadowColor": "#000",
"shadowOffset": Object {
"height": 1,
"width": -1,
},
"shadowOpacity": 0.3,
"shadowRadius": 5,
"top": 0,
"width": 3,
}
}
/>
<View
style={
Array [
Object {
"flex": 1,
"overflow": "hidden",
},
Array [
Object {
"backgroundColor": "rgb(242, 242, 242)",
},
undefined,
],
]
}
>
<View
style={
Object {
"flex": 1,
"flexDirection": "column-reverse",
}
}
>
<View
style={
Object {
"flex": 1,
}
}
/>
</View>
</View>
</View>
</PanGestureHandler>
</View>
</View>
</View>
</View>
<View
pointerEvents="box-none"
style={
Object {
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View
accessibilityElementsHidden={false}
importantForAccessibility="auto"
onLayout={[Function]}
pointerEvents="box-none"
style={null}
>
<View
pointerEvents="none"
style={
Object {
"bottom": 0,
"left": 0,
"opacity": 1,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View
style={
Object {
"backgroundColor": "#fff",
"borderBottomColor": "#a7a7aa",
"flex": 1,
"shadowColor": "#a7a7aa",
"shadowOffset": Object {
"height": 0.5,
"width": 0,
},
"shadowOpacity": 0.85,
"shadowRadius": 0,
}
}
/>
</View>
<View
pointerEvents="box-none"
style={
Object {
"height": 44,
"maxHeight": undefined,
"minHeight": undefined,
"opacity": undefined,
"transform": undefined,
}
}
>
<View
pointerEvents="none"
style={
Object {
"height": 0,
}
}
/>
<View
pointerEvents="box-none"
style={
Object {
"alignItems": "center",
"flex": 1,
"flexDirection": "row",
"justifyContent": "center",
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"marginHorizontal": 18,
"opacity": 1,
}
}
>
<Text
accessibilityRole="header"
numberOfLines={1}
onLayout={[Function]}
style={
Object {
"color": "rgba(0, 0, 0, 0.9)",
"fontSize": 17,
"fontWeight": "600",
}
}
>
Home
</Text>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
</PanGestureHandler>
</View>
</View>
</View>
</View>
<View
pointerEvents="box-none"
style={
Object {
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View
accessibilityElementsHidden={false}
@@ -467,7 +19,7 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
style={null}
>
<View
pointerEvents="none"
pointerEvents="box-none"
style={
Object {
"bottom": 0,
@@ -476,6 +28,7 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
"position": "absolute",
"right": 0,
"top": 0,
"zIndex": 0,
}
}
>
@@ -531,7 +84,7 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
pointerEvents="box-none"
style={
Object {
"marginHorizontal": 18,
"marginHorizontal": 16,
"opacity": 1,
}
}
@@ -555,6 +108,435 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
</View>
</View>
</View>
<View
onLayout={[Function]}
style={
Object {
"flex": 1,
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View
accessibilityElementsHidden={false}
closing={false}
gestureVelocityImpact={0.3}
importantForAccessibility="auto"
onClose={[Function]}
onGestureBegin={[Function]}
onGestureCanceled={[Function]}
onOpen={[Function]}
onTransitionStart={[Function]}
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
transitionSpec={
Object {
"close": Object {
"animation": "spring",
"config": Object {
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 10,
"restSpeedThreshold": 10,
"stiffness": 1000,
},
},
"open": Object {
"animation": "spring",
"config": Object {
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 10,
"restSpeedThreshold": 10,
"stiffness": 1000,
},
},
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"flex": 1,
}
}
>
<PanGestureHandler
enabled={false}
hitSlop={
Object {
"right": -700,
}
}
maxDeltaY={20}
minOffsetX={5}
onHandlerStateChange={[Function]}
>
<View
style={
Object {
"flex": 1,
"transform": Array [
Object {
"translateX": 0,
},
Object {
"translateX": 0,
},
],
}
}
>
<View
pointerEvents="none"
style={
Object {
"backgroundColor": "rgb(242, 242, 242)",
"bottom": 0,
"left": 0,
"position": "absolute",
"shadowColor": "#000",
"shadowOffset": Object {
"height": 1,
"width": -1,
},
"shadowOpacity": 0.3,
"shadowRadius": 5,
"top": 0,
"width": 3,
}
}
/>
<View
style={
Array [
Object {
"flex": 1,
"overflow": "hidden",
},
Array [
Object {
"backgroundColor": "rgb(242, 242, 242)",
},
undefined,
],
]
}
>
<View
style={
Object {
"flex": 1,
"flexDirection": "column-reverse",
}
}
>
<View
style={
Object {
"flex": 1,
}
}
>
<View
style={
Object {
"flex": 1,
}
}
>
<View
pointerEvents="box-none"
>
<View
accessibilityElementsHidden={false}
importantForAccessibility="auto"
onLayout={[Function]}
pointerEvents="box-none"
style={null}
>
<View
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"opacity": 1,
"position": "absolute",
"right": 0,
"top": 0,
"zIndex": 0,
}
}
>
<View
style={
Object {
"backgroundColor": "#fff",
"borderBottomColor": "#a7a7aa",
"flex": 1,
"shadowColor": "#a7a7aa",
"shadowOffset": Object {
"height": 0.5,
"width": 0,
},
"shadowOpacity": 0.85,
"shadowRadius": 0,
}
}
/>
</View>
<View
pointerEvents="box-none"
style={
Object {
"height": 44,
"maxHeight": undefined,
"minHeight": undefined,
"opacity": undefined,
"transform": undefined,
}
}
>
<View
pointerEvents="none"
style={
Object {
"height": 0,
}
}
/>
<View
pointerEvents="box-none"
style={
Object {
"alignItems": "center",
"flex": 1,
"flexDirection": "row",
"justifyContent": "center",
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"marginHorizontal": 16,
"opacity": 1,
}
}
>
<Text
accessibilityRole="header"
numberOfLines={1}
onLayout={[Function]}
style={
Object {
"color": "rgba(0, 0, 0, 0.9)",
"fontSize": 17,
"fontWeight": "600",
}
}
>
Home
</Text>
</View>
</View>
</View>
</View>
</View>
<View
onLayout={[Function]}
style={
Object {
"flex": 1,
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View
accessibilityElementsHidden={false}
closing={false}
gestureVelocityImpact={0.3}
importantForAccessibility="auto"
onClose={[Function]}
onGestureBegin={[Function]}
onGestureCanceled={[Function]}
onOpen={[Function]}
onTransitionStart={[Function]}
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
transitionSpec={
Object {
"close": Object {
"animation": "spring",
"config": Object {
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 10,
"restSpeedThreshold": 10,
"stiffness": 1000,
},
},
"open": Object {
"animation": "spring",
"config": Object {
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 10,
"restSpeedThreshold": 10,
"stiffness": 1000,
},
},
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"flex": 1,
}
}
>
<PanGestureHandler
enabled={false}
hitSlop={
Object {
"right": -700,
}
}
maxDeltaY={20}
minOffsetX={5}
onHandlerStateChange={[Function]}
>
<View
style={
Object {
"flex": 1,
"transform": Array [
Object {
"translateX": 0,
},
Object {
"translateX": 0,
},
],
}
}
>
<View
pointerEvents="none"
style={
Object {
"backgroundColor": "rgb(242, 242, 242)",
"bottom": 0,
"left": 0,
"position": "absolute",
"shadowColor": "#000",
"shadowOffset": Object {
"height": 1,
"width": -1,
},
"shadowOpacity": 0.3,
"shadowRadius": 5,
"top": 0,
"width": 3,
}
}
/>
<View
style={
Array [
Object {
"flex": 1,
"overflow": "hidden",
},
Array [
Object {
"backgroundColor": "rgb(242, 242, 242)",
},
undefined,
],
]
}
>
<View
style={
Object {
"flex": 1,
"flexDirection": "column-reverse",
}
}
>
<View
style={
Object {
"flex": 1,
}
}
/>
</View>
</View>
</View>
</PanGestureHandler>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
</PanGestureHandler>
</View>
</View>
</View>
</View>
</View>
`;

View File

@@ -8,178 +8,8 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
}
}
>
<View
onLayout={[Function]}
style={
Object {
"flex": 1,
"overflow": "hidden",
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View
accessibilityElementsHidden={false}
closing={false}
gestureVelocityImpact={0.3}
importantForAccessibility="auto"
onClose={[Function]}
onGestureBegin={[Function]}
onGestureCanceled={[Function]}
onOpen={[Function]}
onTransitionStart={[Function]}
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
transitionSpec={
Object {
"close": Object {
"animation": "spring",
"config": Object {
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 0.01,
"restSpeedThreshold": 0.01,
"stiffness": 1000,
},
},
"open": Object {
"animation": "spring",
"config": Object {
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 0.01,
"restSpeedThreshold": 0.01,
"stiffness": 1000,
},
},
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"flex": 1,
"marginTop": 44,
}
}
>
<PanGestureHandler
enabled={false}
hitSlop={
Object {
"right": -700,
}
}
maxDeltaY={20}
minOffsetX={5}
onHandlerStateChange={[Function]}
>
<View
style={
Object {
"flex": 1,
"transform": Array [
Object {
"translateX": 0,
},
Object {
"translateX": 0,
},
],
}
}
>
<View
pointerEvents="none"
style={
Object {
"backgroundColor": "#fff",
"bottom": 0,
"left": 0,
"position": "absolute",
"shadowColor": "#000",
"shadowOffset": Object {
"height": 1,
"width": -1,
},
"shadowOpacity": 0.3,
"shadowRadius": 5,
"top": 0,
"width": 3,
}
}
/>
<View
style={
Array [
Object {
"flex": 1,
"overflow": "hidden",
},
Array [
Object {
"backgroundColor": "rgb(242, 242, 242)",
},
undefined,
],
]
}
>
<View
style={
Object {
"flex": 1,
"flexDirection": "column-reverse",
}
}
>
<View
style={
Object {
"flex": 1,
}
}
/>
</View>
</View>
</View>
</PanGestureHandler>
</View>
</View>
</View>
</View>
<View
pointerEvents="box-none"
style={
Object {
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View
accessibilityElementsHidden={false}
@@ -189,7 +19,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
style={null}
>
<View
pointerEvents="none"
pointerEvents="box-none"
style={
Object {
"bottom": 0,
@@ -198,6 +28,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
"position": "absolute",
"right": 0,
"top": 0,
"zIndex": 0,
}
}
>
@@ -254,7 +85,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
pointerEvents="box-none"
style={
Object {
"marginHorizontal": 18,
"marginHorizontal": 16,
"opacity": 1,
}
}
@@ -294,23 +125,11 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
</View>
</View>
</View>
</View>
`;
exports[`StackNavigator renders successfully 1`] = `
<View
style={
Object {
"flex": 1,
}
}
>
<View
onLayout={[Function]}
style={
Object {
"flex": 1,
"overflow": "hidden",
}
}
>
@@ -354,8 +173,8 @@ exports[`StackNavigator renders successfully 1`] = `
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 0.01,
"restSpeedThreshold": 0.01,
"restDisplacementThreshold": 10,
"restSpeedThreshold": 10,
"stiffness": 1000,
},
},
@@ -365,8 +184,8 @@ exports[`StackNavigator renders successfully 1`] = `
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 0.01,
"restSpeedThreshold": 0.01,
"restDisplacementThreshold": 10,
"restSpeedThreshold": 10,
"stiffness": 1000,
},
},
@@ -378,7 +197,6 @@ exports[`StackNavigator renders successfully 1`] = `
style={
Object {
"flex": 1,
"marginTop": 44,
}
}
>
@@ -412,7 +230,7 @@ exports[`StackNavigator renders successfully 1`] = `
pointerEvents="none"
style={
Object {
"backgroundColor": "#fff",
"backgroundColor": "rgb(242, 242, 242)",
"bottom": 0,
"left": 0,
"position": "absolute",
@@ -467,16 +285,19 @@ exports[`StackNavigator renders successfully 1`] = `
</View>
</View>
</View>
</View>
`;
exports[`StackNavigator renders successfully 1`] = `
<View
style={
Object {
"flex": 1,
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View
accessibilityElementsHidden={false}
@@ -486,7 +307,7 @@ exports[`StackNavigator renders successfully 1`] = `
style={null}
>
<View
pointerEvents="none"
pointerEvents="box-none"
style={
Object {
"bottom": 0,
@@ -495,6 +316,7 @@ exports[`StackNavigator renders successfully 1`] = `
"position": "absolute",
"right": 0,
"top": 0,
"zIndex": 0,
}
}
>
@@ -551,7 +373,7 @@ exports[`StackNavigator renders successfully 1`] = `
pointerEvents="box-none"
style={
Object {
"marginHorizontal": 18,
"marginHorizontal": 16,
"opacity": 1,
}
}
@@ -575,5 +397,165 @@ exports[`StackNavigator renders successfully 1`] = `
</View>
</View>
</View>
<View
onLayout={[Function]}
style={
Object {
"flex": 1,
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
>
<View
accessibilityElementsHidden={false}
closing={false}
gestureVelocityImpact={0.3}
importantForAccessibility="auto"
onClose={[Function]}
onGestureBegin={[Function]}
onGestureCanceled={[Function]}
onOpen={[Function]}
onTransitionStart={[Function]}
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
transitionSpec={
Object {
"close": Object {
"animation": "spring",
"config": Object {
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 10,
"restSpeedThreshold": 10,
"stiffness": 1000,
},
},
"open": Object {
"animation": "spring",
"config": Object {
"damping": 500,
"mass": 3,
"overshootClamping": true,
"restDisplacementThreshold": 10,
"restSpeedThreshold": 10,
"stiffness": 1000,
},
},
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"flex": 1,
}
}
>
<PanGestureHandler
enabled={false}
hitSlop={
Object {
"right": -700,
}
}
maxDeltaY={20}
minOffsetX={5}
onHandlerStateChange={[Function]}
>
<View
style={
Object {
"flex": 1,
"transform": Array [
Object {
"translateX": 0,
},
Object {
"translateX": 0,
},
],
}
}
>
<View
pointerEvents="none"
style={
Object {
"backgroundColor": "rgb(242, 242, 242)",
"bottom": 0,
"left": 0,
"position": "absolute",
"shadowColor": "#000",
"shadowOffset": Object {
"height": 1,
"width": -1,
},
"shadowOpacity": 0.3,
"shadowRadius": 5,
"top": 0,
"width": 3,
}
}
/>
<View
style={
Array [
Object {
"flex": 1,
"overflow": "hidden",
},
Array [
Object {
"backgroundColor": "rgb(242, 242, 242)",
},
undefined,
],
]
}
>
<View
style={
Object {
"flex": 1,
"flexDirection": "column-reverse",
}
}
>
<View
style={
Object {
"flex": 1,
}
}
/>
</View>
</View>
</View>
</PanGestureHandler>
</View>
</View>
</View>
</View>
</View>
`;

View File

@@ -30,7 +30,7 @@ function createStackNavigator(
return createNavigator(
// TODO: don't have time to fix it right now
// @ts-ignore
navigatorProps => <StackView {...navigatorProps} />,
(navigatorProps) => <StackView {...navigatorProps} />,
router,
stackConfig
);

View File

@@ -58,7 +58,7 @@ export default function validateDeprecatedConfig(
) {
let result = options;
Object.keys(validations).forEach(name => {
Object.keys(validations).forEach((name) => {
if (name in config) {
const { compat, message } = validations[name];

View File

@@ -12,10 +12,10 @@ const shownWarnings: string[] = [];
const validations: Validation[] = [
{
check: o => typeof o.headerForceInset === 'object',
check: (o) => typeof o.headerForceInset === 'object',
deprecated: 'headerForceInset',
updated: 'safeAreaInsets',
compat: o => {
compat: (o) => {
const { headerForceInset, ...rest } = o;
let safeAreaInsets: Partial<EdgeInsets> | undefined = {
@@ -55,20 +55,20 @@ const validations: Validation[] = [
},
},
{
check: o => o.gesturesEnabled !== undefined,
check: (o) => o.gesturesEnabled !== undefined,
deprecated: 'gesturesEnabled',
updated: 'gestureEnabled',
compat: o => {
compat: (o) => {
const { gesturesEnabled, ...rest } = o;
return { ...rest, gestureEnabled: gesturesEnabled };
},
},
{
check: o => o.header === null,
check: (o) => o.header === null,
deprecated: 'header: null',
updated: 'headerShown: false',
compat: o => {
compat: (o) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { header, ...rest } = o;
@@ -76,26 +76,26 @@ const validations: Validation[] = [
},
},
{
check: o => o.header != null && typeof o.header !== 'function',
check: (o) => o.header != null && typeof o.header !== 'function',
deprecated: 'header: <SomeElement />',
updated: 'header: () => <SomeElement />',
compat: o => ({ ...o, header: () => o.header }),
compat: (o) => ({ ...o, header: () => o.header }),
},
{
check: o =>
check: (o) =>
o.headerTitle !== undefined &&
typeof o.headerTitle !== 'string' &&
typeof o.headerTitle !== 'function',
deprecated: 'headerTitle: <SomeElement />',
updated: 'headerTitle: () => <SomeElement />',
compat: o => ({ ...o, headerTitle: () => o.headerTitle }),
compat: (o) => ({ ...o, headerTitle: () => o.headerTitle }),
},
...['headerLeft', 'headerRight', 'headerBackground', 'headerBackImage'].map(
(p): Validation => ({
check: (o: any) => o[p] !== undefined && typeof o[p] !== 'function',
deprecated: `${p}: <SomeElement />`,
updated: `${p}: () => <SomeElement />`,
compat: o => ({ ...o, [p]: () => o[p] }),
compat: (o) => ({ ...o, [p]: () => o[p] }),
})
),
];
@@ -108,7 +108,7 @@ export default function validateDeprecatedOptions(
const warnings: Validation[] = [];
// Validate options to show warnings for deprecations
validations.forEach(v => {
validations.forEach((v) => {
if (v.check(options)) {
result = v.compat(result);
@@ -127,7 +127,7 @@ export default function validateDeprecatedOptions(
console.warn(
`Deprecation in 'navigationOptions':\n${warnings
.map(
v =>
(v) =>
`- '${v.deprecated}' will be removed in a future version. Use '${v.updated}' instead`
)
.join('\n')}`

View File

@@ -164,6 +164,7 @@ export function forModalPresentationIOS({
borderTopLeftRadius: borderRadius,
borderTopRightRadius: borderRadius,
marginTop: index === 0 ? 0 : statusBarHeight,
marginBottom: index === 0 ? 0 : topOffset,
transform: [{ translateY }, { scale }],
},
overlayStyle: { opacity: overlayOpacity },

View File

@@ -11,8 +11,8 @@ export const TransitionIOSSpec: TransitionSpec = {
damping: 500,
mass: 3,
overshootClamping: true,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 0.01,
restDisplacementThreshold: 10,
restSpeedThreshold: 10,
},
};

View File

@@ -43,11 +43,12 @@ export { default as useGestureHandlerRef } from './utils/useGestureHandlerRef';
/**
* Types
*/
export {
export type {
NavigationStackState,
StackNavigationProp as NavigationStackProp,
StackNavigationOptions as NavigationStackOptions,
StackNavigationConfig as NavigationStackConfig,
StackHeaderProps,
StackHeaderLeftButtonProps,
StackHeaderTitleProps,
StackCardInterpolatedStyle,

View File

@@ -131,7 +131,7 @@ export type StackHeaderOptions = {
* This may lead to white space or overlap between `headerLeft` and `headerTitle` if a customized `headerLeft` is used.
* It can be solved by adjusting `left` and `right` style in `headerTitleContainerStyle` and `marginHorizontal` in `headerTitleStyle`.
*/
headerTitleContainerStyle?: StyleProp<ViewStyle>;
headerTitleContainerStyle?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
/**
* Tint color for the header.
*/
@@ -169,7 +169,7 @@ export type StackHeaderOptions = {
/**
* Style object for the container of the `headerLeft` component, for example to add padding.
*/
headerLeftContainerStyle?: StyleProp<ViewStyle>;
headerLeftContainerStyle?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
/**
* Function which returns a React Element to display on the right side of the header.
*/
@@ -177,7 +177,7 @@ export type StackHeaderOptions = {
/**
* Style object for the container of the `headerRight` component, for example to add padding.
*/
headerRightContainerStyle?: StyleProp<ViewStyle>;
headerRightContainerStyle?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
/**
* Function which returns a React Element to display custom image in header's back button.
* It receives the `tintColor` in in the options object as an argument. object.
@@ -199,7 +199,7 @@ export type StackHeaderOptions = {
/**
* Style object for the header. You can specify a custom background color here, for example.
*/
headerStyle?: StyleProp<ViewStyle>;
headerStyle?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
/**
* Defaults to `false`. If `true`, the header will not have a background unless you explicitly provide it with `headerBackground`.
* The header will also float over the screen so that it overlaps the content underneath.
@@ -300,7 +300,8 @@ export type StackNavigationOptions = StackHeaderOptions &
cardStyle?: StyleProp<ViewStyle>;
/**
* Whether transition animation should be enabled the screen.
* If you set it to `false`, the screen won't animate when pushing or popping. Defaults to `true`.
* If you set it to `false`, the screen won't animate when pushing or popping.
* Defaults to `true` on Android and iOS, `false` on Web.
*/
animationEnabled?: boolean;
/**
@@ -310,10 +311,12 @@ export type StackNavigationOptions = StackHeaderOptions &
animationTypeForReplace?: 'push' | 'pop';
/**
* Whether you can use gestures to dismiss this screen. Defaults to `true` on iOS, `false` on Android.
* Not supported on Web.
*/
gestureEnabled?: boolean;
/**
* Object to override the distance of touch start from the edge of the screen to recognize gestures.
* Not supported on Web.
*/
gestureResponseDistance?: {
/**
@@ -326,8 +329,8 @@ export type StackNavigationOptions = StackHeaderOptions &
horizontal?: number;
};
/**
* Number which determines the relevance of velocity for the gesture.
* Defaults to 0.3.
* Number which determines the relevance of velocity for the gesture. Defaults to 0.3.
* Not supported on Web.
*/
gestureVelocityImpact?: number;
/**
@@ -394,7 +397,7 @@ export type StackHeaderLeftButtonProps = {
/**
* Style object for the label.
*/
labelStyle?: React.ComponentProps<typeof Animated.Text>['style'];
labelStyle?: Animated.WithAnimatedValue<StyleProp<TextStyle>>;
/**
* Whether label font should scale to respect Text Size accessibility settings.
*/

View File

@@ -1,6 +1,5 @@
import * as React from 'react';
import { PanGestureHandler } from 'react-native-gesture-handler';
export default React.createContext<React.Ref<PanGestureHandler> | undefined>(
undefined
);
export default React.createContext<React.Ref<
import('react-native-gesture-handler').PanGestureHandler
> | null>(null);

View File

@@ -4,7 +4,7 @@ export default function debounce<T extends (...args: any[]) => void>(
): T {
let timeout: NodeJS.Timeout | number | undefined;
return function(this: any, ...args) {
return function (this: any, ...args) {
if (!timeout) {
// eslint-disable-next-line babel/no-invalid-this
func.apply(this, args);

View File

@@ -0,0 +1,22 @@
import * as React from 'react';
import {
PanGestureHandler as PanGestureHandlerNative,
PanGestureHandlerProperties,
} from 'react-native-gesture-handler';
import GestureHandlerRefContext from '../utils/GestureHandlerRefContext';
export function PanGestureHandler(props: PanGestureHandlerProperties) {
const gestureRef = React.useRef<PanGestureHandlerNative>(null);
return (
<GestureHandlerRefContext.Provider value={gestureRef}>
<PanGestureHandlerNative {...props} />
</GestureHandlerRefContext.Provider>
);
}
export {
GestureHandlerRootView,
State as GestureState,
PanGestureHandlerGestureEvent,
} from 'react-native-gesture-handler';

View File

@@ -0,0 +1,24 @@
import * as React from 'react';
import { View } from 'react-native';
import type { PanGestureHandlerProperties } from 'react-native-gesture-handler';
const Dummy: any = ({ children }: { children: React.ReactNode }) => (
<>{children}</>
);
export const PanGestureHandler = Dummy as React.ComponentType<
PanGestureHandlerProperties
>;
export const GestureHandlerRootView = View;
export const GestureState = {
UNDETERMINED: 0,
FAILED: 1,
BEGAN: 2,
CANCELLED: 3,
ACTIVE: 4,
END: 5,
};
export type { PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';

View File

@@ -43,6 +43,7 @@ const Header = React.memo(function Header(props: StackHeaderProps) {
: previous.route.routeName;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
const goBack = React.useCallback(
debounce(() => {
navigation.dispatch(StackActions.pop({ key: scene.route.key }));

View File

@@ -9,7 +9,7 @@ import {
LayoutChangeEvent,
} from 'react-native';
import MaskedView from '../MaskedView';
import TouchableItem from '../TouchableItem';
import { TouchableItem } from '../TouchableItem';
import useTheme from '../../../utils/useTheme';
import { StackHeaderLeftButtonProps } from '../../types';

View File

@@ -1,8 +1,16 @@
import * as React from 'react';
import { Animated, StyleSheet, Platform, ViewProps } from 'react-native';
import {
Animated,
StyleSheet,
Platform,
ViewProps,
StyleProp,
ViewStyle,
} from 'react-native';
import useTheme from '../../../utils/useTheme';
type Props = ViewProps & {
style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
children?: React.ReactNode;
};

View File

@@ -118,7 +118,7 @@ export default function HeaderContainer({
<View
onLayout={
onContentHeightChange
? e =>
? (e) =>
onContentHeightChange({
route: scene.route,
height: e.nativeEvent.layout.height,

View File

@@ -38,7 +38,7 @@ type State = {
};
const warnIfHeaderStylesDefined = (styles: Record<string, any>) => {
Object.keys(styles).forEach(styleProp => {
Object.keys(styles).forEach((styleProp) => {
const value = styles[styleProp];
if (styleProp === 'position' && value === 'absolute') {
@@ -307,11 +307,13 @@ export default class HeaderSegment extends React.Component<Props, State> {
})
: null;
const rightButton = right ? right({ tintColor: headerTintColor }) : null;
return (
<React.Fragment>
<Animated.View
pointerEvents="none"
style={[StyleSheet.absoluteFill, backgroundStyle]}
pointerEvents="box-none"
style={[StyleSheet.absoluteFill, { zIndex: 0 }, backgroundStyle]}
>
{headerBackground ? (
headerBackground({ style: safeStyles })
@@ -345,8 +347,19 @@ export default class HeaderSegment extends React.Component<Props, State> {
pointerEvents="box-none"
style={[
headerTitleAlign === 'left'
? { position: 'absolute', left: leftButton ? 72 : 16 }
: { marginHorizontal: 18 },
? {
position: 'absolute',
left: (leftButton ? 72 : 16) + insets.left,
right: (rightButton ? 72 : 16) + insets.right,
}
: {
marginHorizontal:
(leftButton ? 32 : 16) +
(leftButton && headerBackTitleVisible !== false
? 40
: 0) +
Math.max(insets.left, insets.right),
},
titleStyle,
titleContainerStyle,
]}
@@ -359,7 +372,7 @@ export default class HeaderSegment extends React.Component<Props, State> {
style: customTitleStyle,
})}
</Animated.View>
{right ? (
{rightButton ? (
<Animated.View
pointerEvents="box-none"
style={[
@@ -369,7 +382,7 @@ export default class HeaderSegment extends React.Component<Props, State> {
rightContainerStyle,
]}
>
{right({ tintColor: headerTintColor })}
{rightButton}
</Animated.View>
) : null}
</View>

View File

@@ -2,7 +2,7 @@ import * as React from 'react';
import { Animated, StyleSheet, Platform } from 'react-native';
import useTheme from '../../../utils/useTheme';
type Props = React.ComponentProps<typeof Animated.Text> & {
type Props = Omit<React.ComponentProps<typeof Animated.Text>, 'key'> & {
tintColor?: string;
children?: string;
};

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { TextInput } from 'react-native';
import { TextInput, Platform, Keyboard } from 'react-native';
type Props = {
enabled: boolean;
@@ -56,7 +56,9 @@ export default class KeyboardManager extends React.Component<Props> {
const input = this.previouslyFocusedTextInput;
if (input) {
if (Platform.OS === 'android') {
Keyboard.dismiss();
} else if (input) {
TextInput.State.blurTextInput(input);
}

View File

@@ -1,17 +1,26 @@
import * as React from 'react';
import { UIManager } from 'react-native';
import RNCMaskedView from '@react-native-community/masked-view';
type Props = React.ComponentProps<typeof RNCMaskedView> & {
type MaskedViewType = typeof import('@react-native-community/masked-view').default;
type Props = React.ComponentProps<MaskedViewType> & {
children: React.ReactElement;
};
let RNCMaskedView: MaskedViewType | undefined;
try {
RNCMaskedView = require('@react-native-community/masked-view').default;
} catch (e) {
// Ignore
}
const isMaskedViewAvailable =
// @ts-ignore
UIManager.getViewManagerConfig('RNCMaskedView') != null;
export default function MaskedView({ children, ...rest }: Props) {
if (isMaskedViewAvailable) {
if (isMaskedViewAvailable && RNCMaskedView) {
return <RNCMaskedView {...rest}>{children}</RNCMaskedView>;
}

View File

@@ -30,7 +30,7 @@ type Props = {
export default function SafeAreaProviderCompat({ children }: Props) {
return (
<SafeAreaConsumer>
{insets => {
{(insets) => {
if (insets) {
// If we already have insets, don't wrap the stack in another safe area provider
// This avoids an issue with updates at the cost of potentially incorrect values

View File

@@ -0,0 +1,71 @@
import * as React from 'react';
import { Animated, View, Platform, ViewProps } from 'react-native';
let Screens: typeof import('react-native-screens') | undefined;
try {
Screens = require('react-native-screens');
} catch (e) {
// Ignore
}
// The web implementation in react-native-screens seems buggy.
// The view doesn't become visible after coming back in some cases.
// So we use our custom implementation.
class WebScreen extends React.Component<
ViewProps & {
active: number;
children: React.ReactNode;
}
> {
render() {
const { active, style, ...rest } = this.props;
return (
<View
// @ts-ignore
hidden={!active}
style={[style, { display: active ? 'flex' : 'none' }]}
{...rest}
/>
);
}
}
const AnimatedWebScreen = Animated.createAnimatedComponent(WebScreen);
export const MaybeScreenContainer = ({
enabled,
...rest
}: ViewProps & {
enabled: boolean;
children: React.ReactNode;
}) => {
if (enabled && Platform.OS !== 'web' && Screens && Screens.screensEnabled()) {
return <Screens.ScreenContainer {...rest} />;
}
return <View {...rest} />;
};
export const MaybeScreen = ({
enabled,
active,
...rest
}: ViewProps & {
enabled: boolean;
active: number | Animated.AnimatedInterpolation;
children: React.ReactNode;
}) => {
if (enabled && Platform.OS === 'web') {
// @ts-ignore
return <AnimatedWebScreen active={active} {...rest} />;
}
if (enabled && Screens && Screens.screensEnabled()) {
// @ts-ignore
return <Screens.Screen active={active} {...rest} />;
}
return <View {...rest} />;
};

View File

@@ -9,14 +9,15 @@ import {
Platform,
InteractionManager,
} from 'react-native';
import {
PanGestureHandler,
State as GestureState,
PanGestureHandlerGestureEvent,
} from 'react-native-gesture-handler';
import { EdgeInsets } from 'react-native-safe-area-context';
import Color from 'color';
import StackGestureRefContext from '../../utils/GestureHandlerRefContext';
import CardSheet from './CardSheet';
import {
PanGestureHandler,
GestureState,
PanGestureHandlerGestureEvent,
} from '../GestureHandler';
import CardAnimationContext from '../../utils/CardAnimationContext';
import getDistanceForDirection from '../../utils/getDistanceForDirection';
import getInvertedMultiplier from '../../utils/getInvertedMultiplier';
@@ -36,6 +37,7 @@ type Props = ViewProps & {
gesture: Animated.Value;
layout: Layout;
insets: EdgeInsets;
pageOverflowEnabled: boolean;
gestureDirection: GestureDirection;
onOpen: () => void;
onClose: () => void;
@@ -44,7 +46,9 @@ type Props = ViewProps & {
onGestureCanceled?: () => void;
onGestureEnd?: () => void;
children: React.ReactNode;
overlay: (props: { style: StyleProp<ViewStyle> }) => React.ReactNode;
overlay: (props: {
style: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
}) => React.ReactNode;
overlayEnabled: boolean;
shadowEnabled: boolean;
gestureEnabled: boolean;
@@ -81,7 +85,11 @@ export default class Card extends React.Component<Props> {
shadowEnabled: true,
gestureEnabled: true,
gestureVelocityImpact: GESTURE_VELOCITY_IMPACT,
overlay: ({ style }: { style: StyleProp<ViewStyle> }) =>
overlay: ({
style,
}: {
style: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
}) =>
style ? (
<Animated.View pointerEvents="none" style={[styles.overlay, style]} />
) : null,
@@ -246,11 +254,21 @@ export default class Card extends React.Component<Props> {
this.handleStartInteraction();
onGestureBegin?.();
break;
case GestureState.CANCELLED:
case GestureState.CANCELLED: {
this.isSwiping.setValue(FALSE);
this.handleEndInteraction();
const velocity =
gestureDirection === 'vertical' ||
gestureDirection === 'vertical-inverted'
? nativeEvent.velocityY
: nativeEvent.velocityX;
this.animate({ closing: this.props.closing, velocity });
onGestureCanceled?.();
break;
}
case GestureState.END: {
this.isSwiping.setValue(FALSE);
@@ -271,17 +289,12 @@ export default class Card extends React.Component<Props> {
velocity = nativeEvent.velocityX;
}
const gestureDirectionFactor =
gestureDirection === 'horizontal' || gestureDirection === 'vertical'
? 1
: -1;
const closing =
gestureDirectionFactor *
(translation + velocity * gestureVelocityImpact) >
(translation + velocity * gestureVelocityImpact) *
getInvertedMultiplier(gestureDirection) >
distance / 2
? velocity !== 0 || translation !== 0
: false;
: this.props.closing;
this.animate({ closing, velocity });
@@ -407,8 +420,6 @@ export default class Card extends React.Component<Props> {
}
}
private gestureRef = React.createRef<PanGestureHandler>();
private contentRef = React.createRef<View>();
render() {
@@ -425,6 +436,7 @@ export default class Card extends React.Component<Props> {
shadowEnabled,
gestureEnabled,
gestureDirection,
pageOverflowEnabled,
children,
containerStyle: customContainerStyle,
contentStyle,
@@ -485,7 +497,7 @@ export default class Card extends React.Component<Props> {
<CardAnimationContext.Provider value={animationContext}>
<View pointerEvents="box-none" {...rest}>
{overlayEnabled ? (
<View style={StyleSheet.absoluteFill}>
<View pointerEvents="box-none" style={StyleSheet.absoluteFill}>
{overlay({ style: overlayStyle })}
</View>
) : null}
@@ -494,7 +506,6 @@ export default class Card extends React.Component<Props> {
pointerEvents="box-none"
>
<PanGestureHandler
ref={this.gestureRef}
enabled={layout.width !== 0 && gestureEnabled}
onGestureEvent={handleGestureEvent}
onHandlerStateChange={this.handleGestureStateChange}
@@ -512,19 +523,20 @@ export default class Card extends React.Component<Props> {
: gestureDirection === 'vertical'
? [styles.shadowVertical, styles.shadowTop]
: [styles.shadowVertical, styles.shadowBottom],
{ backgroundColor },
shadowStyle,
]}
pointerEvents="none"
/>
) : null}
<View
<CardSheet
ref={this.contentRef}
style={[styles.content, contentStyle]}
enabled={pageOverflowEnabled}
layout={layout}
style={contentStyle}
>
<StackGestureRefContext.Provider value={this.gestureRef}>
{children}
</StackGestureRefContext.Provider>
</View>
{children}
</CardSheet>
</Animated.View>
</PanGestureHandler>
</Animated.View>
@@ -538,17 +550,12 @@ const styles = StyleSheet.create({
container: {
flex: 1,
},
content: {
flex: 1,
overflow: 'hidden',
},
overlay: {
flex: 1,
backgroundColor: '#000',
},
shadow: {
position: 'absolute',
backgroundColor: '#fff',
shadowRadius: 5,
shadowColor: '#000',
shadowOpacity: 0.3,

View File

@@ -9,6 +9,7 @@ import {
Scene,
Layout,
StackHeaderMode,
StackCardMode,
TransitionPreset,
} from '../../types';
@@ -42,7 +43,6 @@ type Props = TransitionPreset & {
closing: boolean
) => void;
onTransitionEnd?: (props: { route: Route<string> }, closing: boolean) => void;
onTransitionComplete: (props: { route: Route<string> }) => void;
onPageChangeStart?: () => void;
onPageChangeConfirm?: () => void;
onPageChangeCancel?: () => void;
@@ -52,9 +52,9 @@ type Props = TransitionPreset & {
horizontal?: number;
};
gestureVelocityImpact?: number;
mode: StackCardMode;
headerMode: StackHeaderMode;
headerShown?: boolean;
headerTransparent?: boolean;
hasAbsoluteHeader: boolean;
headerHeight: number;
onHeaderHeightChange: (props: {
route: Route<string>;
@@ -80,17 +80,16 @@ function CardContainer({
gestureVelocityImpact,
getPreviousRoute,
getFocusedRoute,
mode,
headerMode,
headerShown,
headerStyleInterpolator,
headerTransparent,
hasAbsoluteHeader,
headerHeight,
onHeaderHeightChange,
index,
layout,
onCloseRoute,
onOpenRoute,
onTransitionComplete,
onPageChangeCancel,
onPageChangeConfirm,
onPageChangeStart,
@@ -160,9 +159,6 @@ function CardContainer({
};
}, [pointerEvents, scene.progress.next]);
// eslint-disable-next-line react-hooks/exhaustive-deps
React.useEffect(() => onTransitionComplete({ route: scene.route }), []);
return (
<Card
index={index}
@@ -189,12 +185,9 @@ function CardContainer({
accessibilityElementsHidden={!focused}
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
pointerEvents={active ? 'box-none' : pointerEvents}
containerStyle={
headerMode === 'float' && !headerTransparent && headerShown !== false
? { marginTop: headerHeight }
: null
}
contentStyle={[{ backgroundColor: colors.background }, cardStyle] as any}
pageOverflowEnabled={headerMode === 'screen' && mode === 'card'}
containerStyle={hasAbsoluteHeader ? { marginTop: headerHeight } : null}
contentStyle={[{ backgroundColor: colors.background }, cardStyle]}
style={StyleSheet.absoluteFill}
>
<View style={styles.container}>

View File

@@ -0,0 +1,49 @@
import * as React from 'react';
import { View, ViewProps, StyleSheet } from 'react-native';
type Props = ViewProps & {
enabled: boolean;
layout: { width: number; height: number };
children: React.ReactNode;
};
// This component will render a page which overflows the screen
// if the container fills the body by comparing the size
// This lets the document.body handle scrolling of the content
// It's necessary for mobile browsers to be able to hide address bar on scroll
export default React.forwardRef<View, Props>(function CardSheet(
{ enabled, layout, style, ...rest },
ref
) {
const [fill, setFill] = React.useState(false);
React.useEffect(() => {
if (typeof document === 'undefined' || !document.body) {
// Only run when DOM is available
return;
}
const width = document.body.clientWidth;
const height = document.body.clientHeight;
setFill(width === layout.width && height === layout.height);
}, [layout.height, layout.width]);
return (
<View
{...rest}
ref={ref}
style={[enabled && fill ? styles.page : styles.card, style]}
/>
);
});
const styles = StyleSheet.create({
page: {
minHeight: '100%',
},
card: {
flex: 1,
overflow: 'hidden',
},
});

View File

@@ -1,17 +1,15 @@
import * as React from 'react';
import {
Animated,
View,
StyleSheet,
LayoutChangeEvent,
Dimensions,
Platform,
ViewProps,
} from 'react-native';
import { EdgeInsets } from 'react-native-safe-area-context';
import { ScreenContainer, Screen, screensEnabled } from 'react-native-screens'; // Import with * as to prevent getters being called
import { NavigationState as StackNavigationState } from 'react-navigation';
import { MaybeScreenContainer, MaybeScreen } from '../Screens';
import { getDefaultHeaderHeight } from '../Header/HeaderSegment';
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
import CardContainer from './CardContainer';
@@ -54,7 +52,6 @@ type Props = {
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
renderScene: (props: { route: Route<string> }) => React.ReactNode;
headerMode: StackHeaderMode;
onTransitionComplete: (props: { route: Route<string> }) => void;
onTransitionStart: (
props: { route: Route<string> },
closing: boolean
@@ -76,37 +73,6 @@ type State = {
const EPSILON = 0.01;
const MaybeScreenContainer = ({
enabled,
...rest
}: ViewProps & {
enabled: boolean;
children: React.ReactNode;
}) => {
if (enabled && screensEnabled()) {
return <ScreenContainer {...rest} />;
}
return <View {...rest} />;
};
const MaybeScreen = ({
enabled,
active,
...rest
}: ViewProps & {
enabled: boolean;
active: number | Animated.AnimatedInterpolation;
children: React.ReactNode;
}) => {
if (enabled && screensEnabled()) {
// @ts-ignore
return <Screen active={active} {...rest} />;
}
return <View {...rest} />;
};
const FALLBACK_DESCRIPTOR = Object.freeze({ options: {} });
const getHeaderHeights = (
@@ -118,9 +84,10 @@ const getHeaderHeights = (
) => {
return routes.reduce<Record<string, number>>((acc, curr) => {
const { options = {} } = descriptors[curr.key] || {};
const { height = previous[curr.key] } = StyleSheet.flatten(
options.headerStyle || {}
);
const style: any = StyleSheet.flatten(options.headerStyle || {});
const height =
typeof style.height === 'number' ? style.height : previous[curr.key];
const safeAreaInsets = {
...insets,
@@ -334,7 +301,7 @@ export default class CardStack extends React.Component<Props, State> {
props.insets,
state.descriptors,
layout,
{}
state.headerHeights
),
};
});
@@ -369,6 +336,24 @@ export default class CardStack extends React.Component<Props, State> {
return state.routes[state.index];
};
private doesFloatHeaderNeedAbsolutePositioning = () => {
if (this.props.headerMode !== 'float') {
return false;
}
return this.state.scenes.slice(-2).some((scene) => {
const { descriptor } = scene;
const options = descriptor ? descriptor.options : {};
const { headerTransparent, headerShown } = options;
if (headerTransparent || headerShown === false) {
return true;
}
return false;
});
};
render() {
const {
mode,
@@ -384,7 +369,6 @@ export default class CardStack extends React.Component<Props, State> {
renderHeader,
renderScene,
headerMode,
onTransitionComplete,
onTransitionStart,
onTransitionEnd,
onPageChangeStart,
@@ -398,6 +382,8 @@ export default class CardStack extends React.Component<Props, State> {
const focusedDescriptor = descriptors[focusedRoute.key];
const focusedOptions = focusedDescriptor ? focusedDescriptor.options : {};
const isFloatHeaderAbsolute = this.doesFloatHeaderNeedAbsolutePositioning();
let defaultTransitionPreset =
mode === 'modal' ? ModalTransition : DefaultTransition;
@@ -417,10 +403,38 @@ export default class CardStack extends React.Component<Props, State> {
// Screens is buggy on iOS and web, so we only enable it on Android
// For modals, usually we want the screen underneath to be visible, so also disable it there
const isScreensEnabled = Platform.OS === 'android' && mode !== 'modal';
const isScreensEnabled = Platform.OS !== 'ios' && mode !== 'modal';
let floatingHeader;
if (headerMode === 'float') {
floatingHeader = (
<React.Fragment key="header">
{renderHeader({
mode: 'float',
layout,
insets: { top, right, bottom, left },
scenes,
getPreviousRoute,
getFocusedRoute: this.getFocusedRoute,
onContentHeightChange: this.handleHeaderLayout,
gestureDirection:
focusedOptions.gestureDirection !== undefined
? focusedOptions.gestureDirection
: defaultTransitionPreset.gestureDirection,
styleInterpolator:
focusedOptions.headerStyleInterpolator !== undefined
? focusedOptions.headerStyleInterpolator
: defaultTransitionPreset.headerStyleInterpolator,
style: isFloatHeaderAbsolute ? styles.floating : undefined,
})}
</React.Fragment>
);
}
return (
<React.Fragment>
{isFloatHeaderAbsolute ? null : floatingHeader}
<MaybeScreenContainer
enabled={isScreensEnabled}
style={styles.container}
@@ -555,14 +569,17 @@ export default class CardStack extends React.Component<Props, State> {
onHeaderHeightChange={this.handleHeaderLayout}
getPreviousRoute={getPreviousRoute}
getFocusedRoute={this.getFocusedRoute}
mode={mode}
headerMode={headerMode}
headerShown={headerShown}
headerTransparent={headerTransparent}
hasAbsoluteHeader={
isFloatHeaderAbsolute &&
headerShown !== false &&
!headerTransparent
}
renderHeader={renderHeader}
renderScene={renderScene}
onOpenRoute={onOpenRoute}
onCloseRoute={onCloseRoute}
onTransitionComplete={onTransitionComplete}
onTransitionStart={onTransitionStart}
onTransitionEnd={onTransitionEnd}
gestureEnabled={index !== 0 && getGesturesEnabled({ route })}
@@ -573,26 +590,7 @@ export default class CardStack extends React.Component<Props, State> {
);
})}
</MaybeScreenContainer>
{headerMode === 'float'
? renderHeader({
mode: 'float',
layout,
insets: { top, right, bottom, left },
scenes,
getPreviousRoute,
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,
})
: null}
{isFloatHeaderAbsolute ? floatingHeader : null}
</React.Fragment>
);
}
@@ -601,7 +599,6 @@ export default class CardStack extends React.Component<Props, State> {
const styles = StyleSheet.create({
container: {
flex: 1,
overflow: 'hidden',
},
floating: {
position: 'absolute',

Some files were not shown because too many files have changed in this diff Show More