Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow setting of minimum touch clearance from left and/or right edge #46

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
npm-debug.log*
node_modules
lib
.idea/
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# React Native Swipeable [![NPM version][npm-image]][npm-url]

A powerful React Native swipe component. Supports both iOS and Android.
A powerful React Native swipe component. Supports both iOS and Android.

<img src="https://raw.githubusercontent.com/jshanson7/react-native-swipeable/master/demo.gif" width="310">

## Installation

```sh
npm i --save react-native-swipeable
npm i --save react-native-swipeable-row
```

## Usage

Wrap your ListView/TableView items with the `Swipeable` component:

```javascript
import Swipeable from 'react-native-swipeable';
import Swipeable from 'react-native-swipeable-row';

const leftContent = <Text>Pull to activate</Text>;

Expand Down Expand Up @@ -50,6 +50,8 @@ function MyListItem() {
| `rightButtonWidth` | integer | 75 | (optional) resting visible peek of each right button after buttons are swiped open |
| `onRef` | function | `null` | (optional) receive swipeable component instance reference |
| `onPanAnimatedValueRef` | function | `null` | (optional) receive swipeable pan `Animated.ValueXY` reference for upstream animations |
| `bounceOnMount` | boolean | `false` | (optional) To alert the user that swiping is possible |
| `disable` | boolean | `false` | (optional) Disable swiping |

### Advanced Props

Expand Down Expand Up @@ -80,6 +82,14 @@ class MyListItem extends Component {
}
```

#### bounceRight(onDone)

Bounce the right component to alert swiping is possible. `onDone` is an optional callback.

#### bounceLeft(onDone)

Bounce the left component to alert swiping is possible. `onDone` is an optional callback.

## Example

To run [the example](https://github.com/jshanson7/react-native-swipeable/blob/master/example/swipeable-example.js):
Expand Down Expand Up @@ -125,4 +135,4 @@ or:
MIT

[npm-image]: https://badge.fury.io/js/react-native-swipeable.svg
[npm-url]: https://npmjs.org/package/react-native-swipeable
[npm-url]: https://npmjs.org/package/react-native-swipeable-row
5 changes: 5 additions & 0 deletions example/swipeable-example.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ function Example1({onOpen, onClose}) {
]}
onRightButtonsOpenRelease={onOpen}
onRightButtonsCloseRelease={onClose}
swipeStartMinLeftEdgeClearance={10}
swipeStartMinRightEdgeClearance={10}
>
<View style={[styles.listItem, {backgroundColor: 'salmon'}]}>
<Text>Example 1</Text>
Expand Down Expand Up @@ -96,6 +98,8 @@ function Example2({onOpen, onClose}) {
)}
onLeftButtonsOpenRelease={onOpen}
onLeftButtonsCloseRelease={onClose}
swipeStartMinLeftEdgeClearance={10}
swipeStartMinRightEdgeClearance={10}
>
<View style={[styles.listItem, {backgroundColor: 'paleturquoise'}]}>
<Text>Example 2</Text>
Expand Down Expand Up @@ -127,6 +131,7 @@ class Example3 extends Component {
onLeftActionActivate={() => this.setState({leftActionActivated: true})}
onLeftActionDeactivate={() => this.setState({leftActionActivated: false})}
onLeftActionComplete={() => this.setState({toggle: !toggle})}
swipeStartMinLeftEdgeClearance={10}
>
<View style={[styles.listItem, {backgroundColor: toggle ? 'thistle' : 'darkseagreen'}]}>
<Text>Example 3</Text>
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "react-native-swipeable",
"version": "0.6.0",
"description": "A powerful React Native swipe component",
"name": "react-native-swipeable-row",
"version": "0.8.1",
"description": "A powerful React Native swipe component based on @jshanson7 package",
"main": "lib/index.js",
"scripts": {
"build": "make build",
"lint": "make lint"
},
"author": "Jeff Hanson",
"author": "Mårten Cederman",
"license": "MIT",
"devDependencies": {
"babel-cli": "^6.18.0",
Expand All @@ -28,7 +28,7 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/jshanson7/react-native-swipeable.git"
"url": "git+https://github.com/Cederman/react-native-swipeable"
},
"keywords": [
"react",
Expand Down
83 changes: 78 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable import/no-unresolved, import/extensions */
import React, {PureComponent} from 'react';
import {Animated, Easing, PanResponder, StyleSheet, View, ViewPropTypes} from 'react-native';
import {Animated, Dimensions, Easing, PanResponder, StyleSheet, View, ViewPropTypes} from 'react-native';
import {PropTypes} from 'prop-types';
/* eslint-enable import/no-unresolved, import/extensions */

Expand Down Expand Up @@ -74,6 +74,9 @@ export default class Swipeable extends PureComponent {
onRef: PropTypes.func,
onPanAnimatedValueRef: PropTypes.func,
swipeStartMinDistance: PropTypes.number,
swipeStartMinLeftEdgeClearance: PropTypes.number,
swipeStartMinRightEdgeClearance: PropTypes.number,
disable: PropTypes.bool,

// styles
style: ViewPropTypes.style,
Expand Down Expand Up @@ -151,7 +154,11 @@ export default class Swipeable extends PureComponent {
// misc
onRef: noop,
onPanAnimatedValueRef: noop,
swipeStartMinDistance: 15
swipeStartMinDistance: 15,
swipeStartMinLeftEdgeClearance: 0,
swipeStartMinRightEdgeClearance: 0,
bounceOnMount: false,
disable: false,
};

state = {
Expand All @@ -173,6 +180,12 @@ export default class Swipeable extends PureComponent {
onPanAnimatedValueRef(this.state.pan);
}

componentDidMount() {
if (this.props.bounceOnMount) {
setTimeout(this._bounceOnMount, 700);
}
}

componentWillUnmount() {
this._unmounted = true;
}
Expand All @@ -199,25 +212,81 @@ export default class Swipeable extends PureComponent {
animationFn(pan, animationConfig).start(onDone);
};

_bounceOnMount = () => {
if (this._canSwipeLeft()) {
this.bounceRight(this.bounceLeft);
} else if (this._canSwipeRight()) {
this.bounceLeft();
}
};

bounceRight = (onDone) => {
if (this._canSwipeLeft()) {
this.setState({
rightActionActivated: true,
rightButtonsActivated: true,
rightButtonsOpen: true
});
this._bounce({x: -50, y: 0}, onDone);
}
};

bounceLeft = (onDone) => {
if (this._canSwipeRight()) {
this.setState({
leftActionActivated: true,
leftButtonsActivated: true,
leftButtonsOpen: true
});
this._bounce({x: 50, y: 0}, onDone);
}
};

_bounce = (toValue, onDone) => {
const {pan} = this.state;
pan.flattenOffset();

const {swipeReleaseAnimationFn, swipeReleaseAnimationConfig} = this.props;
Animated.timing(pan, {
toValue,
duration: 250,
easing: Easing.elastic(0.5)
}).start(() => this.recenter(swipeReleaseAnimationFn, swipeReleaseAnimationConfig, () => onDone && onDone()));
};

_unmounted = false;

_handlePan = Animated.event([null, {
dx: this.state.pan.x,
dy: this.state.pan.y
}]);

_handleMoveShouldSetPanResponder = (event, gestureState) => (
Math.abs(gestureState.dx) > this.props.swipeStartMinDistance
);
_handleMoveShouldSetPanResponder = (event, gestureState) => {
const {swipeStartMinDistance, swipeStartMinLeftEdgeClearance, swipeStartMinRightEdgeClearance} = this.props;
const gestureStartX = gestureState.moveX - gestureState.dx;
return Math.abs(gestureState.dx) > swipeStartMinDistance
&& (swipeStartMinLeftEdgeClearance === 0
|| gestureStartX >= swipeStartMinLeftEdgeClearance)
&& (swipeStartMinRightEdgeClearance === 0
|| gestureStartX <= Dimensions.get('window').width - swipeStartMinRightEdgeClearance);
};

_handlePanResponderStart = (event, gestureState) => {
if (this.props.disable) {
return;
}

const {lastOffset, pan} = this.state;

pan.setOffset(lastOffset);
this.props.onSwipeStart(event, gestureState, this);
};

_handlePanResponderMove = (event, gestureState) => {
if (this.props.disable) {
return;
}

const {
leftActionActivationDistance,
leftButtonsActivationDistance,
Expand Down Expand Up @@ -313,6 +382,10 @@ export default class Swipeable extends PureComponent {
};

_handlePanResponderEnd = (event, gestureState) => {
if (this.props.disable) {
return;
}

const {
onLeftActionRelease,
onLeftActionDeactivate,
Expand Down