2015-03-24 16:55:38 +00:00
|
|
|
/**
|
2016-09-20 20:58:45 +00:00
|
|
|
* Copyright (c) 2013-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.
|
2015-03-26 16:28:14 +00:00
|
|
|
*
|
2015-07-24 19:58:12 +00:00
|
|
|
* Facebook, Inc. ("Facebook") owns all right, title and interest, including
|
2015-03-26 16:28:14 +00:00
|
|
|
* all intellectual property and other proprietary rights, in and to the React
|
2015-07-24 19:58:12 +00:00
|
|
|
* Native CustomComponents software (the "Software"). Subject to your
|
2015-03-26 16:28:14 +00:00
|
|
|
* 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
|
2015-07-24 19:58:12 +00:00
|
|
|
* ("Your Software"). Facebook reserves all rights not expressly granted to
|
2015-03-26 16:28:14 +00:00
|
|
|
* 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.
|
2015-03-24 16:55:38 +00:00
|
|
|
*
|
2015-03-26 07:41:30 +00:00
|
|
|
* @providesModule NavigatorBreadcrumbNavigationBar
|
2015-03-24 16:55:38 +00:00
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
const NavigatorBreadcrumbNavigationBarStyles = require('NavigatorBreadcrumbNavigationBarStyles');
|
|
|
|
const NavigatorNavigationBarStylesAndroid = require('NavigatorNavigationBarStylesAndroid');
|
|
|
|
const NavigatorNavigationBarStylesIOS = require('NavigatorNavigationBarStylesIOS');
|
|
|
|
const Platform = require('Platform');
|
|
|
|
const React = require('React');
|
|
|
|
const StyleSheet = require('StyleSheet');
|
|
|
|
const View = require('View');
|
2015-03-24 16:55:38 +00:00
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
const guid = require('guid');
|
|
|
|
const invariant = require('fbjs/lib/invariant');
|
2015-07-17 22:20:04 +00:00
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
const { Map } = require('immutable');
|
2015-04-28 04:13:19 +00:00
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
const Interpolators = NavigatorBreadcrumbNavigationBarStyles.Interpolators;
|
|
|
|
const NavigatorNavigationBarStyles = Platform.OS === 'android' ?
|
2015-10-10 22:30:23 +00:00
|
|
|
NavigatorNavigationBarStylesAndroid : NavigatorNavigationBarStylesIOS;
|
2016-09-20 20:58:45 +00:00
|
|
|
const PropTypes = React.PropTypes;
|
2015-03-24 16:55:38 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Reusable props objects.
|
|
|
|
*/
|
2016-09-20 20:58:45 +00:00
|
|
|
const CRUMB_PROPS = Interpolators.map(() => ({style: {}}));
|
|
|
|
const ICON_PROPS = Interpolators.map(() => ({style: {}}));
|
|
|
|
const SEPARATOR_PROPS = Interpolators.map(() => ({style: {}}));
|
|
|
|
const TITLE_PROPS = Interpolators.map(() => ({style: {}}));
|
|
|
|
const RIGHT_BUTTON_PROPS = Interpolators.map(() => ({style: {}}));
|
2015-03-24 16:55:38 +00:00
|
|
|
|
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
function navStatePresentedIndex(navState) {
|
2015-03-24 16:55:38 +00:00
|
|
|
if (navState.presentedIndex !== undefined) {
|
|
|
|
return navState.presentedIndex;
|
|
|
|
}
|
2015-03-30 22:35:04 +00:00
|
|
|
// TODO: rename `observedTopOfStack` to `presentedIndex` in `NavigatorIOS`
|
|
|
|
return navState.observedTopOfStack;
|
2016-10-11 00:43:33 +00:00
|
|
|
}
|
2015-03-24 16:55:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2016-09-20 20:58:45 +00:00
|
|
|
function initStyle(index, presentedIndex) {
|
2015-03-26 07:41:30 +00:00
|
|
|
return index === presentedIndex ? NavigatorBreadcrumbNavigationBarStyles.Center[index] :
|
|
|
|
index < presentedIndex ? NavigatorBreadcrumbNavigationBarStyles.Left[index] :
|
|
|
|
NavigatorBreadcrumbNavigationBarStyles.Right[index];
|
2016-10-11 00:43:33 +00:00
|
|
|
}
|
2015-03-24 16:55:38 +00:00
|
|
|
|
2016-07-26 08:00:02 +00:00
|
|
|
class NavigatorBreadcrumbNavigationBar extends React.Component {
|
|
|
|
static propTypes = {
|
2015-03-24 22:40:48 +00:00
|
|
|
navigator: PropTypes.shape({
|
2015-03-24 16:55:38 +00:00
|
|
|
push: PropTypes.func,
|
|
|
|
pop: PropTypes.func,
|
|
|
|
replace: PropTypes.func,
|
|
|
|
popToRoute: PropTypes.func,
|
|
|
|
popToTop: PropTypes.func,
|
|
|
|
}),
|
2015-04-01 14:50:51 +00:00
|
|
|
routeMapper: PropTypes.shape({
|
2015-03-24 16:55:38 +00:00
|
|
|
rightContentForRoute: PropTypes.func,
|
|
|
|
titleContentForRoute: PropTypes.func,
|
|
|
|
iconForRoute: PropTypes.func,
|
|
|
|
}),
|
2015-03-30 22:35:04 +00:00
|
|
|
navState: React.PropTypes.shape({
|
|
|
|
routeStack: React.PropTypes.arrayOf(React.PropTypes.object),
|
|
|
|
presentedIndex: React.PropTypes.number,
|
|
|
|
}),
|
2015-04-01 14:50:51 +00:00
|
|
|
style: View.propTypes.style,
|
2016-07-26 08:00:02 +00:00
|
|
|
};
|
2015-03-24 16:55:38 +00:00
|
|
|
|
2016-07-26 08:00:02 +00:00
|
|
|
static Styles = NavigatorBreadcrumbNavigationBarStyles;
|
2015-03-26 07:41:30 +00:00
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
_updateIndexProgress(progress, index, fromIndex, toIndex) {
|
2015-03-24 16:55:38 +00:00
|
|
|
var amount = toIndex > fromIndex ? progress : (1 - progress);
|
|
|
|
var oldDistToCenter = index - fromIndex;
|
|
|
|
var newDistToCenter = index - toIndex;
|
|
|
|
var interpolate;
|
2015-04-28 04:13:19 +00:00
|
|
|
invariant(
|
|
|
|
Interpolators[index],
|
|
|
|
'Cannot find breadcrumb interpolators for ' + index
|
|
|
|
);
|
2015-03-24 16:55:38 +00:00
|
|
|
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)) {
|
2015-08-21 00:57:33 +00:00
|
|
|
this._setPropsIfExists('crumb_' + index, CRUMB_PROPS[index]);
|
2015-03-24 16:55:38 +00:00
|
|
|
}
|
|
|
|
if (interpolate.Icon(ICON_PROPS[index].style, amount)) {
|
2015-08-21 00:57:33 +00:00
|
|
|
this._setPropsIfExists('icon_' + index, ICON_PROPS[index]);
|
2015-03-24 16:55:38 +00:00
|
|
|
}
|
|
|
|
if (interpolate.Separator(SEPARATOR_PROPS[index].style, amount)) {
|
2015-08-21 00:57:33 +00:00
|
|
|
this._setPropsIfExists('separator_' + index, SEPARATOR_PROPS[index]);
|
2015-03-24 16:55:38 +00:00
|
|
|
}
|
|
|
|
if (interpolate.Title(TITLE_PROPS[index].style, amount)) {
|
2015-08-21 00:57:33 +00:00
|
|
|
this._setPropsIfExists('title_' + index, TITLE_PROPS[index]);
|
2015-03-24 16:55:38 +00:00
|
|
|
}
|
|
|
|
var right = this.refs['right_' + index];
|
2016-01-26 09:27:14 +00:00
|
|
|
|
|
|
|
const rightButtonStyle = RIGHT_BUTTON_PROPS[index].style;
|
|
|
|
if (right && interpolate.RightItem(rightButtonStyle, amount)) {
|
|
|
|
right.setNativeProps({
|
|
|
|
style: rightButtonStyle,
|
|
|
|
pointerEvents: rightButtonStyle.opacity === 0 ? 'none' : 'auto',
|
|
|
|
});
|
2015-03-24 16:55:38 +00:00
|
|
|
}
|
2016-09-20 20:58:45 +00:00
|
|
|
}
|
2015-03-24 16:55:38 +00:00
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
updateProgress(progress, fromIndex, toIndex) {
|
2015-03-24 16:55:38 +00:00
|
|
|
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);
|
|
|
|
}
|
2016-09-20 20:58:45 +00:00
|
|
|
}
|
2015-03-24 16:55:38 +00:00
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
onAnimationStart(fromIndex, toIndex) {
|
2015-04-21 11:14:17 +00:00
|
|
|
var max = Math.max(fromIndex, toIndex);
|
|
|
|
var min = Math.min(fromIndex, toIndex);
|
|
|
|
for (var index = min; index <= max; index++) {
|
|
|
|
this._setRenderViewsToHardwareTextureAndroid(index, true);
|
|
|
|
}
|
2016-09-20 20:58:45 +00:00
|
|
|
}
|
2015-04-21 11:14:17 +00:00
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
onAnimationEnd() {
|
2015-04-28 04:13:19 +00:00
|
|
|
var max = this.props.navState.routeStack.length - 1;
|
|
|
|
for (var index = 0; index <= max; index++) {
|
2015-04-21 11:14:17 +00:00
|
|
|
this._setRenderViewsToHardwareTextureAndroid(index, false);
|
|
|
|
}
|
2016-09-20 20:58:45 +00:00
|
|
|
}
|
2015-04-21 11:14:17 +00:00
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
_setRenderViewsToHardwareTextureAndroid(index, renderToHardwareTexture) {
|
2015-04-21 11:14:17 +00:00
|
|
|
var props = {
|
|
|
|
renderToHardwareTextureAndroid: renderToHardwareTexture,
|
|
|
|
};
|
|
|
|
|
2015-08-21 00:57:33 +00:00
|
|
|
this._setPropsIfExists('icon_' + index, props);
|
|
|
|
this._setPropsIfExists('separator_' + index, props);
|
|
|
|
this._setPropsIfExists('title_' + index, props);
|
|
|
|
this._setPropsIfExists('right_' + index, props);
|
2016-09-20 20:58:45 +00:00
|
|
|
}
|
2015-04-21 11:14:17 +00:00
|
|
|
|
2016-07-26 08:00:02 +00:00
|
|
|
componentWillMount() {
|
2016-02-09 03:51:06 +00:00
|
|
|
this._reset();
|
2016-07-26 08:00:02 +00:00
|
|
|
}
|
2015-07-17 22:20:04 +00:00
|
|
|
|
2016-07-26 08:00:02 +00:00
|
|
|
render() {
|
2015-03-24 16:55:38 +00:00
|
|
|
var navState = this.props.navState;
|
2015-07-17 22:20:04 +00:00
|
|
|
var icons = navState && navState.routeStack.map(this._getBreadcrumb);
|
|
|
|
var titles = navState.routeStack.map(this._getTitle);
|
|
|
|
var buttons = navState.routeStack.map(this._getRightButton);
|
2016-09-20 20:58:45 +00:00
|
|
|
|
2015-03-24 16:55:38 +00:00
|
|
|
return (
|
2016-02-09 03:51:06 +00:00
|
|
|
<View
|
|
|
|
key={this._key}
|
|
|
|
style={[styles.breadCrumbContainer, this.props.style]}>
|
2015-03-24 16:55:38 +00:00
|
|
|
{titles}
|
|
|
|
{icons}
|
|
|
|
{buttons}
|
|
|
|
</View>
|
|
|
|
);
|
2016-07-26 08:00:02 +00:00
|
|
|
}
|
2015-03-24 16:55:38 +00:00
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
immediatelyRefresh() {
|
2016-02-09 03:51:06 +00:00
|
|
|
this._reset();
|
|
|
|
this.forceUpdate();
|
2016-09-20 20:58:45 +00:00
|
|
|
}
|
2016-02-09 03:51:06 +00:00
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
_reset() {
|
2016-02-09 03:51:06 +00:00
|
|
|
this._key = guid();
|
|
|
|
this._descriptors = {
|
|
|
|
title: new Map(),
|
|
|
|
right: new Map(),
|
|
|
|
};
|
2016-09-20 20:58:45 +00:00
|
|
|
}
|
2016-02-09 03:51:06 +00:00
|
|
|
|
2016-07-26 08:00:02 +00:00
|
|
|
_getBreadcrumb = (route, index) => {
|
2016-09-20 20:58:45 +00:00
|
|
|
/**
|
|
|
|
* To prevent the case where titles on an empty navigation stack covers the first icon and
|
|
|
|
* becomes partially unpressable, we set the first breadcrumb to be unpressable by default, and
|
|
|
|
* make it pressable when there are multiple items in the stack.
|
|
|
|
*/
|
|
|
|
const pointerEvents = (
|
|
|
|
(this.props.navState.routeStack.length <= 1 && index === 0) ?
|
|
|
|
'none' :
|
|
|
|
'auto'
|
|
|
|
);
|
|
|
|
const navBarRouteMapper = this.props.routeMapper;
|
|
|
|
const firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState));
|
2015-07-17 22:20:04 +00:00
|
|
|
|
|
|
|
var breadcrumbDescriptor = (
|
2015-12-02 06:04:55 +00:00
|
|
|
<View
|
|
|
|
key={'crumb_' + index}
|
2016-09-20 20:58:45 +00:00
|
|
|
pointerEvents={pointerEvents}
|
2015-12-02 06:04:55 +00:00
|
|
|
ref={'crumb_' + index}
|
|
|
|
style={firstStyles.Crumb}>
|
2015-07-17 22:20:04 +00:00
|
|
|
<View ref={'icon_' + index} style={firstStyles.Icon}>
|
|
|
|
{navBarRouteMapper.iconForRoute(route, this.props.navigator)}
|
2015-03-24 16:55:38 +00:00
|
|
|
</View>
|
2015-07-17 22:20:04 +00:00
|
|
|
<View ref={'separator_' + index} style={firstStyles.Separator}>
|
|
|
|
{navBarRouteMapper.separatorForRoute(route, this.props.navigator)}
|
|
|
|
</View>
|
|
|
|
</View>
|
2015-03-24 16:55:38 +00:00
|
|
|
);
|
2015-07-17 22:20:04 +00:00
|
|
|
|
|
|
|
return breadcrumbDescriptor;
|
2016-07-26 08:00:02 +00:00
|
|
|
};
|
2015-03-24 16:55:38 +00:00
|
|
|
|
2016-07-26 08:00:02 +00:00
|
|
|
_getTitle = (route, index) => {
|
2015-07-17 22:20:04 +00:00
|
|
|
if (this._descriptors.title.has(route)) {
|
|
|
|
return this._descriptors.title.get(route);
|
2015-03-24 16:55:38 +00:00
|
|
|
}
|
2015-07-17 22:20:04 +00:00
|
|
|
|
|
|
|
var titleContent = this.props.routeMapper.titleContentForRoute(
|
|
|
|
this.props.navState.routeStack[index],
|
2015-03-24 22:40:48 +00:00
|
|
|
this.props.navigator
|
2015-03-24 16:55:38 +00:00
|
|
|
);
|
|
|
|
var firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState));
|
2015-07-17 22:20:04 +00:00
|
|
|
|
|
|
|
var titleDescriptor = (
|
2015-12-02 06:04:55 +00:00
|
|
|
<View
|
|
|
|
key={'title_' + index}
|
|
|
|
ref={'title_' + index}
|
|
|
|
style={firstStyles.Title}>
|
2015-07-17 22:20:04 +00:00
|
|
|
{titleContent}
|
|
|
|
</View>
|
2015-03-24 16:55:38 +00:00
|
|
|
);
|
2015-07-17 22:20:04 +00:00
|
|
|
this._descriptors.title = this._descriptors.title.set(route, titleDescriptor);
|
|
|
|
return titleDescriptor;
|
2016-07-26 08:00:02 +00:00
|
|
|
};
|
2015-03-24 16:55:38 +00:00
|
|
|
|
2016-07-26 08:00:02 +00:00
|
|
|
_getRightButton = (route, index) => {
|
2015-07-17 22:20:04 +00:00
|
|
|
if (this._descriptors.right.has(route)) {
|
|
|
|
return this._descriptors.right.get(route);
|
2015-03-24 16:55:38 +00:00
|
|
|
}
|
2015-07-17 22:20:04 +00:00
|
|
|
var rightContent = this.props.routeMapper.rightContentForRoute(
|
|
|
|
this.props.navState.routeStack[index],
|
2015-03-24 22:40:48 +00:00
|
|
|
this.props.navigator
|
2015-03-24 16:55:38 +00:00
|
|
|
);
|
|
|
|
if (!rightContent) {
|
2015-07-17 22:20:04 +00:00
|
|
|
this._descriptors.right = this._descriptors.right.set(route, null);
|
2015-03-24 16:55:38 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
var firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState));
|
2015-07-17 22:20:04 +00:00
|
|
|
var rightButtonDescriptor = (
|
2015-12-02 06:04:55 +00:00
|
|
|
<View
|
|
|
|
key={'right_' + index}
|
|
|
|
ref={'right_' + index}
|
|
|
|
style={firstStyles.RightItem}>
|
2015-07-17 22:20:04 +00:00
|
|
|
{rightContent}
|
|
|
|
</View>
|
2015-03-24 16:55:38 +00:00
|
|
|
);
|
2015-07-17 22:20:04 +00:00
|
|
|
this._descriptors.right = this._descriptors.right.set(route, rightButtonDescriptor);
|
|
|
|
return rightButtonDescriptor;
|
2016-07-26 08:00:02 +00:00
|
|
|
};
|
2015-08-21 00:57:33 +00:00
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
_setPropsIfExists(ref, props) {
|
2015-08-21 00:57:33 +00:00
|
|
|
var ref = this.refs[ref];
|
|
|
|
ref && ref.setNativeProps(props);
|
2016-09-20 20:58:45 +00:00
|
|
|
}
|
2016-07-26 08:00:02 +00:00
|
|
|
}
|
2015-03-24 16:55:38 +00:00
|
|
|
|
2016-09-20 20:58:45 +00:00
|
|
|
const styles = StyleSheet.create({
|
2015-03-24 16:55:38 +00:00
|
|
|
breadCrumbContainer: {
|
|
|
|
overflow: 'hidden',
|
|
|
|
position: 'absolute',
|
2015-03-26 07:41:30 +00:00
|
|
|
height: NavigatorNavigationBarStyles.General.TotalNavHeight,
|
2015-03-24 16:55:38 +00:00
|
|
|
top: 0,
|
|
|
|
left: 0,
|
2015-04-17 22:56:05 +00:00
|
|
|
right: 0,
|
2015-03-24 16:55:38 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2015-03-26 07:41:30 +00:00
|
|
|
module.exports = NavigatorBreadcrumbNavigationBar;
|