2016-02-25 22:50:28 +00:00
|
|
|
/**
|
2016-03-15 22:58:38 +00:00
|
|
|
* Copyright (c) 2013-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
2016-02-25 22:50:28 +00:00
|
|
|
*
|
|
|
|
* Facebook, Inc. ("Facebook") owns all right, title and interest, including
|
|
|
|
* all intellectual property and other proprietary rights, in and to the React
|
|
|
|
* Native CustomComponents software (the "Software"). Subject to your
|
|
|
|
* compliance with these terms, you are hereby granted a non-exclusive,
|
|
|
|
* worldwide, royalty-free copyright license to (1) use and copy the Software;
|
|
|
|
* and (2) reproduce and distribute the Software as part of your own software
|
|
|
|
* ("Your Software"). Facebook reserves all rights not expressly granted to
|
|
|
|
* you in this license agreement.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
|
|
|
|
* OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
|
|
|
|
* EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
|
|
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* @providesModule NavigationLegacyNavigator
|
|
|
|
* @flow
|
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
|
2016-03-11 01:16:19 +00:00
|
|
|
const NavigationAnimatedValueSubscription = require('NavigationAnimatedValueSubscription');
|
2016-03-10 22:22:26 +00:00
|
|
|
const NavigationAnimatedView = require('NavigationAnimatedView');
|
|
|
|
const NavigationCard = require('NavigationCard');
|
2016-03-15 22:58:38 +00:00
|
|
|
const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator');
|
2016-03-10 22:22:26 +00:00
|
|
|
const NavigationContext = require('NavigationContext');
|
|
|
|
const NavigationLegacyNavigatorRouteStack = require('NavigationLegacyNavigatorRouteStack');
|
2016-03-15 22:58:38 +00:00
|
|
|
const NavigationLinearPanResponder = require('NavigationLinearPanResponder');
|
2016-03-10 22:22:26 +00:00
|
|
|
const NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar');
|
|
|
|
const NavigatorNavigationBar = require('NavigatorNavigationBar');
|
|
|
|
const NavigatorSceneConfigs = require('NavigatorSceneConfigs');
|
|
|
|
const React = require('react-native');
|
|
|
|
const ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin');
|
|
|
|
|
|
|
|
const invariant = require('fbjs/lib/invariant');
|
|
|
|
const guid = require('guid');
|
|
|
|
|
|
|
|
import type {
|
|
|
|
NavigationSceneRenderer,
|
|
|
|
NavigationSceneRendererProps,
|
|
|
|
} from 'NavigationTypeDefinition';
|
|
|
|
|
|
|
|
type Props = {
|
|
|
|
configureScene: any,
|
|
|
|
initialRoute: any,
|
|
|
|
initialRouteStack: any,
|
|
|
|
renderScene: any,
|
2016-03-11 01:16:19 +00:00
|
|
|
navigationBar: any,
|
|
|
|
navigationBarNavigator: any,
|
|
|
|
renderScene: any,
|
2016-03-10 22:22:26 +00:00
|
|
|
style: any,
|
|
|
|
};
|
|
|
|
|
2016-03-11 01:16:19 +00:00
|
|
|
type State = {
|
|
|
|
presentedIndex: number,
|
|
|
|
routeStack: Array<any>,
|
|
|
|
};
|
|
|
|
|
2016-03-10 22:22:26 +00:00
|
|
|
function getConfigPopDirection(config: any): ?string {
|
|
|
|
if (config && config.gestures && config.gestures.pop) {
|
|
|
|
const direction = config.gestures.pop.direction;
|
|
|
|
return direction ? String(direction) : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const RouteStack = NavigationLegacyNavigatorRouteStack;
|
|
|
|
|
2016-02-25 22:50:28 +00:00
|
|
|
/**
|
2016-03-10 22:22:26 +00:00
|
|
|
* NavigationLegacyNavigator is meant to replace Navigator seemlessly with
|
|
|
|
* minimum API changes.
|
|
|
|
*
|
|
|
|
* While the APIs remain compatible with Navigator, it is built with good
|
|
|
|
* intention by using the new Navigation API such as
|
|
|
|
* `NavigationAnimatedView`...etc.
|
2016-02-25 22:50:28 +00:00
|
|
|
*/
|
2016-03-10 22:22:26 +00:00
|
|
|
class NavigationLegacyNavigator extends React.Component<any, Props, State> {
|
2016-03-11 01:16:19 +00:00
|
|
|
static BreadcrumbNavigationBar: any;
|
2016-03-10 22:22:26 +00:00
|
|
|
static NavigationBar: any;
|
|
|
|
static SceneConfigs: any;
|
|
|
|
|
2016-03-11 01:16:19 +00:00
|
|
|
_key: string;
|
|
|
|
_navigationBarRef: any;
|
|
|
|
_onNavigationBarRef: (ref: any) => void;
|
|
|
|
_onPositionChange: (data: {value: number}) => void;
|
|
|
|
_positionListener: ?NavigationAnimatedValueSubscription;
|
|
|
|
_previousStack: NavigationLegacyNavigatorRouteStack;
|
2016-03-10 22:22:26 +00:00
|
|
|
_renderCard: NavigationSceneRenderer;
|
|
|
|
_renderHeader: NavigationSceneRenderer;
|
|
|
|
_renderScene: NavigationSceneRenderer;
|
2016-03-11 01:16:19 +00:00
|
|
|
_stack: NavigationLegacyNavigatorRouteStack;
|
2016-03-10 22:22:26 +00:00
|
|
|
|
|
|
|
navigationContext: NavigationContext;
|
2016-03-11 01:16:19 +00:00
|
|
|
props: Props;
|
|
|
|
state: State;
|
2016-03-10 22:22:26 +00:00
|
|
|
|
|
|
|
constructor(props: Props, context: any) {
|
|
|
|
super(props, context);
|
|
|
|
|
|
|
|
this.navigationContext = new NavigationContext();
|
|
|
|
|
|
|
|
const stack = this._getInitialRouteStack();
|
2016-03-11 01:16:19 +00:00
|
|
|
|
|
|
|
// Unfortunately, due to historical reasons, the `state` has been exposed
|
|
|
|
// as public members of the navigator, therefore we'd keep private state
|
|
|
|
// as private members.
|
|
|
|
this._key = guid();
|
|
|
|
this._previousStack = stack;
|
|
|
|
this._stack = stack;
|
|
|
|
|
2016-03-10 22:22:26 +00:00
|
|
|
this.state = {
|
2016-03-11 01:16:19 +00:00
|
|
|
routeStack: stack.toArray(),
|
|
|
|
presentedIndex: stack.index,
|
2016-03-10 22:22:26 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
jumpTo(route: any): void {
|
2016-03-11 01:16:19 +00:00
|
|
|
const index = this._stack.indexOf(route);
|
2016-03-10 22:22:26 +00:00
|
|
|
invariant(
|
|
|
|
index > -1,
|
|
|
|
'Cannot jump to route that is not in the route stack'
|
|
|
|
);
|
|
|
|
this._jumpToIndex(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
jumpForward(): void {
|
2016-03-11 01:16:19 +00:00
|
|
|
this._jumpToIndex(this._stack.index + 1);
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
jumpBack(): void {
|
2016-03-11 01:16:19 +00:00
|
|
|
this._jumpToIndex(this._stack.index - 1);
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
push(route: any): void {
|
2016-03-11 01:16:19 +00:00
|
|
|
this._applyStack(this._stack.push(route));
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pop(): void {
|
2016-03-11 01:16:19 +00:00
|
|
|
const stack = this._stack;
|
2016-03-10 22:22:26 +00:00
|
|
|
if (stack.size > 1) {
|
2016-03-11 01:16:19 +00:00
|
|
|
this._applyStack(stack.pop());
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
replaceAtIndex(route: any, index: number): void {
|
2016-03-11 01:16:19 +00:00
|
|
|
const stack = this._stack;
|
2016-03-10 22:22:26 +00:00
|
|
|
|
|
|
|
if (index < 0) {
|
|
|
|
index += stack.size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index >= stack.size) {
|
|
|
|
// Nothing to replace.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-03-11 01:16:19 +00:00
|
|
|
this._applyStack(stack.replaceAtIndex(index, route));
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
replace(route: any): void {
|
2016-03-11 01:16:19 +00:00
|
|
|
this.replaceAtIndex(route, this._stack.index);
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
replacePrevious(route: any): void {
|
2016-03-11 01:16:19 +00:00
|
|
|
this.replaceAtIndex(route, this._stack.index - 1);
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
popToTop(): void {
|
2016-03-11 01:16:19 +00:00
|
|
|
this._applyStack(this._stack.slice(0, 1));
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
popToRoute(route: any): void {
|
2016-03-11 01:16:19 +00:00
|
|
|
const stack = this._stack;
|
2016-03-10 22:22:26 +00:00
|
|
|
const nextIndex = stack.indexOf(route);
|
|
|
|
invariant(
|
|
|
|
nextIndex > -1,
|
|
|
|
'Calling popToRoute for a route that doesn\'t exist!'
|
|
|
|
);
|
2016-03-11 01:16:19 +00:00
|
|
|
this._applyStack(stack.slice(0, nextIndex + 1));
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
replacePreviousAndPop(route: any): void {
|
2016-03-11 01:16:19 +00:00
|
|
|
const stack = this._stack;
|
2016-03-10 22:22:26 +00:00
|
|
|
const nextIndex = stack.index - 1;
|
|
|
|
if (nextIndex < 0) {
|
|
|
|
return;
|
|
|
|
}
|
2016-03-11 01:16:19 +00:00
|
|
|
this._applyStack(stack.replaceAtIndex(nextIndex, route).pop());
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
resetTo(route: any): void {
|
|
|
|
invariant(!!route, 'Must supply route');
|
2016-03-11 01:16:19 +00:00
|
|
|
this._applyStack(this._stack.slice(0).replaceAtIndex(0, route));
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
immediatelyResetRouteStack(routes: Array<any>): void {
|
|
|
|
const index = routes.length - 1;
|
|
|
|
const stack = new RouteStack(index, routes);
|
2016-03-11 01:16:19 +00:00
|
|
|
// Immediately blow away all current scenes with a new key.
|
|
|
|
this._key = guid();
|
|
|
|
this._applyStack(stack);
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getCurrentRoutes(): Array<any> {
|
2016-03-11 01:16:19 +00:00
|
|
|
return this._stack.toArray();
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_jumpToIndex(index: number): void {
|
2016-03-11 01:16:19 +00:00
|
|
|
const stack = this._stack;
|
2016-03-10 22:22:26 +00:00
|
|
|
if (index < 0 || index >= stack.size) {
|
|
|
|
return;
|
|
|
|
}
|
2016-03-11 01:16:19 +00:00
|
|
|
this._applyStack(stack.jumpToIndex(index));
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Lyfe cycle and private methods below.
|
|
|
|
|
|
|
|
shouldComponentUpdate(nextProps: Object, nextState: Object): boolean {
|
|
|
|
return ReactComponentWithPureRenderMixin.shouldComponentUpdate.call(
|
|
|
|
this,
|
|
|
|
nextProps,
|
|
|
|
nextState
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillMount(): void {
|
2016-03-11 01:16:19 +00:00
|
|
|
this._onNavigationBarRef = this._onNavigationBarRef.bind(this);
|
|
|
|
this._onPositionChange = this._onPositionChange.bind(this);
|
2016-03-10 22:22:26 +00:00
|
|
|
this._renderCard = this._renderCard.bind(this);
|
|
|
|
this._renderHeader = this._renderHeader.bind(this);
|
|
|
|
this._renderScene = this._renderScene.bind(this);
|
|
|
|
}
|
|
|
|
|
2016-03-11 01:16:19 +00:00
|
|
|
componentWillUnmount(): void {
|
|
|
|
this._positionListener && this._positionListener.remove();
|
|
|
|
}
|
|
|
|
|
2016-03-10 22:22:26 +00:00
|
|
|
render(): ReactElement {
|
|
|
|
return (
|
|
|
|
<NavigationAnimatedView
|
2016-03-11 01:16:19 +00:00
|
|
|
key={'main_' + this._key}
|
|
|
|
navigationState={this._stack.toNavigationState()}
|
2016-03-10 22:22:26 +00:00
|
|
|
renderOverlay={this._renderHeader}
|
|
|
|
renderScene={this._renderCard}
|
|
|
|
style={this.props.style}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
_getInitialRouteStack(): RouteStack {
|
|
|
|
const {initialRouteStack, initialRoute} = this.props;
|
|
|
|
const routes = initialRouteStack || [initialRoute];
|
|
|
|
const index = initialRoute ?
|
|
|
|
routes.indexOf(initialRoute) :
|
|
|
|
routes.length - 1;
|
|
|
|
return new RouteStack(index, routes);
|
|
|
|
}
|
|
|
|
|
|
|
|
_renderHeader(props: NavigationSceneRendererProps): ?ReactElement {
|
2016-03-11 01:16:19 +00:00
|
|
|
this._positionListener && this._positionListener.remove();
|
|
|
|
this._positionListener = new NavigationAnimatedValueSubscription(
|
|
|
|
props.position,
|
|
|
|
this._onPositionChange,
|
|
|
|
);
|
|
|
|
|
|
|
|
const {navigationBar, navigationBarNavigator} = this.props;
|
|
|
|
|
|
|
|
if (!navigationBar) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return React.cloneElement(
|
|
|
|
navigationBar,
|
|
|
|
{
|
|
|
|
ref: this._onNavigationBarRef,
|
|
|
|
navigator: navigationBarNavigator || this,
|
|
|
|
navState: {...this.state},
|
|
|
|
}
|
|
|
|
);
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_renderCard(props: NavigationSceneRendererProps): ReactElement {
|
2016-03-15 22:58:38 +00:00
|
|
|
const {scene} = props;
|
2016-03-10 22:22:26 +00:00
|
|
|
const {configureScene} = this.props;
|
|
|
|
|
2016-03-15 22:58:38 +00:00
|
|
|
let isVertical = false;
|
|
|
|
|
2016-03-10 22:22:26 +00:00
|
|
|
if (configureScene) {
|
2016-03-15 22:58:38 +00:00
|
|
|
const route = RouteStack.getRouteByNavigationState(scene.navigationState);
|
2016-03-11 01:16:19 +00:00
|
|
|
const config = configureScene(route, this.state.routeStack);
|
2016-03-15 22:58:38 +00:00
|
|
|
const direction = getConfigPopDirection(config);
|
2016-03-10 22:22:26 +00:00
|
|
|
|
2016-03-15 22:58:38 +00:00
|
|
|
switch (direction) {
|
2016-03-10 22:22:26 +00:00
|
|
|
case 'left-to-right':
|
2016-03-15 22:58:38 +00:00
|
|
|
// default.
|
2016-03-10 22:22:26 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'top-to-bottom':
|
2016-03-15 22:58:38 +00:00
|
|
|
isVertical = true;
|
2016-03-10 22:22:26 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// unsupported config.
|
|
|
|
if (__DEV__) {
|
2016-03-15 22:58:38 +00:00
|
|
|
console.warn('unsupported scene configuration %s', direction);
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-15 22:58:38 +00:00
|
|
|
const style = isVertical ?
|
|
|
|
NavigationCardStackStyleInterpolator.forVertical(props) :
|
|
|
|
NavigationCardStackStyleInterpolator.forHorizontal(props);
|
|
|
|
|
|
|
|
const panHandlers = isVertical ?
|
|
|
|
NavigationLinearPanResponder.forVertical(props) :
|
|
|
|
NavigationLinearPanResponder.forHorizontal(props);
|
|
|
|
|
2016-03-10 22:22:26 +00:00
|
|
|
return (
|
|
|
|
<NavigationCard
|
|
|
|
{...props}
|
2016-03-15 22:58:38 +00:00
|
|
|
key={'card_' + props.scene.navigationState.key}
|
|
|
|
panHandlers={panHandlers}
|
2016-03-10 22:22:26 +00:00
|
|
|
renderScene={this._renderScene}
|
2016-03-15 22:58:38 +00:00
|
|
|
style={style}
|
2016-03-10 22:22:26 +00:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
_renderScene(props: NavigationSceneRendererProps): ReactElement {
|
|
|
|
const {navigationState} = props.scene;
|
|
|
|
const route = RouteStack.getRouteByNavigationState(navigationState);
|
|
|
|
return this.props.renderScene(route, this);
|
|
|
|
}
|
2016-03-11 01:16:19 +00:00
|
|
|
|
|
|
|
_applyStack(stack: NavigationLegacyNavigatorRouteStack): void {
|
|
|
|
if (stack !== this._stack) {
|
|
|
|
this._previousStack = this._stack;
|
|
|
|
this._stack = stack;
|
|
|
|
this.setState({
|
|
|
|
presentedIndex: stack.index,
|
|
|
|
routeStack: stack.toArray(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_onNavigationBarRef(navigationBarRef: any): void {
|
|
|
|
this._navigationBarRef = navigationBarRef;
|
|
|
|
const {navigationBar} = this.props;
|
|
|
|
if (navigationBar && typeof navigationBar.ref === 'function') {
|
|
|
|
navigationBar.ref(navigationBarRef);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_onPositionChange(data: {value: number}): void {
|
|
|
|
const fromIndex = this._previousStack.index;
|
|
|
|
const toIndex = this._stack.index;
|
|
|
|
|
|
|
|
if (
|
|
|
|
fromIndex !== toIndex &&
|
|
|
|
this._navigationBarRef &&
|
|
|
|
typeof this._navigationBarRef.updateProgress === 'function'
|
|
|
|
) {
|
|
|
|
const progress = (data.value - fromIndex) / (toIndex - fromIndex);
|
|
|
|
this._navigationBarRef.updateProgress(progress, fromIndex, toIndex);
|
|
|
|
}
|
|
|
|
}
|
2016-03-10 22:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Legacy static members.
|
|
|
|
NavigationLegacyNavigator.BreadcrumbNavigationBar = NavigatorBreadcrumbNavigationBar;
|
|
|
|
NavigationLegacyNavigator.NavigationBar = NavigatorNavigationBar;
|
|
|
|
NavigationLegacyNavigator.SceneConfigs = NavigatorSceneConfigs;
|
2016-02-25 22:50:28 +00:00
|
|
|
|
|
|
|
module.exports = NavigationLegacyNavigator;
|