Created
November 17, 2015 15:54
-
-
Save jaygarcia/5c0e91c5c2443ac630f4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright (c) 2015, Facebook, Inc. All rights reserved. | |
* | |
* 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 NavigatorNavigationBar | |
*/ | |
'use strict'; | |
var React = require('React'); | |
var NavigatorNavigationBarStylesAndroid = require('NavigatorNavigationBarStylesAndroid'); | |
var NavigatorNavigationBarStylesIOS = require('NavigatorNavigationBarStylesIOS'); | |
var Platform = require('Platform'); | |
var StaticContainer = require('StaticContainer.react'); | |
var StyleSheet = require('StyleSheet'); | |
var View = require('View'); | |
var { Map } = require('immutable'); | |
var COMPONENT_NAMES = ['Title', 'LeftButton', 'RightButton']; | |
var NavigatorNavigationBarStyles = Platform.OS === 'android' ? | |
NavigatorNavigationBarStylesAndroid : NavigatorNavigationBarStylesIOS; | |
var navStatePresentedIndex = function(navState) { | |
if (navState.presentedIndex !== undefined) { | |
return navState.presentedIndex; | |
} | |
// TODO: rename `observedTopOfStack` to `presentedIndex` in `NavigatorIOS` | |
return navState.observedTopOfStack; | |
}; | |
var NavigatorNavigationBar = React.createClass({ | |
propTypes: { | |
navigator: React.PropTypes.object, | |
routeMapper: React.PropTypes.shape({ | |
Title: React.PropTypes.func.isRequired, | |
LeftButton: React.PropTypes.func.isRequired, | |
RightButton: React.PropTypes.func.isRequired, | |
}).isRequired, | |
navState: React.PropTypes.shape({ | |
routeStack: React.PropTypes.arrayOf(React.PropTypes.object), | |
presentedIndex: React.PropTypes.number, | |
}), | |
navigationStyles: React.PropTypes.object, | |
style: View.propTypes.style, | |
}, | |
statics: { | |
Styles: NavigatorNavigationBarStyles, | |
StylesAndroid: NavigatorNavigationBarStylesAndroid, | |
StylesIOS: NavigatorNavigationBarStylesIOS, | |
}, | |
getDefaultProps() { | |
return { | |
navigationStyles: NavigatorNavigationBarStyles, | |
}; | |
}, | |
componentWillMount: function() { | |
this._components = {}; | |
this._descriptors = {}; | |
COMPONENT_NAMES.forEach(componentName => { | |
this._components[componentName] = new Map(); | |
this._descriptors[componentName] = new Map(); | |
}); | |
}, | |
_getReusableProps: function( | |
/*string*/componentName, | |
/*number*/index | |
) /*object*/ { | |
if (!this._reusableProps) { | |
this._reusableProps = {}; | |
} | |
var propStack = this._reusableProps[componentName]; | |
if (!propStack) { | |
propStack = this._reusableProps[componentName] = []; | |
} | |
var props = propStack[index]; | |
if (!props) { | |
props = propStack[index] = {style:{}}; | |
} | |
return props; | |
}, | |
_updateIndexProgress: function( | |
/*number*/progress, | |
/*number*/index, | |
/*number*/fromIndex, | |
/*number*/toIndex | |
) { | |
var amount = toIndex > fromIndex ? progress : (1 - progress); | |
var oldDistToCenter = index - fromIndex; | |
var newDistToCenter = index - toIndex; | |
var interpolate; | |
if (oldDistToCenter > 0 && newDistToCenter === 0 || | |
newDistToCenter > 0 && oldDistToCenter === 0) { | |
interpolate = this.props.navigationStyles.Interpolators.RightToCenter; | |
} else if (oldDistToCenter < 0 && newDistToCenter === 0 || | |
newDistToCenter < 0 && oldDistToCenter === 0) { | |
interpolate = this.props.navigationStyles.Interpolators.CenterToLeft; | |
} else if (oldDistToCenter === newDistToCenter) { | |
interpolate = this.props.navigationStyles.Interpolators.RightToCenter; | |
} else { | |
interpolate = this.props.navigationStyles.Interpolators.RightToLeft; | |
} | |
COMPONENT_NAMES.forEach(function (componentName) { | |
var component = this._components[componentName].get(this.props.navState.routeStack[index]); | |
var props = this._getReusableProps(componentName, index); | |
if (component && interpolate[componentName](props.style, amount)) { | |
component.setNativeProps(props); | |
} | |
}, this); | |
}, | |
updateProgress: function( | |
/*number*/progress, | |
/*number*/fromIndex, | |
/*number*/toIndex | |
) { | |
var max = Math.max(fromIndex, toIndex); | |
var min = Math.min(fromIndex, toIndex); | |
for (var index = min; index <= max; index++) { | |
this._updateIndexProgress(progress, index, fromIndex, toIndex); | |
} | |
}, | |
render: function() { | |
var navBarStyle = { | |
height: this.props.navigationStyles.General.TotalNavHeight, | |
}; | |
var navState = this.props.navState; | |
var components = COMPONENT_NAMES.map(function (componentName) { | |
return navState.routeStack.map( | |
this._getComponent.bind(this, componentName) | |
); | |
}, this); | |
return ( | |
<View style={[styles.navBarContainer, navBarStyle, this.props.style]}> | |
{components} | |
</View> | |
); | |
}, | |
_getComponent: function( | |
/*string*/componentName, | |
/*object*/route, | |
/*number*/index | |
) /*?Object*/ { | |
if (this._descriptors[componentName].includes(route)) { | |
return this._descriptors[componentName].get(route); | |
} | |
var rendered = null; | |
var content = this.props.routeMapper[componentName]( | |
this.props.navState.routeStack[index], | |
this.props.navigator, | |
index, | |
this.props.navState | |
); | |
if (!content) { | |
return null; | |
} | |
var initialStage = index === navStatePresentedIndex(this.props.navState) ? | |
this.props.navigationStyles.Stages.Center : | |
this.props.navigationStyles.Stages.Left; | |
rendered = ( | |
<View | |
ref={(ref) => { | |
this._components[componentName] = this._components[componentName].set(route, ref); | |
}} | |
style={initialStage[componentName]}> | |
{content} | |
</View> | |
); | |
this._descriptors[componentName] = this._descriptors[componentName].set(route, rendered); | |
return rendered; | |
}, | |
}); | |
var styles = StyleSheet.create({ | |
navBarContainer: { | |
position: 'absolute', | |
top: 0, | |
left: 0, | |
right: 0, | |
backgroundColor: 'transparent', | |
}, | |
}); | |
module.exports = NavigatorNavigationBar; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment