react-native-modal
Advanced tools
Comparing version 4.1.1 to 5.0.0-0
{ | ||
"name": "react-native-modal", | ||
"version": "4.1.1", | ||
"version": "5.0.0-0", | ||
"description": "An enhanced React-Native modal", | ||
@@ -11,3 +11,3 @@ "main": "src/index.js", | ||
"test": "npm run lint", | ||
"prettier": "prettier --write --print-width 100 --single-quote --trailing-comma all src/**/*.js", | ||
"prettier": "prettier --write src/**/*.js", | ||
"reset": "watchman watch-del-all && rm -rf node_modules/ && npm cache clean && npm prune && yarn cache clean" | ||
@@ -27,3 +27,3 @@ }, | ||
"author": "Mazzarolo Matteo", | ||
"license": "ISC", | ||
"license": "MIT", | ||
"homepage": "https://github.com/react-native-community/react-native-modal", | ||
@@ -35,12 +35,12 @@ "repository": { | ||
"dependencies": { | ||
"prop-types": "15.5.10", | ||
"react-native-animatable": "^1.2.3" | ||
"prop-types": "15.6.0", | ||
"react-native-animatable": "^1.2.4" | ||
}, | ||
"devDependencies": { | ||
"babel-eslint": "^7.2.3", | ||
"eslint": "4.5.0", | ||
"babel-eslint": "^8.1.2", | ||
"eslint": "4.14.0", | ||
"eslint-plugin-react-app": "^1.0.2", | ||
"husky": "0.14.3", | ||
"lint-staged": "4.0.4", | ||
"prettier": "1.6.1", | ||
"lint-staged": "6.0.0", | ||
"prettier": "1.9.2", | ||
"react": ">=15.0.0", | ||
@@ -51,4 +51,4 @@ "react-native": ">=0.24.0" | ||
"*.js": [ | ||
"npm run test", | ||
"npm run prettier", | ||
"lint", | ||
"prettier --write", | ||
"git add" | ||
@@ -55,0 +55,0 @@ ] |
171
README.md
@@ -6,11 +6,12 @@ # react-native-modal | ||
An enhanced, animated and customizable react-native modal. | ||
An enhanced, animated and customizable react-native modal. | ||
## Features | ||
- Smooth enter/exit animations | ||
- Plain simple and flexible APIs | ||
- Customizable backdrop opacity, color and timing | ||
- Listeners for the modal animations ending | ||
- Resize itself correctly on device rotation | ||
* Smooth enter/exit animations | ||
* Plain simple and flexible APIs | ||
* Customizable backdrop opacity, color and timing | ||
* Listeners for the modal animations ending | ||
* Resize itself correctly on device rotation | ||
* Swipeable | ||
@@ -31,20 +32,68 @@ ## Demo | ||
Since react-native-modal is an extension of the original react native modal, it works in a similar fashion [react-native original modal](https://facebook.github.io/react-native/docs/modal.html). | ||
1. Import react-native-modal: | ||
```javascript | ||
import React, { Component } from 'react' | ||
import { Text, TouchableOpacity, View } from 'react-native' | ||
import Modal from 'react-native-modal' | ||
import Modal from "react-native-modal"; | ||
``` | ||
2. Create a modal and nest its content inside of it: | ||
```javascript | ||
render () { | ||
return ( | ||
<View> | ||
<Modal> | ||
<View style={{ flex: 1 }}> | ||
<Text>I am the modal content!</Text> | ||
</View> | ||
</Modal> | ||
</View> | ||
) | ||
} | ||
``` | ||
3. Then simply show it by setting the `isVisible` prop to true: | ||
```javascript | ||
render () { | ||
return ( | ||
<View> | ||
<Modal isVisible={true}> | ||
<View style={{ flex: 1 }}> | ||
<Text>I am the modal content!</Text> | ||
</View> | ||
</Modal> | ||
</View> | ||
) | ||
} | ||
``` | ||
The `isVisible` prop is the only prop you'll really need to make the modal work: you should control this prop value by saving it in your state and setting it to `true` or `false` when needed. | ||
## A complete example | ||
The following example consists in a component (`ModalTester`) with a button and a modal. | ||
The modal is controlled by the `isModalVisible` state variable and it is initially hidden, since its value is `false`. | ||
Pressing the button sets `isModalVisible` to true, making the modal visible. | ||
Inside the modal there is another button that, when pressed, sets `isModalVisible` to false, hiding the modal. | ||
```javascript | ||
import React, { Component } from "react"; | ||
import { Text, TouchableOpacity, View } from "react-native"; | ||
import Modal from "react-native-modal"; | ||
export default class ModalTester extends Component { | ||
state = { | ||
isModalVisible: false | ||
} | ||
}; | ||
_showModal = () => this.setState({ isModalVisible: true }) | ||
_toggleModal = () => | ||
this.setState({ isModalVisible: !this.state.isModalVisible }); | ||
_hideModal = () => this.setState({ isModalVisible: false }) | ||
render () { | ||
render() { | ||
return ( | ||
<View style={{ flex: 1 }}> | ||
<TouchableOpacity onPress={this._showModal}> | ||
<TouchableOpacity onPress={this._toggleModal}> | ||
<Text>Show Modal</Text> | ||
@@ -55,10 +104,13 @@ </TouchableOpacity> | ||
<Text>Hello!</Text> | ||
<TouchableOpacity onPress={this._toggleModal}> | ||
<Text>Hide me!</Text> | ||
</TouchableOpacity> | ||
</View> | ||
</Modal> | ||
</View> | ||
) | ||
); | ||
} | ||
} | ||
``` | ||
For a more complex example take a look at the `/example` directory. | ||
@@ -68,21 +120,24 @@ | ||
| 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) | | ||
| onBackButtonPress | func | () => null | Called when the Android back button is pressed | | ||
| onBackdropPress | func | () => null | Called when the backdrop is pressed | | ||
| useNativeDriver | bool | false | Define if animations should use [native driver](https://facebook.github.io/react-native/docs/animated.html#using-the-native-driver) | | ||
| isVisible | bool | **REQUIRED** | Show the modal? | | ||
| children | node | **REQUIRED** | The modal content | | ||
| onModalShow | func | () => null | Called when the modal is completely visible | | ||
| onModalHide | func | () => null | Called when the modal is completely hidden | | ||
| 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 | | ||
| 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 | | ||
| 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 | e | | ||
| 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 | Define if animations should use [native driver](https://facebook.github.io/react-native/docs/animated.html#using-the-native-driver) | | ||
| style | any | null | Style applied to the modal | | ||
@@ -92,11 +147,47 @@ ## Frequently Asked Questions | ||
### The component is not working as expected | ||
Under the hood `react-native-modal` uses react-native original [Modal component](https://facebook.github.io/react-native/docs/modal.html). | ||
Before reporting a bug, try swapping `react-native-modal` with react-native original Modal component and, if the issue persists, check if it has already been reported as a [react-native issue](https://github.com/facebook/react-native/issues). | ||
Before reporting a bug, try swapping `react-native-modal` with react-native original Modal component and, if the issue persists, check if it has already been reported as a [react-native issue](https://github.com/facebook/react-native/issues). | ||
## Avaliable animations | ||
### How can I hide the modal by pressing outside of its content? | ||
The prop `onBackdropPress` allows you to handle this situation: | ||
```javascript | ||
<Modal | ||
isVisible={this.state.isVisible} | ||
onBackdropPress={() => this.setState({ isVisible: false })} | ||
> | ||
<View style={{ flex: 1 }}> | ||
<Text>I am the modal content!</Text> | ||
</View> | ||
</Modal> | ||
``` | ||
### How can I hide the modal by swiping it? | ||
The prop `onSwipe` allows you to handle this situation (remember to set `swipeDirection` too!): | ||
```javascript | ||
<Modal | ||
isVisible={this.state.isVisible} | ||
onSwipe={() => this.setState({ isVisible: false })} | ||
swipeDirection="left" | ||
> | ||
<View style={{ flex: 1 }}> | ||
<Text>I am the modal content!</Text> | ||
</View> | ||
</Modal> | ||
``` | ||
### The modal doesn't change orientation | ||
Add a `supportedOrientations={['portrait', 'landscape']}` prop to the component, as described [in the React Native documentation](https://facebook.github.io/react-native/docs/modal.html#supportedorientations) | ||
## Available animations | ||
Take a look at [react-native-animatable](https://github.com/oblador/react-native-animatable) to see the dozens of animations available out-of-the-box. You can also pass in custom animation definitions and have them automatically register with react-native-animatable. For more information on creating custom animations, see the react-native-animatable [animation definition schema](https://github.com/oblador/react-native-animatable#animation-definition-schema). | ||
Pull requests, feedbacks and suggestions are welcome! | ||
Pull requests, feedbacks and suggestions are welcome! | ||
P.S.: Thanks [@oblador](https://github.com/oblador) for react-native-animatable, [@brentvatne](https://github.com/brentvatne) for the npm namespace and to anyone who contributed to this library! | ||
P.S.: Thanks [@oblador](https://github.com/oblador) for react-native-animatable, [@brentvatne](https://github.com/brentvatne) for the npm namespace and to anyone who contributed to this library! |
/** | ||
* Since react-native-animatable applies by default a margin of 100 to its sliding animation, | ||
* Since react-native-animatable applies by default a margin of 100 to its sliding animation, | ||
* we reset them here overriding the margin to 0. | ||
*/ | ||
import { Dimensions } from 'react-native'; | ||
const { height, width } = Dimensions.get('window'); | ||
import { Dimensions } from "react-native"; | ||
const { height, width } = Dimensions.get("window"); | ||
function makeSlideTranslation(translationType, fromValue, toValue) { | ||
const makeSlideTranslation = (translationType, fromValue, toValue) => { | ||
return { | ||
from: { | ||
[translationType]: fromValue, | ||
[translationType]: fromValue | ||
}, | ||
to: { | ||
[translationType]: toValue, | ||
}, | ||
[translationType]: toValue | ||
} | ||
}; | ||
} | ||
}; | ||
export const slideInDown = makeSlideTranslation('translateY', -height, 0); | ||
export const slideInDown = makeSlideTranslation("translateY", -height, 0); | ||
export const slideInUp = makeSlideTranslation('translateY', height, 0); | ||
export const slideInUp = makeSlideTranslation("translateY", height, 0); | ||
export const slideInLeft = makeSlideTranslation('translateX', -width, 0); | ||
export const slideInLeft = makeSlideTranslation("translateX", -width, 0); | ||
export const slideInRight = makeSlideTranslation('translateX', width, 0); | ||
export const slideInRight = makeSlideTranslation("translateX", width, 0); | ||
export const slideOutDown = makeSlideTranslation('translateY', 0, height); | ||
export const slideOutDown = makeSlideTranslation("translateY", 0, height); | ||
export const slideOutUp = makeSlideTranslation('translateY', 0, -height); | ||
export const slideOutUp = makeSlideTranslation("translateY", 0, -height); | ||
export const slideOutLeft = makeSlideTranslation('translateX', 0, -width); | ||
export const slideOutLeft = makeSlideTranslation("translateX", 0, -width); | ||
export const slideOutRight = makeSlideTranslation('translateX', 0, width); | ||
export const slideOutRight = makeSlideTranslation("translateX", 0, width); |
@@ -1,25 +0,28 @@ | ||
declare module 'react-native-modal' { | ||
import { Component, ReactNode } from 'react' | ||
import { StyleProp, ViewStyle } from 'react-native' | ||
declare module "react-native-modal" { | ||
import { Component, ReactNode } from "react"; | ||
import { StyleProp, ViewStyle } from "react-native"; | ||
type AnimationConfig = string | { from: Object, to: Object } | ||
type AnimationConfig = string | { from: Object; to: Object }; | ||
export interface ModalProps { | ||
animationIn?: AnimationConfig | ||
animationInTiming?: number | ||
animationOut?: AnimationConfig | ||
animationOutTiming?: number | ||
avoidKeyboard?: boolean | ||
backdropColor?: string | ||
backdropOpacity?: number | ||
backdropTransitionInTiming?: number | ||
backdropTransitionOutTiming?: number | ||
useNativeDriver?: boolean | ||
children: ReactNode | ||
isVisible: boolean | ||
onModalShow?: () => void | ||
onModalHide?: () => void | ||
onBackButtonPress?: () => void | ||
onBackdropPress?: () => void | ||
style?: StyleProp<ViewStyle> | ||
animationIn?: AnimationConfig; | ||
animationInTiming?: number; | ||
animationOut?: AnimationConfig; | ||
animationOutTiming?: number; | ||
avoidKeyboard?: boolean; | ||
backdropColor?: string; | ||
backdropOpacity?: number; | ||
backdropTransitionInTiming?: number; | ||
backdropTransitionOutTiming?: number; | ||
useNativeDriver?: boolean; | ||
children: ReactNode; | ||
isVisible: boolean; | ||
onModalShow?: () => void; | ||
onModalHide?: () => void; | ||
onBackButtonPress?: () => void; | ||
onBackdropPress?: () => void; | ||
onSwipe?: () => void; | ||
onSwipeThreshold?: number; | ||
style?: StyleProp<ViewStyle>; | ||
swipeDirection?: string; | ||
} | ||
@@ -29,3 +32,3 @@ | ||
export default Modal | ||
export default Modal; | ||
} |
247
src/index.js
@@ -1,2 +0,2 @@ | ||
import React, { Component } from 'react'; | ||
import React, { Component } from "react"; | ||
import { | ||
@@ -8,4 +8,7 @@ Dimensions, | ||
KeyboardAvoidingView, | ||
} from 'react-native'; | ||
import PropTypes from 'prop-types'; | ||
Platform, | ||
PanResponder, | ||
Animated | ||
} from "react-native"; | ||
import PropTypes from "prop-types"; | ||
import { | ||
@@ -15,7 +18,7 @@ View, | ||
registerAnimation, | ||
createAnimation, | ||
} from 'react-native-animatable'; | ||
import * as ANIMATION_DEFINITIONS from './animations'; | ||
createAnimation | ||
} from "react-native-animatable"; | ||
import * as ANIMATION_DEFINITIONS from "./animations"; | ||
import styles from './index.style.js'; | ||
import styles from "./index.style.js"; | ||
@@ -31,3 +34,3 @@ // Override default animations | ||
const isObject = obj => { | ||
return obj !== null && typeof obj === 'object'; | ||
return obj !== null && typeof obj === "object"; | ||
}; | ||
@@ -52,13 +55,16 @@ | ||
onBackdropPress: PropTypes.func, | ||
onSwipe: PropTypes.func, | ||
swipeThreshold: PropTypes.number, | ||
swipeDirection: PropTypes.oneOf(["up", "down", "left", "right"]), | ||
useNativeDriver: PropTypes.bool, | ||
style: PropTypes.any, | ||
style: PropTypes.any | ||
}; | ||
static defaultProps = { | ||
animationIn: 'slideInUp', | ||
animationIn: "slideInUp", | ||
animationInTiming: 300, | ||
animationOut: 'slideOutDown', | ||
animationOut: "slideOutDown", | ||
animationOutTiming: 300, | ||
avoidKeyboard: false, | ||
backdropColor: 'black', | ||
backdropColor: "black", | ||
backdropOpacity: 0.7, | ||
@@ -72,3 +78,4 @@ backdropTransitionInTiming: 300, | ||
onBackButtonPress: () => null, | ||
useNativeDriver: false, | ||
swipeThreshold: 100, | ||
useNativeDriver: false | ||
}; | ||
@@ -79,15 +86,22 @@ | ||
// isVisible prop to false. | ||
// We also store in the state the device width and height so that we can update the modal on | ||
// We store in the state the device width and height so that we can update the modal on | ||
// device rotation. | ||
state = { | ||
isVisible: false, | ||
deviceWidth: Dimensions.get('window').width, | ||
deviceHeight: Dimensions.get('window').height, | ||
deviceWidth: Dimensions.get("window").width, | ||
deviceHeight: Dimensions.get("window").height, | ||
isSwipeable: this.props.swipeDirection ? true : false, | ||
pan: null | ||
}; | ||
transitionLock = null; | ||
inSwipeClosingState = false; | ||
constructor(props) { | ||
super(props); | ||
this._buildAnimations(props); | ||
this.buildAnimations(props); | ||
if (this.state.isSwipeable) { | ||
this.state = { ...this.state, pan: new Animated.ValueXY() }; | ||
this.buildPanResponder(); | ||
} | ||
} | ||
@@ -103,4 +117,13 @@ | ||
) { | ||
this._buildAnimations(nextProps); | ||
this.buildAnimations(nextProps); | ||
} | ||
if ( | ||
this.props.backdropOpacity !== nextProps.backdropOpacity && | ||
this.backdropRef | ||
) { | ||
this.backdropRef.transitionTo( | ||
{ opacity: nextProps.backdropOpacity }, | ||
this.props.backdropTransitionInTiming | ||
); | ||
} | ||
} | ||
@@ -116,9 +139,15 @@ | ||
if (this.state.isVisible) { | ||
this._open(); | ||
this.open(); | ||
} | ||
DeviceEventEmitter.addListener('didUpdateDimensions', this._handleDimensionsUpdate); | ||
DeviceEventEmitter.addListener( | ||
"didUpdateDimensions", | ||
this.handleDimensionsUpdate | ||
); | ||
} | ||
componentWillUnmount() { | ||
DeviceEventEmitter.removeListener('didUpdateDimensions', this._handleDimensionsUpdate); | ||
DeviceEventEmitter.removeListener( | ||
"didUpdateDimensions", | ||
this.handleDimensionsUpdate | ||
); | ||
} | ||
@@ -129,6 +158,5 @@ | ||
if (this.props.isVisible && !prevProps.isVisible) { | ||
this._open(); | ||
} | ||
// On modal close request, we slide the view down and fade out the backdrop | ||
else if (!this.props.isVisible && prevProps.isVisible) { | ||
this.open(); | ||
} else if (!this.props.isVisible && prevProps.isVisible) { | ||
// On modal close request, we slide the view down and fade out the backdrop | ||
this._close(); | ||
@@ -138,4 +166,85 @@ } | ||
buildPanResponder = () => { | ||
let animEvt = null; | ||
if ( | ||
this.props.swipeDirection === "right" || | ||
this.props.swipeDirection === "left" | ||
) { | ||
animEvt = Animated.event([null, { dx: this.state.pan.x }]); | ||
} else { | ||
animEvt = Animated.event([null, { dy: this.state.pan.y }]); | ||
} | ||
this.panResponder = PanResponder.create({ | ||
onStartShouldSetPanResponder: () => true, | ||
onPanResponderMove: (evt, gestureState) => { | ||
// Dim the background while swiping the modal | ||
const accDistance = this.getAccDistancePerDirection(gestureState); | ||
const newOpacityFactor = 1 - accDistance / this.state.deviceWidth; | ||
if (this.isSwipeDirectionAllowed(gestureState)) { | ||
this.backdropRef.transitionTo({ | ||
opacity: this.props.backdropOpacity * newOpacityFactor | ||
}); | ||
animEvt(evt, gestureState); | ||
} | ||
}, | ||
onPanResponderRelease: (evt, gestureState) => { | ||
// Call the onSwipe prop if the threshold has been exceeded | ||
const accDistance = this.getAccDistancePerDirection(gestureState); | ||
if (accDistance > this.props.swipeThreshold) { | ||
if (this.props.onSwipe) { | ||
this.inSwipeClosingState = true; | ||
this.props.onSwipe(); | ||
return; | ||
} | ||
} | ||
//Reset backdrop opacity and modal position | ||
this.backdropRef.transitionTo( | ||
{ opacity: this.props.backdropOpacity }, | ||
this.props.backdropTransitionInTiming | ||
); | ||
Animated.spring(this.state.pan, { | ||
toValue: { x: 0, y: 0 }, | ||
bounciness: 0 | ||
}).start(); | ||
} | ||
}); | ||
}; | ||
getAccDistancePerDirection = gestureState => { | ||
switch (this.props.swipeDirection) { | ||
case "up": | ||
return -gestureState.dy; | ||
case "down": | ||
return gestureState.dy; | ||
case "right": | ||
return gestureState.dx; | ||
case "left": | ||
return -gestureState.dx; | ||
default: | ||
return 0; | ||
} | ||
}; | ||
isSwipeDirectionAllowed = ({ dy, dx }) => { | ||
const draggedDown = dy > 0; | ||
const draggedUp = dy < 0; | ||
const draggedLeft = dx < 0; | ||
const draggedRight = dx > 0; | ||
if (this.props.swipeDirection === "up" && draggedUp) { | ||
return true; | ||
} else if (this.props.swipeDirection === "down" && draggedDown) { | ||
return true; | ||
} else if (this.props.swipeDirection === "right" && draggedRight) { | ||
return true; | ||
} else if (this.props.swipeDirection === "left" && draggedLeft) { | ||
return true; | ||
} | ||
return false; | ||
}; | ||
// User can define custom react-native-animatable animations, see PR #72 | ||
_buildAnimations = props => { | ||
buildAnimations = props => { | ||
let animationIn = props.animationIn; | ||
@@ -145,9 +254,9 @@ let animationOut = props.animationOut; | ||
if (isObject(animationIn)) { | ||
makeAnimation('animationIn', animationIn); | ||
animationIn = 'animationIn'; | ||
makeAnimation("animationIn", animationIn); | ||
animationIn = "animationIn"; | ||
} | ||
if (isObject(animationOut)) { | ||
makeAnimation('animationOut', animationOut); | ||
animationOut = 'animationOut'; | ||
makeAnimation("animationOut", animationOut); | ||
animationOut = "animationOut"; | ||
} | ||
@@ -159,7 +268,10 @@ | ||
_handleDimensionsUpdate = dimensionsUpdate => { | ||
handleDimensionsUpdate = dimensionsUpdate => { | ||
// Here we update the device dimensions in the state if the layout changed (triggering a render) | ||
const deviceWidth = Dimensions.get('window').width; | ||
const deviceHeight = Dimensions.get('window').height; | ||
if (deviceWidth !== this.state.deviceWidth || deviceHeight !== this.state.deviceHeight) { | ||
const deviceWidth = Dimensions.get("window").width; | ||
const deviceHeight = Dimensions.get("window").height; | ||
if ( | ||
deviceWidth !== this.state.deviceWidth || | ||
deviceHeight !== this.state.deviceHeight | ||
) { | ||
this.setState({ deviceWidth, deviceHeight }); | ||
@@ -169,3 +281,3 @@ } | ||
_open = () => { | ||
open = () => { | ||
if (this.transitionLock) return; | ||
@@ -175,4 +287,12 @@ this.transitionLock = true; | ||
{ opacity: this.props.backdropOpacity }, | ||
this.props.backdropTransitionInTiming, | ||
this.props.backdropTransitionInTiming | ||
); | ||
// This is for reset the pan position, if not modal get stuck | ||
// at the last release position when you try to open it. | ||
// Could certainly be improve - no idea for the moment. | ||
if (this.state.isSwipeable) { | ||
this.state.pan.setValue({ x: 0, y: 0 }); | ||
} | ||
this.contentRef[this.animationIn](this.props.animationInTiming).then(() => { | ||
@@ -182,4 +302,3 @@ this.transitionLock = false; | ||
this._close(); | ||
} | ||
else { | ||
} else { | ||
this.props.onModalShow(); | ||
@@ -193,9 +312,27 @@ } | ||
this.transitionLock = true; | ||
this.backdropRef.transitionTo({ opacity: 0 }, this.props.backdropTransitionOutTiming); | ||
this.contentRef[this.animationOut](this.props.animationOutTiming).then(() => { | ||
this.backdropRef.transitionTo( | ||
{ opacity: 0 }, | ||
this.props.backdropTransitionOutTiming | ||
); | ||
let animationOut = this.animationOut; | ||
if (this.inSwipeClosingState) { | ||
this.inSwipeClosingState = false; | ||
if (this.props.swipeDirection === "up") { | ||
animationOut = "slideOutUp"; | ||
} else if (this.props.swipeDirection === "down") { | ||
animationOut = "slideOutDown"; | ||
} else if (this.props.swipeDirection === "right") { | ||
animationOut = "slideOutRight"; | ||
} else if (this.props.swipeDirection === "left") { | ||
animationOut = "slideOutLeft"; | ||
} | ||
} | ||
this.contentRef[animationOut](this.props.animationOutTiming).then(() => { | ||
this.transitionLock = false; | ||
if (this.props.isVisible) { | ||
this._open(); | ||
} | ||
else { | ||
this.open(); | ||
} else { | ||
this.setState({ isVisible: false }); | ||
@@ -232,10 +369,18 @@ this.props.onModalHide(); | ||
styles.content, | ||
style, | ||
style | ||
]; | ||
let panHandlers = {}; | ||
let panPosition = {}; | ||
if (this.state.isSwipeable) { | ||
panHandlers = { ...this.panResponder.panHandlers }; | ||
panPosition = this.state.pan.getLayout(); | ||
} | ||
const containerView = ( | ||
<View | ||
{...panHandlers} | ||
ref={ref => (this.contentRef = ref)} | ||
style={computedStyle} | ||
pointerEvents={'box-none'} | ||
style={[panPosition, computedStyle]} | ||
pointerEvents="box-none" | ||
useNativeDriver={useNativeDriver} | ||
@@ -251,3 +396,3 @@ {...otherProps} | ||
transparent={true} | ||
animationType={'none'} | ||
animationType={"none"} | ||
visible={this.state.isVisible} | ||
@@ -266,4 +411,4 @@ onRequestClose={onBackButtonPress} | ||
width: deviceWidth, | ||
height: deviceHeight, | ||
}, | ||
height: deviceHeight | ||
} | ||
]} | ||
@@ -275,4 +420,4 @@ /> | ||
<KeyboardAvoidingView | ||
behavior={'padding'} | ||
pointerEvents={'box-none'} | ||
behavior={Platform.OS === "ios" ? "padding" : null} | ||
pointerEvents="box-none" | ||
style={computedStyle.concat([{ margin: 0 }])} | ||
@@ -279,0 +424,0 @@ > |
@@ -1,6 +0,6 @@ | ||
import { StyleSheet } from 'react-native'; | ||
import { StyleSheet } from "react-native"; | ||
export default StyleSheet.create({ | ||
backdrop: { | ||
position: 'absolute', | ||
position: "absolute", | ||
top: 0, | ||
@@ -11,8 +11,8 @@ bottom: 0, | ||
opacity: 0, | ||
backgroundColor: 'black', | ||
backgroundColor: "black" | ||
}, | ||
content: { | ||
flex: 1, | ||
justifyContent: 'center', | ||
}, | ||
justifyContent: "center" | ||
} | ||
}); |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
28018
444
189
8
1
+ Addedprop-types@15.6.0(transitive)
- Removedprop-types@15.5.10(transitive)
Updatedprop-types@15.6.0