backed out changeset 87e6fe282c08

Reviewed By: ericvicenti

Differential Revision: D3033857

fb-gh-sync-id: 7391ae943a88c675f8539f5cc81587caec36e80e
shipit-source-id: 7391ae943a88c675f8539f5cc81587caec36e80e
This commit is contained in:
Hedger Wang 2016-03-10 14:22:26 -08:00 committed by Facebook Github Bot 4
parent f5a1600a20
commit 7da65a817c
3 changed files with 290 additions and 12 deletions

View File

@ -27,11 +27,266 @@
*/
'use strict';
const NavigationAnimatedView = require('NavigationAnimatedView');
const NavigationCard = require('NavigationCard');
const NavigationContext = require('NavigationContext');
const NavigationLegacyNavigatorRouteStack = require('NavigationLegacyNavigatorRouteStack');
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 State = any;
type Props = {
configureScene: any,
initialRoute: any,
initialRouteStack: any,
renderScene: any,
style: any,
};
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;
/**
* NavigationLegacyNavigator is meant to replace Navigator seemlessly without
* API changes. While the APIs remain compatible with Navigator, it should
* be built with the new Navigation API such as `NavigationAnimatedView`...etc.
* 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.
*/
const NavigationLegacyNavigator = require('Navigator');
class NavigationLegacyNavigator extends React.Component<any, Props, State> {
static BreadcrumbNavigationBar;
static NavigationBar: any;
static SceneConfigs: any;
_renderCard: NavigationSceneRenderer;
_renderHeader: NavigationSceneRenderer;
_renderScene: NavigationSceneRenderer;
navigationContext: NavigationContext;
constructor(props: Props, context: any) {
super(props, context);
this.navigationContext = new NavigationContext();
const stack = this._getInitialRouteStack();
this.state = {
key: guid(),
stack,
};
}
jumpTo(route: any): void {
const index = this.state.stack.indexOf(route);
invariant(
index > -1,
'Cannot jump to route that is not in the route stack'
);
this._jumpToIndex(index);
}
jumpForward(): void {
this._jumpToIndex(this.state.stack.index + 1);
}
jumpBack(): void {
this._jumpToIndex(this.state.stack.index - 1);
}
push(route: any): void {
this.setState({stack: this.state.stack.push(route)});
}
pop(): void {
const {stack} = this.state;
if (stack.size > 1) {
this.setState({stack: stack.pop()});
}
}
replaceAtIndex(route: any, index: number): void {
const {stack} = this.state;
if (index < 0) {
index += stack.size;
}
if (index >= stack.size) {
// Nothing to replace.
return;
}
this.setState({stack: stack.replaceAtIndex(index, route)});
}
replace(route: any): void {
this.replaceAtIndex(route, this.state.stack.index);
}
replacePrevious(route: any): void {
this.replaceAtIndex(route, this.state.stack.index - 1);
}
popToTop(): void {
this.setState({stack: this.state.stack.slice(0, 1)});
}
popToRoute(route: any): void {
const {stack} = this.state;
const nextIndex = stack.indexOf(route);
invariant(
nextIndex > -1,
'Calling popToRoute for a route that doesn\'t exist!'
);
this.setState({stack: stack.slice(0, nextIndex + 1)});
}
replacePreviousAndPop(route: any): void {
const {stack} = this.state;
const nextIndex = stack.index - 1;
if (nextIndex < 0) {
return;
}
this.setState({stack: stack.replaceAtIndex(nextIndex, route).pop()});
}
resetTo(route: any): void {
invariant(!!route, 'Must supply route');
this.setState({stack: this.state.stack.slice(0).replaceAtIndex(0, route)});
}
immediatelyResetRouteStack(routes: Array<any>): void {
const index = routes.length - 1;
const stack = new RouteStack(index, routes);
this.setState({
key: guid(),
stack,
});
}
getCurrentRoutes(): Array<any> {
return this.state.stack.toArray();
}
_jumpToIndex(index: number): void {
const {stack} = this.state;
if (index < 0 || index >= stack.size) {
return;
}
const nextStack = stack.jumpToIndex(index);
this.setState({stack: nextStack});
}
// Lyfe cycle and private methods below.
shouldComponentUpdate(nextProps: Object, nextState: Object): boolean {
return ReactComponentWithPureRenderMixin.shouldComponentUpdate.call(
this,
nextProps,
nextState
);
}
componentWillMount(): void {
this._renderCard = this._renderCard.bind(this);
this._renderHeader = this._renderHeader.bind(this);
this._renderScene = this._renderScene.bind(this);
}
render(): ReactElement {
return (
<NavigationAnimatedView
key={'main_' + this.state.key}
navigationState={this.state.stack.toNavigationState()}
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 {
// TODO(hedger): Render the legacy header.
return null;
}
_renderCard(props: NavigationSceneRendererProps): ReactElement {
let direction = 'horizontal';
const {navigationState} = props.scene;
const {configureScene} = this.props;
if (configureScene) {
const route = RouteStack.getRouteByNavigationState(navigationState);
const config = configureScene(route, this.state.stack.toArray());
switch (getConfigPopDirection(config)) {
case 'left-to-right':
direction = 'horizontal';
break;
case 'top-to-bottom':
direction = 'vertical';
break;
default:
// unsupported config.
if (__DEV__) {
console.warn('unsupported scene configuration');
}
}
}
return (
<NavigationCard
{...props}
direction={direction}
key={'card_' + navigationState.key}
renderScene={this._renderScene}
/>
);
}
_renderScene(props: NavigationSceneRendererProps): ReactElement {
const {navigationState} = props.scene;
const route = RouteStack.getRouteByNavigationState(navigationState);
return this.props.renderScene(route, this);
}
}
// Legacy static members.
NavigationLegacyNavigator.BreadcrumbNavigationBar = NavigatorBreadcrumbNavigationBar;
NavigationLegacyNavigator.NavigationBar = NavigatorNavigationBar;
NavigationLegacyNavigator.SceneConfigs = NavigatorSceneConfigs;
module.exports = NavigationLegacyNavigator;

View File

@ -46,6 +46,19 @@ let _nextRouteNodeID = 0;
class RouteNode {
key: string;
route: any;
/**
* Cast `navigationState` as `RouteNode`.
* Also see `RouteNode#toNavigationState`.
*/
static fromNavigationState(navigationState: NavigationState): RouteNode {
invariant(
navigationState instanceof RouteNode,
'navigationState should be an instacne of RouteNode'
);
return navigationState;
}
constructor(route: any) {
// Key value gets bigger incrementally. Developer can compare the
// keys of two routes then know which route is added to the stack
@ -84,11 +97,15 @@ let _nextRouteStackID = 0;
* of the routes. This data structure is implemented as immutable data
* and mutation (e.g. push, pop...etc) will yields a new instance.
*/
class RouteStack {
class RouteStack {
_index: number;
_key: string;
_routeNodes: Array<RouteNode>;
static getRouteByNavigationState(navigationState: NavigationState): any {
return RouteNode.fromNavigationState(navigationState).route;
}
constructor(index: number, routes: Array<any>) {
invariant(
routes.length > 0,

View File

@ -24,7 +24,6 @@
*/
'use strict';
jest
.autoMockOff()
.mock('ErrorUtils');
@ -73,13 +72,13 @@ describe('NavigationLegacyNavigatorRouteStack:', () => {
});
it('throws at index out of bound', () => {
expect(() => {
new NavigationLegacyNavigatorRouteStack(-1, ['a', 'b']);
}).toThrow();
expect(
() => new NavigationLegacyNavigatorRouteStack(-1, ['a', 'b'])
).toThrow();
expect(() => {
new NavigationLegacyNavigatorRouteStack(100, ['a', 'b']);
}).toThrow();
expect(
() => new NavigationLegacyNavigatorRouteStack(100, ['a', 'b'])
).toThrow();
});
it('finds index', () => {
@ -386,4 +385,11 @@ describe('NavigationLegacyNavigatorRouteStack:', () => {
],
});
});
it('coverts from navigation state', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
const state = stack.toNavigationState().children[0];
const route = NavigationLegacyNavigatorRouteStack.getRouteByNavigationState(state);
expect(route).toBe('a');
});
});