backed out changeset 87e6fe282c08
Reviewed By: ericvicenti Differential Revision: D3033857 fb-gh-sync-id: 7391ae943a88c675f8539f5cc81587caec36e80e shipit-source-id: 7391ae943a88c675f8539f5cc81587caec36e80e
This commit is contained in:
Родитель
f5a1600a20
Коммит
7da65a817c
|
@ -27,11 +27,266 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'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
|
* NavigationLegacyNavigator is meant to replace Navigator seemlessly with
|
||||||
* API changes. While the APIs remain compatible with Navigator, it should
|
* minimum API changes.
|
||||||
* be built with the new Navigation API such as `NavigationAnimatedView`...etc.
|
*
|
||||||
|
* 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;
|
module.exports = NavigationLegacyNavigator;
|
||||||
|
|
|
@ -46,6 +46,19 @@ let _nextRouteNodeID = 0;
|
||||||
class RouteNode {
|
class RouteNode {
|
||||||
key: string;
|
key: string;
|
||||||
route: any;
|
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) {
|
constructor(route: any) {
|
||||||
// Key value gets bigger incrementally. Developer can compare the
|
// Key value gets bigger incrementally. Developer can compare the
|
||||||
// keys of two routes then know which route is added to the stack
|
// 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
|
* of the routes. This data structure is implemented as immutable data
|
||||||
* and mutation (e.g. push, pop...etc) will yields a new instance.
|
* and mutation (e.g. push, pop...etc) will yields a new instance.
|
||||||
*/
|
*/
|
||||||
class RouteStack {
|
class RouteStack {
|
||||||
_index: number;
|
_index: number;
|
||||||
_key: string;
|
_key: string;
|
||||||
_routeNodes: Array<RouteNode>;
|
_routeNodes: Array<RouteNode>;
|
||||||
|
|
||||||
|
static getRouteByNavigationState(navigationState: NavigationState): any {
|
||||||
|
return RouteNode.fromNavigationState(navigationState).route;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(index: number, routes: Array<any>) {
|
constructor(index: number, routes: Array<any>) {
|
||||||
invariant(
|
invariant(
|
||||||
routes.length > 0,
|
routes.length > 0,
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
jest
|
jest
|
||||||
.autoMockOff()
|
.autoMockOff()
|
||||||
.mock('ErrorUtils');
|
.mock('ErrorUtils');
|
||||||
|
@ -73,13 +72,13 @@ describe('NavigationLegacyNavigatorRouteStack:', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws at index out of bound', () => {
|
it('throws at index out of bound', () => {
|
||||||
expect(() => {
|
expect(
|
||||||
new NavigationLegacyNavigatorRouteStack(-1, ['a', 'b']);
|
() => new NavigationLegacyNavigatorRouteStack(-1, ['a', 'b'])
|
||||||
}).toThrow();
|
).toThrow();
|
||||||
|
|
||||||
expect(() => {
|
expect(
|
||||||
new NavigationLegacyNavigatorRouteStack(100, ['a', 'b']);
|
() => new NavigationLegacyNavigatorRouteStack(100, ['a', 'b'])
|
||||||
}).toThrow();
|
).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('finds index', () => {
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Загрузка…
Ссылка в новой задаче