react-native-modal
Advanced tools
Comparing version 7.1.0-beta.1 to 8.0.0-beta.1
{ | ||
"name": "react-native-modal", | ||
"version": "7.1.0-beta.1", | ||
"version": "8.0.0-beta.1", | ||
"description": "An enhanced React-Native modal", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
@@ -94,3 +94,3 @@ # react-native-modal | ||
state = { | ||
isModalVisible: false | ||
isModalVisible: false, | ||
}; | ||
@@ -125,31 +125,37 @@ | ||
| Name | Type | Default | Description | | ||
| ------------------------------ | ---------------- | -------------- | -------------------------------------------------------------------------------------------- | | ||
| animationIn | string or object | 'slideInUp' | Modal show animation | | ||
| animationInTiming | number | 300 | Timing for the modal show animation (in ms) | | ||
| animationOut | string or object | 'slideOutDown' | Modal hide animation | | ||
| animationOutTiming | number | 300 | Timing for the modal hide animation (in ms) | | ||
| avoidKeyboard | bool | false | Move the modal up if the keyboard is open | | ||
| backdropColor | string | 'black' | The backdrop background color | | ||
| backdropOpacity | number | 0.70 | The backdrop opacity when the modal is visible | | ||
| backdropTransitionInTiming | number | 300 | The backdrop show timing (in ms) | | ||
| backdropTransitionOutTiming | number | 300 | The backdrop hide timing (in ms) | | ||
| children | node | **REQUIRED** | The modal content | | ||
| deviceHeight | number | null | Device height (useful on devices that can hide the navigation bar) | | ||
| deviceWidth | number | null | Device width (useful on devices that can hide the navigation bar) | | ||
| isVisible | bool | **REQUIRED** | Show the modal? | | ||
| onBackButtonPress | func | () => null | Called when the Android back button is pressed | | ||
| onBackdropPress | func | () => null | Called when the backdrop is pressed | | ||
| onModalHide | func | () => null | Called when the modal is completely hidden | | ||
| onModalShow | func | () => null | Called when the modal is completely visible | | ||
| onSwipe | func | null | Called when the `swipeThreshold` has been reached | | ||
| scrollOffset | number | 0 | When > 0, disables swipe-to-close, in order to implement scrollable content | | ||
| scrollOffsetMax | number | 0 | Used to implement overscroll feel when content is scrollable. See `/example` directory | | ||
| scrollTo | func | null | Used to implement scrollable modal. See `/example` directory for reference on how to use it | | ||
| swipeThreshold | number | 100 | Swiping threshold that when reached calls `onSwipe` | | ||
| swipeDirection | string | null | Defines the direction where the modal can be swiped (can be 'up', 'down', 'left, or 'right') | | ||
| useNativeDriver | bool | false | Defines if animations should use native driver | | ||
| hideModalContentWhileAnimating | bool | false | Enhances the performance by hiding the modal content until the animations complete | | ||
| propagateSwipe | bool | false | Allows swipe events to propagate to children components (eg a ScrollView inside a modal) | | ||
| style | any | null | Style applied to the modal | | ||
| Name | Type | Default | Description | | ||
| ------------------------------ | ---------------- | ------------------------- | -------------------------------------------------------------------------------------------- | | ||
| animationIn | string or object | 'slideInUp' | Modal show animation | | ||
| animationInTiming | number | 300 | Timing for the modal show animation (in ms) | | ||
| animationOut | string or object | 'slideOutDown' | Modal hide animation | | ||
| animationOutTiming | number | 300 | Timing for the modal hide animation (in ms) | | ||
| avoidKeyboard | bool | false | Move the modal up if the keyboard is open | | ||
| hasBackdrop | bool | true | Render the backdrop | | ||
| backdropColor | string | 'black' | The backdrop background color | | ||
| backdropOpacity | number | 0.70 | The backdrop opacity when the modal is visible | | ||
| backdropTransitionInTiming | number | 300 | The backdrop show timing (in ms) | | ||
| backdropTransitionOutTiming | number | 300 | The backdrop hide timing (in ms) | | ||
| children | node | **REQUIRED** | The modal content | | ||
| deviceHeight | number | null | Device height (useful on devices that can hide the navigation bar) | | ||
| deviceWidth | number | null | Device width (useful on devices that can hide the navigation bar) | | ||
| isVisible | bool | **REQUIRED** | Show the modal? | | ||
| onBackButtonPress | func | () => null | Called when the Android back button is pressed | | ||
| onBackdropPress | func | () => null | Called when the backdrop is pressed | | ||
| onModalWillHide | func | () => null | Called before the modal hide animation begins | | ||
| onModalHide | func | () => null | Called when the modal is completely hidden | | ||
| onModalWillShow | func | () => null | Called before the modal show animation begins | | ||
| onModalShow | func | () => null | Called when the modal is completely visible | | ||
| onSwipeStart | func | () => null | Called when the swipe action started | | ||
| onSwipeMove | func | (percentageShown) => null | Called on each swipe event | | ||
| onSwipeComplete | func | () => null | Called when the `swipeThreshold` has been reached | | ||
| onSwipeCancel | func | () => null | Called when the `swipeThreshold` has not been reached | | ||
| scrollOffset | number | 0 | When > 0, disables swipe-to-close, in order to implement scrollable content | | ||
| scrollOffsetMax | number | 0 | Used to implement overscroll feel when content is scrollable. See `/example` directory | | ||
| scrollTo | func | null | Used to implement scrollable modal. See `/example` directory for reference on how to use it | | ||
| swipeThreshold | number | 100 | Swiping threshold that when reached calls `onSwipeComplete` | | ||
| swipeDirection | string | null | Defines the direction where the modal can be swiped (can be 'up', 'down', 'left, or 'right') | | ||
| useNativeDriver | bool | false | Defines if animations should use native driver | | ||
| hideModalContentWhileAnimating | bool | false | Enhances the performance by hiding the modal content until the animations complete | | ||
| propagateSwipe | bool | false | Allows swipe events to propagate to children components (eg a ScrollView inside a modal) | | ||
| style | any | null | Style applied to the modal | | ||
@@ -167,3 +173,3 @@ ## Frequently Asked Questions | ||
If you're experiencing this issue, you'll need to install [`react-native-extra-dimensions-android`](https://github.com/Sunhat/react-native-extra-dimensions-android). | ||
Then, provide the real window height (obtained from `react-native-extra-dimensions-android`) to the modal: | ||
Then, provide the real window height (obtained from `react-native-extra-dimensions-android`) to the modal: | ||
@@ -198,4 +204,3 @@ ```javascript | ||
isVisible={this.state.isVisible} | ||
onBackdropPress={() => this.setState({ isVisible: false })} | ||
> | ||
onBackdropPress={() => this.setState({ isVisible: false })}> | ||
<View style={{ flex: 1 }}> | ||
@@ -209,3 +214,3 @@ <Text>I am the modal content!</Text> | ||
The prop `onSwipe` allows you to handle this situation (remember to set `swipeDirection` too!): | ||
The prop `onSwipeComplete` allows you to handle this situation (remember to set `swipeDirection` too!): | ||
@@ -215,5 +220,4 @@ ```javascript | ||
isVisible={this.state.isVisible} | ||
onSwipe={() => this.setState({ isVisible: false })} | ||
swipeDirection="left" | ||
> | ||
onSwipeComplete={() => this.setState({ isVisible: false })} | ||
swipeDirection="left"> | ||
<View style={{ flex: 1 }}> | ||
@@ -225,2 +229,4 @@ <Text>I am the modal content!</Text> | ||
Note that when using `useNativeDriver={true}` the modal won't drag correctly. This is a [known issue](https://github.com/react-native-community/react-native-modal/issues/163#issuecomment-409760695). | ||
### The modal flashes in a weird way when animating | ||
@@ -261,7 +267,6 @@ | ||
The modal style applied by default has a small margin. | ||
If you want the modal to cover the entire screen you can easily override it this way: | ||
If you want the modal to cover the entire screen you can easily override it this way: | ||
```js | ||
<Modal style = {{ margin: 0 }}> | ||
... | ||
</Modal> | ||
<Modal style={{ margin: 0 }}>...</Modal> | ||
``` | ||
@@ -272,8 +277,9 @@ | ||
Enable propagateSwipe to allow your child components to receive swipe events: | ||
```js | ||
<Modal propagateSwipe> | ||
... | ||
</Modal> | ||
<Modal propagateSwipe>...</Modal> | ||
``` | ||
Please notice that this is still a WIP fix and might not fix your issue yet, see [issue #236](https://github.com/react-native-community/react-native-modal/issues/236). | ||
## Available animations | ||
@@ -280,0 +286,0 @@ |
@@ -34,3 +34,6 @@ declare module "react-native-modal" { | ||
onBackdropPress?: () => void; | ||
onSwipe?: () => void; | ||
onSwipeStart?: () => void; | ||
onSwipeMove?: (percentageShown: number) => void; | ||
onSwipeComplete?: () => void; | ||
onSwipeCancel?: () => void; | ||
swipeThreshold?: number; | ||
@@ -37,0 +40,0 @@ style?: StyleProp<ViewStyle>; |
150
src/index.js
@@ -10,3 +10,3 @@ import React, { Component } from "react"; | ||
PanResponder, | ||
Animated | ||
Animated, | ||
} from "react-native"; | ||
@@ -18,3 +18,3 @@ import PropTypes from "prop-types"; | ||
registerAnimation, | ||
createAnimation | ||
createAnimation, | ||
} from "react-native-animatable"; | ||
@@ -44,2 +44,3 @@ import * as ANIMATION_DEFINITIONS from "./animations"; | ||
avoidKeyboard: PropTypes.bool, | ||
hasBackdrop: PropTypes.bool, | ||
backdropColor: PropTypes.string, | ||
@@ -56,6 +57,11 @@ backdropOpacity: PropTypes.number, | ||
onModalShow: PropTypes.func, | ||
onModalWillShow: PropTypes.func, | ||
onModalHide: PropTypes.func, | ||
onModalWillHide: PropTypes.func, | ||
onBackButtonPress: PropTypes.func, | ||
onBackdropPress: PropTypes.func, | ||
onSwipe: PropTypes.func, | ||
onSwipeStart: PropTypes.func, | ||
onSwipeMove: PropTypes.func, | ||
onSwipeComplete: PropTypes.func, | ||
onSwipeCancel: PropTypes.func, | ||
swipeThreshold: PropTypes.number, | ||
@@ -74,5 +80,5 @@ swipeDirection: PropTypes.oneOf(["up", "down", "left", "right"]), | ||
"landscape-left", | ||
"landscape-right" | ||
]) | ||
) | ||
"landscape-right", | ||
]), | ||
), | ||
}; | ||
@@ -86,2 +92,3 @@ | ||
avoidKeyboard: false, | ||
hasBackdrop: true, | ||
backdropColor: "black", | ||
@@ -92,5 +99,7 @@ backdropOpacity: 0.7, | ||
onModalShow: () => null, | ||
onModalWillShow: () => null, | ||
deviceHeight: null, | ||
deviceWidth: null, | ||
onModalHide: () => null, | ||
onModalWillHide: () => null, | ||
isVisible: false, | ||
@@ -106,3 +115,3 @@ hideModalContentWhileAnimating: false, | ||
scrollOffsetMax: 0, | ||
supportedOrientations: ["portrait", "landscape"] | ||
supportedOrientations: ["portrait", "landscape"], | ||
}; | ||
@@ -121,3 +130,3 @@ | ||
isSwipeable: this.props.swipeDirection ? true : false, | ||
pan: null | ||
pan: null, | ||
}; | ||
@@ -139,3 +148,3 @@ | ||
isVisible: true, | ||
showContent: true | ||
showContent: true, | ||
}; | ||
@@ -161,3 +170,3 @@ } | ||
{ opacity: nextProps.backdropOpacity }, | ||
this.props.backdropTransitionInTiming | ||
this.props.backdropTransitionInTiming, | ||
); | ||
@@ -168,2 +177,8 @@ } | ||
componentDidMount() { | ||
// Show deprecation message | ||
if (this.props.onSwipe) { | ||
console.warn( | ||
'`<Modal onSwipe="..." />` is deprecated. Use `<Modal onSwipeComplete="..." />` instead.', | ||
); | ||
} | ||
if (this.state.isVisible) { | ||
@@ -174,3 +189,3 @@ this.open(); | ||
"didUpdateDimensions", | ||
this.handleDimensionsUpdate | ||
this.handleDimensionsUpdate, | ||
); | ||
@@ -182,3 +197,3 @@ } | ||
"didUpdateDimensions", | ||
this.handleDimensionsUpdate | ||
this.handleDimensionsUpdate, | ||
); | ||
@@ -218,13 +233,17 @@ } | ||
// https://github.com/react-native-community/react-native-modal/pull/197 | ||
return ( | ||
Math.abs(gestureState.dx) >= 4 || Math.abs(gestureState.dy) >= 4 | ||
); | ||
const shouldSetPanResponder = | ||
Math.abs(gestureState.dx) >= 4 || Math.abs(gestureState.dy) >= 4; | ||
if (shouldSetPanResponder && this.props.onSwipeStart) { | ||
this.props.onSwipeStart(); | ||
} | ||
return shouldSetPanResponder; | ||
} | ||
}, | ||
onStartShouldSetPanResponder: () => { | ||
if (this.props.scrollTo) { | ||
if (this.props.scrollOffset > 0) { | ||
return false; // user needs to be able to scroll content back up | ||
} | ||
if (this.props.scrollTo && this.props.scrollOffset > 0) { | ||
return false; // user needs to be able to scroll content back up | ||
} | ||
if (this.props.onSwipeStart) { | ||
this.props.onSwipeStart(); | ||
} | ||
return true; | ||
@@ -240,5 +259,8 @@ }, | ||
this.backdropRef.transitionTo({ | ||
opacity: this.props.backdropOpacity * newOpacityFactor | ||
opacity: this.props.backdropOpacity * newOpacityFactor, | ||
}); | ||
animEvt(evt, gestureState); | ||
if (this.props.onSwipeMove) { | ||
this.props.onSwipeMove(newOpacityFactor); | ||
} | ||
} else { | ||
@@ -258,2 +280,8 @@ if (this.props.scrollTo) { | ||
if (accDistance > this.props.swipeThreshold) { | ||
if (this.props.onSwipeComplete) { | ||
this.inSwipeClosingState = true; | ||
this.props.onSwipeComplete(); | ||
return; | ||
} | ||
// Deprecated. Remove later. | ||
if (this.props.onSwipe) { | ||
@@ -266,6 +294,9 @@ this.inSwipeClosingState = true; | ||
//Reset backdrop opacity and modal position | ||
if (this.props.onSwipeCancel) { | ||
this.props.onSwipeCancel(); | ||
} | ||
if (this.backdropRef) { | ||
this.backdropRef.transitionTo( | ||
{ opacity: this.props.backdropOpacity }, | ||
this.props.backdropTransitionInTiming | ||
this.props.backdropTransitionInTiming, | ||
); | ||
@@ -275,3 +306,3 @@ } | ||
toValue: { x: 0, y: 0 }, | ||
bounciness: 0 | ||
bounciness: 0, | ||
}).start(); | ||
@@ -281,6 +312,6 @@ if (this.props.scrollOffset > this.props.scrollOffsetMax) { | ||
y: this.props.scrollOffsetMax, | ||
animated: true | ||
animated: true, | ||
}); | ||
} | ||
} | ||
}, | ||
}); | ||
@@ -364,3 +395,3 @@ }; | ||
{ opacity: this.props.backdropOpacity }, | ||
this.props.backdropTransitionInTiming | ||
this.props.backdropTransitionInTiming, | ||
); | ||
@@ -371,3 +402,3 @@ } | ||
// at the last release position when you try to open it. | ||
// Could certainly be improve - no idea for the moment. | ||
// Could certainly be improved - no idea for the moment. | ||
if (this.state.isSwipeable) { | ||
@@ -378,2 +409,3 @@ this.state.pan.setValue({ x: 0, y: 0 }); | ||
if (this.contentRef) { | ||
this.props.onModalWillShow && this.props.onModalWillShow() | ||
this.contentRef[this.animationIn](this.props.animationInTiming).then( | ||
@@ -387,3 +419,3 @@ () => { | ||
} | ||
} | ||
}, | ||
); | ||
@@ -399,3 +431,3 @@ } | ||
{ opacity: 0 }, | ||
this.props.backdropTransitionOutTiming | ||
this.props.backdropTransitionOutTiming, | ||
); | ||
@@ -420,2 +452,3 @@ } | ||
if (this.contentRef) { | ||
this.props.onModalWillHide && this.props.onModalWillHide() | ||
this.contentRef[animationOut](this.props.animationOutTiming).then(() => { | ||
@@ -428,9 +461,9 @@ this.transitionLock = false; | ||
{ | ||
showContent: false | ||
showContent: false, | ||
}, | ||
() => { | ||
this.setState({ | ||
isVisible: false | ||
isVisible: false, | ||
}); | ||
} | ||
}, | ||
); | ||
@@ -450,2 +483,3 @@ this.props.onModalHide(); | ||
avoidKeyboard, | ||
hasBackdrop, | ||
backdropColor, | ||
@@ -473,3 +507,3 @@ backdropOpacity, | ||
styles.content, | ||
style | ||
style, | ||
]; | ||
@@ -481,3 +515,10 @@ | ||
panHandlers = { ...this.panResponder.panHandlers }; | ||
panPosition = this.state.pan.getLayout(); | ||
if (useNativeDriver) { | ||
panPosition = { | ||
transform: this.state.pan.getTranslateTransform() | ||
}; | ||
} else { | ||
panPosition = this.state.pan.getLayout(); | ||
} | ||
} | ||
@@ -500,4 +541,3 @@ | ||
useNativeDriver={useNativeDriver} | ||
{...otherProps} | ||
> | ||
{...otherProps}> | ||
{_children} | ||
@@ -513,20 +553,21 @@ </View> | ||
onRequestClose={onBackButtonPress} | ||
{...otherProps} | ||
> | ||
<TouchableWithoutFeedback onPress={onBackdropPress}> | ||
<View | ||
ref={ref => (this.backdropRef = ref)} | ||
useNativeDriver={useNativeDriver} | ||
style={[ | ||
styles.backdrop, | ||
{ | ||
backgroundColor: this.state.showContent | ||
? backdropColor | ||
: "transparent", | ||
width: deviceWidth, | ||
height: deviceHeight | ||
} | ||
]} | ||
/> | ||
</TouchableWithoutFeedback> | ||
{...otherProps}> | ||
{hasBackdrop && | ||
<TouchableWithoutFeedback onPress={onBackdropPress}> | ||
<View | ||
ref={ref => (this.backdropRef = ref)} | ||
useNativeDriver={true} | ||
style={[ | ||
styles.backdrop, | ||
{ | ||
backgroundColor: this.state.showContent | ||
? backdropColor | ||
: "transparent", | ||
width: deviceWidth, | ||
height: deviceHeight, | ||
}, | ||
]} | ||
/> | ||
</TouchableWithoutFeedback> | ||
} | ||
@@ -537,4 +578,3 @@ {avoidKeyboard && ( | ||
pointerEvents="box-none" | ||
style={computedStyle.concat([{ margin: 0 }])} | ||
> | ||
style={computedStyle.concat([{ margin: 0 }])}> | ||
{containerView} | ||
@@ -541,0 +581,0 @@ </KeyboardAvoidingView> |
39178
9
620
285