mirror of
https://github.com/zhigang1992/react-native-popover-view.git
synced 2026-01-12 17:42:41 +08:00
Full support for React Navigation v2
This commit is contained in:
46
README.md
46
README.md
@@ -35,9 +35,15 @@ The `<Popover>` is able to handle dynamic content and adapt to screen size chang
|
||||
* Moves to stay visible on orientation change or when entering split-screen mode
|
||||
* (Optional) Integration with [React Navigation](https://reactnavigation.org)
|
||||
|
||||
## <a name="upgrading" />Upgrading from 0.5.x
|
||||
## <a name="upgrading" />Upgrading
|
||||
|
||||
Version 0.6 brought some large changes, increasing efficiency, stability, and flexibility. For React Navigation users, there is a new prop, `showInPopover`, that you might want to pass to `PopoverStackNavigator` if you want to customize when to show stack views in a Popover. This replaces `PopoverNavigation.shouldShowInPopover`. See the new [setup](#setup) instructions below for details.
|
||||
### `0.7` to `1.0`
|
||||
|
||||
The only breaking change in version 1.0 was renaming `PopoverStackNavigator` to `createPopoverStackNavigator`, to match the `react-navigation` other navigation functions.
|
||||
|
||||
### `0.5` to `0.6`
|
||||
|
||||
Version 0.6 brought some large changes, increasing efficiency, stability, and flexibility. For React Navigation users, there is a new prop, `showInPopover`, that you might want to pass to `createPopoverStackNavigator` if you want to customize when to show stack views in a Popover. This replaces `PopoverNavigation.shouldShowInPopover`. See the new [setup](#setup) instructions below for details.
|
||||
|
||||
## <a name="demo"/>Demo App
|
||||
|
||||
@@ -171,9 +177,9 @@ This can also be integrated with react-navigation's StackNavigator, so that on t
|
||||
### <a name="setup"/>Basic Setup
|
||||
|
||||
|
||||
#### 1) Change `StackNavigator` to `PopoverStackNavigator`
|
||||
#### 1) Change `StackNavigator` to `createPopoverStackNavigator`
|
||||
|
||||
`PopoverStackNavigator` is a drop-in replacement for react-navigation's `StackNavigator`. It assumes the first view in your `RouteConfigs` is the base view, and every other view should be shown in a Popover when the `showInPopover` prop is `true` (see step #2).
|
||||
`createPopoverStackNavigator` is a drop-in replacement for react-navigation's `StackNavigator`. It assumes the first view in your `RouteConfigs` is the base view, and every other view should be shown in a Popover when the `showInPopover` prop is `true` (see step #2).
|
||||
You can pass a few (optional) per-screen options through your `RouteConfigs` or globally through your `StackNavigatorConfig`:
|
||||
|
||||
Option | Type | Default | Description
|
||||
@@ -189,9 +195,9 @@ Note: If you pass a value through the `StackNavigatorConfig`, and pass the same
|
||||
|
||||
Example:
|
||||
```jsx
|
||||
import Popover, { PopoverStackNavigator } from 'react-native-popover-view';
|
||||
import Popover, { createPopoverStackNavigator } from 'react-native-popover-view';
|
||||
|
||||
let stack = PopoverStackNavigator({
|
||||
let stack = createPopoverStackNavigator({
|
||||
BaseView: {
|
||||
screen: BaseView,
|
||||
navigationOptions: {
|
||||
@@ -225,10 +231,10 @@ let stack = PopoverStackNavigator({
|
||||
|
||||
#### 2) Define when Popover should be shown
|
||||
|
||||
By default, views will be shown in a Popover view on tablets, and normally on phones. To override this behavior, you can pass the `showInPopover` prop to the class returned by `PopoverStackNavigator`:
|
||||
By default, views will be shown in a Popover view on tablets, and normally on phones. To override this behavior, you can pass the `showInPopover` prop to the class returned by `createPopoverStackNavigator`:
|
||||
|
||||
```jsx
|
||||
let Stack = PopoverStackNavigator(...);
|
||||
let Stack = createPopoverStackNavigator(...);
|
||||
...
|
||||
render() {
|
||||
let smallScreen = this.props.width < 500;
|
||||
@@ -248,13 +254,13 @@ You can register the button as the source of the `Popover` for a particular rout
|
||||
|
||||
We first register the ref for a view:
|
||||
```jsx
|
||||
<TouchableHighlight ref={ref => PopoverStackNavigator.registerRefForView(ref, 'View1')} {...otherProps} />
|
||||
<TouchableHighlight ref={ref => createPopoverStackNavigator.registerRefForView(ref, 'View1')} {...otherProps} />
|
||||
```
|
||||
Then, if `View1` is a route name in a `PopoverStackNavigator`...
|
||||
Then, if `View1` is a route name in a `createPopoverStackNavigator`...
|
||||
```jsx
|
||||
import View1 from './views/View1';
|
||||
...
|
||||
let stack = PopoverStackNavigator({
|
||||
let stack = createPopoverStackNavigator({
|
||||
View1: {
|
||||
screen: View1,
|
||||
navigationOptions: navOptions
|
||||
@@ -269,7 +275,7 @@ this.props.navigation.navigate('View1', params);
|
||||
|
||||
You can register any type of view, not only a `TouchableHighlight`, and the `Popover` will point to the outside of the bounds of that view.
|
||||
|
||||
Note: The map is stored statically, so you cannot register two views with the same name, even if they are in different `PopoverStackNavigator`'s.
|
||||
Note: The map is stored statically, so you cannot register two views with the same name, even if they are in different `createPopoverStackNavigator`'s.
|
||||
|
||||
##### II. Send showFromView
|
||||
|
||||
@@ -287,7 +293,7 @@ See "Show Popover from custom rect" in the Advanced Usage section below.
|
||||
|
||||
```jsx
|
||||
import React, { Component } from 'react';
|
||||
import { PopoverStackNavigator } from 'react-native-popover-view';
|
||||
import { createPopoverStackNavigator } from 'react-native-popover-view';
|
||||
import { MoreHeaderView, ExtraInfoView, MoreOptionView } from './myOtherViews';
|
||||
import { Colors } from './Colors';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
@@ -300,19 +306,19 @@ class MoreView extends Component {
|
||||
<View>
|
||||
<TouchableHighlight
|
||||
style={styles.buttonStyle}
|
||||
ref={touchable => PopoverStackNavigator.registerRefForView(touchable, 'About')}
|
||||
ref={touchable => createPopoverStackNavigator.registerRefForView(touchable, 'About')}
|
||||
onPress={() => this.props.navigation.navigate('About')}>
|
||||
<Text>About the App</Text>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
style={styles.buttonStyle}
|
||||
ref={touchable => PopoverStackNavigator.registerRefForView(touchable, 'Settings')}
|
||||
ref={touchable => createPopoverStackNavigator.registerRefForView(touchable, 'Settings')}
|
||||
onPress={() => this.props.navigation.navigate('Settings')}>
|
||||
<Text>Content Settings</Text>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
style={styles.buttonStyle}
|
||||
ref={touchable => PopoverStackNavigator.registerRefForView(touchable, 'Account')}
|
||||
ref={touchable => createPopoverStackNavigator.registerRefForView(touchable, 'Account')}
|
||||
onPress={() => this.props.navigation.navigate('Account')}>
|
||||
<Text>Account Details</Text>
|
||||
</TouchableHighlight>
|
||||
@@ -323,7 +329,7 @@ class MoreView extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
let MoreStack = PopoverStackNavigator({
|
||||
let MoreStack = createPopoverStackNavigator({
|
||||
MoreView: {
|
||||
screen: MoreView,
|
||||
navigationOptions: {title: 'More'}
|
||||
@@ -388,10 +394,10 @@ let styles = {
|
||||
|
||||
#### Custumize Display Area used by Popovers
|
||||
|
||||
By default, Popover's will query RN's `SafeAreaView` to get the allowed display area on the device, and then add a 10pt padding around all the edges, and set this as the display area. If you want to inject a custum display area to a specific popover, you can do so either through the `PopoverStackNavigator`'s `RouteConfigs` or through params in the `navigate` call:
|
||||
By default, Popover's will query RN's `SafeAreaView` to get the allowed display area on the device, and then add a 10pt padding around all the edges, and set this as the display area. If you want to inject a custum display area to a specific popover, you can do so either through the `createPopoverStackNavigator`'s `RouteConfigs` or through params in the `navigate` call:
|
||||
|
||||
```jsx
|
||||
let Stack = PopoverStackNavigator({
|
||||
let Stack = createPopoverStackNavigator({
|
||||
View1: {
|
||||
screen: 'View1',
|
||||
popoverOptions: {
|
||||
@@ -409,7 +415,7 @@ this.props.navigation.navigate('View1', {displayArea: new Rect(0, 0, 50,50)});
|
||||
|
||||
#### Show Popover from custom rect
|
||||
|
||||
There may be situations in which you want to show a `Popover` with a custom fromRect, not tied to any view. Instead of using `PopoverStackNavigator.registerRefForView`, you can pass in a custom `fromRect` as params to the `navigate()` call. For example:
|
||||
There may be situations in which you want to show a `Popover` with a custom fromRect, not tied to any view. Instead of using `createPopoverStackNavigator.registerRefForView`, you can pass in a custom `fromRect` as params to the `navigate()` call. For example:
|
||||
```jsx
|
||||
import { Rect } from 'react-native-popover-view';
|
||||
...
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-popover-view",
|
||||
"version": "0.7.1",
|
||||
"version": "1.0.0",
|
||||
"description": "A <Popover /> component for react-native",
|
||||
"main": "src/index.js",
|
||||
"author": "Peter Steffey <steffeydev@icloud.com> (https://github.com/steffeydev)",
|
||||
|
||||
@@ -19,7 +19,7 @@ export default class PopoverNavigation extends Component {
|
||||
}
|
||||
|
||||
goBack() {
|
||||
if (this.props.showInPopover())
|
||||
if (this.props.showInPopover)
|
||||
this.setState({visible: false});
|
||||
else
|
||||
this.props.children.props.navigation.goBack();
|
||||
@@ -43,7 +43,7 @@ export default class PopoverNavigation extends Component {
|
||||
const child = React.cloneElement(this.props.children, {goBack: () => this.goBack()});
|
||||
const { contentContainerStyle, popoverStyle, arrowStyle, placement, showInModal, layoutRtl, showBackground, getRegisteredView, displayArea, showInPopover, backgroundColor } = this.props;
|
||||
|
||||
if (showInPopover()) {
|
||||
if (showInPopover) {
|
||||
return (
|
||||
<Popover
|
||||
arrowStyle={arrowStyle}
|
||||
@@ -72,7 +72,7 @@ export default class PopoverNavigation extends Component {
|
||||
|
||||
PopoverNavigation.defaultProps = {
|
||||
contentContainerStyle: {width: 380},
|
||||
showInPopover: () => true
|
||||
showInPopover: true
|
||||
}
|
||||
|
||||
PopoverNavigation.propTypes = {
|
||||
@@ -83,7 +83,7 @@ PopoverNavigation.propTypes = {
|
||||
showBackground: PropTypes.bool,
|
||||
contentContainerStyle: PropTypes.object,
|
||||
displayArea: PropTypes.objectOf(PropTypes.number),
|
||||
showInPopover: PropTypes.func,
|
||||
showInPopover: PropTypes.bool,
|
||||
popoverStyle: PropTypes.object,
|
||||
arrowStyle: PropTypes.object
|
||||
}
|
||||
|
||||
@@ -1,32 +1,109 @@
|
||||
/* @flow */
|
||||
|
||||
// React Imports
|
||||
import * as React from 'react';
|
||||
import createNavigationContainer from '../../react-navigation/src/createNavigationContainer';
|
||||
import createNavigator from '../../react-navigation/src/navigators/createNavigator';
|
||||
import CardStackTransitioner from '.../../react-navigation/src/views/CardStack/CardStackTransitioner';
|
||||
import StackRouter from '../../react-navigation/src/routers/StackRouter';
|
||||
import NavigationActions from '../../react-navigation/src/NavigationActions';
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
// Popover Imports
|
||||
import { popoverTransitionConfig, isTablet } from './Utility'
|
||||
import PopoverNavigation from './PopoverNavigation'
|
||||
|
||||
export let withPopoverNavigation = (Comp, popoverOptions) => props => <PopoverNavigation {...popoverOptions}><Comp {...props} /></PopoverNavigation>;
|
||||
// RN Imports
|
||||
import createNavigationContainer from '../../react-navigation/src/createNavigationContainer';
|
||||
import StackRouter from '../../react-navigation/src/routers/StackRouter';
|
||||
import createKeyboardAwareNavigator from '../../react-navigation/src/navigators/createKeyboardAwareNavigator';
|
||||
import createNavigator from '../../react-navigation/src/navigators/createNavigator';
|
||||
import StackView from '../../react-navigation/src/views/StackView/StackView';
|
||||
import StackViewLayout from '../../react-navigation/src/views/StackView/StackViewLayout';
|
||||
import Transitioner from '../../react-navigation/src/views/Transitioner';
|
||||
import NavigationActions from '../../react-navigation/src/NavigationActions';
|
||||
import StackActions from '../../react-navigation/src/routers/StackActions';
|
||||
import TransitionConfigs from '../../react-navigation/src/views/StackView/StackViewTransitionConfigs';
|
||||
|
||||
const PopoverStackNavigator = (routeConfigMap, stackConfig = {}) => {
|
||||
export let withPopoverNavigation = (Comp, popoverOptions) => props => <PopoverNavigation {...popoverOptions} showInPopover={popoverOptions.isFirstView ? false : props.screenProps.showInPopover}><Comp {...props} /></PopoverNavigation>;
|
||||
|
||||
const NativeAnimatedModule =
|
||||
NativeModules && NativeModules.NativeAnimatedModule;
|
||||
|
||||
class PopoverStackView extends React.Component {
|
||||
static defaultProps = {
|
||||
navigationConfig: {
|
||||
mode: 'card',
|
||||
},
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Transitioner
|
||||
render={this._render}
|
||||
configureTransition={this._configureTransition}
|
||||
navigation={this.props.navigation}
|
||||
descriptors={this.props.descriptors}
|
||||
onTransitionStart={this.props.onTransitionStart}
|
||||
onTransitionEnd={(transition, lastTransition) => {
|
||||
const { onTransitionEnd, navigation } = this.props;
|
||||
if (transition.navigation.state.isTransitioning) {
|
||||
navigation.dispatch(
|
||||
StackActions.completeTransition({
|
||||
key: navigation.state.key,
|
||||
})
|
||||
);
|
||||
}
|
||||
onTransitionEnd && onTransitionEnd(transition, lastTransition);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_shouldShowInPopover = () => this.props.screenProps && this.props.screenProps.showInPopover !== undefined
|
||||
? this.props.screenProps.showInPopover
|
||||
: isTablet()
|
||||
|
||||
_configureTransition = (transitionProps, prevTransitionProps) => {
|
||||
return {
|
||||
...TransitionConfigs.getTransitionConfig(
|
||||
this._shouldShowInPopover() ? popoverTransitionConfig : this.props.navigationConfig.transitionConfig,
|
||||
transitionProps,
|
||||
prevTransitionProps,
|
||||
this.props.navigationConfig.mode === 'modal'
|
||||
).transitionSpec,
|
||||
useNativeDriver: !!NativeAnimatedModule,
|
||||
};
|
||||
};
|
||||
|
||||
_render = (transitionProps, lastTransitionProps) => {
|
||||
const { screenProps, navigationConfig } = this.props;
|
||||
const { headerMode, cardStyle, transitionConfig, ...remainingNavigationConfig } = navigationConfig;
|
||||
return (
|
||||
<StackViewLayout
|
||||
{...remainingNavigationConfig}
|
||||
headerMode={this._shouldShowInPopover() ? 'screen' : headerMode}
|
||||
cardStyle={this._shouldShowInPopover() ? {...cardStyle, backgroundColor: 'transparent'} : cardStyle}
|
||||
transitionConfig={this._shouldShowInPopover() ? popoverTransitionConfig : transitionConfig}
|
||||
onGestureBegin={this.props.onGestureBegin}
|
||||
onGestureCanceled={this.props.onGestureCanceled}
|
||||
onGestureEnd={this.props.onGestureEnd}
|
||||
screenProps={screenProps}
|
||||
descriptors={this.props.descriptors}
|
||||
transitionProps={transitionProps}
|
||||
lastTransitionProps={lastTransitionProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
function createPopoverStackNavigator(routeConfigMap, stackConfig = {}) {
|
||||
const {
|
||||
initialRouteKey,
|
||||
initialRouteName,
|
||||
initialRouteParams,
|
||||
paths,
|
||||
headerMode,
|
||||
headerTransitionPreset,
|
||||
mode,
|
||||
cardStyle,
|
||||
transitionConfig,
|
||||
onTransitionStart,
|
||||
onTransitionEnd,
|
||||
navigationOptions,
|
||||
disableKeyboardHandling,
|
||||
} = stackConfig;
|
||||
|
||||
const stackRouterConfig = {
|
||||
initialRouteKey,
|
||||
initialRouteName,
|
||||
initialRouteParams,
|
||||
paths,
|
||||
@@ -39,9 +116,9 @@ const PopoverStackNavigator = (routeConfigMap, stackConfig = {}) => {
|
||||
width: 0,
|
||||
height: 0
|
||||
};
|
||||
let shouldShowInPopover = isTablet();
|
||||
|
||||
routeKeys.forEach((route, i) => {
|
||||
let getRegisteredView = () => PopoverStackNavigator.registeredViews.hasOwnProperty(route) ? PopoverStackNavigator.registeredViews[route] : null;
|
||||
let getRegisteredView = () => createPopoverStackNavigator.registeredViews.hasOwnProperty(route) ? createPopoverStackNavigator.registeredViews[route] : null;
|
||||
newRouteConfigMap[route] = Object.assign({},
|
||||
routeConfigMap[route],
|
||||
{
|
||||
@@ -49,7 +126,7 @@ const PopoverStackNavigator = (routeConfigMap, stackConfig = {}) => {
|
||||
stackConfig.popoverOptions,
|
||||
routeConfigMap[route].popoverOptions,
|
||||
{
|
||||
showInPopover: i > 0 ? () => shouldShowInPopover : () => false,
|
||||
isFirstView: i === 0,
|
||||
getRegisteredView,
|
||||
backgroundColor: stackConfig.cardStyle ? stackConfig.cardStyle.backgroundColor : (i > 0 ? 'white' : '#E9E9EF')
|
||||
}
|
||||
@@ -63,7 +140,7 @@ const PopoverStackNavigator = (routeConfigMap, stackConfig = {}) => {
|
||||
else if (typeof userNavigationOptions === "object")
|
||||
additionalNavigationOptions = userNavigationOptions;
|
||||
}
|
||||
return i > 0 && shouldShowInPopover
|
||||
return i > 0 && ops.screenProps && ops.screenProps.showInPopover
|
||||
? Object.assign({}, additionalNavigationOptions, {header: null})
|
||||
: additionalNavigationOptions;
|
||||
}
|
||||
@@ -73,40 +150,20 @@ const PopoverStackNavigator = (routeConfigMap, stackConfig = {}) => {
|
||||
|
||||
const router = StackRouter(newRouteConfigMap, stackRouterConfig);
|
||||
|
||||
// Create a navigator with CardStackTransitioner as the view
|
||||
const navigator = createNavigator(
|
||||
router,
|
||||
routeConfigMap,
|
||||
stackConfig
|
||||
)(props => {
|
||||
const { showInPopover, screenProps, ...otherProps } = props;
|
||||
shouldShowInPopover = showInPopover !== undefined ? showInPopover : isTablet();
|
||||
// Create a navigator with StackView as the view
|
||||
let Navigator = createNavigator(PopoverStackView, router, stackConfig);
|
||||
if (!disableKeyboardHandling) {
|
||||
Navigator = createKeyboardAwareNavigator(Navigator);
|
||||
}
|
||||
|
||||
return (
|
||||
<CardStackTransitioner
|
||||
{...otherProps}
|
||||
screenProps={{shouldShowInPopover, ...screenProps}}
|
||||
headerMode={shouldShowInPopover ? 'screen' : headerMode}
|
||||
headerTransitionPreset={headerTransitionPreset}
|
||||
mode={mode}
|
||||
cardStyle={shouldShowInPopover ? {...cardStyle, backgroundColor: 'transparent'} : cardStyle}
|
||||
transitionConfig={shouldShowInPopover ? popoverTransitionConfig : transitionConfig}
|
||||
onTransitionStart={onTransitionStart}
|
||||
onTransitionEnd={(lastTransition, transition) => {
|
||||
const { state, dispatch } = props.navigation;
|
||||
dispatch(NavigationActions.completeTransition({ key: state.key }));
|
||||
onTransitionEnd && onTransitionEnd();
|
||||
}}
|
||||
/>
|
||||
)
|
||||
});
|
||||
|
||||
return createNavigationContainer(navigator);
|
||||
};
|
||||
|
||||
PopoverStackNavigator.registeredViews = {};
|
||||
PopoverStackNavigator.registerRefForView = (ref, view) => {
|
||||
PopoverStackNavigator.registeredViews[view] = ref;
|
||||
// HOC to provide the navigation prop for the top-level navigator (when the prop is missing)
|
||||
return createNavigationContainer(Navigator);
|
||||
}
|
||||
|
||||
export default PopoverStackNavigator;
|
||||
createPopoverStackNavigator.registeredViews = {};
|
||||
createPopoverStackNavigator.registerRefForView = (ref, view) => {
|
||||
createPopoverStackNavigator.registeredViews[view] = ref;
|
||||
}
|
||||
|
||||
export default createPopoverStackNavigator;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export { default as default } from './Popover.js'
|
||||
export { default as PopoverNavigation } from './PopoverNavigation.js'
|
||||
export { default as PopoverStackNavigator, withPopoverNavigation as withPopoverNavigation } from './PopoverStackNavigator.js'
|
||||
export { default as createPopoverStackNavigator, withPopoverNavigation as withPopoverNavigation } from './PopoverStackNavigator.js'
|
||||
export { Rect as Rect, Size as Size, popoverTransitionConfig as popoverTransitionConfig } from './Utility.js'
|
||||
|
||||
Reference in New Issue
Block a user