react-native-swipeout-component
Version:
`React Native Swipeout` is a customizable swipeable row component for React Native, allowing you to create swipe actions like deleting, archiving, or other actions you might want to trigger with a swipe.
508 lines (501 loc) • 17.5 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
Swipeout: () => SwipeoutComponent_default
});
module.exports = __toCommonJS(src_exports);
// src/SwipeoutComponent.tsx
var import_react2 = __toESM(require("react"));
var import_react_native2 = require("react-native");
var import_prop_types = __toESM(require("prop-types"));
// src/SwipeoutBtn.tsx
var import_react = __toESM(require("react"));
var import_react_native = require("react-native");
var SwipeoutBtn = ({
backgroundColor,
color,
component,
onPress,
text,
type,
underlayColor,
height = 0,
width = 0,
disabled = false
}) => {
const styleSwipeoutBtn = [styles.swipeoutBtn];
if (type === "delete") styleSwipeoutBtn.push(styles.colorDelete);
else if (type === "primary") styleSwipeoutBtn.push(styles.colorPrimary);
else if (type === "secondary") styleSwipeoutBtn.push(styles.colorSecondary);
if (backgroundColor) styleSwipeoutBtn.push({ backgroundColor });
styleSwipeoutBtn.push({
height,
width
});
const styleSwipeoutBtnComponent = [{
height,
width
}];
const styleSwipeoutBtnText = [styles.swipeoutBtnText];
if (color) styleSwipeoutBtnText.push({ color });
return /* @__PURE__ */ import_react.default.createElement(
import_react_native.TouchableOpacity,
{
onPress,
disabled,
style: [styles.swipeoutBtnTouchable, styleSwipeoutBtn],
activeOpacity: underlayColor ? 1 : 0.85,
underlayColor
},
component ? /* @__PURE__ */ import_react.default.createElement(import_react_native.View, { style: styleSwipeoutBtnComponent }, component) : /* @__PURE__ */ import_react.default.createElement(import_react_native.Text, { style: styleSwipeoutBtnText }, text)
);
};
var styles = import_react_native.StyleSheet.create({
swipeoutBtn: {
justifyContent: "center",
alignItems: "center"
},
swipeoutBtnText: {
color: "#ffffff",
alignSelf: "center"
},
swipeoutBtnTouchable: {
flex: 1,
justifyContent: "center",
alignItems: "center"
},
colorDelete: {
backgroundColor: "red"
},
colorPrimary: {
backgroundColor: "blue"
},
colorSecondary: {
backgroundColor: "gray"
}
});
var SwipeoutBtn_default = SwipeoutBtn;
// src/SwipeoutComponent.tsx
var import_lodash = require("lodash");
var Swipeout = class extends import_react2.Component {
constructor(props) {
super(props);
this.swipeoutContent = null;
this._handleSwipe = () => {
if (this.props.autoOpenRight && !this.props.disabled && this.props.buttonWidth) {
this._autoOpenRight();
}
if (this.props.autoOpenLeft && !this.props.disabled && this.props.buttonWidth) {
this._autoOpenLeft();
}
};
/**
* _autoOpenBoth = () => {
this._autoOpenRight()
setTimeout(() => {
this.setState({
contentPos: 0,
btnsLeftWidth: 0
});
}, this.props.timeout || 2000)
this._autoOpenLeft()
}
*/
this._autoOpenRight = () => {
setTimeout(() => {
this.setState({
contentPos: -this.props.buttonWidth / 2,
btnsRightWidth: this.props.buttonWidth / 2
});
}, 500);
setTimeout(() => {
this.setState({
contentPos: 0,
btnsRightWidth: 0
});
}, this.props.timeout || 2e3);
};
this._autoOpenLeft = () => {
setTimeout(() => {
this.setState({
contentPos: this.props.buttonWidth / 2,
btnsLeftWidth: this.props.left ? this.props.buttonWidth / 2 * this.props.left.length : 0
});
}, 500);
setTimeout(() => {
this.setState({
contentPos: 0,
btnsLeftWidth: 0
});
}, this.props.timeout || 2e3);
};
this._debouncedOnCTA = (0, import_lodash.debounce)(() => this.props.onCTA(), 300);
this._handlePanResponderGrant = () => {
var _a;
if (this.props.disabled) return;
if (!this.state.openedLeft && !this.state.openedRight) {
this._callOnOpen();
} else {
this._callOnClose();
}
(_a = this.swipeoutContent) == null ? void 0 : _a.measure((ox, oy, width) => {
let buttonWidth = width / 5;
this.setState({
btnWidth: buttonWidth,
btnsLeftWidth: this.props.left ? buttonWidth * this.props.left.length : 0,
btnsRightWidth: this.props.right ? buttonWidth * this.props.right.length : 0,
swiping: true,
timeStart: (/* @__PURE__ */ new Date()).getTime()
});
});
};
this._handlePanResponderMove = (event, gestureState) => {
if (this.props.disabled) return;
let posX = gestureState.dx;
let posY = gestureState.dy;
const leftWidth = this.state.btnsLeftWidth;
const rightWidth = this.state.btnsRightWidth;
if (this.state.openedRight) posX = gestureState.dx - rightWidth;
else if (this.state.openedLeft) posX = gestureState.dx + leftWidth;
const moveX = Math.abs(posX) > Math.abs(posY);
if (this.props.scroll) {
if (moveX) this.props.scroll(false);
else this.props.scroll(true);
}
if (this.state.swiping) {
if (posX < 0 && this.props.right) {
this.setState({
backgroundColor: this.props.rightBackgroundColor
});
if (this.props.dragToCTA && this.props.right.length == 1 && -(rightWidth + 150) >= Math.round(Math.min(posX, 0))) {
return this._debouncedOnCTA();
}
this.setState({ contentPos: Math.min(posX, 0) });
} else if (posX > 0 && this.props.left) {
this.setState({
backgroundColor: this.props.leftBackgroundColor
});
if (this.props.dragToCTA && this.props.left.length == 1 && leftWidth + 70 <= Math.round(Math.max(posX, 0))) {
return this._debouncedOnCTA();
}
this.setState({ contentPos: Math.max(posX, 0) });
}
}
};
this._handlePanResponderEnd = (event, gestureState) => {
if (this.props.disabled) return;
const posX = gestureState.dx;
const contentPos = this.state.contentPos;
const contentWidth = this.state.contentWidth;
const btnsLeftWidth = this.state.btnsLeftWidth;
const btnsRightWidth = this.state.btnsRightWidth;
const openX = contentWidth * 0.33;
let openLeft = posX > openX || posX > btnsLeftWidth / 2;
let openRight = posX < -openX || posX < -btnsRightWidth / 2;
if (this.state.openedRight) openRight = posX - openX < -openX;
if (this.state.openedLeft) openLeft = posX + openX > openX;
const timeDiff = (/* @__PURE__ */ new Date()).getTime() - this.state.timeStart < 200;
if (timeDiff) {
openRight = posX < -openX / 10 && !this.state.openedLeft;
openLeft = posX > openX / 10 && !this.state.openedRight;
}
if (this.state.swiping) {
if (openRight && contentPos < 0 && posX < 0) {
this._open(-btnsRightWidth, "right");
} else if (openLeft && contentPos > 0 && posX > 0) {
this._open(btnsLeftWidth, "left");
} else {
this._close();
}
}
if (this.props.scroll) this.props.scroll(true);
};
this._tweenContent = (state, endValue) => {
this.setState({ [state]: endValue });
};
this._rubberBandEasing = (value, limit) => {
if (value < 0 && value < limit) return limit - Math.pow(limit - value, 0.85);
else if (value > 0 && value > limit) return limit + Math.pow(value - limit, 0.85);
return value;
};
this._autoClose = (btn) => {
if (this.state.autoClose) this._close();
const { onPress } = btn;
if (onPress) onPress();
};
this._open = (contentPos, direction) => {
const left = direction === "left";
const { sectionID, rowID, onOpen } = this.props;
onOpen && onOpen(sectionID, rowID, direction);
this._tweenContent("contentPos", contentPos);
this.setState({
contentPos,
openedLeft: left,
openedRight: !left,
swiping: false
});
};
this._close = () => {
const { sectionID, rowID, onClose } = this.props;
if (onClose && (this.state.openedLeft || this.state.openedRight)) {
const direction = this.state.openedRight ? "right" : "left";
onClose(sectionID, rowID, direction);
}
this._tweenContent("contentPos", 0);
this._callOnClose();
this.setState({
openedRight: false,
openedLeft: false,
swiping: false
});
};
this._callOnClose = () => {
if (this.props.onClose) this.props.onClose(this.props.sectionID, this.props.rowID);
};
this._callOnOpen = () => {
if (this.props.onOpen) this.props.onOpen(this.props.sectionID, this.props.rowID);
};
this._openRight = () => {
var _a;
(_a = this.swipeoutContent) == null ? void 0 : _a.measure((ox, oy, width) => {
let btnWidth = width / 5;
this.setState({
btnWidth,
btnsRightWidth: this.props.right ? btnWidth * this.props.right.length : 0
}, () => {
this._tweenContent("contentPos", -this.state.btnsRightWidth);
this._callOnOpen();
this.setState({
contentPos: -this.state.btnsRightWidth,
openedLeft: false,
openedRight: true,
swiping: false
});
});
});
};
this._openLeft = () => {
var _a;
(_a = this.swipeoutContent) == null ? void 0 : _a.measure((ox, oy, width) => {
let btnWidth = width / 5;
this.setState({
btnWidth,
btnsLeftWidth: this.props.left ? btnWidth * this.props.left.length : 0
}, () => {
this._tweenContent("contentPos", this.state.btnsLeftWidth);
this._callOnOpen();
this.setState({
contentPos: this.state.btnsLeftWidth,
openedLeft: true,
openedRight: false,
swiping: false
});
});
});
};
this._onLayout = (event) => {
const { width, height } = event.nativeEvent.layout;
this.setState({
contentWidth: width,
contentHeight: height
});
};
this._renderButtons = (buttons, isVisible, style) => {
if (buttons && isVisible) {
return /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style }, buttons.map(this._renderButton));
} else {
return /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null);
}
};
this._renderButton = (btn, i) => {
return /* @__PURE__ */ import_react2.default.createElement(
SwipeoutBtn_default,
{
backgroundColor: btn.buttonBackgroundColor,
color: btn.color,
component: btn.component,
disabled: btn.disabled,
height: this.state.contentHeight,
key: i,
onPress: () => this._autoClose(btn),
text: btn.text,
type: btn.type,
underlayColor: btn.underlayColor,
width: this.state.btnWidth
}
);
};
this.state = {
autoClose: this.props.autoClose || false,
btnWidth: 0,
btnsLeftWidth: 0,
btnsRightWidth: 0,
contentHeight: 0,
contentPos: 0,
contentWidth: 0,
openedRight: false,
openedLeft: false,
swiping: false,
tweenDuration: 160,
timeStart: null,
backgroundColor: ""
};
this._initializePanResponder();
}
componentDidMount() {
this._initializePanResponder();
if (this.props.autoOpenRight || this.props.autoOpenLeft) {
this._handleSwipe();
}
}
_initializePanResponder() {
this._panResponder = import_react_native2.PanResponder.create({
onStartShouldSetPanResponder: () => true,
onStartShouldSetPanResponderCapture: () => this.state.openedLeft || this.state.openedRight,
onMoveShouldSetPanResponderCapture: (event, gestureState) => Math.abs(gestureState.dx) > this.props.sensitivity && Math.abs(gestureState.dy) <= this.props.sensitivity,
onPanResponderGrant: this._handlePanResponderGrant,
onPanResponderMove: this._handlePanResponderMove,
onPanResponderRelease: this._handlePanResponderEnd,
onPanResponderTerminate: this._handlePanResponderEnd,
onShouldBlockNativeResponder: () => false,
onPanResponderTerminationRequest: () => false
});
}
render() {
const { style, children, right, left } = this.props;
const contentWidth = this.state.contentWidth;
const height = this.state.contentHeight;
const posX = this.state.contentPos;
const styleSwipeout = [styles2.swipeout, style];
if (this.state.backgroundColor) {
styleSwipeout.push({ backgroundColor: this.state.backgroundColor });
}
let limit = -this.state.btnsRightWidth;
if (posX > 0) limit = this.state.btnsLeftWidth;
const styleLeftPos = {
left: {
left: 0,
overflow: "hidden",
width: Math.min(limit * (posX / limit), limit)
}
};
const styleRightPos = {
right: {
left: Math.abs(contentWidth + Math.max(limit, posX)),
right: 0
}
};
const styleContentPos = {
content: {
transform: [{ translateX: this._rubberBandEasing(posX, limit) }]
}
};
const styleContent = [styles2.swipeoutContent];
styleContent.push(styleContentPos.content);
const styleRight = [styles2.swipeoutBtns];
styleRight.push(styleRightPos.right);
const styleLeft = [styles2.swipeoutBtns];
styleLeft.push(styleLeftPos.left);
const isRightVisible = posX < 0;
const isLeftVisible = posX > 0;
return /* @__PURE__ */ import_react2.default.createElement(
import_react_native2.View,
{ style: styleSwipeout },
/* @__PURE__ */ import_react2.default.createElement(
import_react_native2.View,
__spreadValues({
ref: (node) => this.swipeoutContent = node,
style: [styleContent],
onLayout: this._onLayout
}, this._panResponder.panHandlers),
children
),
//@ts-ignore
this._renderButtons(right, isRightVisible, styleRight),
//@ts-ignore
this._renderButtons(left, isLeftVisible, styleLeft)
);
}
};
Swipeout.propTypes = {
autoClose: import_prop_types.default.bool,
buttonBackgroundColor: import_prop_types.default.string,
close: import_prop_types.default.bool,
left: import_prop_types.default.array,
onOpen: import_prop_types.default.func,
onClose: import_prop_types.default.func,
right: import_prop_types.default.array,
scroll: import_prop_types.default.func,
style: import_prop_types.default.object,
sensitivity: import_prop_types.default.number,
buttonWidth: import_prop_types.default.number,
disabled: import_prop_types.default.bool,
autoOpenRight: import_prop_types.default.bool,
onCTA: import_prop_types.default.func
};
Swipeout.defaultProps = {
disabled: false,
rowID: -1,
sectionID: -1,
sensitivity: 50
};
var styles2 = import_react_native2.StyleSheet.create({
swipeout: {
overflow: "hidden"
},
swipeoutContent: {
flex: 1
},
swipeoutBtns: {
position: "absolute",
top: 0,
bottom: 0,
flexDirection: "row"
}
});
var SwipeoutComponent_default = Swipeout;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Swipeout
});