Compare commits

...

19 Commits

Author SHA1 Message Date
Satyajit Sahoo
853740bfaf chore: publish
- @react-navigation/bottom-tabs@5.2.3
 - @react-navigation/compat@5.1.5
 - @react-navigation/core@5.3.0
 - @react-navigation/drawer@5.3.3
 - @react-navigation/material-bottom-tabs@5.1.5
 - @react-navigation/material-top-tabs@5.1.5
 - @react-navigation/native@5.1.2
 - @react-navigation/routers@5.2.0
 - @react-navigation/stack@5.2.4
2020-03-23 00:00:55 +01:00
Satyajit Sahoo
179b6312fe chore: update prettier 2020-03-22 23:58:06 +01:00
Satyajit Sahoo
043924ca48 fix: fix swipe not dismissing card in RTL
closes #7841
2020-03-22 23:55:16 +01:00
Satyajit Sahoo
813a5903b5 feat: add keys to routes missing keys during reset 2020-03-22 23:38:40 +01:00
Satyajit Sahoo
3709e652f4 feat: support function in listeners prop 2020-03-22 23:33:25 +01:00
Satyajit Sahoo
5b15c7164f fix: return correct value for isFocused after changing screens
fixes #7843
2020-03-22 23:31:04 +01:00
Satyajit Sahoo
e030932497 chore: publish
- @react-navigation/stack@5.2.3
2020-03-19 21:56:36 +01:00
Tien Pham
adbfedcd58 fix: use the correct velocity value in closing animation (#7836)
In this commit f24d3a3461 we modified the `velocity` in inverted gesture, but since we also use this value in the closing animation, the change in that commit also introduced a new bug:
![Mar-20-2020 03-40-05](https://user-images.githubusercontent.com/57227217/77113229-006f0500-6a5d-11ea-97b5-571e8301cd87.gif)

This PR fixes the issue by keeping the original velocity value.
2020-03-19 21:55:03 +01:00
Satyajit Sahoo
bc9b044fb3 chore: publish
- @react-navigation/bottom-tabs@5.2.2
 - @react-navigation/compat@5.1.4
 - @react-navigation/core@5.2.3
 - @react-navigation/drawer@5.3.2
 - @react-navigation/material-bottom-tabs@5.1.4
 - @react-navigation/material-top-tabs@5.1.4
 - @react-navigation/native@5.1.1
 - @react-navigation/stack@5.2.2
2020-03-19 19:48:37 +01:00
Alexey Vlasenko
f24d3a3461 fix: fix closing stack using inverted gesture. (#7824)
Co-authored-by: Satyajit Sahoo <satyajit.happy@gmail.com>
2020-03-19 19:32:29 +01:00
Satyajit Sahoo
3df65e2819 fix: initialize height and width to zero if undefined
closes #6789
2020-03-19 19:03:23 +01:00
Satyajit Sahoo
5c4afc5cb4 fix: close drawer on pressing Esc on web
closes #6745
2020-03-19 18:51:16 +01:00
Satyajit Sahoo
d5bb357053 chore: temporarily disables devtools until we add a public API
closes #7726
2020-03-19 18:39:04 +01:00
Satyajit Sahoo
b1fe73097f fix: only dismiss previously focused input on page change. closes #6918 2020-03-19 18:30:54 +01:00
Satyajit Sahoo
49f6fed6d3 fix: fix blank page if stack was inside display: none before 2020-03-19 18:11:55 +01:00
Satyajit Sahoo
b1a65fc73e fix: don't use react-native-screens on web
seems `react-native-screens` doesn't handle active screens properly and shows a blank page instead on web when a number is specified in the `active` prop.

closes #7485
2020-03-19 17:28:35 +01:00
Noemi Rozpara
3ea8eec432 fix: fix permanent sidebar position (#7830) 2020-03-19 11:44:13 +01:00
Satyajit Sahoo
00e0f05190 chore: publish
- @react-navigation/drawer@5.3.1
2020-03-17 20:13:03 +01:00
Satyajit Sahoo
193c344ba5 refactor: fix useIsDrawerOpen hook 2020-03-17 19:22:12 +01:00
87 changed files with 750 additions and 340 deletions

View File

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

View File

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

View File

@@ -28,7 +28,7 @@ export default function BottomTabsScreen() {
return ( return (
<BottomTabs.Navigator <BottomTabs.Navigator
screenOptions={{ screenOptions={{
tabBarButton: props => <TouchableBounce {...props} />, tabBarButton: (props) => <TouchableBounce {...props} />,
}} }}
> >
<BottomTabs.Screen <BottomTabs.Screen
@@ -38,7 +38,7 @@ export default function BottomTabsScreen() {
tabBarIcon: getTabBarIcon('file-document-box'), tabBarIcon: getTabBarIcon('file-document-box'),
}} }}
> >
{props => <SimpleStackScreen {...props} headerMode="none" />} {(props) => <SimpleStackScreen {...props} headerMode="none" />}
</BottomTabs.Screen> </BottomTabs.Screen>
<BottomTabs.Screen <BottomTabs.Screen
name="Chat" name="Chat"

View File

@@ -15,7 +15,7 @@ export default function BottomTabsScreen() {
return ( return (
<BottomTabs.Navigator> <BottomTabs.Navigator>
{tabs.map(i => ( {tabs.map((i) => (
<BottomTabs.Screen <BottomTabs.Screen
key={i} key={i}
name={`tab-${i}`} name={`tab-${i}`}
@@ -29,12 +29,14 @@ export default function BottomTabsScreen() {
{() => ( {() => (
<View style={styles.container}> <View style={styles.container}>
<Title>Tab {i}</Title> <Title>Tab {i}</Title>
<Button onPress={() => setTabs(tabs => [...tabs, tabs.length])}> <Button onPress={() => setTabs((tabs) => [...tabs, tabs.length])}>
Add a tab Add a tab
</Button> </Button>
<Button <Button
onPress={() => onPress={() =>
setTabs(tabs => (tabs.length > 1 ? tabs.slice(0, -1) : tabs)) setTabs((tabs) =>
tabs.length > 1 ? tabs.slice(0, -1) : tabs
)
} }
> >
Remove a tab Remove a tab

View File

@@ -28,7 +28,7 @@ export default function MaterialBottomTabsScreen() {
tabBarColor: '#C9E7F8', tabBarColor: '#C9E7F8',
}} }}
> >
{props => <SimpleStackScreen {...props} headerMode="none" />} {(props) => <SimpleStackScreen {...props} headerMode="none" />}
</MaterialBottomTabs.Screen> </MaterialBottomTabs.Screen>
<MaterialBottomTabs.Screen <MaterialBottomTabs.Screen
name="Chat" name="Chat"

View File

@@ -68,10 +68,7 @@ export default function Chat(props: Partial<ScrollViewProps>) {
styles.input, styles.input,
{ backgroundColor: colors.card, color: colors.text }, { backgroundColor: colors.card, color: colors.text },
]} ]}
placeholderTextColor={Color(colors.text) placeholderTextColor={Color(colors.text).alpha(0.5).rgb().string()}
.alpha(0.5)
.rgb()
.string()}
placeholder="Write a message" placeholder="Write a message"
underlineColorAndroid="transparent" underlineColorAndroid="transparent"
/> />

View File

@@ -51,10 +51,7 @@ export default function NewsFeed(props: Props) {
<Card style={styles.card}> <Card style={styles.card}>
<TextInput <TextInput
placeholder="What's on your mind?" placeholder="What's on your mind?"
placeholderTextColor={Color(colors.text) placeholderTextColor={Color(colors.text).alpha(0.5).rgb().string()}
.alpha(0.5)
.rgb()
.string()}
style={styles.input} style={styles.input}
/> />
</Card> </Card>

View File

@@ -224,7 +224,7 @@ export default function App() {
<NavigationContainer <NavigationContainer
ref={containerRef} ref={containerRef}
initialState={initialState} initialState={initialState}
onStateChange={state => onStateChange={(state) =>
AsyncStorage.setItem( AsyncStorage.setItem(
NAVIGATION_PERSISTENCE_KEY, NAVIGATION_PERSISTENCE_KEY,
JSON.stringify(state) JSON.stringify(state)
@@ -298,12 +298,12 @@ export default function App() {
theme.dark ? 'light' : 'dark' theme.dark ? 'light' : 'dark'
); );
setTheme(t => (t.dark ? DefaultTheme : DarkTheme)); setTheme((t) => (t.dark ? DefaultTheme : DarkTheme));
}} }}
/> />
<Divider /> <Divider />
{(Object.keys(SCREENS) as (keyof typeof SCREENS)[]).map( {(Object.keys(SCREENS) as (keyof typeof SCREENS)[]).map(
name => ( (name) => (
<List.Item <List.Item
key={name} key={name}
title={SCREENS[name].title} title={SCREENS[name].title}
@@ -315,7 +315,7 @@ export default function App() {
)} )}
</Stack.Screen> </Stack.Screen>
{(Object.keys(SCREENS) as (keyof typeof SCREENS)[]).map( {(Object.keys(SCREENS) as (keyof typeof SCREENS)[]).map(
name => ( (name) => (
<Stack.Screen <Stack.Screen
key={name} key={name}
name={name} name={name}

View File

@@ -7,7 +7,7 @@ const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const node_modules = path.resolve(__dirname, '..', 'node_modules'); const node_modules = path.resolve(__dirname, '..', 'node_modules');
const packages = path.resolve(__dirname, '..', 'packages'); const packages = path.resolve(__dirname, '..', 'packages');
module.exports = async function(env, argv) { module.exports = async function (env, argv) {
const config = await createExpoWebpackConfigAsync(env, argv); const config = await createExpoWebpackConfigAsync(env, argv);
config.context = path.resolve(__dirname, '..'); config.context = path.resolve(__dirname, '..');
@@ -20,7 +20,7 @@ module.exports = async function(env, argv) {
}); });
config.resolve.plugins = config.resolve.plugins.filter( config.resolve.plugins = config.resolve.plugins.filter(
p => !(p instanceof ModuleScopePlugin) (p) => !(p instanceof ModuleScopePlugin)
); );
Object.assign(config.resolve.alias, { Object.assign(config.resolve.alias, {
@@ -30,7 +30,7 @@ module.exports = async function(env, argv) {
'@expo/vector-icons': path.resolve(node_modules, '@expo/vector-icons'), '@expo/vector-icons': path.resolve(node_modules, '@expo/vector-icons'),
}); });
fs.readdirSync(packages).forEach(name => { fs.readdirSync(packages).forEach((name) => {
config.resolve.alias[`@react-navigation/${name}`] = path.resolve( config.resolve.alias[`@react-navigation/${name}`] = path.resolve(
packages, packages,
name, name,

View File

@@ -43,7 +43,7 @@
"husky": "^4.2.3", "husky": "^4.2.3",
"jest": "^25.1.0", "jest": "^25.1.0",
"lerna": "^3.20.2", "lerna": "^3.20.2",
"prettier": "^1.19.1", "prettier": "^2.0.1",
"typescript": "^3.7.5" "typescript": "^3.7.5"
}, },
"resolutions": { "resolutions": {

View File

@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.2.3](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.2...@react-navigation/bottom-tabs@5.2.3) (2020-03-22)
**Note:** Version bump only for package @react-navigation/bottom-tabs
## [5.2.2](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.1...@react-navigation/bottom-tabs@5.2.2) (2020-03-19)
### Bug Fixes
* don't use react-native-screens on web ([b1a65fc](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/b1a65fc73e8603ae2c06ef101a74df31e80bb9b2)), closes [#7485](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/7485)
* initialize height and width to zero if undefined ([3df65e2](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/commit/3df65e28197db3bb8371059146546d57661c5ba3)), closes [#6789](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/issues/6789)
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.0...@react-navigation/bottom-tabs@5.2.1) (2020-03-17) ## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/bottom-tabs/compare/@react-navigation/bottom-tabs@5.2.0...@react-navigation/bottom-tabs@5.2.1) (2020-03-17)
**Note:** Version bump only for package @react-navigation/bottom-tabs **Note:** Version bump only for package @react-navigation/bottom-tabs

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/bottom-tabs", "name": "@react-navigation/bottom-tabs",
"description": "Bottom tab navigator following iOS design guidelines", "description": "Bottom tab navigator following iOS design guidelines",
"version": "5.2.1", "version": "5.2.3",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -35,7 +35,7 @@
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.10.0", "@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.1.0", "@react-navigation/native": "^5.1.2",
"@types/color": "^3.0.1", "@types/color": "^3.0.1",
"@types/react": "^16.9.23", "@types/react": "^16.9.23",
"@types/react-native": "^0.61.22", "@types/react-native": "^0.61.22",

View File

@@ -51,7 +51,12 @@ export default function BottomTabBar({
}: Props) { }: Props) {
const { colors } = useTheme(); const { colors } = useTheme();
const [dimensions, setDimensions] = React.useState(Dimensions.get('window')); const [dimensions, setDimensions] = React.useState(() => {
const { height = 0, width = 0 } = Dimensions.get('window');
return { height, width };
});
const [layout, setLayout] = React.useState({ const [layout, setLayout] = React.useState({
height: 0, height: 0,
width: dimensions.width, width: dimensions.width,
@@ -116,7 +121,7 @@ export default function BottomTabBar({
const handleLayout = (e: LayoutChangeEvent) => { const handleLayout = (e: LayoutChangeEvent) => {
const { height, width } = e.nativeEvent.layout; const { height, width } = e.nativeEvent.layout;
setLayout(layout => { setLayout((layout) => {
if (height === layout.height && width === layout.width) { if (height === layout.height && width === layout.width) {
return layout; return layout;
} else { } else {

View File

@@ -133,9 +133,7 @@ export default function BottomTabBarItem({
const inactiveTintColor = const inactiveTintColor =
customInactiveTintColor === undefined customInactiveTintColor === undefined
? Color(colors.text) ? Color(colors.text).mix(Color(colors.card), 0.5).hex()
.mix(Color(colors.card), 0.5)
.hex()
: customInactiveTintColor; : customInactiveTintColor;
const renderLabel = ({ focused }: { focused: boolean }) => { const renderLabel = ({ focused }: { focused: boolean }) => {

View File

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

View File

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

View File

@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.1.5](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.4...@react-navigation/compat@5.1.5) (2020-03-22)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.4](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.3...@react-navigation/compat@5.1.4) (2020-03-19)
**Note:** Version bump only for package @react-navigation/compat
## [5.1.3](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.2...@react-navigation/compat@5.1.3) (2020-03-17) ## [5.1.3](https://github.com/react-navigation/react-navigation/tree/master/packages/compat/compare/@react-navigation/compat@5.1.2...@react-navigation/compat@5.1.3) (2020-03-17)
**Note:** Version bump only for package @react-navigation/compat **Note:** Version bump only for package @react-navigation/compat

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/compat", "name": "@react-navigation/compat",
"description": "Compatibility layer to write navigator definitions in static configuration format", "description": "Compatibility layer to write navigator definitions in static configuration format",
"version": "5.1.3", "version": "5.1.5",
"license": "MIT", "license": "MIT",
"repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat", "repository": "https://github.com/react-navigation/react-navigation/tree/master/packages/compat",
"bugs": { "bugs": {
@@ -26,7 +26,7 @@
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.10.0", "@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.1.0", "@react-navigation/native": "^5.1.2",
"@types/react": "^16.9.23", "@types/react": "^16.9.23",
"react": "~16.9.0", "react": "~16.9.0",
"typescript": "^3.7.5" "typescript": "^3.7.5"

View File

@@ -69,7 +69,7 @@ export default function createCompatNavigatorFactory<
function Navigator({ screenProps }: { screenProps?: unknown }) { function Navigator({ screenProps }: { screenProps?: unknown }) {
const screens = React.useMemo( const screens = React.useMemo(
() => () =>
routeNames.map(name => { routeNames.map((name) => {
let getScreenComponent: () => CompatScreenType<NavigationPropType>; let getScreenComponent: () => CompatScreenType<NavigationPropType>;
let initialParams; let initialParams;

View File

@@ -16,7 +16,7 @@ export default function useCompatNavigation<
const route = useRoute(); const route = useRoute();
const isFirstRouteInParent = useNavigationState( const isFirstRouteInParent = useNavigationState(
state => state.routes[0].key === route.key (state) => state.routes[0].key === route.key
); );
const context = React.useRef<Record<string, any>>({}); const context = React.useRef<Record<string, any>>({});

View File

@@ -26,8 +26,9 @@ export default function withNavigation<
return <Comp ref={onRef} navigation={navigation} {...rest} />; return <Comp ref={onRef} navigation={navigation} {...rest} />;
}; };
WrappedComponent.displayName = `withNavigation(${Comp.displayName || WrappedComponent.displayName = `withNavigation(${
Comp.name})`; Comp.displayName || Comp.name
})`;
return WrappedComponent; return WrappedComponent;
} }

View File

@@ -23,8 +23,9 @@ export default function withNavigationFocus<
return <Comp ref={onRef} isFocused={isFocused} {...rest} />; return <Comp ref={onRef} isFocused={isFocused} {...rest} />;
}; };
WrappedComponent.displayName = `withNavigationFocus(${Comp.displayName || WrappedComponent.displayName = `withNavigationFocus(${
Comp.name})`; Comp.displayName || Comp.name
})`;
return WrappedComponent; return WrappedComponent;
} }

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.3.0](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.2.3...@react-navigation/core@5.3.0) (2020-03-22)
### Bug Fixes
* return correct value for isFocused after changing screens ([5b15c71](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/5b15c7164f5503f2f0d51006a3f23bd0c58fd9b7)), closes [#7843](https://github.com/react-navigation/react-navigation/tree/master/packages/core/issues/7843)
### Features
* support function in listeners prop ([3709e65](https://github.com/react-navigation/react-navigation/tree/master/packages/core/commit/3709e652f41a16c2c2b05d5dbbe1da2017ba2c3f))
## [5.2.3](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.2.2...@react-navigation/core@5.2.3) (2020-03-19)
**Note:** Version bump only for package @react-navigation/core
## [5.2.2](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.2.1...@react-navigation/core@5.2.2) (2020-03-16) ## [5.2.2](https://github.com/react-navigation/react-navigation/tree/master/packages/core/compare/@react-navigation/core@5.2.1...@react-navigation/core@5.2.2) (2020-03-16)
**Note:** Version bump only for package @react-navigation/core **Note:** Version bump only for package @react-navigation/core

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/core", "name": "@react-navigation/core",
"description": "Core utilities for building navigators", "description": "Core utilities for building navigators",
"version": "5.2.2", "version": "5.3.0",
"keywords": [ "keywords": [
"react", "react",
"react-native", "react-native",
@@ -29,7 +29,7 @@
"clean": "del lib" "clean": "del lib"
}, },
"dependencies": { "dependencies": {
"@react-navigation/routers": "^5.1.1", "@react-navigation/routers": "^5.2.0",
"escape-string-regexp": "^2.0.0", "escape-string-regexp": "^2.0.0",
"query-string": "^6.11.1", "query-string": "^6.11.1",
"react-is": "^16.13.0", "react-is": "^16.13.0",

View File

@@ -73,7 +73,7 @@ const getPartialState = (
return { return {
...partialState, ...partialState,
stale: true, stale: true,
routes: state.routes.map(route => { routes: state.routes.map((route) => {
if (route.state === undefined) { if (route.state === undefined) {
return route as Route<string> & { return route as Route<string> & {
state?: PartialState<NavigationState>; state?: PartialState<NavigationState>;
@@ -136,6 +136,7 @@ const BaseNavigationContainer = React.forwardRef(
); );
const { trackState, trackAction } = useDevTools({ const { trackState, trackAction } = useDevTools({
enabled: false,
name: '@react-navigation', name: '@react-navigation',
reset, reset,
state, state,
@@ -155,7 +156,7 @@ const BaseNavigationContainer = React.forwardRef(
throw new Error(NOT_INITIALIZED_ERROR); throw new Error(NOT_INITIALIZED_ERROR);
} }
listeners[0](navigation => navigation.dispatch(action)); listeners[0]((navigation) => navigation.dispatch(action));
}; };
const canGoBack = () => { const canGoBack = () => {
@@ -163,7 +164,7 @@ const BaseNavigationContainer = React.forwardRef(
return false; return false;
} }
const { result, handled } = listeners[0](navigation => const { result, handled } = listeners[0]((navigation) =>
navigation.canGoBack() navigation.canGoBack()
); );

View File

@@ -51,7 +51,7 @@ export default function SceneView<
const getCurrentState = React.useCallback(() => { const getCurrentState = React.useCallback(() => {
const state = getState(); const state = getState();
const currentRoute = state.routes.find(r => r.key === route.key); const currentRoute = state.routes.find((r) => r.key === route.key);
return currentRoute ? currentRoute.state : undefined; return currentRoute ? currentRoute.state : undefined;
}, [getState, route.key]); }, [getState, route.key]);
@@ -62,7 +62,7 @@ export default function SceneView<
setState({ setState({
...state, ...state,
routes: state.routes.map(r => routes: state.routes.map((r) =>
r.key === route.key ? { ...r, state: child } : r r.key === route.key ? { ...r, state: child } : r
), ),
}); });

View File

@@ -122,7 +122,7 @@ it('handle dispatching with ref', () => {
return ( return (
<React.Fragment> <React.Fragment>
{state.routes.map(route => descriptors[route.key].render())} {state.routes.map((route) => descriptors[route.key].render())}
</React.Fragment> </React.Fragment>
); );
}; };
@@ -220,7 +220,7 @@ it('handle resetting state with ref', () => {
return ( return (
<React.Fragment> <React.Fragment>
{state.routes.map(route => descriptors[route.key].render())} {state.routes.map((route) => descriptors[route.key].render())}
</React.Fragment> </React.Fragment>
); );
}; };
@@ -371,7 +371,7 @@ it('emits state events when the state changes', () => {
return ( return (
<React.Fragment> <React.Fragment>
{state.routes.map(route => descriptors[route.key].render())} {state.routes.map((route) => descriptors[route.key].render())}
</React.Fragment> </React.Fragment>
); );
}; };

View File

@@ -27,7 +27,7 @@ export default function MockRouter(options: DefaultRouterOptions) {
key: String(MockRouterKey.current++), key: String(MockRouterKey.current++),
index, index,
routeNames, routeNames,
routes: routeNames.map(name => ({ routes: routeNames.map((name) => ({
name, name,
key: name, key: name,
params: routeParamList[name], params: routeParamList[name],
@@ -43,9 +43,9 @@ export default function MockRouter(options: DefaultRouterOptions) {
} }
const routes = state.routes const routes = state.routes
.filter(route => routeNames.includes(route.name)) .filter((route) => routeNames.includes(route.name))
.map( .map(
route => (route) =>
({ ({
...route, ...route,
key: route.key || `${route.name}-${MockRouterKey.current++}`, key: route.key || `${route.name}-${MockRouterKey.current++}`,
@@ -73,7 +73,7 @@ export default function MockRouter(options: DefaultRouterOptions) {
}, },
getStateForRouteNamesChange(state, { routeNames }) { getStateForRouteNamesChange(state, { routeNames }) {
const routes = state.routes.filter(route => const routes = state.routes.filter((route) =>
routeNames.includes(route.name) routeNames.includes(route.name)
); );
@@ -86,7 +86,7 @@ export default function MockRouter(options: DefaultRouterOptions) {
}, },
getStateForRouteFocus(state, key) { getStateForRouteFocus(state, key) {
const index = state.routes.findIndex(r => r.key === key); const index = state.routes.findIndex((r) => r.key === key);
if (index === -1 || index === state.index) { if (index === -1 || index === state.index) {
return state; return state;
@@ -105,7 +105,7 @@ export default function MockRouter(options: DefaultRouterOptions) {
case 'NAVIGATE': { case 'NAVIGATE': {
const index = state.routes.findIndex( const index = state.routes.findIndex(
route => route.name === action.payload.name (route) => route.name === action.payload.name
); );
if (index === -1) { if (index === -1) {

View File

@@ -42,7 +42,8 @@ it('converts state to path string with config', () => {
Baz: { Baz: {
path: 'baz/:author', path: 'baz/:author',
parse: { parse: {
author: (author: string) => author.replace(/^\w/, c => c.toUpperCase()), author: (author: string) =>
author.replace(/^\w/, (c) => c.toUpperCase()),
id: (id: string) => Number(id.replace(/^x/, '')), id: (id: string) => Number(id.replace(/^x/, '')),
valid: Boolean, valid: Boolean,
}, },
@@ -128,7 +129,8 @@ it('handles state with config with nested screens', () => {
Baz: { Baz: {
path: 'baz/:author', path: 'baz/:author',
parse: { parse: {
author: (author: string) => author.replace(/^\w/, c => c.toUpperCase()), author: (author: string) =>
author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number, count: Number,
valid: Boolean, valid: Boolean,
}, },
@@ -192,12 +194,14 @@ it('handles state with config with nested screens and unused configs', () => {
Baz: { Baz: {
path: 'baz/:author', path: 'baz/:author',
parse: { parse: {
author: (author: string) => author.replace(/^\w/, c => c.toUpperCase()), author: (author: string) =>
author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number, count: Number,
valid: Boolean, valid: Boolean,
}, },
stringify: { stringify: {
author: (author: string) => author.replace(/^\w/, c => c.toLowerCase()), author: (author: string) =>
author.replace(/^\w/, (c) => c.toLowerCase()),
unknown: (_: unknown) => 'x', unknown: (_: unknown) => 'x',
}, },
}, },
@@ -255,11 +259,11 @@ it('handles nested object with stringify in it', () => {
path: 'bis/:author', path: 'bis/:author',
stringify: { stringify: {
author: (author: string) => author: (author: string) =>
author.replace(/^\w/, c => c.toLowerCase()), author.replace(/^\w/, (c) => c.toLowerCase()),
}, },
parse: { parse: {
author: (author: string) => author: (author: string) =>
author.replace(/^\w/, c => c.toUpperCase()), author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number, count: Number,
valid: Boolean, valid: Boolean,
}, },

View File

@@ -42,7 +42,8 @@ it('converts path string to initial state with config', () => {
Baz: { Baz: {
path: 'baz/:author', path: 'baz/:author',
parse: { parse: {
author: (author: string) => author.replace(/^\w/, c => c.toUpperCase()), author: (author: string) =>
author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number, count: Number,
valid: Boolean, valid: Boolean,
}, },
@@ -153,7 +154,8 @@ it('converts path string to initial state with config with nested screens', () =
Baz: { Baz: {
path: 'baz/:author', path: 'baz/:author',
parse: { parse: {
author: (author: string) => author.replace(/^\w/, c => c.toUpperCase()), author: (author: string) =>
author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number, count: Number,
valid: Boolean, valid: Boolean,
}, },
@@ -217,7 +219,8 @@ it('converts path string to initial state with config with nested screens and un
Baz: { Baz: {
path: 'baz/:author', path: 'baz/:author',
parse: { parse: {
author: (author: string) => author.replace(/^\w/, c => c.toUpperCase()), author: (author: string) =>
author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number, count: Number,
valid: Boolean, valid: Boolean,
id: Boolean, id: Boolean,
@@ -277,11 +280,11 @@ it('handles nested object with unused configs and with parse in it', () => {
path: 'bis/:author', path: 'bis/:author',
stringify: { stringify: {
author: (author: string) => author: (author: string) =>
author.replace(/^\w/, c => c.toLowerCase()), author.replace(/^\w/, (c) => c.toLowerCase()),
}, },
parse: { parse: {
author: (author: string) => author: (author: string) =>
author.replace(/^\w/, c => c.toUpperCase()), author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number, count: Number,
valid: Boolean, valid: Boolean,
}, },
@@ -528,11 +531,11 @@ it('handles two initialRouteNames', () => {
path: 'bis/:author', path: 'bis/:author',
stringify: { stringify: {
author: (author: string) => author: (author: string) =>
author.replace(/^\w/, c => c.toLowerCase()), author.replace(/^\w/, (c) => c.toLowerCase()),
}, },
parse: { parse: {
author: (author: string) => author: (author: string) =>
author.replace(/^\w/, c => c.toUpperCase()), author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number, count: Number,
valid: Boolean, valid: Boolean,
}, },
@@ -610,11 +613,11 @@ it('accepts initialRouteName without config for it', () => {
path: 'bis/:author', path: 'bis/:author',
stringify: { stringify: {
author: (author: string) => author: (author: string) =>
author.replace(/^\w/, c => c.toLowerCase()), author.replace(/^\w/, (c) => c.toLowerCase()),
}, },
parse: { parse: {
author: (author: string) => author: (author: string) =>
author.replace(/^\w/, c => c.toUpperCase()), author.replace(/^\w/, (c) => c.toUpperCase()),
count: Number, count: Number,
valid: Boolean, valid: Boolean,
}, },

View File

@@ -119,7 +119,7 @@ it('sets options with screenOptions prop as an object', () => {
return ( return (
<> <>
{state.routes.map(route => { {state.routes.map((route) => {
const { render, options } = descriptors[route.key]; const { render, options } = descriptors[route.key];
return ( return (
@@ -179,7 +179,7 @@ it('sets options with screenOptions prop as a fuction', () => {
return ( return (
<> <>
{state.routes.map(route => { {state.routes.map((route) => {
const { render, options } = descriptors[route.key]; const { render, options } = descriptors[route.key];
return ( return (
@@ -273,7 +273,7 @@ it('sets initial options with setOptions', () => {
<BaseNavigationContainer> <BaseNavigationContainer>
<TestNavigator> <TestNavigator>
<Screen name="foo" options={{ color: 'blue' }}> <Screen name="foo" options={{ color: 'blue' }}>
{props => <TestScreen {...props} />} {(props) => <TestScreen {...props} />}
</Screen> </Screen>
<Screen name="bar" component={jest.fn()} /> <Screen name="bar" component={jest.fn()} />
</TestNavigator> </TestNavigator>
@@ -338,7 +338,7 @@ it('updates options with setOptions', () => {
<BaseNavigationContainer> <BaseNavigationContainer>
<TestNavigator> <TestNavigator>
<Screen name="foo" options={{ color: 'blue' }}> <Screen name="foo" options={{ color: 'blue' }}>
{props => <TestScreen {...props} />} {(props) => <TestScreen {...props} />}
</Screen> </Screen>
<Screen name="bar" component={jest.fn()} /> <Screen name="bar" component={jest.fn()} />
</TestNavigator> </TestNavigator>

View File

@@ -15,7 +15,7 @@ it('fires focus and blur events in root navigator', () => {
React.useImperativeHandle(ref, () => navigation, [navigation]); React.useImperativeHandle(ref, () => navigation, [navigation]);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}); });
const firstFocusCallback = jest.fn(); const firstFocusCallback = jest.fn();
@@ -106,7 +106,7 @@ it('fires focus and blur events in nested navigator', () => {
React.useImperativeHandle(ref, () => navigation, [navigation]); React.useImperativeHandle(ref, () => navigation, [navigation]);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}); });
const firstFocusCallback = jest.fn(); const firstFocusCallback = jest.fn();
@@ -376,7 +376,7 @@ it('fires custom events added with addListener', () => {
state, state,
]); ]);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}); });
const firstCallback = jest.fn(); const firstCallback = jest.fn();
@@ -456,7 +456,7 @@ it("doesn't call same listener multiple times with addListener", () => {
state, state,
]); ]);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}); });
const callback = jest.fn(); const callback = jest.fn();
@@ -640,7 +640,7 @@ it('has option to prevent default', () => {
state, state,
]); ]);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}); });
const callback = (e: any) => { const callback = (e: any) => {

View File

@@ -10,7 +10,7 @@ it('runs focus effect on focus change', () => {
const TestNavigator = (props: any): any => { const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props); const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}; };
const focusEffect = jest.fn(); const focusEffect = jest.fn();
@@ -107,7 +107,7 @@ it('runs focus effect when initial state is given', () => {
const TestNavigator = (props: any): any => { const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props); const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}; };
const focusEffect = jest.fn(); const focusEffect = jest.fn();

View File

@@ -10,7 +10,7 @@ it('renders correct focus state', () => {
const TestNavigator = (props: any): any => { const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props); const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}; };
const Test = () => { const Test = () => {

View File

@@ -12,7 +12,7 @@ it('gets navigation prop from context', () => {
const TestNavigator = (props: any): any => { const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props); const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}; };
const Test = () => { const Test = () => {
@@ -38,7 +38,7 @@ it("gets navigation's parent from context", () => {
const TestNavigator = (props: any): any => { const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props); const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}; };
const Test = () => { const Test = () => {
@@ -70,7 +70,7 @@ it("gets navigation's parent's parent from context", () => {
const TestNavigator = (props: any): any => { const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props); const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}; };
const Test = () => { const Test = () => {

View File

@@ -1,7 +1,10 @@
import * as React from 'react'; import * as React from 'react';
import { render } from 'react-native-testing-library'; import { render, act } from 'react-native-testing-library';
import useEventEmitter from '../useEventEmitter'; import useEventEmitter from '../useEventEmitter';
import useNavigationCache from '../useNavigationCache'; import useNavigationCache from '../useNavigationCache';
import useNavigationBuilder from '../useNavigationBuilder';
import BaseNavigationContainer from '../BaseNavigationContainer';
import Screen from '../Screen';
import MockRouter, { MockRouterKey } from './__fixtures__/MockRouter'; import MockRouter, { MockRouterKey } from './__fixtures__/MockRouter';
beforeEach(() => (MockRouterKey.current = 0)); beforeEach(() => (MockRouterKey.current = 0));
@@ -40,7 +43,7 @@ it('preserves reference for navigation objects', () => {
}); });
if (previous.current) { if (previous.current) {
Object.keys(navigations).forEach(key => { Object.keys(navigations).forEach((key) => {
expect(navigations[key]).toBe(previous.current[key]); expect(navigations[key]).toBe(previous.current[key]);
}); });
} }
@@ -56,3 +59,136 @@ it('preserves reference for navigation objects', () => {
root.update(<Test />); root.update(<Test />);
}); });
it('returns correct value for isFocused', () => {
const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return state.routes.map((route) => descriptors[route.key].render());
};
let navigation: any;
const Test = (props: any) => {
navigation = props.navigation;
return null;
};
render(
<BaseNavigationContainer>
<TestNavigator>
<Screen name="first">{() => null}</Screen>
<Screen name="second" component={Test} />
<Screen name="third">{() => null}</Screen>
</TestNavigator>
</BaseNavigationContainer>
);
expect(navigation.isFocused()).toBe(false);
act(() => navigation.navigate('second'));
expect(navigation.isFocused()).toBe(true);
act(() => navigation.navigate('third'));
expect(navigation.isFocused()).toBe(false);
act(() => navigation.navigate('second'));
expect(navigation.isFocused()).toBe(true);
});
it('returns correct value for isFocused after changing screens', () => {
const TestRouter = (
options: Parameters<typeof MockRouter>[0]
): ReturnType<typeof MockRouter> => {
const router = MockRouter(options);
return {
...router,
getStateForRouteNamesChange(state, { routeNames }) {
const routes = routeNames.map(
(name) =>
state.routes.find((r) => r.name === name) || {
name,
key: name,
}
);
return {
...state,
routeNames,
routes,
index: routes.length - 1,
};
},
};
};
const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(TestRouter, props);
return state.routes.map((route) => descriptors[route.key].render());
};
let navigation: any;
const Test = (props: any) => {
navigation = props.navigation;
return null;
};
const root = render(
<BaseNavigationContainer>
<TestNavigator>
<Screen name="first">{() => null}</Screen>
<Screen name="second" component={Test} />
<Screen name="third">{() => null}</Screen>
</TestNavigator>
</BaseNavigationContainer>
);
expect(navigation.isFocused()).toBe(false);
root.update(
<BaseNavigationContainer>
<TestNavigator>
<Screen name="first">{() => null}</Screen>
<Screen name="third">{() => null}</Screen>
<Screen name="second" component={Test} />
</TestNavigator>
</BaseNavigationContainer>
);
expect(navigation.isFocused()).toBe(true);
root.update(
<BaseNavigationContainer>
<TestNavigator>
<Screen name="first">{() => null}</Screen>
<Screen name="third">{() => null}</Screen>
<Screen name="fourth">{() => null}</Screen>
<Screen name="second" component={Test} />
</TestNavigator>
</BaseNavigationContainer>
);
expect(navigation.isFocused()).toBe(true);
root.update(
<BaseNavigationContainer>
<TestNavigator>
<Screen name="first">{() => null}</Screen>
<Screen name="third">{() => null}</Screen>
<Screen name="second" component={Test} />
<Screen name="fourth">{() => null}</Screen>
</TestNavigator>
</BaseNavigationContainer>
);
expect(navigation.isFocused()).toBe(false);
});

View File

@@ -11,13 +11,13 @@ it('gets the current navigation state', () => {
const TestNavigator = (props: any): any => { const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props); const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}; };
const callback = jest.fn(); const callback = jest.fn();
const Test = () => { const Test = () => {
const state = useNavigationState(state => state); const state = useNavigationState((state) => state);
callback(state); callback(state);
@@ -62,13 +62,13 @@ it('gets the current navigation state with selector', () => {
const TestNavigator = (props: any): any => { const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props); const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}; };
const callback = jest.fn(); const callback = jest.fn();
const Test = () => { const Test = () => {
const index = useNavigationState(state => state.index); const index = useNavigationState((state) => state.index);
callback(index); callback(index);
@@ -112,7 +112,7 @@ it('gets the correct value if selector changes', () => {
const TestNavigator = (props: any): any => { const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props); const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}; };
const callback = jest.fn(); const callback = jest.fn();
@@ -144,12 +144,12 @@ it('gets the correct value if selector changes', () => {
); );
}; };
const root = render(<App selector={state => state.index} />); const root = render(<App selector={(state) => state.index} />);
expect(callback).toBeCalledTimes(1); expect(callback).toBeCalledTimes(1);
expect(callback.mock.calls[0][0]).toBe(0); expect(callback.mock.calls[0][0]).toBe(0);
root.update(<App selector={state => state.routes[state.index].name} />); root.update(<App selector={(state) => state.routes[state.index].name} />);
expect(callback).toBeCalledTimes(2); expect(callback).toBeCalledTimes(2);
expect(callback.mock.calls[1][0]).toBe('first'); expect(callback.mock.calls[1][0]).toBe('first');

View File

@@ -137,7 +137,7 @@ it("lets children handle the action if parent didn't", () => {
return ( return (
<React.Fragment> <React.Fragment>
{state.routes.map(route => descriptors[route.key].render())} {state.routes.map((route) => descriptors[route.key].render())}
</React.Fragment> </React.Fragment>
); );
}; };
@@ -270,7 +270,7 @@ it("action doesn't bubble if target is specified", () => {
return ( return (
<React.Fragment> <React.Fragment>
{state.routes.map(route => descriptors[route.key].render())} {state.routes.map((route) => descriptors[route.key].render())}
</React.Fragment> </React.Fragment>
); );
}; };
@@ -317,7 +317,7 @@ it('logs error if no navigator handled the action', () => {
return ( return (
<React.Fragment> <React.Fragment>
{state.routes.map(route => descriptors[route.key].render())} {state.routes.map((route) => descriptors[route.key].render())}
</React.Fragment> </React.Fragment>
); );
}; };

View File

@@ -13,7 +13,7 @@ it('gets route prop from context', () => {
const TestNavigator = (props: any): any => { const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props); const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return state.routes.map(route => descriptors[route.key].render()); return state.routes.map((route) => descriptors[route.key].render());
}; };
const Test = () => { const Test = () => {

View File

@@ -16,7 +16,7 @@ export default function createNavigatorFactory<
EventMap extends EventMapBase, EventMap extends EventMapBase,
NavigatorComponent extends React.ComponentType<any> NavigatorComponent extends React.ComponentType<any>
>(Navigator: NavigatorComponent) { >(Navigator: NavigatorComponent) {
return function<ParamList extends ParamListBase>(): TypedNavigator< return function <ParamList extends ParamListBase>(): TypedNavigator<
ParamList, ParamList,
State, State,
ScreenOptions, ScreenOptions,

View File

@@ -125,7 +125,7 @@ export default function getPathFromState(
if (currentOptions[route.name] !== undefined) { if (currentOptions[route.name] !== undefined) {
path += pattern path += pattern
.split('/') .split('/')
.map(p => { .map((p) => {
const name = p.replace(/^:/, ''); const name = p.replace(/^:/, '');
// If the path has a pattern for a param, put the param in the path // If the path has a pattern for a param, put the param in the path

View File

@@ -64,7 +64,7 @@ export default function getStateFromPath(
let initialRoutes: InitialRouteConfig[] = []; let initialRoutes: InitialRouteConfig[] = [];
// Create a normalized configs array which will be easier to use // Create a normalized configs array which will be easier to use
const configs = ([] as RouteConfig[]).concat( const configs = ([] as RouteConfig[]).concat(
...Object.keys(options).map(key => ...Object.keys(options).map((key) =>
createNormalizedConfigs(key, options, [], initialRoutes) createNormalizedConfigs(key, options, [], initialRoutes)
) )
); );
@@ -91,7 +91,7 @@ export default function getStateFromPath(
const paramPatterns = config.pattern const paramPatterns = config.pattern
.split('/') .split('/')
.filter(p => p.startsWith(':')); .filter((p) => p.startsWith(':'));
if (paramPatterns.length) { if (paramPatterns.length) {
params = paramPatterns.reduce<Record<string, any>>((acc, p, i) => { params = paramPatterns.reduce<Record<string, any>>((acc, p, i) => {
@@ -188,7 +188,7 @@ export default function getStateFromPath(
const parseFunction = findParseConfigForRoute(route.name, configs); const parseFunction = findParseConfigForRoute(route.name, configs);
if (parseFunction) { if (parseFunction) {
Object.keys(params).forEach(name => { Object.keys(params).forEach((name) => {
if (parseFunction[name] && typeof params[name] === 'string') { if (parseFunction[name] && typeof params[name] === 'string') {
params[name] = parseFunction[name](params[name] as string); params[name] = parseFunction[name](params[name] as string);
} }
@@ -233,7 +233,7 @@ function createNormalizedConfigs(
connectedRoutes: Object.keys(value.screens), connectedRoutes: Object.keys(value.screens),
}); });
} }
Object.keys(value.screens).forEach(nestedConfig => { Object.keys(value.screens).forEach((nestedConfig) => {
const result = createNormalizedConfigs( const result = createNormalizedConfigs(
nestedConfig, nestedConfig,
value.screens as Options, value.screens as Options,

View File

@@ -346,6 +346,16 @@ export type Descriptor<
>; >;
}; };
export type ScreenListeners<
State extends NavigationState,
EventMap extends EventMapBase
> = Partial<
{
[EventName in keyof (EventMap &
EventMapCore<State>)]: EventListenerCallback<EventMap, EventName>;
}
>;
export type RouteConfig< export type RouteConfig<
ParamList extends ParamListBase, ParamList extends ParamListBase,
RouteName extends keyof ParamList, RouteName extends keyof ParamList,
@@ -371,12 +381,12 @@ export type RouteConfig<
/** /**
* Event listeners for this screen. * Event listeners for this screen.
*/ */
listeners?: Partial< listeners?:
{ | ScreenListeners<State, EventMap>
[EventName in keyof (EventMap & | ((props: {
EventMapCore<State>)]: EventListenerCallback<EventMap, EventName>; route: RouteProp<ParamList, RouteName>;
} navigation: any;
>; }) => ScreenListeners<State, EventMap>);
/** /**
* Initial params object for the route. * Initial params object for the route.

View File

@@ -8,6 +8,7 @@ import {
type State = NavigationState | PartialState<NavigationState> | undefined; type State = NavigationState | PartialState<NavigationState> | undefined;
type Options = { type Options = {
enabled: boolean;
name: string; name: string;
reset: (state: NavigationState) => void; reset: (state: NavigationState) => void;
state: State; state: State;
@@ -35,10 +36,11 @@ declare global {
} }
} }
export default function useDevTools({ name, reset, state }: Options) { export default function useDevTools({ name, reset, state, enabled }: Options) {
const devToolsRef = React.useRef<DevTools>(); const devToolsRef = React.useRef<DevTools>();
if ( if (
enabled &&
process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'production' &&
global.__REDUX_DEVTOOLS_EXTENSION__ && global.__REDUX_DEVTOOLS_EXTENSION__ &&
devToolsRef.current === undefined devToolsRef.current === undefined
@@ -56,7 +58,7 @@ export default function useDevTools({ name, reset, state }: Options) {
React.useEffect( React.useEffect(
() => () =>
devTools?.subscribe(message => { devTools?.subscribe((message) => {
if (message.type === 'DISPATCH' && message.state) { if (message.type === 'DISPATCH' && message.state) {
reset(JSON.parse(message.state)); reset(JSON.parse(message.state));
} }

View File

@@ -69,7 +69,7 @@ export default function useEventEmitter(
target !== undefined target !== undefined
? items[target] && items[target].slice() ? items[target] && items[target].slice()
: ([] as Listeners) : ([] as Listeners)
.concat(...Object.keys(items).map(t => items[t])) .concat(...Object.keys(items).map((t) => items[t]))
.filter((cb, i, self) => self.lastIndexOf(cb) === i); .filter((cb, i, self) => self.lastIndexOf(cb) === i);
const event: EventArg<any, any, any> = { const event: EventArg<any, any, any> = {
@@ -117,7 +117,7 @@ export default function useEventEmitter(
listenRef.current?.(event); listenRef.current?.(event);
callbacks?.forEach(cb => cb(event)); callbacks?.forEach((cb) => cb(event));
return event as any; return event as any;
}, },

View File

@@ -9,6 +9,7 @@ import {
RouterFactory, RouterFactory,
PartialState, PartialState,
NavigationAction, NavigationAction,
Route,
} from '@react-navigation/routers'; } from '@react-navigation/routers';
import { NavigationStateContext } from './BaseNavigationContainer'; import { NavigationStateContext } from './BaseNavigationContainer';
import NavigationRouteContext from './NavigationRouteContext'; import NavigationRouteContext from './NavigationRouteContext';
@@ -103,7 +104,7 @@ const getRouteConfigsFromChildren = <
}, []); }, []);
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
configs.forEach(config => { configs.forEach((config) => {
const { name, children, component } = config as any; const { name, children, component } = config as any;
if (typeof name !== 'string' || !name) { if (typeof name !== 'string' || !name) {
@@ -212,7 +213,7 @@ export default function useNavigationBuilder<
return acc; return acc;
}, {}); }, {});
const routeNames = routeConfigs.map(config => config.name); const routeNames = routeConfigs.map((config) => config.name);
const routeParamList = routeNames.reduce<Record<string, object | undefined>>( const routeParamList = routeNames.reduce<Record<string, object | undefined>>(
(acc, curr) => { (acc, curr) => {
const { initialParams } = screens[curr]; const { initialParams } = screens[curr];
@@ -241,12 +242,12 @@ export default function useNavigationBuilder<
} }
const isStateValid = React.useCallback( const isStateValid = React.useCallback(
state => state.type === undefined || state.type === router.type, (state) => state.type === undefined || state.type === router.type,
[router.type] [router.type]
); );
const isStateInitialized = React.useCallback( const isStateInitialized = React.useCallback(
state => (state) =>
state !== undefined && state.stale === false && isStateValid(state), state !== undefined && state.stale === false && isStateValid(state),
[isStateValid] [isStateValid]
); );
@@ -367,14 +368,16 @@ export default function useNavigationBuilder<
: (initializedStateRef.current as State); : (initializedStateRef.current as State);
}, [getCurrentState, isStateInitialized]); }, [getCurrentState, isStateInitialized]);
const emitter = useEventEmitter(e => { const emitter = useEventEmitter((e) => {
let routeNames = []; let routeNames = [];
if (e.target) { let target: Route<string> | undefined;
const name = state.routes.find(route => route.key === e.target)?.name;
if (name) { if (e.target) {
routeNames.push(name); target = state.routes.find((route) => route.key === e.target);
if (target?.name) {
routeNames.push(target.name);
} }
} else { } else {
routeNames.push(...Object.keys(screens)); routeNames.push(...Object.keys(screens));
@@ -382,19 +385,28 @@ export default function useNavigationBuilder<
const listeners = ([] as (((e: any) => void) | undefined)[]) const listeners = ([] as (((e: any) => void) | undefined)[])
.concat( .concat(
...routeNames.map(name => { ...routeNames.map((name) => {
const { listeners } = screens[name]; const { listeners } = screens[name];
return listeners return listeners
? Object.keys(listeners) ? Object.keys(listeners)
.filter(type => type === e.type) .filter((type) => type === e.type)
.map(type => listeners[type]) .map((type) => {
if (typeof listeners === 'function') {
const route = target ?? state.routes[state.index];
const navigation = descriptors[route.key].navigation;
return listeners({ route: route as any, navigation })[type];
}
return listeners[type];
})
: undefined; : undefined;
}) })
) )
.filter((cb, i, self) => cb && self.lastIndexOf(cb) === i); .filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
listeners.forEach(listener => listener?.(e)); listeners.forEach((listener) => listener?.(e));
}); });
useFocusEvents({ state, emitter }); useFocusEvents({ state, emitter });

View File

@@ -63,7 +63,7 @@ export default function useNavigationCache<
}; };
cache.current = state.routes.reduce<NavigationCache<State, ScreenOptions>>( cache.current = state.routes.reduce<NavigationCache<State, ScreenOptions>>(
(acc, route, index) => { (acc, route) => {
const previous = cache.current[route.key]; const previous = cache.current[route.key];
if (previous) { if (previous) {
@@ -103,14 +103,14 @@ export default function useNavigationCache<
dangerouslyGetState: getState, dangerouslyGetState: getState,
dispatch, dispatch,
setOptions: (options: object) => setOptions: (options: object) =>
setOptions(o => ({ setOptions((o) => ({
...o, ...o,
[route.key]: { ...o[route.key], ...options }, [route.key]: { ...o[route.key], ...options },
})), })),
isFocused: () => { isFocused: () => {
const state = getState(); const state = getState();
if (index !== state.index) { if (state.routes[state.index].key !== route.key) {
return false; return false;
} }

View File

@@ -26,7 +26,7 @@ export default function useNavigationState<T>(selector: Selector<T>): T {
}); });
React.useEffect(() => { React.useEffect(() => {
const unsubscribe = navigation.addListener('state', e => { const unsubscribe = navigation.addListener('state', (e) => {
setResult(selectorRef.current(e.data.state)); setResult(selectorRef.current(e.data.state));
}); });

View File

@@ -18,7 +18,7 @@ export default function useOnGetState({
const state = getState(); const state = getState();
return { return {
...state, ...state,
routes: state.routes.map(route => ({ routes: state.routes.map((route) => ({
...route, ...route,
state: getStateForRoute(route.key), state: getStateForRoute(route.key),
})), })),

View File

@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.3.3](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.3.2...@react-navigation/drawer@5.3.3) (2020-03-22)
**Note:** Version bump only for package @react-navigation/drawer
## [5.3.2](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.3.1...@react-navigation/drawer@5.3.2) (2020-03-19)
### Bug Fixes
* close drawer on pressing Esc on web ([5c4afc5](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/5c4afc5cb40c1206a9d8c40efe3cf947030da48e)), closes [#6745](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/6745)
* don't use react-native-screens on web ([b1a65fc](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/b1a65fc73e8603ae2c06ef101a74df31e80bb9b2)), closes [#7485](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/7485)
* fix permanent sidebar position ([#7830](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/7830)) ([3ea8eec](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/3ea8eec4324ea82f0ed427f4662e68e1115e60ab))
* initialize height and width to zero if undefined ([3df65e2](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/commit/3df65e28197db3bb8371059146546d57661c5ba3)), closes [#6789](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/issues/6789)
## [5.3.1](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.3.0...@react-navigation/drawer@5.3.1) (2020-03-17)
**Note:** Version bump only for package @react-navigation/drawer
# [5.3.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.2.0...@react-navigation/drawer@5.3.0) (2020-03-17) # [5.3.0](https://github.com/react-navigation/react-navigation/tree/master/packages/drawer/compare/@react-navigation/drawer@5.2.0...@react-navigation/drawer@5.3.0) (2020-03-17)

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/drawer", "name": "@react-navigation/drawer",
"description": "Drawer navigator component with animated transitions and gesturess", "description": "Drawer navigator component with animated transitions and gesturess",
"version": "5.3.0", "version": "5.3.3",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -40,7 +40,7 @@
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.10.0", "@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.1.0", "@react-navigation/native": "^5.1.2",
"@types/react": "^16.9.23", "@types/react": "^16.9.23",
"@types/react-native": "^0.61.22", "@types/react-native": "^0.61.22",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",

View File

@@ -1,44 +1,16 @@
import * as React from 'react'; import * as React from 'react';
import { useNavigation, ParamListBase } from '@react-navigation/native'; import DrawerOpenContext from './DrawerOpenContext';
import { DrawerNavigationProp } from '../types';
import DrawerOpenContext from '../views/DrawerOpenContext';
/** /**
* Hook to detect if the drawer is open in a parent navigator. * Hook to detect if the drawer is open in a parent navigator.
*/ */
export default function useIsDrawerOpen() { export default function useIsDrawerOpen() {
const navigation = useNavigation(); const isDrawerOpen = React.useContext(DrawerOpenContext);
let drawer = navigation as DrawerNavigationProp<ParamListBase>; if (typeof isDrawerOpen !== 'boolean') {
throw new Error(
const drawerOpenContext = React.useContext(DrawerOpenContext); "Couldn't find a drawer. Is your component inside a drawer navigator?"
);
// The screen might be inside another navigator such as stack nested in drawer
// We need to find the closest drawer navigator and add the listener there
while (drawer && drawer.dangerouslyGetState().type !== 'drawer') {
drawer = drawer.dangerouslyGetParent();
}
const [isDrawerOpen, setIsDrawerOpen] = React.useState(() =>
drawer
? Boolean(
drawer.dangerouslyGetState().history.find(it => it.type === 'drawer')
)
: false
);
React.useEffect(() => {
const unsubscribe = drawer.addListener('state', e => {
setIsDrawerOpen(
Boolean(e.data.state.history.find(it => it.type === 'drawer'))
);
});
return unsubscribe;
}, [drawer, isDrawerOpen]);
if (drawerOpenContext !== null) {
return drawerOpenContext;
} }
return isDrawerOpen; return isDrawerOpen;

View File

@@ -18,7 +18,6 @@ import {
} from 'react-native-gesture-handler'; } from 'react-native-gesture-handler';
import Animated from 'react-native-reanimated'; import Animated from 'react-native-reanimated';
import Overlay from './Overlay'; import Overlay from './Overlay';
import DrawerOpenContext from './DrawerOpenContext';
const { const {
Clock, Clock,
@@ -128,6 +127,12 @@ export default class DrawerView extends React.PureComponent<Props> {
statusBarAnimation: 'slide', statusBarAnimation: 'slide',
}; };
componentDidMount() {
if (Platform.OS === 'web') {
document?.body?.addEventListener?.('keyup', this.handleEscape);
}
}
componentDidUpdate(prevProps: Props) { componentDidUpdate(prevProps: Props) {
const { const {
open, open,
@@ -183,8 +188,22 @@ export default class DrawerView extends React.PureComponent<Props> {
componentWillUnmount() { componentWillUnmount() {
this.toggleStatusBar(false); this.toggleStatusBar(false);
this.handleEndInteraction(); this.handleEndInteraction();
if (Platform.OS === 'web') {
document?.body?.removeEventListener?.('keyup', this.handleEscape);
}
} }
private handleEscape = (e: KeyboardEvent) => {
const { open, onClose } = this.props;
if (e.key === 'Escape') {
if (open) {
onClose();
}
}
};
private handleEndInteraction = () => { private handleEndInteraction = () => {
if (this.interactionHandle !== undefined) { if (this.interactionHandle !== undefined) {
InteractionManager.clearInteractionHandle(this.interactionHandle); InteractionManager.clearInteractionHandle(this.interactionHandle);
@@ -593,8 +612,9 @@ export default class DrawerView extends React.PureComponent<Props> {
onLayout={this.handleContainerLayout} onLayout={this.handleContainerLayout}
style={[ style={[
styles.main, styles.main,
drawerType === 'permanent' && { {
flexDirection: 'row-reverse', flexDirection:
drawerType === 'permanent' && !isRight ? 'row-reverse' : 'row',
}, },
]} ]}
> >
@@ -661,9 +681,7 @@ export default class DrawerView extends React.PureComponent<Props> {
drawerStyle as any, drawerStyle as any,
]} ]}
> >
<DrawerOpenContext.Provider value={isOpen}> {renderDrawerContent({ progress })}
{renderDrawerContent({ progress })}
</DrawerOpenContext.Provider>
</Animated.View> </Animated.View>
</Animated.View> </Animated.View>
</PanGestureHandler> </PanGestureHandler>

View File

@@ -72,14 +72,8 @@ export default function DrawerItem(props: Props) {
labelStyle, labelStyle,
focused = false, focused = false,
activeTintColor = colors.primary, activeTintColor = colors.primary,
inactiveTintColor = Color(colors.text) inactiveTintColor = Color(colors.text).alpha(0.68).rgb().string(),
.alpha(0.68) activeBackgroundColor = Color(activeTintColor).alpha(0.12).rgb().string(),
.rgb()
.string(),
activeBackgroundColor = Color(activeTintColor)
.alpha(0.12)
.rgb()
.string(),
inactiveBackgroundColor = 'transparent', inactiveBackgroundColor = 'transparent',
style, style,
onPress, onPress,

View File

@@ -32,6 +32,7 @@ import {
DrawerNavigationHelpers, DrawerNavigationHelpers,
DrawerContentComponentProps, DrawerContentComponentProps,
} from '../types'; } from '../types';
import DrawerOpenContext from '../utils/DrawerOpenContext';
import DrawerPositionContext from '../utils/DrawerPositionContext'; import DrawerPositionContext from '../utils/DrawerPositionContext';
type Props = DrawerNavigationConfig & { type Props = DrawerNavigationConfig & {
@@ -88,15 +89,17 @@ export default function DrawerView({
sceneContainerStyle, sceneContainerStyle,
}: Props) { }: Props) {
const [loaded, setLoaded] = React.useState([state.index]); const [loaded, setLoaded] = React.useState([state.index]);
const [drawerWidth, setDrawerWidth] = React.useState(() => const [drawerWidth, setDrawerWidth] = React.useState(() => {
getDefaultDrawerWidth(Dimensions.get('window')) const { height = 0, width = 0 } = Dimensions.get('window');
);
return getDefaultDrawerWidth({ height, width });
});
const drawerGestureRef = React.useRef<PanGestureHandler>(null); const drawerGestureRef = React.useRef<PanGestureHandler>(null);
const { colors } = useTheme(); const { colors } = useTheme();
const isDrawerOpen = Boolean(state.history.find(it => it.type === 'drawer')); const isDrawerOpen = state.history.some((it) => it.type === 'drawer');
const handleDrawerOpen = React.useCallback(() => { const handleDrawerOpen = React.useCallback(() => {
navigation.dispatch({ navigation.dispatch({
@@ -203,40 +206,48 @@ export default function DrawerView({
<GestureHandlerWrapper style={styles.content}> <GestureHandlerWrapper style={styles.content}>
<SafeAreaProviderCompat> <SafeAreaProviderCompat>
<DrawerGestureContext.Provider value={drawerGestureRef}> <DrawerGestureContext.Provider value={drawerGestureRef}>
<Drawer <DrawerOpenContext.Provider value={isDrawerOpen}>
open={isDrawerOpen} <Drawer
gestureEnabled={gestureEnabled} open={isDrawerOpen}
onOpen={handleDrawerOpen} gestureEnabled={gestureEnabled}
onClose={handleDrawerClose} onOpen={handleDrawerOpen}
onGestureRef={ref => { onClose={handleDrawerClose}
// @ts-ignore onGestureRef={(ref) => {
drawerGestureRef.current = ref; // @ts-ignore
}} drawerGestureRef.current = ref;
gestureHandlerProps={gestureHandlerProps} }}
drawerType={drawerType} gestureHandlerProps={gestureHandlerProps}
drawerPosition={drawerPosition} drawerType={drawerType}
sceneContainerStyle={[ drawerPosition={drawerPosition}
{ backgroundColor: colors.background }, sceneContainerStyle={[
sceneContainerStyle, { backgroundColor: colors.background },
]} sceneContainerStyle,
drawerStyle={[ ]}
{ width: drawerWidth, backgroundColor: colors.card }, drawerStyle={[
drawerType === 'permanent' && { { width: drawerWidth, backgroundColor: colors.card },
borderRightColor: colors.border, drawerType === 'permanent' &&
borderRightWidth: StyleSheet.hairlineWidth, (drawerPosition === 'left'
}, ? {
drawerStyle, borderRightColor: colors.border,
]} borderRightWidth: StyleSheet.hairlineWidth,
overlayStyle={{ backgroundColor: overlayColor }} }
swipeEdgeWidth={edgeWidth} : {
swipeDistanceThreshold={minSwipeDistance} borderLeftColor: colors.border,
hideStatusBar={hideStatusBar} borderLeftWidth: StyleSheet.hairlineWidth,
statusBarAnimation={statusBarAnimation} }),
renderDrawerContent={renderNavigationView} drawerStyle,
renderSceneContent={renderContent} ]}
keyboardDismissMode={keyboardDismissMode} overlayStyle={{ backgroundColor: overlayColor }}
drawerPostion={drawerPosition} swipeEdgeWidth={edgeWidth}
/> swipeDistanceThreshold={minSwipeDistance}
hideStatusBar={hideStatusBar}
statusBarAnimation={statusBarAnimation}
renderDrawerContent={renderNavigationView}
renderSceneContent={renderContent}
keyboardDismissMode={keyboardDismissMode}
drawerPostion={drawerPosition}
/>
</DrawerOpenContext.Provider>
</DrawerGestureContext.Provider> </DrawerGestureContext.Provider>
</SafeAreaProviderCompat> </SafeAreaProviderCompat>
</GestureHandlerWrapper> </GestureHandlerWrapper>

View File

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

View File

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

View File

@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.1.5](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.4...@react-navigation/material-bottom-tabs@5.1.5) (2020-03-22)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.1.4](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.3...@react-navigation/material-bottom-tabs@5.1.4) (2020-03-19)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
## [5.1.3](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.2...@react-navigation/material-bottom-tabs@5.1.3) (2020-03-17) ## [5.1.3](https://github.com/react-navigation/react-navigation/tree/master/packages/material-bottom-tabs/compare/@react-navigation/material-bottom-tabs@5.1.2...@react-navigation/material-bottom-tabs@5.1.3) (2020-03-17)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs **Note:** Version bump only for package @react-navigation/material-bottom-tabs

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/material-bottom-tabs", "name": "@react-navigation/material-bottom-tabs",
"description": "Integration for bottom navigation component from react-native-paper", "description": "Integration for bottom navigation component from react-native-paper",
"version": "5.1.3", "version": "5.1.5",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -36,7 +36,7 @@
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.10.0", "@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.1.0", "@react-navigation/native": "^5.1.2",
"@types/react": "^16.9.23", "@types/react": "^16.9.23",
"@types/react-native": "^0.61.22", "@types/react-native": "^0.61.22",
"@types/react-native-vector-icons": "^6.4.5", "@types/react-native-vector-icons": "^6.4.5",

View File

@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.1.5](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.4...@react-navigation/material-top-tabs@5.1.5) (2020-03-22)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.1.4](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.3...@react-navigation/material-top-tabs@5.1.4) (2020-03-19)
**Note:** Version bump only for package @react-navigation/material-top-tabs
## [5.1.3](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.2...@react-navigation/material-top-tabs@5.1.3) (2020-03-17) ## [5.1.3](https://github.com/react-navigation/react-navigation/tree/master/packages/material-top-tabs/compare/@react-navigation/material-top-tabs@5.1.2...@react-navigation/material-top-tabs@5.1.3) (2020-03-17)
**Note:** Version bump only for package @react-navigation/material-top-tabs **Note:** Version bump only for package @react-navigation/material-top-tabs

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/material-top-tabs", "name": "@react-navigation/material-top-tabs",
"description": "Integration for the animated tab view component from react-native-tab-view", "description": "Integration for the animated tab view component from react-native-tab-view",
"version": "5.1.3", "version": "5.1.5",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -39,7 +39,7 @@
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.10.0", "@react-native-community/bob": "^0.10.0",
"@react-navigation/native": "^5.1.0", "@react-navigation/native": "^5.1.2",
"@types/react": "^16.9.23", "@types/react": "^16.9.23",
"@types/react-native": "^0.61.22", "@types/react-native": "^0.61.22",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",

View File

@@ -14,17 +14,11 @@ export default function TabBarTop(props: MaterialTopTabBarProps) {
navigation, navigation,
descriptors, descriptors,
activeTintColor = colors.text, activeTintColor = colors.text,
inactiveTintColor = Color(activeTintColor) inactiveTintColor = Color(activeTintColor).alpha(0.5).rgb().string(),
.alpha(0.5)
.rgb()
.string(),
allowFontScaling = true, allowFontScaling = true,
showIcon = false, showIcon = false,
showLabel = true, showLabel = true,
pressColor = Color(activeTintColor) pressColor = Color(activeTintColor).alpha(0.08).rgb().string(),
.alpha(0.08)
.rgb()
.string(),
iconStyle, iconStyle,
labelStyle, labelStyle,
indicatorStyle, indicatorStyle,

View File

@@ -47,7 +47,7 @@ export default function MaterialTopTabView({
return ( return (
<TabView <TabView
{...rest} {...rest}
onIndexChange={index => onIndexChange={(index) =>
navigation.dispatch({ navigation.dispatch({
...TabActions.jumpTo(state.routes[index].name), ...TabActions.jumpTo(state.routes[index].name),
target: state.key, target: state.key,

View File

@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.1.2](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.1...@react-navigation/native@5.1.2) (2020-03-22)
**Note:** Version bump only for package @react-navigation/native
## [5.1.1](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.1.0...@react-navigation/native@5.1.1) (2020-03-19)
**Note:** Version bump only for package @react-navigation/native
# [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.0.10...@react-navigation/native@5.1.0) (2020-03-17) # [5.1.0](https://github.com/react-navigation/react-navigation/tree/master/packages/native/compare/@react-navigation/native@5.0.10...@react-navigation/native@5.1.0) (2020-03-17)

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/native", "name": "@react-navigation/native",
"description": "React Native integration for React Navigation", "description": "React Native integration for React Navigation",
"version": "5.1.0", "version": "5.1.2",
"keywords": [ "keywords": [
"react-native", "react-native",
"react-navigation", "react-navigation",
@@ -31,7 +31,7 @@
"clean": "del lib" "clean": "del lib"
}, },
"dependencies": { "dependencies": {
"@react-navigation/core": "^5.2.2" "@react-navigation/core": "^5.3.0"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.10.0", "@react-native-community/bob": "^0.10.0",

View File

@@ -1,4 +1,4 @@
export default function() { export default function () {
throw new Error( throw new Error(
"'NavigationNativeContainer' has been renamed to 'NavigationContainer" "'NavigationNativeContainer' has been renamed to 'NavigationContainer"
); );

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.2.0](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.1.1...@react-navigation/routers@5.2.0) (2020-03-22)
### Features
* add keys to routes missing keys during reset ([813a590](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/commit/813a5903b5f44506b9097538ed85229e565b855e))
## [5.1.1](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.1.0...@react-navigation/routers@5.1.1) (2020-03-16) ## [5.1.1](https://github.com/react-navigation/react-navigation/tree/master/packages/routers/compare/@react-navigation/routers@5.1.0...@react-navigation/routers@5.1.1) (2020-03-16)

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/routers", "name": "@react-navigation/routers",
"description": "Routers to help build custom navigators", "description": "Routers to help build custom navigators",
"version": "5.1.1", "version": "5.2.0",
"keywords": [ "keywords": [
"react", "react",
"react-native", "react-native",

View File

@@ -1,3 +1,4 @@
import shortid from 'shortid';
import { CommonNavigationAction, NavigationState, PartialState } from './types'; import { CommonNavigationAction, NavigationState, PartialState } from './types';
/** /**
@@ -12,7 +13,7 @@ const BaseRouter = {
switch (action.type) { switch (action.type) {
case 'SET_PARAMS': { case 'SET_PARAMS': {
const index = action.source const index = action.source
? state.routes.findIndex(r => r.key === action.source) ? state.routes.findIndex((r) => r.key === action.source)
: state.index; : state.index;
if (index === -1) { if (index === -1) {
@@ -32,15 +33,6 @@ const BaseRouter = {
case 'RESET': { case 'RESET': {
const nextState = action.payload as State | PartialState<State>; const nextState = action.payload as State | PartialState<State>;
if (nextState.stale === false) {
if (
state.routeNames.length !== nextState.routeNames.length ||
nextState.routeNames.some(name => !state.routeNames.includes(name))
) {
return null;
}
}
if ( if (
nextState.routes.length === 0 || nextState.routes.length === 0 ||
nextState.routes.some( nextState.routes.some(
@@ -50,6 +42,26 @@ const BaseRouter = {
return null; return null;
} }
if (nextState.stale === false) {
if (
state.routeNames.length !== nextState.routeNames.length ||
nextState.routeNames.some(
(name) => !state.routeNames.includes(name)
)
) {
return null;
}
return {
...nextState,
routes: nextState.routes.map((route) =>
route.key
? route
: { ...route, key: `${route.name}-${shortid()}` }
),
};
}
return nextState; return nextState;
} }

View File

@@ -71,7 +71,7 @@ export const DrawerActions = {
const isDrawerOpen = ( const isDrawerOpen = (
state: DrawerNavigationState | PartialState<DrawerNavigationState> state: DrawerNavigationState | PartialState<DrawerNavigationState>
) => Boolean(state.history?.find(it => it.type === 'drawer')); ) => Boolean(state.history?.find((it) => it.type === 'drawer'));
const openDrawer = (state: DrawerNavigationState): DrawerNavigationState => { const openDrawer = (state: DrawerNavigationState): DrawerNavigationState => {
if (isDrawerOpen(state)) { if (isDrawerOpen(state)) {
@@ -91,7 +91,7 @@ const closeDrawer = (state: DrawerNavigationState): DrawerNavigationState => {
return { return {
...state, ...state,
history: state.history.filter(it => it.type !== 'drawer'), history: state.history.filter((it) => it.type !== 'drawer'),
}; };
}; };

View File

@@ -134,9 +134,9 @@ export default function StackRouter(options: StackRouterOptions) {
} }
const routes = state.routes const routes = state.routes
.filter(route => routeNames.includes(route.name)) .filter((route) => routeNames.includes(route.name))
.map( .map(
route => (route) =>
({ ({
...route, ...route,
key: route.key || `${route.name}-${shortid()}`, key: route.key || `${route.name}-${shortid()}`,
@@ -174,7 +174,7 @@ export default function StackRouter(options: StackRouterOptions) {
}, },
getStateForRouteNamesChange(state, { routeNames, routeParamList }) { getStateForRouteNamesChange(state, { routeNames, routeParamList }) {
const routes = state.routes.filter(route => const routes = state.routes.filter((route) =>
routeNames.includes(route.name) routeNames.includes(route.name)
); );
@@ -201,7 +201,7 @@ export default function StackRouter(options: StackRouterOptions) {
}, },
getStateForRouteFocus(state, key) { getStateForRouteFocus(state, key) {
const index = state.routes.findIndex(r => r.key === key); const index = state.routes.findIndex((r) => r.key === key);
if (index === -1 || index === state.index) { if (index === -1 || index === state.index) {
return state; return state;
@@ -220,7 +220,7 @@ export default function StackRouter(options: StackRouterOptions) {
switch (action.type) { switch (action.type) {
case 'REPLACE': { case 'REPLACE': {
const index = action.source const index = action.source
? state.routes.findIndex(r => r.key === action.source) ? state.routes.findIndex((r) => r.key === action.source)
: state.index; : state.index;
if (index === -1) { if (index === -1) {
@@ -283,7 +283,7 @@ export default function StackRouter(options: StackRouterOptions) {
case 'POP': { case 'POP': {
const index = const index =
action.target === state.key && action.source action.target === state.key && action.source
? state.routes.findIndex(r => r.key === action.source) ? state.routes.findIndex((r) => r.key === action.source)
: state.index; : state.index;
if (index > 0) { if (index > 0) {

View File

@@ -93,7 +93,7 @@ const changeIndex = (
const currentKey = state.routes[index].key; const currentKey = state.routes[index].key;
history = state.history history = state.history
.filter(it => (it.type === 'route' ? it.key !== currentKey : false)) .filter((it) => (it.type === 'route' ? it.key !== currentKey : false))
.concat({ type: TYPE_ROUTE, key: currentKey }); .concat({ type: TYPE_ROUTE, key: currentKey });
} else { } else {
history = getRouteHistory(state.routes, index, backBehavior); history = getRouteHistory(state.routes, index, backBehavior);
@@ -124,7 +124,7 @@ export default function TabRouter({
? routeNames.indexOf(initialRouteName) ? routeNames.indexOf(initialRouteName)
: 0; : 0;
const routes = routeNames.map(name => ({ const routes = routeNames.map((name) => ({
name, name,
key: `${name}-${shortid()}`, key: `${name}-${shortid()}`,
params: routeParamList[name], params: routeParamList[name],
@@ -150,9 +150,9 @@ export default function TabRouter({
return state; return state;
} }
const routes = routeNames.map(name => { const routes = routeNames.map((name) => {
const route = (state as PartialState<TabNavigationState>).routes.find( const route = (state as PartialState<TabNavigationState>).routes.find(
r => r.name === name (r) => r.name === name
); );
return { return {
@@ -184,8 +184,8 @@ export default function TabRouter({
routes.length - 1 routes.length - 1
); );
let history = state.history?.filter(it => let history = state.history?.filter((it) =>
routes.find(r => r.key === it.key) routes.find((r) => r.key === it.key)
); );
if (!history?.length) { if (!history?.length) {
@@ -205,8 +205,8 @@ export default function TabRouter({
getStateForRouteNamesChange(state, { routeNames, routeParamList }) { getStateForRouteNamesChange(state, { routeNames, routeParamList }) {
const routes = routeNames.map( const routes = routeNames.map(
name => (name) =>
state.routes.find(r => r.name === name) || { state.routes.find((r) => r.name === name) || {
name, name,
key: `${name}-${shortid()}`, key: `${name}-${shortid()}`,
params: routeParamList[name], params: routeParamList[name],
@@ -218,8 +218,8 @@ export default function TabRouter({
routeNames.indexOf(state.routes[state.index].name) routeNames.indexOf(state.routes[state.index].name)
); );
let history = state.history.filter(it => let history = state.history.filter((it) =>
routes.find(r => r.key === it.key) routes.find((r) => r.key === it.key)
); );
if (!history.length) { if (!history.length) {
@@ -236,7 +236,7 @@ export default function TabRouter({
}, },
getStateForRouteFocus(state, key) { getStateForRouteFocus(state, key) {
const index = state.routes.findIndex(r => r.key === key); const index = state.routes.findIndex((r) => r.key === key);
if (index === -1 || index === state.index) { if (index === -1 || index === state.index) {
return state; return state;
@@ -253,11 +253,11 @@ export default function TabRouter({
if (action.type === 'NAVIGATE' && action.payload.key) { if (action.type === 'NAVIGATE' && action.payload.key) {
index = state.routes.findIndex( index = state.routes.findIndex(
route => route.key === action.payload.key (route) => route.key === action.payload.key
); );
} else { } else {
index = state.routes.findIndex( index = state.routes.findIndex(
route => route.name === action.payload.name (route) => route.name === action.payload.name
); );
} }
@@ -295,7 +295,7 @@ export default function TabRouter({
const previousKey = state.history[state.history.length - 2].key; const previousKey = state.history[state.history.length - 2].key;
const index = state.routes.findIndex( const index = state.routes.findIndex(
route => route.key === previousKey (route) => route.key === previousKey
); );
if (index === -1) { if (index === -1) {

View File

@@ -84,6 +84,22 @@ it('resets state to new state with RESET', () => {
expect(result).toEqual({ index: 0, routes }); expect(result).toEqual({ index: 0, routes });
}); });
it('adds keys to routes missing keys during RESET', () => {
const result = BaseRouter.getStateForAction(
STATE,
// @ts-ignore
CommonActions.reset({
...STATE,
routes: [...STATE.routes, { name: 'qux' }],
})
);
expect(result).toEqual({
...STATE,
routes: [...STATE.routes, { key: 'qux-test', name: 'qux' }],
});
});
it("doesn't handle RESET if routes don't match routeNames", () => { it("doesn't handle RESET if routes don't match routeNames", () => {
const routes = [ const routes = [
{ key: 'bar', name: 'bar', params: { fruit: 'orange' } }, { key: 'bar', name: 'bar', params: { fruit: 'orange' } },

View File

@@ -3,6 +3,43 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.2.4](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.3...@react-navigation/stack@5.2.4) (2020-03-22)
### Bug Fixes
* fix swipe not dismissing card in RTL ([043924c](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/043924ca4843b6f02626532cbf4aafc7cad9fab1)), closes [#7841](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7841)
## [5.2.3](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.2...@react-navigation/stack@5.2.3) (2020-03-19)
### Bug Fixes
* use the correct velocity value in closing animation ([#7836](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7836)) ([adbfedc](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/adbfedcd58d4e3d358c6c9691710bb8e4e0d8afb))
## [5.2.2](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.1...@react-navigation/stack@5.2.2) (2020-03-19)
### Bug Fixes
* don't use react-native-screens on web ([b1a65fc](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/b1a65fc73e8603ae2c06ef101a74df31e80bb9b2)), closes [#7485](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7485)
* fix blank page if stack was inside display: none before ([49f6fed](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/49f6fed6d3da878e02a9fe9115c05bcf84e332bf))
* fix closing stack using inverted gesture. ([#7824](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/7824)) ([f24d3a3](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/f24d3a3461ee8a566c25ce7d13f31035b4be2691))
* initialize height and width to zero if undefined ([3df65e2](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/3df65e28197db3bb8371059146546d57661c5ba3)), closes [#6789](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/6789)
* only dismiss previously focused input on page change. closes [#6918](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/issues/6918) ([b1fe730](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/commit/b1fe73097f3ad58d3ba4a8a9b875276d1d8d220c))
## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.0...@react-navigation/stack@5.2.1) (2020-03-17) ## [5.2.1](https://github.com/react-navigation/react-navigation/tree/master/packages/stack/compare/@react-navigation/stack@5.2.0...@react-navigation/stack@5.2.1) (2020-03-17)
**Note:** Version bump only for package @react-navigation/stack **Note:** Version bump only for package @react-navigation/stack

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/stack", "name": "@react-navigation/stack",
"description": "Stack navigator component for iOS and Android with animated transitions and gestures", "description": "Stack navigator component for iOS and Android with animated transitions and gestures",
"version": "5.2.1", "version": "5.2.4",
"keywords": [ "keywords": [
"react-native-component", "react-native-component",
"react-component", "react-component",
@@ -40,7 +40,7 @@
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.10.0", "@react-native-community/bob": "^0.10.0",
"@react-native-community/masked-view": "^0.1.7", "@react-native-community/masked-view": "^0.1.7",
"@react-navigation/native": "^5.1.0", "@react-navigation/native": "^5.1.2",
"@types/color": "^3.0.1", "@types/color": "^3.0.1",
"@types/react": "^16.9.23", "@types/react": "^16.9.23",
"@types/react-native": "^0.61.22", "@types/react-native": "^0.61.22",

View File

@@ -40,7 +40,7 @@ function StackNavigator({
React.useEffect( React.useEffect(
() => () =>
navigation.addListener && navigation.addListener &&
navigation.addListener('tabPress', e => { navigation.addListener('tabPress', (e) => {
const isFocused = navigation.isFocused(); const isFocused = navigation.isFocused();
// Run the operation in the next frame so we're sure all listeners have been run // Run the operation in the next frame so we're sure all listeners have been run

View File

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

View File

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

View File

@@ -38,7 +38,7 @@ type State = {
}; };
const warnIfHeaderStylesDefined = (styles: Record<string, any>) => { const warnIfHeaderStylesDefined = (styles: Record<string, any>) => {
Object.keys(styles).forEach(styleProp => { Object.keys(styles).forEach((styleProp) => {
const value = styles[styleProp]; const value = styles[styleProp];
if (styleProp === 'position' && value === 'absolute') { if (styleProp === 'position' && value === 'absolute') {

View File

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

View File

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

View File

@@ -272,7 +272,10 @@ export default class Card extends React.Component<Props> {
} }
const closing = const closing =
translation + velocity * gestureVelocityImpact > distance / 2 (translation + velocity) *
gestureVelocityImpact *
getInvertedMultiplier(gestureDirection) >
distance / 2
? velocity !== 0 || translation !== 0 ? velocity !== 0 || translation !== 0
: false; : false;

View File

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

View File

@@ -115,7 +115,7 @@ export default class StackView extends React.Component<Props, State> {
// We only need to animate routes if the focused route changed // We only need to animate routes if the focused route changed
// Animating previous routes won't be visible coz the focused route is on top of everything // Animating previous routes won't be visible coz the focused route is on top of everything
if (!previousRoutes.find(r => r.key === nextFocusedRoute.key)) { if (!previousRoutes.find((r) => r.key === nextFocusedRoute.key)) {
// A new route has come to the focus, we treat this as a push // A new route has come to the focus, we treat this as a push
// A replace can also trigger this, the animation should look like push // A replace can also trigger this, the animation should look like push
@@ -128,17 +128,17 @@ export default class StackView extends React.Component<Props, State> {
openingRouteKeys = [...openingRouteKeys, nextFocusedRoute.key]; openingRouteKeys = [...openingRouteKeys, nextFocusedRoute.key];
closingRouteKeys = closingRouteKeys.filter( closingRouteKeys = closingRouteKeys.filter(
key => key !== nextFocusedRoute.key (key) => key !== nextFocusedRoute.key
); );
replacingRouteKeys = replacingRouteKeys.filter( replacingRouteKeys = replacingRouteKeys.filter(
key => key !== nextFocusedRoute.key (key) => key !== nextFocusedRoute.key
); );
if (!routes.find(r => r.key === previousFocusedRoute.key)) { if (!routes.find((r) => r.key === previousFocusedRoute.key)) {
// The previous focused route isn't present in state, we treat this as a replace // The previous focused route isn't present in state, we treat this as a replace
openingRouteKeys = openingRouteKeys.filter( openingRouteKeys = openingRouteKeys.filter(
key => key !== previousFocusedRoute.key (key) => key !== previousFocusedRoute.key
); );
if (getAnimationTypeForReplace(nextFocusedRoute.key) === 'pop') { if (getAnimationTypeForReplace(nextFocusedRoute.key) === 'pop') {
@@ -151,7 +151,7 @@ export default class StackView extends React.Component<Props, State> {
// But since user configured it to animate the old screen like a pop, we need to add this without animation // But since user configured it to animate the old screen like a pop, we need to add this without animation
// So remove it from `openingRouteKeys` which will remove the animation // So remove it from `openingRouteKeys` which will remove the animation
openingRouteKeys = openingRouteKeys.filter( openingRouteKeys = openingRouteKeys.filter(
key => key !== nextFocusedRoute.key (key) => key !== nextFocusedRoute.key
); );
// Keep the route being removed at the end to animate it out // Keep the route being removed at the end to animate it out
@@ -163,7 +163,7 @@ export default class StackView extends React.Component<Props, State> {
]; ];
closingRouteKeys = closingRouteKeys.filter( closingRouteKeys = closingRouteKeys.filter(
key => key !== previousFocusedRoute.key (key) => key !== previousFocusedRoute.key
); );
// Keep the old route in the state because it's visible under the new route, and removing it will feel abrupt // Keep the old route in the state because it's visible under the new route, and removing it will feel abrupt
@@ -174,7 +174,7 @@ export default class StackView extends React.Component<Props, State> {
} }
} }
} }
} else if (!routes.find(r => r.key === previousFocusedRoute.key)) { } else if (!routes.find((r) => r.key === previousFocusedRoute.key)) {
// The previously focused route was removed, we treat this as a pop // The previously focused route was removed, we treat this as a pop
if ( if (
@@ -186,10 +186,10 @@ export default class StackView extends React.Component<Props, State> {
// Sometimes a route can be closed before the opening animation finishes // Sometimes a route can be closed before the opening animation finishes
// So we also need to remove it from the opening list // So we also need to remove it from the opening list
openingRouteKeys = openingRouteKeys.filter( openingRouteKeys = openingRouteKeys.filter(
key => key !== previousFocusedRoute.key (key) => key !== previousFocusedRoute.key
); );
replacingRouteKeys = replacingRouteKeys.filter( replacingRouteKeys = replacingRouteKeys.filter(
key => key !== previousFocusedRoute.key (key) => key !== previousFocusedRoute.key
); );
// Keep a copy of route being removed in the state to be able to animate it // Keep a copy of route being removed in the state to be able to animate it
@@ -271,13 +271,13 @@ export default class StackView extends React.Component<Props, State> {
private getPreviousRoute = ({ route }: { route: Route<string> }) => { private getPreviousRoute = ({ route }: { route: Route<string> }) => {
const { closingRouteKeys, replacingRouteKeys } = this.state; const { closingRouteKeys, replacingRouteKeys } = this.state;
const routes = this.state.routes.filter( const routes = this.state.routes.filter(
r => (r) =>
r.key === route.key || r.key === route.key ||
(!closingRouteKeys.includes(r.key) && (!closingRouteKeys.includes(r.key) &&
!replacingRouteKeys.includes(r.key)) !replacingRouteKeys.includes(r.key))
); );
const index = routes.findIndex(r => r.key === route.key); const index = routes.findIndex((r) => r.key === route.key);
return routes[index - 1]; return routes[index - 1];
}; };
@@ -298,12 +298,16 @@ export default class StackView extends React.Component<Props, State> {
}; };
private handleOpenRoute = ({ route }: { route: Route<string> }) => { private handleOpenRoute = ({ route }: { route: Route<string> }) => {
this.setState(state => ({ this.setState((state) => ({
routes: state.replacingRouteKeys.length routes: state.replacingRouteKeys.length
? state.routes.filter(r => !state.replacingRouteKeys.includes(r.key)) ? state.routes.filter((r) => !state.replacingRouteKeys.includes(r.key))
: state.routes, : state.routes,
openingRouteKeys: state.openingRouteKeys.filter(key => key !== route.key), openingRouteKeys: state.openingRouteKeys.filter(
closingRouteKeys: state.closingRouteKeys.filter(key => key !== route.key), (key) => key !== route.key
),
closingRouteKeys: state.closingRouteKeys.filter(
(key) => key !== route.key
),
replacingRouteKeys: [], replacingRouteKeys: [],
})); }));
}; };
@@ -311,7 +315,7 @@ export default class StackView extends React.Component<Props, State> {
private handleCloseRoute = ({ route }: { route: Route<string> }) => { private handleCloseRoute = ({ route }: { route: Route<string> }) => {
const { state, navigation } = this.props; const { state, navigation } = this.props;
if (state.routes.find(r => r.key === route.key)) { if (state.routes.find((r) => r.key === route.key)) {
// If a route exists in state, trigger a pop // If a route exists in state, trigger a pop
// This will happen in when the route was closed from the card component // This will happen in when the route was closed from the card component
// e.g. When the close animation triggered from a gesture ends // e.g. When the close animation triggered from a gesture ends
@@ -322,13 +326,13 @@ export default class StackView extends React.Component<Props, State> {
}); });
} else { } else {
// We need to clean up any state tracking the route and pop it immediately // We need to clean up any state tracking the route and pop it immediately
this.setState(state => ({ this.setState((state) => ({
routes: state.routes.filter(r => r.key !== route.key), routes: state.routes.filter((r) => r.key !== route.key),
openingRouteKeys: state.openingRouteKeys.filter( openingRouteKeys: state.openingRouteKeys.filter(
key => key !== route.key (key) => key !== route.key
), ),
closingRouteKeys: state.closingRouteKeys.filter( closingRouteKeys: state.closingRouteKeys.filter(
key => key !== route.key (key) => key !== route.key
), ),
})); }));
} }
@@ -378,9 +382,9 @@ export default class StackView extends React.Component<Props, State> {
<GestureHandlerWrapper style={styles.container}> <GestureHandlerWrapper style={styles.container}>
<SafeAreaProviderCompat> <SafeAreaProviderCompat>
<SafeAreaConsumer> <SafeAreaConsumer>
{insets => ( {(insets) => (
<KeyboardManager enabled={keyboardHandlingEnabled !== false}> <KeyboardManager enabled={keyboardHandlingEnabled !== false}>
{props => ( {(props) => (
<CardStack <CardStack
mode={mode} mode={mode}
insets={insets as EdgeInsets} insets={insets as EdgeInsets}

View File

@@ -9,7 +9,7 @@ const packages = path.join(__dirname, '..', 'packages');
const invalid = []; const invalid = [];
fs.readdirSync(packages).forEach(name => { fs.readdirSync(packages).forEach((name) => {
const dir = path.join(packages, name); const dir = path.join(packages, name);
if (fs.statSync(path.join(packages, name)).isDirectory()) { if (fs.statSync(path.join(packages, name)).isDirectory()) {
@@ -26,6 +26,6 @@ fs.readdirSync(packages).forEach(name => {
if (invalid.length) { if (invalid.length) {
console.log( console.log(
'Found invalid path to type definitions in the following packages:\n', 'Found invalid path to type definitions in the following packages:\n',
invalid.map(p => `- ${p.name} (${p.types})`).join('\n') invalid.map((p) => `- ${p.name} (${p.types})`).join('\n')
); );
} }

View File

@@ -14637,10 +14637,10 @@ prettier-linter-helpers@^1.0.0:
dependencies: dependencies:
fast-diff "^1.1.2" fast-diff "^1.1.2"
prettier@^1.19.1: prettier@^2.0.1:
version "1.19.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.1.tgz#3f00ac71263be34684b2b2c8d7e7f63737592dac"
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== integrity sha512-piXGBcY1zoFOG0MvHpNE5reAGseLmaCRifQ/fmfF49BcYkInEs/naD/unxGNAeOKFA5+JxVrPyMvMlpzcd20UA==
pretty-bytes@^4.0.2: pretty-bytes@^4.0.2:
version "4.0.2" version "4.0.2"