Kill NavigationLegacyNavigator.

Summary:
NavigationLegacyNavigator was originally created to help people to migrate to the new
navigation library without API changes. Therefore we'd have to port all the old APIs that
don't necessarily seem align well with the new navigation library.

Consider the production usage of NavigationLegacyNavigator does not exist, it's better
to kill it and we'd just rename the `Navigator` to `NavigatorDeprecated` later instead.

Reviewed By: ericvicenti

Differential Revision: D3263704

fb-gh-sync-id: a851fda1516d694cb7d119f5a1344f8fc676f7fd
fbshipit-source-id: a851fda1516d694cb7d119f5a1344f8fc676f7fd
This commit is contained in:
Hedger Wang 2016-05-05 10:55:49 -07:00 committed by Facebook Github Bot 4
parent dd8caf4cf2
commit ef44781f3d
9 changed files with 9 additions and 2095 deletions

View File

@ -1,165 +0,0 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
NavigationExperimental,
StyleSheet,
ScrollView,
Text,
TouchableHighlight,
TouchableOpacity
} = ReactNative;
var _getRandomRoute = function() {
return {
title: '#' + Math.ceil(Math.random() * 1000),
};
};
var Navigator = NavigationExperimental.LegacyNavigator;
class NavButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#B5B5B5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
var BreadcrumbNavSample = React.createClass({
componentWillMount: function() {
this._navBarRouteMapper = {
rightContentForRoute: function(route, navigator) {
return null;
},
titleContentForRoute: function(route, navigator) {
return (
<TouchableOpacity
onPress={() => navigator.push(_getRandomRoute())}>
<Text style={styles.titleText}>{route.title}</Text>
</TouchableOpacity>
);
},
iconForRoute: function(route, navigator) {
return (
<TouchableOpacity
onPress={() => { navigator.popToRoute(route); }}
style={styles.crumbIconPlaceholder}
/>
);
},
separatorForRoute: function(route, navigator) {
return (
<TouchableOpacity
onPress={navigator.pop}
style={styles.crumbSeparatorPlaceholder}
/>
);
}
};
},
_renderScene: function(route, navigator) {
return (
<ScrollView style={styles.scene}>
<NavButton
onPress={() => { navigator.push(_getRandomRoute()); }}
text="Push"
/>
<NavButton
onPress={() => { navigator.immediatelyResetRouteStack([_getRandomRoute(), _getRandomRoute()]); }}
text="Reset w/ 2 scenes"
/>
<NavButton
onPress={() => { navigator.popToTop(); }}
text="Pop to top"
/>
<NavButton
onPress={() => { navigator.replace(_getRandomRoute()); }}
text="Replace"
/>
<NavButton
onPress={() => { this.props.navigator.pop(); }}
text="Close breadcrumb example"
/>
</ScrollView>
);
},
render: function() {
return (
<Navigator
style={styles.container}
initialRoute={_getRandomRoute()}
renderScene={this._renderScene}
navigationBar={
<Navigator.BreadcrumbNavigationBar
routeMapper={this._navBarRouteMapper}
/>
}
/>
);
},
});
var styles = StyleSheet.create({
scene: {
paddingTop: 50,
flex: 1,
},
button: {
backgroundColor: 'white',
padding: 15,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#CDCDCD',
},
buttonText: {
fontSize: 17,
fontWeight: '500',
},
container: {
overflow: 'hidden',
backgroundColor: '#dddddd',
flex: 1,
},
titleText: {
fontSize: 18,
color: '#666666',
textAlign: 'center',
fontWeight: 'bold',
lineHeight: 32,
},
crumbIconPlaceholder: {
flex: 1,
backgroundColor: '#666666',
},
crumbSeparatorPlaceholder: {
flex: 1,
backgroundColor: '#aaaaaa',
},
});
module.exports = BreadcrumbNavSample;

View File

@ -1,219 +0,0 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
NavigationExperimental,
StyleSheet,
ScrollView,
TabBarIOS,
Text,
TouchableHighlight,
View,
} = ReactNative;
var _getRandomRoute = function() {
return {
randNumber: Math.ceil(Math.random() * 1000),
};
};
var Navigator = NavigationExperimental.LegacyNavigator;
class NavButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#B5B5B5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
var ROUTE_STACK = [
_getRandomRoute(),
_getRandomRoute(),
_getRandomRoute(),
];
var INIT_ROUTE_INDEX = 1;
class JumpingNavBar extends React.Component {
constructor(props) {
super(props);
this.state = {
tabIndex: props.initTabIndex,
};
}
handleWillFocus(route) {
var tabIndex = ROUTE_STACK.indexOf(route);
this.setState({ tabIndex, });
}
render() {
return (
<View style={styles.tabs}>
<TabBarIOS>
<TabBarIOS.Item
icon={require('image!tabnav_notification')}
selected={this.state.tabIndex === 0}
onPress={() => {
this.props.onTabIndex(0);
this.setState({ tabIndex: 0, });
}}>
<View />
</TabBarIOS.Item>
<TabBarIOS.Item
icon={require('image!tabnav_list')}
selected={this.state.tabIndex === 1}
onPress={() => {
this.props.onTabIndex(1);
this.setState({ tabIndex: 1, });
}}>
<View />
</TabBarIOS.Item>
<TabBarIOS.Item
icon={require('image!tabnav_settings')}
selected={this.state.tabIndex === 2}
onPress={() => {
this.props.onTabIndex(2);
this.setState({ tabIndex: 2, });
}}>
<View />
</TabBarIOS.Item>
</TabBarIOS>
</View>
);
}
}
var JumpingNavSample = React.createClass({
render: function() {
return (
<Navigator
debugOverlay={false}
style={styles.appContainer}
ref={(navigator) => {
this._navigator = navigator;
}}
initialRoute={ROUTE_STACK[INIT_ROUTE_INDEX]}
initialRouteStack={ROUTE_STACK}
renderScene={this.renderScene}
configureScene={() => ({
...Navigator.SceneConfigs.HorizontalSwipeJump,
})}
navigationBar={
<JumpingNavBar
ref={(navBar) => { this.navBar = navBar; }}
initTabIndex={INIT_ROUTE_INDEX}
routeStack={ROUTE_STACK}
onTabIndex={(index) => {
this._navigator.jumpTo(ROUTE_STACK[index]);
}}
/>
}
/>
);
},
renderScene: function(route, navigator) {
var backBtn;
var forwardBtn;
if (ROUTE_STACK.indexOf(route) !== 0) {
backBtn = (
<NavButton
onPress={() => {
navigator.jumpBack();
}}
text="jumpBack"
/>
);
}
if (ROUTE_STACK.indexOf(route) !== ROUTE_STACK.length - 1) {
forwardBtn = (
<NavButton
onPress={() => {
navigator.jumpForward();
}}
text="jumpForward"
/>
);
}
return (
<ScrollView style={styles.scene}>
<Text style={styles.messageText}>#{route.randNumber}</Text>
{backBtn}
{forwardBtn}
<NavButton
onPress={() => {
navigator.jumpTo(ROUTE_STACK[1]);
}}
text="jumpTo middle route"
/>
<NavButton
onPress={() => {
this.props.navigator.pop();
}}
text="Exit Navigation Example"
/>
<NavButton
onPress={() => {
this.props.navigator.push({
message: 'Came from jumping example',
});
}}
text="Nav Menu"
/>
</ScrollView>
);
},
});
var styles = StyleSheet.create({
button: {
backgroundColor: 'white',
padding: 15,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#CDCDCD',
},
buttonText: {
fontSize: 17,
fontWeight: '500',
},
appContainer: {
overflow: 'hidden',
backgroundColor: '#dddddd',
flex: 1,
},
messageText: {
fontSize: 17,
fontWeight: '500',
padding: 15,
marginTop: 50,
marginLeft: 15,
},
scene: {
flex: 1,
paddingTop: 20,
backgroundColor: '#EAEAEA',
},
tabs: {
height: 50,
}
});
module.exports = JumpingNavSample;

View File

@ -1,220 +0,0 @@
/**
* 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.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
NavigationExperimental,
ScrollView,
StyleSheet,
Text,
TouchableHighlight,
} = ReactNative;
var BreadcrumbNavSample = require('./BreadcrumbNavSample');
var NavigationBarSample = require('./NavigationBarSample');
var JumpingNavSample = require('./JumpingNavSample');
var Navigator = NavigationExperimental.LegacyNavigator;
class NavButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#B5B5B5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
class NavMenu extends React.Component {
render() {
return (
<ScrollView style={styles.scene}>
<Text style={styles.messageText}>{this.props.message}</Text>
<NavButton
onPress={() => {
this.props.navigator.push({
message: 'Swipe right to dismiss',
sceneConfig: Navigator.SceneConfigs.FloatFromRight,
});
}}
text="Float in from right"
/>
<NavButton
onPress={() => {
this.props.navigator.push({
message: 'Swipe down to dismiss',
sceneConfig: Navigator.SceneConfigs.FloatFromBottom,
});
}}
text="Float in from bottom"
/>
<NavButton
onPress={() => {
this.props.navigator.pop();
}}
text="Pop"
/>
<NavButton
onPress={() => {
this.props.navigator.popToTop();
}}
text="Pop to top"
/>
<NavButton
onPress={() => {
this.props.navigator.push({ id: 'navbar' });
}}
text="Navbar Example"
/>
<NavButton
onPress={() => {
this.props.navigator.push({ id: 'jumping' });
}}
text="Jumping Example"
/>
<NavButton
onPress={() => {
this.props.navigator.push({ id: 'breadcrumbs' });
}}
text="Breadcrumbs Example"
/>
<NavButton
onPress={() => {
this.props.onExampleExit();
}}
text="Exit <LegacyNavigator> Example"
/>
</ScrollView>
);
}
}
var TabBarExample = React.createClass({
statics: {
title: '<LegacyNavigator> (Experimental)',
description: 'Experimental navigator that ports the API of the current ' +
'Navigator component',
},
renderScene: function(route, nav) {
switch (route.id) {
case 'navbar':
return <NavigationBarSample navigator={nav} />;
case 'breadcrumbs':
return <BreadcrumbNavSample navigator={nav} />;
case 'jumping':
return <JumpingNavSample navigator={nav} />;
default:
return (
<NavMenu
message={route.message}
navigator={nav}
onExampleExit={this.props.onExampleExit}
/>
);
}
},
render: function() {
return (
<Navigator
ref={this._setNavigatorRef}
style={styles.container}
initialRoute={{ message: 'First Scene', }}
renderScene={this.renderScene}
configureScene={(route) => {
if (route.sceneConfig) {
return route.sceneConfig;
}
return Navigator.SceneConfigs.FloatFromBottom;
}}
/>
);
},
componentWillUnmount: function() {
this._listeners && this._listeners.forEach(listener => listener.remove());
},
_setNavigatorRef: function(navigator) {
if (navigator !== this._navigator) {
this._navigator = navigator;
if (navigator) {
var callback = (event) => {
console.log(
`LegacyNavigator: event ${event.type}`,
{
route: JSON.stringify(event.data.route),
target: event.target,
type: event.type,
}
);
};
// Observe focus change events from the owner.
this._listeners = [
navigator.navigationContext.addListener('willfocus', callback),
navigator.navigationContext.addListener('didfocus', callback),
];
}
}
},
});
var styles = StyleSheet.create({
messageText: {
fontSize: 17,
fontWeight: '500',
padding: 15,
marginTop: 50,
marginLeft: 15,
},
container: {
flex: 1,
},
button: {
backgroundColor: 'white',
padding: 15,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#CDCDCD',
},
buttonText: {
fontSize: 17,
fontWeight: '500',
},
scene: {
flex: 1,
paddingTop: 20,
backgroundColor: '#ccc',
}
});
TabBarExample.external = true;
module.exports = TabBarExample;

View File

@ -1,202 +0,0 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
NavigationExperimental,
ScrollView,
StyleSheet,
Text,
TouchableHighlight,
TouchableOpacity,
} = ReactNative;
var Navigator = NavigationExperimental.LegacyNavigator;
class NavButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#B5B5B5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
var NavigationBarRouteMapper = {
LeftButton: function(route, navigator, index, navState) {
if (index === 0) {
return null;
}
var previousRoute = navState.routeStack[index - 1];
return (
<TouchableOpacity
onPress={() => navigator.pop()}
style={styles.navBarLeftButton}>
<Text style={[styles.navBarText, styles.navBarButtonText]}>
{previousRoute.title}
</Text>
</TouchableOpacity>
);
},
RightButton: function(route, navigator, index, navState) {
return (
<TouchableOpacity
onPress={() => navigator.push(newRandomRoute())}
style={styles.navBarRightButton}>
<Text style={[styles.navBarText, styles.navBarButtonText]}>
Next
</Text>
</TouchableOpacity>
);
},
Title: function(route, navigator, index, navState) {
return (
<Text style={[styles.navBarText, styles.navBarTitleText]}>
{route.title} [{index}]
</Text>
);
},
};
function newRandomRoute() {
return {
title: '#' + Math.ceil(Math.random() * 1000),
};
}
var NavigationBarSample = React.createClass({
componentWillMount: function() {
var navigator = this.props.navigator;
var callback = (event) => {
console.log(
`NavigationBarSample : event ${event.type}`,
{
route: JSON.stringify(event.data.route),
target: event.target,
type: event.type,
}
);
};
// Observe focus change events from this component.
this._listeners = [
navigator.navigationContext.addListener('willfocus', callback),
navigator.navigationContext.addListener('didfocus', callback),
];
},
componentWillUnmount: function() {
this._listeners && this._listeners.forEach(listener => listener.remove());
},
render: function() {
return (
<Navigator
debugOverlay={false}
style={styles.appContainer}
initialRoute={newRandomRoute()}
renderScene={(route, navigator) => (
<ScrollView style={styles.scene}>
<Text style={styles.messageText}>{route.content}</Text>
<NavButton
onPress={() => {
navigator.immediatelyResetRouteStack([
newRandomRoute(),
newRandomRoute(),
newRandomRoute(),
]);
}}
text="Reset w/ 3 scenes"
/>
<NavButton
onPress={() => {
this.props.navigator.pop();
}}
text="Exit NavigationBar Example"
/>
</ScrollView>
)}
navigationBar={
<Navigator.NavigationBar
routeMapper={NavigationBarRouteMapper}
style={styles.navBar}
/>
}
/>
);
},
});
var styles = StyleSheet.create({
messageText: {
fontSize: 17,
fontWeight: '500',
padding: 15,
marginTop: 50,
marginLeft: 15,
},
button: {
backgroundColor: 'white',
padding: 15,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#CDCDCD',
},
buttonText: {
fontSize: 17,
fontWeight: '500',
},
navBar: {
backgroundColor: 'white',
},
navBarText: {
fontSize: 16,
marginVertical: 10,
},
navBarTitleText: {
color: '#373E4D',
fontWeight: '500',
marginVertical: 9,
},
navBarLeftButton: {
paddingLeft: 10,
},
navBarRightButton: {
paddingRight: 10,
},
navBarButtonText: {
color: '#5890FF',
},
scene: {
flex: 1,
paddingTop: 20,
backgroundColor: '#EAEAEA',
},
});
module.exports = NavigationBarSample;

View File

@ -1,4 +1,11 @@
/**
* 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.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
@ -20,7 +27,7 @@ export type UIExplorerExample = {
module: Object;
};
var ComponentExamples: Array<UIExplorerExample> = [
const ComponentExamples: Array<UIExplorerExample> = [
{
key: 'ActivityIndicatorIOSExample',
module: require('./ActivityIndicatorIOSExample'),
@ -135,7 +142,7 @@ var ComponentExamples: Array<UIExplorerExample> = [
},
];
var APIExamples: Array<UIExplorerExample> = [
const APIExamples: Array<UIExplorerExample> = [
{
key: 'AccessibilityIOSExample',
module: require('./AccessibilityIOSExample'),
@ -216,10 +223,6 @@ var APIExamples: Array<UIExplorerExample> = [
key: 'NavigationExperimentalExample',
module: require('./NavigationExperimental/NavigationExperimentalExample'),
},
{
key: 'NavigationExperimentalLegacyNavigatorExample',
module: require('./NavigationExperimental/LegacyNavigator/LegacyNavigatorExample'),
},
{
key: 'NetInfoExample',
module: require('./NetInfoExample'),

View File

@ -1,446 +0,0 @@
/**
* 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.
*
* 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';
const Animated = require('Animated');
const NavigationAnimatedValueSubscription = require('NavigationAnimatedValueSubscription');
const NavigationAnimatedView = require('NavigationAnimatedView');
const NavigationCard = require('NavigationCard');
const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator');
const NavigationContext = require('NavigationContext');
const NavigationLegacyNavigatorRouteStack = require('NavigationLegacyNavigatorRouteStack');
const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder');
const NavigationPagerPanResponder = require('NavigationPagerPanResponder');
const NavigationPagerStyleInterpolator = require('NavigationPagerStyleInterpolator');
const NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar');
const NavigatorNavigationBar = require('NavigatorNavigationBar');
const NavigatorSceneConfigs = require('NavigatorSceneConfigs');
const React = require('React');
const ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin');
import type {
NavigationActionCaller,
NavigationAnimatedValue,
NavigationAnimationSetter,
NavigationParentState,
NavigationSceneRenderer,
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
type Props = {
configureScene: any,
initialRoute: any,
initialRouteStack: any,
navigationBar: any,
navigationBarNavigator: any,
navigator: any,
onDidFocus: any,
onWillFocus: any,
renderScene: any,
renderScene: any,
style: any,
};
type State = {
presentedIndex: number,
routeStack: Array<any>,
};
const RouteStack = NavigationLegacyNavigatorRouteStack;
/**
* 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.
*/
class NavigationLegacyNavigator extends React.Component<any, Props, State> {
static BreadcrumbNavigationBar: any;
static NavigationBar: any;
static SceneConfigs: any;
_applyAnimation: NavigationAnimationSetter;
_navigationBarRef: any;
_onNavigationBarRef: (ref: any) => void;
_onPositionChange: (data: {value: number}) => void;
_positionListener: ?NavigationAnimatedValueSubscription;
_previousStack: NavigationLegacyNavigatorRouteStack;
_renderCard: NavigationSceneRenderer;
_renderHeader: NavigationSceneRenderer;
_renderScene: NavigationSceneRenderer;
_routeFocused: any;
_routeToFocus: any;
_onNavigate: NavigationActionCaller;
_stack: NavigationLegacyNavigatorRouteStack;
_useAnimation: boolean;
navigationContext: NavigationContext;
parentNavigator: any;
props: Props;
state: State;
constructor(props: Props, context: any) {
super(props, context);
const stack = this._getInitialRouteStack();
// 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._previousStack = stack;
this._stack = stack;
this._useAnimation = false;
// Legacy members portred from `Navigator`.
this.parentNavigator = props.navigator;
this.navigationContext = new NavigationContext();
this.state = {
routeStack: stack.toArray(),
presentedIndex: stack.index,
};
}
jumpTo(route: any): void {
this._applyStack(this._stack.jumpTo(route));
}
jumpForward(): void {
this._applyStack(this._stack.jumpForward());
}
jumpBack(): void {
this._applyStack(this._stack.jumpBack());
}
push(route: any): void {
this._applyStack(this._stack.push(route));
}
pop(): void {
this._applyStack(this._stack.pop());
}
replaceAtIndex(route: any, index: number): void {
this._applyStack(this._stack.replaceAtIndex(index, route));
}
replace(route: any): void {
this.replaceAtIndex(route, this._stack.index);
}
replacePrevious(route: any): void {
this.replaceAtIndex(route, this._stack.index - 1);
}
popToTop(): void {
this._applyStack(this._stack.slice(0, 1));
}
popToRoute(route: any): void {
this._applyStack(this._stack.popToRoute(route));
}
replacePreviousAndPop(route: any): void {
this._applyStack(this._stack.replacePreviousAndPop(route));
}
resetTo(route: any): void {
this._applyStack(this._stack.resetTo(route));
}
immediatelyResetRouteStack(routes: Array<any>): void {
this._applyStack(this._stack.resetRoutes(routes), true);
}
getCurrentRoutes(): Array<any> {
return this._stack.toArray();
}
// Life cycle and private methods below.
shouldComponentUpdate(nextProps: Object, nextState: Object): boolean {
return ReactComponentWithPureRenderMixin.shouldComponentUpdate.call(
this,
nextProps,
nextState
);
}
componentWillMount(): void {
this._applyAnimation = this._applyAnimation.bind(this);
this._onNavigate = this._onNavigate.bind(this);
this._onNavigationBarRef = this._onNavigationBarRef.bind(this);
this._onPositionChange = this._onPositionChange.bind(this);
this._renderCard = this._renderCard.bind(this);
this._renderHeader = this._renderHeader.bind(this);
this._renderScene = this._renderScene.bind(this);
this._willFocus();
}
componentDidMount(): void {
this._didFocus();
}
componentWillUnmount(): void {
this._positionListener && this._positionListener.remove();
}
componentWillUpdate(nextProps: Props, nextState: State): void {
this._willFocus();
}
componentDidUpdate(prevProps: Props, prevState: State): void {
if (this._useAnimation) {
// will play animation.
return;
}
this._didFocus();
}
render(): ReactElement {
return (
<NavigationAnimatedView
applyAnimation={this._applyAnimation}
navigationState={this._stack.toNavigationState()}
onNavigate={this._onNavigate}
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 {
// `_renderHeader` is always called before `_renderCard`. We should
// subscribe to the position here.
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,
{
key: 'header_' + props.scene.key,
ref: this._onNavigationBarRef,
navigator: navigationBarNavigator || this,
navState: {...this.state},
}
);
}
_renderCard(props: NavigationSceneRendererProps): ReactElement {
const {scene} = props;
const {configureScene} = this.props;
// Default getters for style and pan responders.
let styleGetter = NavigationCardStackStyleInterpolator.forHorizontal;
let panResponderGetter = NavigationCardStackPanResponder.forHorizontal;
if (configureScene) {
const route = RouteStack.getRouteByNavigationState(scene.navigationState);
const config = configureScene(route, this.state.routeStack);
if (config) {
const gestures = config.gestures || {};
if (gestures.pop && gestures.pop.direction === 'left-to-right') {
// pass, will use default getters.
} else if (gestures.pop && gestures.pop.direction === 'top-to-bottom') {
styleGetter = NavigationCardStackStyleInterpolator.forVertical;
panResponderGetter = NavigationCardStackPanResponder.forVertical;
} else if (
gestures.jumpBack &&
gestures.jumpForward &&
gestures.jumpBack.direction === 'left-to-right' &&
gestures.jumpForward.direction === 'right-to-left'
) {
styleGetter = NavigationPagerStyleInterpolator.forHorizontal;
panResponderGetter = NavigationPagerPanResponder.forHorizontal;
} else if (__DEV__) {
console.warn('unsupported scene configuration', config);
}
}
}
const style = styleGetter(props);
const panHandlers = panResponderGetter(props);
return (
<NavigationCard
{...props}
key={'card_' + props.scene.key}
panHandlers={panHandlers}
renderScene={this._renderScene}
style={style}
/>
);
}
_renderScene(props: NavigationSceneRendererProps): ReactElement {
const {navigationState} = props.scene;
const route = RouteStack.getRouteByNavigationState(navigationState);
return this.props.renderScene(route, this);
}
_applyStack(
stack: NavigationLegacyNavigatorRouteStack,
noAnimation: ?boolean,
): void {
if (stack !== this._stack) {
this._previousStack = this._stack;
this._stack = stack;
this._useAnimation = noAnimation ||
this._previousStack.index !== stack.index;
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);
}
const diff = this._stack.index - data.value;
// When animation stops, the `diff` can still be very a small non-zero value
// (e.g. 0.00000002). Call `willFocus` when `diff` is small enough.
if (diff < 0.05) {
this._didFocus();
}
}
_applyAnimation(
position: NavigationAnimatedValue,
nextState: NavigationParentState,
prevState: NavigationParentState,
): void {
const {index} = nextState;
if (!this._useAnimation) {
position.setValue(index);
return;
}
Animated.timing(
position,
{
duration: 500,
toValue: index,
}
).start();
}
_willFocus(): void {
const route = this._stack.get(this._stack.index);
if (this._routeToFocus === route) {
return;
}
this._routeToFocus = route;
this.navigationContext.emit('willfocus', {route: route});
this.props.onWillFocus && this.props.onWillFocus(route);
}
_didFocus(): void {
const route = this._stack.get(this._stack.index);
if (this._routeFocused === route) {
return;
}
this._routeFocused = route;
this.navigationContext.emit('didfocus', {route: route});
this.props.onDidFocus && this.props.onDidFocus(route);
}
_onNavigate(action: any): void {
switch (action) {
case NavigationCardStackPanResponder.Actions.BACK:
this.pop();
break;
case NavigationPagerPanResponder.Actions.JUMP_BACK:
this.jumpBack();
break;
case NavigationPagerPanResponder.Actions.JUMP_FORWARD:
this.jumpForward();
break;
default:
if (__DEV__) {
console.warn('unsupported gesture action', action);
}
}
}
}
// Legacy static members.
NavigationLegacyNavigator.BreadcrumbNavigationBar = NavigatorBreadcrumbNavigationBar;
NavigationLegacyNavigator.NavigationBar = NavigatorNavigationBar;
NavigationLegacyNavigator.SceneConfigs = NavigatorSceneConfigs;
module.exports = NavigationLegacyNavigator;

View File

@ -1,381 +0,0 @@
/**
* 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.
*
* @providesModule NavigationLegacyNavigatorRouteStack
* @flow
*/
'use strict';
const invariant = require('fbjs/lib/invariant');
import type {
NavigationState,
NavigationParentState,
} from 'NavigationTypeDefinition';
type IterationCallback = (route: any, index: number, key: string) => void;
function isRouteEmpty(route: any): boolean {
return (route === undefined || route === null || route === '') || false;
}
function areRouteNodesEqual(
one: Array<RouteNode>,
two: Array<RouteNode>,
): boolean {
if (one === two) {
return true;
}
if (one.length !== two.length) {
return false;
}
for (let ii = 0, jj = one.length; ii < jj; ii++) {
if (one[ii] !== two[ii]) {
return false;
}
}
return true;
}
let _nextRouteNodeID = 0;
/**
* Private struct class that holds the key for a route.
*/
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
// earlier.
const key = String(_nextRouteNodeID++);
if (__DEV__ ) {
// Ensure the immutability of the node.
Object.defineProperty(this, 'key' , {
enumerable: true,
configurable: false,
writable: false,
value: key,
});
Object.defineProperty(this, 'route' , {
enumerable: true,
configurable: false,
writable: false,
value: route,
});
} else {
this.key = key;
this.route = route;
}
}
toNavigationState(): NavigationState {
return this;
}
}
let _nextRouteStackID = 0;
/**
* The data structure that holds a list of routes and the focused index
* 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 {
_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,
'routes must not be an empty array'
);
invariant(
index > -1 && index <= routes.length - 1,
'RouteStack: index out of bound'
);
let routeNodes;
if (routes[0] instanceof RouteNode) {
// The array is already an array of <RouteNode>.
routeNodes = routes;
} else {
// Wrap the route with <RouteNode>.
routeNodes = routes.map((route) => {
invariant(!isRouteEmpty(route), 'route must not be mepty');
return new RouteNode(route);
});
}
this._routeNodes = routeNodes;
this._index = index;
this._key = String(_nextRouteStackID++);
}
/* $FlowFixMe - get/set properties not yet supported */
get size(): number {
return this._routeNodes.length;
}
/* $FlowFixMe - get/set properties not yet supported */
get index(): number {
return this._index;
}
// Export as...
toArray(): Array<any> {
return this._routeNodes.map(node => node.route);
}
toNavigationState(): NavigationParentState {
return {
index: this._index,
key: this._key,
children: this._routeNodes.map(node => node.toNavigationState()),
};
}
get(index: number): any {
if (index < 0 || index > this._routeNodes.length - 1) {
return null;
}
return this._routeNodes[index].route;
}
/**
* Returns the key associated with the route.
* When a route is added to a stack, the stack creates a key for this route.
* The key will persist until the initial stack and its derived stack
* no longer contains this route.
*/
keyOf(route: any): ?string {
if (isRouteEmpty(route)) {
return null;
}
const index = this.indexOf(route);
return index > -1 ?
this._routeNodes[index].key :
null;
}
indexOf(route: any): number {
if (isRouteEmpty(route)) {
return -1;
}
for (let ii = 0, jj = this._routeNodes.length; ii < jj; ii++) {
const node = this._routeNodes[ii];
if (node.route === route) {
return ii;
}
}
return -1;
}
slice(begin: ?number, end: ?number): RouteStack {
// check `begin` and `end` first to keep @flow happy.
const routeNodes = (end === undefined || end === null) ?
this._routeNodes.slice(begin || 0) :
this._routeNodes.slice(begin || 0, end || 0);
const index = Math.min(this._index, routeNodes.length - 1);
return this._update(index, routeNodes);
}
/**
* Returns a new stack with the provided route appended,
* starting at this stack size.
*/
push(route: any): RouteStack {
invariant(
!isRouteEmpty(route),
'Must supply route to push'
);
invariant(this.indexOf(route) === -1, 'route must be unique');
// When pushing, removes the rest of the routes past the current index.
const routeNodes = this._routeNodes.slice(0, this._index + 1);
routeNodes.push(new RouteNode(route));
return this._update(routeNodes.length - 1, routeNodes);
}
/**
* Returns a new stack a size ones less than this stack,
* excluding the last index in this stack.
*/
pop(): RouteStack {
if (this._routeNodes.length <= 1) {
return this;
}
// When popping, removes the rest of the routes past the current index.
const routeNodes = this._routeNodes.slice(0, this._index);
return this._update(routeNodes.length - 1, routeNodes);
}
popToRoute(route: any): RouteStack {
const index = this.indexOf(route);
invariant(
index > -1,
'Calling popToRoute for a route that doesn\'t exist!'
);
return this.slice(0, index + 1);
}
jumpTo(route: any): RouteStack {
const index = this.indexOf(route);
return this.jumpToIndex(index);
}
jumpToIndex(index: number): RouteStack {
invariant(
index > -1 && index < this._routeNodes.length,
'jumpToIndex: index out of bound'
);
return this._update(index, this._routeNodes);
}
jumpForward(): RouteStack {
const index = this._index + 1;
if (index >= this._routeNodes.length) {
return this;
}
return this._update(index, this._routeNodes);
}
jumpBack(): RouteStack {
const index = this._index - 1;
if (index < 0) {
return this;
}
return this._update(index, this._routeNodes);
}
/**
* Replace a route in the navigation stack.
*
* `index` specifies the route in the stack that should be replaced.
* If it's negative, it counts from the back.
*/
replaceAtIndex(index: number, route: any): RouteStack {
invariant(
!isRouteEmpty(route),
'Must supply route to replace'
);
if (this.get(index) === route) {
return this;
}
invariant(this.indexOf(route) === -1, 'route must be unique');
const size = this._routeNodes.length;
if (index < 0) {
index += size;
}
if (index < 0 || index >= size) {
return this;
}
const routeNodes = this._routeNodes.slice(0);
routeNodes[index] = new RouteNode(route);
return this._update(index, routeNodes);
}
replacePreviousAndPop(route: any): RouteStack {
if (this._index < 1) {
// stack is too small.
return this;
}
const index = this.indexOf(route);
invariant(
index === -1 || index === this._index - 1,
'route already exists in the stack'
);
return this.replaceAtIndex(this._index - 1, route).popToRoute(route);
}
// Reset
/**
* Replace the current active route with a new route, and pops out
* the rest routes after it.
*/
resetTo(route: any): RouteStack {
invariant(!isRouteEmpty(route), 'Must supply route');
const index = this.indexOf(route);
if (index === this._index) {
// Already has this active route.
return this;
}
invariant(index === -1, 'route already exists in the stack');
const routeNodes = this._routeNodes.slice(0, this._index);
routeNodes.push(new RouteNode(route));
return this._update(routeNodes.length - 1, routeNodes);
}
resetRoutes(routes: Array<any>): RouteStack {
const index = routes.length - 1;
return new RouteStack(index, routes);
}
// Iterations
forEach(callback: IterationCallback, context: ?Object): void {
this._routeNodes.forEach((node, index) => {
callback.call(context, node.route, index, node.key);
});
}
mapToArray(callback: IterationCallback, context: ?Object): Array<any> {
return this._routeNodes.map((node, index) => {
return callback.call(context, node.route, index, node.key);
});
}
_update(index: number, routeNodes: Array<RouteNode>): RouteStack {
if (
this._index === index &&
areRouteNodesEqual(this._routeNodes, routeNodes)
) {
return this;
}
return new RouteStack(index, routeNodes);
}
}
module.exports = RouteStack;

View File

@ -1,454 +0,0 @@
/**
* 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.
*
* 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.
*/
'use strict';
jest.unmock('NavigationLegacyNavigatorRouteStack');
const NavigationLegacyNavigatorRouteStack = require('NavigationLegacyNavigatorRouteStack');
function assetStringNotEmpty(str) {
expect(!!str && typeof str === 'string').toBe(true);
}
describe('NavigationLegacyNavigatorRouteStack:', () => {
// Different types of routes.
const ROUTES = [
'foo',
1,
true,
{foo: 'bar'},
['foo'],
];
// Basic
it('gets index', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
expect(stack.index).toBe(1);
});
it('gets size', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
expect(stack.size).toBe(3);
});
it('gets route', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b', 'c']);
expect(stack.get(2)).toBe('c');
});
it('converts to an array', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
expect(stack.toArray()).toEqual(['a', 'b']);
});
it('creates a new stack after mutation', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
const stack2 = stack1.push('c');
expect(stack1).not.toBe(stack2);
});
it('throws at index out of bound', () => {
expect(
() => new NavigationLegacyNavigatorRouteStack(-1, ['a', 'b'])
).toThrow();
expect(
() => new NavigationLegacyNavigatorRouteStack(100, ['a', 'b'])
).toThrow();
});
it('finds index', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
expect(stack.indexOf('b')).toBe(1);
expect(stack.indexOf('c')).toBe(-1);
});
// Key
it('gets key for route', () => {
const test = (route) => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
const key = stack.push(route).keyOf(route);
expect(typeof key).toBe('string');
expect(!!key).toBe(true);
};
ROUTES.forEach(test);
});
it('gets a key of larger value for route', () => {
let lastKey = '';
const test = (route) => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
const key = stack.push(route).keyOf(route);
expect(key > lastKey).toBe(true);
lastKey = key;
};
ROUTES.forEach(test);
});
it('gets an unique key for a different route', () => {
let stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
const keys = {};
const test = (route) => {
stack = stack.push(route);
const key = stack.keyOf(route);
expect(keys[key]).toBe(undefined);
keys[key] = true;
};
ROUTES.forEach(test);
});
it('gets the same unique key for the same route', () => {
const test = (route) => {
const stack = new NavigationLegacyNavigatorRouteStack(0, [route]);
expect(stack.keyOf(route)).toBe(stack.keyOf(route));
};
ROUTES.forEach(test);
});
it('gets the same unique key form the derived stack', () => {
const test = (route) => {
const stack = new NavigationLegacyNavigatorRouteStack(0, [route]);
const derivedStack = stack.push('wow').pop().slice(0, 10).push('blah');
expect(derivedStack.keyOf(route)).toBe(stack.keyOf(route));
};
ROUTES.forEach(test);
});
it('gets a different key from a different stack', () => {
const test = (route) => {
const stack1 = new NavigationLegacyNavigatorRouteStack(0, [route]);
const stack2 = new NavigationLegacyNavigatorRouteStack(0, [route]);
expect(stack1.keyOf(route)).not.toBe(stack2.keyOf(route));
};
ROUTES.forEach(test);
});
it('gets no key for a route that does not contains this route', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
expect(stack.keyOf('b')).toBe(null);
});
it('gets a new key for a route that was removed and added again', () => {
const test = (route) => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
const key1 = stack.push(route).keyOf(route);
const key2 = stack.push(route).pop().push(route).keyOf(route);
expect(key1).not.toBe(key2);
};
ROUTES.forEach(test);
});
// Slice
it('slices', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c', 'd']);
const stack2 = stack1.slice(1, 3);
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['b', 'c']);
});
it('may update index after slicing', () => {
const stack = new NavigationLegacyNavigatorRouteStack(2, ['a', 'b', 'c']);
expect(stack.slice().index).toBe(2);
expect(stack.slice(0, 1).index).toBe(0);
expect(stack.slice(0, 2).index).toBe(1);
expect(stack.slice(0, 3).index).toBe(2);
expect(stack.slice(0, 100).index).toBe(2);
expect(stack.slice(-2).index).toBe(1);
});
it('slices without specifying params', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
const stack2 = stack1.slice();
expect(stack2).toBe(stack1);
});
it('slices to from the end', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c', 'd']);
const stack2 = stack1.slice(-2);
expect(stack2.toArray()).toEqual(['c', 'd']);
});
it('throws when slicing to empty', () => {
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.slice(100);
}).toThrow();
});
// Push
it('pushes route', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
const stack2 = stack1.push('c');
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['a', 'b', 'c']);
expect(stack2.index).toBe(2);
expect(stack2.size).toBe(3);
});
it('throws when pushing empty route', () => {
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.push(null);
}).toThrow();
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.push('');
}).toThrow();
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.push(undefined);
}).toThrow();
});
it('replaces routes on push', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
const stack2 = stack1.push('d');
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['a', 'b', 'd']);
expect(stack2.index).toBe(2);
});
// Pop
it('pops route', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(2, ['a', 'b', 'c']);
const stack2 = stack1.pop();
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['a', 'b']);
expect(stack2.index).toBe(1);
expect(stack2.size).toBe(2);
});
it('replaces routes on pop', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
const stack2 = stack1.pop();
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['a']);
expect(stack2.index).toBe(0);
});
it('does nothing while popping to empty', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
expect(stack.pop()).toBe(stack);
expect(stack.pop().pop()).toBe(stack);
});
it('pops to route', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
expect(stack.popToRoute('b').toArray()).toEqual(['a', 'b']);
expect(stack.popToRoute('b').index).toBe(1);
expect(stack.popToRoute('a').toArray()).toEqual(['a']);
expect(stack.popToRoute('a').index).toBe(0);
expect(() => {stack.popToRoute('x');}).toThrow();
});
// Jump
it('jumps to index', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b', 'c']);
const stack2 = stack1.jumpToIndex(2);
expect(stack2).not.toBe(stack1);
expect(stack2.index).toBe(2);
});
it('throws then jumping to index out of bound', () => {
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.jumpToIndex(2);
}).toThrow();
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.jumpToIndex(-1);
}).toThrow();
});
it('jumps to route', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
expect(stack.jumpTo('b').index).toBe(1);
});
it('jumps backward', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
expect(stack.jumpBack().index).toBe(0);
expect(stack.jumpBack().jumpBack().jumpBack().index).toBe(0);
});
it('jumps forward', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
expect(stack.jumpForward().index).toBe(1);
expect(stack.jumpForward().jumpForward().jumpForward().index).toBe(1);
});
// Replace
it('replaces route at index', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
const stack2 = stack1.replaceAtIndex(0, 'x');
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['x', 'b']);
expect(stack2.index).toBe(0);
});
it('replaces route at negative index', () => {
const stack1 = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
const stack2 = stack1.replaceAtIndex(-1, 'x');
expect(stack2).not.toBe(stack1);
expect(stack2.toArray()).toEqual(['a', 'x']);
expect(stack2.index).toBe(1);
});
it('throws when replacing empty route', () => {
expect(() => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
stack.replaceAtIndex(1, null);
}).toThrow();
});
it('does nothing when replacing at index out of bound', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b']);
expect(stack.replaceAtIndex(100, 'x')).toBe(stack);
expect(stack.replaceAtIndex(-100, 'x')).toBe(stack);
});
it('replaces previous and pop route', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
expect(stack.replacePreviousAndPop('x').toArray()).toEqual(['x']);
expect(stack.replacePreviousAndPop('x').index).toBe(0);
});
it('does nothing when there is nothing to replace', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b', 'c']);
expect(stack.replacePreviousAndPop('x')).toBe(stack);
});
// Reset
it('resets route', () => {
const stack = new NavigationLegacyNavigatorRouteStack(1, ['a', 'b', 'c']);
expect(stack.resetTo('b')).toBe(stack);
expect(stack.resetTo('x').toArray()).toEqual(['a', 'x']);
expect(stack.resetTo('x').index).toBe(1);
expect(() => {stack.resetTo(null);}).toThrow();
expect(() => {stack.resetTo('a');}).toThrow();
});
it('resets routes', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a']);
expect(stack.resetRoutes(['x', 'y']).toArray()).toEqual(['x', 'y']);
expect(stack.resetRoutes(['x', 'y']).index).toBe(1);
});
// Iteration
it('iterates each item', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
const logs = [];
const keys = {};
const context = {name: 'yo'};
stack.forEach(function (route, index, key) {
assetStringNotEmpty(key);
if (!keys.hasOwnProperty(key)) {
keys[key] = true;
logs.push([
route,
index,
this.name,
]);
}
}, context);
expect(logs).toEqual([
['a', 0, 'yo'],
['b', 1, 'yo'],
]);
});
it('Maps to an array', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
const keys = {};
const context = {name: 'yo'};
const logs = stack.mapToArray(function(route, index, key) {
assetStringNotEmpty(key);
if (!keys.hasOwnProperty(key)) {
keys[key] = true;
return [
route,
index,
this.name,
];
}
}, context);
expect(logs).toEqual([
['a', 0, 'yo'],
['b', 1, 'yo'],
]);
});
// Navigation State
it('coverts to navigation state', () => {
const stack = new NavigationLegacyNavigatorRouteStack(0, ['a', 'b']);
const state = stack.toNavigationState();
expect(state).toEqual({
index: 0,
key: '0',
children:[
{key: '0', route: 'a'},
{key: '1', route: 'b'},
],
});
});
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');
});
});

View File

@ -16,7 +16,6 @@ const NavigationCard = require('NavigationCard');
const NavigationCardStack = require('NavigationCardStack');
const NavigationContainer = require('NavigationContainer');
const NavigationHeader = require('NavigationHeader');
const NavigationLegacyNavigator = require('NavigationLegacyNavigator');
const NavigationReducer = require('NavigationReducer');
const NavigationRootContainer = require('NavigationRootContainer');
const NavigationStateUtils = require('NavigationStateUtils');
@ -40,7 +39,6 @@ const NavigationExperimental = {
Card: NavigationCard,
CardStack: NavigationCardStack,
Header: NavigationHeader,
LegacyNavigator: NavigationLegacyNavigator,
PropTypes: NavigationPropTypes,
};