mirror of
https://github.com/status-im/react-native.git
synced 2025-01-23 16:00:37 +00:00
613ca14612
Summary:Since the React 0.14 split of modules, the findNodeHandle feature is part of the renderer and not the generic React API. This just greps for React.findNodeHandle and replace them with ReactNative.findNodeHandle. I fixed up the imports manually. I also found two callers each of ReactNative.createClass and React.render with the exception of downstream and examples will fix them separately. I'll need to find more things like `var { PropTypes } = ReactNative;` separately. I think this is a good start though. Reviewed By: vjeux Differential Revision: D3149356 fb-gh-sync-id: 50ed60bc67270b16f561d4c641f2f19e85724d3b fbshipit-source-id: 50ed60bc67270b16f561d4c641f2f19e85724d3b
716 lines
22 KiB
JavaScript
716 lines
22 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-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 NavigatorIOS
|
|
* @flow
|
|
*/
|
|
'use strict';
|
|
|
|
var EventEmitter = require('EventEmitter');
|
|
var Image = require('Image');
|
|
var NavigationContext = require('NavigationContext');
|
|
var RCTNavigatorManager = require('NativeModules').NavigatorManager;
|
|
var React = require('React');
|
|
var ReactNative = require('ReactNative');
|
|
var StaticContainer = require('StaticContainer.react');
|
|
var StyleSheet = require('StyleSheet');
|
|
var View = require('View');
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var logError = require('logError');
|
|
var requireNativeComponent = require('requireNativeComponent');
|
|
var resolveAssetSource = require('resolveAssetSource');
|
|
|
|
var TRANSITIONER_REF = 'transitionerRef';
|
|
|
|
var PropTypes = React.PropTypes;
|
|
|
|
var __uid = 0;
|
|
function getuid() {
|
|
return __uid++;
|
|
}
|
|
|
|
var NavigatorTransitionerIOS = React.createClass({
|
|
requestSchedulingNavigation: function(cb) {
|
|
RCTNavigatorManager.requestSchedulingJavaScriptNavigation(
|
|
ReactNative.findNodeHandle(this),
|
|
logError,
|
|
cb
|
|
);
|
|
},
|
|
|
|
render: function() {
|
|
return (
|
|
<RCTNavigator {...this.props}/>
|
|
);
|
|
},
|
|
});
|
|
|
|
type Route = {
|
|
component: Function;
|
|
title: string;
|
|
passProps?: Object;
|
|
backButtonTitle?: string;
|
|
backButtonIcon?: Object;
|
|
leftButtonTitle?: string;
|
|
leftButtonIcon?: Object;
|
|
onLeftButtonPress?: Function;
|
|
rightButtonTitle?: string;
|
|
rightButtonIcon?: Object;
|
|
onRightButtonPress?: Function;
|
|
wrapperStyle?: any;
|
|
};
|
|
|
|
type State = {
|
|
idStack: Array<number>;
|
|
routeStack: Array<Route>;
|
|
requestedTopOfStack: number;
|
|
observedTopOfStack: number;
|
|
progress: number;
|
|
fromIndex: number;
|
|
toIndex: number;
|
|
makingNavigatorRequest: boolean;
|
|
updatingAllIndicesAtOrBeyond: ?number;
|
|
}
|
|
|
|
type Event = Object;
|
|
|
|
/**
|
|
* Think of `<NavigatorIOS>` as simply a component that renders an
|
|
* `RCTNavigator`, and moves the `RCTNavigator`'s `requestedTopOfStack` pointer
|
|
* forward and backward. The `RCTNavigator` interprets changes in
|
|
* `requestedTopOfStack` to be pushes and pops of children that are rendered.
|
|
* `<NavigatorIOS>` always ensures that whenever the `requestedTopOfStack`
|
|
* pointer is moved, that we've also rendered enough children so that the
|
|
* `RCTNavigator` can carry out the push/pop with those children.
|
|
* `<NavigatorIOS>` also removes children that will no longer be needed
|
|
* (after the pop of a child has been fully completed/animated out).
|
|
*/
|
|
|
|
/**
|
|
* NavigatorIOS wraps UIKit navigation and allows you to add back-swipe
|
|
* functionality across your app.
|
|
*
|
|
* > **NOTE**: This Component is not maintained by Facebook
|
|
* >
|
|
* > This component is under community responsibility.
|
|
* > If a pure JavaScript solution fits your needs you may try the `Navigator`
|
|
* > component instead.
|
|
*
|
|
* #### Routes
|
|
* A route is an object used to describe each page in the navigator. The first
|
|
* route is provided to NavigatorIOS as `initialRoute`:
|
|
*
|
|
* ```
|
|
* render: function() {
|
|
* return (
|
|
* <NavigatorIOS
|
|
* initialRoute={{
|
|
* component: MyView,
|
|
* title: 'My View Title',
|
|
* passProps: { myProp: 'foo' },
|
|
* }}
|
|
* />
|
|
* );
|
|
* },
|
|
* ```
|
|
*
|
|
* Now MyView will be rendered by the navigator. It will receive the route
|
|
* object in the `route` prop, a navigator, and all of the props specified in
|
|
* `passProps`.
|
|
*
|
|
* See the initialRoute propType for a complete definition of a route.
|
|
*
|
|
* #### Navigator
|
|
*
|
|
* A `navigator` is an object of navigation functions that a view can call. It
|
|
* is passed as a prop to any component rendered by NavigatorIOS.
|
|
*
|
|
* ```
|
|
* var MyView = React.createClass({
|
|
* _handleBackButtonPress: function() {
|
|
* this.props.navigator.pop();
|
|
* },
|
|
* _handleNextButtonPress: function() {
|
|
* this.props.navigator.push(nextRoute);
|
|
* },
|
|
* ...
|
|
* });
|
|
* ```
|
|
*
|
|
* A navigation object contains the following functions:
|
|
*
|
|
* - `push(route)` - Navigate forward to a new route
|
|
* - `pop()` - Go back one page
|
|
* - `popN(n)` - Go back N pages at once. When N=1, behavior matches `pop()`
|
|
* - `replace(route)` - Replace the route for the current page and immediately
|
|
* load the view for the new route
|
|
* - `replacePrevious(route)` - Replace the route/view for the previous page
|
|
* - `replacePreviousAndPop(route)` - Replaces the previous route/view and
|
|
* transitions back to it
|
|
* - `resetTo(route)` - Replaces the top item and popToTop
|
|
* - `popToRoute(route)` - Go back to the item for a particular route object
|
|
* - `popToTop()` - Go back to the top item
|
|
*
|
|
* Navigator functions are also available on the NavigatorIOS component:
|
|
*
|
|
* ```
|
|
* var MyView = React.createClass({
|
|
* _handleNavigationRequest: function() {
|
|
* this.refs.nav.push(otherRoute);
|
|
* },
|
|
* render: () => (
|
|
* <NavigatorIOS
|
|
* ref="nav"
|
|
* initialRoute={...}
|
|
* />
|
|
* ),
|
|
* });
|
|
* ```
|
|
*
|
|
* Props passed to the NavigatorIOS component will set the default configuration
|
|
* for the navigation bar. Props passed as properties to a route object will set
|
|
* the configuration for that route's navigation bar, overriding any props
|
|
* passed to the NavigatorIOS component.
|
|
*
|
|
*/
|
|
var NavigatorIOS = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
/**
|
|
* NavigatorIOS uses "route" objects to identify child views, their props,
|
|
* and navigation bar configuration. "push" and all the other navigation
|
|
* operations expect routes to be like this:
|
|
*/
|
|
initialRoute: PropTypes.shape({
|
|
/**
|
|
* The React Class to render for this route
|
|
*/
|
|
component: PropTypes.func.isRequired,
|
|
|
|
/**
|
|
* The title displayed in the nav bar and back button for this route
|
|
*/
|
|
title: PropTypes.string.isRequired,
|
|
|
|
/**
|
|
* Specify additional props passed to the component. NavigatorIOS will
|
|
* automatically provide "route" and "navigator" components
|
|
*/
|
|
passProps: PropTypes.object,
|
|
|
|
/**
|
|
* If set, the left header button image will appear with this source. Note
|
|
* that this doesn't apply for the header of the current view, but the
|
|
* ones of the views that are pushed afterward.
|
|
*/
|
|
backButtonIcon: Image.propTypes.source,
|
|
|
|
/**
|
|
* If set, the left header button will appear with this name. Note that
|
|
* this doesn't apply for the header of the current view, but the ones
|
|
* of the views that are pushed afterward.
|
|
*/
|
|
backButtonTitle: PropTypes.string,
|
|
|
|
/**
|
|
* If set, the left header button image will appear with this source
|
|
*/
|
|
leftButtonIcon: Image.propTypes.source,
|
|
|
|
/**
|
|
* If set, the left header button will appear with this name
|
|
*/
|
|
leftButtonTitle: PropTypes.string,
|
|
|
|
/**
|
|
* Called when the left header button is pressed
|
|
*/
|
|
onLeftButtonPress: PropTypes.func,
|
|
|
|
/**
|
|
* If set, the right header button image will appear with this source
|
|
*/
|
|
rightButtonIcon: Image.propTypes.source,
|
|
|
|
/**
|
|
* If set, the right header button will appear with this name
|
|
*/
|
|
rightButtonTitle: PropTypes.string,
|
|
|
|
/**
|
|
* Called when the right header button is pressed
|
|
*/
|
|
onRightButtonPress: PropTypes.func,
|
|
|
|
/**
|
|
* Styles for the navigation item containing the component
|
|
*/
|
|
wrapperStyle: View.propTypes.style,
|
|
|
|
/**
|
|
* A Boolean value that indicates whether the navigation bar is hidden
|
|
*/
|
|
navigationBarHidden: PropTypes.bool,
|
|
|
|
/**
|
|
* A Boolean value that indicates whether to hide the 1px hairline shadow
|
|
*/
|
|
shadowHidden: PropTypes.bool,
|
|
|
|
/**
|
|
* The color used for buttons in the navigation bar
|
|
*/
|
|
tintColor: PropTypes.string,
|
|
|
|
/**
|
|
* The background color of the navigation bar
|
|
*/
|
|
barTintColor: PropTypes.string,
|
|
|
|
/**
|
|
* The text color of the navigation bar title
|
|
*/
|
|
titleTextColor: PropTypes.string,
|
|
|
|
/**
|
|
* A Boolean value that indicates whether the navigation bar is translucent
|
|
*/
|
|
translucent: PropTypes.bool,
|
|
|
|
}).isRequired,
|
|
|
|
/**
|
|
* A Boolean value that indicates whether the navigation bar is hidden by default
|
|
*/
|
|
navigationBarHidden: PropTypes.bool,
|
|
|
|
/**
|
|
* A Boolean value that indicates whether to hide the 1px hairline shadow by default
|
|
*/
|
|
shadowHidden: PropTypes.bool,
|
|
|
|
/**
|
|
* The default wrapper style for components in the navigator.
|
|
* A common use case is to set the backgroundColor for every page
|
|
*/
|
|
itemWrapperStyle: View.propTypes.style,
|
|
|
|
/**
|
|
* The default color used for buttons in the navigation bar
|
|
*/
|
|
tintColor: PropTypes.string,
|
|
|
|
/**
|
|
* The default background color of the navigation bar
|
|
*/
|
|
barTintColor: PropTypes.string,
|
|
|
|
/**
|
|
* The default text color of the navigation bar title
|
|
*/
|
|
titleTextColor: PropTypes.string,
|
|
|
|
/**
|
|
* A Boolean value that indicates whether the navigation bar is translucent by default
|
|
*/
|
|
translucent: PropTypes.bool,
|
|
|
|
},
|
|
|
|
navigator: (undefined: ?Object),
|
|
navigationContext: new NavigationContext(),
|
|
|
|
componentWillMount: function() {
|
|
// Precompute a pack of callbacks that's frequently generated and passed to
|
|
// instances.
|
|
this.navigator = {
|
|
push: this.push,
|
|
pop: this.pop,
|
|
popN: this.popN,
|
|
replace: this.replace,
|
|
replacePrevious: this.replacePrevious,
|
|
replacePreviousAndPop: this.replacePreviousAndPop,
|
|
resetTo: this.resetTo,
|
|
popToRoute: this.popToRoute,
|
|
popToTop: this.popToTop,
|
|
navigationContext: this.navigationContext,
|
|
};
|
|
this._emitWillFocus(this.state.routeStack[this.state.observedTopOfStack]);
|
|
},
|
|
|
|
componentDidMount: function() {
|
|
this._emitDidFocus(this.state.routeStack[this.state.observedTopOfStack]);
|
|
},
|
|
|
|
componentWillUnmount: function() {
|
|
this.navigationContext.dispose();
|
|
this.navigationContext = new NavigationContext();
|
|
},
|
|
|
|
getDefaultProps: function(): Object {
|
|
return {
|
|
translucent: true,
|
|
};
|
|
},
|
|
|
|
getInitialState: function(): State {
|
|
return {
|
|
idStack: [getuid()],
|
|
routeStack: [this.props.initialRoute],
|
|
// The navigation index that we wish to push/pop to.
|
|
requestedTopOfStack: 0,
|
|
// The last index that native has sent confirmation of completed push/pop
|
|
// for. At this point, we can discard any views that are beyond the
|
|
// `requestedTopOfStack`. A value of `null` means we have not received
|
|
// any confirmation, ever. We may receive an `observedTopOfStack` without
|
|
// ever requesting it - native can instigate pops of its own with the
|
|
// backswipe gesture.
|
|
observedTopOfStack: 0,
|
|
progress: 1,
|
|
fromIndex: 0,
|
|
toIndex: 0,
|
|
// Whether or not we are making a navigator request to push/pop. (Used
|
|
// for performance optimization).
|
|
makingNavigatorRequest: false,
|
|
// Whether or not we are updating children of navigator and if so (not
|
|
// `null`) which index marks the beginning of all updates. Used for
|
|
// performance optimization.
|
|
updatingAllIndicesAtOrBeyond: 0,
|
|
};
|
|
},
|
|
|
|
_toFocusOnNavigationComplete: (undefined: any),
|
|
|
|
_handleFocusRequest: function(item: any) {
|
|
if (this.state.makingNavigatorRequest) {
|
|
this._toFocusOnNavigationComplete = item;
|
|
} else {
|
|
this._getFocusEmitter().emit('focus', item);
|
|
}
|
|
},
|
|
|
|
_focusEmitter: (undefined: ?EventEmitter),
|
|
|
|
_getFocusEmitter: function(): EventEmitter {
|
|
// Flow not yet tracking assignments to instance fields.
|
|
var focusEmitter = this._focusEmitter;
|
|
if (!focusEmitter) {
|
|
focusEmitter = new EventEmitter();
|
|
this._focusEmitter = focusEmitter;
|
|
}
|
|
return focusEmitter;
|
|
},
|
|
|
|
getChildContext: function(): {
|
|
onFocusRequested: Function;
|
|
focusEmitter: EventEmitter;
|
|
} {
|
|
return {
|
|
onFocusRequested: this._handleFocusRequest,
|
|
focusEmitter: this._getFocusEmitter(),
|
|
};
|
|
},
|
|
|
|
childContextTypes: {
|
|
onFocusRequested: React.PropTypes.func,
|
|
focusEmitter: React.PropTypes.instanceOf(EventEmitter),
|
|
},
|
|
|
|
_tryLockNavigator: function(cb: () => void) {
|
|
this.refs[TRANSITIONER_REF].requestSchedulingNavigation(
|
|
(acquiredLock) => acquiredLock && cb()
|
|
);
|
|
},
|
|
|
|
_handleNavigatorStackChanged: function(e: Event) {
|
|
var newObservedTopOfStack = e.nativeEvent.stackLength - 1;
|
|
this._emitDidFocus(this.state.routeStack[newObservedTopOfStack]);
|
|
|
|
invariant(
|
|
newObservedTopOfStack <= this.state.requestedTopOfStack,
|
|
'No navigator item should be pushed without JS knowing about it %s %s', newObservedTopOfStack, this.state.requestedTopOfStack
|
|
);
|
|
var wasWaitingForConfirmation =
|
|
this.state.requestedTopOfStack !== this.state.observedTopOfStack;
|
|
if (wasWaitingForConfirmation) {
|
|
invariant(
|
|
newObservedTopOfStack === this.state.requestedTopOfStack,
|
|
'If waiting for observedTopOfStack to reach requestedTopOfStack, ' +
|
|
'the only valid observedTopOfStack should be requestedTopOfStack.'
|
|
);
|
|
}
|
|
// Mark the most recent observation regardless of if we can lock the
|
|
// navigator. `observedTopOfStack` merely represents what we've observed
|
|
// and this first `setState` is only executed to update debugging
|
|
// overlays/navigation bar.
|
|
// Also reset progress, toIndex, and fromIndex as they might not end
|
|
// in the correct states for a two possible reasons:
|
|
// Progress isn't always 0 or 1 at the end, the system rounds
|
|
// If the Navigator is offscreen these values won't be updated
|
|
// TOOD: Revisit this decision when no longer relying on native navigator.
|
|
var nextState = {
|
|
observedTopOfStack: newObservedTopOfStack,
|
|
makingNavigatorRequest: false,
|
|
updatingAllIndicesAtOrBeyond: null,
|
|
progress: 1,
|
|
toIndex: newObservedTopOfStack,
|
|
fromIndex: newObservedTopOfStack,
|
|
};
|
|
this.setState(nextState, this._eliminateUnneededChildren);
|
|
},
|
|
|
|
_eliminateUnneededChildren: function() {
|
|
// Updating the indices that we're deleting and that's all. (Truth: Nothing
|
|
// even uses the indices in this case, but let's make this describe the
|
|
// truth anyways).
|
|
var updatingAllIndicesAtOrBeyond =
|
|
this.state.routeStack.length > this.state.observedTopOfStack + 1 ?
|
|
this.state.observedTopOfStack + 1 :
|
|
null;
|
|
this.setState({
|
|
idStack: this.state.idStack.slice(0, this.state.observedTopOfStack + 1),
|
|
routeStack: this.state.routeStack.slice(0, this.state.observedTopOfStack + 1),
|
|
// Now we rerequest the top of stack that we observed.
|
|
requestedTopOfStack: this.state.observedTopOfStack,
|
|
makingNavigatorRequest: true,
|
|
updatingAllIndicesAtOrBeyond: updatingAllIndicesAtOrBeyond,
|
|
});
|
|
},
|
|
|
|
_emitDidFocus: function(route: Route) {
|
|
this.navigationContext.emit('didfocus', {route: route});
|
|
},
|
|
|
|
_emitWillFocus: function(route: Route) {
|
|
this.navigationContext.emit('willfocus', {route: route});
|
|
},
|
|
|
|
push: function(route: Route) {
|
|
invariant(!!route, 'Must supply route to push');
|
|
// Make sure all previous requests are caught up first. Otherwise reject.
|
|
if (this.state.requestedTopOfStack === this.state.observedTopOfStack) {
|
|
this._tryLockNavigator(() => {
|
|
this._emitWillFocus(route);
|
|
|
|
var nextStack = this.state.routeStack.concat([route]);
|
|
var nextIDStack = this.state.idStack.concat([getuid()]);
|
|
this.setState({
|
|
// We have to make sure that we've also supplied enough views to
|
|
// satisfy our request to adjust the `requestedTopOfStack`.
|
|
idStack: nextIDStack,
|
|
routeStack: nextStack,
|
|
requestedTopOfStack: nextStack.length - 1,
|
|
makingNavigatorRequest: true,
|
|
updatingAllIndicesAtOrBeyond: nextStack.length - 1,
|
|
});
|
|
});
|
|
}
|
|
},
|
|
|
|
popN: function(n: number) {
|
|
if (n === 0) {
|
|
return;
|
|
}
|
|
// Make sure all previous requests are caught up first. Otherwise reject.
|
|
if (this.state.requestedTopOfStack === this.state.observedTopOfStack) {
|
|
if (this.state.requestedTopOfStack > 0) {
|
|
this._tryLockNavigator(() => {
|
|
var newRequestedTopOfStack = this.state.requestedTopOfStack - n;
|
|
invariant(newRequestedTopOfStack >= 0, 'Cannot pop below 0');
|
|
this._emitWillFocus(this.state.routeStack[newRequestedTopOfStack]);
|
|
this.setState({
|
|
requestedTopOfStack: newRequestedTopOfStack,
|
|
makingNavigatorRequest: true,
|
|
updatingAllIndicesAtOrBeyond: this.state.requestedTopOfStack - n,
|
|
});
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
pop: function() {
|
|
this.popN(1);
|
|
},
|
|
|
|
/**
|
|
* 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: function(route: Route, index: number) {
|
|
invariant(!!route, 'Must supply route to replace');
|
|
if (index < 0) {
|
|
index += this.state.routeStack.length;
|
|
}
|
|
|
|
if (this.state.routeStack.length <= index) {
|
|
return;
|
|
}
|
|
|
|
// I don't believe we need to lock for a replace since there's no
|
|
// navigation actually happening
|
|
var nextIDStack = this.state.idStack.slice();
|
|
var nextRouteStack = this.state.routeStack.slice();
|
|
nextIDStack[index] = getuid();
|
|
nextRouteStack[index] = route;
|
|
|
|
this.setState({
|
|
idStack: nextIDStack,
|
|
routeStack: nextRouteStack,
|
|
makingNavigatorRequest: false,
|
|
updatingAllIndicesAtOrBeyond: index,
|
|
});
|
|
|
|
this._emitWillFocus(route);
|
|
this._emitDidFocus(route);
|
|
},
|
|
|
|
/**
|
|
* Replaces the top of the navigation stack.
|
|
*/
|
|
replace: function(route: Route) {
|
|
this.replaceAtIndex(route, -1);
|
|
},
|
|
|
|
/**
|
|
* Replace the current route's parent.
|
|
*/
|
|
replacePrevious: function(route: Route) {
|
|
this.replaceAtIndex(route, -2);
|
|
},
|
|
|
|
popToTop: function() {
|
|
this.popToRoute(this.state.routeStack[0]);
|
|
},
|
|
|
|
popToRoute: function(route: Route) {
|
|
var indexOfRoute = this.state.routeStack.indexOf(route);
|
|
invariant(
|
|
indexOfRoute !== -1,
|
|
'Calling pop to route for a route that doesn\'t exist!'
|
|
);
|
|
var numToPop = this.state.routeStack.length - indexOfRoute - 1;
|
|
this.popN(numToPop);
|
|
},
|
|
|
|
replacePreviousAndPop: function(route: Route) {
|
|
// Make sure all previous requests are caught up first. Otherwise reject.
|
|
if (this.state.requestedTopOfStack !== this.state.observedTopOfStack) {
|
|
return;
|
|
}
|
|
if (this.state.routeStack.length < 2) {
|
|
return;
|
|
}
|
|
this._tryLockNavigator(() => {
|
|
this.replacePrevious(route);
|
|
this.setState({
|
|
requestedTopOfStack: this.state.requestedTopOfStack - 1,
|
|
makingNavigatorRequest: true,
|
|
});
|
|
});
|
|
},
|
|
|
|
resetTo: function(route: Route) {
|
|
invariant(!!route, 'Must supply route to push');
|
|
// Make sure all previous requests are caught up first. Otherwise reject.
|
|
if (this.state.requestedTopOfStack !== this.state.observedTopOfStack) {
|
|
return;
|
|
}
|
|
this.replaceAtIndex(route, 0);
|
|
this.popToRoute(route);
|
|
},
|
|
|
|
handleNavigationComplete: function(e: Event) {
|
|
if (this._toFocusOnNavigationComplete) {
|
|
this._getFocusEmitter().emit('focus', this._toFocusOnNavigationComplete);
|
|
this._toFocusOnNavigationComplete = null;
|
|
}
|
|
this._handleNavigatorStackChanged(e);
|
|
},
|
|
|
|
_routeToStackItem: function(routeArg: Route, i: number) {
|
|
var {component, wrapperStyle, passProps, ...route} = routeArg;
|
|
var {itemWrapperStyle, ...props} = this.props;
|
|
var shouldUpdateChild =
|
|
this.state.updatingAllIndicesAtOrBeyond != null &&
|
|
this.state.updatingAllIndicesAtOrBeyond >= i;
|
|
var Component = component;
|
|
return (
|
|
<StaticContainer key={'nav' + i} shouldUpdate={shouldUpdateChild}>
|
|
<RCTNavigatorItem
|
|
{...props}
|
|
{...route}
|
|
style={[
|
|
styles.stackItem,
|
|
itemWrapperStyle,
|
|
wrapperStyle
|
|
]}>
|
|
<Component
|
|
navigator={this.navigator}
|
|
route={route}
|
|
{...passProps}
|
|
/>
|
|
</RCTNavigatorItem>
|
|
</StaticContainer>
|
|
);
|
|
},
|
|
|
|
renderNavigationStackItems: function() {
|
|
var shouldRecurseToNavigator =
|
|
this.state.makingNavigatorRequest ||
|
|
this.state.updatingAllIndicesAtOrBeyond !== null;
|
|
// If not recursing update to navigator at all, may as well avoid
|
|
// computation of navigator children.
|
|
var items = shouldRecurseToNavigator ?
|
|
this.state.routeStack.map(this._routeToStackItem) : null;
|
|
return (
|
|
<StaticContainer shouldUpdate={shouldRecurseToNavigator}>
|
|
<NavigatorTransitionerIOS
|
|
ref={TRANSITIONER_REF}
|
|
style={styles.transitioner}
|
|
vertical={this.props.vertical}
|
|
requestedTopOfStack={this.state.requestedTopOfStack}
|
|
onNavigationComplete={this.handleNavigationComplete}>
|
|
{items}
|
|
</NavigatorTransitionerIOS>
|
|
</StaticContainer>
|
|
);
|
|
},
|
|
|
|
render: function() {
|
|
return (
|
|
<View style={this.props.style}>
|
|
{this.renderNavigationStackItems()}
|
|
</View>
|
|
);
|
|
},
|
|
});
|
|
|
|
var styles = StyleSheet.create({
|
|
stackItem: {
|
|
backgroundColor: 'white',
|
|
overflow: 'hidden',
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
},
|
|
transitioner: {
|
|
flex: 1,
|
|
},
|
|
});
|
|
|
|
var RCTNavigator = requireNativeComponent('RCTNavigator');
|
|
var RCTNavigatorItem = requireNativeComponent('RCTNavItem');
|
|
|
|
module.exports = NavigatorIOS;
|