@momo-kits/swipe 0.0.32-beta → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SwipeAction.js CHANGED
@@ -1 +1,656 @@
1
- var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(exports,"__esModule",{value:true});exports.default=void 0;var _extends2=_interopRequireDefault(require("@babel/runtime/helpers/extends"));var _defineProperty2=_interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));var _classCallCheck2=_interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));var _createClass2=_interopRequireDefault(require("@babel/runtime/helpers/createClass"));var _inherits2=_interopRequireDefault(require("@babel/runtime/helpers/inherits"));var _possibleConstructorReturn2=_interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));var _getPrototypeOf2=_interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));var _react=_interopRequireWildcard(require("react"));var _propTypes=_interopRequireDefault(require("prop-types"));var _reactNative=require("react-native");var _core=require("@momo-kits/core");var _jsxFileName="/Users/trinh.ho2/momo-folk/kits/src/libs/swipe/dist/SwipeAction.js";function _getRequireWildcardCache(nodeInterop){if(typeof WeakMap!=="function")return null;var cacheBabelInterop=new WeakMap();var cacheNodeInterop=new WeakMap();return(_getRequireWildcardCache=function _getRequireWildcardCache(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop;})(nodeInterop);}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule){return obj;}if(obj===null||typeof obj!=="object"&&typeof obj!=="function"){return{default:obj};}var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj)){return cache.get(obj);}var newObj={};var hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj){if(key!=="default"&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;if(desc&&(desc.get||desc.set)){Object.defineProperty(newObj,key,desc);}else{newObj[key]=obj[key];}}}newObj.default=obj;if(cache){cache.set(obj,newObj);}return newObj;}function ownKeys(object,enumerableOnly){var keys=Object.keys(object);if(Object.getOwnPropertySymbols){var symbols=Object.getOwnPropertySymbols(object);if(enumerableOnly){symbols=symbols.filter(function(sym){return Object.getOwnPropertyDescriptor(object,sym).enumerable;});}keys.push.apply(keys,symbols);}return keys;}function _objectSpread(target){for(var i=1;i<arguments.length;i++){var source=arguments[i]!=null?arguments[i]:{};if(i%2){ownKeys(Object(source),true).forEach(function(key){(0,_defineProperty2.default)(target,key,source[key]);});}else if(Object.getOwnPropertyDescriptors){Object.defineProperties(target,Object.getOwnPropertyDescriptors(source));}else{ownKeys(Object(source)).forEach(function(key){Object.defineProperty(target,key,Object.getOwnPropertyDescriptor(source,key));});}}return target;}function _createSuper(Derived){var hasNativeReflectConstruct=_isNativeReflectConstruct();return function _createSuperInternal(){var Super=(0,_getPrototypeOf2.default)(Derived),result;if(hasNativeReflectConstruct){var NewTarget=(0,_getPrototypeOf2.default)(this).constructor;result=Reflect.construct(Super,arguments,NewTarget);}else{result=Super.apply(this,arguments);}return(0,_possibleConstructorReturn2.default)(this,result);};}function _isNativeReflectConstruct(){if(typeof Reflect==="undefined"||!Reflect.construct)return false;if(Reflect.construct.sham)return false;if(typeof Proxy==="function")return true;try{Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}));return true;}catch(e){return false;}}var DEFAULT_PREVIEW_OPEN_DELAY=700;var PREVIEW_CLOSE_DELAY=300;var MAX_VELOCITY_CONTRIBUTION=5;var SCROLL_LOCK_MILLISECONDS=300;var SCREEN_WIDTH=_reactNative.Dimensions.get('window').width;var WIDTH_HIDDEN_RIGHT=190;var SwipeAction=function(_Component){(0,_inherits2.default)(SwipeAction,_Component);var _super=_createSuper(SwipeAction);function SwipeAction(props){var _this;(0,_classCallCheck2.default)(this,SwipeAction);_this=_super.call(this,props);_this.ensureScrollEnabled=function(){if(!_this.parentScrollEnabled){_this.parentScrollEnabled=true;_this.props.setScrollEnabled&&_this.props.setScrollEnabled(true);}};_this.onLeftActionLayout=function(_ref){var nativeEvent=_ref.nativeEvent;_this.setState({leftOpenValue:nativeEvent.layout.width});};_this.onRightActionLayout=function(_ref2){var nativeEvent=_ref2.nativeEvent;_this.setState({rightOpenValue:-nativeEvent.layout.width});};_this.renderHiddenContent=function(){var _rightAction$slice;var _this$props=_this.props,actionBackground=_this$props.actionBackground,rowIndex=_this$props.rowIndex,rightAction=_this$props.rightAction,leftAction=_this$props.leftAction;var hiddenRowStyle=_objectSpread(_objectSpread({},styles.standaloneRowBack),{backgroundColor:actionBackground});return _react.default.createElement(_reactNative.View,{style:hiddenRowStyle,__source:{fileName:_jsxFileName,lineNumber:439,columnNumber:4}},_this.state.isLeftActionVisible&&_react.default.createElement(_reactNative.View,{style:styles.leftItemRow,__source:{fileName:_jsxFileName,lineNumber:443,columnNumber:7}},_react.default.createElement(_reactNative.View,{style:{flexDirection:'row'},onLayout:_this.onLeftActionLayout,__source:{fileName:_jsxFileName,lineNumber:444,columnNumber:8}},leftAction==null?void 0:leftAction.map==null?void 0:leftAction.map(function(item,index){return _this.renderLeftAction(item,index);}))),_this.state.isRightActionVisible&&_react.default.createElement(_reactNative.View,{style:styles.rightItemRow,__source:{fileName:_jsxFileName,lineNumber:455,columnNumber:7}},_react.default.createElement(_reactNative.View,{style:{flexDirection:'row'},onLayout:_this.onRightActionLayout,__source:{fileName:_jsxFileName,lineNumber:456,columnNumber:8}},rightAction==null?void 0:rightAction.slice==null?void 0:(_rightAction$slice=rightAction.slice(0))==null?void 0:_rightAction$slice.reverse().map(function(item,index){return _this.renderRightAction(item,index);}))));};_this.renderIcon=function(icon){var actionIconStyle=_this.props.actionIconStyle;var iconSource=_core.ValueUtil.getImageSource(icon);return _react.default.createElement(_reactNative.Image,{source:iconSource,resizeMode:"contain",style:[styles.icon,actionIconStyle],__source:{fileName:_jsxFileName,lineNumber:473,columnNumber:4}});};_this.renderTitle=function(title){var actionTextStyle=_this.props.actionTextStyle;return _react.default.createElement(_core.Text.SubTitle,{style:[styles.text,actionTextStyle],__source:{fileName:_jsxFileName,lineNumber:484,columnNumber:4}},title);};_this.onActionPress=function(item,rowIndex){return function(){var onPress=item.onPress;onPress==null?void 0:onPress(item,rowIndex);};};_this.renderRightAction=function(item,index){var _this$props2=_this.props,rowIndex=_this$props2.rowIndex,renderRightAction=_this$props2.renderRightAction;if(renderRightAction){return _react.default.createElement(_reactNative.View,{key:item+index,__source:{fileName:_jsxFileName,lineNumber:499,columnNumber:5}},renderRightAction({item:item,index:index,rowIndex:rowIndex}));}return _react.default.createElement(_reactNative.TouchableOpacity,{key:index.toString(),style:[styles.buttonContainer,{backgroundColor:item.color,height:_this.state.hiddenHeight}],onPress:_this.onActionPress(item,rowIndex),__source:{fileName:_jsxFileName,lineNumber:505,columnNumber:4}},item.icon&&_this.renderIcon(item.icon),item.title&&_this.renderTitle(item.title));};_this.renderLeftAction=function(item,index){var _this$props3=_this.props,rowIndex=_this$props3.rowIndex,renderLeftAction=_this$props3.renderLeftAction;if(renderLeftAction){return _react.default.createElement(_reactNative.View,{key:item+index,__source:{fileName:_jsxFileName,lineNumber:523,columnNumber:5}},renderLeftAction({item:item,index:index,rowIndex:rowIndex}));}return _react.default.createElement(_reactNative.TouchableOpacity,{key:index.toString(),style:[styles.buttonContainer,{backgroundColor:item.color,height:_this.state.hiddenHeight}],onPress:_this.onActionPress(item,rowIndex),__source:{fileName:_jsxFileName,lineNumber:529,columnNumber:4}},item.icon&&_this.renderIcon(item.icon),item.title&&_this.renderTitle(item.title));};_this.isOpen=false;_this.previousTrackedTranslateX=0;_this.previousTrackedDirection=null;_this.horizontalSwipeGestureBegan=false;_this.swipeInitialX=null;_this.parentScrollEnabled=true;_this.ranPreview=false;_this._ensureScrollEnabledTimer=null;_this.isForceClosing=false;_this.state={dimensionsSet:false,hiddenHeight:_this.props.disableHiddenLayoutCalculation?'100%':0,hiddenWidth:_this.props.disableHiddenLayoutCalculation?'100%':0,isLeftActionVisible:false,isRightActionVisible:false,leftOpenValue:0,rightOpenValue:0};var _this$props4=_this.props,_leftAction=_this$props4.leftAction,_rightAction=_this$props4.rightAction;var buttonCellWidth=WIDTH_HIDDEN_RIGHT/2;_this.stopLeftSwipe=SCREEN_WIDTH;_this.stopRightSwipe=-SCREEN_WIDTH;_this._translateX=new _reactNative.Animated.Value(0);if(_this.props.onSwipeValueChange){_this._translateX.addListener(function(_ref3){var value=_ref3.value;var direction=_this.previousTrackedDirection;if(value!==_this.previousTrackedTranslateX){direction=value>_this.previousTrackedTranslateX?'right':'left';}_this.props.onSwipeValueChange&&_this.props.onSwipeValueChange({isOpen:_this.isOpen,direction:direction,value:value});_this.previousTrackedTranslateX=value;_this.previousTrackedDirection=direction;});}if(_this.props.forceCloseToRightThreshold&&_this.props.forceCloseToRightThreshold>0){_this._translateX.addListener(function(_ref4){var value=_ref4.value;if(!_this.isForceClosing&&SCREEN_WIDTH+value<_this.props.forceCloseToRightThreshold){_this.isForceClosing=true;_this.forceCloseRow('right');if(_this.props.onForceCloseToRight){_this.props.onForceCloseToRight();}}});}if(_this.props.forceCloseToLeftThreshold&&_this.props.forceCloseToLeftThreshold>0){_this._translateX.addListener(function(_ref5){var value=_ref5.value;if(!_this.isForceClosing&&SCREEN_WIDTH-value<_this.props.forceCloseToLeftThreshold){_this.isForceClosing=true;_this.forceCloseRow('left');if(_this.props.onForceCloseToLeft){_this.props.onForceCloseToLeft();}}});}return _this;}(0,_createClass2.default)(SwipeAction,[{key:"UNSAFE_componentWillMount",value:function UNSAFE_componentWillMount(){var _this2=this;this._panResponder=_reactNative.PanResponder.create({onMoveShouldSetPanResponder:function onMoveShouldSetPanResponder(e,gs){return _this2.handleOnMoveShouldSetPanResponder(e,gs);},onPanResponderMove:function onPanResponderMove(e,gs){return _this2.handlePanResponderMove(e,gs);},onPanResponderRelease:function onPanResponderRelease(e,gs){return _this2.handlePanResponderEnd(e,gs);},onPanResponderTerminate:function onPanResponderTerminate(e,gs){return _this2.handlePanResponderEnd(e,gs);},onShouldBlockNativeResponder:function onShouldBlockNativeResponder(_){return false;}});}},{key:"componentWillUnmount",value:function componentWillUnmount(){clearTimeout(this._ensureScrollEnabledTimer);this._translateX.removeAllListeners();}},{key:"shouldComponentUpdate",value:function shouldComponentUpdate(nextProps,nextState){if(this.state.hiddenHeight!==nextState.hiddenHeight||this.state.hiddenWidth!==nextState.hiddenWidth||!this.props.shouldItemUpdate||this.props.shouldItemUpdate&&this.props.shouldItemUpdate(this.props.item,nextProps.item)){return true;}return false;}},{key:"getPreviewAnimation",value:function getPreviewAnimation(toValue,delay){return _reactNative.Animated.timing(this._translateX,{duration:this.props.previewDuration,toValue:toValue,delay:delay,useNativeDriver:this.props.useNativeDriver});}},{key:"onContentLayout",value:function onContentLayout(e){var _this3=this;this.setState(_objectSpread({dimensionsSet:!this.props.recalculateHiddenLayout},!this.props.disableHiddenLayoutCalculation?{hiddenHeight:e.nativeEvent.layout.height,hiddenWidth:e.nativeEvent.layout.width}:{}));if(this.props.preview&&!this.ranPreview){this.ranPreview=true;var previewOpenValue=this.props.previewOpenValue||this.state.rightOpenValue*0.5;this.getPreviewAnimation(previewOpenValue,this.props.previewOpenDelay).start(function(_){_this3.getPreviewAnimation(0,PREVIEW_CLOSE_DELAY).start();});}}},{key:"onPress",value:function onPress(){if(this.swipeInitialX==null||this.swipeInitialX===0){if(this.props.onPress&&typeof this.props.onPress==='function'){this.props.onPress();}}else{if(this.props.closeOnPress){this.closeRow();}}}},{key:"handleOnMoveShouldSetPanResponder",value:function handleOnMoveShouldSetPanResponder(e,gs){var dx=gs.dx;return Math.abs(dx)>this.props.directionalDistanceChangeThreshold;}},{key:"handlePanResponderMove",value:function handlePanResponderMove(e,gestureState){if(this.isForceClosing){return;}var dx=gestureState.dx,dy=gestureState.dy;var absDx=Math.abs(dx);var absDy=Math.abs(dy);if(absDx>this.props.directionalDistanceChangeThreshold||absDy>this.props.directionalDistanceChangeThreshold){if(absDy>absDx&&!this.horizontalSwipeGestureBegan){return;}if(this.parentScrollEnabled){this.parentScrollEnabled=false;this.props.setScrollEnabled&&this.props.setScrollEnabled(false);}if(this.swipeInitialX===null){this.swipeInitialX=this._translateX._value;}if(!this.horizontalSwipeGestureBegan){this.horizontalSwipeGestureBegan=true;var direction=dx>0?'TO_RIGHT':'TO_LEFT';this.props.swipeGestureBegan&&this.props.swipeGestureBegan(direction);}var newDX=this.swipeInitialX+dx;if(this.props.disableRightAction&&newDX<0){newDX=0;}if(this.props.disableLeftAction&&newDX>0){newDX=0;}if(this.stopLeftSwipe&&newDX>this.stopLeftSwipe){newDX=this.stopLeftSwipe;}if(this.stopRightSwipe&&newDX<this.stopRightSwipe){newDX=this.stopRightSwipe;}if(newDX>0){this.setState({isLeftActionVisible:true,isRightActionVisible:false});}else if(newDX<0){this.setState({isRightActionVisible:true,isLeftActionVisible:false});}this._translateX.setValue(newDX);}}},{key:"handlePanResponderEnd",value:function handlePanResponderEnd(e,gestureState){if(this.isForceClosing){this.isForceClosing=false;}var swipeToOpenVelocityContribution=this.props.swipeToOpenVelocityContribution;var possibleExtraPixels=this.state.rightOpenValue*swipeToOpenVelocityContribution;var clampedVelocity=Math.min(gestureState.vx,MAX_VELOCITY_CONTRIBUTION);var projectedExtraPixels=possibleExtraPixels*(clampedVelocity/MAX_VELOCITY_CONTRIBUTION);this._ensureScrollEnabledTimer=setTimeout(this.ensureScrollEnabled,SCROLL_LOCK_MILLISECONDS);var toValue=0;if(this._translateX._value>=0){if(this.swipeInitialX<this._translateX._value){if(this._translateX._value-projectedExtraPixels>this.state.leftOpenValue*(this.props.swipeToOpenPercent/100)){toValue=this.state.leftOpenValue;}}else if(this._translateX._value-projectedExtraPixels>this.state.leftOpenValue*(1-this.props.swipeToClosePercent/100)){toValue=this.state.leftOpenValue;}}else{if(this.swipeInitialX>this._translateX._value){if(this._translateX._value-projectedExtraPixels<this.state.rightOpenValue*(this.props.swipeToOpenPercent/100)){toValue=this.state.rightOpenValue;}}else if(this._translateX._value-projectedExtraPixels<this.state.rightOpenValue*(1-this.props.swipeToClosePercent/100)){toValue=this.state.rightOpenValue;}}this.props.swipeGestureEnd&&this.props.swipeGestureEnd();this.manuallySwipeRow(toValue);}},{key:"closeRow",value:function closeRow(){this.manuallySwipeRow(0);}},{key:"forceCloseRow",value:function forceCloseRow(direction){var _this4=this;this.manuallySwipeRow(0,function(){if(direction==='right'&&_this4.props.onForceCloseToRightEnd){_this4.props.onForceCloseToRightEnd();}else if(direction==='left'&&_this4.props.onForceCloseToLeftEnd){_this4.props.onForceCloseToLeftEnd();}});}},{key:"closeRowWithoutAnimation",value:function closeRowWithoutAnimation(){this._translateX.setValue(0);this.ensureScrollEnabled();this.isOpen=false;this.props.onDidClose&&this.props.onDidClose();this.props.onClose&&this.props.onClose();this.swipeInitialX=null;this.horizontalSwipeGestureBegan=false;}},{key:"manuallySwipeRow",value:function manuallySwipeRow(toValue,onAnimationEnd){var _this5=this;_reactNative.Animated.spring(this._translateX,{toValue:toValue,friction:this.props.friction,tension:this.props.tension,useNativeDriver:this.props.useNativeDriver}).start(function(_){_this5._translateX.setValue(toValue);_this5.ensureScrollEnabled();if(toValue===0){_this5.isOpen=false;_this5.props.onDidClose&&_this5.props.onDidClose();}else{_this5.isOpen=true;_this5.props.onDidOpen&&_this5.props.onDidOpen(toValue);}if(onAnimationEnd){onAnimationEnd();}});if(toValue===0){this.props.onClose&&this.props.onClose();}else{this.props.onOpen&&this.props.onOpen(toValue);}this.swipeInitialX=toValue;this.horizontalSwipeGestureBegan=false;}},{key:"renderVisibleContent",value:function renderVisibleContent(){var _this6=this;if(this.props.children){var onPress=this.props.children.props.onPress;if(onPress){var newOnPress=function newOnPress(_){_this6.onPress();onPress();};return _react.default.cloneElement(this.props.children,_objectSpread(_objectSpread({},this.props.children.props),{},{onPress:newOnPress}));}return _react.default.createElement(_reactNative.TouchableOpacity,{activeOpacity:1,onPress:function onPress(_){return _this6.onPress();},__source:{fileName:_jsxFileName,lineNumber:372,columnNumber:5}},this.props.children);}}},{key:"renderRowContent",value:function renderRowContent(){var _this7=this;if(this.state.dimensionsSet){return _react.default.createElement(_reactNative.Animated.View,(0,_extends2.default)({manipulationModes:['translateX']},this._panResponder.panHandlers,{style:{zIndex:2,transform:[{translateX:this._translateX}]},__source:{fileName:_jsxFileName,lineNumber:387,columnNumber:5}}),this.renderVisibleContent());}return _react.default.createElement(_reactNative.Animated.View,(0,_extends2.default)({manipulationModes:['translateX']},this._panResponder.panHandlers,{onLayout:function onLayout(e){return _this7.onContentLayout(e);},style:{zIndex:2,transform:[{translateX:this._translateX}]},__source:{fileName:_jsxFileName,lineNumber:402,columnNumber:4}}),this.renderVisibleContent());}},{key:"render",value:function render(){return _react.default.createElement(_reactNative.View,{style:this.props.style?this.props.style:styles.container,__source:{fileName:_jsxFileName,lineNumber:545,columnNumber:4}},_react.default.createElement(_reactNative.View,{style:[styles.hidden,{height:this.state.hiddenHeight,width:this.state.hiddenWidth}],__source:{fileName:_jsxFileName,lineNumber:546,columnNumber:5}},this.renderHiddenContent()),this.renderRowContent());}}]);return SwipeAction;}(_react.Component);var styles=_reactNative.StyleSheet.create({container:{},hidden:{zIndex:1,bottom:0,left:0,overflow:'hidden',position:'absolute',right:0,top:0},standaloneRowBack:{backgroundColor:_core.Colors.white,alignItems:'center',flex:1,flexDirection:'row',justifyContent:'space-between'},leftItemRow:{alignItems:'center',flex:1,flexDirection:'row',justifyContent:'flex-start',paddingRight:10},rightItemRow:{alignItems:'center',flex:1,flexDirection:'row',justifyContent:'flex-end',paddingLeft:10},buttonContainer:{backgroundColor:'red',alignItems:'center',justifyContent:'center',width:88},icon:{height:24,width:24,tintColor:'white'},text:{color:_core.Colors.white,textAlign:'center',marginHorizontal:10,marginTop:10}});SwipeAction.propTypes={onOpen:_propTypes.default.func,closeOnPress:_propTypes.default.bool,disableRightAction:_propTypes.default.bool,disableLeftAction:_propTypes.default.bool,onClose:_propTypes.default.func,style:_propTypes.default.oneOfType([_propTypes.default.object,_propTypes.default.array]),leftAction:_propTypes.default.array,rightAction:_propTypes.default.array,renderRightAction:_propTypes.default.func,renderLeftAction:_propTypes.default.func,onPress:_propTypes.default.func,children:_propTypes.default.element.isRequired,actionBackground:_propTypes.default.string,actionTextStyle:_propTypes.default.object};SwipeAction.defaultProps={closeOnPress:true,disableRightAction:true,disableLeftAction:true,recalculateHiddenLayout:false,disableHiddenLayoutCalculation:false,preview:false,previewDuration:300,previewOpenDelay:DEFAULT_PREVIEW_OPEN_DELAY,directionalDistanceChangeThreshold:2,swipeToOpenPercent:50,swipeToOpenVelocityContribution:0,swipeToClosePercent:50,swipeToPerformActionPercent:50,item:{},useNativeDriver:true,rightAction:[],leftAction:[],actionBackground:_core.Colors.white};var _default=SwipeAction;exports.default=_default;
1
+ /* eslint-disable react/default-props-match-prop-types */
2
+ /* eslint-disable react/jsx-closing-tag-location */
3
+ /* eslint-disable react/jsx-indent-props */
4
+ /* eslint-disable indent */
5
+ /* eslint-disable react/jsx-indent */
6
+ /* eslint-disable no-unused-vars */
7
+ /* eslint-disable no-mixed-spaces-and-tabs */
8
+ /* eslint-disable no-lonely-if */
9
+ /* eslint-disable no-tabs */
10
+ /* eslint-disable no-unused-expressions */
11
+ /* eslint-disable react/destructuring-assignment */
12
+
13
+ import React, {
14
+ Component,
15
+ } from 'react';
16
+ import PropTypes from 'prop-types';
17
+ import {
18
+ Dimensions,
19
+ Animated,
20
+ PanResponder,
21
+ StyleSheet,
22
+ TouchableOpacity,
23
+ View,
24
+ Image
25
+ } from 'react-native';
26
+ import { Colors, ValueUtil, Text } from '@momo-kits/core';
27
+
28
+ const DEFAULT_PREVIEW_OPEN_DELAY = 700;
29
+ const PREVIEW_CLOSE_DELAY = 300;
30
+ const MAX_VELOCITY_CONTRIBUTION = 5;
31
+ const SCROLL_LOCK_MILLISECONDS = 300;
32
+ const SCREEN_WIDTH = Dimensions.get('window').width;
33
+ const WIDTH_HIDDEN_RIGHT = 190;
34
+ class SwipeAction extends Component {
35
+ constructor(props) {
36
+ super(props);
37
+ this.isOpen = false;
38
+ this.previousTrackedTranslateX = 0;
39
+ this.previousTrackedDirection = null;
40
+ this.horizontalSwipeGestureBegan = false;
41
+ this.swipeInitialX = null;
42
+ this.parentScrollEnabled = true;
43
+ this.ranPreview = false;
44
+ this._ensureScrollEnabledTimer = null;
45
+ this.isForceClosing = false;
46
+ this.state = {
47
+ dimensionsSet: false,
48
+ hiddenHeight: this.props.disableHiddenLayoutCalculation ? '100%' : 0,
49
+ hiddenWidth: this.props.disableHiddenLayoutCalculation ? '100%' : 0,
50
+ isLeftActionVisible: false,
51
+ isRightActionVisible: false,
52
+ leftOpenValue: 0,
53
+ rightOpenValue: 0
54
+ };
55
+ const { leftAction, rightAction } = this.props;
56
+ const buttonCellWidth = WIDTH_HIDDEN_RIGHT / 2;
57
+ // leftAction ? this.leftOpenValue = leftAction.length * buttonCellWidth : 0;
58
+ // rightAction ? this.rightOpenValue = -rightAction.length * buttonCellWidth : 0;
59
+ this.stopLeftSwipe = SCREEN_WIDTH;
60
+ this.stopRightSwipe = -SCREEN_WIDTH;
61
+ this._translateX = new Animated.Value(0);
62
+ if (this.props.onSwipeValueChange) {
63
+ this._translateX.addListener(({ value }) => {
64
+ let direction = this.previousTrackedDirection;
65
+ if (value !== this.previousTrackedTranslateX) {
66
+ direction = value > this.previousTrackedTranslateX ? 'right' : 'left';
67
+ }
68
+ this.props.onSwipeValueChange && this.props.onSwipeValueChange({
69
+ isOpen: this.isOpen,
70
+ direction,
71
+ value,
72
+ });
73
+ this.previousTrackedTranslateX = value;
74
+ this.previousTrackedDirection = direction;
75
+ });
76
+ }
77
+
78
+ if (this.props.forceCloseToRightThreshold && this.props.forceCloseToRightThreshold > 0) {
79
+ this._translateX.addListener(({ value }) => {
80
+ if (!this.isForceClosing && (SCREEN_WIDTH + value) < this.props.forceCloseToRightThreshold) {
81
+ this.isForceClosing = true;
82
+ this.forceCloseRow('right');
83
+ if (this.props.onForceCloseToRight) {
84
+ this.props.onForceCloseToRight();
85
+ }
86
+ }
87
+ });
88
+ }
89
+
90
+ if (this.props.forceCloseToLeftThreshold && this.props.forceCloseToLeftThreshold > 0) {
91
+ this._translateX.addListener(({ value }) => {
92
+ if (!this.isForceClosing && (SCREEN_WIDTH - value) < this.props.forceCloseToLeftThreshold) {
93
+ this.isForceClosing = true;
94
+ this.forceCloseRow('left');
95
+ if (this.props.onForceCloseToLeft) {
96
+ this.props.onForceCloseToLeft();
97
+ }
98
+ }
99
+ });
100
+ }
101
+ }
102
+
103
+ UNSAFE_componentWillMount() {
104
+ this._panResponder = PanResponder.create({
105
+ onMoveShouldSetPanResponder: (e, gs) => this.handleOnMoveShouldSetPanResponder(e, gs),
106
+ onPanResponderMove: (e, gs) => this.handlePanResponderMove(e, gs),
107
+ onPanResponderRelease: (e, gs) => this.handlePanResponderEnd(e, gs),
108
+ onPanResponderTerminate: (e, gs) => this.handlePanResponderEnd(e, gs),
109
+ onShouldBlockNativeResponder: (_) => false,
110
+ });
111
+ }
112
+
113
+ componentWillUnmount() {
114
+ clearTimeout(this._ensureScrollEnabledTimer);
115
+ this._translateX.removeAllListeners();
116
+ }
117
+
118
+ shouldComponentUpdate(nextProps, nextState) {
119
+ if (this.state.hiddenHeight !== nextState.hiddenHeight
120
+ || this.state.hiddenWidth !== nextState.hiddenWidth
121
+ || !this.props.shouldItemUpdate
122
+ || (this.props.shouldItemUpdate && this.props.shouldItemUpdate(this.props.item, nextProps.item))) {
123
+ return true;
124
+ }
125
+
126
+ return false;
127
+ }
128
+
129
+ getPreviewAnimation(toValue, delay) {
130
+ return Animated.timing(
131
+ this._translateX,
132
+ {
133
+ duration: this.props.previewDuration, toValue, delay, useNativeDriver: this.props.useNativeDriver
134
+ }
135
+ );
136
+ }
137
+
138
+ onContentLayout(e) {
139
+ this.setState({
140
+ dimensionsSet: !this.props.recalculateHiddenLayout,
141
+ ...(!this.props.disableHiddenLayoutCalculation ? {
142
+ hiddenHeight: e.nativeEvent.layout.height,
143
+ hiddenWidth: e.nativeEvent.layout.width,
144
+ } : {})
145
+ });
146
+
147
+ if (this.props.preview && !this.ranPreview) {
148
+ this.ranPreview = true;
149
+ const previewOpenValue = this.props.previewOpenValue || this.state.rightOpenValue * 0.5;
150
+ this.getPreviewAnimation(previewOpenValue, this.props.previewOpenDelay)
151
+ .start((_) => {
152
+ this.getPreviewAnimation(0, PREVIEW_CLOSE_DELAY).start();
153
+ });
154
+ }
155
+ }
156
+
157
+ onPress() {
158
+ if (this.swipeInitialX == null || this.swipeInitialX === 0) {
159
+ if (this.props.onPress && typeof this.props.onPress === 'function') {
160
+ this.props.onPress();
161
+ }
162
+ } else {
163
+ if (this.props.closeOnPress) {
164
+ this.closeRow();
165
+ }
166
+ }
167
+ }
168
+
169
+ handleOnMoveShouldSetPanResponder(e, gs) {
170
+ const { dx } = gs;
171
+ return Math.abs(dx) > this.props.directionalDistanceChangeThreshold;
172
+ }
173
+
174
+ handlePanResponderMove(e, gestureState) {
175
+ /* If the view is force closing, then ignore Moves. Return */
176
+ if (this.isForceClosing) {
177
+ return;
178
+ }
179
+
180
+ /* Else, do normal job */
181
+ const { dx, dy } = gestureState;
182
+ const absDx = Math.abs(dx);
183
+ const absDy = Math.abs(dy);
184
+
185
+ // this check may not be necessary because we don't capture the move until we pass the threshold
186
+ // just being extra safe here
187
+ if (absDx > this.props.directionalDistanceChangeThreshold || absDy > this.props.directionalDistanceChangeThreshold) {
188
+ // we have enough to determine direction
189
+ if (absDy > absDx && !this.horizontalSwipeGestureBegan) {
190
+ // user is moving vertically, do nothing, listView will handle
191
+ return;
192
+ }
193
+
194
+ // user is moving horizontally
195
+ if (this.parentScrollEnabled) {
196
+ // disable scrolling on the listView parent
197
+ this.parentScrollEnabled = false;
198
+ this.props.setScrollEnabled && this.props.setScrollEnabled(false);
199
+ }
200
+
201
+ if (this.swipeInitialX === null) {
202
+ // set tranlateX value when user started swiping
203
+ this.swipeInitialX = this._translateX._value;
204
+ }
205
+ if (!this.horizontalSwipeGestureBegan) {
206
+ this.horizontalSwipeGestureBegan = true;
207
+ const direction = dx > 0 ? 'TO_RIGHT' : 'TO_LEFT';
208
+ this.props.swipeGestureBegan && this.props.swipeGestureBegan(direction);
209
+ }
210
+
211
+ let newDX = this.swipeInitialX + dx;
212
+ if (this.props.disableRightAction && newDX < 0) { newDX = 0; }
213
+ if (this.props.disableLeftAction && newDX > 0) { newDX = 0; }
214
+
215
+ if (this.stopLeftSwipe && newDX > this.stopLeftSwipe) { newDX = this.stopLeftSwipe; }
216
+ if (this.stopRightSwipe && newDX < this.stopRightSwipe) { newDX = this.stopRightSwipe; }
217
+
218
+ // set action buttons visibility
219
+ if (newDX > 0) {
220
+ this.setState({
221
+ isLeftActionVisible: true,
222
+ isRightActionVisible: false
223
+ });
224
+ } else if (newDX < 0) {
225
+ this.setState({
226
+ isRightActionVisible: true,
227
+ isLeftActionVisible: false,
228
+ });
229
+ }
230
+
231
+ this._translateX.setValue(newDX);
232
+ }
233
+ }
234
+
235
+ ensureScrollEnabled = () => {
236
+ if (!this.parentScrollEnabled) {
237
+ this.parentScrollEnabled = true;
238
+ this.props.setScrollEnabled && this.props.setScrollEnabled(true);
239
+ }
240
+ }
241
+
242
+ handlePanResponderEnd(e, gestureState) {
243
+ /* PandEnd will reset the force-closing state when it's true. */
244
+ if (this.isForceClosing) {
245
+ this.isForceClosing = false;
246
+ }
247
+ // decide how much the velocity will affect the final position that the list item settles in.
248
+ const { swipeToOpenVelocityContribution } = this.props;
249
+ const possibleExtraPixels = this.state.rightOpenValue * (swipeToOpenVelocityContribution);
250
+ const clampedVelocity = Math.min(gestureState.vx, MAX_VELOCITY_CONTRIBUTION);
251
+ const projectedExtraPixels = possibleExtraPixels * (clampedVelocity / MAX_VELOCITY_CONTRIBUTION);
252
+
253
+ // re-enable scrolling on listView parent
254
+ this._ensureScrollEnabledTimer = setTimeout(this.ensureScrollEnabled, SCROLL_LOCK_MILLISECONDS);
255
+
256
+ // finish up the animation
257
+ let toValue = 0;
258
+ if (this._translateX._value >= 0) {
259
+ // trying to swipe right
260
+ if (this.swipeInitialX < this._translateX._value) {
261
+ if ((this._translateX._value - projectedExtraPixels) > this.state.leftOpenValue * (this.props.swipeToOpenPercent / 100)) {
262
+ // we're more than halfway
263
+ toValue = this.state.leftOpenValue;
264
+ }
265
+ } else if ((this._translateX._value - projectedExtraPixels) > this.state.leftOpenValue * (1 - (this.props.swipeToClosePercent / 100))) {
266
+ toValue = this.state.leftOpenValue;
267
+ }
268
+ } else {
269
+ // trying to swipe left
270
+ if (this.swipeInitialX > this._translateX._value) {
271
+ if ((this._translateX._value - projectedExtraPixels) < this.state.rightOpenValue * (this.props.swipeToOpenPercent / 100)) {
272
+ // we're more than halfway
273
+ toValue = this.state.rightOpenValue;
274
+ }
275
+ } else if ((this._translateX._value - projectedExtraPixels) < this.state.rightOpenValue * (1 - (this.props.swipeToClosePercent / 100))) {
276
+ toValue = this.state.rightOpenValue;
277
+ }
278
+ }
279
+
280
+ this.props.swipeGestureEnd && this.props.swipeGestureEnd();
281
+ this.manuallySwipeRow(toValue);
282
+ }
283
+
284
+ /*
285
+ * This method is called by SwipeListView
286
+ */
287
+ closeRow() {
288
+ this.manuallySwipeRow(0);
289
+ }
290
+
291
+ /**
292
+ * Force close the row toward the end of the given direction.
293
+ * @param {String} direction The direction to force close.
294
+ */
295
+ forceCloseRow(direction) {
296
+ this.manuallySwipeRow(0, () => {
297
+ if (direction === 'right' && this.props.onForceCloseToRightEnd) {
298
+ this.props.onForceCloseToRightEnd();
299
+ } else if (direction === 'left' && this.props.onForceCloseToLeftEnd) {
300
+ this.props.onForceCloseToLeftEnd();
301
+ }
302
+ });
303
+ }
304
+
305
+ closeRowWithoutAnimation() {
306
+ this._translateX.setValue(0);
307
+
308
+ this.ensureScrollEnabled();
309
+ this.isOpen = false;
310
+ this.props.onDidClose && this.props.onDidClose();
311
+
312
+ this.props.onClose && this.props.onClose();
313
+
314
+ this.swipeInitialX = null;
315
+ this.horizontalSwipeGestureBegan = false;
316
+ }
317
+
318
+ manuallySwipeRow(toValue, onAnimationEnd) {
319
+ Animated.spring(
320
+ this._translateX,
321
+ {
322
+ toValue,
323
+ friction: this.props.friction,
324
+ tension: this.props.tension,
325
+ useNativeDriver: this.props.useNativeDriver,
326
+ }
327
+ ).start((_) => {
328
+ this._translateX.setValue(toValue);
329
+ this.ensureScrollEnabled();
330
+ if (toValue === 0) {
331
+ this.isOpen = false;
332
+ this.props.onDidClose && this.props.onDidClose();
333
+ } else {
334
+ this.isOpen = true;
335
+ this.props.onDidOpen && this.props.onDidOpen(toValue);
336
+ }
337
+ if (onAnimationEnd) {
338
+ onAnimationEnd();
339
+ }
340
+ });
341
+
342
+ if (toValue === 0) {
343
+ this.props.onClose && this.props.onClose();
344
+ } else {
345
+ this.props.onOpen && this.props.onOpen(toValue);
346
+ }
347
+
348
+ // reset everything
349
+ this.swipeInitialX = toValue;
350
+ this.horizontalSwipeGestureBegan = false;
351
+ }
352
+
353
+ renderVisibleContent() {
354
+ if (this.props.children) {
355
+ // handle touchables
356
+ const { onPress } = this.props.children.props;
357
+
358
+ if (onPress) {
359
+ const newOnPress = (_) => {
360
+ this.onPress();
361
+ onPress();
362
+ };
363
+ return React.cloneElement(
364
+ this.props.children,
365
+ {
366
+ ...this.props.children.props,
367
+ onPress: newOnPress
368
+ }
369
+ );
370
+ }
371
+ return (
372
+ <TouchableOpacity
373
+ activeOpacity={1}
374
+ onPress={(_) => this.onPress()}
375
+ >
376
+ {this.props.children}
377
+ </TouchableOpacity>
378
+ );
379
+ }
380
+ }
381
+
382
+ renderRowContent() {
383
+ // We do this annoying if statement for performance.
384
+ // We don't want the onLayout func to run after it runs once.
385
+ if (this.state.dimensionsSet) {
386
+ return (
387
+ <Animated.View
388
+ manipulationModes={['translateX']}
389
+ {...this._panResponder.panHandlers}
390
+ style={{
391
+ zIndex: 2,
392
+ transform: [
393
+ { translateX: this._translateX }
394
+ ]
395
+ }}
396
+ >
397
+ {this.renderVisibleContent()}
398
+ </Animated.View>
399
+ );
400
+ }
401
+ return (
402
+ <Animated.View
403
+ manipulationModes={['translateX']}
404
+ {...this._panResponder.panHandlers}
405
+ onLayout={(e) => this.onContentLayout(e)}
406
+ style={{
407
+ zIndex: 2,
408
+ transform: [
409
+ { translateX: this._translateX }
410
+ ]
411
+ }}
412
+ >
413
+ {this.renderVisibleContent()}
414
+ </Animated.View>
415
+ );
416
+ }
417
+
418
+ onLeftActionLayout = ({ nativeEvent }) => {
419
+ this.setState({
420
+ leftOpenValue: nativeEvent.layout.width
421
+ });
422
+ }
423
+
424
+ onRightActionLayout = ({ nativeEvent }) => {
425
+ this.setState({
426
+ rightOpenValue: -nativeEvent.layout.width
427
+ });
428
+ }
429
+
430
+ renderHiddenContent = () => {
431
+ const {
432
+ actionBackground, rowIndex, rightAction, leftAction
433
+ } = this.props;
434
+ const hiddenRowStyle = {
435
+ ...styles.standaloneRowBack,
436
+ ...{ backgroundColor: actionBackground }
437
+ };
438
+ return (
439
+ <View style={hiddenRowStyle}>
440
+ {
441
+ this.state.isLeftActionVisible
442
+ && (
443
+ <View style={styles.leftItemRow}>
444
+ <View style={{ flexDirection: 'row' }} onLayout={this.onLeftActionLayout}>
445
+ {
446
+ leftAction?.map?.((item, index) => this.renderLeftAction(item, index))
447
+ }
448
+ </View>
449
+ </View>
450
+ )
451
+ }
452
+ {
453
+ this.state.isRightActionVisible
454
+ && (
455
+ <View style={styles.rightItemRow}>
456
+ <View style={{ flexDirection: 'row' }} onLayout={this.onRightActionLayout}>
457
+ {
458
+ rightAction?.slice?.(0)?.reverse().map((item, index) => this.renderRightAction(item, index))
459
+ }
460
+ </View>
461
+ </View>
462
+ )
463
+ }
464
+
465
+ </View>
466
+ );
467
+ }
468
+
469
+ renderIcon = (icon) => {
470
+ const { actionIconStyle } = this.props;
471
+ const iconSource = ValueUtil.getImageSource(icon);
472
+ return (
473
+ <Image
474
+ source={iconSource}
475
+ resizeMode="contain"
476
+ style={[styles.icon, actionIconStyle]}
477
+ />
478
+ );
479
+ }
480
+
481
+ renderTitle = (title) => {
482
+ const { actionTextStyle } = this.props;
483
+ return (
484
+ <Text.SubTitle style={[styles.text, actionTextStyle]}>
485
+ {title}
486
+ </Text.SubTitle>
487
+ );
488
+ }
489
+
490
+ onActionPress = (item, rowIndex) => () => {
491
+ const { onPress } = item;
492
+ onPress?.(item, rowIndex);
493
+ }
494
+
495
+ renderRightAction = (item, index) => {
496
+ const { rowIndex, renderRightAction } = this.props;
497
+ if (renderRightAction) {
498
+ return (
499
+ <View key={item + index}>
500
+ {renderRightAction({ item, index, rowIndex })}
501
+ </View>
502
+ );
503
+ }
504
+ return (
505
+ <TouchableOpacity
506
+ key={index.toString()}
507
+ style={[
508
+ styles.buttonContainer,
509
+ { backgroundColor: item.color, height: this.state.hiddenHeight }
510
+ ]}
511
+ onPress={this.onActionPress(item, rowIndex)}
512
+ >
513
+ {item.icon && this.renderIcon(item.icon)}
514
+ {item.title && this.renderTitle(item.title)}
515
+ </TouchableOpacity>
516
+ );
517
+ }
518
+
519
+ renderLeftAction = (item, index) => {
520
+ const { rowIndex, renderLeftAction } = this.props;
521
+ if (renderLeftAction) {
522
+ return (
523
+ <View key={item + index}>
524
+ {renderLeftAction({ item, index, rowIndex })}
525
+ </View>
526
+ );
527
+ }
528
+ return (
529
+ <TouchableOpacity
530
+ key={index.toString()}
531
+ style={[
532
+ styles.buttonContainer,
533
+ { backgroundColor: item.color, height: this.state.hiddenHeight }
534
+ ]}
535
+ onPress={this.onActionPress(item, rowIndex)}
536
+ >
537
+ {item.icon && this.renderIcon(item.icon)}
538
+ {item.title && this.renderTitle(item.title)}
539
+ </TouchableOpacity>
540
+ );
541
+ }
542
+
543
+ render() {
544
+ return (
545
+ <View style={this.props.style ? this.props.style : styles.container}>
546
+ <View style={[
547
+ styles.hidden,
548
+ {
549
+ height: this.state.hiddenHeight,
550
+ width: this.state.hiddenWidth,
551
+ }
552
+ ]}
553
+ >
554
+ {this.renderHiddenContent()}
555
+ </View>
556
+ {this.renderRowContent()}
557
+ </View>
558
+ );
559
+ }
560
+ }
561
+
562
+ const styles = StyleSheet.create({
563
+ container: {
564
+ // As of RN 0.29 flex: 1 is causing all rows to be the same height
565
+ // flex: 1
566
+ },
567
+ hidden: {
568
+ zIndex: 1,
569
+ bottom: 0,
570
+ left: 0,
571
+ overflow: 'hidden',
572
+ position: 'absolute',
573
+ right: 0,
574
+ top: 0,
575
+ },
576
+ standaloneRowBack: {
577
+ backgroundColor: Colors.white,
578
+ alignItems: 'center',
579
+ flex: 1,
580
+ flexDirection: 'row',
581
+ justifyContent: 'space-between'
582
+ },
583
+ leftItemRow: {
584
+ alignItems: 'center',
585
+ flex: 1,
586
+ flexDirection: 'row',
587
+ justifyContent: 'flex-start',
588
+ paddingRight: 10
589
+ },
590
+ rightItemRow: {
591
+ alignItems: 'center',
592
+ flex: 1,
593
+ flexDirection: 'row',
594
+ justifyContent: 'flex-end',
595
+ paddingLeft: 10,
596
+ },
597
+ buttonContainer: {
598
+ backgroundColor: 'red',
599
+ alignItems: 'center',
600
+ justifyContent: 'center',
601
+ width: 88
602
+ },
603
+ icon: {
604
+ height: 24,
605
+ width: 24,
606
+ tintColor: 'white'
607
+ },
608
+ text: {
609
+ color: Colors.white,
610
+ textAlign: 'center',
611
+ marginHorizontal: 10,
612
+ marginTop: 10
613
+ }
614
+ });
615
+
616
+ SwipeAction.propTypes = {
617
+ onOpen: PropTypes.func,
618
+ closeOnPress: PropTypes.bool,
619
+ disableRightAction: PropTypes.bool,
620
+ disableLeftAction: PropTypes.bool,
621
+ onClose: PropTypes.func,
622
+ style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
623
+ leftAction: PropTypes.array,
624
+ rightAction: PropTypes.array,
625
+ renderRightAction: PropTypes.func,
626
+ renderLeftAction: PropTypes.func,
627
+ onPress: PropTypes.func,
628
+ children: PropTypes.element.isRequired,
629
+ actionBackground: PropTypes.string,
630
+ actionTextStyle: PropTypes.object,
631
+ swipeGestureBegan: PropTypes.func,
632
+ swipeGestureEnd: PropTypes.func,
633
+ };
634
+
635
+ SwipeAction.defaultProps = {
636
+ closeOnPress: true,
637
+ disableRightAction: true,
638
+ disableLeftAction: true,
639
+ recalculateHiddenLayout: false,
640
+ disableHiddenLayoutCalculation: false,
641
+ preview: false,
642
+ previewDuration: 300,
643
+ previewOpenDelay: DEFAULT_PREVIEW_OPEN_DELAY,
644
+ directionalDistanceChangeThreshold: 2,
645
+ swipeToOpenPercent: 50,
646
+ swipeToOpenVelocityContribution: 0,
647
+ swipeToClosePercent: 50,
648
+ swipeToPerformActionPercent: 50,
649
+ item: {},
650
+ useNativeDriver: true,
651
+ rightAction: [],
652
+ leftAction: [],
653
+ actionBackground: Colors.white
654
+ };
655
+
656
+ export default SwipeAction;