mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-13 09:30:30 +08:00
Compare commits
40 Commits
react-navi
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6515fbe2dc | ||
|
|
454e05b02e | ||
|
|
442ca3e700 | ||
|
|
e00c63e32b | ||
|
|
ba6d7dcbed | ||
|
|
2ca8c34b2d | ||
|
|
03f9f8cd8e | ||
|
|
1ee8c76df3 | ||
|
|
bc1313b61d | ||
|
|
28982ffca0 | ||
|
|
bb82d5000f | ||
|
|
1f30227049 | ||
|
|
f34ec46111 | ||
|
|
8b79e9bee6 | ||
|
|
e09906a423 | ||
|
|
ed7ffb98f4 | ||
|
|
90eff6a5e2 | ||
|
|
b4691d7de2 | ||
|
|
f99f523a37 | ||
|
|
05b47dbb09 | ||
|
|
4c4e66f05c | ||
|
|
720d1f970d | ||
|
|
66a4dbccd8 | ||
|
|
7925f0c8a3 | ||
|
|
b3d77b79b7 | ||
|
|
1d527ce16a | ||
|
|
3bec1c964a | ||
|
|
a7e0c193cd | ||
|
|
ac98c0a668 | ||
|
|
14a6538cc8 | ||
|
|
c9313a1419 | ||
|
|
ec39721a74 | ||
|
|
a54d48b650 | ||
|
|
8a9a9cd7d1 | ||
|
|
cd08338186 | ||
|
|
d2433f0ab8 | ||
|
|
66374db859 | ||
|
|
8dbe0299a8 | ||
|
|
e4bc6dd506 | ||
|
|
4bac3bfc1a |
@@ -41,7 +41,7 @@ import StackWithTranslucentHeader from './src/StackWithTranslucentHeader';
|
||||
import SwitchWithStacks from './src/SwitchWithStacks';
|
||||
import TabsInDrawer from './src/TabsInDrawer';
|
||||
import DragLimitedToModal from './src/DragLimitedToModal';
|
||||
import EventsStack from './src/EventsStack';
|
||||
import FocusEvents from './src/FocusEvents';
|
||||
import FullScreen from './src/FullScreen';
|
||||
import GestureInteraction from './src/GestureInteraction';
|
||||
import {
|
||||
@@ -144,7 +144,7 @@ const ExampleInfo = {
|
||||
name: 'Drag limited to modal',
|
||||
screen: DragLimitedToModal,
|
||||
},
|
||||
EventsStack: { name: 'Events Stack', screen: EventsStack },
|
||||
EventsStack: { name: 'Focus Events', screen: FocusEvents },
|
||||
FullScreen: { name: 'Fullscreen Stack', screen: FullScreen },
|
||||
GestureInteraction: {
|
||||
name: 'Gesture interaction',
|
||||
@@ -346,6 +346,7 @@ class MainScreen extends React.Component<any, State> {
|
||||
if (route.screen || route.path || route.params) {
|
||||
// @ts-ignore
|
||||
const { path, params, screen } = route;
|
||||
// @ts-ignore
|
||||
const { router } = screen;
|
||||
const action =
|
||||
path &&
|
||||
@@ -386,6 +387,7 @@ class MainScreen extends React.Component<any, State> {
|
||||
|
||||
const Navigation = createAppContainer(
|
||||
createStackNavigator(
|
||||
// @ts-ignore
|
||||
{
|
||||
...ExampleInfo,
|
||||
Index: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Button, ScrollView, View, Text } from 'react-native';
|
||||
import { createBottomTabNavigator } from 'react-navigation-tabs';
|
||||
import {
|
||||
createStackNavigator,
|
||||
NavigationStackProp,
|
||||
@@ -22,24 +23,36 @@ class FocusTag extends React.Component<NavigationStackScreenProps> {
|
||||
state = { mode: 'didBlur' };
|
||||
|
||||
componentDidMount() {
|
||||
this.props.navigation.addListener('willFocus', () => {
|
||||
this.setMode('willFocus');
|
||||
this._willFocusSub = this.props.navigation.addListener('willFocus', () => {
|
||||
this._handleEvent('willFocus');
|
||||
});
|
||||
|
||||
this.props.navigation.addListener('willBlur', () => {
|
||||
this.setMode('willBlur');
|
||||
this._willBlurSub = this.props.navigation.addListener('willBlur', () => {
|
||||
this._handleEvent('willBlur');
|
||||
});
|
||||
|
||||
this.props.navigation.addListener('didFocus', () => {
|
||||
this.setMode('didFocus');
|
||||
this._didFocusSub = this.props.navigation.addListener('didFocus', () => {
|
||||
this._handleEvent('didFocus');
|
||||
});
|
||||
|
||||
this.props.navigation.addListener('didBlur', () => {
|
||||
this.setMode('didBlur');
|
||||
this._didBlurSub = this.props.navigation.addListener('didBlur', () => {
|
||||
this._handleEvent('didBlur');
|
||||
});
|
||||
}
|
||||
|
||||
setMode = (mode: string) => {
|
||||
componentWillUnmount() {
|
||||
this._willFocusSub?.remove();
|
||||
this._willBlurSub?.remove();
|
||||
this._didFocusSub?.remove();
|
||||
this._didBlurSub?.remove();
|
||||
}
|
||||
|
||||
_willFocusSub: { remove: () => void } | undefined;
|
||||
_willBlurSub: { remove: () => void } | undefined;
|
||||
_didFocusSub: { remove: () => void } | undefined;
|
||||
_didBlurSub: { remove: () => void } | undefined;
|
||||
|
||||
_handleEvent = (mode: string) => {
|
||||
this.setState({ mode });
|
||||
};
|
||||
|
||||
@@ -119,18 +132,19 @@ class SampleScreen extends React.Component<NavigationStackScreenProps> {
|
||||
}
|
||||
}
|
||||
|
||||
const SimpleStack = createStackNavigator(
|
||||
const Stack = createStackNavigator(
|
||||
{
|
||||
PageOne: {
|
||||
screen: SampleScreen,
|
||||
},
|
||||
PageTwo: {
|
||||
screen: SampleScreen,
|
||||
},
|
||||
PageOne: SampleScreen,
|
||||
PageTwo: SampleScreen,
|
||||
},
|
||||
{
|
||||
initialRouteName: 'PageOne',
|
||||
}
|
||||
);
|
||||
|
||||
export default SimpleStack;
|
||||
const Tabs = createBottomTabNavigator({
|
||||
A: Stack,
|
||||
B: Stack,
|
||||
});
|
||||
|
||||
export default Tabs;
|
||||
@@ -114,7 +114,7 @@ const MyNavScreen = ({
|
||||
title="Set unlocked"
|
||||
/>
|
||||
),
|
||||
unlocked: (
|
||||
'unlocked': (
|
||||
<Button
|
||||
onPress={() =>
|
||||
navigation.setParams({ drawerLockMode: 'locked-open' })
|
||||
|
||||
@@ -106,7 +106,7 @@ const MyNavScreen = ({
|
||||
title="Set unlocked"
|
||||
/>
|
||||
),
|
||||
unlocked: (
|
||||
'unlocked': (
|
||||
<Button
|
||||
onPress={() =>
|
||||
navigation.setParams({ drawerLockMode: 'locked-open' })
|
||||
|
||||
@@ -64,7 +64,7 @@ class ListScreen extends React.Component {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#fff',
|
||||
// backgroundColor: '#fff',
|
||||
}}
|
||||
>
|
||||
<Text>List Screen</Text>
|
||||
@@ -96,7 +96,7 @@ class DetailsScreen extends React.Component<NavigationStackScreenProps> {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#fff',
|
||||
// backgroundColor: '#fff',
|
||||
}}
|
||||
>
|
||||
<Text>Details Screen</Text>
|
||||
@@ -119,7 +119,7 @@ class HeaderlessScreen extends React.Component {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#fff',
|
||||
// backgroundColor: '#fff',
|
||||
}}
|
||||
>
|
||||
<Text>Headerless Screen</Text>
|
||||
|
||||
@@ -91,8 +91,4 @@ const DrawerExample = createDrawerNavigator(
|
||||
}
|
||||
);
|
||||
|
||||
DrawerExample.navigationOptions = {
|
||||
headerShown: false,
|
||||
};
|
||||
|
||||
export default DrawerExample;
|
||||
|
||||
11
package.json
11
package.json
@@ -18,7 +18,7 @@
|
||||
"author": "Adam Miskiewicz <adam@sk3vy.com>, Eric Vicenti <ericvicenti@gmail.com>, Brent Vatne <brent@expo.io>, Satyajit Sahoo <satyajit.happy@gmail.com>",
|
||||
"scripts": {
|
||||
"lint": "eslint --ext '.js,.ts,.tsx' .",
|
||||
"typescript": "tsc --noEmit",
|
||||
"typescript": "tsc --noEmit --composite false",
|
||||
"test": "jest",
|
||||
"prerelease": "yarn lerna run clean",
|
||||
"release": "yarn lerna publish",
|
||||
@@ -33,15 +33,16 @@
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"@commitlint/config-conventional": "^8.3.4",
|
||||
"@types/jest": "^25.1.4",
|
||||
"babel-jest": "^25.2.3",
|
||||
"commitlint": "^8.3.5",
|
||||
"core-js": "^3.6.4",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-satya164": "^3.1.5",
|
||||
"eslint": "^7.0.0",
|
||||
"eslint-config-satya164": "^3.1.7",
|
||||
"husky": "^4.2.1",
|
||||
"jest": "^25.1.0",
|
||||
"lerna": "^3.20.2",
|
||||
"prettier": "^2.0.1",
|
||||
"typescript": "~3.7.5"
|
||||
"prettier": "^2.0.5",
|
||||
"typescript": "^3.9.5"
|
||||
},
|
||||
"resolutions": {
|
||||
"react": "~16.9.0",
|
||||
|
||||
@@ -3,6 +3,65 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.6.0](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.12...react-navigation-animated-switch@0.6.0) (2020-06-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* sync latest stack and upgrade typescript ([454e05b](https://github.com/react-navigation/react-navigation/commit/454e05b02ec97f053b381fdc801df899d8c93cb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.5.12](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.11...react-navigation-animated-switch@0.5.12) (2020-04-30)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-animated-switch
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.5.11](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.10...react-navigation-animated-switch@0.5.11) (2020-04-22)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-animated-switch
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.5.10](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.9...react-navigation-animated-switch@0.5.10) (2020-04-02)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-animated-switch
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.5.9](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.8...react-navigation-animated-switch@0.5.9) (2020-03-31)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-animated-switch
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.5.8](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.6...react-navigation-animated-switch@0.5.8) (2020-03-28)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-animated-switch
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.5.7](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.6...react-navigation-animated-switch@0.5.7) (2020-03-27)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-animated-switch
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.5.6](https://github.com/react-navigation/react-navigation/compare/react-navigation-animated-switch@0.5.5...react-navigation-animated-switch@0.5.6) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-animated-switch
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-navigation-animated-switch",
|
||||
"version": "0.5.6",
|
||||
"version": "0.6.0",
|
||||
"description": "Animated switch for React Navigation",
|
||||
"main": "lib/commonjs/index.js",
|
||||
"react-native": "lib/module/index.js",
|
||||
@@ -28,8 +28,8 @@
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-native-reanimated": "~1.7.0",
|
||||
"react-navigation": "^4.3.3",
|
||||
"typescript": "~3.7.5"
|
||||
"react-navigation": "^4.4.0",
|
||||
"typescript": "^3.9.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
|
||||
@@ -3,6 +3,65 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.7.6](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.5...@react-navigation/core@3.7.6) (2020-04-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* change old docUrl to new docUrl due v5 ([e09906a](https://github.com/react-navigation/react-navigation-core/commit/e09906a4235a0fca09140923ebe7af34b50b491a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.7.5](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.4...@react-navigation/core@3.7.5) (2020-04-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* emit refocus for child navigators ([66a4dbc](https://github.com/react-navigation/react-navigation-core/commit/66a4dbccd8e7b3cda51a1d9c7e9397dfc58d6b9e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.7.4](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.3...@react-navigation/core@3.7.4) (2020-03-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove isTransitioning from SwitchRouter state ([3bec1c9](https://github.com/react-navigation/react-navigation-core/commit/3bec1c964a49136c0ead8e8ba8a8c66c556bbcba))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.7.3](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.1...@react-navigation/core@3.7.3) (2020-03-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* always emit didFocus/didBlur for root navigator ([ac98c0a](https://github.com/react-navigation/react-navigation-core/commit/ac98c0a668fe21200d0c6f62ae3043f92cc5aa7e))
|
||||
* don't handle prune if there's only one route ([d2433f0](https://github.com/react-navigation/react-navigation-core/commit/d2433f0ab8f9791df8169de4ddfdeed9bc699e3e))
|
||||
* emit didFocus and didBlur events based on parent's transition ([14a6538](https://github.com/react-navigation/react-navigation-core/commit/14a6538cc8e12c50d5d10722d75c9395a0a281ec))
|
||||
* rework focus and blur events to make them more reliable ([cd08338](https://github.com/react-navigation/react-navigation-core/commit/cd083381866506a192f1ec842ac169f2b4277ca5)), closes [#4867](https://github.com/react-navigation/react-navigation-core/issues/4867) [#6187](https://github.com/react-navigation/react-navigation-core/issues/6187) [#6451](https://github.com/react-navigation/react-navigation-core/issues/6451) [#7628](https://github.com/react-navigation/react-navigation-core/issues/7628) [#7749](https://github.com/react-navigation/react-navigation-core/issues/7749)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.7.2](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.1...@react-navigation/core@3.7.2) (2020-03-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don't handle prune if there's only one route ([d2433f0](https://github.com/react-navigation/react-navigation-core/commit/d2433f0ab8f9791df8169de4ddfdeed9bc699e3e))
|
||||
* rework focus and blur events to make them more reliable ([cd08338](https://github.com/react-navigation/react-navigation-core/commit/cd083381866506a192f1ec842ac169f2b4277ca5)), closes [#4867](https://github.com/react-navigation/react-navigation-core/issues/4867) [#6187](https://github.com/react-navigation/react-navigation-core/issues/6187) [#6451](https://github.com/react-navigation/react-navigation-core/issues/6451) [#7628](https://github.com/react-navigation/react-navigation-core/issues/7628) [#7749](https://github.com/react-navigation/react-navigation-core/issues/7749)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.7.1](https://github.com/react-navigation/react-navigation-core/compare/@react-navigation/core@3.7.0...@react-navigation/core@3.7.1) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/core
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-navigation/core",
|
||||
"version": "3.7.1",
|
||||
"version": "3.7.6",
|
||||
"description": "Core utilities for the react-navigation framework",
|
||||
"main": "lib/commonjs/index.js",
|
||||
"react-native": "lib/module/index.js",
|
||||
|
||||
148
packages/core/src/__fixtures__/createNavigationContainer.js
Normal file
148
packages/core/src/__fixtures__/createNavigationContainer.js
Normal file
@@ -0,0 +1,148 @@
|
||||
/* eslint-disable react/sort-comp */
|
||||
|
||||
import React from 'react';
|
||||
import { NavigationActions, getNavigation, NavigationProvider } from '../index';
|
||||
|
||||
export default function createNavigationContainer(Component) {
|
||||
class NavigationContainer extends React.Component {
|
||||
static router = Component.router;
|
||||
static navigationOptions = null;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._initialAction = NavigationActions.init();
|
||||
|
||||
this.state = {
|
||||
nav: !props.loadNavigationState
|
||||
? Component.router.getStateForAction(this._initialAction)
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
_actionEventSubscribers = new Set();
|
||||
|
||||
_onNavigationStateChange(prevNav, nav, action) {
|
||||
if (typeof this.props.onNavigationStateChange === 'function') {
|
||||
this.props.onNavigationStateChange(prevNav, nav, action);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
// Clear cached _navState every tick
|
||||
if (this._navState === this.state.nav) {
|
||||
this._navState = null;
|
||||
}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
// Initialize state. This must be done *after* any async code
|
||||
// so we don't end up with a different value for this.state.nav
|
||||
// due to changes while async function was resolving
|
||||
let action = this._initialAction;
|
||||
// eslint-disable-next-line react/no-access-state-in-setstate
|
||||
let startupState = this.state.nav;
|
||||
if (!startupState) {
|
||||
startupState = Component.router.getStateForAction(action);
|
||||
}
|
||||
|
||||
const dispatchActions = () =>
|
||||
this._actionEventSubscribers.forEach((subscriber) =>
|
||||
subscriber({
|
||||
type: 'action',
|
||||
action,
|
||||
state: this.state.nav,
|
||||
lastState: null,
|
||||
})
|
||||
);
|
||||
|
||||
if (startupState === this.state.nav) {
|
||||
dispatchActions();
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/no-did-mount-set-state
|
||||
this.setState({ nav: startupState }, () => {
|
||||
dispatchActions();
|
||||
});
|
||||
}
|
||||
|
||||
dispatch = (action) => {
|
||||
if (this.props.navigation) {
|
||||
return this.props.navigation.dispatch(action);
|
||||
}
|
||||
|
||||
// navState will have the most up-to-date value, because setState sometimes behaves asyncronously
|
||||
this._navState = this._navState || this.state.nav;
|
||||
const lastNavState = this._navState;
|
||||
|
||||
const reducedState = Component.router.getStateForAction(
|
||||
action,
|
||||
lastNavState
|
||||
);
|
||||
const navState = reducedState === null ? lastNavState : reducedState;
|
||||
|
||||
const dispatchActionEvents = () => {
|
||||
this._actionEventSubscribers.forEach((subscriber) =>
|
||||
subscriber({
|
||||
type: 'action',
|
||||
action,
|
||||
state: navState,
|
||||
lastState: lastNavState,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
if (reducedState === null) {
|
||||
// The router will return null when action has been handled and the state hasn't changed.
|
||||
// dispatch returns true when something has been handled.
|
||||
dispatchActionEvents();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (navState !== lastNavState) {
|
||||
// Cache updates to state.nav during the tick to ensure that subsequent calls will not discard this change
|
||||
this._navState = navState;
|
||||
this.setState({ nav: navState }, () => {
|
||||
this._onNavigationStateChange(lastNavState, navState, action);
|
||||
dispatchActionEvents();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
dispatchActionEvents();
|
||||
return false;
|
||||
};
|
||||
|
||||
render() {
|
||||
let navigation = this.props.navigation;
|
||||
|
||||
const navState = this.state.nav;
|
||||
|
||||
if (!navState) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this._navigation || this._navigation.state !== navState) {
|
||||
this._navigation = getNavigation(
|
||||
Component.router,
|
||||
navState,
|
||||
this.dispatch,
|
||||
this._actionEventSubscribers,
|
||||
this._getScreenProps,
|
||||
() => this._navigation
|
||||
);
|
||||
}
|
||||
|
||||
navigation = this._navigation;
|
||||
|
||||
return (
|
||||
<NavigationProvider value={navigation}>
|
||||
<Component {...this.props} navigation={navigation} />
|
||||
</NavigationProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return NavigationContainer;
|
||||
}
|
||||
241
packages/core/src/__tests__/NavigationFocusEvents.test.js
Normal file
241
packages/core/src/__tests__/NavigationFocusEvents.test.js
Normal file
@@ -0,0 +1,241 @@
|
||||
import * as React from 'react';
|
||||
import { render, act } from 'react-native-testing-library';
|
||||
import { navigate } from '../NavigationActions';
|
||||
import TabRouter from '../routers/TabRouter';
|
||||
import createNavigator from '../navigators/createNavigator';
|
||||
import createNavigationContainer from '../__fixtures__/createNavigationContainer';
|
||||
|
||||
it('fires focus and blur events in root navigator', async () => {
|
||||
function createTestNavigator(routeConfigMap, config = {}) {
|
||||
const router = TabRouter(routeConfigMap, config);
|
||||
|
||||
return createNavigator(
|
||||
({ descriptors, navigation }) =>
|
||||
navigation.state.routes.map((route) => {
|
||||
const Comp = descriptors[route.key].getComponent();
|
||||
return (
|
||||
<Comp
|
||||
key={route.key}
|
||||
navigation={descriptors[route.key].navigation}
|
||||
/>
|
||||
);
|
||||
}),
|
||||
router,
|
||||
config
|
||||
);
|
||||
}
|
||||
|
||||
const firstFocusCallback = jest.fn();
|
||||
const firstBlurCallback = jest.fn();
|
||||
|
||||
const secondFocusCallback = jest.fn();
|
||||
const secondBlurCallback = jest.fn();
|
||||
|
||||
const thirdFocusCallback = jest.fn();
|
||||
const thirdBlurCallback = jest.fn();
|
||||
|
||||
const fourthFocusCallback = jest.fn();
|
||||
const fourthBlurCallback = jest.fn();
|
||||
|
||||
const createComponent = (focusCallback, blurCallback) =>
|
||||
class TestComponent extends React.Component {
|
||||
componentDidMount() {
|
||||
const { navigation } = this.props;
|
||||
|
||||
this.focusSub = navigation.addListener('willFocus', focusCallback);
|
||||
this.blurSub = navigation.addListener('willBlur', blurCallback);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.focusSub?.remove();
|
||||
this.blurSub?.remove();
|
||||
}
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const navigation = React.createRef();
|
||||
|
||||
const Navigator = createNavigationContainer(
|
||||
createTestNavigator({
|
||||
first: createComponent(firstFocusCallback, firstBlurCallback),
|
||||
second: createComponent(secondFocusCallback, secondBlurCallback),
|
||||
third: createComponent(thirdFocusCallback, thirdBlurCallback),
|
||||
fourth: createComponent(fourthFocusCallback, fourthBlurCallback),
|
||||
})
|
||||
);
|
||||
|
||||
const element = <Navigator ref={navigation} />;
|
||||
|
||||
render(element);
|
||||
|
||||
expect(firstFocusCallback).toBeCalledTimes(1);
|
||||
expect(firstBlurCallback).toBeCalledTimes(0);
|
||||
expect(secondFocusCallback).toBeCalledTimes(0);
|
||||
expect(secondBlurCallback).toBeCalledTimes(0);
|
||||
expect(thirdFocusCallback).toBeCalledTimes(0);
|
||||
expect(thirdBlurCallback).toBeCalledTimes(0);
|
||||
expect(fourthFocusCallback).toBeCalledTimes(0);
|
||||
expect(fourthBlurCallback).toBeCalledTimes(0);
|
||||
|
||||
act(() => {
|
||||
navigation.current.dispatch(navigate({ routeName: 'second' }));
|
||||
});
|
||||
|
||||
expect(firstBlurCallback).toBeCalledTimes(1);
|
||||
expect(secondFocusCallback).toBeCalledTimes(1);
|
||||
|
||||
act(() => {
|
||||
navigation.current.dispatch(navigate({ routeName: 'fourth' }));
|
||||
});
|
||||
|
||||
expect(firstFocusCallback).toBeCalledTimes(1);
|
||||
expect(firstBlurCallback).toBeCalledTimes(1);
|
||||
expect(secondFocusCallback).toBeCalledTimes(1);
|
||||
expect(secondBlurCallback).toBeCalledTimes(1);
|
||||
expect(thirdFocusCallback).toBeCalledTimes(0);
|
||||
expect(thirdBlurCallback).toBeCalledTimes(0);
|
||||
expect(fourthFocusCallback).toBeCalledTimes(1);
|
||||
expect(fourthBlurCallback).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
it('fires focus and blur events in nested navigator', () => {
|
||||
function createTestNavigator(routeConfigMap, config = {}) {
|
||||
const router = TabRouter(routeConfigMap, config);
|
||||
|
||||
return createNavigator(
|
||||
({ descriptors, navigation }) =>
|
||||
navigation.state.routes.map((route) => {
|
||||
const Comp = descriptors[route.key].getComponent();
|
||||
return (
|
||||
<Comp
|
||||
key={route.key}
|
||||
navigation={descriptors[route.key].navigation}
|
||||
/>
|
||||
);
|
||||
}),
|
||||
router,
|
||||
config
|
||||
);
|
||||
}
|
||||
|
||||
const firstFocusCallback = jest.fn();
|
||||
const firstBlurCallback = jest.fn();
|
||||
|
||||
const secondFocusCallback = jest.fn();
|
||||
const secondBlurCallback = jest.fn();
|
||||
|
||||
const thirdFocusCallback = jest.fn();
|
||||
const thirdBlurCallback = jest.fn();
|
||||
|
||||
const fourthFocusCallback = jest.fn();
|
||||
const fourthBlurCallback = jest.fn();
|
||||
|
||||
const createComponent = (focusCallback, blurCallback) =>
|
||||
class TestComponent extends React.Component {
|
||||
componentDidMount() {
|
||||
const { navigation } = this.props;
|
||||
|
||||
this.focusSub = navigation.addListener('willFocus', focusCallback);
|
||||
this.blurSub = navigation.addListener('willBlur', blurCallback);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.focusSub?.remove();
|
||||
this.blurSub?.remove();
|
||||
}
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const Navigator = createNavigationContainer(
|
||||
createTestNavigator({
|
||||
first: createComponent(firstFocusCallback, firstBlurCallback),
|
||||
second: createComponent(secondFocusCallback, secondBlurCallback),
|
||||
nested: createTestNavigator({
|
||||
third: createComponent(thirdFocusCallback, thirdBlurCallback),
|
||||
fourth: createComponent(fourthFocusCallback, fourthBlurCallback),
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
const navigation = React.createRef();
|
||||
|
||||
const element = <Navigator ref={navigation} />;
|
||||
|
||||
render(element);
|
||||
|
||||
expect(thirdFocusCallback).toBeCalledTimes(0);
|
||||
expect(firstFocusCallback).toBeCalledTimes(1);
|
||||
|
||||
act(() => {
|
||||
navigation.current.dispatch(navigate({ routeName: 'nested' }));
|
||||
});
|
||||
|
||||
expect(firstFocusCallback).toBeCalledTimes(1);
|
||||
expect(fourthFocusCallback).toBeCalledTimes(0);
|
||||
expect(thirdFocusCallback).toBeCalledTimes(1);
|
||||
|
||||
act(() => {
|
||||
navigation.current.dispatch(navigate({ routeName: 'second' }));
|
||||
});
|
||||
|
||||
expect(thirdFocusCallback).toBeCalledTimes(1);
|
||||
expect(secondFocusCallback).toBeCalledTimes(1);
|
||||
expect(fourthBlurCallback).toBeCalledTimes(0);
|
||||
|
||||
act(() => {
|
||||
navigation.current.dispatch(navigate({ routeName: 'nested' }));
|
||||
});
|
||||
|
||||
expect(firstBlurCallback).toBeCalledTimes(1);
|
||||
expect(secondBlurCallback).toBeCalledTimes(1);
|
||||
expect(thirdFocusCallback).toBeCalledTimes(2);
|
||||
expect(fourthFocusCallback).toBeCalledTimes(0);
|
||||
|
||||
act(() => {
|
||||
navigation.current.dispatch(navigate({ routeName: 'third' }));
|
||||
});
|
||||
|
||||
expect(fourthBlurCallback).toBeCalledTimes(0);
|
||||
expect(thirdFocusCallback).toBeCalledTimes(2);
|
||||
|
||||
act(() => {
|
||||
navigation.current.dispatch(navigate({ routeName: 'first' }));
|
||||
});
|
||||
|
||||
expect(firstFocusCallback).toBeCalledTimes(2);
|
||||
expect(thirdBlurCallback).toBeCalledTimes(2);
|
||||
|
||||
act(() => {
|
||||
navigation.current.dispatch(navigate({ routeName: 'fourth' }));
|
||||
});
|
||||
|
||||
expect(fourthFocusCallback).toBeCalledTimes(1);
|
||||
expect(thirdBlurCallback).toBeCalledTimes(2);
|
||||
expect(firstBlurCallback).toBeCalledTimes(2);
|
||||
|
||||
act(() => {
|
||||
navigation.current.dispatch(navigate({ routeName: 'third' }));
|
||||
});
|
||||
|
||||
expect(thirdFocusCallback).toBeCalledTimes(3);
|
||||
expect(fourthBlurCallback).toBeCalledTimes(1);
|
||||
|
||||
// Make sure nothing else has changed
|
||||
expect(firstFocusCallback).toBeCalledTimes(2);
|
||||
expect(firstBlurCallback).toBeCalledTimes(2);
|
||||
|
||||
expect(secondFocusCallback).toBeCalledTimes(1);
|
||||
expect(secondBlurCallback).toBeCalledTimes(1);
|
||||
|
||||
expect(thirdFocusCallback).toBeCalledTimes(3);
|
||||
expect(thirdBlurCallback).toBeCalledTimes(2);
|
||||
|
||||
expect(fourthFocusCallback).toBeCalledTimes(1);
|
||||
expect(fourthBlurCallback).toBeCalledTimes(1);
|
||||
});
|
||||
@@ -1,543 +0,0 @@
|
||||
import getChildEventSubscriber from '../getChildEventSubscriber';
|
||||
|
||||
it('child action events only flow when focused', () => {
|
||||
const parentSubscriber = jest.fn();
|
||||
const emitParentAction = (payload) => {
|
||||
parentSubscriber.mock.calls.forEach((subs) => {
|
||||
if (subs[0] === payload.type) {
|
||||
subs[1](payload);
|
||||
}
|
||||
});
|
||||
};
|
||||
const subscriptionRemove = () => {};
|
||||
parentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
|
||||
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
|
||||
.addListener;
|
||||
const testState = {
|
||||
key: 'foo',
|
||||
routeName: 'FooRoute',
|
||||
routes: [{ key: 'key0' }, { key: 'key1' }],
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
};
|
||||
const focusedTestState = {
|
||||
...testState,
|
||||
index: 1,
|
||||
};
|
||||
const childActionHandler = jest.fn();
|
||||
const childWillFocusHandler = jest.fn();
|
||||
const childDidFocusHandler = jest.fn();
|
||||
childEventSubscriber('action', childActionHandler);
|
||||
childEventSubscriber('willFocus', childWillFocusHandler);
|
||||
childEventSubscriber('didFocus', childDidFocusHandler);
|
||||
emitParentAction({
|
||||
type: 'action',
|
||||
state: focusedTestState,
|
||||
lastState: testState,
|
||||
action: { type: 'FooAction' },
|
||||
});
|
||||
expect(childActionHandler.mock.calls.length).toBe(0);
|
||||
expect(childWillFocusHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidFocusHandler.mock.calls.length).toBe(1);
|
||||
emitParentAction({
|
||||
type: 'action',
|
||||
state: focusedTestState,
|
||||
lastState: focusedTestState,
|
||||
action: { type: 'FooAction' },
|
||||
});
|
||||
expect(childActionHandler.mock.calls.length).toBe(1);
|
||||
expect(childWillFocusHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidFocusHandler.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('grandchildren subscription', () => {
|
||||
const grandParentSubscriber = jest.fn();
|
||||
const emitGrandParentAction = (payload) => {
|
||||
grandParentSubscriber.mock.calls.forEach((subs) => {
|
||||
if (subs[0] === payload.type) {
|
||||
subs[1](payload);
|
||||
}
|
||||
});
|
||||
};
|
||||
const subscriptionRemove = () => {};
|
||||
grandParentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
|
||||
const parentSubscriber = getChildEventSubscriber(
|
||||
grandParentSubscriber,
|
||||
'parent'
|
||||
).addListener;
|
||||
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
|
||||
.addListener;
|
||||
const parentBlurState = {
|
||||
key: 'foo',
|
||||
routeName: 'FooRoute',
|
||||
routes: [
|
||||
{ key: 'aunt' },
|
||||
{
|
||||
key: 'parent',
|
||||
routes: [{ key: 'key0' }, { key: 'key1' }],
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
},
|
||||
],
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
};
|
||||
const parentTransitionState = {
|
||||
...parentBlurState,
|
||||
index: 1,
|
||||
isTransitioning: true,
|
||||
};
|
||||
const parentFocusState = {
|
||||
...parentTransitionState,
|
||||
isTransitioning: false,
|
||||
};
|
||||
const childActionHandler = jest.fn();
|
||||
const childWillFocusHandler = jest.fn();
|
||||
const childDidFocusHandler = jest.fn();
|
||||
childEventSubscriber('action', childActionHandler);
|
||||
childEventSubscriber('willFocus', childWillFocusHandler);
|
||||
childEventSubscriber('didFocus', childDidFocusHandler);
|
||||
emitGrandParentAction({
|
||||
type: 'action',
|
||||
state: parentTransitionState,
|
||||
lastState: parentBlurState,
|
||||
action: { type: 'FooAction' },
|
||||
});
|
||||
expect(childActionHandler.mock.calls.length).toBe(0);
|
||||
expect(childWillFocusHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidFocusHandler.mock.calls.length).toBe(0);
|
||||
emitGrandParentAction({
|
||||
type: 'action',
|
||||
state: parentFocusState,
|
||||
lastState: parentTransitionState,
|
||||
action: { type: 'FooAction' },
|
||||
});
|
||||
expect(childActionHandler.mock.calls.length).toBe(0);
|
||||
expect(childWillFocusHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidFocusHandler.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('grandchildren transitions', () => {
|
||||
const grandParentSubscriber = jest.fn();
|
||||
const emitGrandParentAction = (payload) => {
|
||||
grandParentSubscriber.mock.calls.forEach((subs) => {
|
||||
if (subs[0] === payload.type) {
|
||||
subs[1](payload);
|
||||
}
|
||||
});
|
||||
};
|
||||
const subscriptionRemove = () => {};
|
||||
grandParentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
|
||||
const parentSubscriber = getChildEventSubscriber(
|
||||
grandParentSubscriber,
|
||||
'parent'
|
||||
).addListener;
|
||||
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
|
||||
.addListener;
|
||||
const makeFakeState = (childIndex, childIsTransitioning) => ({
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'nothing' },
|
||||
{
|
||||
key: 'parent',
|
||||
index: childIndex,
|
||||
isTransitioning: childIsTransitioning,
|
||||
routes: [{ key: 'key0' }, { key: 'key1' }, { key: 'key2' }],
|
||||
},
|
||||
],
|
||||
});
|
||||
const blurredState = makeFakeState(0, false);
|
||||
const transitionState = makeFakeState(1, true);
|
||||
const focusState = makeFakeState(1, false);
|
||||
const transition2State = makeFakeState(2, true);
|
||||
const blurred2State = makeFakeState(2, false);
|
||||
|
||||
const childActionHandler = jest.fn();
|
||||
const childWillFocusHandler = jest.fn();
|
||||
const childDidFocusHandler = jest.fn();
|
||||
const childWillBlurHandler = jest.fn();
|
||||
const childDidBlurHandler = jest.fn();
|
||||
childEventSubscriber('action', childActionHandler);
|
||||
childEventSubscriber('willFocus', childWillFocusHandler);
|
||||
childEventSubscriber('didFocus', childDidFocusHandler);
|
||||
childEventSubscriber('willBlur', childWillBlurHandler);
|
||||
childEventSubscriber('didBlur', childDidBlurHandler);
|
||||
emitGrandParentAction({
|
||||
type: 'action',
|
||||
state: transitionState,
|
||||
lastState: blurredState,
|
||||
action: { type: 'FooAction' },
|
||||
});
|
||||
expect(childActionHandler.mock.calls.length).toBe(0);
|
||||
expect(childWillFocusHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidFocusHandler.mock.calls.length).toBe(0);
|
||||
emitGrandParentAction({
|
||||
type: 'action',
|
||||
state: focusState,
|
||||
lastState: transitionState,
|
||||
action: { type: 'FooAction' },
|
||||
});
|
||||
expect(childActionHandler.mock.calls.length).toBe(0);
|
||||
expect(childWillFocusHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidFocusHandler.mock.calls.length).toBe(1);
|
||||
emitGrandParentAction({
|
||||
type: 'action',
|
||||
state: focusState,
|
||||
lastState: focusState,
|
||||
action: { type: 'TestAction' },
|
||||
});
|
||||
expect(childWillFocusHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidFocusHandler.mock.calls.length).toBe(1);
|
||||
expect(childActionHandler.mock.calls.length).toBe(1);
|
||||
emitGrandParentAction({
|
||||
type: 'action',
|
||||
state: transition2State,
|
||||
lastState: focusState,
|
||||
action: { type: 'CauseWillBlurAction' },
|
||||
});
|
||||
expect(childWillBlurHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidBlurHandler.mock.calls.length).toBe(0);
|
||||
expect(childActionHandler.mock.calls.length).toBe(1);
|
||||
emitGrandParentAction({
|
||||
type: 'action',
|
||||
state: blurred2State,
|
||||
lastState: transition2State,
|
||||
action: { type: 'CauseDidBlurAction' },
|
||||
});
|
||||
expect(childWillBlurHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidBlurHandler.mock.calls.length).toBe(1);
|
||||
expect(childActionHandler.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('grandchildren pass through transitions', () => {
|
||||
const grandParentSubscriber = jest.fn();
|
||||
const emitGrandParentAction = (payload) => {
|
||||
grandParentSubscriber.mock.calls.forEach((subs) => {
|
||||
if (subs[0] === payload.type) {
|
||||
subs[1](payload);
|
||||
}
|
||||
});
|
||||
};
|
||||
const subscriptionRemove = () => {};
|
||||
grandParentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
|
||||
const parentSubscriber = getChildEventSubscriber(
|
||||
grandParentSubscriber,
|
||||
'parent'
|
||||
).addListener;
|
||||
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
|
||||
.addListener;
|
||||
const makeFakeState = (childIndex, childIsTransitioning) => ({
|
||||
index: childIndex,
|
||||
isTransitioning: childIsTransitioning,
|
||||
routes: [
|
||||
{ key: 'nothing' },
|
||||
{
|
||||
key: 'parent',
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
routes: [{ key: 'key0' }, { key: 'key1' }, { key: 'key2' }],
|
||||
},
|
||||
].slice(0, childIndex + 1),
|
||||
});
|
||||
const blurredState = makeFakeState(0, false);
|
||||
const transitionState = makeFakeState(1, true);
|
||||
const focusState = makeFakeState(1, false);
|
||||
const transition2State = makeFakeState(0, true);
|
||||
const blurred2State = makeFakeState(0, false);
|
||||
|
||||
const childActionHandler = jest.fn();
|
||||
const childWillFocusHandler = jest.fn();
|
||||
const childDidFocusHandler = jest.fn();
|
||||
const childWillBlurHandler = jest.fn();
|
||||
const childDidBlurHandler = jest.fn();
|
||||
childEventSubscriber('action', childActionHandler);
|
||||
childEventSubscriber('willFocus', childWillFocusHandler);
|
||||
childEventSubscriber('didFocus', childDidFocusHandler);
|
||||
childEventSubscriber('willBlur', childWillBlurHandler);
|
||||
childEventSubscriber('didBlur', childDidBlurHandler);
|
||||
emitGrandParentAction({
|
||||
type: 'action',
|
||||
state: transitionState,
|
||||
lastState: blurredState,
|
||||
action: { type: 'FooAction' },
|
||||
});
|
||||
expect(childActionHandler.mock.calls.length).toBe(0);
|
||||
expect(childWillFocusHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidFocusHandler.mock.calls.length).toBe(0);
|
||||
emitGrandParentAction({
|
||||
type: 'action',
|
||||
state: focusState,
|
||||
lastState: transitionState,
|
||||
action: { type: 'FooAction' },
|
||||
});
|
||||
expect(childActionHandler.mock.calls.length).toBe(0);
|
||||
expect(childWillFocusHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidFocusHandler.mock.calls.length).toBe(1);
|
||||
emitGrandParentAction({
|
||||
type: 'action',
|
||||
state: focusState,
|
||||
lastState: focusState,
|
||||
action: { type: 'TestAction' },
|
||||
});
|
||||
expect(childWillFocusHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidFocusHandler.mock.calls.length).toBe(1);
|
||||
expect(childActionHandler.mock.calls.length).toBe(1);
|
||||
emitGrandParentAction({
|
||||
type: 'action',
|
||||
state: transition2State,
|
||||
lastState: focusState,
|
||||
action: { type: 'CauseWillBlurAction' },
|
||||
});
|
||||
expect(childWillBlurHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidBlurHandler.mock.calls.length).toBe(0);
|
||||
expect(childActionHandler.mock.calls.length).toBe(1);
|
||||
emitGrandParentAction({
|
||||
type: 'action',
|
||||
state: blurred2State,
|
||||
lastState: transition2State,
|
||||
action: { type: 'CauseDidBlurAction' },
|
||||
});
|
||||
expect(childWillBlurHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidBlurHandler.mock.calls.length).toBe(1);
|
||||
expect(childActionHandler.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('child focus with transition', () => {
|
||||
const parentSubscriber = jest.fn();
|
||||
const emitParentAction = (payload) => {
|
||||
parentSubscriber.mock.calls.forEach((subs) => {
|
||||
if (subs[0] === payload.type) {
|
||||
subs[1](payload);
|
||||
}
|
||||
});
|
||||
};
|
||||
const subscriptionRemove = () => {};
|
||||
parentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
|
||||
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
|
||||
.addListener;
|
||||
const randomAction = { type: 'FooAction' };
|
||||
const testState = {
|
||||
key: 'foo',
|
||||
routeName: 'FooRoute',
|
||||
routes: [{ key: 'key0' }, { key: 'key1' }],
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
};
|
||||
const childWillFocusHandler = jest.fn();
|
||||
const childDidFocusHandler = jest.fn();
|
||||
const childWillBlurHandler = jest.fn();
|
||||
const childDidBlurHandler = jest.fn();
|
||||
childEventSubscriber('willFocus', childWillFocusHandler);
|
||||
childEventSubscriber('didFocus', childDidFocusHandler);
|
||||
childEventSubscriber('willBlur', childWillBlurHandler);
|
||||
childEventSubscriber('didBlur', childDidBlurHandler);
|
||||
emitParentAction({
|
||||
type: 'didFocus',
|
||||
action: randomAction,
|
||||
lastState: testState,
|
||||
state: testState,
|
||||
});
|
||||
emitParentAction({
|
||||
type: 'action',
|
||||
action: randomAction,
|
||||
lastState: testState,
|
||||
state: {
|
||||
...testState,
|
||||
index: 1,
|
||||
isTransitioning: true,
|
||||
},
|
||||
});
|
||||
expect(childWillFocusHandler.mock.calls.length).toBe(1);
|
||||
emitParentAction({
|
||||
type: 'action',
|
||||
action: randomAction,
|
||||
lastState: {
|
||||
...testState,
|
||||
index: 1,
|
||||
isTransitioning: true,
|
||||
},
|
||||
state: {
|
||||
...testState,
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
},
|
||||
});
|
||||
expect(childDidFocusHandler.mock.calls.length).toBe(1);
|
||||
emitParentAction({
|
||||
type: 'action',
|
||||
action: randomAction,
|
||||
lastState: {
|
||||
...testState,
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
},
|
||||
state: {
|
||||
...testState,
|
||||
index: 0,
|
||||
isTransitioning: true,
|
||||
},
|
||||
});
|
||||
expect(childWillBlurHandler.mock.calls.length).toBe(1);
|
||||
emitParentAction({
|
||||
type: 'action',
|
||||
action: randomAction,
|
||||
lastState: {
|
||||
...testState,
|
||||
index: 0,
|
||||
isTransitioning: true,
|
||||
},
|
||||
state: {
|
||||
...testState,
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
},
|
||||
});
|
||||
expect(childDidBlurHandler.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('child focus with immediate transition', () => {
|
||||
const parentSubscriber = jest.fn();
|
||||
const emitParentAction = (payload) => {
|
||||
parentSubscriber.mock.calls.forEach((subs) => {
|
||||
if (subs[0] === payload.type) {
|
||||
subs[1](payload);
|
||||
}
|
||||
});
|
||||
};
|
||||
const subscriptionRemove = () => {};
|
||||
parentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
|
||||
const childEventSubscriber = getChildEventSubscriber(parentSubscriber, 'key1')
|
||||
.addListener;
|
||||
const randomAction = { type: 'FooAction' };
|
||||
const testState = {
|
||||
key: 'foo',
|
||||
routeName: 'FooRoute',
|
||||
routes: [{ key: 'key0' }, { key: 'key1' }],
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
};
|
||||
const childWillFocusHandler = jest.fn();
|
||||
const childDidFocusHandler = jest.fn();
|
||||
const childWillBlurHandler = jest.fn();
|
||||
const childDidBlurHandler = jest.fn();
|
||||
childEventSubscriber('willFocus', childWillFocusHandler);
|
||||
childEventSubscriber('didFocus', childDidFocusHandler);
|
||||
childEventSubscriber('willBlur', childWillBlurHandler);
|
||||
childEventSubscriber('didBlur', childDidBlurHandler);
|
||||
emitParentAction({
|
||||
type: 'didFocus',
|
||||
action: randomAction,
|
||||
lastState: testState,
|
||||
state: testState,
|
||||
});
|
||||
emitParentAction({
|
||||
type: 'action',
|
||||
action: randomAction,
|
||||
lastState: testState,
|
||||
state: {
|
||||
...testState,
|
||||
index: 1,
|
||||
},
|
||||
});
|
||||
expect(childWillFocusHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidFocusHandler.mock.calls.length).toBe(1);
|
||||
|
||||
emitParentAction({
|
||||
type: 'action',
|
||||
action: randomAction,
|
||||
lastState: {
|
||||
...testState,
|
||||
index: 1,
|
||||
},
|
||||
state: {
|
||||
...testState,
|
||||
index: 0,
|
||||
},
|
||||
});
|
||||
expect(childWillBlurHandler.mock.calls.length).toBe(1);
|
||||
expect(childDidBlurHandler.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
const setupEventTest = (subscriptionKey, initialLastFocusEvent) => {
|
||||
const parentSubscriber = jest.fn();
|
||||
const emitEvent = (payload) => {
|
||||
parentSubscriber.mock.calls.forEach((subs) => {
|
||||
if (subs[0] === payload.type) {
|
||||
subs[1](payload);
|
||||
}
|
||||
});
|
||||
};
|
||||
const subscriptionRemove = () => {};
|
||||
parentSubscriber.mockReturnValueOnce({ remove: subscriptionRemove });
|
||||
const evtProvider = getChildEventSubscriber(
|
||||
parentSubscriber,
|
||||
subscriptionKey,
|
||||
initialLastFocusEvent
|
||||
);
|
||||
const handlers = {};
|
||||
evtProvider.addListener('action', (handlers.action = jest.fn()));
|
||||
evtProvider.addListener('willFocus', (handlers.willFocus = jest.fn()));
|
||||
evtProvider.addListener('didFocus', (handlers.didFocus = jest.fn()));
|
||||
evtProvider.addListener('willBlur', (handlers.willBlur = jest.fn()));
|
||||
evtProvider.addListener('didBlur', (handlers.didBlur = jest.fn()));
|
||||
return { emitEvent, handlers, evtProvider };
|
||||
};
|
||||
|
||||
it('immediate back with uncompleted transition will focus first screen again', () => {
|
||||
const { handlers, emitEvent } = setupEventTest('key0', 'didFocus');
|
||||
emitEvent({
|
||||
type: 'action',
|
||||
state: {
|
||||
index: 1,
|
||||
routes: [{ key: 'key0' }, { key: 'key1' }],
|
||||
isTransitioning: true,
|
||||
},
|
||||
lastState: {
|
||||
index: 0,
|
||||
routes: [{ key: 'key0' }],
|
||||
isTransitioning: false,
|
||||
},
|
||||
action: { type: 'Any action, does not matter here' },
|
||||
});
|
||||
expect(handlers.willFocus.mock.calls.length).toBe(0);
|
||||
expect(handlers.didFocus.mock.calls.length).toBe(0);
|
||||
expect(handlers.willBlur.mock.calls.length).toBe(1);
|
||||
expect(handlers.didBlur.mock.calls.length).toBe(0);
|
||||
emitEvent({
|
||||
type: 'action',
|
||||
state: {
|
||||
index: 0,
|
||||
routes: [{ key: 'key0' }],
|
||||
isTransitioning: true,
|
||||
},
|
||||
lastState: {
|
||||
index: 1,
|
||||
routes: [{ key: 'key0' }, { key: 'key1' }],
|
||||
isTransitioning: true,
|
||||
},
|
||||
action: { type: 'Any action, does not matter here' },
|
||||
});
|
||||
expect(handlers.willFocus.mock.calls.length).toBe(1);
|
||||
expect(handlers.didFocus.mock.calls.length).toBe(0);
|
||||
expect(handlers.willBlur.mock.calls.length).toBe(1);
|
||||
expect(handlers.didBlur.mock.calls.length).toBe(0);
|
||||
emitEvent({
|
||||
type: 'action',
|
||||
state: {
|
||||
index: 0,
|
||||
routes: [{ key: 'key0' }],
|
||||
isTransitioning: false,
|
||||
},
|
||||
lastState: {
|
||||
index: 0,
|
||||
routes: [{ key: 'key0' }],
|
||||
isTransitioning: true,
|
||||
},
|
||||
action: { type: 'Any action, does not matter here' },
|
||||
});
|
||||
expect(handlers.willFocus.mock.calls.length).toBe(1);
|
||||
expect(handlers.didFocus.mock.calls.length).toBe(1);
|
||||
expect(handlers.willBlur.mock.calls.length).toBe(1);
|
||||
expect(handlers.didBlur.mock.calls.length).toBe(0);
|
||||
});
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
* This is used to extract one children's worth of events from a stream of navigation action events
|
||||
*
|
||||
* Based on the 'action' events that get fired for this navigation state, this utility will fire
|
||||
* focus and blur events for this child
|
||||
*/
|
||||
export default function getChildEventSubscriber(
|
||||
addListener,
|
||||
key,
|
||||
initialLastFocusEvent = 'didBlur'
|
||||
) {
|
||||
const actionSubscribers = new Set();
|
||||
const willFocusSubscribers = new Set();
|
||||
const didFocusSubscribers = new Set();
|
||||
const willBlurSubscribers = new Set();
|
||||
const didBlurSubscribers = new Set();
|
||||
const refocusSubscribers = new Set();
|
||||
|
||||
const removeAll = () => {
|
||||
[
|
||||
actionSubscribers,
|
||||
willFocusSubscribers,
|
||||
didFocusSubscribers,
|
||||
willBlurSubscribers,
|
||||
didBlurSubscribers,
|
||||
refocusSubscribers,
|
||||
].forEach((set) => set.clear());
|
||||
|
||||
upstreamSubscribers.forEach((subs) => subs && subs.remove());
|
||||
};
|
||||
|
||||
const getChildSubscribers = (evtName) => {
|
||||
switch (evtName) {
|
||||
case 'action':
|
||||
return actionSubscribers;
|
||||
case 'willFocus':
|
||||
return willFocusSubscribers;
|
||||
case 'didFocus':
|
||||
return didFocusSubscribers;
|
||||
case 'willBlur':
|
||||
return willBlurSubscribers;
|
||||
case 'didBlur':
|
||||
return didBlurSubscribers;
|
||||
case 'refocus':
|
||||
return refocusSubscribers;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const emit = (type, payload) => {
|
||||
const payloadWithType = { ...payload, type };
|
||||
const subscribers = getChildSubscribers(type);
|
||||
subscribers &&
|
||||
subscribers.forEach((subs) => {
|
||||
subs(payloadWithType);
|
||||
});
|
||||
};
|
||||
|
||||
// lastFocusEvent keeps track of focus state for one route. First we assume
|
||||
// we are blurred. If we are focused on initialization, the first 'action'
|
||||
// event will cause onFocus+willFocus events because we had previously been
|
||||
// considered blurred
|
||||
let lastFocusEvent = initialLastFocusEvent;
|
||||
|
||||
const upstreamEvents = [
|
||||
'willFocus',
|
||||
'didFocus',
|
||||
'willBlur',
|
||||
'didBlur',
|
||||
'refocus',
|
||||
'action',
|
||||
];
|
||||
|
||||
const upstreamSubscribers = upstreamEvents.map((eventName) =>
|
||||
addListener(eventName, (payload) => {
|
||||
if (eventName === 'refocus') {
|
||||
emit(eventName, payload);
|
||||
return;
|
||||
}
|
||||
|
||||
const { state, lastState, action } = payload;
|
||||
const lastRoutes = lastState && lastState.routes;
|
||||
const routes = state && state.routes;
|
||||
|
||||
// const lastFocusKey =
|
||||
// lastState && lastState.routes && lastState.routes[lastState.index].key;
|
||||
const focusKey = routes && routes[state.index].key;
|
||||
|
||||
const isChildFocused = focusKey === key;
|
||||
const lastRoute =
|
||||
lastRoutes && lastRoutes.find((route) => route.key === key);
|
||||
const newRoute = routes && routes.find((route) => route.key === key);
|
||||
const childPayload = {
|
||||
context: `${key}:${action.type}_${payload.context || 'Root'}`,
|
||||
state: newRoute,
|
||||
lastState: lastRoute,
|
||||
action,
|
||||
type: eventName,
|
||||
};
|
||||
const isTransitioning = !!state && state.isTransitioning;
|
||||
|
||||
const previouslylastFocusEvent = lastFocusEvent;
|
||||
|
||||
if (lastFocusEvent === 'didBlur') {
|
||||
// The child is currently blurred. Look for willFocus conditions
|
||||
if (eventName === 'willFocus' && isChildFocused) {
|
||||
emit((lastFocusEvent = 'willFocus'), childPayload);
|
||||
} else if (eventName === 'action' && isChildFocused) {
|
||||
emit((lastFocusEvent = 'willFocus'), childPayload);
|
||||
}
|
||||
}
|
||||
if (lastFocusEvent === 'willFocus') {
|
||||
// We are currently mid-focus. Look for didFocus conditions.
|
||||
// If state.isTransitioning is false, this child event happens immediately after willFocus
|
||||
if (eventName === 'didFocus' && isChildFocused && !isTransitioning) {
|
||||
emit((lastFocusEvent = 'didFocus'), childPayload);
|
||||
} else if (
|
||||
eventName === 'action' &&
|
||||
isChildFocused &&
|
||||
!isTransitioning
|
||||
) {
|
||||
emit((lastFocusEvent = 'didFocus'), childPayload);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastFocusEvent === 'didFocus') {
|
||||
// The child is currently focused. Look for blurring events
|
||||
if (!isChildFocused) {
|
||||
// The child is no longer focused within this navigation state
|
||||
emit((lastFocusEvent = 'willBlur'), childPayload);
|
||||
} else if (eventName === 'willBlur') {
|
||||
// The parent is getting a willBlur event
|
||||
emit((lastFocusEvent = 'willBlur'), childPayload);
|
||||
} else if (
|
||||
eventName === 'action' &&
|
||||
previouslylastFocusEvent === 'didFocus'
|
||||
) {
|
||||
// While focused, pass action events to children for grandchildren focus
|
||||
emit('action', childPayload);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastFocusEvent === 'willBlur') {
|
||||
// The child is mid-blur. Wait for transition to end
|
||||
if (eventName === 'action' && !isChildFocused && !isTransitioning) {
|
||||
// The child is done blurring because transitioning is over, or isTransitioning
|
||||
// never began and didBlur fires immediately after willBlur
|
||||
emit((lastFocusEvent = 'didBlur'), childPayload);
|
||||
} else if (eventName === 'didBlur') {
|
||||
// Pass through the parent didBlur event if it happens
|
||||
emit((lastFocusEvent = 'didBlur'), childPayload);
|
||||
} else if (
|
||||
eventName === 'action' &&
|
||||
isChildFocused &&
|
||||
!isTransitioning
|
||||
) {
|
||||
emit((lastFocusEvent = 'didFocus'), childPayload);
|
||||
} else if (
|
||||
eventName === 'action' &&
|
||||
isChildFocused &&
|
||||
isTransitioning
|
||||
) {
|
||||
emit((lastFocusEvent = 'willFocus'), childPayload);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastFocusEvent === 'didBlur' && !newRoute) {
|
||||
removeAll();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
addListener(eventName, eventHandler) {
|
||||
const subscribers = getChildSubscribers(eventName);
|
||||
if (!subscribers) {
|
||||
throw new Error(`Invalid event name "${eventName}"`);
|
||||
}
|
||||
subscribers.add(eventHandler);
|
||||
const remove = () => {
|
||||
subscribers.delete(eventHandler);
|
||||
};
|
||||
return { remove };
|
||||
},
|
||||
emit(eventName, payload) {
|
||||
if (eventName !== 'refocus') {
|
||||
console.error(
|
||||
`navigation.emit only supports the 'refocus' event currently.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
emit(eventName, payload);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import getChildEventSubscriber from './getChildEventSubscriber';
|
||||
import getChildRouter from './getChildRouter';
|
||||
import getNavigationActionCreators from './routers/getNavigationActionCreators';
|
||||
import getChildrenNavigationCache from './getChildrenNavigationCache';
|
||||
import getEventManager from './getEventManager';
|
||||
|
||||
const createParamGetter = (route) => (paramName, defaultValue) => {
|
||||
const params = route.params;
|
||||
@@ -78,10 +78,7 @@ function getChildNavigation(navigation, childKey, getCurrentParentNavigation) {
|
||||
};
|
||||
return children[childKey];
|
||||
} else {
|
||||
const childSubscriber = getChildEventSubscriber(
|
||||
navigation.addListener,
|
||||
childKey
|
||||
);
|
||||
const { addListener, emit } = getEventManager(childKey);
|
||||
|
||||
children[childKey] = {
|
||||
...actionHelpers,
|
||||
@@ -115,9 +112,10 @@ function getChildNavigation(navigation, childKey, getCurrentParentNavigation) {
|
||||
dispatch: navigation.dispatch,
|
||||
getScreenProps: navigation.getScreenProps,
|
||||
dangerouslyGetParent: getCurrentParentNavigation,
|
||||
addListener: childSubscriber.addListener,
|
||||
emit: childSubscriber.emit,
|
||||
addListener,
|
||||
emit,
|
||||
};
|
||||
|
||||
return children[childKey];
|
||||
}
|
||||
}
|
||||
|
||||
61
packages/core/src/getEventManager.js
Normal file
61
packages/core/src/getEventManager.js
Normal file
@@ -0,0 +1,61 @@
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @param {string} target
|
||||
*/
|
||||
export default function getEventManager(target) {
|
||||
/**
|
||||
* @type {Record<string, Record<string, ((e: any) => void)[]>>}
|
||||
*/
|
||||
const listeners = {};
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {() => void} callback
|
||||
*/
|
||||
const removeListener = (type, callback) => {
|
||||
const callbacks = listeners[type] ? listeners[type][target] : undefined;
|
||||
|
||||
if (!callbacks) {
|
||||
return;
|
||||
}
|
||||
|
||||
const index = callbacks.indexOf(callback);
|
||||
|
||||
callbacks.splice(index, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {() => void} callback
|
||||
*/
|
||||
const addListener = (type, callback) => {
|
||||
listeners[type] = listeners[type] || {};
|
||||
listeners[type][target] = listeners[type][target] || [];
|
||||
listeners[type][target].push(callback);
|
||||
|
||||
return {
|
||||
remove: () => removeListener(type, callback),
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
addListener,
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {any} [data]
|
||||
*/
|
||||
emit: (type, data) => {
|
||||
const items = listeners[type] || {};
|
||||
|
||||
/**
|
||||
* Copy the current list of callbacks in case they are mutated during execution
|
||||
* @type {((data: any) => void)[] | undefined}
|
||||
*/
|
||||
const callbacks = items[target] && items[target].slice();
|
||||
|
||||
callbacks?.forEach((cb) => cb(data));
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import invariant from '../utils/invariant';
|
||||
import ThemeContext from '../views/ThemeContext';
|
||||
import NavigationFocusEvents from '../views/NavigationFocusEvents';
|
||||
|
||||
function createNavigator(NavigatorView, router, navigationConfig) {
|
||||
class Navigator extends React.Component {
|
||||
@@ -78,13 +79,21 @@ function createNavigator(NavigatorView, router, navigationConfig) {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<NavigatorView
|
||||
{...this.props}
|
||||
screenProps={this.state.screenProps}
|
||||
navigation={this.props.navigation}
|
||||
navigationConfig={navigationConfig}
|
||||
descriptors={this.state.descriptors}
|
||||
/>
|
||||
<React.Fragment>
|
||||
<NavigationFocusEvents
|
||||
navigation={this.props.navigation}
|
||||
onEvent={(target, type, data) => {
|
||||
this.state.descriptors[target]?.navigation.emit(type, data);
|
||||
}}
|
||||
/>
|
||||
<NavigatorView
|
||||
{...this.props}
|
||||
screenProps={this.state.screenProps}
|
||||
navigation={this.props.navigation}
|
||||
navigationConfig={navigationConfig}
|
||||
descriptors={this.state.descriptors}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,18 +536,21 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
|
||||
if (action.type === StackActions.POP && prune === false && key) {
|
||||
const index = state.routes.findIndex((r) => r.key === key);
|
||||
const count = Math.max(index - (n == null ? 1 : n) + 1, 1);
|
||||
const routes = state.routes
|
||||
.slice(0, count)
|
||||
.concat(state.routes.slice(index + 1));
|
||||
|
||||
if (routes.length) {
|
||||
return {
|
||||
...state,
|
||||
routes,
|
||||
index: routes.length - 1,
|
||||
isTransitioning: immediate !== true,
|
||||
};
|
||||
if (index > 0) {
|
||||
const count = Math.max(index - (n ?? 1) + 1, 1);
|
||||
const routes = state.routes
|
||||
.slice(0, count)
|
||||
.concat(state.routes.slice(index + 1));
|
||||
|
||||
if (routes.length) {
|
||||
return {
|
||||
...state,
|
||||
routes,
|
||||
index: routes.length - 1,
|
||||
isTransitioning: immediate !== true,
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let backRouteIndex = state.index;
|
||||
|
||||
@@ -128,8 +128,8 @@ export default (routeConfigs, config = {}) => {
|
||||
const initialState = {
|
||||
routes,
|
||||
index: initialRouteIndex,
|
||||
isTransitioning: false,
|
||||
};
|
||||
|
||||
if (backBehavior === 'history') {
|
||||
const initialKey = routes[initialRouteIndex].key;
|
||||
initialState['routeKeyHistory'] = [initialKey];
|
||||
|
||||
@@ -26,7 +26,6 @@ describe('TabRouter', () => {
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(state).toEqual(expectedState);
|
||||
const state2 = router.getStateForAction(
|
||||
@@ -39,7 +38,6 @@ describe('TabRouter', () => {
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(state2).toEqual(expectedState2);
|
||||
expect(router.getComponentForState(expectedState)).toEqual(ScreenA);
|
||||
@@ -65,7 +63,6 @@ describe('TabRouter', () => {
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(state).toEqual(expectedState);
|
||||
const state2 = router.getStateForAction(
|
||||
@@ -78,7 +75,6 @@ describe('TabRouter', () => {
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(state2).toEqual(expectedState2);
|
||||
expect(router.getComponentForState(expectedState)).toEqual(ScreenA);
|
||||
@@ -102,7 +98,6 @@ describe('TabRouter', () => {
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -118,7 +113,6 @@ describe('TabRouter', () => {
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar', params: { name: 'Qux' } },
|
||||
],
|
||||
isTransitioning: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -230,7 +224,7 @@ describe('TabRouter', () => {
|
||||
const state = router.getStateForAction(navAction);
|
||||
expect(state).toEqual({
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{
|
||||
key: 'Foo',
|
||||
@@ -238,7 +232,7 @@ describe('TabRouter', () => {
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
params: { foo: '42' },
|
||||
@@ -281,14 +275,14 @@ describe('TabRouter', () => {
|
||||
let state = router.getStateForAction(navAction);
|
||||
expect(state).toEqual({
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{
|
||||
index: 0,
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
@@ -308,7 +302,7 @@ describe('TabRouter', () => {
|
||||
);
|
||||
expect(state && state.routes[1]).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
routes: [
|
||||
@@ -335,14 +329,14 @@ describe('TabRouter', () => {
|
||||
});
|
||||
expect(state).toEqual({
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{
|
||||
index: 1,
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
@@ -357,14 +351,14 @@ describe('TabRouter', () => {
|
||||
);
|
||||
expect(state2).toEqual({
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{
|
||||
index: 0,
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
@@ -403,19 +397,19 @@ describe('TabRouter', () => {
|
||||
const state = router.getStateForAction(INIT_ACTION);
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
key: 'Fee',
|
||||
routeName: 'Fee',
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
@@ -425,7 +419,7 @@ describe('TabRouter', () => {
|
||||
index: 0,
|
||||
key: 'Bar',
|
||||
routeName: 'Bar',
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Zoo', routeName: 'Zoo' },
|
||||
{ key: 'Zap', routeName: 'Zap' },
|
||||
@@ -442,19 +436,19 @@ describe('TabRouter', () => {
|
||||
);
|
||||
expect(state2).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{
|
||||
index: 1,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
key: 'Fee',
|
||||
routeName: 'Fee',
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
@@ -464,7 +458,7 @@ describe('TabRouter', () => {
|
||||
index: 1,
|
||||
key: 'Bar',
|
||||
routeName: 'Bar',
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Zoo', routeName: 'Zoo' },
|
||||
{ key: 'Zap', routeName: 'Zap' },
|
||||
@@ -494,19 +488,19 @@ describe('TabRouter', () => {
|
||||
});
|
||||
expect(state4).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{
|
||||
index: 1,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
key: 'Fee',
|
||||
routeName: 'Fee',
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
@@ -516,7 +510,7 @@ describe('TabRouter', () => {
|
||||
index: 1,
|
||||
key: 'Bar',
|
||||
routeName: 'Bar',
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Zoo', routeName: 'Zoo' },
|
||||
{ key: 'Zap', routeName: 'Zap' },
|
||||
@@ -557,7 +551,7 @@ describe('TabRouter', () => {
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const expectedState = {
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
@@ -567,7 +561,7 @@ describe('TabRouter', () => {
|
||||
const state2 = router.getStateForAction(expectedAction, state);
|
||||
const expectedState2 = {
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo', params: undefined },
|
||||
{
|
||||
@@ -628,13 +622,13 @@ describe('TabRouter', () => {
|
||||
|
||||
const state = {
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{
|
||||
index: 1,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
@@ -660,7 +654,7 @@ describe('TabRouter', () => {
|
||||
|
||||
expect(state0).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'a' },
|
||||
{ key: 'b', routeName: 'b' },
|
||||
@@ -676,7 +670,7 @@ describe('TabRouter', () => {
|
||||
|
||||
expect(state1).toEqual({
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'a' },
|
||||
{ key: 'b', routeName: 'b', params },
|
||||
@@ -765,13 +759,13 @@ describe('TabRouter', () => {
|
||||
const screenApreState = {
|
||||
index: 0,
|
||||
key: 'Foo',
|
||||
isTransitioning: false,
|
||||
|
||||
routeName: 'Foo',
|
||||
routes: [{ key: 'Bar', routeName: 'Bar' }],
|
||||
};
|
||||
const preState = {
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [screenApreState],
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export default function docsUrl(path: string) {
|
||||
return `https://reactnavigation.org/docs/${path}`;
|
||||
return `https://reactnavigation.org/docs/4.x/${path}`;
|
||||
}
|
||||
|
||||
360
packages/core/src/views/NavigationFocusEvents.js
Normal file
360
packages/core/src/views/NavigationFocusEvents.js
Normal file
@@ -0,0 +1,360 @@
|
||||
// @ts-check
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
/**
|
||||
* @typedef {object} State
|
||||
* @prop {number} index
|
||||
* @prop {({ key: string } & (State | {}))[]} routes
|
||||
* @prop {boolean} [isTransitioning]
|
||||
*
|
||||
* @typedef {object} ParentPayload
|
||||
* @prop {string} type
|
||||
* @prop {object} action
|
||||
* @prop {State} state
|
||||
* @prop {State | {key: string, routes?: undefined, index?: undefined, isTransitioning?: undefined} | undefined | null} lastState
|
||||
* @prop {string} [context]
|
||||
*
|
||||
* @typedef {object} Payload
|
||||
* @prop {string} type
|
||||
* @prop {object} action
|
||||
* @prop {State | {key: string}} state
|
||||
* @prop {State | {key: string} | undefined | null} lastState
|
||||
* @prop {string} [context]
|
||||
*
|
||||
* @typedef {object} Props
|
||||
* @prop {object} navigation
|
||||
* @prop {Function} navigation.addListener
|
||||
* @prop {Function} navigation.removeListener
|
||||
* @prop {() => boolean} navigation.isFocused
|
||||
* @prop {() => object | undefined} navigation.dangerouslyGetParent
|
||||
* @prop {State} navigation.state
|
||||
* @prop {(target: string, type: string, data: any) => void} onEvent
|
||||
*
|
||||
* @extends {React.Component<Props>}
|
||||
*/
|
||||
export default class NavigationEventManager extends React.Component {
|
||||
componentDidMount() {
|
||||
const { navigation } = this.props;
|
||||
|
||||
this._actionSubscription = navigation.addListener(
|
||||
'action',
|
||||
this._handleAction
|
||||
);
|
||||
|
||||
this._willFocusSubscription = navigation.addListener(
|
||||
'willFocus',
|
||||
this._handleWillFocus
|
||||
);
|
||||
|
||||
this._willBlurSubscription = navigation.addListener(
|
||||
'willBlur',
|
||||
this._handleWillBlur
|
||||
);
|
||||
|
||||
this._didFocusSubscription = navigation.addListener(
|
||||
'didFocus',
|
||||
this._handleDidFocus
|
||||
);
|
||||
|
||||
this._didBlurSubscription = navigation.addListener(
|
||||
'didBlur',
|
||||
this._handleDidBlur
|
||||
);
|
||||
|
||||
this._refocusSubscription = navigation.addListener(
|
||||
'refocus',
|
||||
this._handleRefocus
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._actionSubscription?.remove();
|
||||
this._willFocusSubscription?.remove();
|
||||
this._willBlurSubscription?.remove();
|
||||
this._didFocusSubscription?.remove();
|
||||
this._didBlurSubscription?.remove();
|
||||
this._refocusSubscription?.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {{ remove(): void } | undefined}
|
||||
*/
|
||||
_actionSubscription;
|
||||
|
||||
/**
|
||||
* @type {{ remove(): void } | undefined}
|
||||
*/
|
||||
_willFocusSubscription;
|
||||
|
||||
/**
|
||||
* @type {{ remove(): void } | undefined}
|
||||
*/
|
||||
_willBlurSubscription;
|
||||
|
||||
/**
|
||||
* @type {{ remove(): void } | undefined}
|
||||
*/
|
||||
_didFocusSubscription;
|
||||
|
||||
/**
|
||||
* @type {{ remove(): void } | undefined}
|
||||
*/
|
||||
_didBlurSubscription;
|
||||
|
||||
/**
|
||||
* @type {{ remove(): void } | undefined}
|
||||
*/
|
||||
_refocusSubscription;
|
||||
|
||||
/**
|
||||
* @type {string | undefined}
|
||||
*/
|
||||
_lastWillBlurKey;
|
||||
|
||||
/**
|
||||
* @type {string | undefined}
|
||||
*/
|
||||
_lastWillFocusKey;
|
||||
|
||||
/**
|
||||
* @type {string | undefined}
|
||||
*/
|
||||
_lastDidBlurKey;
|
||||
|
||||
/**
|
||||
* @type {string | undefined}
|
||||
*/
|
||||
_lastDidFocusKey;
|
||||
|
||||
/**
|
||||
* The 'action' event will fire when navigation state changes.
|
||||
* Detect if the focused route changed here and emit appropriate events.
|
||||
*
|
||||
* @param {ParentPayload} payload
|
||||
*/
|
||||
_handleAction = ({ state, lastState, action, type, context }) => {
|
||||
const { navigation, onEvent } = this.props;
|
||||
|
||||
// We should only emit events when the navigator is focused
|
||||
// When navigator is not focused, screens inside shouldn't receive focused status either
|
||||
if (!navigation.isFocused()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const previous = lastState
|
||||
? lastState.routes?.[lastState.index]
|
||||
: undefined;
|
||||
const current = state.routes[state.index];
|
||||
|
||||
const payload = {
|
||||
context: `${current.key}:${action.type}_${context || 'Root'}`,
|
||||
state: current,
|
||||
lastState: previous,
|
||||
action,
|
||||
type,
|
||||
};
|
||||
|
||||
if (previous?.key !== current.key) {
|
||||
this._emitWillFocus(current.key, payload);
|
||||
|
||||
if (previous?.key) {
|
||||
this._emitWillBlur(previous.key, payload);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
lastState?.isTransitioning !== state.isTransitioning &&
|
||||
state.isTransitioning === false
|
||||
) {
|
||||
if (this._lastWillBlurKey) {
|
||||
this._emitDidBlur(this._lastWillBlurKey, payload);
|
||||
}
|
||||
|
||||
if (this._lastWillFocusKey) {
|
||||
this._emitDidFocus(this._lastWillFocusKey, payload);
|
||||
}
|
||||
}
|
||||
|
||||
onEvent(current.key, 'action', payload);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ParentPayload} payload
|
||||
*/
|
||||
_handleWillFocus = ({ lastState, action, context, type }) => {
|
||||
const { navigation } = this.props;
|
||||
const route = navigation.state.routes[navigation.state.index];
|
||||
|
||||
this._emitWillFocus(route.key, {
|
||||
context: `${route.key}:${action.type}_${context || 'Root'}`,
|
||||
state: route,
|
||||
lastState: lastState?.routes?.find((r) => r.key === route.key),
|
||||
action,
|
||||
type,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ParentPayload} payload
|
||||
*/
|
||||
_handleWillBlur = ({ lastState, action, context, type }) => {
|
||||
const { navigation } = this.props;
|
||||
const route = navigation.state.routes[navigation.state.index];
|
||||
|
||||
this._emitWillBlur(route.key, {
|
||||
context: `${route.key}:${action.type}_${context || 'Root'}`,
|
||||
state: route,
|
||||
lastState: lastState?.routes?.find((r) => r.key === route.key),
|
||||
action,
|
||||
type,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ParentPayload} payload
|
||||
*/
|
||||
_handleDidFocus = ({ lastState, action, context, type }) => {
|
||||
const { navigation } = this.props;
|
||||
|
||||
if (this._lastWillFocusKey) {
|
||||
const route = navigation.state.routes.find(
|
||||
(r) => r.key === this._lastWillFocusKey
|
||||
);
|
||||
|
||||
if (route) {
|
||||
this._emitDidFocus(route.key, {
|
||||
context: `${route.key}:${action.type}_${context || 'Root'}`,
|
||||
state: route,
|
||||
lastState: lastState?.routes?.find((r) => r.key === route.key),
|
||||
action,
|
||||
type,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ParentPayload} payload
|
||||
*/
|
||||
_handleDidBlur = ({ lastState, action, context, type }) => {
|
||||
const { navigation } = this.props;
|
||||
|
||||
if (this._lastWillBlurKey) {
|
||||
const route = navigation.state.routes.find(
|
||||
(r) => r.key === this._lastWillBlurKey
|
||||
);
|
||||
|
||||
if (route) {
|
||||
this._emitDidBlur(route.key, {
|
||||
context: `${route.key}:${action.type}_${context || 'Root'}`,
|
||||
state: route,
|
||||
lastState: lastState?.routes?.find((r) => r.key === route.key),
|
||||
action,
|
||||
type,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_handleRefocus = () => {
|
||||
const { onEvent, navigation } = this.props;
|
||||
const route = navigation.state.routes[navigation.state.index];
|
||||
|
||||
onEvent(route.key, 'refocus');
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} target
|
||||
* @param {Payload} payload
|
||||
*/
|
||||
_emitWillFocus = (target, payload) => {
|
||||
if (this._lastWillBlurKey === target) {
|
||||
this._lastWillBlurKey = undefined;
|
||||
}
|
||||
|
||||
if (this._lastWillFocusKey === target) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._lastDidFocusKey = undefined;
|
||||
this._lastWillFocusKey = target;
|
||||
|
||||
const { navigation, onEvent } = this.props;
|
||||
|
||||
onEvent(target, 'willFocus', payload);
|
||||
|
||||
if (
|
||||
typeof navigation.state.isTransitioning !== 'boolean' ||
|
||||
(navigation.state.isTransitioning !== true &&
|
||||
!navigation.dangerouslyGetParent())
|
||||
) {
|
||||
this._emitDidFocus(target, payload);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} target
|
||||
* @param {Payload} payload
|
||||
*/
|
||||
_emitWillBlur = (target, payload) => {
|
||||
if (this._lastWillFocusKey === target) {
|
||||
this._lastWillFocusKey = undefined;
|
||||
}
|
||||
|
||||
if (this._lastWillBlurKey === target) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._lastDidBlurKey = undefined;
|
||||
this._lastWillBlurKey = target;
|
||||
|
||||
const { navigation, onEvent } = this.props;
|
||||
|
||||
onEvent(target, 'willBlur', payload);
|
||||
|
||||
if (
|
||||
typeof navigation.state.isTransitioning !== 'boolean' ||
|
||||
(navigation.state.isTransitioning !== true &&
|
||||
!navigation.dangerouslyGetParent())
|
||||
) {
|
||||
this._emitDidBlur(target, payload);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} target
|
||||
* @param {Payload} payload
|
||||
*/
|
||||
_emitDidFocus = (target, payload) => {
|
||||
if (this._lastWillFocusKey !== target || this._lastDidFocusKey === target) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._lastDidFocusKey = target;
|
||||
|
||||
const { onEvent } = this.props;
|
||||
|
||||
onEvent(target, 'didFocus', payload);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} target
|
||||
* @param {Payload} payload
|
||||
*/
|
||||
_emitDidBlur = (target, payload) => {
|
||||
if (this._lastWillBlurKey !== target || this._lastDidBlurKey === target) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._lastDidBlurKey = target;
|
||||
|
||||
const { onEvent } = this.props;
|
||||
|
||||
onEvent(target, 'didBlur', payload);
|
||||
};
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -8,25 +8,25 @@ export default function withNavigationFocus(Component) {
|
||||
Component.displayName || Component.name
|
||||
})`;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
state = {
|
||||
isFocused: this.props.navigation.isFocused(),
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const { navigation } = this.props;
|
||||
|
||||
this.subscriptions = [
|
||||
props.navigation.addListener('didFocus', () =>
|
||||
navigation.addListener('willFocus', () =>
|
||||
this.setState({ isFocused: true })
|
||||
),
|
||||
props.navigation.addListener('willBlur', () =>
|
||||
navigation.addListener('willBlur', () =>
|
||||
this.setState({ isFocused: false })
|
||||
),
|
||||
];
|
||||
|
||||
this.state = {
|
||||
isFocused: props.navigation ? props.navigation.isFocused() : false,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.subscriptions.forEach((sub) => sub.remove());
|
||||
this.subscriptions?.forEach((sub) => sub.remove());
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
6
packages/core/tsconfig.json
Normal file
6
packages/core/tsconfig.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": {
|
||||
"allowJs": true
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,68 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.5.0](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.13...react-navigation-drawer@2.5.0) (2020-06-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* sync latest stack and upgrade typescript ([454e05b](https://github.com/react-navigation/drawer/commit/454e05b02ec97f053b381fdc801df899d8c93cb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.4.13](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.12...react-navigation-drawer@2.4.13) (2020-04-30)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.4.12](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.11...react-navigation-drawer@2.4.12) (2020-04-22)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.4.11](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.10...react-navigation-drawer@2.4.11) (2020-04-02)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.4.10](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.9...react-navigation-drawer@2.4.10) (2020-03-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove isTransitioning from SwitchRouter state ([3bec1c9](https://github.com/react-navigation/drawer/commit/3bec1c964a49136c0ead8e8ba8a8c66c556bbcba))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.4.9](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.7...react-navigation-drawer@2.4.9) (2020-03-28)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.4.8](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.7...react-navigation-drawer@2.4.8) (2020-03-27)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.4.7](https://github.com/react-navigation/drawer/compare/react-navigation-drawer@2.4.6...react-navigation-drawer@2.4.7) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-drawer
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-navigation-drawer",
|
||||
"version": "2.4.7",
|
||||
"version": "2.5.0",
|
||||
"description": "Drawer navigator component for React Navigation",
|
||||
"main": "lib/commonjs/index.js",
|
||||
"react-native": "lib/module/index.js",
|
||||
@@ -49,8 +49,8 @@
|
||||
"react-native-reanimated": "^1.2.0",
|
||||
"react-native-screens": "^2.3.0",
|
||||
"react-native-testing-library": "^1.12.0",
|
||||
"react-navigation": "^4.3.3",
|
||||
"typescript": "~3.7.5"
|
||||
"react-navigation": "^4.4.0",
|
||||
"typescript": "^3.9.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
|
||||
@@ -75,8 +75,11 @@ const DrawerNavigator = (
|
||||
const drawerRouter = DrawerRouter(routeConfigs, mergedConfig);
|
||||
|
||||
// TODO: don't have time to fix it right now
|
||||
// @ts-ignore
|
||||
const navigator = createNavigator(DrawerView, drawerRouter, mergedConfig);
|
||||
const navigator = createNavigator(
|
||||
DrawerView as any,
|
||||
drawerRouter,
|
||||
mergedConfig
|
||||
);
|
||||
return navigator;
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ it('handles basic drawer logic and fires close on switch', () => {
|
||||
const state = router.getStateForAction(INIT_ACTION);
|
||||
const expectedState = {
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo', params: undefined },
|
||||
{ key: 'Bar', routeName: 'Bar', params: undefined },
|
||||
@@ -32,7 +32,7 @@ it('handles basic drawer logic and fires close on switch', () => {
|
||||
);
|
||||
const expectedState2 = {
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo', params: undefined },
|
||||
{ key: 'Bar', routeName: 'Bar', params: undefined },
|
||||
@@ -65,7 +65,7 @@ it('handles initial route navigation', () => {
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
isDrawerOpen: false,
|
||||
isTransitioning: false,
|
||||
|
||||
routes: [
|
||||
{
|
||||
key: 'Foo',
|
||||
|
||||
@@ -160,10 +160,6 @@ export default class DrawerView extends React.PureComponent<Props, State> {
|
||||
drawerOpenProgress={progress}
|
||||
navigation={this.props.navigation}
|
||||
descriptors={this.props.descriptors}
|
||||
contentComponent={this.props.navigationConfig.contentComponent}
|
||||
contentOptions={this.props.navigationConfig.contentOptions}
|
||||
drawerPosition={this.props.navigationConfig.drawerPosition}
|
||||
style={this.props.navigationConfig.style}
|
||||
{...this.props.navigationConfig}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,65 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.3.0](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.12...react-navigation-material-bottom-tabs@2.3.0) (2020-06-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* sync latest stack and upgrade typescript ([454e05b](https://github.com/react-navigation/react-navigation-material-bottom-tabs/commit/454e05b02ec97f053b381fdc801df899d8c93cb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.12](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.11...react-navigation-material-bottom-tabs@2.2.12) (2020-04-30)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.11](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.10...react-navigation-material-bottom-tabs@2.2.11) (2020-04-22)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.10](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.9...react-navigation-material-bottom-tabs@2.2.10) (2020-04-02)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.9](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.8...react-navigation-material-bottom-tabs@2.2.9) (2020-03-31)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.8](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.6...react-navigation-material-bottom-tabs@2.2.8) (2020-03-28)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.7](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.6...react-navigation-material-bottom-tabs@2.2.7) (2020-03-27)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.6](https://github.com/react-navigation/react-navigation-material-bottom-tabs/compare/react-navigation-material-bottom-tabs@2.2.5...react-navigation-material-bottom-tabs@2.2.6) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-material-bottom-tabs
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-navigation-material-bottom-tabs",
|
||||
"version": "2.2.6",
|
||||
"version": "2.3.0",
|
||||
"description": "Material Bottom Tab Navigation component for React Navigation",
|
||||
"main": "lib/commonjs/index.js",
|
||||
"module": "lib/module/index.js",
|
||||
@@ -48,8 +48,8 @@
|
||||
"react-native-gesture-handler": "^1.6.0",
|
||||
"react-native-paper": "^3.1.1",
|
||||
"react-native-reanimated": "^1.2.0",
|
||||
"react-navigation": "^4.3.3",
|
||||
"typescript": "~3.7.5"
|
||||
"react-navigation": "^4.4.0",
|
||||
"typescript": "^3.9.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
|
||||
@@ -3,6 +3,76 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.8.0](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.13...@react-navigation/native@3.8.0) (2020-06-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve error message when installing wrong version ([442ca3e](https://github.com/react-navigation/react-navigation-native/commit/442ca3e700c5805cdc5cd02c68f49042887a5054))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* sync latest stack and upgrade typescript ([454e05b](https://github.com/react-navigation/react-navigation-native/commit/454e05b02ec97f053b381fdc801df899d8c93cb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.7.13](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.12...@react-navigation/native@3.7.13) (2020-04-30)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.7.12](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.11...@react-navigation/native@3.7.12) (2020-04-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.7.11](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.10...@react-navigation/native@3.7.11) (2020-04-02)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.7.10](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.9...@react-navigation/native@3.7.10) (2020-03-31)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.7.9](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.7...@react-navigation/native@3.7.9) (2020-03-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* rework focus and blur events to make them more reliable ([cd08338](https://github.com/react-navigation/react-navigation-native/commit/cd083381866506a192f1ec842ac169f2b4277ca5)), closes [#4867](https://github.com/react-navigation/react-navigation-native/issues/4867) [#6187](https://github.com/react-navigation/react-navigation-native/issues/6187) [#6451](https://github.com/react-navigation/react-navigation-native/issues/6451) [#7628](https://github.com/react-navigation/react-navigation-native/issues/7628) [#7749](https://github.com/react-navigation/react-navigation-native/issues/7749)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.7.8](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.7...@react-navigation/native@3.7.8) (2020-03-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* rework focus and blur events to make them more reliable ([cd08338](https://github.com/react-navigation/react-navigation-native/commit/cd083381866506a192f1ec842ac169f2b4277ca5)), closes [#4867](https://github.com/react-navigation/react-navigation-native/issues/4867) [#6187](https://github.com/react-navigation/react-navigation-native/issues/6187) [#6451](https://github.com/react-navigation/react-navigation-native/issues/6451) [#7628](https://github.com/react-navigation/react-navigation-native/issues/7628) [#7749](https://github.com/react-navigation/react-navigation-native/issues/7749)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.7.7](https://github.com/react-navigation/react-navigation-native/compare/@react-navigation/native@3.7.6...@react-navigation/native@3.7.7) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-navigation/native",
|
||||
"version": "3.7.7",
|
||||
"version": "3.8.0",
|
||||
"description": "React Native support for React Navigation",
|
||||
"main": "lib/commonjs/index.js",
|
||||
"react-native": "lib/module/index.js",
|
||||
@@ -36,11 +36,11 @@
|
||||
"homepage": "https://github.com/react-navigation/react-navigation-native#readme",
|
||||
"dependencies": {
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"react-native-safe-area-view": "^0.14.8"
|
||||
"react-native-safe-area-view": "^0.14.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-navigation/core": "^3.7.1",
|
||||
"@react-navigation/core": "^3.7.6",
|
||||
"@types/react-test-renderer": "^16.9.2",
|
||||
"del-cli": "^3.0.0",
|
||||
"react": "~16.9.0",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { withNavigation } from '@react-navigation/core';
|
||||
|
||||
export default function createNavigationAwareScrollable(Component) {
|
||||
const ComponentWithNavigationScrolling = withNavigation(
|
||||
class extends React.PureComponent<any> {
|
||||
class extends React.PureComponent {
|
||||
static displayName = `withNavigationScrolling(${
|
||||
Component.displayName || Component.name
|
||||
})`;
|
||||
@@ -60,7 +60,7 @@ export default function createNavigationAwareScrollable(Component) {
|
||||
}
|
||||
);
|
||||
|
||||
class NavigationAwareScrollable extends React.PureComponent<any> {
|
||||
class NavigationAwareScrollable extends React.PureComponent {
|
||||
static displayName = `NavigationAwareScrollable(${
|
||||
Component.displayName || Component.name
|
||||
})`;
|
||||
|
||||
@@ -43,6 +43,24 @@ module.exports = {
|
||||
get createNavigationFactory() {
|
||||
return require('./throwIfWrongVersion').default;
|
||||
},
|
||||
get useNavigationBuilder() {
|
||||
return require('./throwIfWrongVersion').default;
|
||||
},
|
||||
get useNavigation() {
|
||||
return require('./throwIfWrongVersion').default;
|
||||
},
|
||||
get useRoute() {
|
||||
return require('./throwIfWrongVersion').default;
|
||||
},
|
||||
get useFocusEffect() {
|
||||
return require('./throwIfWrongVersion').default;
|
||||
},
|
||||
get useIsFocused() {
|
||||
return require('./throwIfWrongVersion').default;
|
||||
},
|
||||
get useNavigationState() {
|
||||
return require('./throwIfWrongVersion').default;
|
||||
},
|
||||
get NavigationContainer() {
|
||||
return require('./throwIfWrongVersion').default;
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default function () {
|
||||
throw new Error(
|
||||
"This version of '@react-navigation/native' is not compatible with React Navigation 5. Make sure you install 5.x version of '@react-navigation/native'."
|
||||
'Looks like you are trying to use React Navigation 5 APIs, but have React Navigation 4 installed.\n\nMixing React Navigation 4 and 5 is not supported. See the documentation for installation instructions and usage guide for appropriate versions:\n\n- React Navigation 5: https://reactnavigation.org/docs/getting-started\n- React Navigation 4: https://reactnavigation.org/docs/4.x/getting-started'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,68 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [4.4.0](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.9...react-navigation@4.4.0) (2020-06-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* sync latest stack and upgrade typescript ([454e05b](https://github.com/react-navigation/react-navigation/commit/454e05b02ec97f053b381fdc801df899d8c93cb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.9](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.8...react-navigation@4.3.9) (2020-04-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* change old docUrl to new docUrl due v5 ([e09906a](https://github.com/react-navigation/react-navigation/commit/e09906a4235a0fca09140923ebe7af34b50b491a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.8](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.7...react-navigation@4.3.8) (2020-04-22)
|
||||
|
||||
**Note:** Version bump only for package react-navigation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.7](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.6...react-navigation@4.3.7) (2020-04-02)
|
||||
|
||||
**Note:** Version bump only for package react-navigation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.6](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.5...react-navigation@4.3.6) (2020-03-31)
|
||||
|
||||
**Note:** Version bump only for package react-navigation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.5](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.3...react-navigation@4.3.5) (2020-03-28)
|
||||
|
||||
**Note:** Version bump only for package react-navigation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.4](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.3...react-navigation@4.3.4) (2020-03-27)
|
||||
|
||||
**Note:** Version bump only for package react-navigation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.3.3](https://github.com/react-navigation/react-navigation/compare/react-navigation@4.3.2...react-navigation@4.3.3) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package react-navigation
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-navigation",
|
||||
"version": "4.3.3",
|
||||
"version": "4.4.0",
|
||||
"description": "Routing and navigation for your React Native apps",
|
||||
"main": "src/index.js",
|
||||
"types": "typescript/react-navigation.d.ts",
|
||||
@@ -24,8 +24,8 @@
|
||||
"react-native": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/core": "^3.7.1",
|
||||
"@react-navigation/native": "^3.7.7"
|
||||
"@react-navigation/core": "^3.7.6",
|
||||
"@react-navigation/native": "^3.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^16.9.23",
|
||||
@@ -34,6 +34,6 @@
|
||||
"react": "~16.9.0",
|
||||
"react-native": "~0.61.5",
|
||||
"react-test-renderer": "^16.9.2",
|
||||
"typescript": "~3.7.5"
|
||||
"typescript": "^3.9.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export default function docsUrl(path) {
|
||||
return `https://reactnavigation.org/docs/${path}`;
|
||||
return `https://reactnavigation.org/docs/4.x/${path}`;
|
||||
}
|
||||
|
||||
@@ -734,7 +734,9 @@ export namespace StackActions {
|
||||
export const REPLACE: 'Navigation/REPLACE';
|
||||
export const COMPLETE_TRANSITION: 'Navigation/COMPLETE_TRANSITION';
|
||||
|
||||
export function pop(options: NavigationPopActionPayload): NavigationPopAction;
|
||||
export function pop(
|
||||
options?: NavigationPopActionPayload
|
||||
): NavigationPopAction;
|
||||
export function popToTop(
|
||||
options?: NavigationPopToTopActionPayload
|
||||
): NavigationPopToTopAction;
|
||||
@@ -810,17 +812,15 @@ export interface NavigationDescriptor<
|
||||
getComponent: () => React.ComponentType;
|
||||
}
|
||||
|
||||
export type NavigationView<
|
||||
Options,
|
||||
State,
|
||||
ScreenProps = unknown
|
||||
> = React.ComponentType<
|
||||
{
|
||||
descriptors: { [key: string]: NavigationDescriptor };
|
||||
navigationConfig: Options;
|
||||
screenProps?: ScreenProps;
|
||||
} & NavigationInjectedProps
|
||||
>;
|
||||
export type NavigationView<Options, State, ScreenProps = unknown> =
|
||||
| React.ComponentType<
|
||||
{
|
||||
descriptors: { [key: string]: NavigationDescriptor };
|
||||
navigationConfig: Options;
|
||||
screenProps?: ScreenProps;
|
||||
} & NavigationInjectedProps
|
||||
>
|
||||
| React.ComponentType<any>;
|
||||
|
||||
export type CreateNavigatorConfig<
|
||||
NavigatorConfig,
|
||||
|
||||
@@ -3,6 +3,146 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.8.0](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.7.0...react-navigation-stack@2.8.0) (2020-06-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* sync latest stack and upgrade typescript ([454e05b](https://github.com/react-navigation/react-navigation-stack/commit/454e05b02ec97f053b381fdc801df899d8c93cb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.7.0](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.6.0...react-navigation-stack@2.7.0) (2020-06-08)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* sync latest stack ([ba6d7dc](https://github.com/react-navigation/react-navigation-stack/commit/ba6d7dcbedcfca774a53de32f92acc45140bb1fe))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.6.0](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.5.1...react-navigation-stack@2.6.0) (2020-06-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* sync latest stack ([03f9f8c](https://github.com/react-navigation/react-navigation-stack/commit/03f9f8cd8ec3b5fe9dc2ed1ed2024e79dd06f854))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.5.1](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.5.0...react-navigation-stack@2.5.1) (2020-05-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* sync latest stack ([bc1313b](https://github.com/react-navigation/react-navigation-stack/commit/bc1313b61da6134255adff477ded2ed8f632bf4a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.5.0](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.4.0...react-navigation-stack@2.5.0) (2020-05-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* sync latest stack ([bb82d50](https://github.com/react-navigation/react-navigation-stack/commit/bb82d5000f6577f421cc7f72df8064eb4c2a0650))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.4.0](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.13...react-navigation-stack@2.4.0) (2020-04-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* sync latest stack ([8b79e9b](https://github.com/react-navigation/react-navigation-stack/commit/8b79e9bee65c3ba6a788ca76771379319168f117))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.13](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.12...react-navigation-stack@2.3.13) (2020-04-22)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.12](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.11...react-navigation-stack@2.3.12) (2020-04-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* stack peer warnings ([4c4e66f](https://github.com/react-navigation/react-navigation-stack/commit/4c4e66f05cd4d694f512f4cc38827f3fbf0f70de))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.11](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.10...react-navigation-stack@2.3.11) (2020-04-02)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.10](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.9...react-navigation-stack@2.3.10) (2020-03-31)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.9](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.7...react-navigation-stack@2.3.9) (2020-03-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* emit didFocus and didBlur events based on parent's transition ([14a6538](https://github.com/react-navigation/react-navigation-stack/commit/14a6538cc8e12c50d5d10722d75c9395a0a281ec))
|
||||
* rework focus and blur events to make them more reliable ([cd08338](https://github.com/react-navigation/react-navigation-stack/commit/cd083381866506a192f1ec842ac169f2b4277ca5)), closes [#4867](https://github.com/react-navigation/react-navigation-stack/issues/4867) [#6187](https://github.com/react-navigation/react-navigation-stack/issues/6187) [#6451](https://github.com/react-navigation/react-navigation-stack/issues/6451) [#7628](https://github.com/react-navigation/react-navigation-stack/issues/7628) [#7749](https://github.com/react-navigation/react-navigation-stack/issues/7749)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.8](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.7...react-navigation-stack@2.3.8) (2020-03-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* rework focus and blur events to make them more reliable ([cd08338](https://github.com/react-navigation/react-navigation-stack/commit/cd083381866506a192f1ec842ac169f2b4277ca5)), closes [#4867](https://github.com/react-navigation/react-navigation-stack/issues/4867) [#6187](https://github.com/react-navigation/react-navigation-stack/issues/6187) [#6451](https://github.com/react-navigation/react-navigation-stack/issues/6451) [#7628](https://github.com/react-navigation/react-navigation-stack/issues/7628) [#7749](https://github.com/react-navigation/react-navigation-stack/issues/7749)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.7](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.6...react-navigation-stack@2.3.7) (2020-03-27)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.6](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.5...react-navigation-stack@2.3.6) (2020-03-23)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.5](https://github.com/react-navigation/react-navigation-stack/compare/react-navigation-stack@2.3.4...react-navigation-stack@2.3.5) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-stack
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-navigation-stack",
|
||||
"version": "2.3.5",
|
||||
"version": "2.8.0",
|
||||
"description": "Stack navigator component for React Navigation",
|
||||
"main": "lib/commonjs/index.js",
|
||||
"module": "lib/module/index.js",
|
||||
@@ -45,7 +45,7 @@
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.10.0",
|
||||
"@react-native-community/masked-view": "^0.1.7",
|
||||
"@react-navigation/stack": "^5.2.4",
|
||||
"@react-navigation/stack": "^5.6.0",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-native": "^0.61.22",
|
||||
@@ -56,16 +56,16 @@
|
||||
"react-native-gesture-handler": "^1.6.0",
|
||||
"react-native-safe-area-context": "^0.7.3",
|
||||
"react-native-screens": "^2.3.0",
|
||||
"react-navigation": "^4.3.3",
|
||||
"react-navigation": "^4.4.0",
|
||||
"react-test-renderer": "~16.9.0",
|
||||
"typescript": "~3.7.5"
|
||||
"typescript": "^3.9.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-native-community/masked-view": "^0.1.1",
|
||||
"@react-native-community/masked-view": ">=0.1.0",
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": "^1.5.0",
|
||||
"react-native-safe-area-context": "^0.6.0",
|
||||
"react-native-gesture-handler": ">= 1.5.0",
|
||||
"react-native-safe-area-context": ">= 0.6.0",
|
||||
"react-native-screens": ">=1.0.0 || >= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0",
|
||||
"react-navigation": "^4.1.1"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/index.tsx src/vendor/index.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/index.tsx 2020-03-23 00:04:17.000000000 +0100
|
||||
+++ src/vendor/index.tsx 2020-03-23 00:04:37.000000000 +0100
|
||||
--- ../../node_modules/@react-navigation/stack/src/index.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/index.tsx 2020-06-24 23:16:17.000000000 +0200
|
||||
@@ -3,11 +3,6 @@
|
||||
import * as TransitionSpecs from './TransitionConfigs/TransitionSpecs';
|
||||
import * as TransitionPresets from './TransitionConfigs/TransitionPresets';
|
||||
@@ -16,22 +16,23 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/index.tsx src/vendor/i
|
||||
@@ -49,9 +44,10 @@
|
||||
* Types
|
||||
*/
|
||||
export {
|
||||
export type {
|
||||
- StackNavigationOptions,
|
||||
- StackNavigationProp,
|
||||
- StackHeaderProps,
|
||||
- StackScreenProps,
|
||||
+ NavigationStackState,
|
||||
+ StackNavigationProp as NavigationStackProp,
|
||||
+ StackNavigationOptions as NavigationStackOptions,
|
||||
+ StackNavigationConfig as NavigationStackConfig,
|
||||
StackHeaderProps,
|
||||
StackHeaderLeftButtonProps,
|
||||
StackHeaderTitleProps,
|
||||
StackCardInterpolatedStyle,
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStackNavigator.tsx src/vendor/navigators/createStackNavigator.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/navigators/createStackNavigator.tsx 2020-03-23 00:04:17.000000000 +0100
|
||||
--- ../../node_modules/@react-navigation/stack/src/navigators/createStackNavigator.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/navigators/createStackNavigator.tsx 1970-01-01 01:00:00.000000000 +0100
|
||||
@@ -1,81 +0,0 @@
|
||||
@@ -1,96 +0,0 @@
|
||||
-import * as React from 'react';
|
||||
-import { Platform } from 'react-native';
|
||||
-import {
|
||||
- useNavigationBuilder,
|
||||
- createNavigatorFactory,
|
||||
@@ -43,7 +44,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStack
|
||||
- StackActions,
|
||||
-} from '@react-navigation/native';
|
||||
-import StackView from '../views/Stack/StackView';
|
||||
-import {
|
||||
-import type {
|
||||
- StackNavigationConfig,
|
||||
- StackNavigationOptions,
|
||||
- StackNavigationEventMap,
|
||||
@@ -59,6 +60,11 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStack
|
||||
- screenOptions,
|
||||
- ...rest
|
||||
-}: Props) {
|
||||
- const defaultOptions = {
|
||||
- gestureEnabled: Platform.OS === 'ios',
|
||||
- animationEnabled: Platform.OS !== 'web',
|
||||
- };
|
||||
-
|
||||
- const { state, descriptors, navigation } = useNavigationBuilder<
|
||||
- StackNavigationState,
|
||||
- StackRouterOptions,
|
||||
@@ -67,7 +73,16 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStack
|
||||
- >(StackRouter, {
|
||||
- initialRouteName,
|
||||
- children,
|
||||
- screenOptions,
|
||||
- screenOptions:
|
||||
- typeof screenOptions === 'function'
|
||||
- ? (...args) => ({
|
||||
- ...defaultOptions,
|
||||
- ...screenOptions(...args),
|
||||
- })
|
||||
- : {
|
||||
- ...defaultOptions,
|
||||
- ...screenOptions,
|
||||
- },
|
||||
- });
|
||||
-
|
||||
- React.useEffect(
|
||||
@@ -113,12 +128,12 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStack
|
||||
- typeof StackNavigator
|
||||
->(StackNavigator);
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/types.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/types.tsx 2020-03-23 00:04:17.000000000 +0100
|
||||
+++ src/vendor/types.tsx 2020-03-23 00:04:40.000000000 +0100
|
||||
@@ -8,14 +8,28 @@
|
||||
--- ../../node_modules/@react-navigation/stack/src/types.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/types.tsx 2020-06-24 23:18:40.000000000 +0200
|
||||
@@ -8,15 +8,28 @@
|
||||
} from 'react-native';
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import {
|
||||
import type { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import type {
|
||||
+ NavigationRoute,
|
||||
+ NavigationState,
|
||||
+ NavigationScreenProp,
|
||||
@@ -129,6 +144,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
|
||||
- NavigationHelpers,
|
||||
- StackNavigationState,
|
||||
- StackActionHelpers,
|
||||
- RouteProp,
|
||||
-} from '@react-navigation/native';
|
||||
+ NavigationParams,
|
||||
+ NavigationNavigateAction,
|
||||
@@ -151,7 +167,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
|
||||
|
||||
export type StackNavigationEventMap = {
|
||||
/**
|
||||
@@ -28,22 +42,29 @@
|
||||
@@ -29,29 +42,28 @@
|
||||
transitionEnd: { data: { closing: boolean } };
|
||||
};
|
||||
|
||||
@@ -172,6 +188,13 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
|
||||
- StackNavigationEventMap
|
||||
-> &
|
||||
- StackActionHelpers<ParamList>;
|
||||
-
|
||||
-export type StackScreenProps<
|
||||
- ParamList extends ParamListBase,
|
||||
- RouteName extends keyof ParamList = string
|
||||
-> = {
|
||||
- navigation: StackNavigationProp<ParamList, RouteName>;
|
||||
- route: RouteProp<ParamList, RouteName>;
|
||||
+ State = NavigationRoute,
|
||||
+ Params = NavigationParams
|
||||
+> = NavigationScreenProp<State, Params> & {
|
||||
@@ -192,11 +215,10 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
|
||||
+ event: NavigationStackEventName,
|
||||
+ callback: NavigationEventCallback
|
||||
+ ) => NavigationEventSubscription;
|
||||
+};
|
||||
};
|
||||
|
||||
export type Layout = { width: number; height: number };
|
||||
|
||||
@@ -218,24 +239,27 @@
|
||||
@@ -228,24 +240,27 @@
|
||||
/**
|
||||
* Navigation prop for the header.
|
||||
*/
|
||||
@@ -230,7 +252,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
|
||||
export type StackNavigationOptions = StackHeaderOptions &
|
||||
Partial<TransitionPreset> & {
|
||||
/**
|
||||
@@ -317,6 +341,8 @@
|
||||
@@ -330,6 +345,8 @@
|
||||
bottom?: number;
|
||||
left?: number;
|
||||
};
|
||||
@@ -239,28 +261,40 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t
|
||||
};
|
||||
|
||||
export type StackNavigationConfig = {
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/utils/PreviousSceneContext.tsx src/vendor/utils/PreviousSceneContext.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/utils/PreviousSceneContext.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/utils/PreviousSceneContext.tsx 2020-06-24 23:26:38.000000000 +0200
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
-import type { Route } from '@react-navigation/native';
|
||||
-import type { Scene } from '../types';
|
||||
+import type { Route, Scene } from '../types';
|
||||
|
||||
const PreviousSceneContext = React.createContext<
|
||||
Scene<Route<string>> | undefined
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.tsx src/vendor/views/Header/Header.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Header/Header.tsx 2020-03-23 00:04:17.000000000 +0100
|
||||
+++ src/vendor/views/Header/Header.tsx 2020-03-23 00:04:40.000000000 +0100
|
||||
@@ -1,12 +1,14 @@
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Header/Header.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/views/Header/Header.tsx 2020-06-24 23:33:25.000000000 +0200
|
||||
@@ -1,12 +1,15 @@
|
||||
import * as React from 'react';
|
||||
-import { StackActions } from '@react-navigation/native';
|
||||
+import { Dimensions } from 'react-native';
|
||||
+import { StackActions } from 'react-navigation';
|
||||
+import { getStatusBarHeight } from 'react-native-iphone-x-helper';
|
||||
+
|
||||
+import HeaderSegment, { getDefaultHeaderHeight } from './HeaderSegment';
|
||||
|
||||
-import HeaderSegment from './HeaderSegment';
|
||||
+import HeaderSegment, { getDefaultHeaderHeight } from './HeaderSegment';
|
||||
import HeaderTitle from './HeaderTitle';
|
||||
import debounce from '../../utils/debounce';
|
||||
import { StackHeaderProps, StackHeaderTitleProps } from '../../types';
|
||||
import type { StackHeaderProps, StackHeaderTitleProps } from '../../types';
|
||||
|
||||
-export default React.memo(function Header(props: StackHeaderProps) {
|
||||
+const Header = React.memo(function Header(props: StackHeaderProps) {
|
||||
const {
|
||||
scene,
|
||||
previous,
|
||||
@@ -22,7 +24,7 @@
|
||||
@@ -22,7 +25,7 @@
|
||||
? options.headerTitle
|
||||
: options.title !== undefined
|
||||
? options.title
|
||||
@@ -269,7 +303,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
|
||||
|
||||
let leftLabel;
|
||||
|
||||
@@ -38,17 +40,12 @@
|
||||
@@ -38,17 +41,18 @@
|
||||
? o.headerTitle
|
||||
: o.title !== undefined
|
||||
? o.title
|
||||
@@ -277,6 +311,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
|
||||
+ : previous.route.routeName;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const goBack = React.useCallback(
|
||||
debounce(() => {
|
||||
- if (navigation.isFocused() && navigation.canGoBack()) {
|
||||
@@ -284,12 +319,16 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
|
||||
- ...StackActions.pop(),
|
||||
- source: scene.route.key,
|
||||
- });
|
||||
- }
|
||||
+ navigation.dispatch(StackActions.pop({ key: scene.route.key }));
|
||||
+ if (navigation.isFirstRouteInParent()) {
|
||||
+ // If we're the first route, we're going back to a parent navigator
|
||||
+ // So we can't specify a key here
|
||||
+ navigation.dispatch(StackActions.pop());
|
||||
+ } else {
|
||||
+ navigation.dispatch(StackActions.pop({ key: scene.route.key }));
|
||||
}
|
||||
}, 50),
|
||||
[navigation, scene.route.key]
|
||||
);
|
||||
@@ -63,7 +60,10 @@
|
||||
@@ -64,7 +68,10 @@
|
||||
leftLabel={leftLabel}
|
||||
headerTitle={
|
||||
typeof options.headerTitle !== 'function'
|
||||
@@ -301,7 +340,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
|
||||
: options.headerTitle
|
||||
}
|
||||
onGoBack={previous ? goBack : undefined}
|
||||
@@ -71,3 +71,18 @@
|
||||
@@ -72,3 +79,18 @@
|
||||
/>
|
||||
);
|
||||
});
|
||||
@@ -321,33 +360,34 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts
|
||||
+
|
||||
+export default Header;
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackButton.tsx src/vendor/views/Header/HeaderBackButton.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackButton.tsx 2020-03-23 00:04:17.000000000 +0100
|
||||
+++ src/vendor/views/Header/HeaderBackButton.tsx 2020-03-23 00:04:40.000000000 +0100
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackButton.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/views/Header/HeaderBackButton.tsx 2020-06-24 23:21:49.000000000 +0200
|
||||
@@ -8,9 +8,9 @@
|
||||
StyleSheet,
|
||||
LayoutChangeEvent,
|
||||
} from 'react-native';
|
||||
-import { useTheme } from '@react-navigation/native';
|
||||
import MaskedView from '../MaskedView';
|
||||
import TouchableItem from '../TouchableItem';
|
||||
import { TouchableItem } from '../TouchableItem';
|
||||
+import useTheme from '../../../utils/useTheme';
|
||||
import { StackHeaderLeftButtonProps } from '../../types';
|
||||
import type { StackHeaderLeftButtonProps } from '../../types';
|
||||
|
||||
type Props = StackHeaderLeftButtonProps;
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackground.tsx src/vendor/views/Header/HeaderBackground.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackground.tsx 2020-03-23 00:04:17.000000000 +0100
|
||||
+++ src/vendor/views/Header/HeaderBackground.tsx 2020-03-23 00:04:40.000000000 +0100
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { Animated, StyleSheet, Platform, ViewProps } from 'react-native';
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackground.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/views/Header/HeaderBackground.tsx 2020-06-24 23:16:23.000000000 +0200
|
||||
@@ -7,7 +7,7 @@
|
||||
StyleProp,
|
||||
ViewStyle,
|
||||
} from 'react-native';
|
||||
-import { useTheme } from '@react-navigation/native';
|
||||
+import useTheme from '../../../utils/useTheme';
|
||||
|
||||
type Props = ViewProps & {
|
||||
children?: React.ReactNode;
|
||||
style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx src/vendor/views/Header/HeaderContainer.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx 2020-03-23 00:04:17.000000000 +0100
|
||||
+++ src/vendor/views/Header/HeaderContainer.tsx 2020-03-23 00:04:40.000000000 +0100
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/views/Header/HeaderContainer.tsx 2020-06-24 23:21:29.000000000 +0200
|
||||
@@ -1,11 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
||||
@@ -358,18 +398,18 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderCon
|
||||
- ParamListBase,
|
||||
-} from '@react-navigation/native';
|
||||
+import { NavigationContext } from 'react-navigation';
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import type { EdgeInsets } from 'react-native-safe-area-context';
|
||||
|
||||
import Header from './Header';
|
||||
@@ -16,6 +11,7 @@
|
||||
forSlideRight,
|
||||
} from '../../TransitionConfigs/HeaderStyleInterpolators';
|
||||
import {
|
||||
+ Route,
|
||||
@@ -19,6 +14,7 @@
|
||||
import PreviousSceneContext from '../../utils/PreviousSceneContext';
|
||||
import type {
|
||||
Layout,
|
||||
+ Route,
|
||||
Scene,
|
||||
StackHeaderStyleInterpolator,
|
||||
@@ -99,9 +95,7 @@
|
||||
StackNavigationProp,
|
||||
@@ -109,9 +105,7 @@
|
||||
insets,
|
||||
scene,
|
||||
previous,
|
||||
@@ -380,7 +420,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderCon
|
||||
styleInterpolator:
|
||||
mode === 'float'
|
||||
? isHeaderStatic
|
||||
@@ -120,7 +114,7 @@
|
||||
@@ -130,7 +124,7 @@
|
||||
key={scene.route.key}
|
||||
value={scene.descriptor.navigation}
|
||||
>
|
||||
@@ -389,9 +429,9 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderCon
|
||||
<View
|
||||
onLayout={
|
||||
onContentHeightChange
|
||||
@@ -152,7 +146,7 @@
|
||||
)
|
||||
) : null}
|
||||
@@ -156,7 +150,7 @@
|
||||
>
|
||||
{header !== undefined ? header(props) : <Header {...props} />}
|
||||
</View>
|
||||
- </NavigationRouteContext.Provider>
|
||||
+ </>
|
||||
@@ -399,14 +439,14 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderCon
|
||||
);
|
||||
})}
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSegment.tsx src/vendor/views/Header/HeaderSegment.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSegment.tsx 2020-03-23 00:04:17.000000000 +0100
|
||||
+++ src/vendor/views/Header/HeaderSegment.tsx 2020-03-23 00:04:40.000000000 +0100
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSegment.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/views/Header/HeaderSegment.tsx 2020-06-24 23:20:58.000000000 +0200
|
||||
@@ -8,7 +8,7 @@
|
||||
ViewStyle,
|
||||
} from 'react-native';
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
-import { Route } from '@react-navigation/native';
|
||||
+import { NavigationRoute } from 'react-navigation';
|
||||
import type { EdgeInsets } from 'react-native-safe-area-context';
|
||||
-import type { Route } from '@react-navigation/native';
|
||||
+import type { NavigationRoute } from 'react-navigation';
|
||||
import HeaderBackButton from './HeaderBackButton';
|
||||
import HeaderBackground from './HeaderBackground';
|
||||
import memoize from '../../utils/memoize';
|
||||
@@ -420,20 +460,20 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSeg
|
||||
};
|
||||
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderTitle.tsx src/vendor/views/Header/HeaderTitle.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderTitle.tsx 2020-03-23 00:04:17.000000000 +0100
|
||||
+++ src/vendor/views/Header/HeaderTitle.tsx 2020-03-23 00:04:40.000000000 +0100
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderTitle.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/views/Header/HeaderTitle.tsx 2020-06-24 23:16:23.000000000 +0200
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { Animated, StyleSheet, Platform } from 'react-native';
|
||||
-import { useTheme } from '@react-navigation/native';
|
||||
+import useTheme from '../../../utils/useTheme';
|
||||
|
||||
type Props = React.ComponentProps<typeof Animated.Text> & {
|
||||
type Props = Omit<React.ComponentProps<typeof Animated.Text>, 'key'> & {
|
||||
tintColor?: string;
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx src/vendor/views/Stack/Card.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx 2020-03-23 00:04:17.000000000 +0100
|
||||
+++ src/vendor/views/Stack/Card.tsx 2020-03-23 00:04:40.000000000 +0100
|
||||
@@ -138,7 +138,7 @@
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/views/Stack/Card.tsx 2020-06-24 23:16:23.000000000 +0200
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
private interactionHandle: number | undefined;
|
||||
|
||||
@@ -443,133 +483,71 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx s
|
||||
private animate = ({
|
||||
closing,
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardContainer.tsx src/vendor/views/Stack/CardContainer.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardContainer.tsx 2020-03-23 00:04:17.000000000 +0100
|
||||
+++ src/vendor/views/Stack/CardContainer.tsx 2020-03-23 00:04:40.000000000 +0100
|
||||
@@ -1,10 +1,16 @@
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardContainer.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/views/Stack/CardContainer.tsx 2020-06-24 23:20:38.000000000 +0200
|
||||
@@ -1,12 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import { Animated, View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
||||
-import { Route, useTheme } from '@react-navigation/native';
|
||||
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
||||
import type { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
||||
import Card from './Card';
|
||||
import HeaderHeightContext from '../../utils/HeaderHeightContext';
|
||||
-import { Scene, Layout, StackHeaderMode, TransitionPreset } from '../../types';
|
||||
import HeaderShownContext from '../../utils/HeaderShownContext';
|
||||
import PreviousSceneContext from '../../utils/PreviousSceneContext';
|
||||
+import useTheme from '../../../utils/useTheme';
|
||||
+import {
|
||||
import type {
|
||||
+ Route,
|
||||
+ Scene,
|
||||
+ Layout,
|
||||
+ StackHeaderMode,
|
||||
+ TransitionPreset,
|
||||
+} from '../../types';
|
||||
|
||||
type Props = TransitionPreset & {
|
||||
index: number;
|
||||
@@ -36,6 +42,7 @@
|
||||
closing: boolean
|
||||
) => void;
|
||||
onTransitionEnd?: (props: { route: Route<string> }, closing: boolean) => void;
|
||||
+ onTransitionComplete: (props: { route: Route<string> }) => void;
|
||||
onPageChangeStart?: () => void;
|
||||
onPageChangeConfirm?: () => void;
|
||||
onPageChangeCancel?: () => void;
|
||||
@@ -83,6 +90,7 @@
|
||||
layout,
|
||||
onCloseRoute,
|
||||
onOpenRoute,
|
||||
+ onTransitionComplete,
|
||||
onPageChangeCancel,
|
||||
onPageChangeConfirm,
|
||||
onPageChangeStart,
|
||||
@@ -152,6 +160,9 @@
|
||||
};
|
||||
}, [pointerEvents, scene.progress.next]);
|
||||
|
||||
+ // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
+ React.useEffect(() => onTransitionComplete({ route: scene.route }), []);
|
||||
+
|
||||
return (
|
||||
<Card
|
||||
index={index}
|
||||
@@ -183,7 +194,7 @@
|
||||
? { marginTop: headerHeight }
|
||||
: null
|
||||
}
|
||||
- contentStyle={[{ backgroundColor: colors.background }, cardStyle]}
|
||||
+ contentStyle={[{ backgroundColor: colors.background }, cardStyle] as any}
|
||||
style={StyleSheet.absoluteFill}
|
||||
>
|
||||
<View style={styles.container}>
|
||||
Scene,
|
||||
Layout,
|
||||
StackHeaderMode,
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.tsx src/vendor/views/Stack/CardStack.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.tsx 2020-03-23 00:04:17.000000000 +0100
|
||||
+++ src/vendor/views/Stack/CardStack.tsx 2020-03-23 00:04:40.000000000 +0100
|
||||
@@ -9,9 +9,8 @@
|
||||
ViewProps,
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/views/Stack/CardStack.tsx 2020-06-24 23:20:16.000000000 +0200
|
||||
@@ -7,7 +7,7 @@
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
-// eslint-disable-next-line import/no-unresolved
|
||||
import { ScreenContainer, Screen, screensEnabled } from 'react-native-screens'; // Import with * as to prevent getters being called
|
||||
-import { Route, StackNavigationState } from '@react-navigation/native';
|
||||
+import { NavigationState as StackNavigationState } from 'react-navigation';
|
||||
import type { EdgeInsets } from 'react-native-safe-area-context';
|
||||
-import type { Route, StackNavigationState } from '@react-navigation/native';
|
||||
+import type { NavigationState as StackNavigationState } from 'react-navigation';
|
||||
|
||||
import { MaybeScreenContainer, MaybeScreen } from '../Screens';
|
||||
import { getDefaultHeaderHeight } from '../Header/HeaderSegment';
|
||||
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
||||
@@ -24,6 +23,7 @@
|
||||
import { forNoAnimation as forNoAnimationCard } from '../../TransitionConfigs/CardStyleInterpolators';
|
||||
import getDistanceForDirection from '../../utils/getDistanceForDirection';
|
||||
import {
|
||||
+ Route,
|
||||
@@ -25,6 +25,7 @@
|
||||
Layout,
|
||||
StackHeaderMode,
|
||||
StackCardMode,
|
||||
@@ -54,6 +54,7 @@
|
||||
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
|
||||
renderScene: (props: { route: Route<string> }) => React.ReactNode;
|
||||
headerMode: StackHeaderMode;
|
||||
+ onTransitionComplete: (props: { route: Route<string> }) => void;
|
||||
onTransitionStart: (
|
||||
props: { route: Route<string> },
|
||||
closing: boolean
|
||||
@@ -383,6 +384,7 @@
|
||||
renderHeader,
|
||||
renderScene,
|
||||
headerMode,
|
||||
+ onTransitionComplete,
|
||||
onTransitionStart,
|
||||
onTransitionEnd,
|
||||
onPageChangeStart,
|
||||
@@ -560,6 +562,7 @@
|
||||
renderScene={renderScene}
|
||||
onOpenRoute={onOpenRoute}
|
||||
onCloseRoute={onCloseRoute}
|
||||
+ onTransitionComplete={onTransitionComplete}
|
||||
onTransitionStart={onTransitionStart}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
gestureEnabled={index !== 0 && getGesturesEnabled({ route })}
|
||||
+ Route,
|
||||
Scene,
|
||||
StackDescriptorMap,
|
||||
StackNavigationOptions,
|
||||
diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.tsx src/vendor/views/Stack/StackView.tsx
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.tsx 2020-03-23 00:04:17.000000000 +0100
|
||||
+++ src/vendor/views/Stack/StackView.tsx 2020-03-23 00:07:11.000000000 +0100
|
||||
@@ -4,9 +4,9 @@
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
--- ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.tsx 2020-06-24 23:11:52.000000000 +0200
|
||||
+++ src/vendor/views/Stack/StackView.tsx 2020-06-24 23:19:46.000000000 +0200
|
||||
@@ -2,11 +2,11 @@
|
||||
import { View, Platform, StyleSheet } from 'react-native';
|
||||
import { SafeAreaConsumer, EdgeInsets } from 'react-native-safe-area-context';
|
||||
import {
|
||||
- NavigationHelpersContext,
|
||||
StackActions,
|
||||
- StackNavigationState,
|
||||
- Route,
|
||||
-} from '@react-navigation/native';
|
||||
+ NavigationState as StackNavigationState,
|
||||
+ NavigationActions,
|
||||
+ SceneView,
|
||||
+} from 'react-navigation';
|
||||
|
||||
import { GestureHandlerRootView } from '../GestureHandler';
|
||||
import CardStack from './CardStack';
|
||||
import KeyboardManager from '../KeyboardManager';
|
||||
@@ -15,6 +15,7 @@
|
||||
@@ -16,6 +16,7 @@
|
||||
} from '../Header/HeaderContainer';
|
||||
import SafeAreaProviderCompat from '../SafeAreaProviderCompat';
|
||||
import {
|
||||
import type {
|
||||
+ Route,
|
||||
StackNavigationHelpers,
|
||||
StackNavigationConfig,
|
||||
StackDescriptorMap,
|
||||
@@ -24,6 +25,7 @@
|
||||
@@ -25,6 +26,7 @@
|
||||
state: StackNavigationState;
|
||||
navigation: StackNavigationHelpers;
|
||||
descriptors: StackDescriptorMap;
|
||||
@@ -577,7 +555,18 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.
|
||||
};
|
||||
|
||||
type State = {
|
||||
@@ -290,14 +292,37 @@
|
||||
@@ -293,7 +295,9 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
- return gestureEnabled !== false;
|
||||
+ return gestureEnabled !== undefined
|
||||
+ ? gestureEnabled
|
||||
+ : Platform.OS !== 'android';
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -321,26 +325,49 @@
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -598,25 +587,39 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.
|
||||
return <HeaderContainer {...props} />;
|
||||
};
|
||||
|
||||
+ private handleTransitionComplete = ({ route }: { route: Route<string> }) => {
|
||||
+ private handleTransitionComplete = () => {
|
||||
+ const { state, navigation } = this.props;
|
||||
+
|
||||
+ if (state.isTransitioning) {
|
||||
+ navigation.dispatch(
|
||||
+ StackActions.completeTransition({
|
||||
+ key: navigation.state.key,
|
||||
+ toChildKey: route.key,
|
||||
+ toChildKey: state.routes[state.index].key,
|
||||
+ })
|
||||
+ );
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
private handleOpenRoute = ({ route }: { route: Route<string> }) => {
|
||||
+ this.handleTransitionComplete({ route });
|
||||
this.setState((state) => ({
|
||||
routes: state.replacingRouteKeys.length
|
||||
? state.routes.filter((r) => !state.replacingRouteKeys.includes(r.key))
|
||||
@@ -319,12 +344,17 @@
|
||||
const { state, navigation } = this.props;
|
||||
const { closingRouteKeys, replacingRouteKeys } = this.state;
|
||||
|
||||
+ this.handleTransitionComplete();
|
||||
+
|
||||
if (
|
||||
closingRouteKeys.some((key) => key === route.key) &&
|
||||
replacingRouteKeys.every((key) => key !== route.key) &&
|
||||
- state.routeNames.includes(route.name) &&
|
||||
!state.routes.some((r) => r.key === route.key)
|
||||
) {
|
||||
// If route isn't present in current state, but was closing, assume that a close animation was cancelled
|
||||
// So we need to add this route back to the state
|
||||
- navigation.navigate(route);
|
||||
+ navigation.dispatch(NavigationActions.navigate(route));
|
||||
} else {
|
||||
this.setState((state) => ({
|
||||
routes: state.replacingRouteKeys.length
|
||||
@@ -366,12 +393,11 @@
|
||||
// If a route exists in state, trigger a pop
|
||||
// This will happen in when the route was closed from the card component
|
||||
// e.g. When the close animation triggered from a gesture ends
|
||||
@@ -628,18 +631,12 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.
|
||||
+ // @ts-ignore
|
||||
+ navigation.dispatch(StackActions.pop({ key: route.key, prune: false }));
|
||||
} else {
|
||||
+ // While closing route we need to point to the previous one assuming that
|
||||
+ // this previous one in routes array
|
||||
+ const index = this.state.routes.findIndex((r) => r.key === route.key);
|
||||
+
|
||||
+ this.handleTransitionComplete({
|
||||
+ route: this.state.routes[Math.max(index - 1, 0)],
|
||||
+ });
|
||||
+ this.handleTransitionComplete();
|
||||
+
|
||||
// We need to clean up any state tracking the route and pop it immediately
|
||||
this.setState((state) => ({
|
||||
routes: state.routes.filter((r) => r.key !== route.key),
|
||||
@@ -341,22 +371,24 @@
|
||||
@@ -388,26 +414,29 @@
|
||||
private handleTransitionStart = (
|
||||
{ route }: { route: Route<string> },
|
||||
closing: boolean
|
||||
@@ -676,11 +673,26 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.
|
||||
|
||||
render() {
|
||||
const {
|
||||
@@ -395,6 +427,7 @@
|
||||
closingRouteKeys={closingRouteKeys}
|
||||
onOpenRoute={this.handleOpenRoute}
|
||||
onCloseRoute={this.handleCloseRoute}
|
||||
+ onTransitionComplete={this.handleTransitionComplete}
|
||||
onTransitionStart={this.handleTransitionStart}
|
||||
onTransitionEnd={this.handleTransitionEnd}
|
||||
renderHeader={this.renderHeader}
|
||||
state,
|
||||
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
navigation,
|
||||
keyboardHandlingEnabled,
|
||||
mode = 'card',
|
||||
@@ -427,7 +456,7 @@
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
- <NavigationHelpersContext.Provider value={navigation}>
|
||||
+ <>
|
||||
<GestureHandlerWrapper style={styles.container}>
|
||||
<SafeAreaProviderCompat>
|
||||
<SafeAreaConsumer>
|
||||
@@ -460,7 +489,7 @@
|
||||
</SafeAreaConsumer>
|
||||
</SafeAreaProviderCompat>
|
||||
</GestureHandlerWrapper>
|
||||
- </NavigationHelpersContext.Provider>
|
||||
+ </>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export { default as createStackNavigator } from './navigators/createStackNavigat
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
export {
|
||||
export type {
|
||||
NavigationStackScreenComponent,
|
||||
NavigationStackScreenProps,
|
||||
} from './types';
|
||||
|
||||
@@ -9,7 +9,7 @@ import createAppContainer, {
|
||||
_TESTING_ONLY_reset_container_count,
|
||||
// @ts-ignore
|
||||
} from '../../../../native/src/createAppContainer';
|
||||
import { StackNavigationProp } from '../../vendor/types';
|
||||
import type { StackNavigationProp } from '../../vendor/types';
|
||||
|
||||
const NavigationTestUtils = {
|
||||
resetInternalState: _TESTING_ONLY_reset_container_count,
|
||||
|
||||
@@ -8,12 +8,119 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"zIndex": 1,
|
||||
},
|
||||
false,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
accessibilityElementsHidden={false}
|
||||
importantForAccessibility="auto"
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"opacity": 1,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
"zIndex": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#fff",
|
||||
"borderBottomColor": "#a7a7aa",
|
||||
"flex": 1,
|
||||
"shadowColor": "#a7a7aa",
|
||||
"shadowOffset": Object {
|
||||
"height": 0.5,
|
||||
"width": 0,
|
||||
},
|
||||
"shadowOpacity": 0.85,
|
||||
"shadowRadius": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"height": 44,
|
||||
"maxHeight": undefined,
|
||||
"minHeight": undefined,
|
||||
"opacity": undefined,
|
||||
"transform": undefined,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={
|
||||
Object {
|
||||
"height": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"justifyContent": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"marginHorizontal": 16,
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
accessibilityRole="header"
|
||||
numberOfLines={1}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(0, 0, 0, 0.9)",
|
||||
"fontSize": 17,
|
||||
"fontWeight": "600",
|
||||
}
|
||||
}
|
||||
>
|
||||
Sub
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"overflow": "hidden",
|
||||
}
|
||||
}
|
||||
>
|
||||
@@ -29,6 +136,14 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={false}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
accessibilityElementsHidden={false}
|
||||
closing={false}
|
||||
@@ -57,8 +172,8 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
"damping": 500,
|
||||
"mass": 3,
|
||||
"overshootClamping": true,
|
||||
"restDisplacementThreshold": 0.01,
|
||||
"restSpeedThreshold": 0.01,
|
||||
"restDisplacementThreshold": 10,
|
||||
"restSpeedThreshold": 10,
|
||||
"stiffness": 1000,
|
||||
},
|
||||
},
|
||||
@@ -68,8 +183,8 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
"damping": 500,
|
||||
"mass": 3,
|
||||
"overshootClamping": true,
|
||||
"restDisplacementThreshold": 0.01,
|
||||
"restSpeedThreshold": 0.01,
|
||||
"restDisplacementThreshold": 10,
|
||||
"restSpeedThreshold": 10,
|
||||
"stiffness": 1000,
|
||||
},
|
||||
},
|
||||
@@ -81,7 +196,6 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"marginTop": 44,
|
||||
}
|
||||
}
|
||||
>
|
||||
@@ -115,7 +229,7 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
pointerEvents="none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#fff",
|
||||
"backgroundColor": "rgb(242, 242, 242)",
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
@@ -174,7 +288,6 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"overflow": "hidden",
|
||||
}
|
||||
}
|
||||
>
|
||||
@@ -190,6 +303,14 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={false}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
accessibilityElementsHidden={false}
|
||||
closing={false}
|
||||
@@ -218,8 +339,8 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
"damping": 500,
|
||||
"mass": 3,
|
||||
"overshootClamping": true,
|
||||
"restDisplacementThreshold": 0.01,
|
||||
"restSpeedThreshold": 0.01,
|
||||
"restDisplacementThreshold": 10,
|
||||
"restSpeedThreshold": 10,
|
||||
"stiffness": 1000,
|
||||
},
|
||||
},
|
||||
@@ -229,8 +350,8 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
"damping": 500,
|
||||
"mass": 3,
|
||||
"overshootClamping": true,
|
||||
"restDisplacementThreshold": 0.01,
|
||||
"restSpeedThreshold": 0.01,
|
||||
"restDisplacementThreshold": 10,
|
||||
"restSpeedThreshold": 10,
|
||||
"stiffness": 1000,
|
||||
},
|
||||
},
|
||||
@@ -242,7 +363,7 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"marginTop": 44,
|
||||
"marginTop": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
@@ -276,7 +397,7 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
pointerEvents="none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#fff",
|
||||
"backgroundColor": "rgb(242, 242, 242)",
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
@@ -334,110 +455,19 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"zIndex": 1,
|
||||
},
|
||||
Object {
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
accessibilityElementsHidden={false}
|
||||
importantForAccessibility="auto"
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"opacity": 1,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#fff",
|
||||
"borderBottomColor": "#a7a7aa",
|
||||
"flex": 1,
|
||||
"shadowColor": "#a7a7aa",
|
||||
"shadowOffset": Object {
|
||||
"height": 0.5,
|
||||
"width": 0,
|
||||
},
|
||||
"shadowOpacity": 0.85,
|
||||
"shadowRadius": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"height": 44,
|
||||
"maxHeight": undefined,
|
||||
"minHeight": undefined,
|
||||
"opacity": undefined,
|
||||
"transform": undefined,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={
|
||||
Object {
|
||||
"height": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"justifyContent": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"marginHorizontal": 18,
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
accessibilityRole="header"
|
||||
numberOfLines={1}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(0, 0, 0, 0.9)",
|
||||
"fontSize": 17,
|
||||
"fontWeight": "600",
|
||||
}
|
||||
}
|
||||
>
|
||||
Home
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -448,113 +478,6 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
accessibilityElementsHidden={false}
|
||||
importantForAccessibility="auto"
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"opacity": 1,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#fff",
|
||||
"borderBottomColor": "#a7a7aa",
|
||||
"flex": 1,
|
||||
"shadowColor": "#a7a7aa",
|
||||
"shadowOffset": Object {
|
||||
"height": 0.5,
|
||||
"width": 0,
|
||||
},
|
||||
"shadowOpacity": 0.85,
|
||||
"shadowRadius": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"height": 44,
|
||||
"maxHeight": undefined,
|
||||
"minHeight": undefined,
|
||||
"opacity": undefined,
|
||||
"transform": undefined,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={
|
||||
Object {
|
||||
"height": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"justifyContent": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"marginHorizontal": 18,
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
accessibilityRole="header"
|
||||
numberOfLines={1}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(0, 0, 0, 0.9)",
|
||||
"fontSize": 17,
|
||||
"fontWeight": "600",
|
||||
}
|
||||
}
|
||||
>
|
||||
Sub
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
`;
|
||||
|
||||
|
||||
@@ -8,177 +8,15 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"overflow": "hidden",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
accessibilityElementsHidden={false}
|
||||
closing={false}
|
||||
gestureVelocityImpact={0.3}
|
||||
importantForAccessibility="auto"
|
||||
onClose={[Function]}
|
||||
onGestureBegin={[Function]}
|
||||
onGestureCanceled={[Function]}
|
||||
onOpen={[Function]}
|
||||
onTransitionStart={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
transitionSpec={
|
||||
Object {
|
||||
"close": Object {
|
||||
"animation": "spring",
|
||||
"config": Object {
|
||||
"damping": 500,
|
||||
"mass": 3,
|
||||
"overshootClamping": true,
|
||||
"restDisplacementThreshold": 0.01,
|
||||
"restSpeedThreshold": 0.01,
|
||||
"stiffness": 1000,
|
||||
},
|
||||
},
|
||||
"open": Object {
|
||||
"animation": "spring",
|
||||
"config": Object {
|
||||
"damping": 500,
|
||||
"mass": 3,
|
||||
"overshootClamping": true,
|
||||
"restDisplacementThreshold": 0.01,
|
||||
"restSpeedThreshold": 0.01,
|
||||
"stiffness": 1000,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"marginTop": 44,
|
||||
}
|
||||
}
|
||||
>
|
||||
<PanGestureHandler
|
||||
enabled={false}
|
||||
hitSlop={
|
||||
Object {
|
||||
"right": -700,
|
||||
}
|
||||
}
|
||||
maxDeltaY={20}
|
||||
minOffsetX={5}
|
||||
onHandlerStateChange={[Function]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"transform": Array [
|
||||
Object {
|
||||
"translateX": 0,
|
||||
},
|
||||
Object {
|
||||
"translateX": 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#fff",
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"shadowColor": "#000",
|
||||
"shadowOffset": Object {
|
||||
"height": 1,
|
||||
"width": -1,
|
||||
},
|
||||
"shadowOpacity": 0.3,
|
||||
"shadowRadius": 5,
|
||||
"top": 0,
|
||||
"width": 3,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flex": 1,
|
||||
"overflow": "hidden",
|
||||
},
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgb(242, 242, 242)",
|
||||
},
|
||||
undefined,
|
||||
],
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"flexDirection": "column-reverse",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</PanGestureHandler>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"zIndex": 1,
|
||||
},
|
||||
false,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
@@ -189,7 +27,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
pointerEvents="none"
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
@@ -198,6 +36,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
"zIndex": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
@@ -254,7 +93,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"marginHorizontal": 18,
|
||||
"marginHorizontal": 16,
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
@@ -294,23 +133,11 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`StackNavigator renders successfully 1`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"overflow": "hidden",
|
||||
}
|
||||
}
|
||||
>
|
||||
@@ -326,6 +153,14 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={false}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
accessibilityElementsHidden={false}
|
||||
closing={false}
|
||||
@@ -354,8 +189,8 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
"damping": 500,
|
||||
"mass": 3,
|
||||
"overshootClamping": true,
|
||||
"restDisplacementThreshold": 0.01,
|
||||
"restSpeedThreshold": 0.01,
|
||||
"restDisplacementThreshold": 10,
|
||||
"restSpeedThreshold": 10,
|
||||
"stiffness": 1000,
|
||||
},
|
||||
},
|
||||
@@ -365,8 +200,8 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
"damping": 500,
|
||||
"mass": 3,
|
||||
"overshootClamping": true,
|
||||
"restDisplacementThreshold": 0.01,
|
||||
"restSpeedThreshold": 0.01,
|
||||
"restDisplacementThreshold": 10,
|
||||
"restSpeedThreshold": 10,
|
||||
"stiffness": 1000,
|
||||
},
|
||||
},
|
||||
@@ -378,7 +213,6 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"marginTop": 44,
|
||||
}
|
||||
}
|
||||
>
|
||||
@@ -412,7 +246,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
pointerEvents="none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#fff",
|
||||
"backgroundColor": "rgb(242, 242, 242)",
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
@@ -467,15 +301,26 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`StackNavigator renders successfully 1`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"zIndex": 1,
|
||||
},
|
||||
false,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
@@ -486,7 +331,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
pointerEvents="none"
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
@@ -495,6 +340,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
"zIndex": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
@@ -551,7 +397,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"marginHorizontal": 18,
|
||||
"marginHorizontal": 16,
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
@@ -575,5 +421,173 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={false}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
accessibilityElementsHidden={false}
|
||||
closing={false}
|
||||
gestureVelocityImpact={0.3}
|
||||
importantForAccessibility="auto"
|
||||
onClose={[Function]}
|
||||
onGestureBegin={[Function]}
|
||||
onGestureCanceled={[Function]}
|
||||
onOpen={[Function]}
|
||||
onTransitionStart={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
transitionSpec={
|
||||
Object {
|
||||
"close": Object {
|
||||
"animation": "spring",
|
||||
"config": Object {
|
||||
"damping": 500,
|
||||
"mass": 3,
|
||||
"overshootClamping": true,
|
||||
"restDisplacementThreshold": 10,
|
||||
"restSpeedThreshold": 10,
|
||||
"stiffness": 1000,
|
||||
},
|
||||
},
|
||||
"open": Object {
|
||||
"animation": "spring",
|
||||
"config": Object {
|
||||
"damping": 500,
|
||||
"mass": 3,
|
||||
"overshootClamping": true,
|
||||
"restDisplacementThreshold": 10,
|
||||
"restSpeedThreshold": 10,
|
||||
"stiffness": 1000,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<PanGestureHandler
|
||||
enabled={false}
|
||||
hitSlop={
|
||||
Object {
|
||||
"right": -700,
|
||||
}
|
||||
}
|
||||
maxDeltaY={20}
|
||||
minOffsetX={5}
|
||||
onHandlerStateChange={[Function]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"transform": Array [
|
||||
Object {
|
||||
"translateX": 0,
|
||||
},
|
||||
Object {
|
||||
"translateX": 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgb(242, 242, 242)",
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"shadowColor": "#000",
|
||||
"shadowOffset": Object {
|
||||
"height": 1,
|
||||
"width": -1,
|
||||
},
|
||||
"shadowOpacity": 0.3,
|
||||
"shadowRadius": 5,
|
||||
"top": 0,
|
||||
"width": 3,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flex": 1,
|
||||
"overflow": "hidden",
|
||||
},
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgb(242, 242, 242)",
|
||||
},
|
||||
undefined,
|
||||
],
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"flexDirection": "column-reverse",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</PanGestureHandler>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
`;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
CreateNavigatorConfig,
|
||||
} from 'react-navigation';
|
||||
import StackView from '../views/StackView';
|
||||
import {
|
||||
import type {
|
||||
StackNavigationConfig,
|
||||
StackNavigationOptions,
|
||||
StackNavigationProp,
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import {
|
||||
import type {
|
||||
NavigationRoute,
|
||||
NavigationParams,
|
||||
NavigationScreenConfig,
|
||||
SupportedThemes,
|
||||
} from 'react-navigation';
|
||||
import { StackNavigationProp, StackNavigationOptions } from './vendor/types';
|
||||
import type {
|
||||
StackNavigationProp,
|
||||
StackNavigationOptions,
|
||||
} from './vendor/types';
|
||||
|
||||
export type NavigationStackScreenProps<
|
||||
Params = NavigationParams,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { StyleProp, ViewStyle } from 'react-native';
|
||||
import { StackNavigationOptions, StackNavigationConfig } from '../vendor/types';
|
||||
import type { StyleProp, ViewStyle } from 'react-native';
|
||||
import type {
|
||||
StackNavigationOptions,
|
||||
StackNavigationConfig,
|
||||
} from '../vendor/types';
|
||||
|
||||
type Validation = {
|
||||
message: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import { StackNavigationOptions } from '../vendor/types';
|
||||
import type { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import type { StackNavigationOptions } from '../vendor/types';
|
||||
|
||||
type Validation = {
|
||||
check: (o: Record<string, any>) => boolean;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Animated } from 'react-native';
|
||||
import { isIphoneX } from 'react-native-iphone-x-helper';
|
||||
import conditional from '../utils/conditional';
|
||||
import {
|
||||
import type {
|
||||
StackCardInterpolationProps,
|
||||
StackCardInterpolatedStyle,
|
||||
} from '../types';
|
||||
@@ -164,6 +164,7 @@ export function forModalPresentationIOS({
|
||||
borderTopLeftRadius: borderRadius,
|
||||
borderTopRightRadius: borderRadius,
|
||||
marginTop: index === 0 ? 0 : statusBarHeight,
|
||||
marginBottom: index === 0 ? 0 : topOffset,
|
||||
transform: [{ translateY }, { scale }],
|
||||
},
|
||||
overlayStyle: { opacity: overlayOpacity },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Animated, I18nManager } from 'react-native';
|
||||
import {
|
||||
import type {
|
||||
StackHeaderInterpolationProps,
|
||||
StackHeaderInterpolatedStyle,
|
||||
} from '../types';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Platform } from 'react-native';
|
||||
import {
|
||||
forHorizontalIOS,
|
||||
forVerticalIOS,
|
||||
@@ -14,8 +15,7 @@ import {
|
||||
FadeOutToBottomAndroidSpec,
|
||||
FadeInFromBottomAndroidSpec,
|
||||
} from './TransitionSpecs';
|
||||
import { TransitionPreset } from '../types';
|
||||
import { Platform } from 'react-native';
|
||||
import type { TransitionPreset } from '../types';
|
||||
|
||||
const ANDROID_VERSION_PIE = 28;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Easing } from 'react-native';
|
||||
import { TransitionSpec } from '../types';
|
||||
import type { TransitionSpec } from '../types';
|
||||
|
||||
/**
|
||||
* Exact values from UINavigationController's animation configuration.
|
||||
@@ -11,8 +11,8 @@ export const TransitionIOSSpec: TransitionSpec = {
|
||||
damping: 500,
|
||||
mass: 3,
|
||||
overshootClamping: true,
|
||||
restDisplacementThreshold: 0.01,
|
||||
restSpeedThreshold: 0.01,
|
||||
restDisplacementThreshold: 10,
|
||||
restSpeedThreshold: 10,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
3
packages/stack/src/vendor/index.tsx
vendored
3
packages/stack/src/vendor/index.tsx
vendored
@@ -43,11 +43,12 @@ export { default as useGestureHandlerRef } from './utils/useGestureHandlerRef';
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
export {
|
||||
export type {
|
||||
NavigationStackState,
|
||||
StackNavigationProp as NavigationStackProp,
|
||||
StackNavigationOptions as NavigationStackOptions,
|
||||
StackNavigationConfig as NavigationStackConfig,
|
||||
StackHeaderProps,
|
||||
StackHeaderLeftButtonProps,
|
||||
StackHeaderTitleProps,
|
||||
StackCardInterpolatedStyle,
|
||||
|
||||
30
packages/stack/src/vendor/types.tsx
vendored
30
packages/stack/src/vendor/types.tsx
vendored
@@ -1,13 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
import type * as React from 'react';
|
||||
import type {
|
||||
Animated,
|
||||
StyleProp,
|
||||
TextStyle,
|
||||
ViewStyle,
|
||||
LayoutChangeEvent,
|
||||
} from 'react-native';
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import {
|
||||
import type { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import type {
|
||||
NavigationRoute,
|
||||
NavigationState,
|
||||
NavigationScreenProp,
|
||||
@@ -131,7 +131,7 @@ export type StackHeaderOptions = {
|
||||
* This may lead to white space or overlap between `headerLeft` and `headerTitle` if a customized `headerLeft` is used.
|
||||
* It can be solved by adjusting `left` and `right` style in `headerTitleContainerStyle` and `marginHorizontal` in `headerTitleStyle`.
|
||||
*/
|
||||
headerTitleContainerStyle?: StyleProp<ViewStyle>;
|
||||
headerTitleContainerStyle?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
|
||||
/**
|
||||
* Tint color for the header.
|
||||
*/
|
||||
@@ -145,7 +145,8 @@ export type StackHeaderOptions = {
|
||||
*/
|
||||
headerBackAllowFontScaling?: boolean;
|
||||
/**
|
||||
* Title string used by the back button on iOS, or `null` to disable label. Defaults to the previous scene's `headerTitle`.
|
||||
* Title string used by the back button on iOS. Defaults to the previous scene's `headerTitle`.
|
||||
* Use `headerBackTitleVisible: false` to hide it.
|
||||
*/
|
||||
headerBackTitle?: string;
|
||||
/**
|
||||
@@ -169,7 +170,7 @@ export type StackHeaderOptions = {
|
||||
/**
|
||||
* Style object for the container of the `headerLeft` component, for example to add padding.
|
||||
*/
|
||||
headerLeftContainerStyle?: StyleProp<ViewStyle>;
|
||||
headerLeftContainerStyle?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
|
||||
/**
|
||||
* Function which returns a React Element to display on the right side of the header.
|
||||
*/
|
||||
@@ -177,7 +178,7 @@ export type StackHeaderOptions = {
|
||||
/**
|
||||
* Style object for the container of the `headerRight` component, for example to add padding.
|
||||
*/
|
||||
headerRightContainerStyle?: StyleProp<ViewStyle>;
|
||||
headerRightContainerStyle?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
|
||||
/**
|
||||
* Function which returns a React Element to display custom image in header's back button.
|
||||
* It receives the `tintColor` in in the options object as an argument. object.
|
||||
@@ -199,7 +200,7 @@ export type StackHeaderOptions = {
|
||||
/**
|
||||
* Style object for the header. You can specify a custom background color here, for example.
|
||||
*/
|
||||
headerStyle?: StyleProp<ViewStyle>;
|
||||
headerStyle?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
|
||||
/**
|
||||
* Defaults to `false`. If `true`, the header will not have a background unless you explicitly provide it with `headerBackground`.
|
||||
* The header will also float over the screen so that it overlaps the content underneath.
|
||||
@@ -300,7 +301,8 @@ export type StackNavigationOptions = StackHeaderOptions &
|
||||
cardStyle?: StyleProp<ViewStyle>;
|
||||
/**
|
||||
* Whether transition animation should be enabled the screen.
|
||||
* If you set it to `false`, the screen won't animate when pushing or popping. Defaults to `true`.
|
||||
* If you set it to `false`, the screen won't animate when pushing or popping.
|
||||
* Defaults to `true` on Android and iOS, `false` on Web.
|
||||
*/
|
||||
animationEnabled?: boolean;
|
||||
/**
|
||||
@@ -310,10 +312,12 @@ export type StackNavigationOptions = StackHeaderOptions &
|
||||
animationTypeForReplace?: 'push' | 'pop';
|
||||
/**
|
||||
* Whether you can use gestures to dismiss this screen. Defaults to `true` on iOS, `false` on Android.
|
||||
* Not supported on Web.
|
||||
*/
|
||||
gestureEnabled?: boolean;
|
||||
/**
|
||||
* Object to override the distance of touch start from the edge of the screen to recognize gestures.
|
||||
* Not supported on Web.
|
||||
*/
|
||||
gestureResponseDistance?: {
|
||||
/**
|
||||
@@ -326,8 +330,8 @@ export type StackNavigationOptions = StackHeaderOptions &
|
||||
horizontal?: number;
|
||||
};
|
||||
/**
|
||||
* Number which determines the relevance of velocity for the gesture.
|
||||
* Defaults to 0.3.
|
||||
* Number which determines the relevance of velocity for the gesture. Defaults to 0.3.
|
||||
* Not supported on Web.
|
||||
*/
|
||||
gestureVelocityImpact?: number;
|
||||
/**
|
||||
@@ -394,7 +398,7 @@ export type StackHeaderLeftButtonProps = {
|
||||
/**
|
||||
* Style object for the label.
|
||||
*/
|
||||
labelStyle?: React.ComponentProps<typeof Animated.Text>['style'];
|
||||
labelStyle?: Animated.WithAnimatedValue<StyleProp<TextStyle>>;
|
||||
/**
|
||||
* Whether label font should scale to respect Text Size accessibility settings.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { StackCardInterpolationProps } from '../types';
|
||||
import type { StackCardInterpolationProps } from '../types';
|
||||
|
||||
export default React.createContext<StackCardInterpolationProps | undefined>(
|
||||
undefined
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { PanGestureHandler } from 'react-native-gesture-handler';
|
||||
|
||||
export default React.createContext<React.Ref<PanGestureHandler> | undefined>(
|
||||
undefined
|
||||
);
|
||||
export default React.createContext<React.Ref<
|
||||
import('react-native-gesture-handler').PanGestureHandler
|
||||
> | null>(null);
|
||||
|
||||
5
packages/stack/src/vendor/utils/HeaderShownContext.tsx
vendored
Normal file
5
packages/stack/src/vendor/utils/HeaderShownContext.tsx
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import * as React from 'react';
|
||||
|
||||
const HeaderShownContext = React.createContext(false);
|
||||
|
||||
export default HeaderShownContext;
|
||||
8
packages/stack/src/vendor/utils/PreviousSceneContext.tsx
vendored
Normal file
8
packages/stack/src/vendor/utils/PreviousSceneContext.tsx
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import type { Route, Scene } from '../types';
|
||||
|
||||
const PreviousSceneContext = React.createContext<
|
||||
Scene<Route<string>> | undefined
|
||||
>(undefined);
|
||||
|
||||
export default PreviousSceneContext;
|
||||
@@ -1,5 +1,5 @@
|
||||
import getInvertedMultiplier from './getInvertedMultiplier';
|
||||
import { GestureDirection, Layout } from '../types';
|
||||
import type { GestureDirection, Layout } from '../types';
|
||||
|
||||
export default function getDistanceForDirection(
|
||||
layout: Layout,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { I18nManager } from 'react-native';
|
||||
import { GestureDirection } from '../types';
|
||||
import type { GestureDirection } from '../types';
|
||||
|
||||
export default function getInvertedMultiplier(
|
||||
gestureDirection: GestureDirection
|
||||
|
||||
@@ -39,7 +39,7 @@ export default class BorderlessButton extends React.Component<Props> {
|
||||
const { children, style, enabled, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
// @ts-ignore
|
||||
// @ts-expect-error: the Animated.createAnimatedComponent types don't work properly
|
||||
<AnimatedBaseButton
|
||||
{...rest}
|
||||
onActiveStateChange={this.handleActiveStateChange}
|
||||
|
||||
22
packages/stack/src/vendor/views/GestureHandler.native.tsx
vendored
Normal file
22
packages/stack/src/vendor/views/GestureHandler.native.tsx
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
PanGestureHandler as PanGestureHandlerNative,
|
||||
PanGestureHandlerProperties,
|
||||
} from 'react-native-gesture-handler';
|
||||
import GestureHandlerRefContext from '../utils/GestureHandlerRefContext';
|
||||
|
||||
export function PanGestureHandler(props: PanGestureHandlerProperties) {
|
||||
const gestureRef = React.useRef<PanGestureHandlerNative>(null);
|
||||
|
||||
return (
|
||||
<GestureHandlerRefContext.Provider value={gestureRef}>
|
||||
<PanGestureHandlerNative {...props} ref={gestureRef} />
|
||||
</GestureHandlerRefContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
GestureHandlerRootView,
|
||||
State as GestureState,
|
||||
PanGestureHandlerGestureEvent,
|
||||
} from 'react-native-gesture-handler';
|
||||
24
packages/stack/src/vendor/views/GestureHandler.tsx
vendored
Normal file
24
packages/stack/src/vendor/views/GestureHandler.tsx
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import * as React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import type { PanGestureHandlerProperties } from 'react-native-gesture-handler';
|
||||
|
||||
const Dummy: any = ({ children }: { children: React.ReactNode }) => (
|
||||
<>{children}</>
|
||||
);
|
||||
|
||||
export const PanGestureHandler = Dummy as React.ComponentType<
|
||||
PanGestureHandlerProperties
|
||||
>;
|
||||
|
||||
export const GestureHandlerRootView = View;
|
||||
|
||||
export const GestureState = {
|
||||
UNDETERMINED: 0,
|
||||
FAILED: 1,
|
||||
BEGAN: 2,
|
||||
CANCELLED: 3,
|
||||
ACTIVE: 4,
|
||||
END: 5,
|
||||
};
|
||||
|
||||
export type { PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';
|
||||
@@ -4,9 +4,10 @@ import { StackActions } from 'react-navigation';
|
||||
import { getStatusBarHeight } from 'react-native-iphone-x-helper';
|
||||
|
||||
import HeaderSegment, { getDefaultHeaderHeight } from './HeaderSegment';
|
||||
|
||||
import HeaderTitle from './HeaderTitle';
|
||||
import debounce from '../../utils/debounce';
|
||||
import { StackHeaderProps, StackHeaderTitleProps } from '../../types';
|
||||
import type { StackHeaderProps, StackHeaderTitleProps } from '../../types';
|
||||
|
||||
const Header = React.memo(function Header(props: StackHeaderProps) {
|
||||
const {
|
||||
@@ -43,9 +44,16 @@ const Header = React.memo(function Header(props: StackHeaderProps) {
|
||||
: previous.route.routeName;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const goBack = React.useCallback(
|
||||
debounce(() => {
|
||||
navigation.dispatch(StackActions.pop({ key: scene.route.key }));
|
||||
if (navigation.isFirstRouteInParent()) {
|
||||
// If we're the first route, we're going back to a parent navigator
|
||||
// So we can't specify a key here
|
||||
navigation.dispatch(StackActions.pop());
|
||||
} else {
|
||||
navigation.dispatch(StackActions.pop({ key: scene.route.key }));
|
||||
}
|
||||
}, 50),
|
||||
[navigation, scene.route.key]
|
||||
);
|
||||
|
||||
@@ -9,9 +9,9 @@ import {
|
||||
LayoutChangeEvent,
|
||||
} from 'react-native';
|
||||
import MaskedView from '../MaskedView';
|
||||
import TouchableItem from '../TouchableItem';
|
||||
import { TouchableItem } from '../TouchableItem';
|
||||
import useTheme from '../../../utils/useTheme';
|
||||
import { StackHeaderLeftButtonProps } from '../../types';
|
||||
import type { StackHeaderLeftButtonProps } from '../../types';
|
||||
|
||||
type Props = StackHeaderLeftButtonProps;
|
||||
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import * as React from 'react';
|
||||
import { Animated, StyleSheet, Platform, ViewProps } from 'react-native';
|
||||
import {
|
||||
Animated,
|
||||
StyleSheet,
|
||||
Platform,
|
||||
ViewProps,
|
||||
StyleProp,
|
||||
ViewStyle,
|
||||
} from 'react-native';
|
||||
import useTheme from '../../../utils/useTheme';
|
||||
|
||||
type Props = ViewProps & {
|
||||
style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
||||
import { NavigationContext } from 'react-navigation';
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import type { EdgeInsets } from 'react-native-safe-area-context';
|
||||
|
||||
import Header from './Header';
|
||||
import {
|
||||
@@ -10,9 +10,11 @@ import {
|
||||
forNoAnimation,
|
||||
forSlideRight,
|
||||
} from '../../TransitionConfigs/HeaderStyleInterpolators';
|
||||
import {
|
||||
Route,
|
||||
import HeaderShownContext from '../../utils/HeaderShownContext';
|
||||
import PreviousSceneContext from '../../utils/PreviousSceneContext';
|
||||
import type {
|
||||
Layout,
|
||||
Route,
|
||||
Scene,
|
||||
StackHeaderStyleInterpolator,
|
||||
StackNavigationProp,
|
||||
@@ -24,9 +26,10 @@ export type Props = {
|
||||
layout: Layout;
|
||||
insets: EdgeInsets;
|
||||
scenes: (Scene<Route<string>> | undefined)[];
|
||||
getPreviousRoute: (props: {
|
||||
getPreviousScene: (props: {
|
||||
route: Route<string>;
|
||||
}) => Route<string> | undefined;
|
||||
index: number;
|
||||
}) => Scene<Route<string>> | undefined;
|
||||
getFocusedRoute: () => Route<string>;
|
||||
onContentHeightChange?: (props: {
|
||||
route: Route<string>;
|
||||
@@ -42,14 +45,16 @@ export default function HeaderContainer({
|
||||
scenes,
|
||||
layout,
|
||||
insets,
|
||||
getPreviousScene,
|
||||
getFocusedRoute,
|
||||
getPreviousRoute,
|
||||
onContentHeightChange,
|
||||
gestureDirection,
|
||||
styleInterpolator,
|
||||
style,
|
||||
}: Props) {
|
||||
const focusedRoute = getFocusedRoute();
|
||||
const isParentHeaderShown = React.useContext(HeaderShownContext);
|
||||
const parentPreviousScene = React.useContext(PreviousSceneContext);
|
||||
|
||||
return (
|
||||
<View pointerEvents="box-none" style={style}>
|
||||
@@ -58,36 +63,41 @@ export default function HeaderContainer({
|
||||
return null;
|
||||
}
|
||||
|
||||
const { options } = scene.descriptor;
|
||||
const isFocused = focusedRoute.key === scene.route.key;
|
||||
const previousRoute = getPreviousRoute({ route: scene.route });
|
||||
const {
|
||||
header,
|
||||
headerShown = isParentHeaderShown === false,
|
||||
headerTransparent,
|
||||
} = scene.descriptor.options || {};
|
||||
|
||||
let previous;
|
||||
|
||||
if (previousRoute) {
|
||||
// The previous scene will be shortly before the current scene in the array
|
||||
// So loop back from current index to avoid looping over the full array
|
||||
for (let j = i - 1; j >= 0; j--) {
|
||||
const s = self[j];
|
||||
|
||||
if (s && s.route.key === previousRoute.key) {
|
||||
previous = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!headerShown) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isFocused = focusedRoute.key === scene.route.key;
|
||||
const previous =
|
||||
getPreviousScene({
|
||||
route: scene.route,
|
||||
index: i,
|
||||
}) ?? parentPreviousScene;
|
||||
|
||||
// If the screen is next to a headerless screen, we need to make the header appear static
|
||||
// This makes the header look like it's moving with the screen
|
||||
const previousScene = self[i - 1];
|
||||
const nextScene = self[i + 1];
|
||||
|
||||
const {
|
||||
headerShown: previousHeaderShown = isParentHeaderShown === false,
|
||||
} = previousScene?.descriptor.options || {};
|
||||
|
||||
const { headerShown: nextHeaderShown = isParentHeaderShown === false } =
|
||||
nextScene?.descriptor.options || {};
|
||||
|
||||
const isHeaderStatic =
|
||||
(previousScene &&
|
||||
previousScene.descriptor.options.headerShown === false &&
|
||||
(previousHeaderShown === false &&
|
||||
// We still need to animate when coming back from next scene
|
||||
// A hacky way to check this is if the next scene exists
|
||||
!nextScene) ||
|
||||
(nextScene && nextScene.descriptor.options.headerShown === false);
|
||||
nextHeaderShown === false;
|
||||
|
||||
const props = {
|
||||
mode,
|
||||
@@ -133,18 +143,12 @@ export default function HeaderContainer({
|
||||
style={
|
||||
// Avoid positioning the focused header absolutely
|
||||
// Otherwise accessibility tools don't seem to be able to find it
|
||||
(mode === 'float' && !isFocused) || options.headerTransparent
|
||||
(mode === 'float' && !isFocused) || headerTransparent
|
||||
? styles.header
|
||||
: null
|
||||
}
|
||||
>
|
||||
{options.headerShown !== false ? (
|
||||
options.header !== undefined ? (
|
||||
options.header(props)
|
||||
) : (
|
||||
<Header {...props} />
|
||||
)
|
||||
) : null}
|
||||
{header !== undefined ? header(props) : <Header {...props} />}
|
||||
</View>
|
||||
</>
|
||||
</NavigationContext.Provider>
|
||||
|
||||
@@ -7,12 +7,12 @@ import {
|
||||
Platform,
|
||||
ViewStyle,
|
||||
} from 'react-native';
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import { NavigationRoute } from 'react-navigation';
|
||||
import type { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import type { NavigationRoute } from 'react-navigation';
|
||||
import HeaderBackButton from './HeaderBackButton';
|
||||
import HeaderBackground from './HeaderBackground';
|
||||
import memoize from '../../utils/memoize';
|
||||
import {
|
||||
import type {
|
||||
Layout,
|
||||
StackHeaderStyleInterpolator,
|
||||
StackHeaderLeftButtonProps,
|
||||
@@ -62,7 +62,6 @@ export const getDefaultHeaderHeight = (
|
||||
let headerHeight;
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
// @ts-ignore
|
||||
if (isLandscape && !Platform.isPad) {
|
||||
headerHeight = 32;
|
||||
} else {
|
||||
@@ -208,7 +207,7 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
||||
borderTopStartRadius,
|
||||
borderTopWidth,
|
||||
borderWidth,
|
||||
// @ts-ignore: web support for shadow
|
||||
// @ts-expect-error: web support for shadow
|
||||
boxShadow,
|
||||
elevation,
|
||||
shadowColor,
|
||||
@@ -250,7 +249,6 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
||||
borderTopStartRadius,
|
||||
borderTopWidth,
|
||||
borderWidth,
|
||||
// @ts-ignore
|
||||
boxShadow,
|
||||
elevation,
|
||||
shadowColor,
|
||||
@@ -265,9 +263,9 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
||||
// So we need to filter them out
|
||||
// Users can use `null` instead
|
||||
for (const styleProp in safeStyles) {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error: typescript wrongly complains that styleProp cannot be used to index safeStyles
|
||||
if (safeStyles[styleProp] === undefined) {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete safeStyles[styleProp];
|
||||
}
|
||||
@@ -307,11 +305,13 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
||||
})
|
||||
: null;
|
||||
|
||||
const rightButton = right ? right({ tintColor: headerTintColor }) : null;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Animated.View
|
||||
pointerEvents="none"
|
||||
style={[StyleSheet.absoluteFill, backgroundStyle]}
|
||||
pointerEvents="box-none"
|
||||
style={[StyleSheet.absoluteFill, { zIndex: 0 }, backgroundStyle]}
|
||||
>
|
||||
{headerBackground ? (
|
||||
headerBackground({ style: safeStyles })
|
||||
@@ -345,8 +345,19 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
||||
pointerEvents="box-none"
|
||||
style={[
|
||||
headerTitleAlign === 'left'
|
||||
? { position: 'absolute', left: leftButton ? 72 : 16 }
|
||||
: { marginHorizontal: 18 },
|
||||
? {
|
||||
position: 'absolute',
|
||||
left: (leftButton ? 72 : 16) + insets.left,
|
||||
right: (rightButton ? 72 : 16) + insets.right,
|
||||
}
|
||||
: {
|
||||
marginHorizontal:
|
||||
(leftButton ? 32 : 16) +
|
||||
(leftButton && headerBackTitleVisible !== false
|
||||
? 40
|
||||
: 0) +
|
||||
Math.max(insets.left, insets.right),
|
||||
},
|
||||
titleStyle,
|
||||
titleContainerStyle,
|
||||
]}
|
||||
@@ -359,7 +370,7 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
||||
style: customTitleStyle,
|
||||
})}
|
||||
</Animated.View>
|
||||
{right ? (
|
||||
{rightButton ? (
|
||||
<Animated.View
|
||||
pointerEvents="box-none"
|
||||
style={[
|
||||
@@ -369,7 +380,7 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
||||
rightContainerStyle,
|
||||
]}
|
||||
>
|
||||
{right({ tintColor: headerTintColor })}
|
||||
{rightButton}
|
||||
</Animated.View>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
import { Animated, StyleSheet, Platform } from 'react-native';
|
||||
import useTheme from '../../../utils/useTheme';
|
||||
|
||||
type Props = React.ComponentProps<typeof Animated.Text> & {
|
||||
type Props = Omit<React.ComponentProps<typeof Animated.Text>, 'key'> & {
|
||||
tintColor?: string;
|
||||
children?: string;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { TextInput } from 'react-native';
|
||||
import { TextInput, Platform, Keyboard } from 'react-native';
|
||||
|
||||
type Props = {
|
||||
enabled: boolean;
|
||||
@@ -35,7 +35,11 @@ export default class KeyboardManager extends React.Component<Props> {
|
||||
|
||||
this.clearKeyboardTimeout();
|
||||
|
||||
const input = TextInput.State.currentlyFocusedField();
|
||||
// @ts-expect-error: currentlyFocusedInput is pretty new, so not in the type definitions
|
||||
const input = TextInput.State.currentlyFocusedInput
|
||||
? // @ts-expect-error
|
||||
TextInput.State.currentlyFocusedInput()
|
||||
: TextInput.State.currentlyFocusedField();
|
||||
|
||||
// When a page change begins, blur the currently focused input
|
||||
TextInput.State.blurTextInput(input);
|
||||
@@ -56,7 +60,9 @@ export default class KeyboardManager extends React.Component<Props> {
|
||||
|
||||
const input = this.previouslyFocusedTextInput;
|
||||
|
||||
if (input) {
|
||||
if (Platform.OS === 'android') {
|
||||
Keyboard.dismiss();
|
||||
} else if (input) {
|
||||
TextInput.State.blurTextInput(input);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
import * as React from 'react';
|
||||
import { UIManager } from 'react-native';
|
||||
import RNCMaskedView from '@react-native-community/masked-view';
|
||||
|
||||
type Props = React.ComponentProps<typeof RNCMaskedView> & {
|
||||
type MaskedViewType = typeof import('@react-native-community/masked-view').default;
|
||||
|
||||
type Props = React.ComponentProps<MaskedViewType> & {
|
||||
children: React.ReactElement;
|
||||
};
|
||||
|
||||
let RNCMaskedView: MaskedViewType | undefined;
|
||||
|
||||
try {
|
||||
RNCMaskedView = require('@react-native-community/masked-view').default;
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
const isMaskedViewAvailable =
|
||||
// @ts-ignore
|
||||
UIManager.getViewManagerConfig('RNCMaskedView') != null;
|
||||
|
||||
export default function MaskedView({ children, ...rest }: Props) {
|
||||
if (isMaskedViewAvailable) {
|
||||
if (isMaskedViewAvailable && RNCMaskedView) {
|
||||
return <RNCMaskedView {...rest}>{children}</RNCMaskedView>;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import type * as React from 'react';
|
||||
|
||||
type Props = {
|
||||
maskElement: React.ReactElement;
|
||||
|
||||
71
packages/stack/src/vendor/views/Screens.tsx
vendored
Normal file
71
packages/stack/src/vendor/views/Screens.tsx
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
import * as React from 'react';
|
||||
import { Animated, View, Platform, ViewProps } from 'react-native';
|
||||
|
||||
let Screens: typeof import('react-native-screens') | undefined;
|
||||
|
||||
try {
|
||||
Screens = require('react-native-screens');
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
// The web implementation in react-native-screens seems buggy.
|
||||
// The view doesn't become visible after coming back in some cases.
|
||||
// So we use our custom implementation.
|
||||
class WebScreen extends React.Component<
|
||||
ViewProps & {
|
||||
active: number;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
> {
|
||||
render() {
|
||||
const { active, style, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
<View
|
||||
// @ts-expect-error: hidden exists on web, but not in React Native
|
||||
hidden={!active}
|
||||
style={[style, { display: active ? 'flex' : 'none' }]}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const AnimatedWebScreen = Animated.createAnimatedComponent(WebScreen);
|
||||
|
||||
export const MaybeScreenContainer = ({
|
||||
enabled,
|
||||
...rest
|
||||
}: ViewProps & {
|
||||
enabled: boolean;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
if (enabled && Platform.OS !== 'web' && Screens && Screens.screensEnabled()) {
|
||||
return <Screens.ScreenContainer {...rest} />;
|
||||
}
|
||||
|
||||
return <View {...rest} />;
|
||||
};
|
||||
|
||||
export const MaybeScreen = ({
|
||||
enabled,
|
||||
active,
|
||||
...rest
|
||||
}: ViewProps & {
|
||||
enabled: boolean;
|
||||
active: 0 | 1 | Animated.AnimatedInterpolation;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
if (enabled && Platform.OS === 'web') {
|
||||
// @ts-expect-error: the Animated.createAnimatedComponent types don't work properly
|
||||
return <AnimatedWebScreen active={active} {...rest} />;
|
||||
}
|
||||
|
||||
if (enabled && Screens && Screens.screensEnabled()) {
|
||||
// @ts-expect-error: stackPresentation is incorrectly marked as required
|
||||
return <Screens.Screen active={active} {...rest} />;
|
||||
}
|
||||
|
||||
return <View {...rest} />;
|
||||
};
|
||||
76
packages/stack/src/vendor/views/Stack/Card.tsx
vendored
76
packages/stack/src/vendor/views/Stack/Card.tsx
vendored
@@ -9,19 +9,20 @@ import {
|
||||
Platform,
|
||||
InteractionManager,
|
||||
} from 'react-native';
|
||||
import type { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import Color from 'color';
|
||||
|
||||
import CardSheet from './CardSheet';
|
||||
import {
|
||||
PanGestureHandler,
|
||||
State as GestureState,
|
||||
GestureState,
|
||||
PanGestureHandlerGestureEvent,
|
||||
} from 'react-native-gesture-handler';
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import Color from 'color';
|
||||
import StackGestureRefContext from '../../utils/GestureHandlerRefContext';
|
||||
} from '../GestureHandler';
|
||||
import CardAnimationContext from '../../utils/CardAnimationContext';
|
||||
import getDistanceForDirection from '../../utils/getDistanceForDirection';
|
||||
import getInvertedMultiplier from '../../utils/getInvertedMultiplier';
|
||||
import memoize from '../../utils/memoize';
|
||||
import {
|
||||
import type {
|
||||
TransitionSpec,
|
||||
StackCardStyleInterpolator,
|
||||
GestureDirection,
|
||||
@@ -36,6 +37,7 @@ type Props = ViewProps & {
|
||||
gesture: Animated.Value;
|
||||
layout: Layout;
|
||||
insets: EdgeInsets;
|
||||
pageOverflowEnabled: boolean;
|
||||
gestureDirection: GestureDirection;
|
||||
onOpen: () => void;
|
||||
onClose: () => void;
|
||||
@@ -44,7 +46,9 @@ type Props = ViewProps & {
|
||||
onGestureCanceled?: () => void;
|
||||
onGestureEnd?: () => void;
|
||||
children: React.ReactNode;
|
||||
overlay: (props: { style: StyleProp<ViewStyle> }) => React.ReactNode;
|
||||
overlay: (props: {
|
||||
style: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
|
||||
}) => React.ReactNode;
|
||||
overlayEnabled: boolean;
|
||||
shadowEnabled: boolean;
|
||||
gestureEnabled: boolean;
|
||||
@@ -81,7 +85,11 @@ export default class Card extends React.Component<Props> {
|
||||
shadowEnabled: true,
|
||||
gestureEnabled: true,
|
||||
gestureVelocityImpact: GESTURE_VELOCITY_IMPACT,
|
||||
overlay: ({ style }: { style: StyleProp<ViewStyle> }) =>
|
||||
overlay: ({
|
||||
style,
|
||||
}: {
|
||||
style: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
|
||||
}) =>
|
||||
style ? (
|
||||
<Animated.View pointerEvents="none" style={[styles.overlay, style]} />
|
||||
) : null,
|
||||
@@ -246,11 +254,21 @@ export default class Card extends React.Component<Props> {
|
||||
this.handleStartInteraction();
|
||||
onGestureBegin?.();
|
||||
break;
|
||||
case GestureState.CANCELLED:
|
||||
case GestureState.CANCELLED: {
|
||||
this.isSwiping.setValue(FALSE);
|
||||
this.handleEndInteraction();
|
||||
|
||||
const velocity =
|
||||
gestureDirection === 'vertical' ||
|
||||
gestureDirection === 'vertical-inverted'
|
||||
? nativeEvent.velocityY
|
||||
: nativeEvent.velocityX;
|
||||
|
||||
this.animate({ closing: this.props.closing, velocity });
|
||||
|
||||
onGestureCanceled?.();
|
||||
break;
|
||||
}
|
||||
case GestureState.END: {
|
||||
this.isSwiping.setValue(FALSE);
|
||||
|
||||
@@ -272,12 +290,11 @@ export default class Card extends React.Component<Props> {
|
||||
}
|
||||
|
||||
const closing =
|
||||
(translation + velocity) *
|
||||
gestureVelocityImpact *
|
||||
(translation + velocity * gestureVelocityImpact) *
|
||||
getInvertedMultiplier(gestureDirection) >
|
||||
distance / 2
|
||||
? velocity !== 0 || translation !== 0
|
||||
: false;
|
||||
: this.props.closing;
|
||||
|
||||
this.animate({ closing, velocity });
|
||||
|
||||
@@ -403,8 +420,6 @@ export default class Card extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
private gestureRef = React.createRef<PanGestureHandler>();
|
||||
|
||||
private contentRef = React.createRef<View>();
|
||||
|
||||
render() {
|
||||
@@ -421,6 +436,7 @@ export default class Card extends React.Component<Props> {
|
||||
shadowEnabled,
|
||||
gestureEnabled,
|
||||
gestureDirection,
|
||||
pageOverflowEnabled,
|
||||
children,
|
||||
containerStyle: customContainerStyle,
|
||||
contentStyle,
|
||||
@@ -479,9 +495,20 @@ export default class Card extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<CardAnimationContext.Provider value={animationContext}>
|
||||
<Animated.View
|
||||
style={{
|
||||
// This is a dummy style that doesn't actually change anything visually.
|
||||
// Animated needs the animated value to be used somewhere, otherwise things don't update properly.
|
||||
// If we disable animations and hide header, it could end up making the value unused.
|
||||
// So we have this dummy style that will always be used regardless of what else changed.
|
||||
opacity: current,
|
||||
}}
|
||||
// Make sure that this view isn't removed. If this view is removed, our style with animated value won't apply
|
||||
collapsable={false}
|
||||
/>
|
||||
<View pointerEvents="box-none" {...rest}>
|
||||
{overlayEnabled ? (
|
||||
<View style={StyleSheet.absoluteFill}>
|
||||
<View pointerEvents="box-none" style={StyleSheet.absoluteFill}>
|
||||
{overlay({ style: overlayStyle })}
|
||||
</View>
|
||||
) : null}
|
||||
@@ -490,7 +517,6 @@ export default class Card extends React.Component<Props> {
|
||||
pointerEvents="box-none"
|
||||
>
|
||||
<PanGestureHandler
|
||||
ref={this.gestureRef}
|
||||
enabled={layout.width !== 0 && gestureEnabled}
|
||||
onGestureEvent={handleGestureEvent}
|
||||
onHandlerStateChange={this.handleGestureStateChange}
|
||||
@@ -508,19 +534,20 @@ export default class Card extends React.Component<Props> {
|
||||
: gestureDirection === 'vertical'
|
||||
? [styles.shadowVertical, styles.shadowTop]
|
||||
: [styles.shadowVertical, styles.shadowBottom],
|
||||
{ backgroundColor },
|
||||
shadowStyle,
|
||||
]}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
) : null}
|
||||
<View
|
||||
<CardSheet
|
||||
ref={this.contentRef}
|
||||
style={[styles.content, contentStyle]}
|
||||
enabled={pageOverflowEnabled}
|
||||
layout={layout}
|
||||
style={contentStyle}
|
||||
>
|
||||
<StackGestureRefContext.Provider value={this.gestureRef}>
|
||||
{children}
|
||||
</StackGestureRefContext.Provider>
|
||||
</View>
|
||||
{children}
|
||||
</CardSheet>
|
||||
</Animated.View>
|
||||
</PanGestureHandler>
|
||||
</Animated.View>
|
||||
@@ -534,17 +561,12 @@ const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
overlay: {
|
||||
flex: 1,
|
||||
backgroundColor: '#000',
|
||||
},
|
||||
shadow: {
|
||||
position: 'absolute',
|
||||
backgroundColor: '#fff',
|
||||
shadowRadius: 5,
|
||||
shadowColor: '#000',
|
||||
shadowOpacity: 0.3,
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import * as React from 'react';
|
||||
import { Animated, View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
||||
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
||||
import type { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
||||
import Card from './Card';
|
||||
import HeaderHeightContext from '../../utils/HeaderHeightContext';
|
||||
import HeaderShownContext from '../../utils/HeaderShownContext';
|
||||
import PreviousSceneContext from '../../utils/PreviousSceneContext';
|
||||
import useTheme from '../../../utils/useTheme';
|
||||
import {
|
||||
import type {
|
||||
Route,
|
||||
Scene,
|
||||
Layout,
|
||||
StackHeaderMode,
|
||||
StackCardMode,
|
||||
TransitionPreset,
|
||||
} from '../../types';
|
||||
|
||||
@@ -19,7 +22,6 @@ type Props = TransitionPreset & {
|
||||
closing: boolean;
|
||||
layout: Layout;
|
||||
gesture: Animated.Value;
|
||||
previousScene?: Scene<Route<string>>;
|
||||
scene: Scene<Route<string>>;
|
||||
safeAreaInsetTop: number;
|
||||
safeAreaInsetRight: number;
|
||||
@@ -29,9 +31,10 @@ type Props = TransitionPreset & {
|
||||
cardOverlayEnabled?: boolean;
|
||||
cardShadowEnabled?: boolean;
|
||||
cardStyle?: StyleProp<ViewStyle>;
|
||||
getPreviousRoute: (props: {
|
||||
getPreviousScene: (props: {
|
||||
route: Route<string>;
|
||||
}) => Route<string> | undefined;
|
||||
index: number;
|
||||
}) => Scene<Route<string>> | undefined;
|
||||
getFocusedRoute: () => Route<string>;
|
||||
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
|
||||
renderScene: (props: { route: Route<string> }) => React.ReactNode;
|
||||
@@ -42,7 +45,6 @@ type Props = TransitionPreset & {
|
||||
closing: boolean
|
||||
) => void;
|
||||
onTransitionEnd?: (props: { route: Route<string> }, closing: boolean) => void;
|
||||
onTransitionComplete: (props: { route: Route<string> }) => void;
|
||||
onPageChangeStart?: () => void;
|
||||
onPageChangeConfirm?: () => void;
|
||||
onPageChangeCancel?: () => void;
|
||||
@@ -52,9 +54,10 @@ type Props = TransitionPreset & {
|
||||
horizontal?: number;
|
||||
};
|
||||
gestureVelocityImpact?: number;
|
||||
mode: StackCardMode;
|
||||
headerMode: StackHeaderMode;
|
||||
headerShown?: boolean;
|
||||
headerTransparent?: boolean;
|
||||
headerShown: boolean;
|
||||
hasAbsoluteHeader: boolean;
|
||||
headerHeight: number;
|
||||
onHeaderHeightChange: (props: {
|
||||
route: Route<string>;
|
||||
@@ -78,25 +81,24 @@ function CardContainer({
|
||||
gestureEnabled,
|
||||
gestureResponseDistance,
|
||||
gestureVelocityImpact,
|
||||
getPreviousRoute,
|
||||
getPreviousScene,
|
||||
getFocusedRoute,
|
||||
mode,
|
||||
headerMode,
|
||||
headerShown,
|
||||
headerStyleInterpolator,
|
||||
headerTransparent,
|
||||
hasAbsoluteHeader,
|
||||
headerHeight,
|
||||
onHeaderHeightChange,
|
||||
index,
|
||||
layout,
|
||||
onCloseRoute,
|
||||
onOpenRoute,
|
||||
onTransitionComplete,
|
||||
onPageChangeCancel,
|
||||
onPageChangeConfirm,
|
||||
onPageChangeStart,
|
||||
onTransitionEnd,
|
||||
onTransitionStart,
|
||||
previousScene,
|
||||
renderHeader,
|
||||
renderScene,
|
||||
safeAreaInsetBottom,
|
||||
@@ -144,8 +146,7 @@ function CardContainer({
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
// `addListener` may not exist on web and older versions of React Native
|
||||
// @ts-ignore
|
||||
// @ts-expect-error: AnimatedInterpolation optionally has addListener, but the type defs don't think so
|
||||
const listener = scene.progress.next?.addListener?.(
|
||||
({ value }: { value: number }) => {
|
||||
setPointerEvents(value <= EPSILON ? 'box-none' : 'none');
|
||||
@@ -154,14 +155,15 @@ function CardContainer({
|
||||
|
||||
return () => {
|
||||
if (listener) {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error: AnimatedInterpolation optionally has removedListener, but the type defs don't think so
|
||||
scene.progress.next?.removeListener?.(listener);
|
||||
}
|
||||
};
|
||||
}, [pointerEvents, scene.progress.next]);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
React.useEffect(() => onTransitionComplete({ route: scene.route }), []);
|
||||
const isParentHeaderShown = React.useContext(HeaderShownContext);
|
||||
const isCurrentHeaderShown = headerMode !== 'none' && headerShown !== false;
|
||||
const previousScene = getPreviousScene({ route: scene.route, index });
|
||||
|
||||
return (
|
||||
<Card
|
||||
@@ -189,19 +191,22 @@ function CardContainer({
|
||||
accessibilityElementsHidden={!focused}
|
||||
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
|
||||
pointerEvents={active ? 'box-none' : pointerEvents}
|
||||
containerStyle={
|
||||
headerMode === 'float' && !headerTransparent && headerShown !== false
|
||||
? { marginTop: headerHeight }
|
||||
: null
|
||||
}
|
||||
contentStyle={[{ backgroundColor: colors.background }, cardStyle] as any}
|
||||
pageOverflowEnabled={headerMode === 'screen' && mode === 'card'}
|
||||
containerStyle={hasAbsoluteHeader ? { marginTop: headerHeight } : null}
|
||||
contentStyle={[{ backgroundColor: colors.background }, cardStyle]}
|
||||
style={StyleSheet.absoluteFill}
|
||||
>
|
||||
<View style={styles.container}>
|
||||
<View style={styles.scene}>
|
||||
<HeaderHeightContext.Provider value={headerHeight}>
|
||||
{renderScene({ route: scene.route })}
|
||||
</HeaderHeightContext.Provider>
|
||||
<PreviousSceneContext.Provider value={previousScene}>
|
||||
<HeaderShownContext.Provider
|
||||
value={isParentHeaderShown || isCurrentHeaderShown}
|
||||
>
|
||||
<HeaderHeightContext.Provider value={headerHeight}>
|
||||
{renderScene({ route: scene.route })}
|
||||
</HeaderHeightContext.Provider>
|
||||
</HeaderShownContext.Provider>
|
||||
</PreviousSceneContext.Provider>
|
||||
</View>
|
||||
{headerMode === 'screen'
|
||||
? renderHeader({
|
||||
@@ -209,7 +214,7 @@ function CardContainer({
|
||||
layout,
|
||||
insets,
|
||||
scenes: [previousScene, scene],
|
||||
getPreviousRoute,
|
||||
getPreviousScene,
|
||||
getFocusedRoute,
|
||||
gestureDirection,
|
||||
styleInterpolator: headerStyleInterpolator,
|
||||
|
||||
49
packages/stack/src/vendor/views/Stack/CardSheet.tsx
vendored
Normal file
49
packages/stack/src/vendor/views/Stack/CardSheet.tsx
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
import * as React from 'react';
|
||||
import { View, ViewProps, StyleSheet } from 'react-native';
|
||||
|
||||
type Props = ViewProps & {
|
||||
enabled: boolean;
|
||||
layout: { width: number; height: number };
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
// This component will render a page which overflows the screen
|
||||
// if the container fills the body by comparing the size
|
||||
// This lets the document.body handle scrolling of the content
|
||||
// It's necessary for mobile browsers to be able to hide address bar on scroll
|
||||
export default React.forwardRef<View, Props>(function CardSheet(
|
||||
{ enabled, layout, style, ...rest },
|
||||
ref
|
||||
) {
|
||||
const [fill, setFill] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof document === 'undefined' || !document.body) {
|
||||
// Only run when DOM is available
|
||||
return;
|
||||
}
|
||||
|
||||
const width = document.body.clientWidth;
|
||||
const height = document.body.clientHeight;
|
||||
|
||||
setFill(width === layout.width && height === layout.height);
|
||||
}, [layout.height, layout.width]);
|
||||
|
||||
return (
|
||||
<View
|
||||
{...rest}
|
||||
ref={ref}
|
||||
style={[enabled && fill ? styles.page : styles.card, style]}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
page: {
|
||||
minHeight: '100%',
|
||||
},
|
||||
card: {
|
||||
flex: 1,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
455
packages/stack/src/vendor/views/Stack/CardStack.tsx
vendored
455
packages/stack/src/vendor/views/Stack/CardStack.tsx
vendored
@@ -1,19 +1,17 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Animated,
|
||||
View,
|
||||
StyleSheet,
|
||||
LayoutChangeEvent,
|
||||
Dimensions,
|
||||
Platform,
|
||||
ViewProps,
|
||||
} from 'react-native';
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import { ScreenContainer, Screen, screensEnabled } from 'react-native-screens'; // Import with * as to prevent getters being called
|
||||
import { NavigationState as StackNavigationState } from 'react-navigation';
|
||||
import type { EdgeInsets } from 'react-native-safe-area-context';
|
||||
import type { NavigationState as StackNavigationState } from 'react-navigation';
|
||||
|
||||
import { MaybeScreenContainer, MaybeScreen } from '../Screens';
|
||||
import { getDefaultHeaderHeight } from '../Header/HeaderSegment';
|
||||
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
||||
import type { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
||||
import CardContainer from './CardContainer';
|
||||
import {
|
||||
DefaultTransition,
|
||||
@@ -21,12 +19,13 @@ import {
|
||||
} from '../../TransitionConfigs/TransitionPresets';
|
||||
import { forNoAnimation as forNoAnimationHeader } from '../../TransitionConfigs/HeaderStyleInterpolators';
|
||||
import { forNoAnimation as forNoAnimationCard } from '../../TransitionConfigs/CardStyleInterpolators';
|
||||
import HeaderShownContext from '../../utils/HeaderShownContext';
|
||||
import getDistanceForDirection from '../../utils/getDistanceForDirection';
|
||||
import {
|
||||
Route,
|
||||
import type {
|
||||
Layout,
|
||||
StackHeaderMode,
|
||||
StackCardMode,
|
||||
Route,
|
||||
Scene,
|
||||
StackDescriptorMap,
|
||||
StackNavigationOptions,
|
||||
@@ -54,7 +53,6 @@ type Props = {
|
||||
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
|
||||
renderScene: (props: { route: Route<string> }) => React.ReactNode;
|
||||
headerMode: StackHeaderMode;
|
||||
onTransitionComplete: (props: { route: Route<string> }) => void;
|
||||
onTransitionStart: (
|
||||
props: { route: Route<string> },
|
||||
closing: boolean
|
||||
@@ -76,37 +74,6 @@ type State = {
|
||||
|
||||
const EPSILON = 0.01;
|
||||
|
||||
const MaybeScreenContainer = ({
|
||||
enabled,
|
||||
...rest
|
||||
}: ViewProps & {
|
||||
enabled: boolean;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
if (enabled && screensEnabled()) {
|
||||
return <ScreenContainer {...rest} />;
|
||||
}
|
||||
|
||||
return <View {...rest} />;
|
||||
};
|
||||
|
||||
const MaybeScreen = ({
|
||||
enabled,
|
||||
active,
|
||||
...rest
|
||||
}: ViewProps & {
|
||||
enabled: boolean;
|
||||
active: number | Animated.AnimatedInterpolation;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
if (enabled && screensEnabled()) {
|
||||
// @ts-ignore
|
||||
return <Screen active={active} {...rest} />;
|
||||
}
|
||||
|
||||
return <View {...rest} />;
|
||||
};
|
||||
|
||||
const FALLBACK_DESCRIPTOR = Object.freeze({ options: {} });
|
||||
|
||||
const getHeaderHeights = (
|
||||
@@ -118,9 +85,10 @@ const getHeaderHeights = (
|
||||
) => {
|
||||
return routes.reduce<Record<string, number>>((acc, curr) => {
|
||||
const { options = {} } = descriptors[curr.key] || {};
|
||||
const { height = previous[curr.key] } = StyleSheet.flatten(
|
||||
options.headerStyle || {}
|
||||
);
|
||||
const style: any = StyleSheet.flatten(options.headerStyle || {});
|
||||
|
||||
const height =
|
||||
typeof style.height === 'number' ? style.height : previous[curr.key];
|
||||
|
||||
const safeAreaInsets = {
|
||||
...insets,
|
||||
@@ -276,7 +244,7 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
if (
|
||||
oldScene &&
|
||||
scene.__memo.every((it, i) => {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error: we haven't added __memo to the annotation to prevent usage elsewhere
|
||||
return oldScene.__memo[i] === it;
|
||||
})
|
||||
) {
|
||||
@@ -334,7 +302,7 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
props.insets,
|
||||
state.descriptors,
|
||||
layout,
|
||||
{}
|
||||
state.headerHeights
|
||||
),
|
||||
};
|
||||
});
|
||||
@@ -369,6 +337,33 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
return state.routes[state.index];
|
||||
};
|
||||
|
||||
private getPreviousScene = ({
|
||||
route,
|
||||
index,
|
||||
}: {
|
||||
route: Route<string>;
|
||||
index: number;
|
||||
}) => {
|
||||
const previousRoute = this.props.getPreviousRoute({ route });
|
||||
|
||||
let previous: Scene<Route<string>> | undefined;
|
||||
|
||||
if (previousRoute) {
|
||||
// The previous scene will be shortly before the current scene in the array
|
||||
// So loop back from current index to avoid looping over the full array
|
||||
for (let j = index - 1; j >= 0; j--) {
|
||||
const s = this.state.scenes[j];
|
||||
|
||||
if (s && s.route.key === previousRoute.key) {
|
||||
previous = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return previous;
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
mode,
|
||||
@@ -379,12 +374,10 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
closingRouteKeys,
|
||||
onOpenRoute,
|
||||
onCloseRoute,
|
||||
getPreviousRoute,
|
||||
getGesturesEnabled,
|
||||
renderHeader,
|
||||
renderScene,
|
||||
headerMode,
|
||||
onTransitionComplete,
|
||||
onTransitionStart,
|
||||
onTransitionEnd,
|
||||
onPageChangeStart,
|
||||
@@ -417,183 +410,207 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
|
||||
// Screens is buggy on iOS and web, so we only enable it on Android
|
||||
// For modals, usually we want the screen underneath to be visible, so also disable it there
|
||||
const isScreensEnabled = Platform.OS === 'android' && mode !== 'modal';
|
||||
const isScreensEnabled = Platform.OS !== 'ios' && mode !== 'modal';
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<MaybeScreenContainer
|
||||
enabled={isScreensEnabled}
|
||||
style={styles.container}
|
||||
onLayout={this.handleLayout}
|
||||
>
|
||||
{routes.map((route, index, self) => {
|
||||
const focused = focusedRoute.key === route.key;
|
||||
const gesture = gestures[route.key];
|
||||
const scene = scenes[index];
|
||||
<HeaderShownContext.Consumer>
|
||||
{(isParentHeaderShown) => {
|
||||
const isFloatHeaderAbsolute =
|
||||
headerMode === 'float'
|
||||
? this.state.scenes.slice(-2).some((scene) => {
|
||||
const { descriptor } = scene;
|
||||
const options = descriptor ? descriptor.options : {};
|
||||
const {
|
||||
headerTransparent,
|
||||
headerShown = isParentHeaderShown === false,
|
||||
} = options;
|
||||
|
||||
const isScreenActive = scene.progress.next
|
||||
? scene.progress.next.interpolate({
|
||||
inputRange: [0, 1 - EPSILON, 1],
|
||||
outputRange: [1, 1, 0],
|
||||
extrapolate: 'clamp',
|
||||
if (headerTransparent || headerShown === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
: 1;
|
||||
: false;
|
||||
|
||||
const {
|
||||
safeAreaInsets,
|
||||
headerShown,
|
||||
headerTransparent,
|
||||
cardShadowEnabled,
|
||||
cardOverlayEnabled,
|
||||
cardOverlay,
|
||||
cardStyle,
|
||||
animationEnabled,
|
||||
gestureResponseDistance,
|
||||
gestureVelocityImpact,
|
||||
gestureDirection = defaultTransitionPreset.gestureDirection,
|
||||
transitionSpec = defaultTransitionPreset.transitionSpec,
|
||||
cardStyleInterpolator = animationEnabled === false
|
||||
? forNoAnimationCard
|
||||
: defaultTransitionPreset.cardStyleInterpolator,
|
||||
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
|
||||
} = scene.descriptor
|
||||
? scene.descriptor.options
|
||||
: ({} as StackNavigationOptions);
|
||||
const floatingHeader =
|
||||
headerMode === 'float' ? (
|
||||
<React.Fragment key="header">
|
||||
{renderHeader({
|
||||
mode: 'float',
|
||||
layout,
|
||||
insets: { top, right, bottom, left },
|
||||
scenes,
|
||||
getPreviousScene: this.getPreviousScene,
|
||||
getFocusedRoute: this.getFocusedRoute,
|
||||
onContentHeightChange: this.handleHeaderLayout,
|
||||
gestureDirection:
|
||||
focusedOptions.gestureDirection !== undefined
|
||||
? focusedOptions.gestureDirection
|
||||
: defaultTransitionPreset.gestureDirection,
|
||||
styleInterpolator:
|
||||
focusedOptions.headerStyleInterpolator !== undefined
|
||||
? focusedOptions.headerStyleInterpolator
|
||||
: defaultTransitionPreset.headerStyleInterpolator,
|
||||
style: [
|
||||
styles.floating,
|
||||
isFloatHeaderAbsolute && styles.absolute,
|
||||
],
|
||||
})}
|
||||
</React.Fragment>
|
||||
) : null;
|
||||
|
||||
let transitionConfig = {
|
||||
gestureDirection,
|
||||
transitionSpec,
|
||||
cardStyleInterpolator,
|
||||
headerStyleInterpolator,
|
||||
};
|
||||
|
||||
// When a screen is not the last, it should use next screen's transition config
|
||||
// Many transitions also animate the previous screen, so using 2 different transitions doesn't look right
|
||||
// For example combining a slide and a modal transition would look wrong otherwise
|
||||
// With this approach, combining different transition styles in the same navigator mostly looks right
|
||||
// This will still be broken when 2 transitions have different idle state (e.g. modal presentation),
|
||||
// but majority of the transitions look alright
|
||||
if (index !== self.length - 1) {
|
||||
const nextScene = scenes[index + 1];
|
||||
|
||||
if (nextScene) {
|
||||
const {
|
||||
animationEnabled,
|
||||
gestureDirection = defaultTransitionPreset.gestureDirection,
|
||||
transitionSpec = defaultTransitionPreset.transitionSpec,
|
||||
cardStyleInterpolator = animationEnabled === false
|
||||
? forNoAnimationCard
|
||||
: defaultTransitionPreset.cardStyleInterpolator,
|
||||
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
|
||||
} = nextScene.descriptor
|
||||
? nextScene.descriptor.options
|
||||
: ({} as StackNavigationOptions);
|
||||
|
||||
transitionConfig = {
|
||||
gestureDirection,
|
||||
transitionSpec,
|
||||
cardStyleInterpolator,
|
||||
headerStyleInterpolator,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
top: safeAreaInsetTop = insets.top,
|
||||
right: safeAreaInsetRight = insets.right,
|
||||
bottom: safeAreaInsetBottom = insets.bottom,
|
||||
left: safeAreaInsetLeft = insets.left,
|
||||
} = safeAreaInsets || {};
|
||||
|
||||
const previousRoute = getPreviousRoute({ route: scene.route });
|
||||
|
||||
let previousScene = scenes[index - 1];
|
||||
|
||||
if (previousRoute) {
|
||||
// The previous scene will be shortly before the current scene in the array
|
||||
// So loop back from current index to avoid looping over the full array
|
||||
for (let j = index - 1; j >= 0; j--) {
|
||||
const s = scenes[j];
|
||||
|
||||
if (s && s.route.key === previousRoute.key) {
|
||||
previousScene = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<MaybeScreen
|
||||
key={route.key}
|
||||
style={StyleSheet.absoluteFill}
|
||||
return (
|
||||
<React.Fragment>
|
||||
{isFloatHeaderAbsolute ? null : floatingHeader}
|
||||
<MaybeScreenContainer
|
||||
enabled={isScreensEnabled}
|
||||
active={isScreenActive}
|
||||
pointerEvents="box-none"
|
||||
style={styles.container}
|
||||
onLayout={this.handleLayout}
|
||||
>
|
||||
<CardContainer
|
||||
index={index}
|
||||
active={index === self.length - 1}
|
||||
focused={focused}
|
||||
closing={closingRouteKeys.includes(route.key)}
|
||||
layout={layout}
|
||||
gesture={gesture}
|
||||
scene={scene}
|
||||
previousScene={previousScene}
|
||||
safeAreaInsetTop={safeAreaInsetTop}
|
||||
safeAreaInsetRight={safeAreaInsetRight}
|
||||
safeAreaInsetBottom={safeAreaInsetBottom}
|
||||
safeAreaInsetLeft={safeAreaInsetLeft}
|
||||
cardOverlay={cardOverlay}
|
||||
cardOverlayEnabled={cardOverlayEnabled}
|
||||
cardShadowEnabled={cardShadowEnabled}
|
||||
cardStyle={cardStyle}
|
||||
onPageChangeStart={onPageChangeStart}
|
||||
onPageChangeConfirm={onPageChangeConfirm}
|
||||
onPageChangeCancel={onPageChangeCancel}
|
||||
gestureResponseDistance={gestureResponseDistance}
|
||||
headerHeight={headerHeights[route.key]}
|
||||
onHeaderHeightChange={this.handleHeaderLayout}
|
||||
getPreviousRoute={getPreviousRoute}
|
||||
getFocusedRoute={this.getFocusedRoute}
|
||||
headerMode={headerMode}
|
||||
headerShown={headerShown}
|
||||
headerTransparent={headerTransparent}
|
||||
renderHeader={renderHeader}
|
||||
renderScene={renderScene}
|
||||
onOpenRoute={onOpenRoute}
|
||||
onCloseRoute={onCloseRoute}
|
||||
onTransitionComplete={onTransitionComplete}
|
||||
onTransitionStart={onTransitionStart}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
gestureEnabled={index !== 0 && getGesturesEnabled({ route })}
|
||||
gestureVelocityImpact={gestureVelocityImpact}
|
||||
{...transitionConfig}
|
||||
/>
|
||||
</MaybeScreen>
|
||||
);
|
||||
})}
|
||||
</MaybeScreenContainer>
|
||||
{headerMode === 'float'
|
||||
? renderHeader({
|
||||
mode: 'float',
|
||||
layout,
|
||||
insets: { top, right, bottom, left },
|
||||
scenes,
|
||||
getPreviousRoute,
|
||||
getFocusedRoute: this.getFocusedRoute,
|
||||
onContentHeightChange: this.handleHeaderLayout,
|
||||
gestureDirection:
|
||||
focusedOptions.gestureDirection !== undefined
|
||||
? focusedOptions.gestureDirection
|
||||
: defaultTransitionPreset.gestureDirection,
|
||||
styleInterpolator:
|
||||
focusedOptions.headerStyleInterpolator !== undefined
|
||||
? focusedOptions.headerStyleInterpolator
|
||||
: defaultTransitionPreset.headerStyleInterpolator,
|
||||
style: styles.floating,
|
||||
})
|
||||
: null}
|
||||
</React.Fragment>
|
||||
{routes.map((route, index, self) => {
|
||||
const focused = focusedRoute.key === route.key;
|
||||
const gesture = gestures[route.key];
|
||||
const scene = scenes[index];
|
||||
|
||||
const isScreenActive = scene.progress.next
|
||||
? scene.progress.next.interpolate({
|
||||
inputRange: [0, 1 - EPSILON, 1],
|
||||
outputRange: [1, 1, 0],
|
||||
extrapolate: 'clamp',
|
||||
})
|
||||
: 1;
|
||||
|
||||
const {
|
||||
safeAreaInsets,
|
||||
headerShown = isParentHeaderShown === false,
|
||||
headerTransparent,
|
||||
cardShadowEnabled,
|
||||
cardOverlayEnabled,
|
||||
cardOverlay,
|
||||
cardStyle,
|
||||
animationEnabled,
|
||||
gestureResponseDistance,
|
||||
gestureVelocityImpact,
|
||||
gestureDirection = defaultTransitionPreset.gestureDirection,
|
||||
transitionSpec = defaultTransitionPreset.transitionSpec,
|
||||
cardStyleInterpolator = animationEnabled === false
|
||||
? forNoAnimationCard
|
||||
: defaultTransitionPreset.cardStyleInterpolator,
|
||||
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
|
||||
} = scene.descriptor
|
||||
? scene.descriptor.options
|
||||
: ({} as StackNavigationOptions);
|
||||
|
||||
let transitionConfig = {
|
||||
gestureDirection,
|
||||
transitionSpec,
|
||||
cardStyleInterpolator,
|
||||
headerStyleInterpolator,
|
||||
};
|
||||
|
||||
// When a screen is not the last, it should use next screen's transition config
|
||||
// Many transitions also animate the previous screen, so using 2 different transitions doesn't look right
|
||||
// For example combining a slide and a modal transition would look wrong otherwise
|
||||
// With this approach, combining different transition styles in the same navigator mostly looks right
|
||||
// This will still be broken when 2 transitions have different idle state (e.g. modal presentation),
|
||||
// but majority of the transitions look alright
|
||||
if (index !== self.length - 1) {
|
||||
const nextScene = scenes[index + 1];
|
||||
|
||||
if (nextScene) {
|
||||
const {
|
||||
animationEnabled,
|
||||
gestureDirection = defaultTransitionPreset.gestureDirection,
|
||||
transitionSpec = defaultTransitionPreset.transitionSpec,
|
||||
cardStyleInterpolator = animationEnabled === false
|
||||
? forNoAnimationCard
|
||||
: defaultTransitionPreset.cardStyleInterpolator,
|
||||
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
|
||||
} = nextScene.descriptor
|
||||
? nextScene.descriptor.options
|
||||
: ({} as StackNavigationOptions);
|
||||
|
||||
transitionConfig = {
|
||||
gestureDirection,
|
||||
transitionSpec,
|
||||
cardStyleInterpolator,
|
||||
headerStyleInterpolator,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
top: safeAreaInsetTop = insets.top,
|
||||
right: safeAreaInsetRight = insets.right,
|
||||
bottom: safeAreaInsetBottom = insets.bottom,
|
||||
left: safeAreaInsetLeft = insets.left,
|
||||
} = safeAreaInsets || {};
|
||||
|
||||
const headerHeight =
|
||||
headerMode !== 'none' && headerShown !== false
|
||||
? headerHeights[route.key]
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<MaybeScreen
|
||||
key={route.key}
|
||||
style={StyleSheet.absoluteFill}
|
||||
enabled={isScreensEnabled}
|
||||
active={isScreenActive}
|
||||
pointerEvents="box-none"
|
||||
>
|
||||
<CardContainer
|
||||
index={index}
|
||||
active={index === self.length - 1}
|
||||
focused={focused}
|
||||
closing={closingRouteKeys.includes(route.key)}
|
||||
layout={layout}
|
||||
gesture={gesture}
|
||||
scene={scene}
|
||||
safeAreaInsetTop={safeAreaInsetTop}
|
||||
safeAreaInsetRight={safeAreaInsetRight}
|
||||
safeAreaInsetBottom={safeAreaInsetBottom}
|
||||
safeAreaInsetLeft={safeAreaInsetLeft}
|
||||
cardOverlay={cardOverlay}
|
||||
cardOverlayEnabled={cardOverlayEnabled}
|
||||
cardShadowEnabled={cardShadowEnabled}
|
||||
cardStyle={cardStyle}
|
||||
onPageChangeStart={onPageChangeStart}
|
||||
onPageChangeConfirm={onPageChangeConfirm}
|
||||
onPageChangeCancel={onPageChangeCancel}
|
||||
gestureResponseDistance={gestureResponseDistance}
|
||||
headerHeight={headerHeight}
|
||||
onHeaderHeightChange={this.handleHeaderLayout}
|
||||
getPreviousScene={this.getPreviousScene}
|
||||
getFocusedRoute={this.getFocusedRoute}
|
||||
mode={mode}
|
||||
headerMode={headerMode}
|
||||
headerShown={headerShown}
|
||||
hasAbsoluteHeader={
|
||||
isFloatHeaderAbsolute && !headerTransparent
|
||||
}
|
||||
renderHeader={renderHeader}
|
||||
renderScene={renderScene}
|
||||
onOpenRoute={onOpenRoute}
|
||||
onCloseRoute={onCloseRoute}
|
||||
onTransitionStart={onTransitionStart}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
gestureEnabled={
|
||||
index !== 0 && getGesturesEnabled({ route })
|
||||
}
|
||||
gestureVelocityImpact={gestureVelocityImpact}
|
||||
{...transitionConfig}
|
||||
/>
|
||||
</MaybeScreen>
|
||||
);
|
||||
})}
|
||||
</MaybeScreenContainer>
|
||||
{isFloatHeaderAbsolute ? floatingHeader : null}
|
||||
</React.Fragment>
|
||||
);
|
||||
}}
|
||||
</HeaderShownContext.Consumer>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -601,12 +618,14 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
floating: {
|
||||
absolute: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
floating: {
|
||||
zIndex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
196
packages/stack/src/vendor/views/Stack/StackView.tsx
vendored
196
packages/stack/src/vendor/views/Stack/StackView.tsx
vendored
@@ -1,20 +1,21 @@
|
||||
import * as React from 'react';
|
||||
import { View, Platform, StyleSheet } from 'react-native';
|
||||
import { SafeAreaConsumer, EdgeInsets } from 'react-native-safe-area-context';
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
import {
|
||||
StackActions,
|
||||
NavigationState as StackNavigationState,
|
||||
NavigationActions,
|
||||
SceneView,
|
||||
} from 'react-navigation';
|
||||
|
||||
import { GestureHandlerRootView } from '../GestureHandler';
|
||||
import CardStack from './CardStack';
|
||||
import KeyboardManager from '../KeyboardManager';
|
||||
import HeaderContainer, {
|
||||
Props as HeaderContainerProps,
|
||||
} from '../Header/HeaderContainer';
|
||||
import SafeAreaProviderCompat from '../SafeAreaProviderCompat';
|
||||
import {
|
||||
import type {
|
||||
Route,
|
||||
StackNavigationHelpers,
|
||||
StackNavigationConfig,
|
||||
@@ -48,31 +49,63 @@ type State = {
|
||||
|
||||
const GestureHandlerWrapper = GestureHandlerRootView ?? View;
|
||||
|
||||
/**
|
||||
* Compare two arrays with primitive values as the content.
|
||||
* We need to make sure that both values and order match.
|
||||
*/
|
||||
const isArrayEqual = (a: any[], b: any[]) =>
|
||||
a.length === b.length && a.every((it, index) => it === b[index]);
|
||||
|
||||
export default class StackView extends React.Component<Props, State> {
|
||||
static getDerivedStateFromProps(
|
||||
props: Readonly<Props>,
|
||||
state: Readonly<State>
|
||||
) {
|
||||
// If there was no change in routes, we don't need to compute anything
|
||||
if (props.state.routes === state.previousRoutes && state.routes.length) {
|
||||
if (props.descriptors !== state.previousDescriptors) {
|
||||
const descriptors = state.routes.reduce<StackDescriptorMap>(
|
||||
(acc, route) => {
|
||||
acc[route.key] =
|
||||
props.descriptors[route.key] || state.descriptors[route.key];
|
||||
if (
|
||||
(props.state.routes === state.previousRoutes ||
|
||||
isArrayEqual(
|
||||
props.state.routes.map((r) => r.key),
|
||||
state.previousRoutes.map((r) => r.key)
|
||||
)) &&
|
||||
state.routes.length
|
||||
) {
|
||||
let routes = state.routes;
|
||||
let previousRoutes = state.previousRoutes;
|
||||
let descriptors = props.descriptors;
|
||||
let previousDescriptors = state.previousDescriptors;
|
||||
|
||||
if (props.descriptors !== state.previousDescriptors) {
|
||||
descriptors = state.routes.reduce<StackDescriptorMap>((acc, route) => {
|
||||
acc[route.key] =
|
||||
props.descriptors[route.key] || state.descriptors[route.key];
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
previousDescriptors = props.descriptors;
|
||||
}
|
||||
|
||||
if (props.state.routes !== state.previousRoutes) {
|
||||
// if any route objects have changed, we should update them
|
||||
const map = props.state.routes.reduce<Record<string, Route<string>>>(
|
||||
(acc, route) => {
|
||||
acc[route.key] = route;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
return {
|
||||
previousDescriptors: props.descriptors,
|
||||
descriptors,
|
||||
};
|
||||
routes = state.routes.map((route) => map[route.key] || route);
|
||||
previousRoutes = props.state.routes;
|
||||
}
|
||||
|
||||
return null;
|
||||
return {
|
||||
routes,
|
||||
previousRoutes,
|
||||
descriptors,
|
||||
previousDescriptors,
|
||||
};
|
||||
}
|
||||
|
||||
// Here we determine which routes were added or removed to animate them
|
||||
@@ -117,7 +150,7 @@ export default class StackView extends React.Component<Props, State> {
|
||||
// We only need to animate routes if the focused route changed
|
||||
// Animating previous routes won't be visible coz the focused route is on top of everything
|
||||
|
||||
if (!previousRoutes.find((r) => r.key === nextFocusedRoute.key)) {
|
||||
if (!previousRoutes.some((r) => r.key === nextFocusedRoute.key)) {
|
||||
// A new route has come to the focus, we treat this as a push
|
||||
// A replace can also trigger this, the animation should look like push
|
||||
|
||||
@@ -136,7 +169,7 @@ export default class StackView extends React.Component<Props, State> {
|
||||
(key) => key !== nextFocusedRoute.key
|
||||
);
|
||||
|
||||
if (!routes.find((r) => r.key === previousFocusedRoute.key)) {
|
||||
if (!routes.some((r) => r.key === previousFocusedRoute.key)) {
|
||||
// The previous focused route isn't present in state, we treat this as a replace
|
||||
|
||||
openingRouteKeys = openingRouteKeys.filter(
|
||||
@@ -176,7 +209,7 @@ export default class StackView extends React.Component<Props, State> {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!routes.find((r) => r.key === previousFocusedRoute.key)) {
|
||||
} else if (!routes.some((r) => r.key === previousFocusedRoute.key)) {
|
||||
// The previously focused route was removed, we treat this as a pop
|
||||
|
||||
if (
|
||||
@@ -308,52 +341,62 @@ export default class StackView extends React.Component<Props, State> {
|
||||
return <HeaderContainer {...props} />;
|
||||
};
|
||||
|
||||
private handleTransitionComplete = ({ route }: { route: Route<string> }) => {
|
||||
private handleTransitionComplete = () => {
|
||||
const { state, navigation } = this.props;
|
||||
|
||||
if (state.isTransitioning) {
|
||||
navigation.dispatch(
|
||||
StackActions.completeTransition({
|
||||
key: navigation.state.key,
|
||||
toChildKey: route.key,
|
||||
toChildKey: state.routes[state.index].key,
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
private handleOpenRoute = ({ route }: { route: Route<string> }) => {
|
||||
this.handleTransitionComplete({ route });
|
||||
this.setState((state) => ({
|
||||
routes: state.replacingRouteKeys.length
|
||||
? state.routes.filter((r) => !state.replacingRouteKeys.includes(r.key))
|
||||
: state.routes,
|
||||
openingRouteKeys: state.openingRouteKeys.filter(
|
||||
(key) => key !== route.key
|
||||
),
|
||||
closingRouteKeys: state.closingRouteKeys.filter(
|
||||
(key) => key !== route.key
|
||||
),
|
||||
replacingRouteKeys: [],
|
||||
}));
|
||||
const { state, navigation } = this.props;
|
||||
const { closingRouteKeys, replacingRouteKeys } = this.state;
|
||||
|
||||
this.handleTransitionComplete();
|
||||
|
||||
if (
|
||||
closingRouteKeys.some((key) => key === route.key) &&
|
||||
replacingRouteKeys.every((key) => key !== route.key) &&
|
||||
!state.routes.some((r) => r.key === route.key)
|
||||
) {
|
||||
// If route isn't present in current state, but was closing, assume that a close animation was cancelled
|
||||
// So we need to add this route back to the state
|
||||
navigation.dispatch(NavigationActions.navigate(route));
|
||||
} else {
|
||||
this.setState((state) => ({
|
||||
routes: state.replacingRouteKeys.length
|
||||
? state.routes.filter(
|
||||
(r) => !state.replacingRouteKeys.includes(r.key)
|
||||
)
|
||||
: state.routes,
|
||||
openingRouteKeys: state.openingRouteKeys.filter(
|
||||
(key) => key !== route.key
|
||||
),
|
||||
closingRouteKeys: state.closingRouteKeys.filter(
|
||||
(key) => key !== route.key
|
||||
),
|
||||
replacingRouteKeys: [],
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
private handleCloseRoute = ({ route }: { route: Route<string> }) => {
|
||||
const { state, navigation } = this.props;
|
||||
|
||||
if (state.routes.find((r) => r.key === route.key)) {
|
||||
if (state.routes.some((r) => r.key === route.key)) {
|
||||
// If a route exists in state, trigger a pop
|
||||
// This will happen in when the route was closed from the card component
|
||||
// e.g. When the close animation triggered from a gesture ends
|
||||
// @ts-ignore
|
||||
navigation.dispatch(StackActions.pop({ key: route.key, prune: false }));
|
||||
} else {
|
||||
// While closing route we need to point to the previous one assuming that
|
||||
// this previous one in routes array
|
||||
const index = this.state.routes.findIndex((r) => r.key === route.key);
|
||||
|
||||
this.handleTransitionComplete({
|
||||
route: this.state.routes[Math.max(index - 1, 0)],
|
||||
});
|
||||
this.handleTransitionComplete();
|
||||
|
||||
// We need to clean up any state tracking the route and pop it immediately
|
||||
this.setState((state) => ({
|
||||
@@ -397,6 +440,11 @@ export default class StackView extends React.Component<Props, State> {
|
||||
navigation,
|
||||
keyboardHandlingEnabled,
|
||||
mode = 'card',
|
||||
headerMode = mode === 'card' && Platform.OS === 'ios'
|
||||
? 'float'
|
||||
: 'screen',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
descriptors: _,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
@@ -407,43 +455,41 @@ export default class StackView extends React.Component<Props, State> {
|
||||
closingRouteKeys,
|
||||
} = this.state;
|
||||
|
||||
const headerMode =
|
||||
mode !== 'modal' && Platform.OS === 'ios' ? 'float' : 'screen';
|
||||
|
||||
return (
|
||||
<GestureHandlerWrapper style={styles.container}>
|
||||
<SafeAreaProviderCompat>
|
||||
<SafeAreaConsumer>
|
||||
{(insets) => (
|
||||
<KeyboardManager enabled={keyboardHandlingEnabled !== false}>
|
||||
{(props) => (
|
||||
<CardStack
|
||||
mode={mode}
|
||||
insets={insets as EdgeInsets}
|
||||
getPreviousRoute={this.getPreviousRoute}
|
||||
getGesturesEnabled={this.getGesturesEnabled}
|
||||
routes={routes}
|
||||
openingRouteKeys={openingRouteKeys}
|
||||
closingRouteKeys={closingRouteKeys}
|
||||
onOpenRoute={this.handleOpenRoute}
|
||||
onCloseRoute={this.handleCloseRoute}
|
||||
onTransitionComplete={this.handleTransitionComplete}
|
||||
onTransitionStart={this.handleTransitionStart}
|
||||
onTransitionEnd={this.handleTransitionEnd}
|
||||
renderHeader={this.renderHeader}
|
||||
renderScene={this.renderScene}
|
||||
headerMode={headerMode}
|
||||
state={state}
|
||||
descriptors={descriptors}
|
||||
{...rest}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
</KeyboardManager>
|
||||
)}
|
||||
</SafeAreaConsumer>
|
||||
</SafeAreaProviderCompat>
|
||||
</GestureHandlerWrapper>
|
||||
<>
|
||||
<GestureHandlerWrapper style={styles.container}>
|
||||
<SafeAreaProviderCompat>
|
||||
<SafeAreaConsumer>
|
||||
{(insets) => (
|
||||
<KeyboardManager enabled={keyboardHandlingEnabled !== false}>
|
||||
{(props) => (
|
||||
<CardStack
|
||||
mode={mode}
|
||||
insets={insets as EdgeInsets}
|
||||
getPreviousRoute={this.getPreviousRoute}
|
||||
getGesturesEnabled={this.getGesturesEnabled}
|
||||
routes={routes}
|
||||
openingRouteKeys={openingRouteKeys}
|
||||
closingRouteKeys={closingRouteKeys}
|
||||
onOpenRoute={this.handleOpenRoute}
|
||||
onCloseRoute={this.handleCloseRoute}
|
||||
onTransitionStart={this.handleTransitionStart}
|
||||
onTransitionEnd={this.handleTransitionEnd}
|
||||
renderHeader={this.renderHeader}
|
||||
renderScene={this.renderScene}
|
||||
headerMode={headerMode}
|
||||
state={state}
|
||||
descriptors={descriptors}
|
||||
{...rest}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
</KeyboardManager>
|
||||
)}
|
||||
</SafeAreaConsumer>
|
||||
</SafeAreaProviderCompat>
|
||||
</GestureHandlerWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
81
packages/stack/src/vendor/views/TouchableItem.native.tsx
vendored
Normal file
81
packages/stack/src/vendor/views/TouchableItem.native.tsx
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* TouchableItem renders a touchable that looks native on both iOS and Android.
|
||||
*
|
||||
* It provides an abstraction on top of TouchableNativeFeedback and
|
||||
* TouchableOpacity.
|
||||
*
|
||||
* On iOS you can pass the props of TouchableOpacity, on Android pass the props
|
||||
* of TouchableNativeFeedback.
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Platform,
|
||||
TouchableNativeFeedback,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
ViewProps,
|
||||
} from 'react-native';
|
||||
|
||||
import BorderlessButton from './BorderlessButton';
|
||||
|
||||
export type Props = ViewProps & {
|
||||
pressColor: string;
|
||||
disabled?: boolean;
|
||||
borderless?: boolean;
|
||||
delayPressIn?: number;
|
||||
onPress?: () => void;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const ANDROID_VERSION_LOLLIPOP = 21;
|
||||
|
||||
export class TouchableItem extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
borderless: false,
|
||||
pressColor: 'rgba(0, 0, 0, .32)',
|
||||
};
|
||||
|
||||
render() {
|
||||
/*
|
||||
* TouchableNativeFeedback.Ripple causes a crash on old Android versions,
|
||||
* therefore only enable it on Android Lollipop and above.
|
||||
*
|
||||
* All touchables on Android should have the ripple effect according to
|
||||
* platform design guidelines.
|
||||
* We need to pass the background prop to specify a borderless ripple effect.
|
||||
*/
|
||||
if (
|
||||
Platform.OS === 'android' &&
|
||||
Platform.Version >= ANDROID_VERSION_LOLLIPOP
|
||||
) {
|
||||
const { style, pressColor, borderless, children, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
<TouchableNativeFeedback
|
||||
{...rest}
|
||||
useForeground={TouchableNativeFeedback.canUseNativeForeground()}
|
||||
background={TouchableNativeFeedback.Ripple(pressColor, borderless)}
|
||||
>
|
||||
<View style={style}>{React.Children.only(children)}</View>
|
||||
</TouchableNativeFeedback>
|
||||
);
|
||||
} else if (Platform.OS === 'ios') {
|
||||
return (
|
||||
<BorderlessButton
|
||||
hitSlop={{ top: 10, bottom: 10, right: 10, left: 10 }}
|
||||
disallowInterruption
|
||||
enabled={!this.props.disabled}
|
||||
{...this.props}
|
||||
>
|
||||
{this.props.children}
|
||||
</BorderlessButton>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<TouchableOpacity {...this.props}>
|
||||
{this.props.children}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +1,3 @@
|
||||
/**
|
||||
* TouchableItem renders a touchable that looks native on both iOS and Android.
|
||||
*
|
||||
* It provides an abstraction on top of TouchableNativeFeedback and
|
||||
* TouchableOpacity.
|
||||
*
|
||||
* On iOS you can pass the props of TouchableOpacity, on Android pass the props
|
||||
* of TouchableNativeFeedback.
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Platform,
|
||||
TouchableNativeFeedback,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
ViewProps,
|
||||
} from 'react-native';
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
|
||||
import BorderlessButton from './BorderlessButton';
|
||||
|
||||
type Props = ViewProps & {
|
||||
pressColor: string;
|
||||
disabled?: boolean;
|
||||
borderless?: boolean;
|
||||
delayPressIn?: number;
|
||||
onPress?: () => void;
|
||||
};
|
||||
|
||||
const ANDROID_VERSION_LOLLIPOP = 21;
|
||||
|
||||
export default class TouchableItem extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
borderless: false,
|
||||
pressColor: 'rgba(0, 0, 0, .32)',
|
||||
};
|
||||
|
||||
render() {
|
||||
/*
|
||||
* TouchableNativeFeedback.Ripple causes a crash on old Android versions,
|
||||
* therefore only enable it on Android Lollipop and above.
|
||||
*
|
||||
* All touchables on Android should have the ripple effect according to
|
||||
* platform design guidelines.
|
||||
* We need to pass the background prop to specify a borderless ripple effect.
|
||||
*/
|
||||
if (
|
||||
Platform.OS === 'android' &&
|
||||
Platform.Version >= ANDROID_VERSION_LOLLIPOP
|
||||
) {
|
||||
const { style, pressColor, borderless, children, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
<TouchableNativeFeedback
|
||||
{...rest}
|
||||
useForeground={TouchableNativeFeedback.canUseNativeForeground()}
|
||||
background={TouchableNativeFeedback.Ripple(pressColor, borderless)}
|
||||
>
|
||||
<View style={style}>{React.Children.only(children)}</View>
|
||||
</TouchableNativeFeedback>
|
||||
);
|
||||
} else if (Platform.OS === 'ios') {
|
||||
return (
|
||||
<BorderlessButton
|
||||
hitSlop={{ top: 10, bottom: 10, right: 10, left: 10 }}
|
||||
disallowInterruption
|
||||
enabled={!this.props.disabled}
|
||||
{...this.props}
|
||||
>
|
||||
{this.props.children}
|
||||
</BorderlessButton>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<TouchableOpacity {...this.props}>
|
||||
{this.props.children}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
export const TouchableItem = (TouchableOpacity as any) as typeof import('./TouchableItem.native').TouchableItem;
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
import validateDeprecatedOptions from '../utils/validateDeprecatedOptions';
|
||||
import validateDeprecatedConfig from '../utils/validateDeprecatedConfig';
|
||||
import StackViewBase from '../vendor/views/Stack/StackView';
|
||||
import {
|
||||
import type {
|
||||
StackNavigationHelpers,
|
||||
StackNavigationConfig,
|
||||
StackDescriptorMap,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": "./tsconfig",
|
||||
"compilerOptions": {
|
||||
"paths": {}
|
||||
"paths": {},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{ "path": "../react-navigation" }
|
||||
],
|
||||
"compilerOptions": {
|
||||
"outDir": "./lib/typescript"
|
||||
"outDir": "./lib/typescript",
|
||||
"importsNotUsedAsValues": "error",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,65 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.9.0](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.13...react-navigation-tabs@2.9.0) (2020-06-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* sync latest stack and upgrade typescript ([454e05b](https://github.com/react-navigation/tabs/commit/454e05b02ec97f053b381fdc801df899d8c93cb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.13](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.12...react-navigation-tabs@2.8.13) (2020-04-30)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.12](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.11...react-navigation-tabs@2.8.12) (2020-04-22)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.11](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.10...react-navigation-tabs@2.8.11) (2020-04-02)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.10](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.9...react-navigation-tabs@2.8.10) (2020-03-31)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.9](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.7...react-navigation-tabs@2.8.9) (2020-03-28)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.8](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.7...react-navigation-tabs@2.8.8) (2020-03-27)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.7](https://github.com/react-navigation/tabs/compare/react-navigation-tabs@2.8.6...react-navigation-tabs@2.8.7) (2020-03-22)
|
||||
|
||||
**Note:** Version bump only for package react-navigation-tabs
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-navigation-tabs",
|
||||
"version": "2.8.7",
|
||||
"version": "2.9.0",
|
||||
"description": "Tab Navigation components for React Navigation",
|
||||
"main": "lib/commonjs/index.js",
|
||||
"module": "lib/module/index.js",
|
||||
@@ -42,7 +42,7 @@
|
||||
"dependencies": {
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"react-lifecycles-compat": "^3.0.4",
|
||||
"react-native-safe-area-view": "^0.14.8",
|
||||
"react-native-safe-area-view": "^0.14.9",
|
||||
"react-native-tab-view": "^2.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -56,8 +56,8 @@
|
||||
"react-native-gesture-handler": "^1.6.0",
|
||||
"react-native-reanimated": "^1.2.0",
|
||||
"react-native-tab-view": "^2.13.0",
|
||||
"react-navigation": "^4.3.3",
|
||||
"typescript": "~3.7.5"
|
||||
"react-navigation": "^4.4.0",
|
||||
"typescript": "^3.9.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"jsx": "react",
|
||||
"lib": ["esnext"],
|
||||
"lib": ["esnext", "dom"],
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
Reference in New Issue
Block a user