UNPKG

rn-sliding-button-fix-lifecycle-hooks

Version:

SlidingButton component for react-native, same api on both android and ios.

224 lines (196 loc) 6.59 kB
'use strict'; import React, { Component } from 'react'; import { StyleSheet, View, PanResponder, Animated, } from 'react-native'; import PropTypes from 'prop-types'; const SlideDirection = { LEFT: "left", RIGHT: "right", ANY: "any" }; export default class RNSlidingButton extends Component { constructor(props) { super(props); this.buttonWidth = 0; this.state = { initialX: 0, locationX: 0, dx: 0, animatedX: new Animated.Value(0), animatedY: new Animated.Value(0), released: false, swiped: true, }; } isSlideSuccessful() { let slidePercent = this.props.successfulSlidePercent || 40; let successfulSlideWidth = this.buttonWidth * slidePercent / 100; if (!this.props.slideDirection) { return this.state.dx > successfulSlideWidth; // Defaults to right slide } else if (this.props.slideDirection === SlideDirection.RIGHT) { return this.state.dx > successfulSlideWidth; } else if (this.props.slideDirection === SlideDirection.LEFT) { return this.state.dx < (-1 * successfulSlideWidth); } else if (this.props.slideDirection === SlideDirection.ANY) { return Math.abs(this.state.dx) > successfulSlideWidth; } } onSlide(x) { if (this.props.onSlide) { this.props.onSlide(x); } } UNSAFE_componentWillMount() { let self = this; this.panResponder = PanResponder.create({ onStartShouldSetPanResponder: (evt, gestureState) => true, onStartShouldSetPanResponderCapture: (evt, gestureState) => true, onMoveShouldSetPanResponder: (evt, gestureState) => true, onMoveShouldSetPanResponderCapture: (evt, gestureState) => true, onPanResponderTerminationRequest: (evt, gestureState) => true, onPanResponderGrant: (evt, gestureState) => { }, onPanResponderMove: (evt, gestureState) => { self.setState({ locationX: evt.nativeEvent.locationX, dx: gestureState.dx }); self.onSlide(gestureState.dx); }, onPanResponderRelease: (evt, gestureState) => { if (this.isSlideSuccessful()) { self.props.onSlidingSuccess(); this.moveButtonOut(() => { self.setState({ swiped: true }); }); } this.snapToPosition(() => { self.setState({ released: false, dx: self.state.initialX }); }); }, onPanResponderTerminate: (evt, gestureState) => { this.snapToPosition(() => { self.setState({ released: false, dx: self.state.initialX }); }); }, onShouldBlockNativeResponder: (evt, gestureState) => { return true; } }); } onSlidingSuccess() { if (this.props.onSlidingSuccess !== undefined) { this.props.onSlidingSuccess(); } } moveButtonOut(onCompleteCallback) { let self = this; let startPos = this.state.initialX + this.state.dx; let endPos = this.state.dx < 0 ? -this.buttonWidth : this.buttonWidth * 2; this.setState({ released: true, animatedX: new Animated.Value(startPos), animatedY: new Animated.Value(startPos * -1) }, () => { Animated.timing( self.state.animatedX, { toValue: endPos } ).start(onCompleteCallback); Animated.timing( this.state.animatedY, { toValue: endPos } ).start(onCompleteCallback); }); } snapToPosition(onCompleteCallback) { let self = this; let startPos = this.state.initialX + this.state.dx; let endPos = this.state.initialX; this.setState({ released: true, animatedX: new Animated.Value(startPos), animatedY: new Animated.Value(startPos * -1) }, () => { Animated.timing( this.state.animatedX, { toValue: endPos } ).start(onCompleteCallback); Animated.timing( this.state.animatedY, { toValue: endPos } ).start(onCompleteCallback); }); } onLayout(event) { this.buttonWidth = event.nativeEvent.layout.width; this.setState({ initialX: event.nativeEvent.layout.x }); } render() { let style = [styles.button, { left: this.state.dx, right: this.state.dx * -1, backgroundColor: 'transparent', }]; let button: undefined; if (this.state.released) { style = [styles.button, { left: this.state.animatedX, right: this.state.animatedY, backgroundColor: 'transparent', }]; button = ( <Animated.View style={style}> {this.props.children} </Animated.View> ); } else { button = ( <View style={style}> <View onLayout={this.onLayout.bind(this)}> {this.props.children} </View> </View> ); } return ( <View style={[styles.slidingContainer, this.props.style, { height: this.props.height }]}> <View style={styles.container} {...this.panResponder.panHandlers}> {button} </View> </View> ); } } RNSlidingButton.propTypes = { successfulSlidePercent: PropTypes.number, height: PropTypes.number.isRequired, slideDirection: PropTypes.string }; const styles = StyleSheet.create({ slidingContainer: { backgroundColor: '#0761c1', overflow: 'hidden', justifyContent: 'center', }, container: { position: 'relative', justifyContent: 'center', flex: 1, }, button: { position: 'absolute', } }); export { SlideDirection }