From 71e59761c950b5b012c1860501f68a96f38a4168 Mon Sep 17 00:00:00 2001 From: Hedger Wang Date: Fri, 4 Mar 2016 14:56:37 -0800 Subject: [PATCH] Clean up APIs. Reviewed By: ericvicenti Differential Revision: D3010136 fb-gh-sync-id: 310864450bfc86ebc2d696f8ef4876b14fa3a57f shipit-source-id: 310864450bfc86ebc2d696f8ef4876b14fa3a57f --- .../NavigationAnimatedExample.js | 87 +++-- .../NavigationCardStackExample.js | 17 +- .../NavigationCompositionExample.js | 94 +++-- Examples/UIExplorer/UIExplorerApp.ios.js | 33 +- .../UIExplorer/UIExplorerNavigationReducer.js | 4 +- .../UIExplorer/UIExplorerStateTitleMap.js | 2 +- .../NavigationExperimental/NavigationCard.js | 344 ++++++++++++------ .../NavigationCardStack.js | 79 ++-- .../NavigationCardStackItem.js | 280 -------------- .../NavigationHeader.js | 37 +- .../NavigationLegacyNavigatorRouteStack.js | 3 +- .../NavigationAnimatedView.js | 193 +++++----- .../NavigationContainer.js | 4 +- .../NavigationLinearPanResponder.js | 25 +- .../NavigationPropTypes.js | 76 ++++ .../NavigationRootContainer.js | 96 +++-- .../NavigationStateUtils.js | 22 +- .../NavigationTypeDefinition.js | 93 +++++ .../Reducer/NavigationFindReducer.js | 7 +- .../Reducer/NavigationStackReducer.js | 2 +- .../Reducer/NavigationTabsReducer.js | 3 +- .../__tests__/NavigationStackReducer-test.js | 1 - .../__mocks__/NavigationRootContainer.js | 16 + 23 files changed, 788 insertions(+), 730 deletions(-) delete mode 100644 Libraries/CustomComponents/NavigationExperimental/NavigationCardStackItem.js create mode 100644 Libraries/NavigationExperimental/NavigationPropTypes.js create mode 100644 Libraries/NavigationExperimental/NavigationTypeDefinition.js create mode 100644 Libraries/NavigationExperimental/__mocks__/NavigationRootContainer.js diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js index e74d80e26..d034d87f5 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js @@ -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 { ( - state.key} - /> - )} + renderOverlay={this._renderHeader} setTiming={(pos, navState) => { Animated.timing(pos, {toValue: navState.index, duration: 1000}).start(); }} - renderScene={(props) => ( - - - - { - onNavigate({ - type: 'push', - key: 'Route #' + props.navigationParentState.children.length - }); - }} - /> - - - - )} + renderScene={this._renderCard} /> ); } + + _renderHeader(/*NavigationSceneRendererProps*/ props) { + return ( + state.key} + /> + ); + } + + _renderCard(/*NavigationSceneRendererProps*/ props) { + return ( + + ); + } + + _renderScene(/*NavigationSceneRendererProps*/ props) { + return ( + + + { + props.onNavigate({ + type: 'push', + key: 'Route #' + props.scenes.length, + }); + }} + /> + + + ); + } } const styles = StyleSheet.create({ diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationCardStackExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationCardStackExample.js index e7feb7c60..b6aa01a79 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationCardStackExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationCardStackExample.js @@ -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 ( { - onNavigate({ + props.onNavigate({ type: 'push', - key: 'Route ' + navigationParentState.children.length, + key: 'Route ' + props.scenes.length, }); }} /> { - onNavigate({ + props.onNavigate({ type: 'pop', }); }} diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js index bc1d184db..084dc1f39 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js @@ -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 ( - ); } - _renderHeader(props) { + _renderHeader(props: NavigationSceneRendererProps) { return ( stateTypeTitleMap(state)} /> ); } - _renderScene(props) { + + _renderScene(props: NavigationSceneRendererProps) { + const {onNavigate} = props; return ( - - - { - this.props.onNavigate(ExampleInfoAction()); - }} - /> - { - this.props.onNavigate(ExampleNotifProfileAction()); - }} - /> - { - this.props.onNavigate(ExampleExitAction()); - }} - /> - - + + { + onNavigate(ExampleInfoAction()); + }} + /> + { + onNavigate(ExampleNotifProfileAction()); + }} + /> + { + onNavigate(ExampleExitAction()); + }} + /> + ); } } diff --git a/Examples/UIExplorer/UIExplorerApp.ios.js b/Examples/UIExplorer/UIExplorerApp.ios.js index 3072b36f7..384ea1c18 100644 --- a/Examples/UIExplorer/UIExplorerApp.ios.js +++ b/Examples/UIExplorer/UIExplorerApp.ios.js @@ -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 ( - ); } - _renderOverlay(props: NavigationStateRendererProps): ReactElement { + _renderOverlay(props: NavigationSceneRendererProps): ReactElement { return ( ); } - _renderCard(props: NavigationStateRendererProps): ReactElement { - return ( - - {this._renderScene(props.navigationState)} - - ); - } - - _renderScene(state: Object): ?ReactElement { + _renderScene(props: NavigationSceneRendererProps): ?ReactElement { + const state = props.scene.navigationState; if (state.key === 'AppList') { return ( { - 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 ( - - {this.props.children} - - ); + + 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 . + */ +class NavigationCard extends React.Component { + props: Props; + state: State; + _calculateState: (t: NavigationLayout) => State; + _layoutListeners: Array; + + 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 ( + + {this.props.renderScene(sceneProps)} + + ); + } + + _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); diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js index f36fbc0fe..3c88d2ac3 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js @@ -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 ( - ); } - _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: { diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackItem.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackItem.js deleted file mode 100644 index 4fe0abf49..000000000 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackItem.js +++ /dev/null @@ -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 . - */ -class NavigationCardStackItem extends React.Component { - props: Props; - state: State; - _calculateState: (t: Layout) => State; - _layoutListeners: Array; - - 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 ( - - {this.props.renderScene(this.props)} - - ); - } - - _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); diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js b/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js index 82a807e2f..3b1c5aa4d 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js @@ -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 ( ); } - _renderBackButton() { + + _renderBackButton(): ?ReactElement { if (this.props.navigationState.index === 0) { return null; } @@ -77,7 +86,8 @@ class NavigationHeader extends React.Component { ); } - _renderTitle(childState, index) { + + _renderTitle(childState: NavigationState, index:number): ?ReactElement { return ( ); } - _handleBackPress() { + + _handleBackPress(): void { this.props.onNavigate(NavigationRootContainer.getBackAction()); } } +NavigationHeader.propTypes = NavigationHeaderPropTypes; + NavigationHeader = NavigationContainer.create(NavigationHeader); const styles = StyleSheet.create({ diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationLegacyNavigatorRouteStack.js b/Libraries/CustomComponents/NavigationExperimental/NavigationLegacyNavigatorRouteStack.js index 309dfde0e..f71aa64e9 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationLegacyNavigatorRouteStack.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationLegacyNavigatorRouteStack.js @@ -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; diff --git a/Libraries/NavigationExperimental/NavigationAnimatedView.js b/Libraries/NavigationExperimental/NavigationAnimatedView.js index 4d1f5b68f..5dade85ad 100644 --- a/Libraries/NavigationExperimental/NavigationAnimatedView.js +++ b/Libraries/NavigationExperimental/NavigationAnimatedView.js @@ -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, +}; + +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 { + _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); diff --git a/Libraries/NavigationExperimental/NavigationContainer.js b/Libraries/NavigationExperimental/NavigationContainer.js index 133b498cb..c865ebe15 100644 --- a/Libraries/NavigationExperimental/NavigationContainer.js +++ b/Libraries/NavigationExperimental/NavigationContainer.js @@ -14,7 +14,9 @@ var React = require('React'); var NavigationRootContainer = require('NavigationRootContainer'); -function createNavigationContainer(Component: React.Component): React.Component { +function createNavigationContainer( + Component: ReactClass, +): ReactClass { class NavigationComponent extends React.Component { render() { return ( diff --git a/Libraries/NavigationExperimental/NavigationLinearPanResponder.js b/Libraries/NavigationExperimental/NavigationLinearPanResponder.js index 704f305c7..904e73993 100644 --- a/Libraries/NavigationExperimental/NavigationLinearPanResponder.js +++ b/Libraries/NavigationExperimental/NavigationLinearPanResponder.js @@ -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; + /** * 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; - /** * 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, }; /** diff --git a/Libraries/NavigationExperimental/NavigationPropTypes.js b/Libraries/NavigationExperimental/NavigationPropTypes.js new file mode 100644 index 000000000..b5d878341 --- /dev/null +++ b/Libraries/NavigationExperimental/NavigationPropTypes.js @@ -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, +}; diff --git a/Libraries/NavigationExperimental/NavigationRootContainer.js b/Libraries/NavigationExperimental/NavigationRootContainer.js index 18d7689e0..1cefdc6d8 100644 --- a/Libraries/NavigationExperimental/NavigationRootContainer.js +++ b/Libraries/NavigationExperimental/NavigationRootContainer.js @@ -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; diff --git a/Libraries/NavigationExperimental/NavigationStateUtils.js b/Libraries/NavigationExperimental/NavigationStateUtils.js index d457ebea8..63da07faf 100644 --- a/Libraries/NavigationExperimental/NavigationStateUtils.js +++ b/Libraries/NavigationExperimental/NavigationStateUtils.js @@ -13,24 +13,10 @@ const invariant = require('fbjs/lib/invariant'); -export type NavigationState = { - key: string; -}; - -export type NavigationParentState = { - key: string; - index: number; - children: Array; -}; - -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 ( diff --git a/Libraries/NavigationExperimental/NavigationTypeDefinition.js b/Libraries/NavigationExperimental/NavigationTypeDefinition.js new file mode 100644 index 000000000..3f6f6e439 --- /dev/null +++ b/Libraries/NavigationExperimental/NavigationTypeDefinition.js @@ -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, +}; + +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, +}; + +// 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; diff --git a/Libraries/NavigationExperimental/Reducer/NavigationFindReducer.js b/Libraries/NavigationExperimental/Reducer/NavigationFindReducer.js index bca336ee6..d7e2d1afa 100644 --- a/Libraries/NavigationExperimental/Reducer/NavigationFindReducer.js +++ b/Libraries/NavigationExperimental/Reducer/NavigationFindReducer.js @@ -20,9 +20,12 @@ import type { NavigationState, NavigationReducer -} from 'NavigationStateUtils'; +} from 'NavigationTypeDefinition'; -function NavigationFindReducer(reducers: Array, defaultState: NavigationState): NavigationReducer { +function NavigationFindReducer( + reducers: Array, + defaultState: NavigationState, +): NavigationReducer { return function(lastState: ?NavigationState, action: ?any): NavigationState { for (let i = 0; i < reducers.length; i++) { let reducer = reducers[i]; diff --git a/Libraries/NavigationExperimental/Reducer/NavigationStackReducer.js b/Libraries/NavigationExperimental/Reducer/NavigationStackReducer.js index 89d96da09..f27df30fb 100644 --- a/Libraries/NavigationExperimental/Reducer/NavigationStackReducer.js +++ b/Libraries/NavigationExperimental/Reducer/NavigationStackReducer.js @@ -17,7 +17,7 @@ import type { NavigationState, NavigationParentState, NavigationReducer, -} from 'NavigationStateUtils'; +} from 'NavigationTypeDefinition'; import type { BackAction, diff --git a/Libraries/NavigationExperimental/Reducer/NavigationTabsReducer.js b/Libraries/NavigationExperimental/Reducer/NavigationTabsReducer.js index eaf9ef83b..5063b72bb 100644 --- a/Libraries/NavigationExperimental/Reducer/NavigationTabsReducer.js +++ b/Libraries/NavigationExperimental/Reducer/NavigationTabsReducer.js @@ -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', diff --git a/Libraries/NavigationExperimental/Reducer/__tests__/NavigationStackReducer-test.js b/Libraries/NavigationExperimental/Reducer/__tests__/NavigationStackReducer-test.js index 9a0d50326..42edc9d34 100644 --- a/Libraries/NavigationExperimental/Reducer/__tests__/NavigationStackReducer-test.js +++ b/Libraries/NavigationExperimental/Reducer/__tests__/NavigationStackReducer-test.js @@ -11,7 +11,6 @@ 'use strict'; jest - .dontMock('NavigationRootContainer') .dontMock('NavigationStackReducer') .dontMock('NavigationStateUtils'); diff --git a/Libraries/NavigationExperimental/__mocks__/NavigationRootContainer.js b/Libraries/NavigationExperimental/__mocks__/NavigationRootContainer.js new file mode 100644 index 000000000..87b23c71b --- /dev/null +++ b/Libraries/NavigationExperimental/__mocks__/NavigationRootContainer.js @@ -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; \ No newline at end of file