Remove Deprecated NavigationExperimental

Summary:
Now that there are a number of good navigation solutions provided by the community, we are ready to remove NavigationExperimental from the RN core. The latest navigation doc explains the available options pretty well: http://facebook.github.io/react-native/docs/navigation.html . We should also add a mention to Airbnb's new native-navigation.

For anybody who continues to rely on it, it is recommended to migrate to React Navigation, which will be maintained over the long-term. For those who cannot migrate yet, it is possible to copy this code into your app.
Closes https://github.com/facebook/react-native/pull/13066

Differential Revision: D4757539

Pulled By: ericvicenti

fbshipit-source-id: 949d9b33f188584fb095155fa67d3ce24beba29f
This commit is contained in:
Eric Vicenti 2017-03-23 17:28:34 -07:00 committed by Facebook Github Bot
parent d868e86df0
commit febf3d00ed
32 changed files with 0 additions and 3522 deletions

View File

@ -1,48 +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 NavigationAnimatedValueSubscription
* @flow
*/
'use strict';
import type {
NavigationAnimatedValue
} from 'NavigationTypeDefinition';
class NavigationAnimatedValueSubscription {
_value: NavigationAnimatedValue;
_token: string;
constructor(value: NavigationAnimatedValue, callback: Function) {
this._value = value;
this._token = value.addListener(callback);
}
remove(): void {
this._value.removeListener(this._token);
}
}
module.exports = NavigationAnimatedValueSubscription;

View File

@ -1,132 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* 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 NavigationCard
* @flow
*/
'use strict';
const Animated = require('Animated');
const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder');
const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator');
const NavigationPagerPanResponder = require('NavigationPagerPanResponder');
const NavigationPagerStyleInterpolator = require('NavigationPagerStyleInterpolator');
const NavigationPointerEventsContainer = require('NavigationPointerEventsContainer');
const NavigationPropTypes = require('NavigationPropTypes');
const React = require('React');
const StyleSheet = require('StyleSheet');
import type {
NavigationPanPanHandlers,
NavigationSceneRenderer,
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
type Props = NavigationSceneRendererProps & {
onComponentRef: (ref: any) => void,
onNavigateBack: ?Function,
panHandlers: ?NavigationPanPanHandlers,
pointerEvents: string,
renderScene: NavigationSceneRenderer,
style: any,
};
const {PropTypes} = React;
/**
* Component that renders the scene as card for the <NavigationCardStack />.
*/
class NavigationCard extends React.Component<any, Props, any> {
props: Props;
static propTypes = {
...NavigationPropTypes.SceneRendererProps,
onComponentRef: PropTypes.func.isRequired,
onNavigateBack: PropTypes.func,
panHandlers: NavigationPropTypes.panHandlers,
pointerEvents: PropTypes.string.isRequired,
renderScene: PropTypes.func.isRequired,
style: PropTypes.any,
};
render(): React.Element<any> {
const {
panHandlers,
pointerEvents,
renderScene,
style,
...props /* NavigationSceneRendererProps */
} = this.props;
const viewStyle = style === undefined ?
NavigationCardStackStyleInterpolator.forHorizontal(props) :
style;
const viewPanHandlers = panHandlers === undefined ?
NavigationCardStackPanResponder.forHorizontal({
...props,
onNavigateBack: this.props.onNavigateBack,
}) :
panHandlers;
return (
<Animated.View
{...viewPanHandlers}
pointerEvents={pointerEvents}
ref={this.props.onComponentRef}
style={[styles.main, viewStyle]}>
{renderScene(props)}
</Animated.View>
);
}
}
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,
},
});
NavigationCard = NavigationPointerEventsContainer.create(NavigationCard);
NavigationCard.CardStackPanResponder = NavigationCardStackPanResponder;
NavigationCard.CardStackStyleInterpolator = NavigationCardStackStyleInterpolator;
NavigationCard.PagerPanResponder = NavigationPagerPanResponder;
NavigationCard.PagerStyleInterpolator = NavigationPagerStyleInterpolator;
module.exports = NavigationCard;

View File

@ -1,329 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* 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 NavigationCardStack
* @flow
*/
'use strict';
const NativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
const NavigationCard = require('NavigationCard');
const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder');
const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator');
const NavigationPropTypes = require('NavigationPropTypes');
const NavigationTransitioner = require('NavigationTransitioner');
const React = require('React');
const StyleSheet = require('StyleSheet');
const View = require('View');
const {PropTypes} = React;
const {Directions} = NavigationCardStackPanResponder;
import type {
NavigationState,
NavigationSceneRenderer,
NavigationSceneRendererProps,
NavigationTransitionProps,
NavigationStyleInterpolator,
} from 'NavigationTypeDefinition';
import type {
NavigationGestureDirection,
} from 'NavigationCardStackPanResponder';
type Props = {
direction: NavigationGestureDirection,
navigationState: NavigationState,
onNavigateBack?: Function,
renderHeader: ?NavigationSceneRenderer,
renderScene: NavigationSceneRenderer,
cardStyle?: any,
style: any,
gestureResponseDistance?: ?number,
enableGestures: ?boolean,
cardStyleInterpolator?: ?NavigationStyleInterpolator,
scenesStyle?: any,
};
type DefaultProps = {
direction: NavigationGestureDirection,
enableGestures: boolean,
};
/**
* A controlled navigation view that renders a stack of cards.
*
* ```html
* +------------+
* +-| Header |
* +-+ |------------|
* | | | |
* | | | Focused |
* | | | Card |
* | | | |
* +-+ | |
* +-+ |
* +------------+
* ```
*
* ## Example
*
* ```js
*
* class App extends React.Component {
* constructor(props, context) {
* this.state = {
* navigation: {
* index: 0,
* routes: [
* {key: 'page 1'},
* },
* },
* };
* }
*
* render() {
* return (
* <NavigationCardStack
* navigationState={this.state.navigation}
* renderScene={this._renderScene}
* />
* );
* }
*
* _renderScene: (props) => {
* return (
* <View>
* <Text>{props.scene.route.key}</Text>
* </View>
* );
* };
* ```
*/
class NavigationCardStack extends React.Component<DefaultProps, Props, void> {
_render : NavigationSceneRenderer;
_renderScene : NavigationSceneRenderer;
static propTypes = {
/**
* Custom style applied to the card.
*/
cardStyle: PropTypes.any,
/**
* Direction of the cards movement. Value could be `horizontal` or
* `vertical`. Default value is `horizontal`.
*/
direction: PropTypes.oneOf([Directions.HORIZONTAL, Directions.VERTICAL]),
/**
* The distance from the edge of the card which gesture response can start
* for. Default value is `30`.
*/
gestureResponseDistance: PropTypes.number,
/**
* An interpolator function that is passed an object parameter of type
* NavigationSceneRendererProps and should return a style object to apply to
* the transitioning navigation card.
*
* Default interpolator transitions translateX, scale, and opacity.
*/
cardStyleInterpolator: PropTypes.func,
/**
* Enable gestures. Default value is true.
*
* When disabled, transition animations will be handled natively, which
* improves performance of the animation. In future iterations, gestures
* will also work with native-driven animation.
*/
enableGestures: PropTypes.bool,
/**
* The controlled navigation state. Typically, the navigation state
* look like this:
*
* ```js
* const navigationState = {
* index: 0, // the index of the selected route.
* routes: [ // A list of routes.
* {key: 'page 1'}, // The 1st route.
* {key: 'page 2'}, // The second route.
* ],
* };
* ```
*/
navigationState: NavigationPropTypes.navigationState.isRequired,
/**
* Callback that is called when the "back" action is performed.
* This happens when the back button is pressed or the back gesture is
* performed.
*/
onNavigateBack: PropTypes.func,
/**
* Function that renders the header.
*/
renderHeader: PropTypes.func,
/**
* Function that renders the a scene for a route.
*/
renderScene: PropTypes.func.isRequired,
/**
* Custom style applied to the cards stack.
*/
style: View.propTypes.style,
/**
* Custom style applied to the scenes stack.
*/
scenesStyle: View.propTypes.style,
};
static defaultProps: DefaultProps = {
direction: Directions.HORIZONTAL,
enableGestures: true,
};
constructor(props: Props, context: any) {
super(props, context);
}
componentWillMount(): void {
this._render = this._render.bind(this);
this._renderScene = this._renderScene.bind(this);
}
render(): React.Element<any> {
return (
<NavigationTransitioner
configureTransition={this._configureTransition}
navigationState={this.props.navigationState}
render={this._render}
style={this.props.style}
/>
);
}
_configureTransition = () => {
const isVertical = this.props.direction === 'vertical';
const animationConfig = {};
if (
!!NativeAnimatedModule
// Gestures do not work with the current iteration of native animation
// driving. When gestures are disabled, we can drive natively.
&& !this.props.enableGestures
// Native animation support also depends on the transforms used:
&& NavigationCardStackStyleInterpolator.canUseNativeDriver(isVertical)
) {
animationConfig.useNativeDriver = true;
}
return animationConfig;
}
_render(props: NavigationTransitionProps): React.Element<any> {
const {
renderHeader,
} = this.props;
const header = renderHeader ? <View>{renderHeader(props)}</View> : null;
const scenes = props.scenes.map(
scene => this._renderScene({
...props,
scene,
})
);
return (
<View style={styles.container}>
<View
style={[styles.scenes, this.props.scenesStyle]}>
{scenes}
</View>
{header}
</View>
);
}
_renderScene(props: NavigationSceneRendererProps): React.Element<any> {
const isVertical = this.props.direction === 'vertical';
const interpolator = this.props.cardStyleInterpolator || (isVertical ?
NavigationCardStackStyleInterpolator.forVertical :
NavigationCardStackStyleInterpolator.forHorizontal);
const style = interpolator(props);
let panHandlers = null;
if (this.props.enableGestures) {
const panHandlersProps = {
...props,
onNavigateBack: this.props.onNavigateBack,
gestureResponseDistance: this.props.gestureResponseDistance,
};
panHandlers = isVertical ?
NavigationCardStackPanResponder.forVertical(panHandlersProps) :
NavigationCardStackPanResponder.forHorizontal(panHandlersProps);
}
return (
<NavigationCard
{...props}
key={'card_' + props.scene.key}
panHandlers={panHandlers}
renderScene={this.props.renderScene}
style={[style, this.props.cardStyle]}
/>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
// Header is physically rendered after scenes so that Header won't be
// covered by the shadows of the scenes.
// That said, we'd have use `flexDirection: 'column-reverse'` to move
// Header above the scenes.
flexDirection: 'column-reverse',
},
scenes: {
flex: 1,
},
});
module.exports = NavigationCardStack;

View File

@ -1,271 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NavigationCardStackPanResponder
* @flow
*/
'use strict';
const Animated = require('Animated');
const I18nManager = require('I18nManager');
const NavigationAbstractPanResponder = require('NavigationAbstractPanResponder');
const clamp = require('clamp');
import type {
NavigationPanPanHandlers,
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
const emptyFunction = () => {};
/**
* The duration of the card animation in milliseconds.
*/
const ANIMATION_DURATION = 250;
/**
* The threshold to invoke the `onNavigateBack` action.
* For instance, `1 / 3` means that moving greater than 1 / 3 of the width of
* the view will navigate.
*/
const POSITION_THRESHOLD = 1 / 3;
/**
* The threshold (in pixels) to start the gesture action.
*/
const RESPOND_THRESHOLD = 15;
/**
* The threshold (in pixels) to finish the gesture action.
*/
const DISTANCE_THRESHOLD = 100;
/**
* Primitive gesture directions.
*/
const Directions = {
'HORIZONTAL': 'horizontal',
'VERTICAL': 'vertical',
};
export type NavigationGestureDirection = 'horizontal' | 'vertical';
type Props = NavigationSceneRendererProps & {
onNavigateBack: ?Function,
/**
* The distance from the edge of the navigator which gesture response can start for.
**/
gestureResponseDistance: ?number,
};
/**
* Pan responder that handles gesture for a card in the cards stack.
*
* +------------+
* +-+ |
* +-+ | |
* | | | |
* | | | Focused |
* | | | Card |
* | | | |
* +-+ | |
* +-+ |
* +------------+
*/
class NavigationCardStackPanResponder extends NavigationAbstractPanResponder {
_isResponding: boolean;
_isVertical: boolean;
_props: Props;
_startValue: number;
constructor(
direction: NavigationGestureDirection,
props: Props,
) {
super();
this._isResponding = false;
this._isVertical = direction === Directions.VERTICAL;
this._props = props;
this._startValue = 0;
// Hack to make this work with native driven animations. We add a single listener
// so the JS value of the following animated values gets updated. We rely on
// some Animated private APIs and not doing so would require using a bunch of
// value listeners but we'd have to remove them to not leak and I'm not sure
// when we'd do that with the current structure we have. `stopAnimation` callback
// is also broken with native animated values that have no listeners so if we
// want to remove this we have to fix this too.
this._addNativeListener(this._props.layout.width);
this._addNativeListener(this._props.layout.height);
this._addNativeListener(this._props.position);
}
onMoveShouldSetPanResponder(event: any, gesture: any): boolean {
const props = this._props;
if (props.navigationState.index !== props.scene.index) {
return false;
}
const layout = props.layout;
const isVertical = this._isVertical;
const index = props.navigationState.index;
const currentDragDistance = gesture[isVertical ? 'dy' : 'dx'];
const currentDragPosition = gesture[isVertical ? 'moveY' : 'moveX'];
const maxDragDistance = isVertical ?
layout.height.__getValue() :
layout.width.__getValue();
const positionMax = isVertical ?
props.gestureResponseDistance :
/**
* For horizontal scroll views, a distance of 30 from the left of the screen is the
* standard maximum position to start touch responsiveness.
*/
props.gestureResponseDistance || 30;
if (positionMax != null && currentDragPosition > positionMax) {
return false;
}
return (
Math.abs(currentDragDistance) > RESPOND_THRESHOLD &&
maxDragDistance > 0 &&
index > 0
);
}
onPanResponderGrant(): void {
this._isResponding = false;
this._props.position.stopAnimation((value: number) => {
this._isResponding = true;
this._startValue = value;
});
}
onPanResponderMove(event: any, gesture: any): void {
if (!this._isResponding) {
return;
}
const props = this._props;
const layout = props.layout;
const isVertical = this._isVertical;
const axis = isVertical ? 'dy' : 'dx';
const index = props.navigationState.index;
const distance = isVertical ?
layout.height.__getValue() :
layout.width.__getValue();
const currentValue = I18nManager.isRTL && axis === 'dx' ?
this._startValue + (gesture[axis] / distance) :
this._startValue - (gesture[axis] / distance);
const value = clamp(
index - 1,
currentValue,
index
);
props.position.setValue(value);
}
onPanResponderRelease(event: any, gesture: any): void {
if (!this._isResponding) {
return;
}
this._isResponding = false;
const props = this._props;
const isVertical = this._isVertical;
const axis = isVertical ? 'dy' : 'dx';
const index = props.navigationState.index;
const distance = I18nManager.isRTL && axis === 'dx' ?
-gesture[axis] :
gesture[axis];
props.position.stopAnimation((value: number) => {
this._reset();
if (!props.onNavigateBack) {
return;
}
if (
distance > DISTANCE_THRESHOLD ||
value <= index - POSITION_THRESHOLD
) {
props.onNavigateBack();
}
});
}
onPanResponderTerminate(): void {
this._isResponding = false;
this._reset();
}
_reset(): void {
const props = this._props;
Animated.timing(
props.position,
{
toValue: props.navigationState.index,
duration: ANIMATION_DURATION,
useNativeDriver: props.position.__isNative,
}
).start();
}
_addNativeListener(animatedValue) {
if (!animatedValue.__isNative) {
return;
}
if (Object.keys(animatedValue._listeners).length === 0) {
animatedValue.addListener(emptyFunction);
}
}
}
function createPanHandlers(
direction: NavigationGestureDirection,
props: Props,
): NavigationPanPanHandlers {
const responder = new NavigationCardStackPanResponder(direction, props);
return responder.panHandlers;
}
function forHorizontal(
props: Props,
): NavigationPanPanHandlers {
return createPanHandlers(Directions.HORIZONTAL, props);
}
function forVertical(
props: Props,
): NavigationPanPanHandlers {
return createPanHandlers(Directions.VERTICAL, props);
}
module.exports = {
// constants
ANIMATION_DURATION,
DISTANCE_THRESHOLD,
POSITION_THRESHOLD,
RESPOND_THRESHOLD,
// enums
Directions,
// methods.
forHorizontal,
forVertical,
};

View File

@ -1,176 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* 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 NavigationCardStackStyleInterpolator
* @flow
*/
'use strict';
const I18nManager = require('I18nManager');
import type {
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
/**
* Utility that builds the style for the card in the cards stack.
*
* +------------+
* +-+ |
* +-+ | |
* | | | |
* | | | Focused |
* | | | Card |
* | | | |
* +-+ | |
* +-+ |
* +------------+
*/
/**
* Render the initial style when the initial layout isn't measured yet.
*/
function forInitial(props: NavigationSceneRendererProps): Object {
const {
navigationState,
scene,
} = props;
const focused = navigationState.index === scene.index;
const opacity = focused ? 1 : 0;
// If not focused, move the scene to the far away.
const translate = focused ? 0 : 1000000;
return {
opacity,
transform: [
{ translateX: translate },
{ translateY: translate },
],
};
}
function forHorizontal(props: NavigationSceneRendererProps): Object {
const {
layout,
position,
scene,
} = props;
if (!layout.isMeasured) {
return forInitial(props);
}
const index = scene.index;
const inputRange = [index - 1, index, index + 0.99, index + 1];
const width = layout.initWidth;
const outputRange = I18nManager.isRTL ?
([-width, 0, 10, 10]: Array<number>) :
([width, 0, -10, -10]: Array<number>);
const opacity = position.interpolate({
inputRange,
outputRange: ([1, 1, 0.3, 0]: Array<number>),
});
const scale = position.interpolate({
inputRange,
outputRange: ([1, 1, 0.95, 0.95]: Array<number>),
});
const translateY = 0;
const translateX = position.interpolate({
inputRange,
outputRange,
});
return {
opacity,
transform: [
{ scale },
{ translateX },
{ translateY },
],
};
}
function forVertical(props: NavigationSceneRendererProps): Object {
const {
layout,
position,
scene,
} = props;
if (!layout.isMeasured) {
return forInitial(props);
}
const index = scene.index;
const inputRange = [index - 1, index, index + 0.99, index + 1];
const height = layout.initHeight;
const opacity = position.interpolate({
inputRange,
outputRange: ([1, 1, 0.3, 0]: Array<number>),
});
const scale = position.interpolate({
inputRange,
outputRange: ([1, 1, 0.95, 0.95]: Array<number>),
});
const translateX = 0;
const translateY = position.interpolate({
inputRange,
outputRange: ([height, 0, -10, -10]: Array<number>),
});
return {
opacity,
transform: [
{ scale },
{ translateX },
{ translateY },
],
};
}
function canUseNativeDriver(isVertical: boolean): boolean {
// The native driver can be enabled for this interpolator because the scale,
// translateX, and translateY transforms are supported with the native
// animation driver.
return true;
}
module.exports = {
forHorizontal,
forVertical,
canUseNativeDriver,
};

View File

@ -1,282 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* 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 NavigationHeader
* @flow
*/
'use strict';
const NavigationHeaderBackButton = require('NavigationHeaderBackButton');
const NavigationHeaderStyleInterpolator = require('NavigationHeaderStyleInterpolator');
const NavigationHeaderTitle = require('NavigationHeaderTitle');
const NavigationPropTypes = require('NavigationPropTypes');
const React = require('React');
const ReactNative = require('react-native');
const TVEventHandler = require('TVEventHandler');
const {
Animated,
Platform,
StyleSheet,
View,
} = ReactNative;
import type {
NavigationSceneRendererProps,
NavigationStyleInterpolator,
} from 'NavigationTypeDefinition';
type SubViewProps = NavigationSceneRendererProps & {
onNavigateBack: ?Function,
};
type SubViewRenderer = (subViewProps: SubViewProps) => ?React.Element<any>;
type DefaultProps = {
renderLeftComponent: SubViewRenderer,
renderRightComponent: SubViewRenderer,
renderTitleComponent: SubViewRenderer,
statusBarHeight: number | Animated.Value,
};
type Props = NavigationSceneRendererProps & {
onNavigateBack: ?Function,
renderLeftComponent: SubViewRenderer,
renderRightComponent: SubViewRenderer,
renderTitleComponent: SubViewRenderer,
style?: any,
viewProps?: any,
statusBarHeight: number | Animated.Value,
};
type SubViewName = 'left' | 'title' | 'right';
const APPBAR_HEIGHT = Platform.OS === 'ios' ? 44 : 56;
const STATUSBAR_HEIGHT = Platform.OS === 'ios' ? 20 : 0;
const {PropTypes} = React;
class NavigationHeader extends React.PureComponent<DefaultProps, Props, any> {
props: Props;
static defaultProps = {
renderTitleComponent: (props: SubViewProps) => {
const title = String(props.scene.route.title || '');
return <NavigationHeaderTitle>{title}</NavigationHeaderTitle>;
},
renderLeftComponent: (props: SubViewProps) => {
if (props.scene.index === 0 || !props.onNavigateBack) {
return null;
}
return (
<NavigationHeaderBackButton
onPress={props.onNavigateBack}
/>
);
},
renderRightComponent: (props: SubViewProps) => {
return null;
},
statusBarHeight: STATUSBAR_HEIGHT,
};
static propTypes = {
...NavigationPropTypes.SceneRendererProps,
onNavigateBack: PropTypes.func,
renderLeftComponent: PropTypes.func,
renderRightComponent: PropTypes.func,
renderTitleComponent: PropTypes.func,
style: View.propTypes.style,
statusBarHeight: PropTypes.number,
viewProps: PropTypes.shape(View.propTypes),
};
_tvEventHandler: TVEventHandler;
componentDidMount(): void {
this._tvEventHandler = new TVEventHandler();
this._tvEventHandler.enable(this, function(cmp, evt) {
if (evt && evt.eventType === 'menu') {
cmp.props.onNavigateBack && cmp.props.onNavigateBack();
}
});
}
componentWillUnmount(): void {
if (this._tvEventHandler) {
this._tvEventHandler.disable();
delete this._tvEventHandler;
}
}
render(): React.Element<any> {
const { scenes, style, viewProps } = this.props;
const scenesProps = scenes.map(scene => {
const props = NavigationPropTypes.extractSceneRendererProps(this.props);
props.scene = scene;
return props;
});
const barHeight = (this.props.statusBarHeight instanceof Animated.Value)
? Animated.add(this.props.statusBarHeight, new Animated.Value(APPBAR_HEIGHT))
: APPBAR_HEIGHT + this.props.statusBarHeight;
return (
<Animated.View style={[
styles.appbar,
{ height: barHeight },
style
]}
{...viewProps}
>
{scenesProps.map(this._renderLeft, this)}
{scenesProps.map(this._renderTitle, this)}
{scenesProps.map(this._renderRight, this)}
</Animated.View>
);
}
_renderLeft = (props: NavigationSceneRendererProps): ?React.Element<any> => {
return this._renderSubView(
props,
'left',
this.props.renderLeftComponent,
NavigationHeaderStyleInterpolator.forLeft,
);
};
_renderTitle = (props: NavigationSceneRendererProps): ?React.Element<any> => {
return this._renderSubView(
props,
'title',
this.props.renderTitleComponent,
NavigationHeaderStyleInterpolator.forCenter,
);
};
_renderRight = (props: NavigationSceneRendererProps): ?React.Element<any> => {
return this._renderSubView(
props,
'right',
this.props.renderRightComponent,
NavigationHeaderStyleInterpolator.forRight,
);
};
_renderSubView(
props: NavigationSceneRendererProps,
name: SubViewName,
renderer: SubViewRenderer,
styleInterpolator: NavigationStyleInterpolator,
): ?React.Element<any> {
const {
scene,
navigationState,
} = props;
const {
index,
isStale,
key,
} = scene;
const offset = navigationState.index - index;
if (Math.abs(offset) > 2) {
// Scene is far away from the active scene. Hides it to avoid unnecessary
// rendering.
return null;
}
const subViewProps = {...props, onNavigateBack: this.props.onNavigateBack};
const subView = renderer(subViewProps);
if (subView === null) {
return null;
}
const pointerEvents = offset !== 0 || isStale ? 'none' : 'box-none';
return (
<Animated.View
pointerEvents={pointerEvents}
key={name + '_' + key}
style={[
styles[name],
{ marginTop: this.props.statusBarHeight },
styleInterpolator(props),
]}>
{subView}
</Animated.View>
);
}
static HEIGHT = APPBAR_HEIGHT + STATUSBAR_HEIGHT;
static Title = NavigationHeaderTitle;
static BackButton = NavigationHeaderBackButton;
}
const styles = StyleSheet.create({
appbar: {
alignItems: 'center',
backgroundColor: Platform.OS === 'ios' ? '#EFEFF2' : '#FFF',
borderBottomColor: 'rgba(0, 0, 0, .15)',
borderBottomWidth: Platform.OS === 'ios' ? StyleSheet.hairlineWidth : 0,
elevation: 4,
flexDirection: 'row',
justifyContent: 'flex-start',
},
title: {
bottom: 0,
left: APPBAR_HEIGHT,
position: 'absolute',
right: APPBAR_HEIGHT,
top: 0,
},
left: {
bottom: 0,
left: 0,
position: 'absolute',
top: 0,
},
right: {
bottom: 0,
position: 'absolute',
right: 0,
top: 0,
},
});
module.exports = NavigationHeader;

View File

@ -1,69 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @providesModule NavigationHeaderBackButton
* @flow
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {
I18nManager,
Image,
Platform,
StyleSheet,
TouchableOpacity,
} = ReactNative;
type Props = {
imageStyle?: any,
onPress: Function,
style?: any,
};
const NavigationHeaderBackButton = (props: Props) => (
<TouchableOpacity style={[styles.buttonContainer, props.style]} onPress={props.onPress}>
<Image style={[styles.button, props.imageStyle]} source={require('./assets/back-icon.png')} />
</TouchableOpacity>
);
NavigationHeaderBackButton.propTypes = {
onPress: React.PropTypes.func.isRequired
};
const styles = StyleSheet.create({
buttonContainer: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
button: {
height: 24,
width: 24,
margin: Platform.OS === 'ios' ? 10 : 16,
resizeMode: 'contain',
transform: [{scaleX: I18nManager.isRTL ? -1 : 1}],
}
});
module.exports = NavigationHeaderBackButton;

View File

@ -1,99 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* 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 NavigationHeaderStyleInterpolator
* @flow
*/
'use strict';
const I18nManager = require('I18nManager');
import type {
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
/**
* Utility that builds the style for the navigation header.
*
* +-------------+-------------+-------------+
* | | | |
* | Left | Title | Right |
* | Component | Component | Component |
* | | | |
* +-------------+-------------+-------------+
*/
function forLeft(props: NavigationSceneRendererProps): Object {
const {position, scene} = props;
const {index} = scene;
return {
opacity: position.interpolate({
inputRange: [ index - 1, index, index + 1 ],
outputRange: ([ 0, 1, 0 ]: Array<number>),
}),
};
}
function forCenter(props: NavigationSceneRendererProps): Object {
const {position, scene} = props;
const {index} = scene;
return {
opacity:position.interpolate({
inputRange: [ index - 1, index, index + 1 ],
outputRange: ([ 0, 1, 0 ]: Array<number>),
}),
transform: [
{
translateX: position.interpolate({
inputRange: [ index - 1, index + 1 ],
outputRange: I18nManager.isRTL ?
([ -200, 200 ]: Array<number>) :
([ 200, -200 ]: Array<number>),
}),
}
],
};
}
function forRight(props: NavigationSceneRendererProps): Object {
const {position, scene} = props;
const {index} = scene;
return {
opacity: position.interpolate({
inputRange: [ index - 1, index, index + 1 ],
outputRange: ([ 0, 1, 0 ]: Array<number>),
}),
};
}
module.exports = {
forCenter,
forLeft,
forRight,
};

View File

@ -1,81 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* 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 NavigationHeaderTitle
* @flow
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {
Platform,
StyleSheet,
View,
Text,
} = ReactNative;
type Props = {
children?: React.Element<any>,
style?: any,
textStyle?: any,
viewProps?: any,
}
const NavigationHeaderTitle = ({ children, style, textStyle, viewProps }: Props) => (
<View style={[ styles.title, style ]} {...viewProps}>
<Text style={[ styles.titleText, textStyle ]}>{children}</Text>
</View>
);
const styles = StyleSheet.create({
title: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
marginHorizontal: 16
},
titleText: {
flex: 1,
fontSize: 18,
fontWeight: '500',
color: 'rgba(0, 0, 0, .9)',
textAlign: Platform.OS === 'ios' ? 'center' : 'left'
}
});
NavigationHeaderTitle.propTypes = {
children: React.PropTypes.node.isRequired,
style: View.propTypes.style,
textStyle: Text.propTypes.style
};
module.exports = NavigationHeaderTitle;

View File

@ -1,238 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NavigationPagerPanResponder
* @flow
*/
'use strict';
const Animated = require('Animated');
const NavigationAbstractPanResponder = require('NavigationAbstractPanResponder');
const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder');
const I18nManager = require('I18nManager');
const clamp = require('clamp');
import type {
NavigationPanPanHandlers,
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
import type {
NavigationGestureDirection,
} from 'NavigationCardStackPanResponder';
type Props = NavigationSceneRendererProps & {
onNavigateBack: ?Function,
onNavigateForward: ?Function,
};
/**
* Primitive gesture directions.
*/
const {
ANIMATION_DURATION,
POSITION_THRESHOLD,
RESPOND_THRESHOLD,
Directions,
} = NavigationCardStackPanResponder;
/**
* The threshold (in pixels) to finish the gesture action.
*/
const DISTANCE_THRESHOLD = 50;
/**
* The threshold to trigger the gesture action. This determines the rate of the
* flick when the action will be triggered
*/
const VELOCITY_THRESHOLD = 1.5;
/**
* Pan responder that handles gesture for a card in the cards list.
*
* +-------------+-------------+-------------+
* | | | |
* | | | |
* | | | |
* | Next | Focused | Previous |
* | Card | Card | Card |
* | | | |
* | | | |
* | | | |
* +-------------+-------------+-------------+
*/
class NavigationPagerPanResponder extends NavigationAbstractPanResponder {
_isResponding: boolean;
_isVertical: boolean;
_props: Props;
_startValue: number;
constructor(
direction: NavigationGestureDirection,
props: Props,
) {
super();
this._isResponding = false;
this._isVertical = direction === Directions.VERTICAL;
this._props = props;
this._startValue = 0;
}
onMoveShouldSetPanResponder(event: any, gesture: any): boolean {
const props = this._props;
if (props.navigationState.index !== props.scene.index) {
return false;
}
const layout = props.layout;
const isVertical = this._isVertical;
const axis = isVertical ? 'dy' : 'dx';
const index = props.navigationState.index;
const distance = isVertical ?
layout.height.__getValue() :
layout.width.__getValue();
return (
Math.abs(gesture[axis]) > RESPOND_THRESHOLD &&
distance > 0 &&
index >= 0
);
}
onPanResponderGrant(): void {
this._isResponding = false;
this._props.position.stopAnimation((value: number) => {
this._isResponding = true;
this._startValue = value;
});
}
onPanResponderMove(event: any, gesture: any): void {
if (!this._isResponding) {
return;
}
const {
layout,
navigationState,
position,
scenes,
} = this._props;
const isVertical = this._isVertical;
const axis = isVertical ? 'dy' : 'dx';
const index = navigationState.index;
const distance = isVertical ?
layout.height.__getValue() :
layout.width.__getValue();
const currentValue = I18nManager.isRTL && axis === 'dx' ?
this._startValue + (gesture[axis] / distance) :
this._startValue - (gesture[axis] / distance);
const prevIndex = Math.max(
0,
index - 1,
);
const nextIndex = Math.min(
index + 1,
scenes.length - 1,
);
const value = clamp(
prevIndex,
currentValue,
nextIndex,
);
position.setValue(value);
}
onPanResponderRelease(event: any, gesture: any): void {
if (!this._isResponding) {
return;
}
this._isResponding = false;
const {
navigationState,
onNavigateBack,
onNavigateForward,
position,
} = this._props;
const isVertical = this._isVertical;
const axis = isVertical ? 'dy' : 'dx';
const velocityAxis = isVertical ? 'vy' : 'vx';
const index = navigationState.index;
const distance = I18nManager.isRTL && axis === 'dx' ?
-gesture[axis] :
gesture[axis];
const moveSpeed = I18nManager.isRTL && velocityAxis === 'vx' ?
-gesture[velocityAxis] :
gesture[velocityAxis];
position.stopAnimation((value: number) => {
this._reset();
if (
distance > DISTANCE_THRESHOLD ||
value <= index - POSITION_THRESHOLD ||
moveSpeed > VELOCITY_THRESHOLD
) {
onNavigateBack && onNavigateBack();
return;
}
if (
distance < -DISTANCE_THRESHOLD ||
value >= index + POSITION_THRESHOLD ||
moveSpeed < -VELOCITY_THRESHOLD
) {
onNavigateForward && onNavigateForward();
}
});
}
onPanResponderTerminate(): void {
this._isResponding = false;
this._reset();
}
_reset(): void {
const props = this._props;
Animated.timing(
props.position,
{
toValue: props.navigationState.index,
duration: ANIMATION_DURATION,
}
).start();
}
}
function createPanHandlers(
direction: NavigationGestureDirection,
props: Props,
): NavigationPanPanHandlers {
const responder = new NavigationPagerPanResponder(direction, props);
return responder.panHandlers;
}
function forHorizontal(
props: Props,
): NavigationPanPanHandlers {
return createPanHandlers(Directions.HORIZONTAL, props);
}
module.exports = {
forHorizontal,
};

View File

@ -1,116 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* 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 NavigationPagerStyleInterpolator
* @flow
*/
'use strict';
const I18nManager = require('I18nManager');
import type {
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
/**
* Utility that builds the style for the card in the cards list.
*
* +-------------+-------------+-------------+
* | | | |
* | | | |
* | | | |
* | Next | Focused | Previous |
* | Card | Card | Card |
* | | | |
* | | | |
* | | | |
* +-------------+-------------+-------------+
*/
/**
* Render the initial style when the initial layout isn't measured yet.
*/
function forInitial(props: NavigationSceneRendererProps): Object {
const {
navigationState,
scene,
} = props;
const focused = navigationState.index === scene.index;
const opacity = focused ? 1 : 0;
// If not focused, move the scene to the far away.
const dir = scene.index > navigationState.index ? 1 : -1;
const translate = focused ? 0 : (1000000 * dir);
return {
opacity,
transform: [
{ translateX: translate },
{ translateY: translate },
],
};
}
function forHorizontal(props: NavigationSceneRendererProps): Object {
const {
layout,
position,
scene,
} = props;
if (!layout.isMeasured) {
return forInitial(props);
}
const index = scene.index;
const inputRange = [index - 1, index, index + 1];
const width = layout.initWidth;
const outputRange = I18nManager.isRTL ?
([-width, 0, width]: Array<number>) :
([width, 0, -width]: Array<number>);
const translateX = position.interpolate({
inputRange,
outputRange,
});
return {
opacity : 1,
shadowColor: 'transparent',
shadowRadius: 0,
transform: [
{ scale: 1 },
{ translateX },
{ translateY: 0 },
],
};
}
module.exports = {
forHorizontal,
};

View File

@ -1,158 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* 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 NavigationPointerEventsContainer
* @flow
*/
'use strict';
const React = require('React');
const NavigationAnimatedValueSubscription = require('NavigationAnimatedValueSubscription');
const invariant = require('fbjs/lib/invariant');
import type {
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
type Props = NavigationSceneRendererProps;
const MIN_POSITION_OFFSET = 0.01;
/**
* Create a higher-order component that automatically computes the
* `pointerEvents` property for a component whenever navigation position
* changes.
*/
function create(
Component: ReactClass<any>,
): ReactClass<any> {
class Container extends React.Component<any, Props, any> {
_component: any;
_onComponentRef: (view: any) => void;
_onPositionChange: (data: {value: number}) => void;
_pointerEvents: string;
_positionListener: ?NavigationAnimatedValueSubscription;
props: Props;
constructor(props: Props, context: any) {
super(props, context);
this._pointerEvents = this._computePointerEvents();
}
componentWillMount(): void {
this._onPositionChange = this._onPositionChange.bind(this);
this._onComponentRef = this._onComponentRef.bind(this);
}
componentDidMount(): void {
this._bindPosition(this.props);
}
componentWillUnmount(): void {
this._positionListener && this._positionListener.remove();
}
componentWillReceiveProps(nextProps: Props): void {
this._bindPosition(nextProps);
}
render(): React.Element<any> {
this._pointerEvents = this._computePointerEvents();
return (
<Component
{...this.props}
pointerEvents={this._pointerEvents}
onComponentRef={this._onComponentRef}
/>
);
}
_onComponentRef(component: any): void {
this._component = component;
if (component) {
invariant(
typeof component.setNativeProps === 'function',
'component must implement method `setNativeProps`',
);
}
}
_bindPosition(props: NavigationSceneRendererProps): void {
this._positionListener && this._positionListener.remove();
this._positionListener = new NavigationAnimatedValueSubscription(
props.position,
this._onPositionChange,
);
}
_onPositionChange(): void {
if (this._component) {
const pointerEvents = this._computePointerEvents();
if (this._pointerEvents !== pointerEvents) {
this._pointerEvents = pointerEvents;
this._component.setNativeProps({pointerEvents});
}
}
}
_computePointerEvents(): string {
const {
navigationState,
position,
scene,
} = this.props;
if (scene.isStale || navigationState.index !== scene.index) {
// The scene isn't focused.
return scene.index > navigationState.index ?
'box-only' :
'none';
}
const offset = position.__getAnimatedValue() - navigationState.index;
if (Math.abs(offset) > MIN_POSITION_OFFSET) {
// The positon is still away from scene's index.
// Scene's children should not receive touches until the position
// is close enough to scene's index.
return 'box-only';
}
return 'auto';
}
}
return Container;
}
module.exports = {
create,
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 439 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

View File

@ -1,56 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NavigationAbstractPanResponder
* @flow
*/
'use strict';
const PanResponder = require('PanResponder');
const invariant = require('fbjs/lib/invariant');
import type {
NavigationPanPanHandlers,
} from 'NavigationTypeDefinition';
const EmptyPanHandlers = {
onMoveShouldSetPanResponder: null,
onPanResponderGrant: null,
onPanResponderMove: null,
onPanResponderRelease: null,
onPanResponderTerminate: null,
};
/**
* Abstract class that defines the common interface of PanResponder that handles
* the gesture actions.
*/
class NavigationAbstractPanResponder {
panHandlers: NavigationPanPanHandlers;
constructor() {
const config = {};
Object.keys(EmptyPanHandlers).forEach(name => {
const fn: any = (this: any)[name];
invariant(
typeof fn === 'function',
'subclass of `NavigationAbstractPanResponder` must implement method %s',
name
);
config[name] = fn.bind(this);
}, this);
this.panHandlers = PanResponder.create(config).panHandlers;
}
}
module.exports = NavigationAbstractPanResponder;

View File

@ -1,48 +0,0 @@
/**
* 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 NavigationExperimental
* @flow
*/
'use strict';
const NavigationCard = require('NavigationCard');
const NavigationCardStack = require('NavigationCardStack');
const NavigationHeader = require('NavigationHeader');
const NavigationPropTypes = require('NavigationPropTypes');
const NavigationStateUtils = require('NavigationStateUtils');
const NavigationTransitioner = require('NavigationTransitioner');
const warning = require('fbjs/lib/warning');
// This warning will only be reached if the user has required the module
warning(
false,
'NavigationExperimental is deprecated and will be removed in a future ' +
'version of React Native. The NavigationExperimental views live on in ' +
'the React-Navigation project, which also makes it easy to declare ' +
'navigation logic for your app. Learn more at https://reactnavigation.org/'
);
const NavigationExperimental = {
// Core
StateUtils: NavigationStateUtils,
// Views
Transitioner: NavigationTransitioner,
// CustomComponents:
Card: NavigationCard,
CardStack: NavigationCardStack,
Header: NavigationHeader,
PropTypes: NavigationPropTypes,
};
module.exports = NavigationExperimental;

View File

@ -1,124 +0,0 @@
/**
* 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';
import type {
NavigationSceneRendererProps,
} from 'NavigationTypeDefinition';
/**
* 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');
const {PropTypes} = React;
/* NavigationAction */
const action = PropTypes.shape({
type: PropTypes.string.isRequired,
});
/* NavigationAnimatedValue */
const animatedValue = PropTypes.instanceOf(Animated.Value);
/* NavigationRoute */
const navigationRoute = PropTypes.shape({
key: PropTypes.string.isRequired,
});
/* NavigationState */
const navigationState = PropTypes.shape({
index: PropTypes.number.isRequired,
routes: PropTypes.arrayOf(navigationRoute),
});
/* NavigationLayout */
const layout = PropTypes.shape({
height: animatedValue,
initHeight: PropTypes.number.isRequired,
initWidth: PropTypes.number.isRequired,
isMeasured: PropTypes.bool.isRequired,
width: animatedValue,
});
/* NavigationScene */
const scene = PropTypes.shape({
index: PropTypes.number.isRequired,
isActive: PropTypes.bool.isRequired,
isStale: PropTypes.bool.isRequired,
key: PropTypes.string.isRequired,
route: navigationRoute.isRequired,
});
/* NavigationSceneRendererProps */
const SceneRendererProps = {
layout: layout.isRequired,
navigationState: navigationState.isRequired,
position: animatedValue.isRequired,
progress: animatedValue.isRequired,
scene: scene.isRequired,
scenes: PropTypes.arrayOf(scene).isRequired,
};
const SceneRenderer = PropTypes.shape(SceneRendererProps);
/* NavigationPanPanHandlers */
const panHandlers = PropTypes.shape({
onMoveShouldSetResponder: PropTypes.func.isRequired,
onMoveShouldSetResponderCapture: PropTypes.func.isRequired,
onResponderEnd: PropTypes.func.isRequired,
onResponderGrant: PropTypes.func.isRequired,
onResponderMove: PropTypes.func.isRequired,
onResponderReject: PropTypes.func.isRequired,
onResponderRelease: PropTypes.func.isRequired,
onResponderStart: PropTypes.func.isRequired,
onResponderTerminate: PropTypes.func.isRequired,
onResponderTerminationRequest: PropTypes.func.isRequired,
onStartShouldSetResponder: PropTypes.func.isRequired,
onStartShouldSetResponderCapture: PropTypes.func.isRequired,
});
/**
* Helper function that extracts the props needed for scene renderer.
*/
function extractSceneRendererProps(
props: NavigationSceneRendererProps,
): NavigationSceneRendererProps {
return {
layout: props.layout,
navigationState: props.navigationState,
position: props.position,
progress: props.progress,
scene: props.scene,
scenes: props.scenes,
};
}
module.exports = {
// helpers
extractSceneRendererProps,
// Bundled propTypes.
SceneRendererProps,
// propTypes
SceneRenderer,
action,
navigationState,
navigationRoute,
panHandlers,
};

View File

@ -1,215 +0,0 @@
/**
* 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 NavigationStateUtils
* @flow
*/
'use strict';
const invariant = require('fbjs/lib/invariant');
import type {
NavigationRoute,
NavigationState
} from 'NavigationTypeDefinition';
/**
* Utilities to perform atomic operation with navigate state and routes.
*
* ```javascript
* const state1 = {key: 'page 1'};
* const state2 = NavigationStateUtils.push(state1, {key: 'page 2'});
* ```
*/
const NavigationStateUtils = {
/**
* Gets a route by key. If the route isn't found, returns `null`.
*/
get(state: NavigationState, key: string): ?NavigationRoute {
return state.routes.find(route => route.key === key) || null;
},
/**
* Returns the first index at which a given route's key can be found in the
* routes of the navigation state, or -1 if it is not present.
*/
indexOf(state: NavigationState, key: string): number {
return state.routes.map(route => route.key).indexOf(key);
},
/**
* Returns `true` at which a given route's key can be found in the
* routes of the navigation state.
*/
has(state: NavigationState, key: string): boolean {
return !!state.routes.some(route => route.key === key);
},
/**
* Pushes a new route into the navigation state.
* Note that this moves the index to the positon to where the last route in the
* stack is at.
*/
push(state: NavigationState, route: NavigationRoute): NavigationState {
invariant(
NavigationStateUtils.indexOf(state, route.key) === -1,
'should not push route with duplicated key %s',
route.key,
);
const routes = state.routes.slice();
routes.push(route);
return {
...state,
index: routes.length - 1,
routes,
};
},
/**
* Pops out a route from the navigation state.
* Note that this moves the index to the positon to where the last route in the
* stack is at.
*/
pop(state: NavigationState): NavigationState {
if (state.index <= 0) {
// [Note]: Over-popping does not throw error. Instead, it will be no-op.
return state;
}
const routes = state.routes.slice(0, -1);
return {
...state,
index: routes.length - 1,
routes,
};
},
/**
* Sets the focused route of the navigation state by index.
*/
jumpToIndex(state: NavigationState, index: number): NavigationState {
if (index === state.index) {
return state;
}
invariant(!!state.routes[index], 'invalid index %s to jump to', index);
return {
...state,
index,
};
},
/**
* Sets the focused route of the navigation state by key.
*/
jumpTo(state: NavigationState, key: string): NavigationState {
const index = NavigationStateUtils.indexOf(state, key);
return NavigationStateUtils.jumpToIndex(state, index);
},
/**
* Sets the focused route to the previous route.
*/
back(state: NavigationState): NavigationState {
const index = state.index - 1;
const route = state.routes[index];
return route ? NavigationStateUtils.jumpToIndex(state, index) : state;
},
/**
* Sets the focused route to the next route.
*/
forward(state: NavigationState): NavigationState {
const index = state.index + 1;
const route = state.routes[index];
return route ? NavigationStateUtils.jumpToIndex(state, index) : state;
},
/**
* Replace a route by a key.
* Note that this moves the index to the positon to where the new route in the
* stack is at.
*/
replaceAt(
state: NavigationState,
key: string,
route: NavigationRoute,
): NavigationState {
const index = NavigationStateUtils.indexOf(state, key);
return NavigationStateUtils.replaceAtIndex(state, index, route);
},
/**
* Replace a route by a index.
* Note that this moves the index to the positon to where the new route in the
* stack is at.
*/
replaceAtIndex(
state: NavigationState,
index: number,
route: NavigationRoute,
): NavigationState {
invariant(
!!state.routes[index],
'invalid index %s for replacing route %s',
index,
route.key,
);
if (state.routes[index] === route) {
return state;
}
const routes = state.routes.slice();
routes[index] = route;
return {
...state,
index,
routes,
};
},
/**
* Resets all routes.
* Note that this moves the index to the positon to where the last route in the
* stack is at if the param `index` isn't provided.
*/
reset(
state: NavigationState,
routes: Array<NavigationRoute>,
index?: number,
): NavigationState {
invariant(
routes.length && Array.isArray(routes),
'invalid routes to replace',
);
const nextIndex: number = index === undefined ? routes.length - 1 : index;
if (state.routes.length === routes.length && state.index === nextIndex) {
const compare = (route, ii) => routes[ii] === route;
if (state.routes.every(compare)) {
return state;
}
}
invariant(!!routes[nextIndex], 'invalid index %s to reset', nextIndex);
return {
...state,
index: nextIndex,
routes,
};
},
};
module.exports = NavigationStateUtils;

View File

@ -1,292 +0,0 @@
/**
* 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 NavigationTransitioner
* @flow
*/
'use strict';
const Animated = require('Animated');
const Easing = require('Easing');
const NavigationPropTypes = require('NavigationPropTypes');
const NavigationScenesReducer = require('NavigationScenesReducer');
const React = require('React');
const StyleSheet = require('StyleSheet');
const View = require('View');
const invariant = require('fbjs/lib/invariant');
import type {
NavigationAnimatedValue,
NavigationLayout,
NavigationScene,
NavigationState,
NavigationTransitionProps,
NavigationTransitionSpec,
} from 'NavigationTypeDefinition';
type Props = {
configureTransition: (
a: NavigationTransitionProps,
b: ?NavigationTransitionProps,
) => NavigationTransitionSpec,
navigationState: NavigationState,
onTransitionEnd: () => void,
onTransitionStart: () => void,
render: (a: NavigationTransitionProps, b: ?NavigationTransitionProps) => any,
style: any,
};
type State = {
layout: NavigationLayout,
position: NavigationAnimatedValue,
progress: NavigationAnimatedValue,
scenes: Array<NavigationScene>,
};
const {PropTypes} = React;
const DefaultTransitionSpec = {
duration: 250,
easing: Easing.inOut(Easing.ease),
timing: Animated.timing,
};
class NavigationTransitioner extends React.Component<any, Props, State> {
_onLayout: (event: any) => void;
_onTransitionEnd: () => void;
_prevTransitionProps: ?NavigationTransitionProps;
_transitionProps: NavigationTransitionProps;
_isMounted: boolean;
props: Props;
state: State;
static propTypes = {
configureTransition: PropTypes.func,
navigationState: NavigationPropTypes.navigationState.isRequired,
onTransitionEnd: PropTypes.func,
onTransitionStart: PropTypes.func,
render: PropTypes.func.isRequired,
};
constructor(props: Props, context: any) {
super(props, context);
// The initial layout isn't measured. Measured layout will be only available
// when the component is mounted.
const layout = {
height: new Animated.Value(0),
initHeight: 0,
initWidth: 0,
isMeasured: false,
width: new Animated.Value(0),
};
this.state = {
layout,
position: new Animated.Value(this.props.navigationState.index),
progress: new Animated.Value(1),
scenes: NavigationScenesReducer([], this.props.navigationState),
};
this._prevTransitionProps = null;
this._transitionProps = buildTransitionProps(props, this.state);
this._isMounted = false;
}
componentWillMount(): void {
this._onLayout = this._onLayout.bind(this);
this._onTransitionEnd = this._onTransitionEnd.bind(this);
}
componentDidMount(): void {
this._isMounted = true;
}
componentWillUnmount(): void {
this._isMounted = false;
}
componentWillReceiveProps(nextProps: Props): void {
const nextScenes = NavigationScenesReducer(
this.state.scenes,
nextProps.navigationState,
this.props.navigationState
);
if (nextScenes === this.state.scenes) {
return;
}
const nextState = {
...this.state,
scenes: nextScenes,
};
const {
position,
progress,
} = nextState;
progress.setValue(0);
this._prevTransitionProps = this._transitionProps;
this._transitionProps = buildTransitionProps(nextProps, nextState);
// get the transition spec.
const transitionUserSpec = nextProps.configureTransition ?
nextProps.configureTransition(
this._transitionProps,
this._prevTransitionProps,
) :
null;
const transitionSpec = {
...DefaultTransitionSpec,
...transitionUserSpec,
};
const {timing} = transitionSpec;
delete transitionSpec.timing;
const animations = [
timing(
progress,
{
...transitionSpec,
toValue: 1,
},
),
];
if (nextProps.navigationState.index !== this.props.navigationState.index) {
animations.push(
timing(
position,
{
...transitionSpec,
toValue: nextProps.navigationState.index,
},
),
);
}
// update scenes and play the transition
this.setState(nextState, () => {
nextProps.onTransitionStart && nextProps.onTransitionStart(
this._transitionProps,
this._prevTransitionProps,
);
Animated.parallel(animations).start(this._onTransitionEnd);
});
}
render(): React.Element<any> {
return (
<View
onLayout={this._onLayout}
style={[styles.main, this.props.style]}>
{this.props.render(this._transitionProps, this._prevTransitionProps)}
</View>
);
}
_onLayout(event: any): void {
const {height, width} = event.nativeEvent.layout;
if (this.state.layout.initWidth === width &&
this.state.layout.initHeight === height) {
return;
}
const layout = {
...this.state.layout,
initHeight: height,
initWidth: width,
isMeasured: true,
};
layout.height.setValue(height);
layout.width.setValue(width);
const nextState = {
...this.state,
layout,
};
this._transitionProps = buildTransitionProps(this.props, nextState);
this.setState(nextState);
}
_onTransitionEnd(): void {
if (!this._isMounted) {
return;
}
const prevTransitionProps = this._prevTransitionProps;
this._prevTransitionProps = null;
const nextState = {
...this.state,
scenes: this.state.scenes.filter(isSceneNotStale),
};
this._transitionProps = buildTransitionProps(this.props, nextState);
this.setState(nextState, () => {
this.props.onTransitionEnd && this.props.onTransitionEnd(
this._transitionProps,
prevTransitionProps,
);
});
}
}
function buildTransitionProps(
props: Props,
state: State,
): NavigationTransitionProps {
const {
navigationState,
} = props;
const {
layout,
position,
progress,
scenes,
} = state;
const scene = scenes.find(isSceneActive);
invariant(scene, 'No active scene when building navigation transition props.');
return {
layout,
navigationState,
position,
progress,
scenes,
scene
};
}
function isSceneNotStale(scene: NavigationScene): boolean {
return !scene.isStale;
}
function isSceneActive(scene: NavigationScene): boolean {
return scene.isActive;
}
const styles = StyleSheet.create({
main: {
flex: 1,
},
});
module.exports = NavigationTransitioner;

View File

@ -1,121 +0,0 @@
/**
* 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');
import type React from 'react';
// Object Instances
export type NavigationAnimatedValue = Animated.Value;
// Value & Structs.
export type NavigationGestureDirection = 'horizontal' | 'vertical';
export type NavigationRoute = {
key: string,
title?: string
};
export type NavigationState = {
index: number,
routes: Array<NavigationRoute>,
};
export type NavigationLayout = {
height: NavigationAnimatedValue,
initHeight: number,
initWidth: number,
isMeasured: boolean,
width: NavigationAnimatedValue,
};
export type NavigationScene = {
index: number,
isActive: boolean,
isStale: boolean,
key: string,
route: NavigationRoute,
};
export type NavigationTransitionProps = {
// The layout of the transitioner of the scenes.
layout: NavigationLayout,
// The navigation state of the transitioner.
navigationState: NavigationState,
// The progressive index of the transitioner's navigation state.
position: NavigationAnimatedValue,
// The value that represents the progress of the transition when navigation
// state changes from one to another. Its numberic value will range from 0
// to 1.
// progress.__getAnimatedValue() < 1 : transtion is happening.
// progress.__getAnimatedValue() == 1 : transtion completes.
progress: NavigationAnimatedValue,
// All the scenes of the transitioner.
scenes: Array<NavigationScene>,
// The active scene, corresponding to the route at
// `navigationState.routes[navigationState.index]`.
scene: NavigationScene,
// The gesture distance for `horizontal` and `vertical` transitions
gestureResponseDistance?: ?number,
};
// Similar to `NavigationTransitionProps`, except that the prop `scene`
// represents the scene for the renderer to render.
export type NavigationSceneRendererProps = NavigationTransitionProps;
export type NavigationPanPanHandlers = {
onMoveShouldSetResponder: Function,
onMoveShouldSetResponderCapture: Function,
onResponderEnd: Function,
onResponderGrant: Function,
onResponderMove: Function,
onResponderReject: Function,
onResponderRelease: Function,
onResponderStart: Function,
onResponderTerminate: Function,
onResponderTerminationRequest: Function,
onStartShouldSetResponder: Function,
onStartShouldSetResponderCapture: Function,
};
export type NavigationTransitionSpec = {
duration?: number,
// An easing function from `Easing`.
easing?: () => any,
// A timing function such as `Animated.timing`.
timing?: (value: NavigationAnimatedValue, config: any) => any,
};
// Functions.
export type NavigationAnimationSetter = (
position: NavigationAnimatedValue,
newState: NavigationState,
lastState: NavigationState,
) => void;
export type NavigationSceneRenderer = (
props: NavigationSceneRendererProps,
) => ?React.Element<any>;
export type NavigationStyleInterpolator = (
props: NavigationSceneRendererProps,
) => Object;

View File

@ -1,210 +0,0 @@
/**
* 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 NavigationScenesReducer
* @flow
*/
'use strict';
const invariant = require('fbjs/lib/invariant');
const shallowEqual = require('fbjs/lib/shallowEqual');
import type {
NavigationRoute,
NavigationScene,
NavigationState,
} from 'NavigationTypeDefinition';
const SCENE_KEY_PREFIX = 'scene_';
/**
* Helper function to compare route keys (e.g. "9", "11").
*/
function compareKey(one: string, two: string): number {
const delta = one.length - two.length;
if (delta > 0) {
return 1;
}
if (delta < 0) {
return -1;
}
return one > two ? 1 : -1;
}
/**
* Helper function to sort scenes based on their index and view key.
*/
function compareScenes(
one: NavigationScene,
two: NavigationScene,
): number {
if (one.index > two.index) {
return 1;
}
if (one.index < two.index) {
return -1;
}
return compareKey(
one.key,
two.key,
);
}
/**
* Whether two routes are the same.
*/
function areScenesShallowEqual(
one: NavigationScene,
two: NavigationScene,
): boolean {
return (
one.key === two.key &&
one.index === two.index &&
one.isStale === two.isStale &&
one.isActive === two.isActive &&
areRoutesShallowEqual(one.route, two.route)
);
}
/**
* Whether two routes are the same.
*/
function areRoutesShallowEqual(
one: ?NavigationRoute,
two: ?NavigationRoute,
): boolean {
if (!one || !two) {
return one === two;
}
if (one.key !== two.key) {
return false;
}
return shallowEqual(one, two);
}
function NavigationScenesReducer(
scenes: Array<NavigationScene>,
nextState: NavigationState,
prevState: ?NavigationState,
): Array<NavigationScene> {
if (prevState === nextState) {
return scenes;
}
const prevScenes: Map<string, NavigationScene> = new Map();
const freshScenes: Map<string, NavigationScene> = new Map();
const staleScenes: Map<string, NavigationScene> = new Map();
// Populate stale scenes from previous scenes marked as stale.
scenes.forEach(scene => {
const {key} = scene;
if (scene.isStale) {
staleScenes.set(key, scene);
}
prevScenes.set(key, scene);
});
const nextKeys = new Set();
nextState.routes.forEach((route, index) => {
const key = SCENE_KEY_PREFIX + route.key;
const scene = {
index,
isActive: false,
isStale: false,
key,
route,
};
invariant(
!nextKeys.has(key),
`navigationState.routes[${index}].key "${key}" conflicts with ` +
'another route!'
);
nextKeys.add(key);
if (staleScenes.has(key)) {
// A previously `stale` scene is now part of the nextState, so we
// revive it by removing it from the stale scene map.
staleScenes.delete(key);
}
freshScenes.set(key, scene);
});
if (prevState) {
// Look at the previous routes and classify any removed scenes as `stale`.
prevState.routes.forEach((route: NavigationRoute, index) => {
const key = SCENE_KEY_PREFIX + route.key;
if (freshScenes.has(key)) {
return;
}
staleScenes.set(key, {
index,
isActive: false,
isStale: true,
key,
route,
});
});
}
const nextScenes = [];
const mergeScene = (nextScene => {
const {key} = nextScene;
const prevScene = prevScenes.has(key) ? prevScenes.get(key) : null;
if (prevScene && areScenesShallowEqual(prevScene, nextScene)) {
// Reuse `prevScene` as `scene` so view can avoid unnecessary re-render.
// This assumes that the scene's navigation state is immutable.
nextScenes.push(prevScene);
} else {
nextScenes.push(nextScene);
}
});
staleScenes.forEach(mergeScene);
freshScenes.forEach(mergeScene);
nextScenes.sort(compareScenes);
let activeScenesCount = 0;
nextScenes.forEach((scene, ii) => {
const isActive = !scene.isStale && scene.index === nextState.index;
if (isActive !== scene.isActive) {
nextScenes[ii] = {
...scene,
isActive,
};
}
if (isActive) {
activeScenesCount++;
}
});
invariant(
activeScenesCount === 1,
'there should always be only one scene active, not %s.',
activeScenesCount,
);
if (nextScenes.length !== scenes.length) {
return nextScenes;
}
if (nextScenes.some(
(scene, index) => !areScenesShallowEqual(scenes[index], scene)
)) {
return nextScenes;
}
// scenes haven't changed.
return scenes;
}
module.exports = NavigationScenesReducer;

View File

@ -1,300 +0,0 @@
/**
* 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.
*
*/
'use strict';
jest.unmock('NavigationScenesReducer');
const NavigationScenesReducer = require('NavigationScenesReducer');
/**
* Simulate scenes transtion with changes of navigation states.
*/
function testTransition(states) {
const routes = states.map(keys => {
return {
index: 0,
routes: keys.map(key => {
return { key };
}),
};
});
let scenes = [];
let prevState = null;
routes.forEach((nextState) => {
scenes = NavigationScenesReducer(scenes, nextState, prevState);
prevState = nextState;
});
return scenes;
}
describe('NavigationScenesReducer', () => {
it('gets initial scenes', () => {
const scenes = testTransition([
['1', '2'],
]);
expect(scenes).toEqual([
{
index: 0,
isActive: true,
isStale: false,
key: 'scene_1',
route: {
key: '1'
},
},
{
index: 1,
isActive: false,
isStale: false,
key: 'scene_2',
route: {
key: '2'
},
},
]);
});
it('pushes new scenes', () => {
// Transition from ['1', '2'] to ['1', '2', '3'].
const scenes = testTransition([
['1', '2'],
['1', '2', '3'],
]);
expect(scenes).toEqual([
{
index: 0,
isActive: true,
isStale: false,
key: 'scene_1',
route: {
key: '1'
},
},
{
index: 1,
isActive: false,
isStale: false,
key: 'scene_2',
route: {
key: '2'
},
},
{
index: 2,
isActive: false,
isStale: false,
key: 'scene_3',
route: {
key: '3'
},
},
]);
});
it('gets active scene when index changes', () => {
const state1 = {
index: 0,
routes: [{key: '1'}, {key: '2'}],
};
const state2 = {
index: 1,
routes: [{key: '1'}, {key: '2'}],
};
const scenes1 = NavigationScenesReducer([], state1, null);
const scenes2 = NavigationScenesReducer(scenes1, state2, state1);
const route = scenes2.find((scene) => scene.isActive).route;
expect(route).toEqual({key: '2'});
});
it('gets same scenes', () => {
const state1 = {
index: 0,
routes: [{key: '1'}, {key: '2'}],
};
const state2 = {
index: 0,
routes: [{key: '1'}, {key: '2'}],
};
const scenes1 = NavigationScenesReducer([], state1, null);
const scenes2 = NavigationScenesReducer(scenes1, state2, state1);
expect(scenes1).toBe(scenes2);
});
it('gets different scenes when keys are different', () => {
const state1 = {
index: 0,
routes: [{key: '1'}, {key: '2'}],
};
const state2 = {
index: 0,
routes: [{key: '2'}, {key: '1'}],
};
const scenes1 = NavigationScenesReducer([], state1, null);
const scenes2 = NavigationScenesReducer(scenes1, state2, state1);
expect(scenes1).not.toBe(scenes2);
});
it('gets different scenes when routes are different', () => {
const state1 = {
index: 0,
routes: [{key: '1', x: 1}, {key: '2', x: 2}],
};
const state2 = {
index: 0,
routes: [{key: '1', x: 3}, {key: '2', x: 4}],
};
const scenes1 = NavigationScenesReducer([], state1, null);
const scenes2 = NavigationScenesReducer(scenes1, state2, state1);
expect(scenes1).not.toBe(scenes2);
});
it('gets different scenes when state index changes', () => {
const state1 = {
index: 0,
routes: [{key: '1', x: 1}, {key: '2', x: 2}],
};
const state2 = {
index: 1,
routes: [{key: '1', x: 1}, {key: '2', x: 2}],
};
const scenes1 = NavigationScenesReducer([], state1, null);
const scenes2 = NavigationScenesReducer(scenes1, state2, state1);
expect(scenes1).not.toBe(scenes2);
});
it('pops scenes', () => {
// Transition from ['1', '2', '3'] to ['1', '2'].
const scenes = testTransition([
['1', '2', '3'],
['1', '2'],
]);
expect(scenes).toEqual([
{
index: 0,
isActive: true,
isStale: false,
key: 'scene_1',
route: {
key: '1'
},
},
{
index: 1,
isActive: false,
isStale: false,
key: 'scene_2',
route: {
key: '2'
},
},
{
index: 2,
isActive: false,
isStale: true,
key: 'scene_3',
route: {
key: '3'
},
},
]);
});
it('replaces scenes', () => {
const scenes = testTransition([
['1', '2'],
['3'],
]);
expect(scenes).toEqual([
{
index: 0,
isActive: false,
isStale: true,
key: 'scene_1',
route: {
key: '1'
},
},
{
index: 0,
isActive: true,
isStale: false,
key: 'scene_3',
route: {
key: '3'
},
},
{
index: 1,
isActive: false,
isStale: true,
key: 'scene_2',
route: {
key: '2'
},
},
]);
});
it('revives scenes', () => {
const scenes = testTransition([
['1', '2'],
['3'],
['2'],
]);
expect(scenes).toEqual([
{
index: 0,
isActive: false,
isStale: true,
key: 'scene_1',
route: {
key: '1'
},
},
{
index: 0,
isActive: true,
isStale: false,
key: 'scene_2',
route: {
key: '2'
},
},
{
index: 0,
isActive: false,
isStale: true,
key: 'scene_3',
route: {
key: '3'
},
},
]);
});
});

View File

@ -1,156 +0,0 @@
/**
* 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.
*/
'use strict';
jest.unmock('NavigationStateUtils');
const NavigationStateUtils = require('NavigationStateUtils');
describe('NavigationStateUtils', () => {
// Getters
it('gets route', () => {
const state = {index: 0, routes: [{key: 'a'}]};
expect(NavigationStateUtils.get(state, 'a')).toEqual({key: 'a'});
expect(NavigationStateUtils.get(state, 'b')).toBe(null);
});
it('gets route index', () => {
const state = {index: 1, routes: [{key: 'a'}, {key: 'b'}]};
expect(NavigationStateUtils.indexOf(state, 'a')).toBe(0);
expect(NavigationStateUtils.indexOf(state, 'b')).toBe(1);
expect(NavigationStateUtils.indexOf(state, 'c')).toBe(-1);
});
it('has a route', () => {
const state = {index: 0, routes: [{key: 'a'}, {key: 'b'}]};
expect(NavigationStateUtils.has(state, 'b')).toBe(true);
expect(NavigationStateUtils.has(state, 'c')).toBe(false);
});
// Push
it('pushes a route', () => {
const state = {index: 0, routes: [{key: 'a'}]};
const newState = {index: 1, routes: [{key: 'a'}, {key: 'b'}]};
expect(NavigationStateUtils.push(state, {key: 'b'})).toEqual(newState);
});
it('does not push duplicated route', () => {
const state = {index: 0, routes: [{key: 'a'}]};
expect(() => NavigationStateUtils.push(state, {key: 'a'})).toThrow();
});
// Pop
it('pops route', () => {
const state = {index: 1, routes: [{key: 'a'}, {key: 'b'}]};
const newState = {index: 0, routes: [{key: 'a'}]};
expect(NavigationStateUtils.pop(state)).toEqual(newState);
});
it('does not pop route if not applicable', () => {
const state = {index: 0, routes: [{key: 'a'}]};
expect(NavigationStateUtils.pop(state)).toBe(state);
});
// Jump
it('jumps to new index', () => {
const state = {index: 0, routes: [{key: 'a'}, {key: 'b'}]};
const newState = {index: 1, routes: [{key: 'a'}, {key: 'b'}]};
expect(NavigationStateUtils.jumpToIndex(state, 0)).toBe(state);
expect(NavigationStateUtils.jumpToIndex(state, 1)).toEqual(newState);
});
it('throws if jumps to invalid index', () => {
const state = {index: 0, routes: [{key: 'a'}, {key: 'b'}]};
expect(() => NavigationStateUtils.jumpToIndex(state, 2)).toThrow();
});
it('jumps to new key', () => {
const state = {index: 0, routes: [{key: 'a'}, {key: 'b'}]};
const newState = {index: 1, routes: [{key: 'a'}, {key: 'b'}]};
expect(NavigationStateUtils.jumpTo(state, 'a')).toBe(state);
expect(NavigationStateUtils.jumpTo(state, 'b')).toEqual(newState);
});
it('throws if jumps to invalid key', () => {
const state = {index: 0, routes: [{key: 'a'}, {key: 'b'}]};
expect(() => NavigationStateUtils.jumpTo(state, 'c')).toThrow();
});
it('move backwards', () => {
const state = {index: 1, routes: [{key: 'a'}, {key: 'b'}]};
const newState = {index: 0, routes: [{key: 'a'}, {key: 'b'}]};
expect(NavigationStateUtils.back(state)).toEqual(newState);
expect(NavigationStateUtils.back(newState)).toBe(newState);
});
it('move forwards', () => {
const state = {index: 0, routes: [{key: 'a'}, {key: 'b'}]};
const newState = {index: 1, routes: [{key: 'a'}, {key: 'b'}]};
expect(NavigationStateUtils.forward(state)).toEqual(newState);
expect(NavigationStateUtils.forward(newState)).toBe(newState);
});
// Replace
it('Replaces by key', () => {
const state = {index: 0, routes: [{key: 'a'}, {key: 'b'}]};
const newState = {index: 1, routes: [{key: 'a'}, {key: 'c'}]};
expect(
NavigationStateUtils.replaceAt(
state,
'b',
{key: 'c'},
)
).toEqual(newState);
});
it('Replaces by index', () => {
const state = {index: 0, routes: [{key: 'a'}, {key: 'b'}]};
const newState = {index: 1, routes: [{key: 'a'}, {key: 'c'}]};
expect(
NavigationStateUtils.replaceAtIndex(
state,
1,
{key: 'c'},
)
).toEqual(newState);
});
// Reset
it('Resets routes', () => {
const state = {index: 0, routes: [{key: 'a'}, {key: 'b'}]};
const newState = {index: 1, routes: [{key: 'x'}, {key: 'y'}]};
expect(
NavigationStateUtils.reset(
state,
[{key: 'x'}, {key: 'y'}],
)
).toEqual(newState);
expect(() => {
NavigationStateUtils.reset(state, []);
}).toThrow();
});
it('Resets routes with index', () => {
const state = {index: 0, routes: [{key: 'a'}, {key: 'b'}]};
const newState = {index: 0, routes: [{key: 'x'}, {key: 'y'}]};
expect(
NavigationStateUtils.reset(
state,
[{key: 'x'}, {key: 'y'}],
0,
)
).toEqual(newState);
expect(() => {
NavigationStateUtils.reset(state, [{key: 'x'}, {key: 'y'}], 100);
}).toThrow();
});
});

View File

@ -95,7 +95,6 @@ const ReactNative = {
get LayoutAnimation() { return require('LayoutAnimation'); },
get Linking() { return require('Linking'); },
get NativeEventEmitter() { return require('NativeEventEmitter'); },
get NavigationExperimental() { return require('NavigationExperimental'); },
get NetInfo() { return require('NetInfo'); },
get PanResponder() { return require('PanResponder'); },
get PermissionsAndroid() { return require('PermissionsAndroid'); },