Compare commits

..

44 Commits

Author SHA1 Message Date
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
Satyajit Sahoo
5afc82b11a chore: publish
- react-navigation-animated-switch@0.5.5
 - react-navigation-drawer@2.4.6
 - react-navigation-material-bottom-tabs@2.2.5
 - @react-navigation/native@3.7.6
 - react-navigation@4.3.2
 - react-navigation-stack@2.3.4
 - react-navigation-tabs@2.8.6
2020-03-20 14:41:05 +01:00
Satyajit Sahoo
80c8c9d1de fix: don't mix import and module.exports. closes #7660 2020-03-20 14:28:24 +01:00
Satyajit Sahoo
fd7c5ca9b2 chore: publish
- react-navigation-stack@2.3.3
2020-03-20 11:49:37 +01:00
Satyajit Sahoo
72bbebc80e chore: sync latest stack 2020-03-20 11:40:21 +01:00
Satyajit Sahoo
915861e601 chore: publish
- react-navigation-stack@2.3.2
2020-03-19 20:12:07 +01:00
Satyajit Sahoo
8152ae1212 chore: sync latest stack 2020-03-19 20:11:34 +01:00
Satyajit Sahoo
79125bfab9 chore: publish
- react-navigation-drawer@2.4.5
 - react-navigation-tabs@2.8.5
2020-03-19 16:44:44 +01:00
Satyajit Sahoo
44c390075f fix: don't use react-native-screens on web
seems `react-native-screens` doesn't handle active screens properly and shows a blank page
 instead on web when a number is specified in the `active` prop.

closes #7485
2020-03-19 16:43:28 +01:00
Satyajit Sahoo
1c5e7a5ff2 chore: publish
- react-navigation-animated-switch@0.5.4
 - react-navigation-drawer@2.4.4
 - react-navigation-material-bottom-tabs@2.2.4
 - @react-navigation/native@3.7.5
 - react-navigation@4.3.1
 - react-navigation-stack@2.3.1
 - react-navigation-tabs@2.8.4
2020-03-16 19:22:04 +01:00
Satyajit Sahoo
cfc1bac4e1 fix: downgrade react-native-safe-area-view
closes #7813
2020-03-16 19:21:05 +01:00
Satyajit Sahoo
c0fb54b5a2 chore: publish
- react-navigation-animated-switch@0.5.3
 - @react-navigation/core@3.7.0
 - react-navigation-drawer@2.4.3
 - react-navigation-material-bottom-tabs@2.2.3
 - @react-navigation/native@3.7.4
 - react-navigation@4.3.0
 - react-navigation-stack@2.3.0
 - react-navigation-tabs@2.8.3
2020-03-16 17:14:15 +01:00
Satyajit Sahoo
5927f4287f feat: add prune option to pop action to match v5 behaviour 2020-03-16 16:37:20 +01:00
Satyajit Sahoo
37a664b433 chore: upgrade depenendecies 2020-03-16 15:24:11 +01:00
Satyajit Sahoo
8d0b61f1b7 chore: sync latest stack 2020-03-16 14:55:51 +01:00
Satyajit Sahoo
d3f092cb84 chore: publish
- react-navigation-stack@2.2.3
2020-03-04 13:37:40 +01:00
Satyajit Sahoo
f5a3880969 fix: dispatch completeTransition on mount so focus event is emitted 2020-03-04 13:36:34 +01:00
Satyajit Sahoo
06f43549db chore: publish
- react-navigation-animated-switch@0.5.2
 - react-navigation-drawer@2.4.2
 - react-navigation-material-bottom-tabs@2.2.2
 - react-navigation@4.2.2
 - react-navigation-stack@2.2.2
 - react-navigation-tabs@2.8.2
2020-02-26 20:11:37 +01:00
Satyajit Sahoo
741f38a028 chore: run clean before release 2020-02-26 20:10:25 +01:00
Satyajit Sahoo
3bb2c196e4 fix: fix typescript errors in the definition file 2020-02-26 20:09:03 +01:00
Satyajit Sahoo
77db2eaf62 chore: publish
- react-navigation-animated-switch@0.5.1
 - @react-navigation/core@3.6.1
 - react-navigation-drawer@2.4.1
 - react-navigation-material-bottom-tabs@2.2.1
 - @react-navigation/native@3.7.3
 - react-navigation@4.2.1
 - react-navigation-stack@2.2.1
 - react-navigation-tabs@2.8.1
2020-02-24 20:00:33 +01:00
Satyajit Sahoo
fc24ebd16c fix: fix file extensions. closes #7246 2020-02-24 19:59:25 +01:00
Satyajit Sahoo
41e1272507 chore: fix publish for native 2020-02-24 19:13:02 +01:00
Satyajit Sahoo
7ad6de59cb chore: fix publish for native
closes #7124
2020-02-24 18:13:59 +01:00
103 changed files with 4459 additions and 3038 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@
.idea
.expo
.gradle
.history
local.properties

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

@@ -8,9 +8,9 @@
"ios": "expo start --ios"
},
"dependencies": {
"@babel/runtime": "^7.8.4",
"@babel/runtime": "^7.8.7",
"@expo/vector-icons": "^10.0.6",
"@react-native-community/masked-view": "0.1.6",
"@react-native-community/masked-view": "0.1.7",
"expo": "^36.0.0",
"expo-asset": "~8.0.0",
"expo-barcode-scanner": "^8.0.0",
@@ -21,16 +21,16 @@
"react-native": "https://github.com/expo/react-native/archive/sdk-36.0.1.tar.gz",
"react-native-gesture-handler": "~1.6.0",
"react-native-iphone-x-helper": "^1.2.1",
"react-native-maps": "0.26.1",
"react-native-maps": "0.27.0",
"react-native-paper": "^3.4.0",
"react-native-reanimated": "~1.7.0",
"react-native-safe-area-context": "0.7.3",
"react-native-screens": "2.0.0-beta.10",
"react-native-screens": "2.3.0",
"react-native-webview": "8.1.2",
"react-navigation-header-buttons": "^3.0.5"
},
"devDependencies": {
"babel-plugin-module-resolver": "^4.0.0",
"expo-cli": "^3.13.1"
"expo-cli": "^3.13.8"
}
}

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

@@ -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

@@ -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,20 +18,22 @@
"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",
"example": "yarn --cwd example"
},
"devDependencies": {
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-proposal-optional-chaining": "^7.8.3",
"@babel/preset-env": "^7.8.4",
"@babel/preset-env": "^7.8.7",
"@babel/preset-react": "^7.8.3",
"@babel/preset-typescript": "^7.8.3",
"@babel/runtime": "^7.8.4",
"@babel/runtime": "^7.8.7",
"@commitlint/config-conventional": "^8.3.4",
"@types/jest": "^25.1.2",
"@types/jest": "^25.1.4",
"babel-jest": "^25.2.3",
"commitlint": "^8.3.5",
"core-js": "^3.6.4",
"eslint": "^6.8.0",
@@ -39,8 +41,8 @@
"husky": "^4.2.1",
"jest": "^25.1.0",
"lerna": "^3.20.2",
"prettier": "^1.19.1",
"typescript": "~3.7.5"
"prettier": "^2.0.1",
"typescript": "~3.8.3"
},
"resolutions": {
"react": "~16.9.0",

View File

@@ -3,6 +3,78 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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
## [0.5.4](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.3...react-navigation-animated-switch@0.5.4) (2020-03-16)
**Note:** Version bump only for package react-navigation-animated-switch
## [0.5.3](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.2...react-navigation-animated-switch@0.5.3) (2020-03-16)
**Note:** Version bump only for package react-navigation-animated-switch
## [0.5.2](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.1...react-navigation-animated-switch@0.5.2) (2020-02-26)
**Note:** Version bump only for package react-navigation-animated-switch
## [0.5.1](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.0...react-navigation-animated-switch@0.5.1) (2020-02-24)
**Note:** Version bump only for package react-navigation-animated-switch
# 0.5.0 (2020-02-24)

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-animated-switch",
"version": "0.5.0",
"version": "0.5.9",
"description": "Animated switch for React Navigation",
"main": "lib/commonjs/index.js",
"react-native": "lib/module/index.js",
@@ -21,15 +21,15 @@
"access": "public"
},
"devDependencies": {
"@react-native-community/bob": "0.9.7",
"@types/react": "16.9.22",
"@types/react-native": "0.61.17",
"@react-native-community/bob": "0.10.0",
"@types/react": "16.9.23",
"@types/react-native": "0.61.22",
"del-cli": "^3.0.0",
"react": "~16.9.0",
"react-native": "~0.61.5",
"react-native-reanimated": "~1.7.0",
"react-navigation": "^4.2.0",
"typescript": "~3.7.5"
"react-navigation": "^4.3.6",
"typescript": "~3.8.3"
},
"peerDependencies": {
"react": "*",

View File

@@ -0,0 +1,13 @@
{
"settings": {
"import/extensions": [".js", ".ts", ".tsx"],
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"]
},
"import/resolver": {
"node": {
"extensions": [".js", ".ts", ".tsx"]
}
}
}
}

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.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)
### Features
* add prune option to pop action to match v5 behaviour ([5927f42](https://github.com/react-navigation/react-navigation-core/commit/5927f4287f5e1ab106537865523daa1c03b14b47))
## [3.6.1](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.6.0...@react-navigation/core@3.6.1) (2020-02-24)
### Bug Fixes
* fix file extensions. closes [#7246](https://github.com/react-navigation/react-navigation-core/issues/7246) ([fc24ebd](https://github.com/react-navigation/react-navigation-core/commit/fc24ebd16c1010be4caaf3ead705909f283532f4))
# 3.6.0 (2020-02-24)

View File

@@ -1,6 +1,6 @@
{
"name": "@react-navigation/core",
"version": "3.6.0",
"version": "3.7.4",
"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",
@@ -36,11 +37,11 @@
"dependencies": {
"hoist-non-react-statics": "^3.3.2",
"path-to-regexp": "^1.8.0",
"query-string": "^6.11.0",
"react-is": "^16.8.6"
"query-string": "^6.11.1",
"react-is": "^16.13.0"
},
"devDependencies": {
"@react-native-community/bob": "^0.9.7",
"@react-native-community/bob": "^0.10.0",
"del-cli": "^3.0.0",
"react": "~16.9.0",
"react-native": "~0.61.5",

View File

@@ -1,4 +1,4 @@
import invariant from './utils/invariant.ts';
import invariant from './utils/invariant';
/**
* Utilities to perform atomic operation with navigate state and routes.
@@ -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

@@ -1,4 +1,4 @@
import * as NavigationActions from '../NavigationActions.ts';
import * as NavigationActions from '../NavigationActions';
describe('generic navigation actions', () => {
const params = { foo: 'bar' };

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.ts';
import getNavigationActionCreators from './routers/getNavigationActionCreators.ts';
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

@@ -1,4 +1,4 @@
import getNavigationActionCreators from './routers/getNavigationActionCreators.ts';
import getNavigationActionCreators from './routers/getNavigationActionCreators';
import getChildNavigation from './getChildNavigation';
import getChildrenNavigationCache from './getChildrenNavigationCache';
@@ -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 invariant from '../utils/invariant.ts';
import ThemeContext from '../views/ThemeContext.ts';
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

@@ -1,11 +1,11 @@
import * as NavigationActions from '../NavigationActions.ts';
import * as NavigationActions from '../NavigationActions';
import * as StackActions from './StackActions';
import createConfigGetter from './createConfigGetter';
import getScreenForRouteName from './getScreenForRouteName';
import StateUtils from '../StateUtils';
import validateRouteConfigMap from './validateRouteConfigMap';
import invariant from '../utils/invariant.ts';
import { generateKey } from './KeyGenerator.ts';
import invariant from '../utils/invariant';
import { generateKey } from './KeyGenerator';
import { createPathParser } from './pathUtils';
function behavesLikePushAction(action) {
@@ -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 = {};
@@ -532,24 +532,46 @@ export default (routeConfigs, stackConfig = {}) => {
action.type === NavigationActions.BACK ||
action.type === StackActions.POP
) {
const { key, n, immediate } = action;
let backRouteIndex = state.index;
if (action.type === StackActions.POP && n != null) {
// determine the index to go back *from*. In this case, n=1 means to go
// 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);
backRouteIndex = state.routes.indexOf(backRoute);
}
const { key, n, immediate, prune } = action;
if (backRouteIndex > 0) {
return {
...state,
routes: state.routes.slice(0, backRouteIndex),
index: backRouteIndex - 1,
isTransitioning: immediate !== true,
};
if (action.type === StackActions.POP && prune === false && key) {
const index = state.routes.findIndex((r) => r.key === key);
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;
if (action.type === StackActions.POP && n != null) {
// determine the index to go back *from*. In this case, n=1 means to go
// 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);
backRouteIndex = state.routes.indexOf(backRoute);
}
if (backRouteIndex > 0) {
return {
...state,
routes: state.routes.slice(0, backRouteIndex),
index: backRouteIndex - 1,
isTransitioning: immediate !== true,
};
}
}
}

View File

@@ -1,9 +1,9 @@
import invariant from '../utils/invariant.ts';
import invariant from '../utils/invariant';
import getScreenForRouteName from './getScreenForRouteName';
import createConfigGetter from './createConfigGetter';
import * as NavigationActions from '../NavigationActions.ts';
import * as SwitchActions from './SwitchActions.ts';
import * as NavigationActions from '../NavigationActions';
import * as SwitchActions from './SwitchActions';
import * as StackActions from './StackActions';
import validateRouteConfigMap from './validateRouteConfigMap';
import { createPathParser } from './pathUtils';
@@ -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

@@ -5,14 +5,14 @@ import React from 'react';
import SwitchRouter from '../SwitchRouter';
import StackRouter from '../StackRouter';
import TabRouter from '../TabRouter';
import * as NavigationActions from '../../NavigationActions.ts';
import { _TESTING_ONLY_normalize_keys } from '../KeyGenerator.ts';
import * as NavigationActions from '../../NavigationActions';
import { _TESTING_ONLY_normalize_keys } from '../KeyGenerator';
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

@@ -6,9 +6,9 @@ import StackRouter from '../StackRouter';
import TabRouter from '../TabRouter';
import SwitchRouter from '../SwitchRouter';
import * as NavigationActions from '../../NavigationActions.ts';
import * as NavigationActions from '../../NavigationActions';
import * as StackActions from '../StackActions';
import { _TESTING_ONLY_normalize_keys } from '../KeyGenerator.ts';
import { _TESTING_ONLY_normalize_keys } from '../KeyGenerator';
beforeEach(() => {
_TESTING_ONLY_normalize_keys();
@@ -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

@@ -4,8 +4,8 @@ import React from 'react';
import StackRouter from '../StackRouter';
import * as StackActions from '../StackActions';
import * as NavigationActions from '../../NavigationActions.ts';
import { _TESTING_ONLY_normalize_keys } from '../KeyGenerator.ts';
import * as NavigationActions from '../../NavigationActions';
import { _TESTING_ONLY_normalize_keys } from '../KeyGenerator';
beforeEach(() => {
_TESTING_ONLY_normalize_keys();
@@ -562,6 +562,31 @@ describe('StackRouter', () => {
expect(poppedState3.routes.length).toBe(1);
expect(poppedState3.index).toBe(0);
expect(poppedState3.isTransitioning).toBe(true);
const poppedState4 = TestRouter.getStateForAction(
StackActions.pop({ key: 'C', prune: false, immediate: true }),
state
);
expect(poppedState4.routes.length).toBe(3);
expect(poppedState4.index).toBe(2);
expect(poppedState4.isTransitioning).toBe(false);
expect(poppedState4.routes).toEqual([
{ key: 'A', routeName: 'foo' },
{ key: 'B', routeName: 'bar', params: { bazId: '321' } },
{ key: 'D', routeName: 'bar' },
]);
const poppedState5 = TestRouter.getStateForAction(
StackActions.pop({ n: 2, key: 'C', prune: false }),
state
);
expect(poppedState5.routes.length).toBe(2);
expect(poppedState5.index).toBe(1);
expect(poppedState5.isTransitioning).toBe(true);
expect(poppedState5.routes).toEqual([
{ key: 'A', routeName: 'foo' },
{ key: 'D', routeName: 'bar' },
]);
});
it('popToTop works as expected', () => {

View File

@@ -3,7 +3,7 @@
import React from 'react';
import SwitchRouter from '../SwitchRouter';
import StackRouter from '../StackRouter';
import * as NavigationActions from '../../NavigationActions.ts';
import * as NavigationActions from '../../NavigationActions';
import { getRouterTestHelper } from './routerTestHelper';
describe('SwitchRouter', () => {

View File

@@ -3,7 +3,7 @@
import React from 'react';
import TabRouter from '../TabRouter';
import * as NavigationActions from '../../NavigationActions.ts';
import * as NavigationActions from '../../NavigationActions';
const INIT_ACTION = { type: NavigationActions.INIT };
@@ -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

@@ -1,5 +1,5 @@
import * as NavigationActions from '../../NavigationActions.ts';
import * as SwitchActions from '../../routers/SwitchActions.ts';
import * as NavigationActions from '../../NavigationActions';
import * as SwitchActions from '../../routers/SwitchActions';
import * as StackActions from '../../routers/StackActions';
// A simple helper that makes it easier to write basic routing tests
@@ -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

@@ -1,4 +1,4 @@
import invariant from '../utils/invariant.ts';
import invariant from '../utils/invariant';
import getScreenForRouteName from './getScreenForRouteName';
import validateScreenOptions from './validateScreenOptions';

View File

@@ -1,6 +1,6 @@
import { isValidElementType } from 'react-is';
import invariant from '../utils/invariant.ts';
import invariant from '../utils/invariant';
/**
* Simple helper that gets a single screen (React component or navigator)
@@ -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

@@ -1,8 +1,8 @@
/* eslint-disable import/no-commonjs */
import pathToRegexp, { compile } from 'path-to-regexp';
import * as NavigationActions from '../NavigationActions.ts';
import invariant from '../utils/invariant.ts';
import * as NavigationActions from '../NavigationActions';
import invariant from '../utils/invariant';
const queryString = require('query-string');
@@ -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

@@ -1,6 +1,6 @@
import { isValidElementType } from 'react-is';
import invariant from '../utils/invariant.ts';
import invariant from '../utils/invariant';
/**
* Make sure the config passed e.g. to StackRouter, TabRouter has
@@ -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

@@ -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,342 @@
// @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
);
}
componentWillUnmount() {
this._actionSubscription?.remove();
this._willFocusSubscription?.remove();
this._willBlurSubscription?.remove();
this._didFocusSubscription?.remove();
this._didBlurSubscription?.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 {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,
});
}
}
};
/**
* @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

@@ -1,5 +1,5 @@
import React from 'react';
import NavigationContext from './NavigationContext.ts';
import NavigationContext from './NavigationContext';
export default class SceneView extends React.PureComponent {
render() {

View File

@@ -1,7 +1,7 @@
import React from 'react';
import renderer from 'react-test-renderer';
import NavigationEvents from '../NavigationEvents';
import NavigationContext from '../NavigationContext.ts';
import NavigationContext from '../NavigationContext';
const createPropListener = () => jest.fn();
@@ -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

@@ -1,21 +1,22 @@
import React from 'react';
import hoistStatics from 'hoist-non-react-statics';
import invariant from '../utils/invariant.ts';
import NavigationContext from './NavigationContext.ts';
import invariant from '../utils/invariant';
import NavigationContext from './NavigationContext';
export default function withNavigation(
Component,
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,92 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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
## [2.4.5](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.4...react-navigation-drawer@2.4.5) (2020-03-19)
### Bug Fixes
* don't use react-native-screens on web ([44c3900](https://github.com/react-navigation/drawer/commit/44c390075f7b76664e09fd9a1a7926645133ebec)), closes [#7485](https://github.com/react-navigation/drawer/issues/7485)
## [2.4.4](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.3...react-navigation-drawer@2.4.4) (2020-03-16)
**Note:** Version bump only for package react-navigation-drawer
## [2.4.3](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.2...react-navigation-drawer@2.4.3) (2020-03-16)
**Note:** Version bump only for package react-navigation-drawer
## [2.4.2](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.1...react-navigation-drawer@2.4.2) (2020-02-26)
**Note:** Version bump only for package react-navigation-drawer
## [2.4.1](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.0...react-navigation-drawer@2.4.1) (2020-02-24)
**Note:** Version bump only for package react-navigation-drawer
# 2.4.0 (2020-02-24)

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-drawer",
"version": "2.4.0",
"version": "2.4.10",
"description": "Drawer navigator component for React Navigation",
"main": "lib/commonjs/index.js",
"react-native": "lib/module/index.js",
@@ -38,26 +38,26 @@
},
"homepage": "https://github.com/react-navigation/drawer#readme",
"devDependencies": {
"@react-native-community/bob": "^0.9.7",
"@types/react": "^16.9.22",
"@types/react-native": "^0.61.17",
"@react-native-community/bob": "^0.10.0",
"@types/react": "^16.9.23",
"@types/react-native": "^0.61.22",
"del-cli": "^3.0.0",
"react": "~16.9.0",
"react-lifecycles-compat": "^3.0.4",
"react-native": "~0.61.5",
"react-native-gesture-handler": "^1.6.0",
"react-native-reanimated": "^1.2.0",
"react-native-screens": "^2.0.0-beta.10",
"react-native-screens": "^2.3.0",
"react-native-testing-library": "^1.12.0",
"react-navigation": "^4.2.0",
"typescript": "~3.7.5"
"react-navigation": "^4.3.6",
"typescript": "~3.8.3"
},
"peerDependencies": {
"react": "*",
"react-native": "*",
"react-native-gesture-handler": "^1.0.12",
"react-native-reanimated": "^1.0.0",
"react-native-screens": "^1.0.0 || ^1.0.0-alpha",
"react-native-screens": ">=1.0.0 || >= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0",
"react-navigation": "^4.1.1"
},
"@react-native-community/bob": {

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

@@ -1,5 +1,3 @@
/* @flow */
import * as React from 'react';
import { Platform, StyleSheet, View } from 'react-native';
import { Screen, screensEnabled } from 'react-native-screens';
@@ -10,21 +8,29 @@ type Props = {
style?: any;
};
const FAR_FAR_AWAY = 3000; // this should be big enough to move the whole view out of its container
const FAR_FAR_AWAY = 30000; // this should be big enough to move the whole view out of its container
export default class ResourceSavingScene extends React.Component<Props> {
render() {
if (screensEnabled?.()) {
// react-native-screens is buggy on web
if (screensEnabled?.() && Platform.OS !== 'web') {
const { isVisible, ...rest } = this.props;
// @ts-ignore
return <Screen active={isVisible ? 1 : 0} {...rest} />;
}
const { isVisible, children, style, ...rest } = this.props;
return (
<View
style={[styles.container, style]}
style={[
styles.container,
Platform.OS === 'web'
? { display: isVisible ? 'flex' : 'none' }
: null,
style,
]}
collapsable={false}
removeClippedSubviews={
// On iOS, set removeClippedSubviews to true only when not focused

View File

@@ -3,6 +3,78 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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
## [2.2.4](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.3...react-navigation-material-bottom-tabs@2.2.4) (2020-03-16)
**Note:** Version bump only for package react-navigation-material-bottom-tabs
## [2.2.3](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.2...react-navigation-material-bottom-tabs@2.2.3) (2020-03-16)
**Note:** Version bump only for package react-navigation-material-bottom-tabs
## [2.2.2](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.1...react-navigation-material-bottom-tabs@2.2.2) (2020-02-26)
**Note:** Version bump only for package react-navigation-material-bottom-tabs
## [2.2.1](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.0...react-navigation-material-bottom-tabs@2.2.1) (2020-02-24)
**Note:** Version bump only for package react-navigation-material-bottom-tabs
# 2.2.0 (2020-02-24)

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-material-bottom-tabs",
"version": "2.2.0",
"version": "2.2.9",
"description": "Material Bottom Tab Navigation component for React Navigation",
"main": "lib/commonjs/index.js",
"module": "lib/module/index.js",
@@ -38,18 +38,18 @@
},
"homepage": "https://github.com/react-navigation/react-navigation-material-bottom-tabs#readme",
"devDependencies": {
"@react-native-community/bob": "^0.9.7",
"@react-native-community/bob": "^0.10.0",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/react": "^16.9.22",
"@types/react-native": "^0.61.17",
"@types/react": "^16.9.23",
"@types/react-native": "^0.61.22",
"del-cli": "^3.0.0",
"react": "~16.9.0",
"react-native": "~0.61.5",
"react-native-gesture-handler": "^1.6.0",
"react-native-paper": "^3.1.1",
"react-native-reanimated": "^1.2.0",
"react-navigation": "^4.2.0",
"typescript": "~3.7.5"
"react-navigation": "^4.3.6",
"typescript": "~3.8.3"
},
"peerDependencies": {
"react": "*",

View File

@@ -3,6 +3,82 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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)
### Bug Fixes
* don't mix import and module.exports. closes [#7660](https://github.com/react-navigation/react-navigation-native/issues/7660) ([80c8c9d](https://github.com/react-navigation/react-navigation-native/commit/80c8c9d1dead57eab3b977a1eebf1e9f5f35cd1a))
## [3.7.5](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.4...@react-navigation/native@3.7.5) (2020-03-16)
### Bug Fixes
* downgrade react-native-safe-area-view ([cfc1bac](https://github.com/react-navigation/react-navigation-native/commit/cfc1bac4e153db4a4ba3f2a9033f77b53367fcbc)), closes [#7813](https://github.com/react-navigation/react-navigation-native/issues/7813)
## [3.7.4](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.3...@react-navigation/native@3.7.4) (2020-03-16)
**Note:** Version bump only for package @react-navigation/native
## [3.7.3](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.0...@react-navigation/native@3.7.3) (2020-02-24)
**Note:** Version bump only for package @react-navigation/native
# 3.7.0 (2020-02-24)

View File

@@ -1,6 +1,6 @@
{
"name": "@react-navigation/native",
"version": "3.7.0",
"version": "3.7.10",
"description": "React Native support for React Navigation",
"main": "lib/commonjs/index.js",
"react-native": "lib/module/index.js",
@@ -9,9 +9,14 @@
"src",
"lib"
],
"scripts": {
"prepare": "bob build",
"clean": "del lib"
},
"sideEffects": false,
"publishConfig": {
"access": "public"
"access": "public",
"tag": "4.x"
},
"keywords": [
"react-native",
@@ -34,13 +39,13 @@
"react-native-safe-area-view": "^0.14.8"
},
"devDependencies": {
"@react-navigation/core": "^3.6.0",
"@react-native-community/bob": "^0.10.0",
"@react-navigation/core": "^3.7.4",
"@types/react-test-renderer": "^16.9.2",
"del-cli": "^3.0.0",
"react": "~16.9.0",
"react-native": "~0.61.5",
"react-native-gesture-handler": "^1.6.0",
"react-native-screens": "^2.0.0-beta.10",
"react-test-renderer": "^16.9.2"
},
"@react-native-community/bob": {

View File

@@ -52,7 +52,7 @@ const WrappedFlatList = React.forwardRef((props, ref) => (
<FlatList
ref={ref}
{...props}
renderScrollComponent={props => (
renderScrollComponent={(props) => (
<WrappedScrollView {...propsMaybeWithRefreshControl(props)} />
)}
/>
@@ -62,15 +62,14 @@ const WrappedSectionList = React.forwardRef((props, ref) => (
<SectionList
ref={ref}
{...props}
renderScrollComponent={props => (
renderScrollComponent={(props) => (
<WrappedScrollView {...propsMaybeWithRefreshControl(props)} />
)}
/>
));
// eslint-disable-next-line import/no-commonjs
module.exports = {
ScrollView: WrappedScrollView,
FlatList: WrappedFlatList,
SectionList: WrappedSectionList,
export {
WrappedScrollView as ScrollView,
WrappedFlatList as FlatList,
WrappedSectionList as SectionList,
};

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,17 +9,17 @@
* 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');
}
};
}
function invariant(condition, format, a, b, c, d, e, f) {
export default function invariant(condition, format, a, b, c, d, e, f) {
validateFormat(format);
if (!condition) {
@@ -32,7 +32,7 @@ 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++];
})
);
@@ -43,6 +43,3 @@ function invariant(condition, format, a, b, c, d, e, f) {
throw error;
}
}
// eslint-disable-next-line import/no-commonjs
module.exports = invariant;

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,84 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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
## [4.3.1](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.0...react-navigation@4.3.1) (2020-03-16)
**Note:** Version bump only for package react-navigation
# [4.3.0](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.2.2...react-navigation@4.3.0) (2020-03-16)
### Features
* add prune option to pop action to match v5 behaviour ([5927f42](https://github.com/react-navigation/react-navigation/commit/5927f4287f5e1ab106537865523daa1c03b14b47))
## [4.2.2](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.2.1...react-navigation@4.2.2) (2020-02-26)
### Bug Fixes
* fix typescript errors in the definition file ([3bb2c19](https://github.com/react-navigation/react-navigation/commit/3bb2c196e4acfa069791003c2b448b5ab51004d2))
## [4.2.1](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.2.0...react-navigation@4.2.1) (2020-02-24)
**Note:** Version bump only for package react-navigation
# 4.2.0 (2020-02-24)

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation",
"version": "4.2.0",
"version": "4.3.6",
"description": "Routing and navigation for your React Native apps",
"main": "src/index.js",
"types": "typescript/react-navigation.d.ts",
@@ -24,16 +24,16 @@
"react-native": "*"
},
"dependencies": {
"@react-navigation/core": "^3.6.0",
"@react-navigation/native": "^3.7.0"
"@react-navigation/core": "^3.7.4",
"@react-navigation/native": "^3.7.10"
},
"devDependencies": {
"@types/react": "^16.9.22",
"@types/react-native": "^0.61.17",
"@types/react": "^16.9.23",
"@types/react-native": "^0.61.22",
"@types/react-test-renderer": "^16.9.2",
"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

@@ -328,6 +328,7 @@ export interface NavigationPopActionPayload {
// n: the number of routes to pop of the stack
n?: number;
immediate?: boolean;
prune?: boolean;
key?: string;
}
@@ -690,21 +691,21 @@ export function createSwitchNavigator(
* NavigationActions
*/
export namespace NavigationActions {
declare const BACK: 'Navigation/BACK';
declare const INIT: 'Navigation/INIT';
declare const NAVIGATE: 'Navigation/NAVIGATE';
declare const SET_PARAMS: 'Navigation/SET_PARAMS';
export const BACK: 'Navigation/BACK';
export const INIT: 'Navigation/INIT';
export const NAVIGATE: 'Navigation/NAVIGATE';
export const SET_PARAMS: 'Navigation/SET_PARAMS';
declare function init(
export function init(
options?: NavigationInitActionPayload
): NavigationInitAction;
declare function navigate(
export function navigate(
options: NavigationNavigateActionPayload
): NavigationNavigateAction;
declare function back(
export function back(
options?: NavigationBackActionPayload
): NavigationBackAction;
declare function setParams(
export function setParams(
options: NavigationSetParamsActionPayload
): NavigationSetParamsAction;
}
@@ -713,45 +714,43 @@ export namespace NavigationActions {
* DrawerActions
*/
export namespace DrawerActions {
declare const OPEN_DRAWER: 'Navigation/OPEN_DRAWER';
declare const CLOSE_DRAWER: 'Navigation/CLOSE_DRAWER';
declare const TOGGLE_DRAWER: 'Navigation/TOGGLE_DRAWER';
export const OPEN_DRAWER: 'Navigation/OPEN_DRAWER';
export const CLOSE_DRAWER: 'Navigation/CLOSE_DRAWER';
export const TOGGLE_DRAWER: 'Navigation/TOGGLE_DRAWER';
declare function openDrawer(): NavigationOpenDrawerAction;
declare function closeDrawer(): NavigationCloseDrawerAction;
declare function toggleDrawer(): NavigationToggleDrawerAction;
export function openDrawer(): NavigationOpenDrawerAction;
export function closeDrawer(): NavigationCloseDrawerAction;
export function toggleDrawer(): NavigationToggleDrawerAction;
}
/**
* StackActions
*/
export namespace StackActions {
declare const POP: 'Navigation/POP';
declare const POP_TO_TOP: 'Navigation/POP_TO_TOP';
declare const PUSH: 'Navigation/PUSH';
declare const RESET: 'Navigation/RESET';
declare const REPLACE: 'Navigation/REPLACE';
declare const COMPLETE_TRANSITION: 'Navigation/COMPLETE_TRANSITION';
export const POP: 'Navigation/POP';
export const POP_TO_TOP: 'Navigation/POP_TO_TOP';
export const PUSH: 'Navigation/PUSH';
export const RESET: 'Navigation/RESET';
export const REPLACE: 'Navigation/REPLACE';
export const COMPLETE_TRANSITION: 'Navigation/COMPLETE_TRANSITION';
declare function pop(
options: NavigationPopActionPayload
): NavigationPopAction;
declare function popToTop(
export function pop(options: NavigationPopActionPayload): NavigationPopAction;
export function popToTop(
options?: NavigationPopToTopActionPayload
): NavigationPopToTopAction;
declare function push(
export function push(
options: NavigationPushActionPayload
): NavigationPushAction;
declare function reset(
export function reset(
options: NavigationResetActionPayload
): NavigationResetAction;
declare function replace(
export function replace(
options: NavigationReplaceActionPayload
): NavigationReplaceAction;
declare function completeTransition(
export function completeTransition(
payload?: NavigationCompleteTransitionActionPayload
): NavigationCompleteTransitionAction;
}
@@ -760,9 +759,9 @@ export namespace StackActions {
* SwitchActions
*/
export namespace SwitchActions {
declare const JUMP_TO: 'Navigation/JUMP_TO';
export const JUMP_TO: 'Navigation/JUMP_TO';
declare function jumpTo(
export function jumpTo(
options: NavigationJumpToActionPayload
): NavigationJumpToAction;
}

View File

@@ -3,6 +3,134 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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
## [2.3.3](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.2...react-navigation-stack@2.3.3) (2020-03-20)
**Note:** Version bump only for package react-navigation-stack
## [2.3.2](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.1...react-navigation-stack@2.3.2) (2020-03-19)
**Note:** Version bump only for package react-navigation-stack
## [2.3.1](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.0...react-navigation-stack@2.3.1) (2020-03-16)
**Note:** Version bump only for package react-navigation-stack
# [2.3.0](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.2.3...react-navigation-stack@2.3.0) (2020-03-16)
### Features
* add prune option to pop action to match v5 behaviour ([5927f42](https://github.com/react-navigation/react-navigation-stack/commit/5927f4287f5e1ab106537865523daa1c03b14b47))
## [2.2.3](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.2.2...react-navigation-stack@2.2.3) (2020-03-04)
### Bug Fixes
* dispatch completeTransition on mount so focus event is emitted ([f5a3880](https://github.com/react-navigation/react-navigation-stack/commit/f5a3880969afad2df409b0606e249b3971361dca))
## [2.2.2](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.2.1...react-navigation-stack@2.2.2) (2020-02-26)
### Bug Fixes
* fix typescript errors in the definition file ([3bb2c19](https://github.com/react-navigation/react-navigation-stack/commit/3bb2c196e4acfa069791003c2b448b5ab51004d2))
## [2.2.1](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.2.0...react-navigation-stack@2.2.1) (2020-02-24)
**Note:** Version bump only for package react-navigation-stack
# 2.2.0 (2020-02-24)

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-stack",
"version": "2.2.0",
"version": "2.3.10",
"description": "Stack navigator component for React Navigation",
"main": "lib/commonjs/index.js",
"module": "lib/module/index.js",
@@ -43,22 +43,22 @@
"react-native-iphone-x-helper": "^1.2.1"
},
"devDependencies": {
"@react-native-community/bob": "^0.9.7",
"@react-native-community/masked-view": "^0.1.6",
"@react-navigation/stack": "^5.0.9",
"@react-native-community/bob": "^0.10.0",
"@react-native-community/masked-view": "^0.1.7",
"@react-navigation/stack": "^5.2.9",
"@types/color": "^3.0.1",
"@types/react": "^16.9.22",
"@types/react-native": "^0.61.17",
"@types/react": "^16.9.23",
"@types/react-native": "^0.61.22",
"@types/react-test-renderer": "^16.9.2",
"del-cli": "^3.0.0",
"react": "~16.9.0",
"react-native": "~0.61.4",
"react-native-gesture-handler": "^1.6.0",
"react-native-safe-area-context": "^0.7.3",
"react-native-screens": "^2.0.0-beta.10",
"react-navigation": "^4.2.0",
"react-native-screens": "^2.3.0",
"react-navigation": "^4.3.6",
"react-test-renderer": "~16.9.0",
"typescript": "~3.7.5"
"typescript": "~3.8.3"
},
"peerDependencies": {
"@react-native-community/masked-view": "^0.1.1",
@@ -66,7 +66,7 @@
"react-native": "*",
"react-native-gesture-handler": "^1.5.0",
"react-native-safe-area-context": "^0.6.0",
"react-native-screens": "^1.0.0 || ^1.0.0-alpha.0 || ^2.0.0-alpha.20",
"react-native-screens": ">=1.0.0 || >= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0",
"react-navigation": "^4.1.1"
},
"@react-native-community/bob": {

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-02-24 11:53:26.000000000 +0100
+++ src/vendor/index.tsx 2020-02-24 11:58:26.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/index.tsx 2020-03-31 17:47:04.000000000 +0200
+++ src/vendor/index.tsx 2020-03-31 18:06:18.000000000 +0200
@@ -3,11 +3,6 @@
import * as TransitionSpecs from './TransitionConfigs/TransitionSpecs';
import * as TransitionPresets from './TransitionConfigs/TransitionPresets';
@@ -28,9 +28,9 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/index.tsx src/vendor/i
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-02-24 11:53:26.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/navigators/createStackNavigator.tsx 2020-03-31 17:47:04.000000000 +0200
+++ src/vendor/navigators/createStackNavigator.tsx 1970-01-01 01:00:00.000000000 +0100
@@ -1,79 +0,0 @@
@@ -1,81 +0,0 @@
-import * as React from 'react';
-import {
- useNavigationBuilder,
@@ -73,7 +73,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStack
- 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
@@ -107,13 +107,15 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStack
-}
-
-export default createNavigatorFactory<
- StackNavigationState,
- StackNavigationOptions,
- StackNavigationEventMap,
- 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-02-24 11:53:26.000000000 +0100
+++ src/vendor/types.tsx 2020-02-24 12:05:09.000000000 +0100
@@ -7,13 +7,28 @@
--- ../../node_modules/@react-navigation/stack/src/types.tsx 2020-03-31 17:47:04.000000000 +0200
+++ src/vendor/types.tsx 2020-03-31 18:06:18.000000000 +0200
@@ -8,14 +8,28 @@
} from 'react-native';
import { EdgeInsets } from 'react-native-safe-area-context';
import {
@@ -126,6 +128,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
- Route,
- NavigationHelpers,
- StackNavigationState,
- StackActionHelpers,
-} from '@react-navigation/native';
+ NavigationParams,
+ NavigationNavigateAction,
@@ -148,7 +151,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
export type StackNavigationEventMap = {
/**
@@ -26,42 +41,29 @@
@@ -28,22 +42,29 @@
transitionEnd: { data: { closing: boolean } };
};
@@ -156,8 +159,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
- ParamListBase,
- StackNavigationEventMap
->;
-
+export type StackNavigationHelpers = NavigationProp<NavigationStackState>;
export type StackNavigationProp<
- ParamList extends ParamListBase,
- RouteName extends keyof ParamList = string
@@ -167,28 +170,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
- StackNavigationState,
- StackNavigationOptions,
- StackNavigationEventMap
-> & {
- /**
- * Push a new screen onto the stack.
- *
- * @param name Name of the route for the tab.
- * @param [params] Params object for the route.
- */
- push<RouteName extends keyof ParamList>(
- ...args: ParamList[RouteName] extends undefined | any
- ? [RouteName] | [RouteName, ParamList[RouteName]]
- : [RouteName, ParamList[RouteName]]
- ): void;
-
- /**
- * Pop a screen from the stack.
- */
- pop(count?: number): void;
-
- /**
- * Pop to the first route in the stack, dismissing all other screens.
- */
- popToTop(): void;
-> &
- StackActionHelpers<ParamList>;
+ State = NavigationRoute,
+ Params = NavigationParams
+> = NavigationScreenProp<State, Params> & {
@@ -209,10 +192,11 @@ 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 };
@@ -237,24 +239,27 @@
@@ -218,24 +239,27 @@
/**
* Navigation prop for the header.
*/
@@ -246,7 +230,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
export type StackNavigationOptions = StackHeaderOptions &
Partial<TransitionPreset> & {
/**
@@ -332,6 +337,8 @@
@@ -317,6 +341,8 @@
bottom?: number;
left?: number;
};
@@ -255,20 +239,9 @@ 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/BorderlessButton.tsx src/vendor/views/BorderlessButton.tsx
--- ../../node_modules/@react-navigation/stack/src/views/BorderlessButton.tsx 2020-02-24 11:53:26.000000000 +0100
+++ src/vendor/views/BorderlessButton.tsx 2020-02-24 12:50:48.000000000 +0100
@@ -39,6 +39,7 @@
const { children, style, enabled, ...rest } = this.props;
return (
+ // @ts-ignore
<AnimatedBaseButton
{...rest}
onActiveStateChange={this.handleActiveStateChange}
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-02-24 11:53:26.000000000 +0100
+++ src/vendor/views/Header/Header.tsx 2020-02-24 12:48:47.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/views/Header/Header.tsx 2020-03-31 17:47:04.000000000 +0200
+++ src/vendor/views/Header/Header.tsx 2020-03-31 18:06:18.000000000 +0200
@@ -1,12 +1,14 @@
import * as React from 'react';
-import { StackActions } from '@react-navigation/native';
@@ -306,7 +279,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
const goBack = React.useCallback(
debounce(() => {
- if (navigation.canGoBack()) {
- if (navigation.isFocused() && navigation.canGoBack()) {
- navigation.dispatch({
- ...StackActions.pop(),
- source: scene.route.key,
@@ -348,8 +321,8 @@ 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-02-24 11:53:26.000000000 +0100
+++ src/vendor/views/Header/HeaderBackButton.tsx 2020-02-24 11:58:32.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackButton.tsx 2020-03-31 17:47:04.000000000 +0200
+++ src/vendor/views/Header/HeaderBackButton.tsx 2020-03-31 18:06:18.000000000 +0200
@@ -8,9 +8,9 @@
StyleSheet,
LayoutChangeEvent,
@@ -362,8 +335,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBac
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-02-24 11:53:26.000000000 +0100
+++ src/vendor/views/Header/HeaderBackground.tsx 2020-02-24 11:58:32.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackground.tsx 2020-03-31 17:47:04.000000000 +0200
+++ src/vendor/views/Header/HeaderBackground.tsx 2020-03-31 18:06:18.000000000 +0200
@@ -1,6 +1,6 @@
import * as React from 'react';
import { Animated, StyleSheet, Platform, ViewProps } from 'react-native';
@@ -373,8 +346,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBac
type Props = ViewProps & {
children?: React.ReactNode;
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-02-24 11:53:26.000000000 +0100
+++ src/vendor/views/Header/HeaderContainer.tsx 2020-02-24 12:08:14.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx 2020-03-31 17:47:04.000000000 +0200
+++ src/vendor/views/Header/HeaderContainer.tsx 2020-03-31 18:06:18.000000000 +0200
@@ -1,11 +1,6 @@
import * as React from 'react';
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
@@ -426,8 +399,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-02-24 11:53:26.000000000 +0100
+++ src/vendor/views/Header/HeaderSegment.tsx 2020-02-24 11:58:32.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSegment.tsx 2020-03-31 17:47:04.000000000 +0200
+++ src/vendor/views/Header/HeaderSegment.tsx 2020-03-31 18:06:18.000000000 +0200
@@ -8,7 +8,7 @@
ViewStyle,
} from 'react-native';
@@ -447,24 +420,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-02-24 11:53:26.000000000 +0100
+++ src/vendor/views/Header/HeaderTitle.tsx 2020-02-24 12:47:49.000000000 +0100
@@ -1,8 +1,8 @@
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderTitle.tsx 2020-03-31 17:47:04.000000000 +0200
+++ src/vendor/views/Header/HeaderTitle.tsx 2020-03-31 18:06:18.000000000 +0200
@@ -1,6 +1,6 @@
import * as React from 'react';
-import { Animated, StyleSheet, Platform, TextProps } from 'react-native';
import { Animated, StyleSheet, Platform } from 'react-native';
-import { useTheme } from '@react-navigation/native';
+import { Animated, StyleSheet, Platform } from 'react-native';
+import useTheme from '../../../utils/useTheme';
-type Props = TextProps & {
+type Props = React.ComponentProps<typeof Animated.Text> & {
type Props = React.ComponentProps<typeof Animated.Text> & {
tintColor?: string;
children?: 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-02-24 11:53:26.000000000 +0100
+++ src/vendor/views/Stack/Card.tsx 2020-02-24 11:58:32.000000000 +0100
@@ -133,7 +133,7 @@
--- ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx 2020-03-31 17:47:04.000000000 +0200
+++ src/vendor/views/Stack/Card.tsx 2020-03-31 18:06:18.000000000 +0200
@@ -138,7 +138,7 @@
private interactionHandle: number | undefined;
@@ -473,18 +442,9 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx s
private animate = ({
closing,
@@ -507,7 +507,7 @@
) : null}
<View
ref={this.contentRef}
- style={[styles.content, contentStyle]}
+ style={[styles.content, contentStyle] as any}
>
<StackGestureRefContext.Provider value={this.gestureRef}>
<CardAnimationContext.Provider value={animationContext}>
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-02-24 11:53:26.000000000 +0100
+++ src/vendor/views/Stack/CardContainer.tsx 2020-02-24 11:58:32.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardContainer.tsx 2020-03-31 17:47:04.000000000 +0200
+++ src/vendor/views/Stack/CardContainer.tsx 2020-03-31 18:06:18.000000000 +0200
@@ -1,10 +1,16 @@
import * as React from 'react';
import { Animated, View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
@@ -504,7 +464,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardContai
type Props = TransitionPreset & {
index: number;
@@ -180,7 +186,7 @@
@@ -183,7 +189,7 @@
? { marginTop: headerHeight }
: null
}
@@ -514,8 +474,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardContai
>
<View style={styles.container}>
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-02-24 11:53:26.000000000 +0100
+++ src/vendor/views/Stack/CardStack.tsx 2020-02-24 12:09:38.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.tsx 2020-03-31 17:47:04.000000000 +0200
+++ src/vendor/views/Stack/CardStack.tsx 2020-03-31 18:06:18.000000000 +0200
@@ -9,9 +9,8 @@
ViewProps,
} from 'react-native';
@@ -535,18 +495,9 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.
Layout,
StackHeaderMode,
StackCardMode,
@@ -103,7 +103,7 @@
<View
collapsable={!enabled}
removeClippedSubviews={Platform.OS !== 'ios' && enabled}
- style={[style, { overflow: 'hidden' }]}
+ style={[style, { overflow: 'hidden' }] as any}
{...rest}
/>
);
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-02-24 11:53:26.000000000 +0100
+++ src/vendor/views/Stack/StackView.tsx 2020-02-24 12:09:08.000000000 +0100
--- ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.tsx 2020-03-31 17:47:04.000000000 +0200
+++ src/vendor/views/Stack/StackView.tsx 2020-03-31 18:06:18.000000000 +0200
@@ -4,9 +4,9 @@
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import {
@@ -576,7 +527,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.
};
type State = {
@@ -290,14 +292,31 @@
@@ -322,14 +324,37 @@
return null;
}
@@ -597,19 +548,25 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.
return <HeaderContainer {...props} />;
};
+ private handleTransitionComplete = ({ route }: { route: Route<string> }) => {
+ // TODO: remove when the new event system lands
+ this.props.navigation.dispatch(
+ StackActions.completeTransition({ toChildKey: route.key })
+ );
+ private handleTransitionComplete = () => {
+ const { state, navigation } = this.props;
+
+ if (state.isTransitioning) {
+ navigation.dispatch(
+ StackActions.completeTransition({
+ key: navigation.state.key,
+ toChildKey: state.routes[state.index].key,
+ })
+ );
+ }
+ };
+
private handleOpenRoute = ({ route }: { route: Route<string> }) => {
+ this.handleTransitionComplete({ route });
this.setState(state => ({
+ this.handleTransitionComplete();
this.setState((state) => ({
routes: state.replacingRouteKeys.length
? state.routes.filter(r => !state.replacingRouteKeys.includes(r.key))
@@ -315,12 +334,17 @@
? state.routes.filter((r) => !state.replacingRouteKeys.includes(r.key))
@@ -351,12 +376,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
@@ -619,20 +576,14 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.
- target: state.key,
- });
+ // @ts-ignore
+ navigation.dispatch(StackActions.pop({ key: route.key }));
+ 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 +361,24 @@
this.setState((state) => ({
routes: state.routes.filter((r) => r.key !== route.key),
@@ -373,22 +397,24 @@
private handleTransitionStart = (
{ route }: { route: Route<string> },
closing: boolean

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

@@ -1,3 +1,4 @@
import * as React from 'react';
import {
Animated,
StyleProp,
@@ -42,7 +43,6 @@ export type StackNavigationEventMap = {
};
export type StackNavigationHelpers = NavigationProp<NavigationStackState>;
export type StackNavigationProp<
State = NavigationRoute,
Params = NavigationParams
@@ -124,7 +124,7 @@ export type StackHeaderOptions = {
/**
* Style object for the title component.
*/
headerTitleStyle?: StyleProp<TextStyle>;
headerTitleStyle?: React.ComponentProps<typeof Animated.Text>['style'];
/**
* Style object for the container of the `headerTitle` component, for example to add padding.
* By default, `headerTitleContainerStyle` is with an absolute position style and offsets both `left` and `right`.
@@ -284,6 +284,10 @@ export type StackNavigationOptions = StackHeaderOptions &
* Defaults to `true` on Android and `false` on iOS.
*/
cardOverlayEnabled?: boolean;
/**
* Function that returns a React Element to display as a overlay for the card.
*/
cardOverlay?: (props: { style: StyleProp<ViewStyle> }) => React.ReactNode;
/**
* Style object for the card in stack.
* You can provide a custom background color to use instead of the default background here.
@@ -437,7 +441,7 @@ export type StackHeaderTitleProps = {
/**
* Style object for the title element.
*/
style?: StyleProp<TextStyle>;
style?: React.ComponentProps<typeof Animated.Text>['style'];
};
export type TransitionSpec =

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

@@ -32,7 +32,7 @@ export default class BorderlessButton extends React.Component<Props> {
}).start();
}
this.props.onActiveStateChange && this.props.onActiveStateChange(active);
this.props.onActiveStateChange?.(active);
};
render() {

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') {

View File

@@ -32,7 +32,8 @@ const styles = StyleSheet.create({
},
android: {
fontSize: 20,
fontWeight: '500',
fontFamily: 'sans-serif-medium',
fontWeight: 'normal',
},
default: {
fontSize: 18,

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { TextInput, Keyboard } from 'react-native';
import { TextInput, Platform, Keyboard } from 'react-native';
type Props = {
enabled: boolean;
@@ -54,7 +54,13 @@ export default class KeyboardManager extends React.Component<Props> {
this.clearKeyboardTimeout();
Keyboard.dismiss();
const input = this.previouslyFocusedTextInput;
if (Platform.OS === 'android') {
Keyboard.dismiss();
} else if (input) {
TextInput.State.blurTextInput(input);
}
// Cleanup the ID on successful page change
this.previouslyFocusedTextInput = null;

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

@@ -44,6 +44,7 @@ type Props = ViewProps & {
onGestureCanceled?: () => void;
onGestureEnd?: () => void;
children: React.ReactNode;
overlay: (props: { style: StyleProp<ViewStyle> }) => React.ReactNode;
overlayEnabled: boolean;
shadowEnabled: boolean;
gestureEnabled: boolean;
@@ -80,6 +81,10 @@ export default class Card extends React.Component<Props> {
shadowEnabled: true,
gestureEnabled: true,
gestureVelocityImpact: GESTURE_VELOCITY_IMPACT,
overlay: ({ style }: { style: StyleProp<ViewStyle> }) =>
style ? (
<Animated.View pointerEvents="none" style={[styles.overlay, style]} />
) : null,
};
componentDidMount() {
@@ -241,11 +246,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);
@@ -267,7 +282,8 @@ export default class Card extends React.Component<Props> {
}
const closing =
Math.abs(translation + velocity * gestureVelocityImpact) >
(translation + velocity * gestureVelocityImpact) *
getInvertedMultiplier(gestureDirection) >
distance / 2
? velocity !== 0 || translation !== 0
: false;
@@ -409,6 +425,7 @@ export default class Card extends React.Component<Props> {
next,
layout,
insets,
overlay,
overlayEnabled,
shadowEnabled,
gestureEnabled,
@@ -470,55 +487,54 @@ export default class Card extends React.Component<Props> {
: false;
return (
<View pointerEvents="box-none" {...rest}>
{overlayEnabled && overlayStyle ? (
<CardAnimationContext.Provider value={animationContext}>
<View pointerEvents="box-none" {...rest}>
{overlayEnabled ? (
<View pointerEvents="box-none" style={StyleSheet.absoluteFill}>
{overlay({ style: overlayStyle })}
</View>
) : null}
<Animated.View
pointerEvents="none"
style={[styles.overlay, overlayStyle]}
/>
) : null}
<Animated.View
style={[styles.container, containerStyle, customContainerStyle]}
pointerEvents="box-none"
>
<PanGestureHandler
ref={this.gestureRef}
enabled={layout.width !== 0 && gestureEnabled}
onGestureEvent={handleGestureEvent}
onHandlerStateChange={this.handleGestureStateChange}
{...this.gestureActivationCriteria()}
style={[styles.container, containerStyle, customContainerStyle]}
pointerEvents="box-none"
>
<Animated.View style={[styles.container, cardStyle]}>
{shadowEnabled && shadowStyle && !isTransparent ? (
<Animated.View
style={[
styles.shadow,
gestureDirection === 'horizontal'
? [styles.shadowHorizontal, styles.shadowLeft]
: gestureDirection === 'horizontal-inverted'
? [styles.shadowHorizontal, styles.shadowRight]
: gestureDirection === 'vertical'
? [styles.shadowVertical, styles.shadowTop]
: [styles.shadowVertical, styles.shadowBottom],
shadowStyle,
]}
pointerEvents="none"
/>
) : null}
<View
ref={this.contentRef}
style={[styles.content, contentStyle] as any}
>
<StackGestureRefContext.Provider value={this.gestureRef}>
<CardAnimationContext.Provider value={animationContext}>
<PanGestureHandler
ref={this.gestureRef}
enabled={layout.width !== 0 && gestureEnabled}
onGestureEvent={handleGestureEvent}
onHandlerStateChange={this.handleGestureStateChange}
{...this.gestureActivationCriteria()}
>
<Animated.View style={[styles.container, cardStyle]}>
{shadowEnabled && shadowStyle && !isTransparent ? (
<Animated.View
style={[
styles.shadow,
gestureDirection === 'horizontal'
? [styles.shadowHorizontal, styles.shadowLeft]
: gestureDirection === 'horizontal-inverted'
? [styles.shadowHorizontal, styles.shadowRight]
: gestureDirection === 'vertical'
? [styles.shadowVertical, styles.shadowTop]
: [styles.shadowVertical, styles.shadowBottom],
shadowStyle,
]}
pointerEvents="none"
/>
) : null}
<View
ref={this.contentRef}
style={[styles.content, contentStyle]}
>
<StackGestureRefContext.Provider value={this.gestureRef}>
{children}
</CardAnimationContext.Provider>
</StackGestureRefContext.Provider>
</View>
</Animated.View>
</PanGestureHandler>
</Animated.View>
</View>
</StackGestureRefContext.Provider>
</View>
</Animated.View>
</PanGestureHandler>
</Animated.View>
</View>
</CardAnimationContext.Provider>
);
}
}
@@ -532,7 +548,7 @@ const styles = StyleSheet.create({
overflow: 'hidden',
},
overlay: {
...StyleSheet.absoluteFillObject,
flex: 1,
backgroundColor: '#000',
},
shadow: {

View File

@@ -25,6 +25,7 @@ type Props = TransitionPreset & {
safeAreaInsetRight: number;
safeAreaInsetBottom: number;
safeAreaInsetLeft: number;
cardOverlay?: (props: { style: StyleProp<ViewStyle> }) => React.ReactNode;
cardOverlayEnabled?: boolean;
cardShadowEnabled?: boolean;
cardStyle?: StyleProp<ViewStyle>;
@@ -64,6 +65,7 @@ const EPSILON = 0.1;
function CardContainer({
active,
cardOverlay,
cardOverlayEnabled,
cardShadowEnabled,
cardStyle,
@@ -168,6 +170,7 @@ function CardContainer({
closing={closing}
onOpen={handleOpen}
onClose={handleClose}
overlay={cardOverlay}
overlayEnabled={cardOverlayEnabled}
shadowEnabled={cardShadowEnabled}
onTransitionStart={handleTransitionStart}

View File

@@ -75,9 +75,6 @@ type State = {
const EPSILON = 0.01;
const dimensions = Dimensions.get('window');
const layout = { width: dimensions.width, height: dimensions.height };
const MaybeScreenContainer = ({
enabled,
...rest
@@ -160,7 +157,16 @@ const getProgressFromGesture = (
layout: Layout,
descriptor?: StackDescriptor
) => {
const distance = getDistanceFromOptions(mode, layout, descriptor);
const distance = getDistanceFromOptions(
mode,
{
// Make sure that we have a non-zero distance, otherwise there will be incorrect progress
// This causes blank screen on web if it was previously inside container with display: none
width: Math.max(1, layout.width),
height: Math.max(1, layout.height),
},
descriptor
);
if (distance > 0) {
return gesture.interpolate({
@@ -290,19 +296,25 @@ export default class CardStack extends React.Component<Props, State> {
};
}
state: State = {
routes: [],
scenes: [],
gestures: {},
layout,
descriptors: this.props.descriptors,
// Used when card's header is null and mode is float to make transition
// between screens with headers and those without headers smooth.
// This is not a great heuristic here. We don't know synchronously
// on mount what the header height is so we have just used the most
// common cases here.
headerHeights: {},
};
constructor(props: Props) {
super(props);
const { height = 0, width = 0 } = Dimensions.get('window');
this.state = {
routes: [],
scenes: [],
gestures: {},
layout: { height, width },
descriptors: this.props.descriptors,
// Used when card's header is null and mode is float to make transition
// between screens with headers and those without headers smooth.
// This is not a great heuristic here. We don't know synchronously
// on mount what the header height is so we have just used the most
// common cases here.
headerHeights: {},
};
}
private handleLayout = (e: LayoutChangeEvent) => {
const { height, width } = e.nativeEvent.layout;
@@ -401,9 +413,9 @@ export default class CardStack extends React.Component<Props, State> {
left = insets.left,
} = focusedOptions.safeAreaInsets || {};
// Screens is buggy on iOS, so we don't enable it there
// 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 !== 'ios' && mode !== 'modal';
const isScreensEnabled = Platform.OS === 'android' && mode !== 'modal';
return (
<React.Fragment>
@@ -431,6 +443,7 @@ export default class CardStack extends React.Component<Props, State> {
headerTransparent,
cardShadowEnabled,
cardOverlayEnabled,
cardOverlay,
cardStyle,
animationEnabled,
gestureResponseDistance,
@@ -528,6 +541,7 @@ export default class CardStack extends React.Component<Props, State> {
safeAreaInsetRight={safeAreaInsetRight}
safeAreaInsetBottom={safeAreaInsetBottom}
safeAreaInsetLeft={safeAreaInsetLeft}
cardOverlay={cardOverlay}
cardOverlayEnabled={cardOverlayEnabled}
cardShadowEnabled={cardShadowEnabled}
cardStyle={cardStyle}

View File

@@ -48,31 +48,63 @@ type State = {
const GestureHandlerWrapper = GestureHandlerRootView ?? View;
/**
* Compare two arrays with primitive values as the content.
* We need to make sure that both values and order match.
*/
const isArrayEqual = (a: any[], b: any[]) =>
a.length === b.length && a.every((it, index) => it === b[index]);
export default class StackView extends React.Component<Props, State> {
static getDerivedStateFromProps(
props: Readonly<Props>,
state: Readonly<State>
) {
// If there was no change in routes, we don't need to compute anything
if (props.state.routes === state.previousRoutes && state.routes.length) {
if (props.descriptors !== state.previousDescriptors) {
const descriptors = state.routes.reduce<StackDescriptorMap>(
(acc, route) => {
acc[route.key] =
props.descriptors[route.key] || state.descriptors[route.key];
if (
(props.state.routes === state.previousRoutes ||
isArrayEqual(
props.state.routes.map((r) => r.key),
state.previousRoutes.map((r) => r.key)
)) &&
state.routes.length
) {
let routes = state.routes;
let previousRoutes = state.previousRoutes;
let descriptors = props.descriptors;
let previousDescriptors = state.previousDescriptors;
if (props.descriptors !== state.previousDescriptors) {
descriptors = state.routes.reduce<StackDescriptorMap>((acc, route) => {
acc[route.key] =
props.descriptors[route.key] || state.descriptors[route.key];
return acc;
}, {});
previousDescriptors = props.descriptors;
}
if (props.state.routes !== state.previousRoutes) {
// if any route objects have changed, we should update them
const map = props.state.routes.reduce<Record<string, Route<string>>>(
(acc, route) => {
acc[route.key] = route;
return acc;
},
{}
);
return {
previousDescriptors: props.descriptors,
descriptors,
};
routes = state.routes.map((route) => map[route.key] || route);
previousRoutes = props.state.routes;
}
return null;
return {
routes,
previousRoutes,
descriptors,
previousDescriptors,
};
}
// Here we determine which routes were added or removed to animate them
@@ -117,7 +149,7 @@ export default class StackView extends React.Component<Props, State> {
// We only need to animate routes if the focused route changed
// Animating previous routes won't be visible coz the focused route is on top of everything
if (!previousRoutes.find(r => r.key === nextFocusedRoute.key)) {
if (!previousRoutes.find((r) => r.key === nextFocusedRoute.key)) {
// A new route has come to the focus, we treat this as a push
// A replace can also trigger this, the animation should look like push
@@ -130,17 +162,17 @@ export default class StackView extends React.Component<Props, State> {
openingRouteKeys = [...openingRouteKeys, nextFocusedRoute.key];
closingRouteKeys = closingRouteKeys.filter(
key => key !== nextFocusedRoute.key
(key) => key !== nextFocusedRoute.key
);
replacingRouteKeys = replacingRouteKeys.filter(
key => key !== nextFocusedRoute.key
(key) => key !== nextFocusedRoute.key
);
if (!routes.find(r => r.key === previousFocusedRoute.key)) {
if (!routes.find((r) => r.key === previousFocusedRoute.key)) {
// The previous focused route isn't present in state, we treat this as a replace
openingRouteKeys = openingRouteKeys.filter(
key => key !== previousFocusedRoute.key
(key) => key !== previousFocusedRoute.key
);
if (getAnimationTypeForReplace(nextFocusedRoute.key) === 'pop') {
@@ -153,7 +185,7 @@ export default class StackView extends React.Component<Props, State> {
// But since user configured it to animate the old screen like a pop, we need to add this without animation
// So remove it from `openingRouteKeys` which will remove the animation
openingRouteKeys = openingRouteKeys.filter(
key => key !== nextFocusedRoute.key
(key) => key !== nextFocusedRoute.key
);
// Keep the route being removed at the end to animate it out
@@ -165,7 +197,7 @@ export default class StackView extends React.Component<Props, State> {
];
closingRouteKeys = closingRouteKeys.filter(
key => key !== previousFocusedRoute.key
(key) => key !== previousFocusedRoute.key
);
// Keep the old route in the state because it's visible under the new route, and removing it will feel abrupt
@@ -176,7 +208,7 @@ export default class StackView extends React.Component<Props, State> {
}
}
}
} else if (!routes.find(r => r.key === previousFocusedRoute.key)) {
} else if (!routes.find((r) => r.key === previousFocusedRoute.key)) {
// The previously focused route was removed, we treat this as a pop
if (
@@ -188,10 +220,10 @@ export default class StackView extends React.Component<Props, State> {
// Sometimes a route can be closed before the opening animation finishes
// So we also need to remove it from the opening list
openingRouteKeys = openingRouteKeys.filter(
key => key !== previousFocusedRoute.key
(key) => key !== previousFocusedRoute.key
);
replacingRouteKeys = replacingRouteKeys.filter(
key => key !== previousFocusedRoute.key
(key) => key !== previousFocusedRoute.key
);
// Keep a copy of route being removed in the state to be able to animate it
@@ -273,13 +305,13 @@ export default class StackView extends React.Component<Props, State> {
private getPreviousRoute = ({ route }: { route: Route<string> }) => {
const { closingRouteKeys, replacingRouteKeys } = this.state;
const routes = this.state.routes.filter(
r =>
(r) =>
r.key === route.key ||
(!closingRouteKeys.includes(r.key) &&
!replacingRouteKeys.includes(r.key))
);
const index = routes.findIndex(r => r.key === route.key);
const index = routes.findIndex((r) => r.key === route.key);
return routes[index - 1];
};
@@ -308,21 +340,31 @@ export default class StackView extends React.Component<Props, State> {
return <HeaderContainer {...props} />;
};
private handleTransitionComplete = ({ route }: { route: Route<string> }) => {
// TODO: remove when the new event system lands
this.props.navigation.dispatch(
StackActions.completeTransition({ toChildKey: route.key })
);
private handleTransitionComplete = () => {
const { state, navigation } = this.props;
if (state.isTransitioning) {
navigation.dispatch(
StackActions.completeTransition({
key: navigation.state.key,
toChildKey: state.routes[state.index].key,
})
);
}
};
private handleOpenRoute = ({ route }: { route: Route<string> }) => {
this.handleTransitionComplete({ route });
this.setState(state => ({
this.handleTransitionComplete();
this.setState((state) => ({
routes: state.replacingRouteKeys.length
? state.routes.filter(r => !state.replacingRouteKeys.includes(r.key))
? state.routes.filter((r) => !state.replacingRouteKeys.includes(r.key))
: state.routes,
openingRouteKeys: state.openingRouteKeys.filter(key => key !== route.key),
closingRouteKeys: state.closingRouteKeys.filter(key => key !== route.key),
openingRouteKeys: state.openingRouteKeys.filter(
(key) => key !== route.key
),
closingRouteKeys: state.closingRouteKeys.filter(
(key) => key !== route.key
),
replacingRouteKeys: [],
}));
};
@@ -330,29 +372,23 @@ export default class StackView extends React.Component<Props, State> {
private handleCloseRoute = ({ route }: { route: Route<string> }) => {
const { state, navigation } = this.props;
if (state.routes.find(r => r.key === route.key)) {
if (state.routes.find((r) => r.key === route.key)) {
// 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
// @ts-ignore
navigation.dispatch(StackActions.pop({ key: route.key }));
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),
this.setState((state) => ({
routes: state.routes.filter((r) => r.key !== route.key),
openingRouteKeys: state.openingRouteKeys.filter(
key => key !== route.key
(key) => key !== route.key
),
closingRouteKeys: state.closingRouteKeys.filter(
key => key !== route.key
(key) => key !== route.key
),
}));
}
@@ -404,9 +440,9 @@ export default class StackView extends React.Component<Props, State> {
<GestureHandlerWrapper style={styles.container}>
<SafeAreaProviderCompat>
<SafeAreaConsumer>
{insets => (
{(insets) => (
<KeyboardManager enabled={keyboardHandlingEnabled !== false}>
{props => (
{(props) => (
<CardStack
mode={mode}
insets={insets as EdgeInsets}

View File

@@ -1,7 +0,0 @@
declare module '*.png' {
import { ImageSourcePropType } from 'react-native';
declare const value: ImageSourcePropType;
export default value;
}

View File

@@ -3,6 +3,92 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.8.10](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.9...react-navigation-tabs@2.8.10) (2020-03-31)
**Note:** Version bump only for package react-navigation-tabs
## [2.8.9](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.7...react-navigation-tabs@2.8.9) (2020-03-28)
**Note:** Version bump only for package react-navigation-tabs
## [2.8.8](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.7...react-navigation-tabs@2.8.8) (2020-03-27)
**Note:** Version bump only for package react-navigation-tabs
## [2.8.7](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.6...react-navigation-tabs@2.8.7) (2020-03-22)
**Note:** Version bump only for package react-navigation-tabs
## [2.8.6](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.5...react-navigation-tabs@2.8.6) (2020-03-20)
**Note:** Version bump only for package react-navigation-tabs
## [2.8.5](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.4...react-navigation-tabs@2.8.5) (2020-03-19)
### Bug Fixes
* don't use react-native-screens on web ([44c3900](https://github.com/react-navigation/tabs/commit/44c390075f7b76664e09fd9a1a7926645133ebec)), closes [#7485](https://github.com/react-navigation/tabs/issues/7485)
## [2.8.4](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.3...react-navigation-tabs@2.8.4) (2020-03-16)
### Bug Fixes
* downgrade react-native-safe-area-view ([cfc1bac](https://github.com/react-navigation/tabs/commit/cfc1bac4e153db4a4ba3f2a9033f77b53367fcbc)), closes [#7813](https://github.com/react-navigation/tabs/issues/7813)
## [2.8.3](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.2...react-navigation-tabs@2.8.3) (2020-03-16)
**Note:** Version bump only for package react-navigation-tabs
## [2.8.2](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.1...react-navigation-tabs@2.8.2) (2020-02-26)
**Note:** Version bump only for package react-navigation-tabs
## [2.8.1](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.0...react-navigation-tabs@2.8.1) (2020-02-24)
**Note:** Version bump only for package react-navigation-tabs
# 2.8.0 (2020-02-24)

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-tabs",
"version": "2.8.0",
"version": "2.8.10",
"description": "Tab Navigation components for React Navigation",
"main": "lib/commonjs/index.js",
"module": "lib/module/index.js",
@@ -42,29 +42,29 @@
"dependencies": {
"hoist-non-react-statics": "^3.3.2",
"react-lifecycles-compat": "^3.0.4",
"react-native-safe-area-view": "^0.14.6",
"react-native-safe-area-view": "^0.14.8",
"react-native-tab-view": "^2.11.0"
},
"devDependencies": {
"@react-native-community/bob": "^0.9.7",
"@react-native-community/bob": "^0.10.0",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/react": "^16.9.22",
"@types/react-native": "^0.61.17",
"@types/react": "^16.9.23",
"@types/react-native": "^0.61.22",
"del-cli": "^3.0.0",
"react": "~16.9.0",
"react-native": "~0.61.5",
"react-native-gesture-handler": "^1.6.0",
"react-native-reanimated": "^1.2.0",
"react-native-tab-view": "^2.13.0",
"react-navigation": "^4.2.0",
"typescript": "~3.7.5"
"react-navigation": "^4.3.6",
"typescript": "~3.8.3"
},
"peerDependencies": {
"react": "*",
"react-native": "*",
"react-native-gesture-handler": "^1.0.0",
"react-native-reanimated": "^1.0.0-alpha",
"react-native-screens": "^1.0.0 || ^1.0.0-alpha",
"react-native-screens": ">=1.0.0 || >= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0",
"react-navigation": "^4.1.1"
},
"@react-native-community/bob": {

View File

@@ -131,7 +131,9 @@ class TabNavigationView extends React.PureComponent<Props, State> {
_jumpTo = (key: string) => {
const { navigation, onIndexChange } = this.props;
const index = navigation.state.routes.findIndex(route => route.key === key);
const index = navigation.state.routes.findIndex(
(route) => route.key === key
);
onIndexChange(index);
};

View File

@@ -134,12 +134,12 @@ class MaterialTabView extends React.PureComponent<Props> {
renderTabBar={this.renderTabBar}
renderLazyPlaceholder={
lazyPlaceholderComponent !== undefined
? props => React.createElement(lazyPlaceholderComponent, props)
? (props) => React.createElement(lazyPlaceholderComponent, props)
: undefined
}
renderPager={
pagerComponent !== undefined
? props => React.createElement(pagerComponent, props)
? (props) => React.createElement(pagerComponent, props)
: undefined
}
/>

View File

@@ -1,6 +1,5 @@
import * as React from 'react';
import { Platform, StyleSheet, View } from 'react-native';
import { Screen, screensEnabled } from 'react-native-screens';
type Props = {
@@ -9,12 +8,14 @@ type Props = {
style?: any;
};
const FAR_FAR_AWAY = 3000; // this should be big enough to move the whole view out of its container
const FAR_FAR_AWAY = 30000; // this should be big enough to move the whole view out of its container
export default class ResourceSavingScene extends React.Component<Props> {
render() {
if (screensEnabled?.()) {
// react-native-screens is buggy on web
if (screensEnabled?.() && Platform.OS !== 'web') {
const { isVisible, ...rest } = this.props;
// @ts-ignore
return <Screen active={isVisible ? 1 : 0} {...rest} />;
}
@@ -23,7 +24,13 @@ export default class ResourceSavingScene extends React.Component<Props> {
return (
<View
style={[styles.container, style, { opacity: isVisible ? 1 : 0 }]}
style={[
styles.container,
Platform.OS === 'web'
? { display: isVisible ? 'flex' : 'none' }
: null,
style,
]}
collapsable={false}
removeClippedSubviews={
// On iOS, set removeClippedSubviews to true only when not focused

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