Compare commits

...

20 Commits

Author SHA1 Message Date
Satyajit Sahoo
1f5000e86b chore: publish
- react-navigation-animated-switch@0.6.2
 - @react-navigation/core@3.7.8
 - react-navigation-drawer@2.5.2
 - react-navigation-material-bottom-tabs@2.3.2
 - @react-navigation/native@3.8.2
 - react-navigation@4.4.2
 - react-navigation-stack@2.8.4
 - react-navigation-tabs@2.9.2
2020-10-02 09:16:08 +02:00
oltrep
6390aacd07 fix: NavigationEvents subscribe events on new nav state (#8920)
If the navigation prop changes, the NavigationEvents component was not
subscribing to the new value. These changes fix this problem and add a
test to verify that behavior.
2020-10-01 16:00:49 +02:00
oltrep
20e2625f35 test: fix usage of routerTestHelper back action (#8844)
Found this case were the key gets set to `{ key: null }` instead of just being `null`. Should I just call `back()` at this point? Because I don't think there is a difference between a `null` vs `undefined` key
2020-09-25 05:44:02 +02:00
Satyajit Sahoo
162e225fa4 chore: publish
- react-navigation-animated-switch@0.6.1
 - @react-navigation/core@3.7.7
 - react-navigation-drawer@2.5.1
 - react-navigation-material-bottom-tabs@2.3.1
 - @react-navigation/native@3.8.1
 - react-navigation@4.4.1
 - react-navigation-stack@2.8.3
 - react-navigation-tabs@2.9.1
2020-09-24 13:18:11 +02:00
Satyajit Sahoo
ce9991ffff chore: sync latest stack 2020-09-24 13:13:12 +02:00
Satyajit Sahoo
3bb21e256f chore: upgrade depenendecies 2020-09-24 01:06:00 +02:00
oltrep
f1a06e2f92 refactor: improve error message of StateUtils.jumpTo (#8831) 2020-09-22 11:59:20 +02:00
oltrep
c0763fca16 refactor: remove undefined assignment in switch router (#8770)
`undefined` should be left to the javascript compiler.

Originally, I opened this PR because I thought there was an inconsistency between the stack and switch routers, in the way they handle params. Turns out it's all fine, but I found this case where `undefined` is assigned to a variable and I thought I could submit a small refactor for it.
2020-09-21 15:20:36 +02:00
oltrep
62da341b67 test: move assertions into their own tests (#8703)
This change makes the tests in NavigationStateUtils more specific by splitting tests with multiple assertions.

I also grouped the tests into describe blocks by the function they test. Let me know if that's something you want or not, I can revert it if needed.

This is my first contribution, but I plan to make more of these which would mostly consists on improving tests.
2020-09-10 13:34:55 +02:00
otrepanier
09a10faa44 test: fix and enable skipped test 2020-09-10 13:33:15 +02:00
otrepanier
b9ecbd222d refactor: rename reduce accumulator variable 2020-09-10 13:32:34 +02:00
otrepanier
a57e47786c test: add KeyGenerator tests 2020-09-02 16:01:41 +02:00
otrepanier
424923019a test: document set params action for routers
These tests document the behavior of using set params action for the
different routers. Even if the action is named `SET_PARAMS`, it merges
the params with the previous one: this behavior matches the `setState`
method used in React's components.
2020-09-02 15:55:31 +02:00
Adrian Carolli
06a69f1bfd fix: don't use deprecated currentlyFocusedField (#8684)
currentlyFocusedField => currentlyFocusedInput

Fixes #8457 for 4.x because `createKeyboardAwareNavigator.js` is not inside the `main` branch.

Fixed in main here: 35d6b9e3a4
2020-09-02 15:54:45 +02:00
otrepanier
1fa2edd9f2 test: add test case for StateUtils.pop
From the current implementation of `NavigationStateUtils.pop`, a new
test case was added to document the behavior when popping when the index
is at the first route and there are multiple routes.
2020-09-02 15:42:59 +02:00
Satyajit Sahoo
fcd7d83c4c chore: sync latest stack 2020-07-11 00:05:15 +02:00
Satyajit Sahoo
4f7983134b chore: publish
- react-navigation-stack@2.8.2
2020-06-25 11:52:12 +02:00
Satyajit Sahoo
46b797dd29 fix: sync latest stack 2020-06-25 11:50:39 +02:00
Satyajit Sahoo
1a6f4a581f chore: publish
- react-navigation-stack@2.8.1
2020-06-25 03:02:46 +02:00
Satyajit Sahoo
2e7f4a6d10 fix: pop with correct key from nested stack 2020-06-25 03:01:47 +02:00
99 changed files with 6579 additions and 5343 deletions

View File

@@ -14,5 +14,8 @@
"react-native-screens"
]
},
"env": { "browser": true, "node": true }
"env": { "browser": true, "node": true },
"rules": {
"import/named": "off"
}
}

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { registerRootComponent } from 'expo';
import {
Animated,

View File

@@ -7,7 +7,6 @@
"slug": "react-navigation-4-example",
"description": "Demo app to showcase various functionality of React Navigation",
"privacy": "public",
"sdkVersion": "36.0.0",
"platforms": [
"ios",
"android",

View File

@@ -8,29 +8,29 @@
"ios": "expo start --ios"
},
"dependencies": {
"@babel/runtime": "^7.8.7",
"@expo/vector-icons": "^10.0.6",
"@react-native-community/masked-view": "0.1.7",
"expo": "^36.0.0",
"expo-asset": "~8.0.0",
"expo-barcode-scanner": "^8.0.0",
"expo-blur": "~8.0.0",
"expo-constants": "~8.0.0",
"@babel/runtime": "^7.11.0",
"@expo/vector-icons": "^10.0.0",
"@react-native-community/masked-view": "0.1.10",
"expo": "^39.0.0",
"expo-asset": "~8.2.0",
"expo-barcode-scanner": "~9.0.0",
"expo-blur": "~8.2.0",
"expo-constants": "~9.2.0",
"hoist-non-react-statics": "^3.3.1",
"react": "16.9.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-36.0.1.tar.gz",
"react-native-gesture-handler": "~1.6.0",
"react": "~16.13.1",
"react-native": "~0.63.2",
"react-native-gesture-handler": "~1.7.0",
"react-native-iphone-x-helper": "^1.2.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.3.0",
"react-native-webview": "8.1.2",
"react-native-maps": "0.27.1",
"react-native-paper": "^4.0.1",
"react-native-reanimated": "~1.13.0",
"react-native-safe-area-context": "3.1.4",
"react-native-screens": "~2.10.1",
"react-native-webview": "10.7.0",
"react-navigation-header-buttons": "^3.0.5"
},
"devDependencies": {
"babel-plugin-module-resolver": "^4.0.0",
"expo-cli": "^3.13.8"
"expo-cli": "^3.27.8"
}
}

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import {
Alert,
TouchableOpacity,

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import { BorderlessButton } from 'react-native-gesture-handler';
import {

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { View, Text, StyleSheet, Dimensions } from 'react-native';
import { NavigationStackScreenComponent } from 'react-navigation-stack';

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { ScrollView, StyleProp, TextStyle } from 'react-native';
import { MaterialIcons } from '@expo/vector-icons';
import {

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { Button, ScrollView, View, Text } from 'react-native';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import {

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import {
Button,
Dimensions,

View File

@@ -1,6 +1,5 @@
import React from 'react';
import * as React from 'react';
import {
AccessibilityStates,
Platform,
StyleSheet,
Text,
@@ -88,11 +87,9 @@ export default class Button extends React.Component<ButtonProps> {
buttonStyles.push({ backgroundColor: color });
}
}
const accessibilityStates: AccessibilityStates[] = [];
if (disabled) {
buttonStyles.push(styles.buttonDisabled);
textStyles.push(styles.textDisabled);
accessibilityStates.push('disabled');
}
const formattedTitle =
Platform.OS === 'android' ? title.toUpperCase() : title;
@@ -100,7 +97,7 @@ export default class Button extends React.Component<ButtonProps> {
<TouchableOpacity
accessibilityLabel={accessibilityLabel}
accessibilityRole="button"
accessibilityStates={accessibilityStates}
accessibilityState={{ disabled }}
testID={testID}
disabled={disabled}
onPress={onPress}

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { Platform, StyleSheet, View } from 'react-native';
import BaseButton, { ButtonProps } from './Button';

View File

@@ -23,13 +23,13 @@ class NavigationAwareScrollViewBase extends React.Component<{
componentDidMount() {
this.subscription = this.props.navigation.addListener('refocus', () => {
if (this.props.navigation.isFocused()) {
this.root.current && this.root.current.scrollTo({ x: 0, y: 0 });
this.root.current?.scrollTo({ x: 0, y: 0 });
}
});
}
componentWillUnmount() {
this.subscription && this.subscription.remove();
this.subscription?.remove();
}
setNativeProps(props: ScrollViewProperties) {

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { Button, ScrollView, StyleSheet, View } from 'react-native';
import {
ThemeColors,

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { Text, View } from 'react-native';
import { Themed } from 'react-navigation';
import {

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { Text } from 'react-native';
import {
Themed,

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { ScrollView, StatusBar } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import {

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import {
ScrollView,
StatusBar,

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { Button, ScrollView } from 'react-native';
import { Themed, SafeAreaView } from 'react-navigation';
import {

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { MaterialIcons } from '@expo/vector-icons';
import { createDrawerNavigator } from 'react-navigation-drawer';
import SimpleTabs from './SimpleTabs';

View File

@@ -25,28 +25,28 @@
"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.7",
"@babel/preset-react": "^7.8.3",
"@babel/preset-typescript": "^7.8.3",
"@babel/runtime": "^7.8.7",
"@commitlint/config-conventional": "^8.3.4",
"@types/jest": "^25.1.4",
"babel-jest": "^25.2.3",
"commitlint": "^8.3.5",
"core-js": "^3.6.4",
"eslint": "^7.0.0",
"eslint-config-satya164": "^3.1.7",
"husky": "^4.2.1",
"jest": "^25.1.0",
"lerna": "^3.20.2",
"prettier": "^2.0.5",
"typescript": "^3.9.5"
"@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/plugin-proposal-optional-chaining": "^7.11.0",
"@babel/preset-env": "^7.11.0",
"@babel/preset-react": "^7.10.4",
"@babel/preset-typescript": "^7.10.4",
"@babel/runtime": "^7.11.0",
"@commitlint/config-conventional": "^11.0.0",
"@types/jest": "^26.0.8",
"babel-jest": "^26.2.2",
"commitlint": "^11.0.0",
"core-js": "^3.6.5",
"eslint": "^7.6.0",
"eslint-config-satya164": "^3.1.8",
"husky": "^4.2.5",
"jest": "^26.2.2",
"lerna": "^3.22.1",
"prettier": "^2.1.2",
"typescript": "^4.0.3"
},
"resolutions": {
"react": "~16.9.0",
"react-native": "~0.61.5"
"react": "~16.13.1",
"react-native": "~0.63.2"
},
"husky": {
"hooks": {

View File

@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.6.2](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.6.1...react-navigation-animated-switch@0.6.2) (2020-10-02)
**Note:** Version bump only for package react-navigation-animated-switch
## [0.6.1](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.6.0...react-navigation-animated-switch@0.6.1) (2020-09-24)
**Note:** Version bump only for package react-navigation-animated-switch
# [0.6.0](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.12...react-navigation-animated-switch@0.6.0) (2020-06-25)

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-animated-switch",
"version": "0.6.0",
"version": "0.6.2",
"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.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.4.0",
"typescript": "^3.9.5"
"@react-native-community/bob": "0.16.2",
"@types/react": "16.9.49",
"@types/react-native": "0.63.20",
"del-cli": "^3.0.1",
"react": "~16.13.1",
"react-native": "~0.63.2",
"react-native-reanimated": "~1.13.0",
"react-navigation": "^4.4.2",
"typescript": "^4.0.3"
},
"peerDependencies": {
"react": "*",

View File

@@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.7.8](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.7...@react-navigation/core@3.7.8) (2020-10-02)
### Bug Fixes
* NavigationEvents subscribe events on new nav state ([#8920](https://github.com/react-navigation/react-navigation-core/issues/8920)) ([6390aac](https://github.com/react-navigation/react-navigation-core/commit/6390aacd07fd647d925dfec842a766c8aad5272f))
## [3.7.7](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.6...@react-navigation/core@3.7.7) (2020-09-24)
**Note:** Version bump only for package @react-navigation/core
## [3.7.6](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.5...@react-navigation/core@3.7.6) (2020-04-30)

View File

@@ -1,6 +1,6 @@
{
"name": "@react-navigation/core",
"version": "3.7.6",
"version": "3.7.8",
"description": "Core utilities for the react-navigation framework",
"main": "lib/commonjs/index.js",
"react-native": "lib/module/index.js",
@@ -37,16 +37,16 @@
"dependencies": {
"hoist-non-react-statics": "^3.3.2",
"path-to-regexp": "^1.8.0",
"query-string": "^6.11.1",
"query-string": "^6.13.1",
"react-is": "^16.13.0"
},
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"del-cli": "^3.0.0",
"react": "~16.9.0",
"react-native": "~0.61.5",
"react-native-testing-library": "^1.12.0",
"react-test-renderer": "^16.9.2"
"@react-native-community/bob": "^0.16.2",
"del-cli": "^3.0.1",
"react": "~16.13.1",
"react-native": "~0.63.2",
"react-native-testing-library": "^6.0.0",
"react-test-renderer": "~16.13.1"
},
"peerDependencies": {
"react": "*"

View File

@@ -93,6 +93,7 @@ const StateUtils = {
*/
jumpTo(state, key) {
const index = StateUtils.indexOf(state, key);
invariant(index !== -1, 'attempt to jump to unknown key "%s"', key);
return StateUtils.jumpToIndex(state, index);
},

View File

@@ -1,6 +1,6 @@
/* eslint-disable react/sort-comp */
import React from 'react';
import * as React from 'react';
import { NavigationActions, getNavigation, NavigationProvider } from '../index';
export default function createNavigationContainer(Component) {

View File

@@ -3,32 +3,51 @@ import NavigationStateUtils from '../StateUtils';
const routeName = 'Anything';
describe('StateUtils', () => {
// Getters
it('gets route', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.get(state, 'a')).toEqual({
key: 'a',
routeName,
describe('get', () => {
it('gets route', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.get(state, 'a')).toEqual({
key: 'a',
routeName,
});
});
it('returns null when getting an unknown route', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.get(state, 'b')).toBe(null);
});
expect(NavigationStateUtils.get(state, 'b')).toBe(null);
});
it('gets route index', () => {
const state = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.indexOf(state, 'a')).toBe(0);
expect(NavigationStateUtils.indexOf(state, 'b')).toBe(1);
expect(NavigationStateUtils.indexOf(state, 'c')).toBe(-1);
describe('indexOf', () => {
it('gets route index', () => {
const state = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.indexOf(state, 'a')).toBe(0);
expect(NavigationStateUtils.indexOf(state, 'b')).toBe(1);
});
it('returns -1 when getting an unknown route index', () => {
const state = {
index: 1,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.indexOf(state, 'b')).toBe(-1);
});
});
it('has a route', () => {
@@ -44,303 +63,390 @@ describe('StateUtils', () => {
expect(NavigationStateUtils.has(state, 'c')).toBe(false);
});
// Push
it('pushes a route', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
const newState = {
index: 1,
isTransitioning: false,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
};
expect(NavigationStateUtils.push(state, { key: 'b', routeName })).toEqual(
newState
);
});
it('does not push duplicated route', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(() =>
NavigationStateUtils.push(state, { key: 'a', routeName })
).toThrow('should not push route with duplicated key a');
});
// Pop
it('pops route', () => {
const state = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.pop(state)).toEqual(newState);
});
it('does not pop route if not applicable', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.pop(state)).toBe(state);
});
// Jump
it('jumps to new index', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.jumpToIndex(state, 0)).toBe(state);
expect(NavigationStateUtils.jumpToIndex(state, 1)).toEqual(newState);
});
it('throws if jumps to invalid index', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(() => NavigationStateUtils.jumpToIndex(state, 2)).toThrow(
'invalid index 2 to jump to'
);
});
it('jumps to new key', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.jumpTo(state, 'a')).toBe(state);
expect(NavigationStateUtils.jumpTo(state, 'b')).toEqual(newState);
});
it('throws if jumps to invalid key', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(() => NavigationStateUtils.jumpTo(state, 'c')).toThrow(
'invalid index -1 to jump to'
);
});
it('move backwards', () => {
const state = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.back(state)).toEqual(newState);
expect(NavigationStateUtils.back(newState)).toBe(newState);
});
it('move forwards', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.forward(state)).toEqual(newState);
expect(NavigationStateUtils.forward(newState)).toBe(newState);
});
// Replace
it('Replaces by key', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'c', routeName },
],
isTransitioning: false,
};
expect(
NavigationStateUtils.replaceAt(state, 'b', { key: 'c', routeName })
).toEqual(newState);
});
it('Replaces by index', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'c', routeName },
],
isTransitioning: false,
};
expect(
NavigationStateUtils.replaceAtIndex(state, 1, { key: 'c', routeName })
).toEqual(newState);
});
it('Returns the state with updated index if route is unchanged but index changes', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(
NavigationStateUtils.replaceAtIndex(state, 1, state.routes[1])
).toEqual({ ...state, index: 1 });
});
// Reset
it('Resets routes', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [
{ key: 'x', routeName },
{ key: 'y', routeName },
],
isTransitioning: false,
};
expect(
NavigationStateUtils.reset(state, [
{ key: 'x', routeName },
{ key: 'y', routeName },
])
).toEqual(newState);
expect(() => {
NavigationStateUtils.reset(state, []);
}).toThrow('invalid routes to replace');
});
it('Resets routes with index', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 0,
routes: [
{ key: 'x', routeName },
{ key: 'y', routeName },
],
isTransitioning: false,
};
expect(
NavigationStateUtils.reset(
state,
[
{ key: 'x', routeName },
{ key: 'y', routeName },
describe('push', () => {
it('pushes a route', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
const newState = {
index: 1,
isTransitioning: false,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
0
)
).toEqual(newState);
expect(() => {
NavigationStateUtils.reset(
state,
[
{ key: 'x', routeName },
{ key: 'y', routeName },
],
100
};
expect(NavigationStateUtils.push(state, { key: 'b', routeName })).toEqual(
newState
);
}).toThrow('invalid index 100 to reset');
});
it('does not push duplicated route', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(() =>
NavigationStateUtils.push(state, { key: 'a', routeName })
).toThrow('should not push route with duplicated key a');
});
});
describe('pop', () => {
it('pops route', () => {
const state = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.pop(state)).toEqual(newState);
});
it('does not pop route if not applicable with single route config', () => {
const state = {
index: 0,
routes: [{ key: 'a', routeName }],
isTransitioning: false,
};
expect(NavigationStateUtils.pop(state)).toBe(state);
});
it('does not pop route if not applicable with multiple route config', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.pop(state)).toBe(state);
});
});
describe('jumpToIndex', () => {
it('jumps to new index', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.jumpToIndex(state, 0)).toBe(state);
expect(NavigationStateUtils.jumpToIndex(state, 1)).toEqual(newState);
});
it('throws if jumps to invalid index', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(() => NavigationStateUtils.jumpToIndex(state, 2)).toThrow(
'invalid index 2 to jump to'
);
});
});
describe('jumpTo', () => {
it('jumps to the current key', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.jumpTo(state, 'a')).toBe(state);
});
it('jumps to new key', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.jumpTo(state, 'b')).toEqual(newState);
});
it('throws if jumps to invalid key', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(() => NavigationStateUtils.jumpTo(state, 'c')).toThrow(
'attempt to jump to unknown key "c"'
);
});
});
describe('back', () => {
it('move backwards', () => {
const state = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.back(state)).toEqual(newState);
});
it('does not move backwards when the active route is the first', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.back(state)).toBe(state);
});
});
describe('forward', () => {
it('move forwards', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.forward(state)).toEqual(newState);
});
it('does not move forward when active route is already the top-most', () => {
const state = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(NavigationStateUtils.forward(state)).toEqual(state);
});
});
describe('replace', () => {
it('Replaces by key', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'c', routeName },
],
isTransitioning: false,
};
expect(
NavigationStateUtils.replaceAt(state, 'b', { key: 'c', routeName })
).toEqual(newState);
});
it('Replaces by index', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [
{ key: 'a', routeName },
{ key: 'c', routeName },
],
isTransitioning: false,
};
expect(
NavigationStateUtils.replaceAtIndex(state, 1, { key: 'c', routeName })
).toEqual(newState);
});
it('Returns the state with updated index if route is unchanged but index changes', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(
NavigationStateUtils.replaceAtIndex(state, 1, state.routes[1])
).toEqual({ ...state, index: 1 });
});
});
describe('reset', () => {
it('Resets routes', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 1,
routes: [
{ key: 'x', routeName },
{ key: 'y', routeName },
],
isTransitioning: false,
};
expect(
NavigationStateUtils.reset(state, [
{ key: 'x', routeName },
{ key: 'y', routeName },
])
).toEqual(newState);
});
it('throws when attempting to set empty state', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(() => {
NavigationStateUtils.reset(state, []);
}).toThrow('invalid routes to replace');
});
it('Resets routes with index', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
const newState = {
index: 0,
routes: [
{ key: 'x', routeName },
{ key: 'y', routeName },
],
isTransitioning: false,
};
expect(
NavigationStateUtils.reset(
state,
[
{ key: 'x', routeName },
{ key: 'y', routeName },
],
0
)
).toEqual(newState);
expect(() => {
NavigationStateUtils.reset(
state,
[
{ key: 'x', routeName },
{ key: 'y', routeName },
],
100
);
}).toThrow('invalid index 100 to reset');
});
it('throws when attempting to set an out of range route index', () => {
const state = {
index: 0,
routes: [
{ key: 'a', routeName },
{ key: 'b', routeName },
],
isTransitioning: false,
};
expect(() => {
NavigationStateUtils.reset(
state,
[
{ key: 'x', routeName },
{ key: 'y', routeName },
],
100
);
}).toThrow('invalid index 100 to reset');
});
});
});

View File

@@ -1,4 +1,5 @@
import getNavigation from '../getNavigation';
import * as NavigationActions from '../NavigationActions';
it('getNavigation provides default action helpers', () => {
const router = {
@@ -22,12 +23,11 @@ it('getNavigation provides default action helpers', () => {
topNav.navigate('GreatRoute');
expect(dispatch.mock.calls.length).toBe(1);
expect(dispatch.mock.calls[0][0].type).toBe('Navigation/NAVIGATE');
expect(dispatch.mock.calls[0][0].type).toBe(NavigationActions.NAVIGATE);
expect(dispatch.mock.calls[0][0].routeName).toBe('GreatRoute');
});
// eslint-disable-next-line jest/no-disabled-tests
it.skip('getNavigation provides router action helpers', () => {
it('getNavigation provides router action helpers', () => {
const router = {
getActionCreators: () => ({
foo: (bar) => ({ type: 'FooBarAction', bar }),
@@ -39,7 +39,8 @@ it.skip('getNavigation provides router action helpers', () => {
const dispatch = jest.fn();
const topNav = getNavigation(
let topNav;
topNav = getNavigation(
router,
{},
dispatch,

View File

@@ -1,5 +1,5 @@
export default function getChildRouter(router: any, routeName: string) {
if (router.childRouters && router.childRouters[routeName]) {
if (router.childRouters?.[routeName]) {
return router.childRouters[routeName];
}

View File

@@ -36,7 +36,7 @@ function createNavigator(NavigatorView, router, navigationConfig) {
);
}
const descriptors = routes.reduce((acc, route) => {
const descriptors = routes.reduce((descriptors, route) => {
if (
prevDescriptors &&
prevDescriptors[route.key] &&
@@ -44,8 +44,8 @@ function createNavigator(NavigatorView, router, navigationConfig) {
screenProps === currentState.screenProps &&
currentState.themeContext === currentState.theme
) {
acc[route.key] = prevDescriptors[route.key];
return acc;
descriptors[route.key] = prevDescriptors[route.key];
return descriptors;
}
const getComponent = router.getComponentForRouteName.bind(
null,
@@ -57,14 +57,14 @@ function createNavigator(NavigatorView, router, navigationConfig) {
screenProps,
currentState.themeContext
);
acc[route.key] = {
descriptors[route.key] = {
key: route.key,
getComponent,
options,
state: route,
navigation: childNavigation,
};
return acc;
return descriptors;
}, {});
return { descriptors, screenProps, theme: state.themeContext };

View File

@@ -19,7 +19,7 @@ export default (routeConfigs, config = {}) => {
const getCustomActionCreators =
config.getCustomActionCreators || defaultActionCreators;
const initialRouteParams = config.initialRouteParams;
const { initialRouteParams } = config;
const initialRouteName = config.initialRouteName || order[0];
const backBehavior = config.backBehavior || 'none';
const resetOnBlur = config.hasOwnProperty('resetOnBlur')
@@ -59,7 +59,7 @@ export default (routeConfigs, config = {}) => {
function resetChildRoute(routeName) {
let initialParams =
routeName === initialRouteName ? initialRouteParams : undefined;
routeName === initialRouteName ? initialRouteParams : null;
// note(brentvatne): merging initialRouteParams *on top* of default params
// on the route seems incorrect but it's consistent with existing behavior
// in stackrouter
@@ -71,13 +71,13 @@ export default (routeConfigs, config = {}) => {
...childRouter.getStateForAction(childAction),
key: routeName,
routeName,
params,
...(params ? { params } : {}),
};
}
return {
key: routeName,
routeName,
params,
...(params ? { params } : {}),
};
}

View File

@@ -0,0 +1,12 @@
import { _TESTING_ONLY_normalize_keys, generateKey } from '../KeyGenerator';
it('should generate a new string key when called', () => {
_TESTING_ONLY_normalize_keys();
expect(generateKey()).toBe('id-0');
expect(generateKey()).toBe('id-1');
});
it('should generate unique string keys without being normalized', () => {
expect(generateKey()).not.toBe(generateKey());
});

View File

@@ -1,6 +1,6 @@
/* eslint no-shadow:0, react/no-multi-comp:0, react/display-name:0 */
import React from 'react';
import * as React from 'react';
import SwitchRouter from '../SwitchRouter';
import StackRouter from '../StackRouter';

View File

@@ -1,6 +1,6 @@
/* eslint react/no-multi-comp:0, react/display-name:0 */
import React from 'react';
import * as React from 'react';
import StackRouter from '../StackRouter';
import TabRouter from '../TabRouter';
@@ -114,6 +114,51 @@ Object.keys(ROUTERS).forEach((routerName) => {
);
expect(state0.routes[state0.index].params.foo).toEqual(42);
});
it('merges existing params when set params on existing state', () => {
const Screen = () => <div />;
const router = Router({
Foo: {
screen: Screen,
params: { a: 1 },
},
});
const key = 'Foo';
const state = router.getStateForAction({
type: NavigationActions.INIT,
key,
});
expect(state).toMatchObject({
index: 0,
routes: [{ key, params: { a: 1 } }],
});
const newState = router.getStateForAction(
NavigationActions.setParams({ key, params: { b: 2 } }),
state
);
expect(newState.routes[newState.index].params).toEqual({ a: 1, b: 2 });
});
it('merges params when setting params during init', () => {
const Screen = () => <div />;
const router = Router({
Foo: {
screen: Screen,
params: { a: 1 },
},
});
const newState = router.getStateForAction(
NavigationActions.setParams({ key: 'Foo', params: { b: 2 } })
);
expect(newState.routes[newState.index].params).toEqual({ a: 1, b: 2 });
});
});
});

View File

@@ -1,6 +1,6 @@
/* eslint no-shadow:0, react/no-multi-comp:0, react/display-name:0 */
import React from 'react';
import * as React from 'react';
import StackRouter from '../StackRouter';
import * as StackActions from '../StackActions';

View File

@@ -1,6 +1,6 @@
/* eslint react/display-name:0 */
import React from 'react';
import * as React from 'react';
import SwitchRouter from '../SwitchRouter';
import StackRouter from '../StackRouter';
import * as NavigationActions from '../../NavigationActions';
@@ -158,7 +158,7 @@ describe('SwitchRouter', () => {
expect(getSubState(1).routeName).toEqual('A');
// The back action should not switch to B. It should stay on A
back({ key: null });
back(null);
expect(getSubState(1).routeName).toEqual('A');
});

View File

@@ -1,6 +1,6 @@
/* eslint react/display-name:0 */
import React from 'react';
import * as React from 'react';
import TabRouter from '../TabRouter';
import * as NavigationActions from '../../NavigationActions';

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import validateRouteConfigMap from '../validateRouteConfigMap';
import StackRouter from '../StackRouter';

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import withNavigation from './withNavigation';
const EventNameToPropName = {
@@ -12,11 +12,28 @@ const EventNames = Object.keys(EventNameToPropName);
class NavigationEvents extends React.Component {
componentDidMount() {
this.subscriptions = {};
// 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)
this.subscribeAll();
}
componentDidUpdate(prevProps) {
if (this.props.navigation !== prevProps.navigation) {
this.removeAll();
this.subscribeAll();
}
}
componentWillUnmount() {
this.removeAll();
}
getPropListener = (eventName) => this.props[EventNameToPropName[eventName]];
subscribeAll() {
this.subscriptions = {};
EventNames.forEach((eventName) => {
this.subscriptions[eventName] = this.props.navigation.addListener(
eventName,
@@ -28,14 +45,12 @@ class NavigationEvents extends React.Component {
});
}
componentWillUnmount() {
removeAll() {
EventNames.forEach((eventName) => {
this.subscriptions[eventName].remove();
});
}
getPropListener = (eventName) => this.props[EventNameToPropName[eventName]];
render() {
return null;
}

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import NavigationContext from './NavigationContext';
export default class SceneView extends React.PureComponent {

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import SceneView from '../SceneView';
export default class SwitchView extends React.Component {

View File

@@ -1,10 +1,17 @@
import React from 'react';
import * as React from 'react';
import renderer from 'react-test-renderer';
import NavigationEvents from '../NavigationEvents';
import NavigationContext from '../NavigationContext';
const createPropListener = () => jest.fn();
const EVENT_TO_PROP_NAME = {
willFocus: 'onWillFocus',
didFocus: 'onDidFocus',
willBlur: 'onWillBlur',
didBlur: 'onDidBlur',
};
// An easy way to create the 4 listeners prop
const createEventListenersProp = () => ({
onWillFocus: createPropListener(),
@@ -122,6 +129,39 @@ describe('NavigationEvents', () => {
checkPropListenerIsCalled('didBlur', 'onDidBlur');
});
it('wires props listeners to latest navigation updates', () => {
const {
navigation,
NavigationListenersAPI,
} = createTestNavigationAndHelpers();
const {
navigation: nextNavigation,
NavigationListenersAPI: nextNavigationListenersAPI,
} = createTestNavigationAndHelpers();
const eventListenerProps = createEventListenersProp();
const component = renderer.create(
<NavigationEvents navigation={navigation} {...eventListenerProps} />
);
Object.entries(EVENT_TO_PROP_NAME).forEach(([eventName, propName]) => {
expect(eventListenerProps[propName]).toHaveBeenCalledTimes(0);
NavigationListenersAPI.call(eventName);
expect(eventListenerProps[propName]).toHaveBeenCalledTimes(1);
});
component.update(
<NavigationEvents navigation={nextNavigation} {...eventListenerProps} />
);
Object.entries(EVENT_TO_PROP_NAME).forEach(([eventName, propName]) => {
NavigationListenersAPI.call(eventName);
expect(eventListenerProps[propName]).toHaveBeenCalledTimes(1);
nextNavigationListenersAPI.call(eventName);
expect(eventListenerProps[propName]).toHaveBeenCalledTimes(2);
});
});
it('wire latest props listener to navigation listeners on updates (support closure/arrow functions update)', () => {
const {
navigation,

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import hoistStatics from 'hoist-non-react-statics';
import invariant from '../utils/invariant';
import NavigationContext from './NavigationContext';

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import hoistStatics from 'hoist-non-react-statics';
import withNavigation from './withNavigation';

View File

@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.5.2](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.5.1...react-navigation-drawer@2.5.2) (2020-10-02)
**Note:** Version bump only for package react-navigation-drawer
## [2.5.1](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.5.0...react-navigation-drawer@2.5.1) (2020-09-24)
**Note:** Version bump only for package react-navigation-drawer
# [2.5.0](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.13...react-navigation-drawer@2.5.0) (2020-06-25)

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-drawer",
"version": "2.5.0",
"version": "2.5.2",
"description": "Drawer navigator component for React Navigation",
"main": "lib/commonjs/index.js",
"react-native": "lib/module/index.js",
@@ -38,19 +38,19 @@
},
"homepage": "https://github.com/react-navigation/drawer#readme",
"devDependencies": {
"@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-community/bob": "^0.16.2",
"@types/react": "^16.9.44",
"@types/react-native": "^0.63.4",
"del-cli": "^3.0.1",
"react": "~16.13.1",
"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.3.0",
"react-native-testing-library": "^1.12.0",
"react-navigation": "^4.4.0",
"typescript": "^3.9.5"
"react-native": "~0.63.2",
"react-native-gesture-handler": "~1.7.0",
"react-native-reanimated": "~1.13.0",
"react-native-screens": "~2.10.1",
"react-native-testing-library": "^6.0.0",
"react-navigation": "^4.4.2",
"typescript": "^4.0.3"
},
"peerDependencies": {
"react": "*",

View File

@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.3.2](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.3.1...react-navigation-material-bottom-tabs@2.3.2) (2020-10-02)
**Note:** Version bump only for package react-navigation-material-bottom-tabs
## [2.3.1](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.3.0...react-navigation-material-bottom-tabs@2.3.1) (2020-09-24)
**Note:** Version bump only for package react-navigation-material-bottom-tabs
# [2.3.0](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.12...react-navigation-material-bottom-tabs@2.3.0) (2020-06-25)

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-material-bottom-tabs",
"version": "2.3.0",
"version": "2.3.2",
"description": "Material Bottom Tab Navigation component for React Navigation",
"main": "lib/commonjs/index.js",
"module": "lib/module/index.js",
@@ -38,18 +38,16 @@
},
"homepage": "https://github.com/react-navigation/react-navigation-material-bottom-tabs#readme",
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"@react-native-community/bob": "^0.16.2",
"@types/hoist-non-react-statics": "^3.3.1",
"@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.4.0",
"typescript": "^3.9.5"
"@types/react": "^16.9.44",
"@types/react-native": "^0.63.4",
"del-cli": "^3.0.1",
"react": "~16.13.1",
"react-native": "~0.63.2",
"react-native-paper": "^4.2.0",
"react-navigation": "^4.4.2",
"typescript": "^4.0.3"
},
"peerDependencies": {
"react": "*",

View File

@@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.8.2](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.8.1...@react-navigation/native@3.8.2) (2020-10-02)
**Note:** Version bump only for package @react-navigation/native
## [3.8.1](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.8.0...@react-navigation/native@3.8.1) (2020-09-24)
### Bug Fixes
* don't use deprecated currentlyFocusedField ([#8684](https://github.com/react-navigation/react-navigation-native/issues/8684)) ([06a69f1](https://github.com/react-navigation/react-navigation-native/commit/06a69f1bfd81fe06b784ff4e6da290fee0c6467d)), closes [#8457](https://github.com/react-navigation/react-navigation-native/issues/8457)
# [3.8.0](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.13...@react-navigation/native@3.8.0) (2020-06-25)

View File

@@ -1,6 +1,6 @@
{
"name": "@react-navigation/native",
"version": "3.8.0",
"version": "3.8.2",
"description": "React Native support for React Navigation",
"main": "lib/commonjs/index.js",
"react-native": "lib/module/index.js",
@@ -39,14 +39,14 @@
"react-native-safe-area-view": "^0.14.9"
},
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"@react-navigation/core": "^3.7.6",
"@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-test-renderer": "^16.9.2"
"@react-native-community/bob": "^0.16.2",
"@react-navigation/core": "^3.7.8",
"@types/react-test-renderer": "^16.9.3",
"del-cli": "^3.0.1",
"react": "~16.13.1",
"react-native": "~0.63.2",
"react-native-gesture-handler": "~1.7.0",
"react-test-renderer": "~16.13.1"
},
"@react-native-community/bob": {
"source": "src",

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { Platform, StyleSheet, View } from 'react-native';
import { SceneView } from '@react-navigation/core';

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import {
ScrollView,
Platform,
@@ -6,7 +6,6 @@ import {
SectionList,
RefreshControl,
} from 'react-native';
// eslint-disable-next-line import/named
import { ScrollView as GHScrollView } from 'react-native-gesture-handler';
import createNavigationAwareScrollable from './createNavigationAwareScrollable';
import invariant from './utils/invariant';

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { View } from 'react-native';
import renderer from 'react-test-renderer';

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { View } from 'react-native';
import renderer from 'react-test-renderer';
import withOrientation, { isOrientationLandscape } from '../withOrientation';

View File

@@ -1,6 +1,6 @@
/* eslint-disable react/sort-comp */
import React from 'react';
import * as React from 'react';
import { Linking, Platform, BackHandler } from 'react-native';
import {
NavigationActions,

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { TextInput } from 'react-native';
export default (Navigator, navigatorConfig) =>
@@ -8,7 +8,9 @@ export default (Navigator, navigatorConfig) =>
_previouslyFocusedTextInput = null;
_handleGestureBegin = () => {
this._previouslyFocusedTextInput = TextInput.State.currentlyFocusedField();
this._previouslyFocusedTextInput = TextInput.State.currentlyFocusedInput
? TextInput.State.currentlyFocusedInput()
: TextInput.State.currentlyFocusedField();
if (this._previouslyFocusedTextInput) {
TextInput.State.blurTextInput(this._previouslyFocusedTextInput);
}
@@ -32,7 +34,9 @@ export default (Navigator, navigatorConfig) =>
// in the case where the index did not change, I believe. We
// should revisit this after 2.0 release.
if (transitionProps.index !== prevTransitionProps.index) {
const currentField = TextInput.State.currentlyFocusedField();
const currentField = TextInput.State.currentlyFocusedInput
? TextInput.State.currentlyFocusedInput()
: TextInput.State.currentlyFocusedField();
if (currentField) {
TextInput.State.blurTextInput(currentField);
}

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import hoistStatics from 'hoist-non-react-statics';
import { withNavigation } from '@react-navigation/core';

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { Dimensions } from 'react-native';
import hoistNonReactStatic from 'hoist-non-react-statics';

View File

@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [4.4.2](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.4.1...react-navigation@4.4.2) (2020-10-02)
**Note:** Version bump only for package react-navigation
## [4.4.1](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.4.0...react-navigation@4.4.1) (2020-09-24)
**Note:** Version bump only for package react-navigation
# [4.4.0](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.9...react-navigation@4.4.0) (2020-06-25)

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation",
"version": "4.4.0",
"version": "4.4.2",
"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.7.6",
"@react-navigation/native": "^3.8.0"
"@react-navigation/core": "^3.7.8",
"@react-navigation/native": "^3.8.2"
},
"devDependencies": {
"@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.9.5"
"@types/react": "^16.9.44",
"@types/react-native": "^0.63.4",
"@types/react-test-renderer": "^16.9.3",
"react": "~16.13.1",
"react-native": "~0.63.2",
"react-test-renderer": "~16.13.1",
"typescript": "^4.0.3"
}
}

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { View } from 'react-native';
import renderer from 'react-test-renderer';

View File

@@ -812,6 +812,7 @@ export interface NavigationDescriptor<
getComponent: () => React.ComponentType;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export type NavigationView<Options, State, ScreenProps = unknown> =
| React.ComponentType<
{
@@ -897,11 +898,13 @@ export function withNavigation<
}
>;
// eslint-disable-next-line no-redeclare
export function withNavigation<P extends NavigationInjectedProps>(
Component: React.ComponentType<P>
): React.ComponentType<Omit<P, keyof NavigationInjectedProps>>;
// For backwards compatibility
// eslint-disable-next-line no-redeclare
export function withNavigation<T = {}, P = NavigationParams>(
Component: React.ComponentType<T & NavigationInjectedProps<P>>
): React.ComponentType<
@@ -929,11 +932,13 @@ export function withNavigationFocus<
}
>;
// eslint-disable-next-line no-redeclare
export function withNavigationFocus<P extends NavigationFocusInjectedProps>(
Component: React.ComponentType<P>
): React.ComponentType<Omit<P, keyof NavigationFocusInjectedProps>>;
// For backwards compatibility
// eslint-disable-next-line no-redeclare
export function withNavigationFocus<T = {}, P = NavigationParams>(
Component: React.ComponentType<T & NavigationFocusInjectedProps<P>>
): React.ComponentType<

View File

@@ -3,6 +3,44 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.8.4](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.8.3...react-navigation-stack@2.8.4) (2020-10-02)
**Note:** Version bump only for package react-navigation-stack
## [2.8.3](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.8.2...react-navigation-stack@2.8.3) (2020-09-24)
**Note:** Version bump only for package react-navigation-stack
## [2.8.2](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.8.1...react-navigation-stack@2.8.2) (2020-06-25)
### Bug Fixes
* sync latest stack ([46b797d](https://github.com/react-navigation/react-navigation-stack/commit/46b797dd297f789e3c92ff6f5c4432fedc891767))
## [2.8.1](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.8.0...react-navigation-stack@2.8.1) (2020-06-25)
### Bug Fixes
* pop with correct key from nested stack ([2e7f4a6](https://github.com/react-navigation/react-navigation-stack/commit/2e7f4a6d10a00930bd5c53ef6f4bf964c9638db5))
# [2.8.0](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.7.0...react-navigation-stack@2.8.0) (2020-06-25)

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-stack",
"version": "2.8.0",
"version": "2.8.4",
"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.10.0",
"@react-native-community/masked-view": "^0.1.7",
"@react-navigation/stack": "^5.6.0",
"@react-native-community/bob": "^0.16.2",
"@react-native-community/masked-view": "0.1.10",
"@react-navigation/stack": "^5.9.0",
"@types/color": "^3.0.1",
"@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.3.0",
"react-navigation": "^4.4.0",
"react-test-renderer": "~16.9.0",
"typescript": "^3.9.5"
"@types/react": "^16.9.44",
"@types/react-native": "^0.63.4",
"@types/react-test-renderer": "^16.9.3",
"del-cli": "^3.0.1",
"react": "~16.13.1",
"react-native": "~0.63.2",
"react-native-gesture-handler": "~1.7.0",
"react-native-safe-area-context": "3.1.4",
"react-native-screens": "~2.10.1",
"react-navigation": "^4.4.2",
"react-test-renderer": "~16.13.1",
"typescript": "^4.0.3"
},
"peerDependencies": {
"@react-native-community/masked-view": ">=0.1.0",

View File

@@ -1,5 +1,5 @@
diff -Naur ../../node_modules/@react-navigation/stack/src/index.tsx src/vendor/index.tsx
--- ../../node_modules/@react-navigation/stack/src/index.tsx 2020-06-24 23:11:52.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/index.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/index.tsx 2020-06-24 23:16:17.000000000 +0200
@@ -3,11 +3,6 @@
import * as TransitionSpecs from './TransitionConfigs/TransitionSpecs';
@@ -28,7 +28,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/index.tsx src/vendor/i
StackHeaderLeftButtonProps,
StackHeaderTitleProps,
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-06-24 23:11:52.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/navigators/createStackNavigator.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/navigators/createStackNavigator.tsx 1970-01-01 01:00:00.000000000 +0100
@@ -1,96 +0,0 @@
-import * as React from 'react';
@@ -128,7 +128,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStack
- typeof StackNavigator
->(StackNavigator);
diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/types.tsx
--- ../../node_modules/@react-navigation/stack/src/types.tsx 2020-06-24 23:11:52.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/types.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/types.tsx 2020-06-24 23:18:40.000000000 +0200
@@ -8,15 +8,28 @@
} from 'react-native';
@@ -262,7 +262,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
export type StackNavigationConfig = {
diff -Naur ../../node_modules/@react-navigation/stack/src/utils/PreviousSceneContext.tsx src/vendor/utils/PreviousSceneContext.tsx
--- ../../node_modules/@react-navigation/stack/src/utils/PreviousSceneContext.tsx 2020-06-24 23:11:52.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/utils/PreviousSceneContext.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/utils/PreviousSceneContext.tsx 2020-06-24 23:26:38.000000000 +0200
@@ -1,6 +1,5 @@
import * as React from 'react';
@@ -273,8 +273,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/utils/PreviousSceneCon
const PreviousSceneContext = React.createContext<
Scene<Route<string>> | undefined
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-06-24 23:11:52.000000000 +0200
+++ src/vendor/views/Header/Header.tsx 2020-06-24 23:33:25.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/views/Header/Header.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/views/Header/Header.tsx 2020-06-25 03:01:13.000000000 +0200
@@ -1,12 +1,15 @@
import * as React from 'react';
-import { StackActions } from '@react-navigation/native';
@@ -303,7 +303,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
let leftLabel;
@@ -38,17 +41,18 @@
@@ -38,17 +41,20 @@
? o.headerTitle
: o.title !== undefined
? o.title
@@ -319,16 +319,18 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
- ...StackActions.pop(),
- source: scene.route.key,
- });
+ if (navigation.isFirstRouteInParent()) {
+ // If we're the first route, we're going back to a parent navigator
+ // So we can't specify a key here
+ navigation.dispatch(StackActions.pop());
+ } else {
+ navigation.dispatch(StackActions.pop({ key: scene.route.key }));
+ const key = navigation.isFirstRouteInParent()
+ ? // If we're the first route, we're going back to a parent navigator
+ // So we need to get the key of the route we're nested in
+ navigation.dangerouslyGetParent()?.state.key
+ : scene.route.key;
+
+ if (key !== undefined) {
+ navigation.dispatch(StackActions.pop({ key }));
}
}, 50),
[navigation, scene.route.key]
@@ -64,7 +68,10 @@
@@ -64,7 +70,10 @@
leftLabel={leftLabel}
headerTitle={
typeof options.headerTitle !== 'function'
@@ -340,7 +342,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
: options.headerTitle
}
onGoBack={previous ? goBack : undefined}
@@ -72,3 +79,18 @@
@@ -72,3 +81,18 @@
/>
);
});
@@ -360,7 +362,7 @@ 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-06-24 23:11:52.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackButton.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/views/Header/HeaderBackButton.tsx 2020-06-24 23:21:49.000000000 +0200
@@ -8,9 +8,9 @@
StyleSheet,
@@ -374,7 +376,7 @@ 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-06-24 23:11:52.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackground.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/views/Header/HeaderBackground.tsx 2020-06-24 23:16:23.000000000 +0200
@@ -7,7 +7,7 @@
StyleProp,
@@ -386,7 +388,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBac
type Props = ViewProps & {
style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx src/vendor/views/Header/HeaderContainer.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx 2020-06-24 23:11:52.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/views/Header/HeaderContainer.tsx 2020-06-24 23:21:29.000000000 +0200
@@ -1,11 +1,6 @@
import * as React from 'react';
@@ -439,7 +441,7 @@ 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-06-24 23:11:52.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSegment.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/views/Header/HeaderSegment.tsx 2020-06-24 23:20:58.000000000 +0200
@@ -8,7 +8,7 @@
ViewStyle,
@@ -460,7 +462,7 @@ 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-06-24 23:11:52.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderTitle.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/views/Header/HeaderTitle.tsx 2020-06-24 23:16:23.000000000 +0200
@@ -1,6 +1,6 @@
import * as React from 'react';
@@ -471,7 +473,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderTit
type Props = Omit<React.ComponentProps<typeof Animated.Text>, 'key'> & {
tintColor?: string;
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx src/vendor/views/Stack/Card.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx 2020-06-24 23:11:52.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/views/Stack/Card.tsx 2020-06-24 23:16:23.000000000 +0200
@@ -146,7 +146,7 @@
@@ -483,7 +485,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx s
private animate = ({
closing,
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardContainer.tsx src/vendor/views/Stack/CardContainer.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardContainer.tsx 2020-06-24 23:11:52.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardContainer.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/views/Stack/CardContainer.tsx 2020-06-24 23:20:38.000000000 +0200
@@ -1,12 +1,13 @@
import * as React from 'react';
@@ -501,7 +503,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardContai
Layout,
StackHeaderMode,
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.tsx src/vendor/views/Stack/CardStack.tsx
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.tsx 2020-06-24 23:11:52.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/views/Stack/CardStack.tsx 2020-06-24 23:20:16.000000000 +0200
@@ -7,7 +7,7 @@
Platform,
@@ -521,7 +523,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.
StackDescriptorMap,
StackNavigationOptions,
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-06-24 23:11:52.000000000 +0200
--- ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.tsx 2020-06-25 01:58:50.000000000 +0200
+++ src/vendor/views/Stack/StackView.tsx 2020-06-24 23:19:46.000000000 +0200
@@ -2,11 +2,11 @@
import { View, Platform, StyleSheet } from 'react-native';

View File

@@ -99,6 +99,7 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
>
<Text
accessibilityRole="header"
aria-level="1"
numberOfLines={1}
onLayout={[Function]}
style={
@@ -152,6 +153,7 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
onClose={[Function]}
onGestureBegin={[Function]}
onGestureCanceled={[Function]}
onGestureEnd={[Function]}
onOpen={[Function]}
onTransitionStart={[Function]}
pointerEvents="box-none"
@@ -319,6 +321,7 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
onClose={[Function]}
onGestureBegin={[Function]}
onGestureCanceled={[Function]}
onGestureEnd={[Function]}
onOpen={[Function]}
onTransitionStart={[Function]}
pointerEvents="box-none"

View File

@@ -100,6 +100,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
>
<Text
accessibilityRole="header"
aria-level="1"
numberOfLines={1}
onLayout={[Function]}
style={
@@ -169,6 +170,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
onClose={[Function]}
onGestureBegin={[Function]}
onGestureCanceled={[Function]}
onGestureEnd={[Function]}
onOpen={[Function]}
onTransitionStart={[Function]}
pointerEvents="box-none"
@@ -404,6 +406,7 @@ exports[`StackNavigator renders successfully 1`] = `
>
<Text
accessibilityRole="header"
aria-level="1"
numberOfLines={1}
onLayout={[Function]}
style={
@@ -457,6 +460,7 @@ exports[`StackNavigator renders successfully 1`] = `
onClose={[Function]}
onGestureBegin={[Function]}
onGestureCanceled={[Function]}
onGestureEnd={[Function]}
onOpen={[Function]}
onTransitionStart={[Function]}
pointerEvents="box-none"

View File

@@ -21,6 +21,7 @@ import type {
} from 'react-navigation';
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export type Route<T extends string> = NavigationRoute;
export type NavigationStackState = NavigationState;
@@ -40,6 +41,18 @@ export type StackNavigationEventMap = {
* Event which fires when a transition animation ends.
*/
transitionEnd: { data: { closing: boolean } };
/**
* Event which fires when navigation gesture starts.
*/
gestureStart: { data: undefined };
/**
* Event which fires when navigation gesture is completed.
*/
gestureEnd: { data: undefined };
/**
* Event which fires when navigation gesture is canceled.
*/
gestureCancel: { data: undefined };
};
export type StackNavigationHelpers = NavigationProp<NavigationStackState>;
@@ -124,7 +137,7 @@ export type StackHeaderOptions = {
/**
* Style object for the title component.
*/
headerTitleStyle?: React.ComponentProps<typeof Animated.Text>['style'];
headerTitleStyle?: Animated.WithAnimatedValue<StyleProp<TextStyle>>;
/**
* 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`.
@@ -288,7 +301,9 @@ export type StackNavigationOptions = StackHeaderOptions &
/**
* Function that returns a React Element to display as a overlay for the card.
*/
cardOverlay?: (props: { style: StyleProp<ViewStyle> }) => React.ReactNode;
cardOverlay?: (props: {
style: Animated.WithAnimatedValue<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.
@@ -423,6 +438,10 @@ export type StackHeaderLeftButtonProps = {
* Accessibility label for the button for screen readers.
*/
accessibilityLabel?: string;
/**
* Style object for the button.
*/
style?: StyleProp<ViewStyle>;
};
export type StackHeaderTitleProps = {
@@ -445,7 +464,7 @@ export type StackHeaderTitleProps = {
/**
* Style object for the title element.
*/
style?: React.ComponentProps<typeof Animated.Text>['style'];
style?: Animated.WithAnimatedValue<StyleProp<TextStyle>>;
};
export type TransitionSpec =

View File

@@ -39,7 +39,6 @@ export default class BorderlessButton extends React.Component<Props> {
const { children, style, enabled, ...rest } = this.props;
return (
// @ts-expect-error: the Animated.createAnimatedComponent types don't work properly
<AnimatedBaseButton
{...rest}
onActiveStateChange={this.handleActiveStateChange}

View File

@@ -0,0 +1 @@
export * from './GestureHandlerNative';

View File

@@ -0,0 +1 @@
export * from './GestureHandlerNative';

View File

@@ -47,12 +47,14 @@ const Header = React.memo(function Header(props: StackHeaderProps) {
// eslint-disable-next-line react-hooks/exhaustive-deps
const goBack = React.useCallback(
debounce(() => {
if (navigation.isFirstRouteInParent()) {
// If we're the first route, we're going back to a parent navigator
// So we can't specify a key here
navigation.dispatch(StackActions.pop());
} else {
navigation.dispatch(StackActions.pop({ key: scene.route.key }));
const key = navigation.isFirstRouteInParent()
? // If we're the first route, we're going back to a parent navigator
// So we need to get the key of the route we're nested in
navigation.dangerouslyGetParent()?.state.key
: scene.route.key;
if (key !== undefined) {
navigation.dispatch(StackActions.pop({ key }));
}
}, 50),
[navigation, scene.route.key]

View File

@@ -8,9 +8,9 @@ import {
StyleSheet,
LayoutChangeEvent,
} from 'react-native';
import MaskedView from '../MaskedView';
import { TouchableItem } from '../TouchableItem';
import useTheme from '../../../utils/useTheme';
import MaskedView from '../MaskedView';
import TouchableItem from '../TouchableItem';
import type { StackHeaderLeftButtonProps } from '../../types';
type Props = StackHeaderLeftButtonProps;
@@ -30,6 +30,7 @@ export default function HeaderBackButton({
titleLayout,
truncatedLabel = 'Back',
accessibilityLabel = label && label !== 'Back' ? `${label}, back` : 'Go back',
style,
}: Props) {
const { dark, colors } = useTheme();
@@ -160,7 +161,7 @@ export default function HeaderBackButton({
delayPressIn={0}
onPress={disabled ? undefined : handlePress}
pressColor={pressColorAndroid}
style={[styles.container, disabled && styles.disabled]}
style={[styles.container, disabled && styles.disabled, style]}
hitSlop={Platform.select({
ios: undefined,
default: { top: 16, right: 16, bottom: 16, left: 16 },

View File

@@ -28,7 +28,6 @@ export type Props = {
scenes: (Scene<Route<string>> | undefined)[];
getPreviousScene: (props: {
route: Route<string>;
index: number;
}) => Scene<Route<string>> | undefined;
getFocusedRoute: () => Route<string>;
onContentHeightChange?: (props: {
@@ -75,10 +74,7 @@ export default function HeaderContainer({
const isFocused = focusedRoute.key === scene.route.key;
const previous =
getPreviousScene({
route: scene.route,
index: i,
}) ?? parentPreviousScene;
getPreviousScene({ route: scene.route }) ?? parentPreviousScene;
// If the screen is next to a headerless screen, we need to make the header appear static
// This makes the header look like it's moving with the screen

View File

@@ -0,0 +1,170 @@
import * as React from 'react';
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
import {
NavigationContext,
NavigationRouteContext,
Route,
ParamListBase,
} from '@react-navigation/native';
import type { EdgeInsets } from 'react-native-safe-area-context';
import Header from './Header';
import {
forSlideLeft,
forSlideUp,
forNoAnimation,
forSlideRight,
} from '../../TransitionConfigs/HeaderStyleInterpolators';
import HeaderShownContext from '../../utils/HeaderShownContext';
import PreviousSceneContext from '../../utils/PreviousSceneContext';
import type {
Layout,
Scene,
StackHeaderStyleInterpolator,
StackNavigationProp,
GestureDirection,
} from '../../types';
export type Props = {
mode: 'float' | 'screen';
layout: Layout;
insets: EdgeInsets;
scenes: (Scene<Route<string>> | undefined)[];
getPreviousScene: (props: {
route: Route<string>;
}) => Scene<Route<string>> | undefined;
getFocusedRoute: () => Route<string>;
onContentHeightChange?: (props: {
route: Route<string>;
height: number;
}) => void;
styleInterpolator: StackHeaderStyleInterpolator;
gestureDirection: GestureDirection;
style?: StyleProp<ViewStyle>;
};
export default function HeaderContainer({
mode,
scenes,
layout,
insets,
getPreviousScene,
getFocusedRoute,
onContentHeightChange,
gestureDirection,
styleInterpolator,
style,
}: Props) {
const focusedRoute = getFocusedRoute();
const isParentHeaderShown = React.useContext(HeaderShownContext);
const parentPreviousScene = React.useContext(PreviousSceneContext);
return (
<View pointerEvents="box-none" style={style}>
{scenes.slice(-3).map((scene, i, self) => {
if ((mode === 'screen' && i !== self.length - 1) || !scene) {
return null;
}
const {
header,
headerShown = isParentHeaderShown === false,
headerTransparent,
} = scene.descriptor.options || {};
if (!headerShown) {
return null;
}
const isFocused = focusedRoute.key === scene.route.key;
const previous =
getPreviousScene({ route: scene.route }) ?? parentPreviousScene;
// If the screen is next to a headerless screen, we need to make the header appear static
// This makes the header look like it's moving with the screen
const previousScene = self[i - 1];
const nextScene = self[i + 1];
const {
headerShown: previousHeaderShown = isParentHeaderShown === false,
} = previousScene?.descriptor.options || {};
const { headerShown: nextHeaderShown = isParentHeaderShown === false } =
nextScene?.descriptor.options || {};
const isHeaderStatic =
(previousHeaderShown === false &&
// We still need to animate when coming back from next scene
// A hacky way to check this is if the next scene exists
!nextScene) ||
nextHeaderShown === false;
const props = {
mode,
layout,
insets,
scene,
previous,
navigation: scene.descriptor.navigation as StackNavigationProp<
ParamListBase
>,
styleInterpolator:
mode === 'float'
? isHeaderStatic
? gestureDirection === 'vertical' ||
gestureDirection === 'vertical-inverted'
? forSlideUp
: gestureDirection === 'horizontal-inverted'
? forSlideRight
: forSlideLeft
: styleInterpolator
: forNoAnimation,
};
return (
<NavigationContext.Provider
key={scene.route.key}
value={scene.descriptor.navigation}
>
<NavigationRouteContext.Provider value={scene.route}>
<View
onLayout={
onContentHeightChange
? (e) =>
onContentHeightChange({
route: scene.route,
height: e.nativeEvent.layout.height,
})
: undefined
}
pointerEvents={isFocused ? 'box-none' : 'none'}
accessibilityElementsHidden={!isFocused}
importantForAccessibility={
isFocused ? 'auto' : 'no-hide-descendants'
}
style={
// Avoid positioning the focused header absolutely
// Otherwise accessibility tools don't seem to be able to find it
(mode === 'float' && !isFocused) || headerTransparent
? styles.header
: null
}
>
{header !== undefined ? header(props) : <Header {...props} />}
</View>
</NavigationRouteContext.Provider>
</NavigationContext.Provider>
);
})}
</View>
);
}
const styles = StyleSheet.create({
header: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
},
});

View File

@@ -223,7 +223,7 @@ export default class HeaderSegment extends React.Component<Props, State> {
warnIfHeaderStylesDefined(unsafeStyles);
}
const safeStyles = {
const safeStyles: ViewStyle = {
backgroundColor,
borderBottomColor,
borderBottomEndRadius,
@@ -249,6 +249,7 @@ export default class HeaderSegment extends React.Component<Props, State> {
borderTopStartRadius,
borderTopWidth,
borderWidth,
// @ts-expect-error: boxShadow is only for Web
boxShadow,
elevation,
shadowColor,

View File

@@ -1,10 +1,18 @@
import * as React from 'react';
import { Animated, StyleSheet, Platform } from 'react-native';
import {
Animated,
StyleSheet,
Platform,
TextProps,
StyleProp,
TextStyle,
} from 'react-native';
import useTheme from '../../../utils/useTheme';
type Props = Omit<React.ComponentProps<typeof Animated.Text>, 'key'> & {
type Props = Omit<TextProps, 'style'> & {
tintColor?: string;
children?: string;
style?: Animated.WithAnimatedValue<StyleProp<TextStyle>>;
};
export default function HeaderTitle({ tintColor, style, ...rest }: Props) {
@@ -13,6 +21,7 @@ export default function HeaderTitle({ tintColor, style, ...rest }: Props) {
return (
<Animated.Text
accessibilityRole="header"
aria-level="1"
numberOfLines={1}
{...rest}
style={[

View File

@@ -17,7 +17,7 @@ export default class KeyboardManager extends React.Component<Props> {
// Numeric id of the previously focused text input
// When a gesture didn't change the tab, we can restore the focused input with this
private previouslyFocusedTextInput: number | null = null;
private previouslyFocusedTextInput: any | null = null;
private startTimestamp: number = 0;
private keyboardTimeout: any;
@@ -35,10 +35,8 @@ export default class KeyboardManager extends React.Component<Props> {
this.clearKeyboardTimeout();
// @ts-expect-error: currentlyFocusedInput is pretty new, so not in the type definitions
const input = TextInput.State.currentlyFocusedInput
? // @ts-expect-error
TextInput.State.currentlyFocusedInput()
const input: any = TextInput.State.currentlyFocusedInput
? TextInput.State.currentlyFocusedInput()
: TextInput.State.currentlyFocusedField();
// When a page change begins, blur the currently focused input

View File

@@ -0,0 +1 @@
export { default } from './MaskedViewNative';

View File

@@ -0,0 +1 @@
export { default } from './MaskedViewNative';

View File

@@ -1,3 +1,6 @@
/**
* Use a stub for MaskedView on all Platforms that don't support it.
*/
import type * as React from 'react';
type Props = {

View File

@@ -1,3 +1,6 @@
/**
* The native MaskedView that we explicitly re-export for supported platforms: Android, iOS.
*/
import * as React from 'react';
import { UIManager } from 'react-native';
@@ -10,6 +13,8 @@ type Props = React.ComponentProps<MaskedViewType> & {
let RNCMaskedView: MaskedViewType | undefined;
try {
// Add try/catch to support usage even if it's not installed, since it's optional.
// Newer versions of Metro will handle it properly.
RNCMaskedView = require('@react-native-community/masked-view').default;
} catch (e) {
// Ignore

View File

@@ -58,7 +58,6 @@ export const MaybeScreen = ({
children: React.ReactNode;
}) => {
if (enabled && Platform.OS === 'web') {
// @ts-expect-error: the Animated.createAnimatedComponent types don't work properly
return <AnimatedWebScreen active={active} {...rest} />;
}

View File

@@ -97,6 +97,7 @@ export default class Card extends React.Component<Props> {
componentDidMount() {
this.animate({ closing: this.props.closing });
this.isCurrentlyMounted = true;
}
componentDidUpdate(prevProps: Props) {
@@ -115,8 +116,11 @@ export default class Card extends React.Component<Props> {
this.inverted.setValue(getInvertedMultiplier(gestureDirection));
}
const toValue = this.getAnimateToValue(this.props);
if (
this.getAnimateToValue(this.props) !== this.getAnimateToValue(prevProps)
this.getAnimateToValue(prevProps) !== toValue ||
this.lastToValue !== toValue
) {
// We need to trigger the animation when route was closed
// Thr route might have been closed by a `POP` action or by a gesture
@@ -128,9 +132,12 @@ export default class Card extends React.Component<Props> {
}
componentWillUnmount() {
this.isCurrentlyMounted = false;
this.handleEndInteraction();
}
private isCurrentlyMounted = false;
private isClosing = new Animated.Value(FALSE);
private inverted = new Animated.Value(
@@ -148,6 +155,8 @@ export default class Card extends React.Component<Props> {
private pendingGestureCallback: any;
private lastToValue: number | undefined;
private animate = ({
closing,
velocity,
@@ -168,6 +177,8 @@ export default class Card extends React.Component<Props> {
closing,
});
this.lastToValue = toValue;
const spec = closing ? transitionSpec.close : transitionSpec.open;
const animation =
@@ -196,6 +207,11 @@ export default class Card extends React.Component<Props> {
} else {
onOpen();
}
if (this.isCurrentlyMounted) {
// Make sure to re-open screen if it wasn't removed
this.forceUpdate();
}
}
});
};
@@ -301,10 +317,13 @@ export default class Card extends React.Component<Props> {
if (closing) {
// We call onClose with a delay to make sure that the animation has already started
// This will make sure that the state update caused by this doesn't affect start of animation
this.pendingGestureCallback = (setTimeout(
onClose,
32
) as any) as number;
this.pendingGestureCallback = (setTimeout(() => {
onClose();
// Trigger an update after we dispatch the action to remove the screen
// This will make sure that we check if the screen didn't get removed so we can cancel the animation
this.forceUpdate();
}, 32) as any) as number;
}
onGestureEnd?.();
@@ -489,9 +508,10 @@ export default class Card extends React.Component<Props> {
: undefined;
const { backgroundColor } = StyleSheet.flatten(contentStyle || {});
const isTransparent = backgroundColor
? Color(backgroundColor).alpha() === 0
: false;
const isTransparent =
typeof backgroundColor === 'string'
? Color(backgroundColor).alpha() === 0
: false;
return (
<CardAnimationContext.Provider value={animationContext}>

View File

@@ -27,13 +27,14 @@ type Props = TransitionPreset & {
safeAreaInsetRight: number;
safeAreaInsetBottom: number;
safeAreaInsetLeft: number;
cardOverlay?: (props: { style: StyleProp<ViewStyle> }) => React.ReactNode;
cardOverlay?: (props: {
style: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
}) => React.ReactNode;
cardOverlayEnabled?: boolean;
cardShadowEnabled?: boolean;
cardStyle?: StyleProp<ViewStyle>;
getPreviousScene: (props: {
route: Route<string>;
index: number;
}) => Scene<Route<string>> | undefined;
getFocusedRoute: () => Route<string>;
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
@@ -48,6 +49,9 @@ type Props = TransitionPreset & {
onPageChangeStart?: () => void;
onPageChangeConfirm?: () => void;
onPageChangeCancel?: () => void;
onGestureStart?: (props: { route: Route<string> }) => void;
onGestureEnd?: (props: { route: Route<string> }) => void;
onGestureCancel?: (props: { route: Route<string> }) => void;
gestureEnabled?: boolean;
gestureResponseDistance?: {
vertical?: number;
@@ -97,6 +101,9 @@ function CardContainer({
onPageChangeCancel,
onPageChangeConfirm,
onPageChangeStart,
onGestureCancel,
onGestureEnd,
onGestureStart,
onTransitionEnd,
onTransitionStart,
renderHeader,
@@ -122,6 +129,20 @@ function CardContainer({
onCloseRoute({ route: scene.route });
};
const handleGestureBegin = () => {
onPageChangeStart?.();
onGestureStart?.({ route: scene.route });
};
const handleGestureCanceled = () => {
onPageChangeCancel?.();
onGestureCancel?.({ route: scene.route });
};
const handleGestureEnd = () => {
onGestureEnd?.({ route: scene.route });
};
const handleTransitionStart = ({ closing }: { closing: boolean }) => {
if (active && closing) {
onPageChangeConfirm?.();
@@ -163,7 +184,7 @@ function CardContainer({
const isParentHeaderShown = React.useContext(HeaderShownContext);
const isCurrentHeaderShown = headerMode !== 'none' && headerShown !== false;
const previousScene = getPreviousScene({ route: scene.route, index });
const previousScene = getPreviousScene({ route: scene.route });
return (
<Card
@@ -181,8 +202,9 @@ function CardContainer({
overlayEnabled={cardOverlayEnabled}
shadowEnabled={cardShadowEnabled}
onTransitionStart={handleTransitionStart}
onGestureBegin={onPageChangeStart}
onGestureCanceled={onPageChangeCancel}
onGestureBegin={handleGestureBegin}
onGestureCanceled={handleGestureCanceled}
onGestureEnd={handleGestureEnd}
gestureEnabled={gestureEnabled}
gestureResponseDistance={gestureResponseDistance}
gestureVelocityImpact={gestureVelocityImpact}

View File

@@ -61,6 +61,9 @@ type Props = {
onPageChangeStart?: () => void;
onPageChangeConfirm?: () => void;
onPageChangeCancel?: () => void;
onGestureStart?: (props: { route: Route<string> }) => void;
onGestureEnd?: (props: { route: Route<string> }) => void;
onGestureCancel?: (props: { route: Route<string> }) => void;
};
type State = {
@@ -337,31 +340,21 @@ export default class CardStack extends React.Component<Props, State> {
return state.routes[state.index];
};
private getPreviousScene = ({
route,
index,
}: {
route: Route<string>;
index: number;
}) => {
const previousRoute = this.props.getPreviousRoute({ route });
private getPreviousScene = ({ route }: { route: Route<string> }) => {
const { getPreviousRoute } = this.props;
const { scenes } = this.state;
let previous: Scene<Route<string>> | undefined;
const previousRoute = getPreviousRoute({ route });
if (previousRoute) {
// The previous scene will be shortly before the current scene in the array
// So loop back from current index to avoid looping over the full array
for (let j = index - 1; j >= 0; j--) {
const s = this.state.scenes[j];
const previousScene = scenes.find(
(scene) => scene.route.key === previousRoute.key
);
if (s && s.route.key === previousRoute.key) {
previous = s;
break;
}
}
return previousScene;
}
return previous;
return undefined;
};
render() {
@@ -383,6 +376,9 @@ export default class CardStack extends React.Component<Props, State> {
onPageChangeStart,
onPageChangeConfirm,
onPageChangeCancel,
onGestureStart,
onGestureEnd,
onGestureCancel,
} = this.props;
const { scenes, layout, gestures, headerHeights } = this.state;
@@ -579,6 +575,9 @@ export default class CardStack extends React.Component<Props, State> {
onPageChangeStart={onPageChangeStart}
onPageChangeConfirm={onPageChangeConfirm}
onPageChangeCancel={onPageChangeCancel}
onGestureStart={onGestureStart}
onGestureCancel={onGestureCancel}
onGestureEnd={onGestureEnd}
gestureResponseDistance={gestureResponseDistance}
headerHeight={headerHeight}
onHeaderHeightChange={this.handleHeaderLayout}

View File

@@ -433,6 +433,18 @@ export default class StackView extends React.Component<Props, State> {
descriptor?.options.onTransitionEnd?.({ closing });
};
private handleGestureStart = () => {
// Do nothing
};
private handleGestureEnd = () => {
// Do nothing
};
private handleGestureCancel = () => {
// Do nothing
};
render() {
const {
state,
@@ -480,6 +492,9 @@ export default class StackView extends React.Component<Props, State> {
headerMode={headerMode}
state={state}
descriptors={descriptors}
onGestureStart={this.handleGestureStart}
onGestureEnd={this.handleGestureEnd}
onGestureCancel={this.handleGestureCancel}
{...rest}
{...props}
/>

View File

@@ -0,0 +1,50 @@
import * as React from 'react';
import { Animated, Platform } from 'react-native';
import { BaseButton } from 'react-native-gesture-handler';
const AnimatedBaseButton = Animated.createAnimatedComponent(BaseButton);
type Props = React.ComponentProps<typeof BaseButton> & {
activeOpacity: number;
};
const useNativeDriver = Platform.OS !== 'web';
export default class TouchableItem extends React.Component<Props> {
static defaultProps = {
activeOpacity: 0.3,
borderless: true,
enabled: true,
};
private opacity = new Animated.Value(1);
private handleActiveStateChange = (active: boolean) => {
Animated.spring(this.opacity, {
stiffness: 1000,
damping: 500,
mass: 3,
overshootClamping: true,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 0.01,
toValue: active ? this.props.activeOpacity : 1,
useNativeDriver,
}).start();
this.props.onActiveStateChange?.(active);
};
render() {
const { children, style, enabled, ...rest } = this.props;
return (
<AnimatedBaseButton
{...rest}
onActiveStateChange={this.handleActiveStateChange}
style={[style, enabled && { opacity: this.opacity }]}
>
{children}
</AnimatedBaseButton>
);
}
}

View File

@@ -1,81 +0,0 @@
/**
* TouchableItem renders a touchable that looks native on both iOS and Android.
*
* It provides an abstraction on top of TouchableNativeFeedback and
* TouchableOpacity.
*
* On iOS you can pass the props of TouchableOpacity, on Android pass the props
* of TouchableNativeFeedback.
*/
import * as React from 'react';
import {
Platform,
TouchableNativeFeedback,
TouchableOpacity,
View,
ViewProps,
} from 'react-native';
import BorderlessButton from './BorderlessButton';
export type Props = ViewProps & {
pressColor: string;
disabled?: boolean;
borderless?: boolean;
delayPressIn?: number;
onPress?: () => void;
children: React.ReactNode;
};
const ANDROID_VERSION_LOLLIPOP = 21;
export class TouchableItem extends React.Component<Props> {
static defaultProps = {
borderless: false,
pressColor: 'rgba(0, 0, 0, .32)',
};
render() {
/*
* TouchableNativeFeedback.Ripple causes a crash on old Android versions,
* therefore only enable it on Android Lollipop and above.
*
* All touchables on Android should have the ripple effect according to
* platform design guidelines.
* We need to pass the background prop to specify a borderless ripple effect.
*/
if (
Platform.OS === 'android' &&
Platform.Version >= ANDROID_VERSION_LOLLIPOP
) {
const { style, pressColor, borderless, children, ...rest } = this.props;
return (
<TouchableNativeFeedback
{...rest}
useForeground={TouchableNativeFeedback.canUseNativeForeground()}
background={TouchableNativeFeedback.Ripple(pressColor, borderless)}
>
<View style={style}>{React.Children.only(children)}</View>
</TouchableNativeFeedback>
);
} else if (Platform.OS === 'ios') {
return (
<BorderlessButton
hitSlop={{ top: 10, bottom: 10, right: 10, left: 10 }}
disallowInterruption
enabled={!this.props.disabled}
{...this.props}
>
{this.props.children}
</BorderlessButton>
);
} else {
return (
<TouchableOpacity {...this.props}>
{this.props.children}
</TouchableOpacity>
);
}
}
}

View File

@@ -1,3 +1,63 @@
import { TouchableOpacity } from 'react-native';
/**
* TouchableItem provides an abstraction on top of TouchableNativeFeedback and
* TouchableOpacity to handle platform differences.
*
* On Android, you can pass the props of TouchableNativeFeedback.
* On other platforms, you can pass the props of TouchableOpacity.
*/
import * as React from 'react';
import {
Platform,
TouchableNativeFeedback,
TouchableOpacity,
View,
ViewProps,
} from 'react-native';
export const TouchableItem = (TouchableOpacity as any) as typeof import('./TouchableItem.native').TouchableItem;
export type Props = ViewProps & {
pressColor?: string;
disabled?: boolean;
borderless?: boolean;
delayPressIn?: number;
onPress?: () => void;
children: React.ReactNode;
};
const ANDROID_VERSION_LOLLIPOP = 21;
export default function TouchableItem({
borderless = false,
pressColor = 'rgba(0, 0, 0, .32)',
style,
children,
...rest
}: Props) {
/*
* TouchableNativeFeedback.Ripple causes a crash on old Android versions,
* therefore only enable it on Android Lollipop and above.
*
* All touchables on Android should have the ripple effect according to
* platform design guidelines.
* We need to pass the background prop to specify a borderless ripple effect.
*/
if (
Platform.OS === 'android' &&
Platform.Version >= ANDROID_VERSION_LOLLIPOP
) {
return (
<TouchableNativeFeedback
{...rest}
useForeground={TouchableNativeFeedback.canUseNativeForeground()}
background={TouchableNativeFeedback.Ripple(pressColor, borderless)}
>
<View style={style}>{React.Children.only(children)}</View>
</TouchableNativeFeedback>
);
} else {
return (
<TouchableOpacity style={style} {...rest}>
{children}
</TouchableOpacity>
);
}
}

View File

@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.9.2](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.9.1...react-navigation-tabs@2.9.2) (2020-10-02)
**Note:** Version bump only for package react-navigation-tabs
## [2.9.1](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.9.0...react-navigation-tabs@2.9.1) (2020-09-24)
**Note:** Version bump only for package react-navigation-tabs
# [2.9.0](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.13...react-navigation-tabs@2.9.0) (2020-06-25)

View File

@@ -1,6 +1,6 @@
{
"name": "react-navigation-tabs",
"version": "2.9.0",
"version": "2.9.2",
"description": "Tab Navigation components for React Navigation",
"main": "lib/commonjs/index.js",
"module": "lib/module/index.js",
@@ -43,21 +43,21 @@
"hoist-non-react-statics": "^3.3.2",
"react-lifecycles-compat": "^3.0.4",
"react-native-safe-area-view": "^0.14.9",
"react-native-tab-view": "^2.11.0"
"react-native-tab-view": "^2.15.1"
},
"devDependencies": {
"@react-native-community/bob": "^0.10.0",
"@react-native-community/bob": "^0.16.2",
"@types/hoist-non-react-statics": "^3.3.1",
"@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",
"@types/react": "^16.9.44",
"@types/react-native": "^0.63.4",
"del-cli": "^3.0.1",
"react": "~16.13.1",
"react-native": "~0.63.2",
"react-native-gesture-handler": "~1.7.0",
"react-native-reanimated": "~1.13.0",
"react-native-tab-view": "^2.13.0",
"react-navigation": "^4.4.0",
"typescript": "^3.9.5"
"react-navigation": "^4.4.2",
"typescript": "^4.0.3"
},
"peerDependencies": {
"react": "*",

View File

@@ -1,10 +1,5 @@
import * as React from 'react';
import {
View,
StyleSheet,
AccessibilityRole,
AccessibilityStates,
} from 'react-native';
import { View, StyleSheet, AccessibilityRole } from 'react-native';
import { NavigationRoute } from 'react-navigation';
import { ScreenContainer } from 'react-native-screens';
@@ -35,7 +30,7 @@ type Props = NavigationViewProps &
getAccessibilityStates: (props: {
route: NavigationRoute;
focused: boolean;
}) => AccessibilityStates[];
}) => string[];
navigation: NavigationTabProp;
descriptors: SceneDescriptorMap;
screenProps?: unknown;
@@ -49,11 +44,8 @@ class TabNavigationView extends React.PureComponent<Props, State> {
static defaultProps = {
lazy: true,
getAccessibilityRole: (): AccessibilityRole => 'button',
getAccessibilityStates: ({
focused,
}: {
focused: boolean;
}): AccessibilityStates[] => (focused ? ['selected'] : []),
getAccessibilityStates: ({ focused }: { focused: boolean }) =>
focused ? ['selected'] : [],
};
static getDerivedStateFromProps(nextProps: Props, prevState: State) {

View File

@@ -1,7 +1,7 @@
import * as React from 'react';
import {
AccessibilityRole,
AccessibilityStates,
AccessibilityState,
StyleProp,
TextStyle,
ViewStyle,
@@ -106,7 +106,8 @@ export type ButtonComponentProps = {
testID?: string;
accessibilityLabel?: string;
accessibilityRole?: AccessibilityRole;
accessibilityStates?: AccessibilityStates[];
accessibilityState?: AccessibilityState;
accessibilityStates?: string[];
style?: StyleProp<ViewStyle>;
};
@@ -123,7 +124,7 @@ export type BottomTabBarProps = BottomTabBarOptions & {
getAccessibilityStates: (props: {
route: NavigationRoute;
focused: boolean;
}) => AccessibilityStates[];
}) => string[];
getButtonComponent: (props: {
route: NavigationRoute;
}) => React.ComponentType<ButtonComponentProps> | undefined;

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import {
Animated,
TouchableWithoutFeedback,
@@ -62,6 +62,7 @@ class TouchableWithoutFeedbackWrapper extends React.Component<
testID,
accessibilityLabel,
accessibilityRole,
accessibilityState,
accessibilityStates,
...rest
} = this.props;
@@ -74,6 +75,8 @@ class TouchableWithoutFeedbackWrapper extends React.Component<
hitSlop={{ left: 15, right: 15, top: 0, bottom: 5 }}
accessibilityLabel={accessibilityLabel}
accessibilityRole={accessibilityRole}
accessibilityState={accessibilityState}
// @ts-expect-error: support older RN versions
accessibilityStates={accessibilityStates}
>
<View {...rest} />
@@ -170,6 +173,7 @@ class TabBarBottom extends React.Component<BottomTabBarProps, State> {
const { animation, config } = this._getKeyboardAnimationConfigByType(
'show'
);
// @ts-expect-error
Animated[animation](this.state.visible, {
toValue: 0,
...config,
@@ -181,6 +185,7 @@ class TabBarBottom extends React.Component<BottomTabBarProps, State> {
const { animation, config } = this._getKeyboardAnimationConfigByType(
'hide'
);
// @ts-expect-error
Animated[animation](this.state.visible, {
toValue: 1,
...config,

View File

@@ -1,4 +1,4 @@
import React from 'react';
import * as React from 'react';
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
import Animated from 'react-native-reanimated';
import { NavigationRoute } from 'react-navigation';

9845
yarn.lock

File diff suppressed because it is too large Load Diff