/** * Copyright 2004-present Facebook. All Rights Reserved. * * @providesModule BreadcrumbNavigationBar */ 'use strict'; var BreadcrumbNavigationBarStyles = require('BreadcrumbNavigationBarStyles'); var PixelRatio = require('PixelRatio'); var React = require('React'); var NavigationBarStyles = require('NavigationBarStyles'); var StaticContainer = require('StaticContainer.react'); var StyleSheet = require('StyleSheet'); var View = require('View'); var Interpolators = BreadcrumbNavigationBarStyles.Interpolators; var PropTypes = React.PropTypes; /** * Reusable props objects. */ var CRUMB_PROPS = Interpolators.map(() => {return {style: {}};}); var ICON_PROPS = Interpolators.map(() => {return {style: {}};}); var SEPARATOR_PROPS = Interpolators.map(() => {return {style: {}};}); var TITLE_PROPS = Interpolators.map(() => {return {style: {}};}); var RIGHT_BUTTON_PROPS = Interpolators.map(() => {return {style: {}};}); /** * TODO: Rename `observedTopOfStack` to `presentedIndex` in `NavigationStack`. */ var navStatePresentedIndex = function(navState) { if (navState.presentedIndex !== undefined) { return navState.presentedIndex; } else { return navState.observedTopOfStack; } }; /** * The first route is initially rendered using a different style than all * future routes. * * @param {number} index Index of breadcrumb. * @return {object} Style config for initial rendering of index. */ var initStyle = function(index, presentedIndex) { return index === presentedIndex ? BreadcrumbNavigationBarStyles.Center[index] : index < presentedIndex ? BreadcrumbNavigationBarStyles.Left[index] : BreadcrumbNavigationBarStyles.Right[index]; }; var BreadcrumbNavigationBar = React.createClass({ propTypes: { navigator: PropTypes.shape({ push: PropTypes.func, pop: PropTypes.func, replace: PropTypes.func, popToRoute: PropTypes.func, popToTop: PropTypes.func, }), navigationBarRouteMapper: PropTypes.shape({ rightContentForRoute: PropTypes.func, titleContentForRoute: PropTypes.func, iconForRoute: PropTypes.func, }), navigationBarStyles: PropTypes.number, }, _updateIndexProgress: function(progress, index, fromIndex, 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 = Interpolators[index].RightToCenter; } else if (oldDistToCenter < 0 && newDistToCenter === 0 || newDistToCenter < 0 && oldDistToCenter === 0) { interpolate = Interpolators[index].CenterToLeft; } else if (oldDistToCenter === newDistToCenter) { interpolate = Interpolators[index].RightToCenter; } else { interpolate = Interpolators[index].RightToLeft; } if (interpolate.Crumb(CRUMB_PROPS[index].style, amount)) { this.refs['crumb_' + index].setNativeProps(CRUMB_PROPS[index]); } if (interpolate.Icon(ICON_PROPS[index].style, amount)) { this.refs['icon_' + index].setNativeProps(ICON_PROPS[index]); } if (interpolate.Separator(SEPARATOR_PROPS[index].style, amount)) { this.refs['separator_' + index].setNativeProps(SEPARATOR_PROPS[index]); } if (interpolate.Title(TITLE_PROPS[index].style, amount)) { this.refs['title_' + index].setNativeProps(TITLE_PROPS[index]); } var right = this.refs['right_' + index]; if (right && interpolate.RightItem(RIGHT_BUTTON_PROPS[index].style, amount)) { right.setNativeProps(RIGHT_BUTTON_PROPS[index]); } }, updateProgress: function(progress, fromIndex, 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 navState = this.props.navState; var icons = navState && navState.routeStack.map(this._renderOrReturnBreadcrumb); var titles = navState.routeStack.map(this._renderOrReturnTitle); var buttons = navState.routeStack.map(this._renderOrReturnRightButton); return ( {titles} {icons} {buttons} ); }, _renderOrReturnBreadcrumb: function(route, index) { var uid = this.props.navState.idStack[index]; var navBarRouteMapper = this.props.navigationBarRouteMapper; var navOps = this.props.navigator; var alreadyRendered = this.refs['crumbContainer' + uid]; if (alreadyRendered) { // Don't bother re-calculating the children return ( ); } var firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState)); return ( {navBarRouteMapper.iconForRoute(route, navOps)} {navBarRouteMapper.separatorForRoute(route, navOps)} ); }, _renderOrReturnTitle: function(route, index) { var navState = this.props.navState; var uid = navState.idStack[index]; var alreadyRendered = this.refs['titleContainer' + uid]; if (alreadyRendered) { // Don't bother re-calculating the children return ( ); } var navBarRouteMapper = this.props.navigationBarRouteMapper; var titleContent = navBarRouteMapper.titleContentForRoute( navState.routeStack[index], this.props.navigator ); var firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState)); return ( {titleContent} ); }, _renderOrReturnRightButton: function(route, index) { var navState = this.props.navState; var navBarRouteMapper = this.props.navigationBarRouteMapper; var uid = navState.idStack[index]; var alreadyRendered = this.refs['rightContainer' + uid]; if (alreadyRendered) { // Don't bother re-calculating the children return ( ); } var rightContent = navBarRouteMapper.rightContentForRoute( navState.routeStack[index], this.props.navigator ); if (!rightContent) { return null; } var firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState)); return ( {rightContent} ); }, }); var styles = StyleSheet.create({ breadCrumbContainer: { overflow: 'hidden', position: 'absolute', height: NavigationBarStyles.General.TotalNavHeight, top: 0, left: 0, width: NavigationBarStyles.General.ScreenWidth, }, }); module.exports = BreadcrumbNavigationBar;