UNPKG

@quuu/react-swipeable-list

Version:
1,309 lines (1,085 loc) 47.2 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('prop-types')) : typeof define === 'function' && define.amd ? define(['exports', 'react', 'prop-types'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@quuu/react-swipeable-list"] = {}, global.React, global.PropTypes)); })(this, (function (exports, React, PropTypes) { 'use strict'; function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes); var LeadingActions = function LeadingActions(_ref) { var children = _ref.children; if (children === null || children === undefined) { return null; } if (Array.isArray(children)) { return React__default["default"].Children.map(children, function (child, index) { if (! /*#__PURE__*/React__default["default"].isValidElement(child)) { return child; } return /*#__PURE__*/React__default["default"].cloneElement(child, { leading: true, main: index === 0 }); }); } return /*#__PURE__*/React__default["default"].cloneElement(children, { leading: true, main: true }); }; function toVal(mix) { var k, y, str=''; if (typeof mix === 'string' || typeof mix === 'number') { str += mix; } else if (typeof mix === 'object') { if (Array.isArray(mix)) { for (k=0; k < mix.length; k++) { if (mix[k]) { if (y = toVal(mix[k])) { str && (str += ' '); str += y; } } } } else { for (k in mix) { if (mix[k]) { str && (str += ' '); str += k; } } } } return str; } function clsx () { var i=0, tmp, x, str=''; while (i < arguments.length) { if (tmp = arguments[i++]) { if (x = toVal(tmp)) { str && (str += ' '); str += x; } } } return str; } var Type = { ANDROID: Symbol('ANDROID'), IOS: Symbol('IOS'), MS: Symbol('MS') }; var SwipeableList = /*#__PURE__*/React__default["default"].memo(function (_ref) { var _ref$actionDelay = _ref.actionDelay, actionDelay = _ref$actionDelay === void 0 ? 0 : _ref$actionDelay, children = _ref.children, _ref$className = _ref.className, className = _ref$className === void 0 ? '' : _ref$className, _ref$fullSwipe = _ref.fullSwipe, fullSwipe = _ref$fullSwipe === void 0 ? false : _ref$fullSwipe, _ref$destructiveCallb = _ref.destructiveCallbackDelay, destructiveCallbackDelay = _ref$destructiveCallb === void 0 ? 1000 : _ref$destructiveCallb, _ref$optOutMouseEvent = _ref.optOutMouseEvents, optOutMouseEvents = _ref$optOutMouseEvent === void 0 ? false : _ref$optOutMouseEvent, style = _ref.style, _ref$type = _ref.type, type = _ref$type === void 0 ? Type.ANDROID : _ref$type, _ref$Tag = _ref.Tag, Tag = _ref$Tag === void 0 ? 'div' : _ref$Tag, scrollStartThreshold = _ref.scrollStartThreshold, swipeStartThreshold = _ref.swipeStartThreshold, _ref$threshold = _ref.threshold, threshold = _ref$threshold === void 0 ? 0.5 : _ref$threshold; var itemsMapRef = React.useRef({}); var clickedItem = React.useCallback(function (itemId) { Object.keys(itemsMapRef.current).forEach(function (listItem) { if (listItem !== itemId) { itemsMapRef.current[listItem](); } }); }, []); return /*#__PURE__*/React__default["default"].createElement(Tag, { className: clsx('swipeable-list', className), style: style }, React__default["default"].Children.map(children, function (child, index) { return /*#__PURE__*/React__default["default"].cloneElement(child, { actionDelay: actionDelay, destructiveCallbackDelay: destructiveCallbackDelay, fullSwipe: fullSwipe, listType: type, optOutMouseEvents: optOutMouseEvents, scrollStartThreshold: scrollStartThreshold, swipeStartThreshold: swipeStartThreshold, threshold: threshold, clickedCallback: clickedItem, id: "listItem-".concat(index), resetState: function resetState(func) { itemsMapRef.current["listItem-".concat(index)] = func; } }); })); }); SwipeableList.propTypes = { actionDelay: PropTypes__default["default"].number, children: PropTypes__default["default"].node, className: PropTypes__default["default"].string, fullSwipe: PropTypes__default["default"].bool, destructiveCallbackDelay: PropTypes__default["default"].number, optOutMouseEvents: PropTypes__default["default"].bool, style: PropTypes__default["default"].object, type: PropTypes__default["default"].oneOf(Object.values(Type)), Tag: PropTypes__default["default"].string, scrollStartThreshold: PropTypes__default["default"].number, swipeStartThreshold: PropTypes__default["default"].number, threshold: PropTypes__default["default"].number }; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } Object.defineProperty(subClass, "prototype", { value: Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }), writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 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; } } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } var ItemContext = /*#__PURE__*/React__default["default"].createContext(); var ActionAnimation = { RETURN: Symbol('Return'), REMOVE: Symbol('Remove'), NONE: Symbol('None') }; var DragDirection = { UP: 'up', DOWN: 'down', LEFT: 'left', RIGHT: 'right', UNKNOWN: 'unknown' }; var measure = function measure(element, fn) { var prevWidth = element.style.width; var prevVisibility = element.style.visibility; element.style.width = 'auto'; element.style.visibility = 'hidden'; var result = fn(element); element.style.width = prevWidth; element.style.visibility = prevVisibility; return result; }; var initialState = { leadingFullSwipe: false, trailingFullSwipe: false, triggerAction: false, scaleLeading: false, scaleTrailing: false, swipeProgress: 0, dragOffset: 0 }; var SwipeableListItem = /*#__PURE__*/function (_PureComponent) { _inherits(SwipeableListItem, _PureComponent); var _super = _createSuper(SwipeableListItem); function SwipeableListItem(props) { var _this; _classCallCheck(this, SwipeableListItem); _this = _super.call(this, props); _defineProperty(_assertThisInitialized(_this), "setLeadingFullSwipeAction", function (action) { _this.leadingFullSwipeAction = action; }); _defineProperty(_assertThisInitialized(_this), "setTrailingFullSwipeAction", function (action) { _this.trailingFullSwipeAction = action; }); _defineProperty(_assertThisInitialized(_this), "resetState", function () { _this.dragStartPoint = { x: -1, y: -1 }; _this.dragDirection = DragDirection.UNKNOWN; _this.left = 0; _this.previousSwipeDistancePercent = 0; _this.leadingActionsOpened = false; _this.trailingActionsOpened = false; _this.setState({ swipeProgress: 0, dragOffset: 0 }); }); _defineProperty(_assertThisInitialized(_this), "handleDragStartMouse", function (event) { window.addEventListener('mouseup', _this.handleDragEndMouse); window.addEventListener('mousemove', _this.handleMouseMove); _this.listElement.addEventListener('mouseup', _this.handleDragEndMouse); _this.listElement.addEventListener('mousemove', _this.handleMouseMove); _this.handleDragStart(event); }); _defineProperty(_assertThisInitialized(_this), "handleDragStartTouch", function (event) { window.addEventListener('touchend', _this.handleDragEndTouch, { passive: true }); var touch = event.targetTouches[0]; _this.handleDragStart(touch); }); _defineProperty(_assertThisInitialized(_this), "handleDragStart", function (_ref) { var clientX = _ref.clientX, clientY = _ref.clientY; if (_this.props.clickedCallback) { _this.props.clickedCallback(_this.id); } if (!_this.leadingActionsOpened && !_this.trailingActionsOpened) { _this.resetState(); _this.setState(initialState); } var startOffsetX = 0; if (_this.leadingActionsOpened) { startOffsetX = -_this.leadingActionsWidth / _this.swipeSensitivity; } if (_this.trailingActionsOpened) { startOffsetX = _this.trailingActionsWidth / _this.swipeSensitivity; } _this.dragStartPoint = { x: clientX + startOffsetX, y: clientY }; // Use CSS classes for GPU-accelerated animations _this.listElement.style.willChange = 'transform'; _this.listElement.style.pointerEvents = 'none'; // Disable pointer events during drag _this.listElement.className = 'swipeable-list-item__content'; if (_this.leadingActionsElement) { _this.leadingActionsElement.style.willChange = 'width'; _this.leadingActionsElement.className = 'swipeable-list-item__leading-actions'; } if (_this.trailingActionsElement) { _this.trailingActionsElement.style.willChange = 'width'; _this.trailingActionsElement.className = 'swipeable-list-item__trailing-actions'; } _this.startTime = Date.now(); // Re-add timing for animation decisions _this.scheduleUpdatePosition(); }); _defineProperty(_assertThisInitialized(_this), "handleMouseMove", function (event) { if (_this.dragStartedWithinItem()) { var clientX = event.clientX, clientY = event.clientY; _this.setDragDirection(clientX, clientY); if (_this.isSwiping()) { event.stopPropagation(); event.preventDefault(); var rawLeft = clientX - _this.dragStartPoint.x; // Apply sensitivity multiplier for snappier response var newLeft = rawLeft * _this.swipeSensitivity; if (newLeft > 0) { _this.left = Math.min(newLeft, _this.maxSwipeThreshold); } else { _this.left = Math.max(newLeft, -_this.maxSwipeThreshold); } _this.scheduleUpdatePosition(); } } }); _defineProperty(_assertThisInitialized(_this), "handleTouchMove", function (event) { if (!_this.dragStartedWithinItem()) { return; } var touch = event.targetTouches[0]; if (!touch) return; var clientX = touch.clientX, clientY = touch.clientY; _this.setDragDirection(clientX, clientY); if (!_this.isSwiping()) { return; } if (event.cancelable) { event.preventDefault(); } event.stopPropagation(); var rawLeft = clientX - _this.dragStartPoint.x; // Apply sensitivity multiplier for snappier response var newLeft = rawLeft * _this.swipeSensitivity; _this.left = newLeft > 0 ? Math.min(newLeft, _this.maxSwipeThreshold) : Math.max(newLeft, -_this.maxSwipeThreshold); _this.scheduleUpdatePosition(); }); _defineProperty(_assertThisInitialized(_this), "handleDragEndMouse", function () { window.removeEventListener('mouseup', _this.handleDragEndMouse); window.removeEventListener('mousemove', _this.handleMouseMove); if (_this.listElement) { _this.listElement.removeEventListener('mouseup', _this.handleDragEndMouse); _this.listElement.removeEventListener('mousemove', _this.handleMouseMove); } _this.handleDragEnd(); }); _defineProperty(_assertThisInitialized(_this), "handleDragEndTouch", function () { window.removeEventListener('touchend', _this.handleDragEndTouch); _this.handleDragEnd(); }); _defineProperty(_assertThisInitialized(_this), "playReturnAnimationForLeadingActions", function (_ref2) { var to = _ref2.to, isIosType = _ref2.isIosType, playMsReturnAnimation = _ref2.playMsReturnAnimation; if (_this.leadingActionsElement) { _this.leadingActionsElement.className = clsx('swipeable-list-item__leading-actions', playMsReturnAnimation ? 'swipeable-list-item__actions--return-ms' : 'swipeable-list-item__leading-actions--return'); if (_this.leadingActionsOpened && isIosType && to !== 0) { _this.leadingActionsElement.className += ' test-actions-opened'; } if (playMsReturnAnimation) { var keepAnimationEnd = function keepAnimationEnd() { _this.leadingActionsElement.removeEventListener('animationend', keepAnimationEnd); _this.leadingActionsElement.style.width = 0; }; _this.leadingActionsElement.addEventListener('animationend', keepAnimationEnd); } else { _this.leadingActionsElement.style.width = "".concat(to === 0 || !isIosType ? 0 : _this.leadingActionsOpened && isIosType ? _this.leadingActionsWidth : 0, "px"); } } }); _defineProperty(_assertThisInitialized(_this), "playReturnAnimationForTrailingActions", function (_ref3) { var to = _ref3.to, isIosType = _ref3.isIosType, playMsReturnAnimation = _ref3.playMsReturnAnimation; if (_this.trailingActionsElement) { _this.trailingActionsElement.className = clsx('swipeable-list-item__trailing-actions', playMsReturnAnimation ? 'swipeable-list-item__actions--return-ms' : 'swipeable-list-item__trailing-actions--return'); if (_this.trailingActionsOpened && isIosType && to !== 0) { _this.trailingActionsElement.className += ' test-actions-opened'; } if (!playMsReturnAnimation) { _this.trailingActionsElement.style.width = "".concat(to === 0 || !isIosType ? 0 : _this.trailingActionsOpened && isIosType ? _this.trailingActionsWidth : 0, "px"); } else { var keepAnimationEnd = function keepAnimationEnd() { _this.trailingActionsElement.removeEventListener('animationend', keepAnimationEnd); _this.trailingActionsElement.style.width = 0; }; _this.trailingActionsElement.addEventListener('animationend', keepAnimationEnd); } } }); _defineProperty(_assertThisInitialized(_this), "playReturnAnimation", function () { var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref4$to = _ref4.to, to = _ref4$to === void 0 ? 0 : _ref4$to; if (Math.abs(_this.left) < 1) { // Skip animation if already at rest position return; } var _assertThisInitialize = _assertThisInitialized(_this), listElement = _assertThisInitialize.listElement; var listType = _this.props.listType; var triggerAction = _this.state.triggerAction; var isIosType = listType === Type.IOS; var isMsType = listType === Type.MS; var playMsReturnAnimation = triggerAction && isMsType; if (playMsReturnAnimation) { var keepAnimationEnd = function keepAnimationEnd() { listElement.removeEventListener('animationend', keepAnimationEnd); listElement.style.transform = "translateX(0)"; }; listElement.addEventListener('animationend', keepAnimationEnd); } if (listElement) { listElement.className = clsx('swipeable-list-item__content', playMsReturnAnimation ? "swipeable-list-item__content--return-".concat(_this.left < 0 ? 'trailing' : 'leading', "-ms") : 'swipeable-list-item__content--return'); if (!playMsReturnAnimation) { listElement.style.transform = "translateX(".concat(isIosType ? to : 0, "px)"); } } if (_this.left < 0) { _this.playReturnAnimationForTrailingActions({ to: to, isIosType: isIosType, playMsReturnAnimation: playMsReturnAnimation }); } else { _this.playReturnAnimationForLeadingActions({ to: to, isIosType: isIosType, playMsReturnAnimation: playMsReturnAnimation }); } if (to === 0) { _this.leadingActionsOpened = false; _this.trailingActionsOpened = false; _this.resetState(); } }); _defineProperty(_assertThisInitialized(_this), "playRemoveAnimation", function () { var _assertThisInitialize2 = _assertThisInitialized(_this), listElement = _assertThisInitialize2.listElement, wrapperElement = _assertThisInitialize2.wrapperElement; var listType = _this.props.listType; if (listElement) { wrapperElement.className = 'swipeable-list-item swipeable-list-item--remove'; listElement.className = 'swipeable-list-item__content swipeable-list-item__content--remove'; var isIosType = listType === Type.IOS; var leadingFullSwipe = isIosType ? _this.leadingActionsOpened : _this.dragDirection === DragDirection.RIGHT; var trailingFullSwipe = isIosType ? _this.trailingActionsOpened : _this.dragDirection === DragDirection.LEFT; var translateLength = listElement.offsetWidth * (leadingFullSwipe ? 1 : -1); listElement.style.transform = "translateX(".concat(translateLength, "px)"); _this.setState({ leadingFullSwipe: leadingFullSwipe, trailingFullSwipe: trailingFullSwipe }); if (leadingFullSwipe) { _this.leadingActionsElement.className += ' swipeable-list-item__leading-actions--return'; _this.leadingActionsElement.style.width = "".concat(Math.abs(translateLength), "px"); } else if (trailingFullSwipe) { _this.trailingActionsElement.className += ' swipeable-list-item__trailing-actions--return'; _this.trailingActionsElement.style.width = "".concat(Math.abs(translateLength), "px"); } } }); _defineProperty(_assertThisInitialized(_this), "playActionAnimation", function (_ref5) { var type = _ref5.type; var _assertThisInitialize3 = _assertThisInitialized(_this), listElement = _assertThisInitialize3.listElement; if (listElement) { switch (type) { case ActionAnimation.REMOVE: _this.playRemoveAnimation(); break; case ActionAnimation.NONE: break; default: _this.playReturnAnimation(); } } }); _defineProperty(_assertThisInitialized(_this), "handleDragEnd", function () { if (_this.requestedAnimationFrame) { cancelAnimationFrame(_this.requestedAnimationFrame); _this.requestedAnimationFrame = null; } // Force a final position update before ending if (_this.isSwiping() && _this.listElement) { _this.listElement.style.transform = "translateX(".concat(_this.left, "px)"); } // Small delay to ensure final position is rendered setTimeout(function () { // Reset will-change and pointer-events to avoid memory overhead if (_this.listElement) { _this.listElement.style.willChange = 'auto'; _this.listElement.style.pointerEvents = 'auto'; } if (_this.leadingActionsElement) { _this.leadingActionsElement.style.willChange = 'auto'; } if (_this.trailingActionsElement) { _this.trailingActionsElement.style.willChange = 'auto'; } }, 10); if (_this.isSwiping()) { var _this$state = _this.state, leadingFullSwipe = _this$state.leadingFullSwipe, trailingFullSwipe = _this$state.trailingFullSwipe, triggerAction = _this$state.triggerAction; var onSwipeEnd = _this.props.onSwipeEnd; if (onSwipeEnd) { onSwipeEnd(_this.dragDirection); } if (triggerAction) { if (leadingFullSwipe) { _this.leadingFullSwipeAction(); return; } if (trailingFullSwipe) { _this.trailingFullSwipeAction(); return; } } if (_this.leadingActionsOpened || _this.trailingActionsOpened) { if (_this.leadingActionsOpened) { _this.left = _this.leadingActionsWidth; } else if (_this.trailingActionsOpened) { _this.left = -_this.trailingActionsWidth; } _this.playReturnAnimation({ to: _this.left }); } else { // Ensure we animate from the current position var currentLeft = _this.left; if (Math.abs(currentLeft) > 10) { // Only animate if we've moved significantly _this.playReturnAnimation(); } _this.resetState(); } } else { // If not swiping, ensure clean state _this.resetState(); } }); _defineProperty(_assertThisInitialized(_this), "dragStartedWithinItem", function () { var _this$dragStartPoint = _this.dragStartPoint, x = _this$dragStartPoint.x, y = _this$dragStartPoint.y; return x !== -1 && y !== -1; }); _defineProperty(_assertThisInitialized(_this), "setDragDirection", function (x, y) { if (_this.dragDirection === DragDirection.UNKNOWN) { var _this$dragStartPoint2 = _this.dragStartPoint, startX = _this$dragStartPoint2.x, startY = _this$dragStartPoint2.y; var horizontalDistance = Math.abs(x - startX); var verticalDistance = Math.abs(y - startY); if (horizontalDistance <= _this.dragHorizontalDirectionThreshold && verticalDistance <= _this.dragVerticalDirectionThreshold) { return; } var angle = Math.atan2(y - startY, x - startX); var octant = Math.round(8 * angle / (2 * Math.PI) + 8) % 8; switch (octant) { case 0: if (_this.leadingActionsElement !== null && horizontalDistance > _this.dragHorizontalDirectionThreshold) { _this.dragDirection = DragDirection.RIGHT; } break; case 1: case 2: case 3: if (verticalDistance > _this.dragVerticalDirectionThreshold) { _this.dragDirection = DragDirection.DOWN; } break; case 4: if (_this.trailingActionsElement !== null && horizontalDistance > _this.dragHorizontalDirectionThreshold) { _this.dragDirection = DragDirection.LEFT; } break; case 5: case 6: case 7: if (verticalDistance > _this.dragVerticalDirectionThreshold) { _this.dragDirection = DragDirection.UP; } break; default: _this.dragDirection = DragDirection.UNKNOWN; } var onSwipeStart = _this.props.onSwipeStart; if (onSwipeStart && _this.isSwiping()) { onSwipeStart(_this.dragDirection); } } }); _defineProperty(_assertThisInitialized(_this), "isSwiping", function () { var blockSwipe = _this.props.blockSwipe; var horizontalDrag = _this.dragDirection === DragDirection.LEFT || _this.dragDirection === DragDirection.RIGHT; return !blockSwipe && _this.dragStartedWithinItem() && horizontalDrag; }); _defineProperty(_assertThisInitialized(_this), "scheduleUpdatePosition", function () { if (_this.requestedAnimationFrame) { cancelAnimationFrame(_this.requestedAnimationFrame); } _this.requestedAnimationFrame = requestAnimationFrame(function () { _this.requestedAnimationFrame = null; _this.updatePosition(); }); }); _defineProperty(_assertThisInitialized(_this), "updatePosition", function () { if (!_this.isSwiping()) { return; } // Remove FPS throttling for smoother performance // The browser's requestAnimationFrame already provides optimal timing var _this$props = _this.props, fullSwipeThreshold = _this$props.threshold, listType = _this$props.listType; var fullSwipe = _this.fullSwipe; var isSwipingLeft = _this.left < 0; var isSwipingRight = _this.left > 0; if (isSwipingLeft) { if (_this.onlyLeadingActions) { _this.left = 0; } if (_this.trailingActionsElement && listType === Type.IOS) { _this.trailingActionsOpened = Math.abs(_this.left) > _this.trailingActionsWidth; _this.leadingActionsOpened = false; } } if (isSwipingRight) { if (_this.onlyTrailingActions) { _this.left = 0; } if (_this.leadingActionsElement && listType === Type.IOS) { _this.leadingActionsOpened = _this.left > _this.leadingActionsWidth; _this.trailingActionsOpened = false; } } if (_this.leadingActionsElement) { _this.leadingActionsElement.style.width = "".concat(_this.left < 0 ? 0 : _this.left, "px"); } if (_this.trailingActionsElement) { _this.trailingActionsElement.style.width = "".concat(_this.left > 0 ? 0 : -_this.left, "px"); } if (_this.listElement) { if (fullSwipe) { var _assertThisInitialize4 = _assertThisInitialized(_this), offsetWidth = _assertThisInitialize4.listElement.offsetWidth; var threshold = offsetWidth * fullSwipeThreshold; if (_this.left < -threshold) { _this.setState({ leadingFullSwipe: false, trailingFullSwipe: true, triggerAction: true, scaleTrailing: true }); } else if (_this.left > threshold) { _this.setState({ leadingFullSwipe: true, trailingFullSwipe: false, triggerAction: true, scaleLeading: true }); } else { _this.setState({ scaleLeading: false, scaleTrailing: false, triggerAction: false }); } } _this.listElement.style.transform = "translateX(".concat(_this.left, "px)"); // Calculate swipe progress var listElementWidth = _this.listElement.offsetWidth; var swipeDistancePercent = _this.previousSwipeDistancePercent; if (listElementWidth !== 0) { var swipeDistance = Math.max(0, listElementWidth - Math.abs(_this.left)); swipeDistancePercent = 100 - Math.round(100 * swipeDistance / listElementWidth); } // Batch state updates for better performance var now = Date.now(); var shouldUpdateProgress = Math.abs(_this.state.swipeProgress - swipeDistancePercent) > 1; var shouldUpdateOffset = Math.abs(_this.state.dragOffset - _this.left) > 2; if (shouldUpdateProgress || shouldUpdateOffset) { // Debounce state updates to every 16ms (60fps) at most if (now - _this.lastStateUpdate > 16) { _this.setState({ swipeProgress: swipeDistancePercent, dragOffset: _this.left }); _this.lastStateUpdate = now; } } if (_this.props.onSwipeProgress) { if (_this.previousSwipeDistancePercent !== swipeDistancePercent) { _this.props.onSwipeProgress(swipeDistancePercent, _this.dragDirection); _this.previousSwipeDistancePercent = swipeDistancePercent; } } } }); _defineProperty(_assertThisInitialized(_this), "onActionTriggered", function (isDestructive) { _this.playActionAnimation({ type: isDestructive ? ActionAnimation.REMOVE : ActionAnimation.RETURN }); }); _defineProperty(_assertThisInitialized(_this), "bindListElement", function (ref) { return _this.listElement = ref; }); _defineProperty(_assertThisInitialized(_this), "bindWrapperElement", function (ref) { return _this.wrapperElement = ref; }); _defineProperty(_assertThisInitialized(_this), "bindLeadingActionsElement", function (ref) { return _this.leadingActionsElement = ref; }); _defineProperty(_assertThisInitialized(_this), "bindTrailingActionsElement", function (ref) { return _this.trailingActionsElement = ref; }); _defineProperty(_assertThisInitialized(_this), "renderActions", function (actions, type, binder) { var _this$props2 = _this.props, actionDelay = _this$props2.actionDelay, destructiveCallbackDelay = _this$props2.destructiveCallbackDelay, listType = _this$props2.listType; var _this$state2 = _this.state, leadingFullSwipe = _this$state2.leadingFullSwipe, trailingFullSwipe = _this$state2.trailingFullSwipe, scaleLeading = _this$state2.scaleLeading, scaleTrailing = _this$state2.scaleTrailing; var _assertThisInitialize5 = _assertThisInitialized(_this), onActionTriggered = _assertThisInitialize5.onActionTriggered, setLeadingFullSwipeAction = _assertThisInitialize5.setLeadingFullSwipeAction, setTrailingFullSwipeAction = _assertThisInitialize5.setTrailingFullSwipeAction; var scaled = listType === Type.MS && (scaleLeading && type === 'leading' || scaleTrailing && type === 'trailing'); return /*#__PURE__*/React__default["default"].createElement("div", { className: clsx("swipeable-list-item__".concat(type, "-actions"), _defineProperty({}, "swipeable-list-item__".concat(type, "-actions--scaled"), scaled)), "data-testid": "".concat(type, "-actions"), ref: binder }, /*#__PURE__*/React__default["default"].createElement(ItemContext.Provider, { value: { actionDelay: actionDelay, destructiveCallbackDelay: destructiveCallbackDelay, listType: listType, leadingFullSwipe: leadingFullSwipe, onActionTriggered: onActionTriggered, scaleLeading: scaleLeading, scaleTrailing: scaleTrailing, setLeadingFullSwipeAction: setLeadingFullSwipeAction, setTrailingFullSwipeAction: setTrailingFullSwipeAction, trailingFullSwipe: trailingFullSwipe, swipeProgress: _this.state.swipeProgress, dragOffset: _this.state.dragOffset } }, actions)); }); _defineProperty(_assertThisInitialized(_this), "handleClick", function (event) { if (_this.props.onClick) { if (_this.leadingActionsOpened || _this.trailingActionsOpened || _this.isSwiping()) { event.preventDefault(); return; } var delta = Math.abs(event.clientX - _this.dragStartPoint.x); if (delta > 10) { // If mouse moved more than threshold, ignore the click event event.preventDefault(); return; } _this.props.onClick(); } }); _this.state = initialState; // binded elements _this.listElement = null; _this.leadingActionsElement = null; _this.trailingActionsElement = null; _this.wrapperElement = null; _this.requestedAnimationFrame = null; _this.leadingActionsWidth = 0; _this.trailingActionsWidth = 0; _this.startTime = null; _this.previousSwipeDistancePercent = 0; _this.leadingFullSwipeAction = null; _this.trailingFullSwipeAction = null; _this.id = props.id; // Add debounced state update for better performance _this.pendingStateUpdate = null; _this.lastStateUpdate = 0; // Swipe sensitivity multiplier for snappier response _this.swipeSensitivity = props.swipeSensitivity || 1.5; // Initialize instance variables directly instead of calling resetState _this.dragStartPoint = { x: -1, y: -1 }; _this.dragDirection = DragDirection.UNKNOWN; _this.left = 0; _this.leadingActionsOpened = false; _this.trailingActionsOpened = false; return _this; } _createClass(SwipeableListItem, [{ key: "dragHorizontalDirectionThreshold", get: function get() { return this.props.swipeStartThreshold || 10; } }, { key: "dragVerticalDirectionThreshold", get: function get() { return this.props.scrollStartThreshold || 10; } }, { key: "fullSwipe", get: function get() { var _this$props3 = this.props, fullSwipe = _this$props3.fullSwipe, listType = _this$props3.listType; if (listType === Type.IOS) { return fullSwipe; } return true; } }, { key: "componentDidMount", value: function componentDidMount() { if (!this.props.optOutMouseEvents) { this.listElement.addEventListener('mousedown', this.handleDragStartMouse); } this.listElement.addEventListener('touchstart', this.handleDragStartTouch, { passive: true }); this.listElement.addEventListener('touchend', this.handleDragEndTouch, { passive: true }); this.listElement.addEventListener('touchmove', this.handleTouchMove, { passive: false }); if (this.leadingActionsElement) { this.leadingActionsWidth = measure(this.leadingActionsElement, function (el) { return el.offsetWidth; }); } if (this.trailingActionsElement) { this.trailingActionsWidth = measure(this.trailingActionsElement, function (el) { return el.offsetWidth; }); } if (this.props.resetState) { this.props.resetState(this.playReturnAnimation); } if (this.props.maxSwipe) { this.maxSwipeThreshold = this.props.maxSwipe * this.listElement.offsetWidth; } else { this.maxSwipeThreshold = this.listElement.offsetWidth; } } }, { key: "componentWillUnmount", value: function componentWillUnmount() { if (this.requestedAnimationFrame) { cancelAnimationFrame(this.requestedAnimationFrame); this.requestedAnimationFrame = null; } if (!this.props.optOutMouseEvents) { this.listElement.removeEventListener('mousedown', this.handleDragStartMouse); } this.listElement.removeEventListener('touchstart', this.handleDragStartTouch); this.listElement.removeEventListener('touchend', this.handleDragEndTouch); this.listElement.removeEventListener('touchmove', this.handleTouchMove); } }, { key: "onlyLeadingActions", get: function get() { return this.leadingActionsElement !== null && this.trailingActionsElement === null; } }, { key: "onlyTrailingActions", get: function get() { return this.leadingActionsElement === null && this.trailingActionsElement !== null; } }, { key: "render", value: function render() { var _this$props4 = this.props, children = _this$props4.children, className = _this$props4.className, leadingActions = _this$props4.leadingActions, trailingActions = _this$props4.trailingActions; return /*#__PURE__*/React__default["default"].createElement("div", { className: clsx('swipeable-list-item', className), id: this.id, ref: this.bindWrapperElement, onClick: this.handleClick }, leadingActions && this.renderActions(leadingActions, 'leading', this.bindLeadingActionsElement), /*#__PURE__*/React__default["default"].createElement("div", { className: "swipeable-list-item__content", "data-testid": "content", ref: this.bindListElement }, children), trailingActions && this.renderActions(trailingActions, 'trailing', this.bindTrailingActionsElement)); } }]); return SwipeableListItem; }(React.PureComponent); SwipeableListItem.propTypes = { actionDelay: PropTypes__default["default"].number, blockSwipe: PropTypes__default["default"].bool, children: PropTypes__default["default"].node, className: PropTypes__default["default"].string, destructiveCallbackDelay: PropTypes__default["default"].number, fullSwipe: PropTypes__default["default"].bool, leadingActions: PropTypes__default["default"].node, listType: PropTypes__default["default"].oneOf(Object.values(Type)), maxSwipe: PropTypes__default["default"].number, onClick: PropTypes__default["default"].func, onSwipeEnd: PropTypes__default["default"].func, onSwipeProgress: PropTypes__default["default"].func, onSwipeStart: PropTypes__default["default"].func, optOutMouseEvents: PropTypes__default["default"].bool, scrollStartThreshold: PropTypes__default["default"].number, swipeStartThreshold: PropTypes__default["default"].number, swipeSensitivity: PropTypes__default["default"].number, threshold: PropTypes__default["default"].number, trailingActions: PropTypes__default["default"].node, clickedCallback: PropTypes__default["default"].func, id: PropTypes__default["default"].string, resetState: PropTypes__default["default"].func }; var SwipeAction = function SwipeAction(_ref) { var children = _ref.children, className = _ref.className, _ref$destructive = _ref.destructive, destructive = _ref$destructive === void 0 ? false : _ref$destructive, _ref$main = _ref.main, main = _ref$main === void 0 ? false : _ref$main, leading = _ref.leading, onClick = _ref.onClick, trailing = _ref.trailing, _ref$Tag = _ref.Tag, Tag = _ref$Tag === void 0 ? 'span' : _ref$Tag; var _React$useContext = React__default["default"].useContext(ItemContext), actionDelay = _React$useContext.actionDelay, destructiveCallbackDelay = _React$useContext.destructiveCallbackDelay, leadingFullSwipe = _React$useContext.leadingFullSwipe, listType = _React$useContext.listType, onActionTriggered = _React$useContext.onActionTriggered, setLeadingFullSwipeAction = _React$useContext.setLeadingFullSwipeAction, setTrailingFullSwipeAction = _React$useContext.setTrailingFullSwipeAction, trailingFullSwipe = _React$useContext.trailingFullSwipe, scaleLeading = _React$useContext.scaleLeading, scaleTrailing = _React$useContext.scaleTrailing, _React$useContext$swi = _React$useContext.swipeProgress, swipeProgress = _React$useContext$swi === void 0 ? 0 : _React$useContext$swi, _React$useContext$dra = _React$useContext.dragOffset, dragOffset = _React$useContext$dra === void 0 ? 0 : _React$useContext$dra; var onHandleClick = React__default["default"].useCallback(function () { if (actionDelay) { window.setTimeout(function () { onActionTriggered(destructive); onClick(); }, actionDelay); return; } onActionTriggered(destructive); if (destructive) { window.setTimeout(function () { return onClick(); }, destructiveCallbackDelay); } else { onClick(); } }, [actionDelay, destructive, destructiveCallbackDelay, onActionTriggered, onClick]); React__default["default"].useEffect(function () { if (leading && main) { setLeadingFullSwipeAction(onHandleClick); } }, [leading, main, onHandleClick, setLeadingFullSwipeAction]); React__default["default"].useEffect(function () { if (trailing && main) { setTrailingFullSwipeAction(onHandleClick); } }, [trailing, main, onHandleClick, setTrailingFullSwipeAction]); var renderContent = function renderContent() { if (typeof children === 'function') { return children({ swipeProgress: swipeProgress, dragOffset: dragOffset, isFullSwipe: leading && leadingFullSwipe || trailing && trailingFullSwipe, isLeading: leading, isTrailing: trailing }); } return children; }; return /*#__PURE__*/React__default["default"].createElement(Tag, { className: clsx('swipe-action', { 'swipe-action__leading': leading, 'swipe-action__trailing': trailing, 'swipe-action__leading--full-swipe-rest': leading && leadingFullSwipe && !main && listType === Type.IOS, 'swipe-action__leading--full-swipe-main': leading && leadingFullSwipe && main && listType === Type.IOS, 'swipe-action__trailing--full-swipe-rest': trailing && trailingFullSwipe && !main && listType === Type.IOS, 'swipe-action__trailing--full-swipe-main': trailing && trailingFullSwipe && main && listType === Type.IOS, 'swipe-action__grayed': listType === Type.MS && !(scaleLeading || scaleTrailing) }, className), onClick: onHandleClick }, renderContent()); }; SwipeAction.propTypes = { children: PropTypes__default["default"].oneOfType([PropTypes__default["default"].node, PropTypes__default["default"].func]).isRequired, className: PropTypes__default["default"].string, destructive: PropTypes__default["default"].bool, main: PropTypes__default["default"].bool, leading: PropTypes__default["default"].bool, onClick: PropTypes__default["default"].func.isRequired, trailing: PropTypes__default["default"].bool, Tag: PropTypes__default["default"].string }; var TrailingActions = function TrailingActions(_ref) { var children = _ref.children; if (children === null || children === undefined) { return null; } if (Array.isArray(children)) { return React__default["default"].Children.map(children, function (child, index) { if (! /*#__PURE__*/React__default["default"].isValidElement(child)) { return child; } return /*#__PURE__*/React__default["default"].cloneElement(child, { main: index === children.length - 1, trailing: true }); }); } return /*#__PURE__*/React__default["default"].cloneElement(children, { main: true, trailing: true }); }; exports.LeadingActions = LeadingActions; exports.SwipeAction = SwipeAction; exports.SwipeableList = SwipeableList; exports.SwipeableListItem = SwipeableListItem; exports.TrailingActions = TrailingActions; exports.Type = Type; Object.defineProperty(exports, '__esModule', { value: true }); }));