UNPKG

citong-react-web

Version:

A framework for building web apps with React

266 lines (234 loc) 8.79 kB
/** * Copyright (c) 2015-present, Alibaba Group Holding Limited. * All rights reserved. * * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * @providesModule ReactNavigatorBreadcrumbNavigationBar */ 'use strict'; import React, { Component, PropTypes } from 'react'; import NavigatorBreadcrumbNavigationBarStyles from 'ReactNavigatorBreadcrumbNavigationBarStyles'; import NavigatorNavigationBarStylesAndroid from 'ReactNavigatorNavigationBarStylesAndroid'; import NavigatorNavigationBarStylesIOS from 'ReactNavigatorNavigationBarStylesIOS'; import Platform from 'ReactPlatform'; import StyleSheet from 'ReactStyleSheet'; import View from 'ReactView'; import { Map } from 'immutable'; import invariant from 'fbjs/lib/invariant'; import autobind from 'autobind-decorator'; var Interpolators = NavigatorBreadcrumbNavigationBarStyles.Interpolators; var NavigatorNavigationBarStyles = Platform.OS === 'android' ? NavigatorNavigationBarStylesAndroid : NavigatorNavigationBarStylesIOS; /** * 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: {}};}); var navStatePresentedIndex = function(navState) { if (navState.presentedIndex !== undefined) { return navState.presentedIndex; } // TODO: rename `observedTopOfStack` to `presentedIndex` in `NavigatorIOS` 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 ? NavigatorBreadcrumbNavigationBarStyles.Center[index] : index < presentedIndex ? NavigatorBreadcrumbNavigationBarStyles.Left[index] : NavigatorBreadcrumbNavigationBarStyles.Right[index]; }; class NavigatorBreadcrumbNavigationBar extends Component { static propTypes = { navigator: PropTypes.shape({ push: PropTypes.func, pop: PropTypes.func, replace: PropTypes.func, popToRoute: PropTypes.func, popToTop: PropTypes.func, }), routeMapper: PropTypes.shape({ rightContentForRoute: PropTypes.func, titleContentForRoute: PropTypes.func, iconForRoute: PropTypes.func, }), navState: React.PropTypes.shape({ routeStack: React.PropTypes.arrayOf(React.PropTypes.object), presentedIndex: React.PropTypes.number, }), style: View.propTypes.style, } static statics = { Styles: NavigatorBreadcrumbNavigationBarStyles, } _updateIndexProgress(progress, index, fromIndex, toIndex) { var amount = toIndex > fromIndex ? progress : (1 - progress); var oldDistToCenter = index - fromIndex; var newDistToCenter = index - toIndex; var interpolate; invariant( Interpolators[index], 'Cannot find breadcrumb interpolators for ' + index ); 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._setPropsIfExists('crumb_' + index, CRUMB_PROPS[index]); } if (interpolate.Icon(ICON_PROPS[index].style, amount)) { this._setPropsIfExists('icon_' + index, ICON_PROPS[index]); } if (interpolate.Separator(SEPARATOR_PROPS[index].style, amount)) { this._setPropsIfExists('separator_' + index, SEPARATOR_PROPS[index]); } if (interpolate.Title(TITLE_PROPS[index].style, amount)) { this._setPropsIfExists('title_' + index, 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(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); } } onAnimationStart(fromIndex, toIndex) { var max = Math.max(fromIndex, toIndex); var min = Math.min(fromIndex, toIndex); for (var index = min; index <= max; index++) { this._setRenderViewsToHardwareTextureAndroid(index, true); } } onAnimationEnd() { var max = this.props.navState.routeStack.length - 1; for (var index = 0; index <= max; index++) { this._setRenderViewsToHardwareTextureAndroid(index, false); } } _setRenderViewsToHardwareTextureAndroid(index, renderToHardwareTexture) { var props = { renderToHardwareTextureAndroid: renderToHardwareTexture, }; this._setPropsIfExists('icon_' + index, props); this._setPropsIfExists('separator_' + index, props); this._setPropsIfExists('title_' + index, props); this._setPropsIfExists('right_' + index, props); } componentWillMount() { this._descriptors = { crumb: new Map(), title: new Map(), right: new Map(), }; } render() { var navState = this.props.navState; var icons = navState && navState.routeStack.map(this._getBreadcrumb); var titles = navState.routeStack.map(this._getTitle); var buttons = navState.routeStack.map(this._getRightButton); return ( <View style={[styles.breadCrumbContainer, this.props.style]}> {titles} {icons} {buttons} </View> ); } _getBreadcrumb(route, index) { if (this._descriptors.crumb.has(route)) { return this._descriptors.crumb.get(route); } var navBarRouteMapper = this.props.routeMapper; var firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState)); var breadcrumbDescriptor = ( <View ref={'crumb_' + index} style={firstStyles.Crumb}> <View ref={'icon_' + index} style={firstStyles.Icon}> {navBarRouteMapper.iconForRoute(route, this.props.navigator)} </View> <View ref={'separator_' + index} style={firstStyles.Separator}> {navBarRouteMapper.separatorForRoute(route, this.props.navigator)} </View> </View> ); this._descriptors.crumb = this._descriptors.crumb.set(route, breadcrumbDescriptor); return breadcrumbDescriptor; } _getTitle(route, index) { if (this._descriptors.title.has(route)) { return this._descriptors.title.get(route); } var titleContent = this.props.routeMapper.titleContentForRoute( this.props.navState.routeStack[index], this.props.navigator ); var firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState)); var titleDescriptor = ( <View ref={'title_' + index} style={firstStyles.Title}> {titleContent} </View> ); this._descriptors.title = this._descriptors.title.set(route, titleDescriptor); return titleDescriptor; } _getRightButton(route, index) { if (this._descriptors.right.has(route)) { return this._descriptors.right.get(route); } var rightContent = this.props.routeMapper.rightContentForRoute( this.props.navState.routeStack[index], this.props.navigator ); if (!rightContent) { this._descriptors.right = this._descriptors.right.set(route, null); return null; } var firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState)); var rightButtonDescriptor = ( <View ref={'right_' + index} style={firstStyles.RightItem}> {rightContent} </View> ); this._descriptors.right = this._descriptors.right.set(route, rightButtonDescriptor); return rightButtonDescriptor; } _setPropsIfExists(ref, props) { var ref = this.refs[ref]; ref && ref.setNativeProps(props); } }; var styles = StyleSheet.create({ breadCrumbContainer: { overflow: 'hidden', position: 'absolute', height: NavigatorNavigationBarStyles.General.TotalNavHeight, top: 0, left: 0, right: 0, }, }); autobind(NavigatorBreadcrumbNavigationBar); module.exports = NavigatorBreadcrumbNavigationBar;