Clean up APIs.

Reviewed By: ericvicenti

Differential Revision: D3010136

fb-gh-sync-id: 310864450bfc86ebc2d696f8ef4876b14fa3a57f
shipit-source-id: 310864450bfc86ebc2d696f8ef4876b14fa3a57f
This commit is contained in:
Hedger Wang 2016-03-04 14:56:37 -08:00 committed by Facebook Github Bot 7
parent fbef6f6893
commit 71e59761c9
23 changed files with 788 additions and 730 deletions

View File

@ -48,7 +48,10 @@ const NavigationBasicReducer = NavigationReducer.StackReducer({
class NavigationAnimatedExample extends React.Component {
componentWillMount() {
this._renderNavigated = this._renderNavigated.bind(this);
this._renderNavigation = this._renderNavigation.bind(this);
this._renderCard = this._renderCard.bind(this);
this._renderScene = this._renderScene.bind(this);
this._renderHeader = this._renderHeader.bind(this);
}
render() {
return (
@ -56,7 +59,7 @@ class NavigationAnimatedExample extends React.Component {
reducer={NavigationBasicReducer}
ref={navRootContainer => { this.navRootContainer = navRootContainer; }}
persistenceKey="NavigationAnimExampleState"
renderNavigation={this._renderNavigated}
renderNavigation={this._renderNavigation}
/>
);
}
@ -66,7 +69,7 @@ class NavigationAnimatedExample extends React.Component {
this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction())
);
}
_renderNavigated(navigationState, onNavigate) {
_renderNavigation(navigationState, onNavigate) {
if (!navigationState) {
return null;
}
@ -74,46 +77,56 @@ class NavigationAnimatedExample extends React.Component {
<NavigationAnimatedView
navigationState={navigationState}
style={styles.animatedView}
renderOverlay={(props) => (
<NavigationHeader
navigationState={props.navigationParentState}
position={props.position}
getTitle={state => state.key}
/>
)}
renderOverlay={this._renderHeader}
setTiming={(pos, navState) => {
Animated.timing(pos, {toValue: navState.index, duration: 1000}).start();
}}
renderScene={(props) => (
<NavigationCard
key={props.navigationState.key}
index={props.index}
navigationState={props.navigationParentState}
position={props.position}
layout={props.layout}>
<ScrollView style={styles.scrollView}>
<NavigationExampleRow
text={props.navigationState.key}
/>
<NavigationExampleRow
text="Push!"
onPress={() => {
onNavigate({
type: 'push',
key: 'Route #' + props.navigationParentState.children.length
});
}}
/>
<NavigationExampleRow
text="Exit Animated Nav Example"
onPress={this.props.onExampleExit}
/>
</ScrollView>
</NavigationCard>
)}
renderScene={this._renderCard}
/>
);
}
_renderHeader(/*NavigationSceneRendererProps*/ props) {
return (
<NavigationHeader
{...props}
getTitle={state => state.key}
/>
);
}
_renderCard(/*NavigationSceneRendererProps*/ props) {
return (
<NavigationCard
{...props}
key={'card_' + props.scene.navigationState.key}
renderScene={this._renderScene}
/>
);
}
_renderScene(/*NavigationSceneRendererProps*/ props) {
return (
<ScrollView style={styles.scrollView}>
<NavigationExampleRow
text={props.scene.navigationState.key}
/>
<NavigationExampleRow
text="Push!"
onPress={() => {
props.onNavigate({
type: 'push',
key: 'Route #' + props.scenes.length,
});
}}
/>
<NavigationExampleRow
text="Exit Animated Nav Example"
onPress={this.props.onExampleExit}
/>
</ScrollView>
);
}
}
const styles = StyleSheet.create({

View File

@ -49,6 +49,7 @@ function reduceNavigationState(initialState) {
const ExampleReducer = reduceNavigationState({
index: 0,
key: 'exmaple',
children: [{key: 'First Route'}],
});
@ -56,12 +57,13 @@ class NavigationCardStackExample extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {isHorizontal: true};
}
componentWillMount() {
this._renderNavigation = this._renderNavigation.bind(this);
this._renderScene = this._renderScene.bind(this);
this._toggleDirection = this._toggleDirection.bind(this);
this.state = {isHorizontal: true};
}
render() {
@ -86,8 +88,7 @@ class NavigationCardStackExample extends React.Component {
);
}
_renderScene(props) {
const {navigationParentState, onNavigate} = props;
_renderScene(/*NavigationSceneRendererProps*/ props) {
return (
<ScrollView style={styles.scrollView}>
<NavigationExampleRow
@ -99,21 +100,21 @@ class NavigationCardStackExample extends React.Component {
onPress={this._toggleDirection}
/>
<NavigationExampleRow
text={'route = ' + props.navigationState.key}
text={'route = ' + props.scene.navigationState.key}
/>
<NavigationExampleRow
text="Push Route"
onPress={() => {
onNavigate({
props.onNavigate({
type: 'push',
key: 'Route ' + navigationParentState.children.length,
key: 'Route ' + props.scenes.length,
});
}}
/>
<NavigationExampleRow
text="Pop Route"
onPress={() => {
onNavigate({
props.onNavigate({
type: 'pop',
});
}}

View File

@ -16,25 +16,32 @@
'use strict';
const React = require('react-native');
const NavigationExampleRow = require('./NavigationExampleRow');
const NavigationExampleTabBar = require('./NavigationExampleTabBar');
const {
NavigationExperimental,
ScrollView,
StyleSheet,
View,
} = React;
const {
AnimatedView: NavigationAnimatedView,
Card: NavigationCard,
CardStack: NavigationCardStack,
Container: NavigationContainer,
RootContainer: NavigationRootContainer,
Header: NavigationHeader,
Reducer: NavigationReducer,
RootContainer: NavigationRootContainer,
View: NavigationView,
} = NavigationExperimental;
const NavigationExampleRow = require('./NavigationExampleRow');
const NavigationExampleTabBar = require('./NavigationExampleTabBar');
import type {NavigationParentState} from 'NavigationStateUtils';
import type {
NavigationParentState,
NavigationSceneRenderer,
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
type Action = {
isExitAction?: boolean,
@ -43,6 +50,7 @@ type Action = {
const ExampleExitAction = () => ({
isExitAction: true,
});
ExampleExitAction.match = (action: Action) => (
action && action.isExitAction === true
);
@ -51,6 +59,7 @@ const PageAction = (type) => ({
type,
isPageAction: true,
});
PageAction.match = (action) => (
action && action.isPageAction === true
);
@ -59,6 +68,7 @@ const ExampleProfilePageAction = (type) => ({
...PageAction(type),
isProfilePageAction: true,
});
ExampleProfilePageAction.match = (action) => (
action && action.isProfilePageAction === true
);
@ -68,7 +78,9 @@ const ExampleInfoAction = () => PageAction('InfoPage');
const ExampleNotifProfileAction = () => ExampleProfilePageAction('NotifProfilePage');
const _jsInstanceUniqueId = '' + Date.now();
let _uniqueIdCount = 0;
function pageStateActionMap(action) {
return {
key: 'page-' + _jsInstanceUniqueId + '-' + (_uniqueIdCount++),
@ -144,55 +156,57 @@ function stateTypeTitleMap(pageState) {
}
class ExampleTabScreen extends React.Component {
_renderCard: NavigationSceneRenderer;
_renderHeader: NavigationSceneRenderer;
_renderScene: NavigationSceneRenderer;
componentWillMount() {
this._renderHeader = this._renderHeader.bind(this);
this._renderScene = this._renderScene.bind(this);
}
render() {
return (
<NavigationAnimatedView
<NavigationCardStack
style={styles.tabContent}
navigationState={this.props.navigationState}
renderOverlay={this._renderHeader.bind(this)}
renderScene={this._renderScene.bind(this)}
renderOverlay={this._renderHeader}
renderScene={this._renderScene}
/>
);
}
_renderHeader(props) {
_renderHeader(props: NavigationSceneRendererProps) {
return (
<NavigationHeader
navigationState={props.navigationParentState}
position={props.position}
layout={props.layout}
{...props}
getTitle={state => stateTypeTitleMap(state)}
/>
);
}
_renderScene(props) {
_renderScene(props: NavigationSceneRendererProps) {
const {onNavigate} = props;
return (
<NavigationCard
key={props.navigationState.key}
index={props.index}
navigationState={props.navigationParentState}
position={props.position}
layout={props.layout}>
<ScrollView style={styles.scrollView}>
<NavigationExampleRow
text="Open page"
onPress={() => {
this.props.onNavigate(ExampleInfoAction());
}}
/>
<NavigationExampleRow
text="Open a page in the profile tab"
onPress={() => {
this.props.onNavigate(ExampleNotifProfileAction());
}}
/>
<NavigationExampleRow
text="Exit Composition Example"
onPress={() => {
this.props.onNavigate(ExampleExitAction());
}}
/>
</ScrollView>
</NavigationCard>
<ScrollView style={styles.scrollView}>
<NavigationExampleRow
text="Open page"
onPress={() => {
onNavigate(ExampleInfoAction());
}}
/>
<NavigationExampleRow
text="Open a page in the profile tab"
onPress={() => {
onNavigate(ExampleNotifProfileAction());
}}
/>
<NavigationExampleRow
text="Exit Composition Example"
onPress={() => {
onNavigate(ExampleExitAction());
}}
/>
</ScrollView>
);
}
}

View File

@ -36,8 +36,7 @@ const {
} = React;
const {
AnimatedView: NavigationAnimatedView,
Card: NavigationCard,
CardStack: NavigationCardStack,
Header: NavigationHeader,
Reducer: NavigationReducer,
RootContainer: NavigationRootContainer,
@ -45,7 +44,7 @@ const {
import type { Value } from 'Animated';
import type { NavigationStateRendererProps } from 'NavigationAnimatedView';
import type { NavigationSceneRendererProps } from 'NavigationTypeDefinition';
import type { UIExplorerNavigationState } from './UIExplorerNavigationReducer';
@ -78,7 +77,6 @@ function URIActionMap(uri: ?string): ?Object {
return PathActionMap(path);
}
class UIExplorerApp extends React.Component {
_navigationRootRef: ?NavigationRootContainer;
_renderNavigation: Function;
@ -89,7 +87,6 @@ class UIExplorerApp extends React.Component {
this._renderNavigation = this._renderNavigation.bind(this);
this._renderOverlay = this._renderOverlay.bind(this);
this._renderScene = this._renderScene.bind(this);
this._renderCard = this._renderCard.bind(this);
}
render() {
return (
@ -118,39 +115,27 @@ class UIExplorerApp extends React.Component {
}
const {stack} = navigationState;
return (
<NavigationAnimatedView
<NavigationCardStack
navigationState={stack}
style={styles.container}
renderOverlay={this._renderOverlay}
renderScene={this._renderCard}
renderScene={this._renderScene}
/>
);
}
_renderOverlay(props: NavigationStateRendererProps): ReactElement {
_renderOverlay(props: NavigationSceneRendererProps): ReactElement {
return (
<NavigationHeader
navigationState={props.navigationParentState}
position={props.position}
{...props}
key={'header_' + props.scene.navigationState.key}
getTitle={UIExplorerStateTitleMap}
/>
);
}
_renderCard(props: NavigationStateRendererProps): ReactElement {
return (
<NavigationCard
index={props.index}
key={props.navigationState.key}
layout={props.layout}
navigationState={props.navigationParentState}
position={props.position}>
{this._renderScene(props.navigationState)}
</NavigationCard>
);
}
_renderScene(state: Object): ?ReactElement {
_renderScene(props: NavigationSceneRendererProps): ?ReactElement {
const state = props.scene.navigationState;
if (state.key === 'AppList') {
return (
<UIExplorerExampleList

View File

@ -26,7 +26,7 @@ const {
} = NavigationExperimental;
const StackReducer = NavigationReducer.StackReducer;
import type {NavigationState} from 'NavigationStateUtils';
import type {NavigationState} from 'NavigationTypeDefinition';
import type {UIExplorerAction} from './UIExplorerActions';
@ -93,7 +93,7 @@ function UIExplorerNavigationReducer(lastState: ?UIExplorerNavigationState, acti
if (newStack !== lastState.stack) {
return {
externalExample: null,
stack: newStack,
stack: newStack,
}
}
return lastState;

View File

@ -18,7 +18,7 @@
// $FlowFixMe : This is a platform-forked component, and flow seems to only run on iOS?
const UIExplorerList = require('./UIExplorerList');
import type {NavigationState} from 'NavigationStateUtils';
import type {NavigationState} from 'NavigationTypeDefinition';
function StateTitleMap(state: NavigationState): string {
if (UIExplorerList.Modules[state.key]) {

View File

@ -28,142 +28,260 @@
'use strict';
const Animated = require('Animated');
const NavigationRootContainer = require('NavigationRootContainer');
const NavigationContainer = require('NavigationContainer');
const PanResponder = require('PanResponder');
const Platform = require('Platform');
const NavigationLinearPanResponder = require('NavigationLinearPanResponder');
const NavigationPropTypes = require('NavigationPropTypes');
const React = require('React');
const ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin');
const StyleSheet = require('StyleSheet');
const View = require('View');
const ENABLE_GESTURES = Platform.OS !== 'android';
const {Directions} = NavigationLinearPanResponder;
import type {
NavigationAnimatedValue,
NavigationLayout,
NavigationPosition,
NavigationSceneRenderer,
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
import type {
NavigationParentState
} from 'NavigationStateUtils';
NavigationGestureDirection
} from 'NavigationLinearPanResponder';
type Layout = {
initWidth: number,
initHeight: number,
width: Animated.Value;
height: Animated.Value;
type State = {
hash: string,
height: number,
width: number,
};
type Props = {
navigationState: NavigationParentState;
index: number;
position: Animated.Value;
layout: Layout;
onNavigate: Function;
children: Object;
type Props = NavigationSceneRendererProps & {
direction: NavigationGestureDirection,
renderScene: NavigationSceneRenderer,
};
class NavigationCard extends React.Component {
_responder: ?Object;
_lastHeight: number;
_lastWidth: number;
_widthListener: string;
_heightListener: string;
props: Props;
componentWillMount() {
if (ENABLE_GESTURES) {
this._enableGestures();
}
const {PropTypes} = React;
const propTypes = {
...NavigationPropTypes.SceneRenderer,
direction: PropTypes.oneOf([Directions.HORIZONTAL, Directions.VERTICAL]),
renderScene: PropTypes.func.isRequired,
};
const defaultProps = {
direction: Directions.HORIZONTAL,
};
class AmimatedValueSubscription {
_value: NavigationAnimatedValue;
_token: string;
constructor(value: NavigationAnimatedValue, callback: Function) {
this._value = value;
this._token = value.addListener(callback);
}
_enableGestures() {
this._responder = PanResponder.create({
onMoveShouldSetPanResponder: (e, {dx, dy, moveX, moveY, x0, y0}) => {
if (this.props.navigationState.index === 0) {
return false;
}
if (moveX > 30) {
return false;
}
if (dx > 5 && Math.abs(dy) < 4) {
return true;
}
return false;
},
onPanResponderGrant: (e, {dx, dy, moveX, moveY, x0, y0}) => {
},
onPanResponderMove: (e, {dx}) => {
const a = (-dx / this._lastWidth) + this.props.navigationState.index;
this.props.position.setValue(a);
},
onPanResponderRelease: (e, {vx, dx}) => {
const xRatio = dx / this._lastWidth;
const doesPop = (xRatio + vx) > 0.45;
if (doesPop) {
// todo: add an action which accepts velocity of the pop action/gesture, which is caught and used by NavigationAnimatedView
this.props.onNavigate(NavigationRootContainer.getBackAction());
return;
}
Animated.spring(this.props.position, {
toValue: this.props.navigationState.index,
}).start();
},
onPanResponderTerminate: (e, {vx, dx}) => {
Animated.spring(this.props.position, {
toValue: this.props.navigationState.index,
}).start();
},
});
}
componentDidMount() {
this._lastHeight = this.props.layout.initHeight;
this._lastWidth = this.props.layout.initWidth;
this._widthListener = this.props.layout.width.addListener(({value}) => {
this._lastWidth = value;
});
this._heightListener = this.props.layout.height.addListener(({value}) => {
this._lastHeight = value;
});
// todo: fix listener and last layout dimentsions when props change. potential bugs here
}
componentWillUnmount() {
this.props.layout.width.removeListener(this._widthListener);
this.props.layout.height.removeListener(this._heightListener);
}
render() {
const cardPosition = Animated.add(this.props.position, new Animated.Value(-this.props.index));
const gestureValue = Animated.multiply(cardPosition, this.props.layout.width);
const touchResponderHandlers = this._responder ? this._responder.panHandlers : null;
return (
<Animated.View
{...touchResponderHandlers}
style={[
styles.card,
{
right: gestureValue,
left: gestureValue.interpolate({
inputRange: [0, 1],
outputRange: [0, -1],
}),
opacity: cardPosition.interpolate({
inputRange: [-1,0,1],
outputRange: [0,1,1],
}),
}
]}>
{this.props.children}
</Animated.View>
);
remove() {
this._value.removeListener(this._token);
}
}
NavigationCard = NavigationContainer.create(NavigationCard);
/**
* Class that provides the required information for the
* `NavigationLinearPanResponder`. This class must implement
* the interface `NavigationLinearPanResponderDelegate`.
*/
class PanResponderDelegate {
_props : Props;
constructor(props: Props) {
this._props = props;
}
getDirection(): NavigationGestureDirection {
return this._props.direction;
}
getIndex(): number {
return this._props.navigationState.index;
}
getLayout(): NavigationLayout {
return this._props.layout;
}
getPosition(): NavigationPosition {
return this._props.position;
}
onNavigate(action: {type: string}): void {
this._props.onNavigate && this._props.onNavigate(action);
}
}
/**
* Component that renders the scene as card for the <NavigationCardStack />.
*/
class NavigationCard extends React.Component {
props: Props;
state: State;
_calculateState: (t: NavigationLayout) => State;
_layoutListeners: Array<AmimatedValueSubscription>;
constructor(props: Props, context: any) {
super(props, context);
this.state = this._calculateState(props.layout);
this._layoutListeners = [];
}
shouldComponentUpdate(nextProps: Object, nextState: Object): boolean {
return ReactComponentWithPureRenderMixin.shouldComponentUpdate.call(
this,
nextProps,
nextState
);
}
componentWillMount(): void {
this._calculateState = this._calculateState.bind(this);
}
componentDidMount(): void {
this._applyLayout(this.props.layout);
}
componentWillUnmount(): void {
this._layoutListeners.forEach(subscription => subscription.remove);
}
componentWillReceiveProps(nextProps: Props): void {
this._applyLayout(nextProps.layout);
}
render(): ReactElement {
const {
direction,
layout,
navigationState,
onNavigate,
position,
scene,
scenes,
} = this.props;
const {
height,
width,
} = this.state;
const index = scene.index;
const isVertical = direction === 'vertical';
const inputRange = [index - 1, index, index + 1];
const animatedStyle = {
opacity: position.interpolate({
inputRange,
outputRange: [1, 1, 0.3],
}),
transform: [
{
scale: position.interpolate({
inputRange,
outputRange: [1, 1, 0.95],
}),
},
{
translateX: isVertical ? 0 :
position.interpolate({
inputRange,
outputRange: [width, 0, -10],
}),
},
{
translateY: !isVertical ? 0 :
position.interpolate({
inputRange,
outputRange: [height, 0, -10],
}),
},
],
};
let panHandlers = null;
if (navigationState.index === index) {
const delegate = new PanResponderDelegate(this.props);
const panResponder = new NavigationLinearPanResponder(delegate);
panHandlers = panResponder.panHandlers;
}
const sceneProps = {
layout,
navigationState,
onNavigate,
position,
scene,
scenes,
};
return (
<Animated.View
{...panHandlers}
style={[styles.main, animatedStyle]}>
{this.props.renderScene(sceneProps)}
</Animated.View>
);
}
_calculateState(layout: NavigationLayout): State {
const width = layout.width.__getValue();
const height = layout.height.__getValue();
const hash = 'layout-' + width + '-' + height;
const state = {
height,
width,
hash,
};
return state;
}
_applyLayout(layout: NavigationLayout) {
this._layoutListeners.forEach(subscription => subscription.remove);
this._layoutListeners.length = 0;
const callback = this._applyLayout.bind(this, layout);
this._layoutListeners.push(
new AmimatedValueSubscription(layout.width, callback),
new AmimatedValueSubscription(layout.height, callback),
);
const nextState = this._calculateState(layout);
if (nextState.hash !== this.state.hash) {
this.setState(nextState);
}
}
}
NavigationCard.propTypes = propTypes;
NavigationCard.defaultProps = defaultProps;
const styles = StyleSheet.create({
card: {
main: {
backgroundColor: '#E9E9EF',
bottom: 0,
left: 0,
position: 'absolute',
right: 0,
shadowColor: 'black',
shadowOpacity: 0.4,
shadowOffset: {width: 0, height: 0},
shadowOpacity: 0.4,
shadowRadius: 10,
top: 0,
bottom: 0,
position: 'absolute',
},
});
module.exports = NavigationCard;
module.exports = NavigationContainer.create(NavigationCard);

View File

@ -29,9 +29,10 @@
const Animated = require('Animated');
const NavigationAnimatedView = require('NavigationAnimatedView');
const NavigationCardStackItem = require('NavigationCardStackItem');
const NavigationCard = require('NavigationCard');
const NavigationContainer = require('NavigationContainer');
const NavigationLinearPanResponder = require('NavigationLinearPanResponder');
const NavigationPropTypes = require('NavigationPropTypes');
const React = require('React');
const ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin');
const StyleSheet = require('StyleSheet');
@ -42,32 +43,48 @@ const {PropTypes} = React;
const {Directions} = NavigationLinearPanResponder;
import type {
NavigationAnimatedValue,
NavigationAnimationSetter,
NavigationParentState,
} from 'NavigationStateUtils';
NavigationSceneRenderer,
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
import type {
NavigationStateRenderer,
NavigationStateRendererProps,
Position,
TimingSetter,
} from 'NavigationAnimatedView';
NavigationGestureDirection,
} from 'NavigationLinearPanResponder';
type Props = {
direction: string,
direction: NavigationGestureDirection,
navigationState: NavigationParentState,
renderOverlay: ?NavigationStateRenderer,
renderScene: NavigationStateRenderer,
renderOverlay: ?NavigationSceneRenderer,
renderScene: NavigationSceneRenderer,
};
const propTypes = {
direction: PropTypes.oneOf([Directions.HORIZONTAL, Directions.VERTICAL]),
navigationState: NavigationPropTypes.navigationParentState.isRequired,
renderOverlay: PropTypes.func,
renderScene: PropTypes.func.isRequired,
};
const defaultProps = {
direction: Directions.HORIZONTAL,
renderOverlay: emptyFunction.thatReturnsNull,
};
/**
* A controlled navigation view that renders a list of cards.
*/
class NavigationCardStack extends React.Component {
_renderScene : NavigationStateRenderer;
_setTiming: TimingSetter;
_renderScene : NavigationSceneRenderer;
_setTiming: NavigationAnimationSetter;
constructor(props: Props, context: any) {
super(props, context);
}
componentWillMount() {
this._renderScene = this._renderScene.bind(this);
this._setTiming = this._setTiming.bind(this);
}
@ -92,30 +109,21 @@ class NavigationCardStack extends React.Component {
);
}
_renderScene(props: NavigationStateRendererProps): ReactElement {
const {
index,
layout,
navigationState,
position,
navigationParentState,
} = props;
_renderScene(props: NavigationSceneRendererProps): ReactElement {
return (
<NavigationCardStackItem
<NavigationCard
{...props}
direction={this.props.direction}
index={index}
key={navigationState.key}
layout={layout}
navigationParentState={navigationParentState}
navigationState={navigationState}
position={position}
key={'card_' + props.scene.navigationState.key}
renderScene={this.props.renderScene}
/>
);
}
_setTiming(position: Position, navigationState: NavigationParentState): void {
_setTiming(
position: NavigationAnimatedValue,
navigationState: NavigationParentState,
): void {
Animated.timing(
position,
{
@ -126,17 +134,8 @@ class NavigationCardStack extends React.Component {
}
}
NavigationCardStack.propTypes = {
direction: PropTypes.oneOf([Directions.HORIZONTAL, Directions.VERTICAL]),
navigationState: PropTypes.object.isRequired,
renderOverlay: PropTypes.func,
renderScene: PropTypes.func.isRequired,
};
NavigationCardStack.defaultProps = {
direction: Directions.HORIZONTAL,
renderOverlay: emptyFunction.thatReturnsNull,
};
NavigationCardStack.propTypes = propTypes;
NavigationCardStack.defaultProps = defaultProps;
const styles = StyleSheet.create({
animatedView: {

View File

@ -1,280 +0,0 @@
/**
* 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 NavigationCardStackItem
* @flow
*/
'use strict';
const Animated = require('Animated');
const NavigationContainer = require('NavigationContainer');
const NavigationLinearPanResponder = require('NavigationLinearPanResponder');
const React = require('React');
const ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin');
const StyleSheet = require('StyleSheet');
const View = require('View');
const {PropTypes} = React;
const {Directions} = NavigationLinearPanResponder;
import type {
NavigationParentState,
} from 'NavigationStateUtils';
import type {
Layout,
Position,
NavigationStateRenderer,
} from 'NavigationAnimatedView';
import type {
Direction,
OnNavigateHandler,
} from 'NavigationLinearPanResponder';
type AnimatedValue = Animated.Value;
type Props = {
direction: Direction,
index: number;
layout: Layout;
navigationParentState: NavigationParentState,
navigationState: NavigationParentState,
position: Position,
onNavigate: ?OnNavigateHandler,
renderScene: NavigationStateRenderer,
};
type State = {
hash: string,
height: number,
width: number,
};
class AmimatedValueSubscription {
_value: AnimatedValue;
_token: string;
constructor(value: AnimatedValue, callback: Function) {
this._value = value;
this._token = value.addListener(callback);
}
remove() {
this._value.removeListener(this._token);
}
}
/**
* Class that provides the required information for the
* `NavigationLinearPanResponder`. This class must implement
* the interface `NavigationLinearPanResponderDelegate`.
*/
class PanResponderDelegate {
_props : Props;
constructor(props: Props) {
this._props = props;
}
getDirection(): Direction {
return this._props.direction;
}
getIndex(): number {
return this._props.navigationParentState.index;
}
getLayout(): Layout {
return this._props.layout;
}
getPosition(): Position {
return this._props.position;
}
onNavigate(action: {type: string}): void {
this._props.onNavigate && this._props.onNavigate(action);
}
}
/**
* Component that renders the scene as card for the <NavigationCardStack />.
*/
class NavigationCardStackItem extends React.Component {
props: Props;
state: State;
_calculateState: (t: Layout) => State;
_layoutListeners: Array<AmimatedValueSubscription>;
constructor(props: Props, context: any) {
super(props, context);
this._calculateState = this._calculateState.bind(this);
this.state = this._calculateState(props.layout);
this._layoutListeners = [];
}
shouldComponentUpdate(nextProps: Object, nextState: Object): boolean {
return ReactComponentWithPureRenderMixin.shouldComponentUpdate.call(
this,
nextProps,
nextState
);
}
componentDidMount(): void {
this._applyLayout(this.props.layout);
}
componentWillUnmount(): void {
this._layoutListeners.forEach(subscription => subscription.remove);
}
componentWillReceiveProps(nextProps: Props): void {
this._applyLayout(nextProps.layout);
}
render(): ReactElement {
const {
direction,
index,
navigationParentState,
position,
} = this.props;
const {
height,
width,
} = this.state;
const isVertical = direction === 'vertical';
const inputRange = [index - 1, index, index + 1];
const animatedStyle = {
opacity: position.interpolate({
inputRange,
outputRange: [1, 1, 0.3],
}),
transform: [
{
scale: position.interpolate({
inputRange,
outputRange: [1, 1, 0.95],
}),
},
{
translateX: isVertical ? 0 :
position.interpolate({
inputRange,
outputRange: [width, 0, -10],
}),
},
{
translateY: !isVertical ? 0 :
position.interpolate({
inputRange,
outputRange: [height, 0, -10],
}),
},
],
};
let panHandlers = null;
if (navigationParentState.index === index) {
const delegate = new PanResponderDelegate(this.props);
const panResponder = new NavigationLinearPanResponder(delegate);
panHandlers = panResponder.panHandlers;
}
return (
<Animated.View
{...panHandlers}
style={[styles.main, animatedStyle]}>
{this.props.renderScene(this.props)}
</Animated.View>
);
}
_calculateState(layout: Layout): State {
const width = layout.width.__getValue();
const height = layout.height.__getValue();
const hash = 'layout-' + width + '-' + height;
const state = {
height,
width,
hash,
};
return state;
}
_applyLayout(layout: Layout) {
this._layoutListeners.forEach(subscription => subscription.remove);
this._layoutListeners.length = 0;
const callback = this._applyLayout.bind(this, layout);
this._layoutListeners.push(
new AmimatedValueSubscription(layout.width, callback),
new AmimatedValueSubscription(layout.height, callback),
);
const nextState = this._calculateState(layout);
if (nextState.hash !== this.state.hash) {
this.setState(nextState);
}
}
}
NavigationCardStackItem.propTypes = {
direction: PropTypes.oneOf([Directions.HORIZONTAL, Directions.VERTICAL]),
index: PropTypes.number.isRequired,
layout: PropTypes.object.isRequired,
navigationState: PropTypes.object.isRequired,
navigationParentState: PropTypes.object.isRequired,
position: PropTypes.object.isRequired,
renderScene: PropTypes.func.isRequired,
};
NavigationCardStackItem.defaultProps = {
direction: Directions.HORIZONTAL,
};
const styles = StyleSheet.create({
main: {
backgroundColor: '#E9E9EF',
bottom: 0,
left: 0,
position: 'absolute',
right: 0,
shadowColor: 'black',
shadowOffset: {width: 0, height: 0},
shadowOpacity: 0.4,
shadowRadius: 10,
top: 0,
},
});
module.exports = NavigationContainer.create(NavigationCardStackItem);

View File

@ -30,6 +30,7 @@
const Animated = require('Animated');
const Image = require('Image');
const NavigationContainer = require('NavigationContainer');
const NavigationPropTypes = require('NavigationPropTypes');
const NavigationRootContainer = require('NavigationRootContainer');
const React = require('react-native');
const StyleSheet = require('StyleSheet');
@ -37,25 +38,32 @@ const Text = require('Text');
const TouchableOpacity = require('TouchableOpacity');
const View = require('View');
import type {
import type {
NavigationState,
NavigationParentState
} from 'NavigationStateUtils';
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
type Props = {
navigationState: NavigationParentState,
onNavigate: Function,
position: Animated.Value,
type Props = NavigationSceneRendererProps & {
getTitle: (navState: NavigationState) => string,
};
const {PropTypes} = React;
const NavigationHeaderPropTypes = {
...NavigationPropTypes.SceneRenderer,
getTitle: PropTypes.func.isRequired,
};
class NavigationHeader extends React.Component {
_handleBackPress: Function;
props: Props;
componentWillMount() {
componentWillMount(): void {
this._handleBackPress = this._handleBackPress.bind(this);
}
render() {
render(): ReactElement {
var state = this.props.navigationState;
return (
<Animated.View
@ -67,7 +75,8 @@ class NavigationHeader extends React.Component {
</Animated.View>
);
}
_renderBackButton() {
_renderBackButton(): ?ReactElement {
if (this.props.navigationState.index === 0) {
return null;
}
@ -77,7 +86,8 @@ class NavigationHeader extends React.Component {
</TouchableOpacity>
);
}
_renderTitle(childState, index) {
_renderTitle(childState: NavigationState, index:number): ?ReactElement {
return (
<Animated.Text
key={childState.key}
@ -102,11 +112,14 @@ class NavigationHeader extends React.Component {
</Animated.Text>
);
}
_handleBackPress() {
_handleBackPress(): void {
this.props.onNavigate(NavigationRootContainer.getBackAction());
}
}
NavigationHeader.propTypes = NavigationHeaderPropTypes;
NavigationHeader = NavigationContainer.create(NavigationHeader);
const styles = StyleSheet.create({

View File

@ -11,8 +11,7 @@ const invariant = require('fbjs/lib/invariant');
import type {
NavigationState,
NavigationParentState,
} from 'NavigationStateUtils';
} from 'NavigationTypeDefinition';
type IterationCallback = (route: any, index: number, key: string) => void;

View File

@ -11,23 +11,20 @@
*/
'use strict';
var Animated = require('Animated');
var Map = require('Map');
var NavigationStateUtils = require('NavigationStateUtils');
var NavigationContainer = require('NavigationContainer');
var React = require('React');
var View = require('View');
const Animated = require('Animated');
const NavigationContainer = require('NavigationContainer');
const NavigationPropTypes = require('NavigationPropTypes');
const NavigationStateUtils = require('NavigationStateUtils');
const React = require('react-native');
const View = require('View');
import type {
NavigationState,
NavigationAnimatedValue,
NavigationAnimationSetter,
NavigationParentState,
} from 'NavigationStateUtils';
type NavigationScene = {
index: number,
state: NavigationState,
isStale: boolean,
};
NavigationScene,
NavigationSceneRenderer,
} from 'NavigationTypeDefinition';
/**
* Helper function to compare route keys (e.g. "9", "11").
@ -48,7 +45,7 @@ function compareKey(one: string, two: string): number {
*/
function compareScenes(
one: NavigationScene,
two: NavigationScene
two: NavigationScene,
): number {
if (one.index > two.index) {
return 1;
@ -58,64 +55,62 @@ function compareScenes(
}
return compareKey(
one.state.key,
two.state.key
one.navigationState.key,
two.navigationState.key,
);
}
type Layout = {
initWidth: number,
initHeight: number,
width: Animated.Value;
height: Animated.Value;
};
type Position = Animated.Value;
/**
* Definition of the props object that is passed to the functions
* that render the overlay and the scene.
*/
type NavigationStateRendererProps = {
// The state of the child view.
navigationState: NavigationState,
// The index of the child view.
index: number,
// The "progressive index" of the containing navigation state.
position: Position,
// The layout of the the containing navigation view.
layout: Layout,
// The state of the the containing navigation view.
navigationParentState: NavigationParentState,
onNavigate: (action: any) => void,
};
type NavigationStateRenderer = (
props: NavigationStateRendererProps,
) => ReactElement;
type TimingSetter = (
position: Animated.Value,
newState: NavigationParentState,
lastState: NavigationParentState,
) => void;
type Props = {
navigationState: NavigationParentState,
onNavigate: (action: any) => void,
renderScene: NavigationStateRenderer,
renderOverlay: ?NavigationStateRenderer,
renderScene: NavigationSceneRenderer,
renderOverlay: ?NavigationSceneRenderer,
style: any,
setTiming: ?TimingSetter,
setTiming: NavigationAnimationSetter,
};
class NavigationAnimatedView extends React.Component {
type State = {
position: NavigationAnimatedValue,
scenes: Array<NavigationScene>,
};
const {PropTypes} = React;
const propTypes = {
navigationState: NavigationPropTypes.navigationState.isRequired,
onNavigate: PropTypes.func.isRequired,
renderScene: PropTypes.func.isRequired,
renderOverlay: PropTypes.func,
setTiming: PropTypes.func,
};
const defaultProps = {
setTiming: (
position: NavigationAnimatedValue,
navigationState: NavigationParentState,
) => {
Animated.spring(
position,
{
bounciness: 0,
toValue: navigationState.index,
}
).start();
},
};
class NavigationAnimatedView
extends React.Component<any, Props, State> {
_animatedHeight: Animated.Value;
_animatedWidth: Animated.Value;
_lastHeight: number;
_lastWidth: number;
_postionListener: any;
props: Props;
state: State;
constructor(props) {
super(props);
this._lastWidth = 0;
@ -125,7 +120,7 @@ class NavigationAnimatedView extends React.Component {
this.state = {
position: new Animated.Value(this.props.navigationState.index),
scenes: new Map(),
scenes: [],
};
}
componentWillMount() {
@ -134,7 +129,7 @@ class NavigationAnimatedView extends React.Component {
});
}
componentDidMount() {
this.postionListener = this.state.position.addListener(this._onProgressChange.bind(this));
this._postionListener = this.state.position.addListener(this._onProgressChange.bind(this));
}
componentWillReceiveProps(nextProps) {
if (nextProps.navigationState !== this.props.navigationState) {
@ -150,8 +145,8 @@ class NavigationAnimatedView extends React.Component {
}
componentWillUnmount() {
if (this.postionListener) {
this.state.position.removeListener(this.postionListener);
this.postionListener = null;
this.state.position.removeListener(this._postionListener);
this._postionListener = null;
}
}
_onProgressChange(data: Object): void {
@ -174,8 +169,8 @@ class NavigationAnimatedView extends React.Component {
let nextScenes = nextState.children.map((child, index) => {
return {
index,
state: child,
isStale: false,
navigationState: child,
};
});
@ -184,8 +179,8 @@ class NavigationAnimatedView extends React.Component {
if (!NavigationStateUtils.get(nextState, child.key) && index !== nextState.index) {
nextScenes.push({
index,
state: child,
isStale: true,
navigationState: child,
});
}
});
@ -221,49 +216,57 @@ class NavigationAnimatedView extends React.Component {
initHeight: this._lastHeight,
};
}
_renderScene(scene: NavigationScene) {
return this.props.renderScene({
index: scene.index,
const {
navigationState,
onNavigate,
renderScene,
} = this.props;
const {
position,
scenes,
} = this.state;
return renderScene({
layout: this._getLayout(),
navigationParentState: this.props.navigationState,
navigationState: scene.state,
onNavigate: this.props.onNavigate,
position: this.state.position,
navigationState,
onNavigate,
position,
scene,
scenes,
});
}
_renderOverlay() {
const {
onNavigate,
renderOverlay,
navigationState,
} = this.props;
if (renderOverlay) {
if (this.props.renderOverlay) {
const {
navigationState,
onNavigate,
renderOverlay,
} = this.props;
const {
position,
scenes,
} = this.state;
return renderOverlay({
index: navigationState.index,
layout: this._getLayout(),
navigationParentState: navigationState,
navigationState: navigationState.children[navigationState.index],
onNavigate: onNavigate,
position: this.state.position,
navigationState,
onNavigate,
position,
scene: scenes[navigationState.index],
scenes,
});
}
return null;
}
}
function setDefaultTiming(position, navigationState) {
Animated.spring(
position,
{
bounciness: 0,
toValue: navigationState.index,
}
).start();
}
NavigationAnimatedView.defaultProps = {
setTiming: setDefaultTiming,
};
NavigationAnimatedView.propTypes = propTypes;
NavigationAnimatedView.defaultProps = defaultProps;
NavigationAnimatedView = NavigationContainer.create(NavigationAnimatedView);

View File

@ -14,7 +14,9 @@
var React = require('React');
var NavigationRootContainer = require('NavigationRootContainer');
function createNavigationContainer(Component: React.Component): React.Component {
function createNavigationContainer(
Component: ReactClass<any, any, any>,
): ReactClass {
class NavigationComponent extends React.Component {
render() {
return (

View File

@ -12,6 +12,12 @@ const NavigationAbstractPanResponder = require('NavigationAbstractPanResponder')
const clamp = require('clamp');
import {
NavigationActionCaller,
NavigationLayout,
NavigationPosition,
} from 'NavigationTypeDefinition';
/**
* The duration of the card animation in milliseconds.
*/
@ -42,6 +48,8 @@ const Directions = {
'VERTICAL': 'vertical',
};
export type NavigationGestureDirection = $Enum<typeof Directions>;
/**
* Primitive gesture actions.
*/
@ -52,25 +60,16 @@ const Actions = {
BACK: {type: 'back'},
};
import type {
Layout,
Position,
} from 'NavigationAnimatedView';
export type OnNavigateHandler = (action: {type: string}) => void;
export type Direction = $Enum<typeof Directions>;
/**
* The type interface of the object that provides the information required by
* NavigationLinearPanResponder.
*/
export type NavigationLinearPanResponderDelegate = {
getDirection: () => Direction;
getDirection: () => NavigationGestureDirection;
getIndex: () => number,
getLayout: () => Layout,
getPosition: () => Position,
onNavigate: OnNavigateHandler,
getLayout: () => NavigationLayout,
getPosition: () => NavigationPosition,
onNavigate: NavigationActionCaller,
};
/**

View File

@ -0,0 +1,76 @@
/**
* 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 NavigationPropTypes
* @flow
*/
'use strict';
/**
* React component PropTypes Definitions. Consider using this as a supplementary
* measure with `NavigationTypeDefinition`. This helps to capture the propType
* error at run-time, where as `NavigationTypeDefinition` capture the flow
* type check errors at build time.
*/
const Animated = require('Animated');
const React = require('react-native');
const {PropTypes} = React;
/* NavigationAction */
const action = PropTypes.shape({
type: PropTypes.string.isRequired,
});
/* NavigationAnimatedValue */
const animatedValue = PropTypes.instanceOf(Animated.Value);
/* NavigationState */
const navigationState = PropTypes.shape({
key: PropTypes.string.isRequired,
});
/* NavigationParentState */
const navigationParentState = PropTypes.shape({
index: PropTypes.number.isRequired,
key: PropTypes.string.isRequired,
children: PropTypes.arrayOf(navigationState),
});
/* NavigationLayout */
const layout = PropTypes.shape({
height: animatedValue,
initHeight: PropTypes.number.isRequired,
initWidth: PropTypes.number.isRequired,
width: animatedValue,
});
/* NavigationScene */
const scene = PropTypes.shape({
index: PropTypes.number.isRequired,
isStale: PropTypes.bool.isRequired,
navigationState,
});
/* NavigationSceneRendererProps */
const SceneRenderer = {
layout: layout.isRequired,
navigationState: navigationParentState.isRequired,
onNavigate: PropTypes.func.isRequired,
position: animatedValue.isRequired,
scene: scene.isRequired,
scenes: PropTypes.arrayOf(scene).isRequired,
};
module.exports = {
SceneRenderer,
action,
navigationParentState,
navigationState,
};

View File

@ -13,23 +13,18 @@
const AsyncStorage = require('AsyncStorage');
const Linking = require('Linking');
const React = require('React');
const BackAndroid = require('BackAndroid');
const Platform = require('Platform');
const React = require('React');
const NavigationPropTypes = require('NavigationPropTypes');
import type {
NavigationAction,
NavigationState,
NavigationReducer
} from 'NavigationStateUtils';
export type NavigationRenderer = (
navigationState: NavigationState,
onNavigate: Function
) => ReactElement;
NavigationReducer,
NavigationRenderer,
} from 'NavigationTypeDefinition';
export type BackAction = {
type: 'BackAction';
type: 'BackAction',
};
function getBackAction(): BackAction {
@ -37,40 +32,60 @@ function getBackAction(): BackAction {
}
type Props = {
/*
* Set up the rendering of the app for a given navigation state
*/
renderNavigation: NavigationRenderer;
/*
* A function that will output the latest navigation state as a function of
* the (optional) previous state, and an action
*/
reducer: NavigationReducer;
/*
* Provide this key, and the container will store the navigation state in
* AsyncStorage through refreshes, with the provided key
*/
persistenceKey: ?string;
/*
* The default action to be passed into the reducer when getting the first
* state. Defaults to {type: 'RootContainerInitialAction'}
*/
initialAction: NavigationAction;
initialAction: NavigationAction,
/*
* Provide linkingActionMap to instruct the container to subscribe to linking
* events, and use this mapper to convert URIs into actions that your app can
* handle
*/
linkingActionMap: (uri: string) => NavigationAction;
linkingActionMap: ?((uri: string) => NavigationAction),
/*
* Provide this key, and the container will store the navigation state in
* AsyncStorage through refreshes, with the provided key
*/
persistenceKey: ?string,
/*
* A function that will output the latest navigation state as a function of
* the (optional) previous state, and an action
*/
reducer: NavigationReducer,
/*
* Set up the rendering of the app for a given navigation state
*/
renderNavigation: NavigationRenderer,
};
const {PropTypes} = React;
const propTypes = {
initialAction: NavigationPropTypes.action.isRequired,
linkingActionMap: PropTypes.func,
persistenceKey: PropTypes.string,
reducer: PropTypes.func.isRequired,
renderNavigation: PropTypes.func.isRequired,
};
const defaultProps = {
initialAction: {
type: 'RootContainerInitialAction',
},
};
class NavigationRootContainer extends React.Component {
_handleOpenURLEvent: Function;
props: Props;
constructor(props: Props) {
super(props);
this.handleNavigation = this.handleNavigation.bind(this);
@ -81,10 +96,11 @@ class NavigationRootContainer extends React.Component {
}
this.state = { navState };
}
componentDidMount() {
if (this.props.LinkingActionMap) {
Linking.getInitialURL().then(this._handleOpenURL.bind(this));
Platform.OS === 'ios' && Linking.addEventListener('url', this._handleOpenURLEvent);
Platform.OS === 'ios' && Linking.addEventListener('url', this._handleOpenURLEvent);
}
if (this.props.persistenceKey) {
AsyncStorage.getItem(this.props.persistenceKey, (err, storedString) => {
@ -100,12 +116,15 @@ class NavigationRootContainer extends React.Component {
});
}
}
componentWillUnmount() {
Platform.OS === 'ios' && Linking.removeEventListener('url', this._handleOpenURLEvent);
}
_handleOpenURLEvent(event: {url: string}) {
this._handleOpenURL(event.url);
}
_handleOpenURL(url: ?string) {
if (!this.props.LinkingActionMap) {
return;
@ -115,11 +134,13 @@ class NavigationRootContainer extends React.Component {
this.handleNavigation(action);
}
}
getChildContext(): Object {
return {
onNavigate: this.handleNavigation,
};
}
handleNavigation(action: Object): boolean {
const navState = this.props.reducer(this.state.navState, action);
if (navState === this.state.navState) {
@ -128,11 +149,14 @@ class NavigationRootContainer extends React.Component {
this.setState({
navState,
});
if (this.props.persistenceKey) {
AsyncStorage.setItem(this.props.persistenceKey, JSON.stringify(navState));
}
return true;
}
render(): ReactElement {
const navigation = this.props.renderNavigation(
this.state.navState,
@ -143,15 +167,11 @@ class NavigationRootContainer extends React.Component {
}
NavigationRootContainer.childContextTypes = {
onNavigate: React.PropTypes.func,
};
NavigationRootContainer.defaultProps = {
initialAction: {
type: 'RootContainerInitialAction',
},
onNavigate: PropTypes.func,
};
NavigationRootContainer.propTypes = propTypes;
NavigationRootContainer.defaultProps = defaultProps;
NavigationRootContainer.getBackAction = getBackAction;
module.exports = NavigationRootContainer;

View File

@ -13,24 +13,10 @@
const invariant = require('fbjs/lib/invariant');
export type NavigationState = {
key: string;
};
export type NavigationParentState = {
key: string;
index: number;
children: Array<NavigationState>;
};
export type NavigationAction = {
type: string;
};
export type NavigationReducer = (
state: ?NavigationState,
action: ?NavigationAction
) => NavigationState;
import type {
NavigationState,
NavigationParentState,
} from 'NavigationTypeDefinition';
function getParent(state: NavigationState): ?NavigationParentState {
if (

View File

@ -0,0 +1,93 @@
/**
* 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 NavigationTypeDefinition
* @flow
*/
'use strict';
const Animated = require('Animated');
// Object Instances
export type NavigationAnimatedValue = Animated.Value;
// Value & Structs.
export type NavigationGestureDirection = 'horizontal' | 'vertical';
export type NavigationState = {
key: string,
};
export type NavigationParentState = {
index: number,
key: string,
children: Array<NavigationState>,
};
export type NavigationAction = any;
export type NavigationLayout = {
height: NavigationAnimatedValue,
initHeight: number,
initWidth: number,
width: NavigationAnimatedValue,
};
export type NavigationPosition = NavigationAnimatedValue;
export type NavigationScene = {
index: number,
isStale: boolean,
navigationState: NavigationState,
};
export type NavigationSceneRendererProps = {
// The layout of the containing view of the scenes.
layout: NavigationLayout,
// The navigation state of the containing view.
navigationState: NavigationParentState,
// Callback to navigation with an action.
onNavigate: NavigationActionCaller,
// The progressive index of the containing view's navigation state.
position: NavigationPosition,
// The scene to render.
scene: NavigationScene,
// All the scenes of the containing view's.
scenes: Array<NavigationScene>,
};
// Functions.
export type NavigationActionCaller = Function;
export type NavigationAnimationSetter = (
position: NavigationAnimatedValue,
newState: NavigationParentState,
lastState: NavigationParentState,
) => void;
export type NavigationRenderer = (
navigationState: NavigationState,
onNavigate: NavigationActionCaller,
) => ReactElement;
export type NavigationReducer = (
state: ?NavigationState,
action: ?NavigationAction,
) => NavigationState;
export type NavigationSceneRenderer = (
props: NavigationSceneRendererProps,
) => ?ReactElement;

View File

@ -20,9 +20,12 @@
import type {
NavigationState,
NavigationReducer
} from 'NavigationStateUtils';
} from 'NavigationTypeDefinition';
function NavigationFindReducer(reducers: Array<NavigationReducer>, defaultState: NavigationState): NavigationReducer {
function NavigationFindReducer(
reducers: Array<NavigationReducer>,
defaultState: NavigationState,
): NavigationReducer {
return function(lastState: ?NavigationState, action: ?any): NavigationState {
for (let i = 0; i < reducers.length; i++) {
let reducer = reducers[i];

View File

@ -17,7 +17,7 @@ import type {
NavigationState,
NavigationParentState,
NavigationReducer,
} from 'NavigationStateUtils';
} from 'NavigationTypeDefinition';
import type {
BackAction,

View File

@ -17,8 +17,7 @@ const NavigationStateUtils = require('NavigationStateUtils');
import type {
NavigationReducer,
NavigationState,
NavigationParentState
} from 'NavigationStateUtils';
} from 'NavigationTypeDefinition';
const ActionTypes = {
JUMP_TO: 'react-native/NavigationExperimental/tabs-jumpTo',

View File

@ -11,7 +11,6 @@
'use strict';
jest
.dontMock('NavigationRootContainer')
.dontMock('NavigationStackReducer')
.dontMock('NavigationStateUtils');

View File

@ -0,0 +1,16 @@
/**
* 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.
*/
const NavigationRootContainer = {
getBackAction: () => {
return { type: 'BackAction' };
}
};
module.exports = NavigationRootContainer;