/** * 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 StyleSheet = require('StyleSheet'); var View = require('View'); var guid = require('guid'); 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; }; class NavigatorNavigationBar extends React.Component { static 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, }; static Styles = NavigatorNavigationBarStyles; static StylesAndroid = NavigatorNavigationBarStylesAndroid; static StylesIOS = NavigatorNavigationBarStylesIOS; static defaultProps = { navigationStyles: NavigatorNavigationBarStyles, }; componentWillMount() { this._reset(); } /** * Stop transtion, immediately resets the cached state and re-render the * whole view. */ immediatelyRefresh = () => { this._reset(); this.forceUpdate(); }; _reset = () => { this._key = guid(); this._reusableProps = {}; this._components = {}; this._descriptors = {}; COMPONENT_NAMES.forEach(componentName => { this._components[componentName] = new Map(); this._descriptors[componentName] = new Map(); }); }; _getReusableProps = (/*string*/componentName, /*number*/index) => /*object*/ { var propStack = this._reusableProps[componentName]; if (!propStack) { propStack = this._reusableProps[componentName] = []; } var props = propStack[index]; if (!props) { props = propStack[index] = {style:{}}; } return props; }; _updateIndexProgress = ( /*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)) { props.pointerEvents = props.style.opacity === 0 ? 'none' : 'box-none'; component.setNativeProps(props); } }, this); }; updateProgress = (/*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() { var navBarStyle = { height: this.props.navigationStyles.General.TotalNavHeight, }; var navState = this.props.navState; var components = navState.routeStack.map((route, index) => COMPONENT_NAMES.map(componentName => this._getComponent(componentName, route, index) ) ); return ( {components} ); } _getComponent = (/*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 componentIsActive = index === navStatePresentedIndex(this.props.navState); var initialStage = componentIsActive ? this.props.navigationStyles.Stages.Center : this.props.navigationStyles.Stages.Left; rendered = ( { this._components[componentName] = this._components[componentName].set(route, ref); }} pointerEvents={componentIsActive ? 'box-none' : 'none'} style={initialStage[componentName]}> {content} ); 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;