2016-02-05 22:25:17 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2015-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.
|
|
|
|
*
|
|
|
|
* @providesModule NavigationAnimatedView
|
|
|
|
* @flow
|
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
|
2016-03-04 22:56:37 +00:00
|
|
|
const Animated = require('Animated');
|
|
|
|
const NavigationContainer = require('NavigationContainer');
|
|
|
|
const NavigationPropTypes = require('NavigationPropTypes');
|
2016-03-21 18:13:59 +00:00
|
|
|
const NavigationScenesReducer = require('NavigationScenesReducer');
|
2016-03-04 22:56:37 +00:00
|
|
|
const React = require('react-native');
|
2016-03-11 01:16:19 +00:00
|
|
|
const StyleSheet = require('StyleSheet');
|
2016-03-04 22:56:37 +00:00
|
|
|
const View = require('View');
|
2016-02-05 22:25:17 +00:00
|
|
|
|
|
|
|
import type {
|
2016-03-04 22:56:37 +00:00
|
|
|
NavigationAnimatedValue,
|
|
|
|
NavigationAnimationSetter,
|
2016-03-08 18:56:58 +00:00
|
|
|
NavigationLayout,
|
2016-02-05 22:25:17 +00:00
|
|
|
NavigationParentState,
|
2016-03-04 22:56:37 +00:00
|
|
|
NavigationScene,
|
|
|
|
NavigationSceneRenderer,
|
|
|
|
} from 'NavigationTypeDefinition';
|
2016-03-03 12:20:34 +00:00
|
|
|
|
2016-03-04 22:56:37 +00:00
|
|
|
type Props = {
|
2016-03-09 07:31:27 +00:00
|
|
|
applyAnimation: NavigationAnimationSetter,
|
2016-03-04 22:56:37 +00:00
|
|
|
navigationState: NavigationParentState,
|
|
|
|
onNavigate: (action: any) => void,
|
|
|
|
renderOverlay: ?NavigationSceneRenderer,
|
2016-03-09 07:31:27 +00:00
|
|
|
renderScene: NavigationSceneRenderer,
|
2016-03-04 22:56:37 +00:00
|
|
|
style: any,
|
2016-02-05 22:25:17 +00:00
|
|
|
};
|
|
|
|
|
2016-03-04 22:56:37 +00:00
|
|
|
type State = {
|
|
|
|
position: NavigationAnimatedValue,
|
|
|
|
scenes: Array<NavigationScene>,
|
2016-02-29 21:21:43 +00:00
|
|
|
};
|
|
|
|
|
2016-03-04 22:56:37 +00:00
|
|
|
const {PropTypes} = React;
|
2016-02-23 00:15:43 +00:00
|
|
|
|
2016-03-04 22:56:37 +00:00
|
|
|
const propTypes = {
|
2016-03-09 07:31:27 +00:00
|
|
|
applyAnimation: PropTypes.func,
|
2016-03-04 22:56:37 +00:00
|
|
|
navigationState: NavigationPropTypes.navigationState.isRequired,
|
|
|
|
onNavigate: PropTypes.func.isRequired,
|
|
|
|
renderOverlay: PropTypes.func,
|
2016-03-09 07:31:27 +00:00
|
|
|
renderScene: PropTypes.func.isRequired,
|
2016-03-04 22:56:37 +00:00
|
|
|
};
|
2016-02-05 22:25:17 +00:00
|
|
|
|
2016-03-04 22:56:37 +00:00
|
|
|
const defaultProps = {
|
2016-03-09 07:31:27 +00:00
|
|
|
applyAnimation: (
|
2016-03-04 22:56:37 +00:00
|
|
|
position: NavigationAnimatedValue,
|
|
|
|
navigationState: NavigationParentState,
|
|
|
|
) => {
|
|
|
|
Animated.spring(
|
|
|
|
position,
|
|
|
|
{
|
|
|
|
bounciness: 0,
|
|
|
|
toValue: navigationState.index,
|
|
|
|
}
|
|
|
|
).start();
|
|
|
|
},
|
2016-02-05 22:25:17 +00:00
|
|
|
};
|
|
|
|
|
2016-03-04 22:56:37 +00:00
|
|
|
class NavigationAnimatedView
|
|
|
|
extends React.Component<any, Props, State> {
|
|
|
|
|
2016-03-08 18:56:58 +00:00
|
|
|
_layout: NavigationLayout;
|
|
|
|
_onLayout: (event: any) => void;
|
|
|
|
_onProgressChange: (data: {value: number}) => void;
|
2016-03-05 23:47:51 +00:00
|
|
|
_positionListener: any;
|
2016-03-04 22:56:37 +00:00
|
|
|
|
2016-02-05 22:25:17 +00:00
|
|
|
props: Props;
|
2016-03-04 22:56:37 +00:00
|
|
|
state: State;
|
|
|
|
|
2016-03-08 18:56:58 +00:00
|
|
|
constructor(props: Props, context: any) {
|
|
|
|
super(props, context);
|
|
|
|
|
|
|
|
this._layout = {
|
|
|
|
initWidth: 0,
|
|
|
|
initHeight: 0,
|
|
|
|
width: new Animated.Value(0),
|
|
|
|
height: new Animated.Value(0),
|
|
|
|
};
|
2016-03-01 17:43:35 +00:00
|
|
|
|
2016-02-05 22:25:17 +00:00
|
|
|
this.state = {
|
|
|
|
position: new Animated.Value(this.props.navigationState.index),
|
2016-03-21 18:13:59 +00:00
|
|
|
scenes: NavigationScenesReducer([], this.props.navigationState),
|
2016-02-05 22:25:17 +00:00
|
|
|
};
|
|
|
|
}
|
2016-03-08 18:56:58 +00:00
|
|
|
|
|
|
|
componentWillMount(): void {
|
|
|
|
this._onLayout = this._onLayout.bind(this);
|
|
|
|
this._onProgressChange = this._onProgressChange.bind(this);
|
2016-02-05 22:25:17 +00:00
|
|
|
}
|
2016-03-08 18:56:58 +00:00
|
|
|
|
|
|
|
componentDidMount(): void {
|
|
|
|
this._positionListener =
|
|
|
|
this.state.position.addListener(this._onProgressChange);
|
2016-02-05 22:25:17 +00:00
|
|
|
}
|
2016-03-08 18:56:58 +00:00
|
|
|
|
|
|
|
componentWillReceiveProps(nextProps: Props): void {
|
2016-02-05 22:25:17 +00:00
|
|
|
if (nextProps.navigationState !== this.props.navigationState) {
|
|
|
|
this.setState({
|
2016-03-21 18:13:59 +00:00
|
|
|
scenes: NavigationScenesReducer(
|
2016-03-08 18:56:58 +00:00
|
|
|
this.state.scenes,
|
|
|
|
nextProps.navigationState,
|
|
|
|
this.props.navigationState
|
|
|
|
),
|
2016-02-05 22:25:17 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2016-03-08 18:56:58 +00:00
|
|
|
|
|
|
|
componentDidUpdate(lastProps: Props): void {
|
|
|
|
if (lastProps.navigationState.index !== this.props.navigationState.index) {
|
2016-03-09 07:31:27 +00:00
|
|
|
this.props.applyAnimation(
|
2016-03-08 18:56:58 +00:00
|
|
|
this.state.position,
|
|
|
|
this.props.navigationState,
|
|
|
|
lastProps.navigationState
|
|
|
|
);
|
2016-02-05 22:25:17 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-08 18:56:58 +00:00
|
|
|
|
|
|
|
componentWillUnmount(): void {
|
|
|
|
this.state.position.removeListener(this._positionListener);
|
2016-02-05 22:25:17 +00:00
|
|
|
}
|
2016-03-08 18:56:58 +00:00
|
|
|
|
2016-02-05 22:25:17 +00:00
|
|
|
_onProgressChange(data: Object): void {
|
2016-03-08 18:56:58 +00:00
|
|
|
const delta = Math.abs(data.value - this.props.navigationState.index);
|
|
|
|
if (delta > Number.EPSILON) {
|
2016-02-05 22:25:17 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-03-08 18:56:58 +00:00
|
|
|
|
|
|
|
const scenes = this.state.scenes.filter(scene => {
|
|
|
|
return !scene.isStale;
|
2016-02-05 22:25:17 +00:00
|
|
|
});
|
2016-03-08 18:56:58 +00:00
|
|
|
|
|
|
|
if (scenes.length !== this.state.scenes.length) {
|
|
|
|
this.setState({ scenes });
|
|
|
|
}
|
2016-02-05 22:25:17 +00:00
|
|
|
}
|
2016-03-08 18:56:58 +00:00
|
|
|
|
|
|
|
render(): ReactElement {
|
2016-03-11 01:16:19 +00:00
|
|
|
const overlay = this._renderOverlay();
|
|
|
|
const scenes = this._renderScenes();
|
2016-02-05 22:25:17 +00:00
|
|
|
return (
|
|
|
|
<View
|
2016-03-08 18:56:58 +00:00
|
|
|
onLayout={this._onLayout}
|
2016-02-05 22:25:17 +00:00
|
|
|
style={this.props.style}>
|
2016-03-11 01:16:19 +00:00
|
|
|
<View style={styles.scenes} key="scenes">
|
|
|
|
{scenes}
|
|
|
|
</View>
|
|
|
|
{overlay}
|
2016-02-05 22:25:17 +00:00
|
|
|
</View>
|
|
|
|
);
|
|
|
|
}
|
2016-03-04 22:56:37 +00:00
|
|
|
|
2016-03-11 01:16:19 +00:00
|
|
|
_renderScenes(): Array<?ReactElement> {
|
|
|
|
return this.state.scenes.map(this._renderScene, this);
|
|
|
|
}
|
|
|
|
|
2016-03-08 18:56:58 +00:00
|
|
|
_renderScene(scene: NavigationScene): ?ReactElement {
|
2016-03-04 22:56:37 +00:00
|
|
|
const {
|
|
|
|
navigationState,
|
|
|
|
onNavigate,
|
|
|
|
renderScene,
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
const {
|
|
|
|
position,
|
|
|
|
scenes,
|
|
|
|
} = this.state;
|
|
|
|
|
|
|
|
return renderScene({
|
2016-03-08 18:56:58 +00:00
|
|
|
layout: this._layout,
|
2016-03-04 22:56:37 +00:00
|
|
|
navigationState,
|
|
|
|
onNavigate,
|
|
|
|
position,
|
|
|
|
scene,
|
|
|
|
scenes,
|
2016-02-29 21:21:43 +00:00
|
|
|
});
|
2016-02-05 22:25:17 +00:00
|
|
|
}
|
2016-03-04 22:56:37 +00:00
|
|
|
|
2016-03-08 18:56:58 +00:00
|
|
|
_renderOverlay(): ?ReactElement {
|
2016-03-04 22:56:37 +00:00
|
|
|
if (this.props.renderOverlay) {
|
|
|
|
const {
|
|
|
|
navigationState,
|
|
|
|
onNavigate,
|
|
|
|
renderOverlay,
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
const {
|
|
|
|
position,
|
|
|
|
scenes,
|
|
|
|
} = this.state;
|
|
|
|
|
2016-02-29 21:21:43 +00:00
|
|
|
return renderOverlay({
|
2016-03-08 18:56:58 +00:00
|
|
|
layout: this._layout,
|
2016-03-04 22:56:37 +00:00
|
|
|
navigationState,
|
|
|
|
onNavigate,
|
|
|
|
position,
|
|
|
|
scene: scenes[navigationState.index],
|
|
|
|
scenes,
|
2016-02-29 21:21:43 +00:00
|
|
|
});
|
2016-02-05 22:25:17 +00:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2016-03-08 18:56:58 +00:00
|
|
|
|
|
|
|
_onLayout(event: any): void {
|
|
|
|
const {height, width} = event.nativeEvent.layout;
|
|
|
|
|
|
|
|
const layout = {
|
|
|
|
...this._layout,
|
|
|
|
initHeight: height,
|
|
|
|
initWidth: width,
|
|
|
|
};
|
|
|
|
|
|
|
|
this._layout = layout;
|
|
|
|
|
|
|
|
layout.height.setValue(height);
|
|
|
|
layout.width.setValue(width);
|
|
|
|
}
|
2016-02-05 22:25:17 +00:00
|
|
|
}
|
|
|
|
|
2016-03-11 01:16:19 +00:00
|
|
|
const styles = StyleSheet.create({
|
|
|
|
scenes: {
|
|
|
|
flex: 1,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2016-03-04 22:56:37 +00:00
|
|
|
NavigationAnimatedView.propTypes = propTypes;
|
|
|
|
NavigationAnimatedView.defaultProps = defaultProps;
|
2016-02-23 00:15:43 +00:00
|
|
|
|
2016-02-05 22:25:17 +00:00
|
|
|
NavigationAnimatedView = NavigationContainer.create(NavigationAnimatedView);
|
|
|
|
|
|
|
|
module.exports = NavigationAnimatedView;
|