UNPKG

@chayns-components/swipeable-wrapper

Version:

A set of beautiful React components for developing your own applications with chayns.

201 lines (195 loc) • 8.71 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _chaynsApi = require("chayns-api"); var _react = require("motion/react"); var _react2 = _interopRequireWildcard(require("react")); var _threshold = require("../../utils/threshold"); var _SwipeableAction = _interopRequireWildcard(require("./swipeable-action/SwipeableAction")); var _SwipeableWrapper = require("./SwipeableWrapper.styles"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } const SwipeableWrapper = ({ children, leftActions = [], rightActions = [], shouldUseOpacityAnimation, isDisabled = false, onSwipeEnd, onSwipeStart }) => { const [leftThreshold, setLeftThreshold] = (0, _react2.useState)((0, _threshold.calcThreshold)({ actionCount: leftActions.length, direction: 'left', width: window.innerWidth })); const [rightThreshold, setRightThreshold] = (0, _react2.useState)((0, _threshold.calcThreshold)({ actionCount: rightActions.length, direction: 'right', width: window.innerWidth })); const swipeableWrapperRef = (0, _react2.useRef)(null); const isSwipingRef = (0, _react2.useRef)(false); const listItemXOffset = (0, _react.useMotionValue)(0); const close = (0, _react2.useCallback)(() => { void (0, _react.animate)(listItemXOffset, 0); }, [listItemXOffset]); const open = (0, _react2.useCallback)(direction => { switch (direction) { case 'left': void (0, _react.animate)(listItemXOffset, _SwipeableAction.SWIPEABLE_ACTION_WIDTH * leftActions.length); break; case 'right': void (0, _react.animate)(listItemXOffset, -_SwipeableAction.SWIPEABLE_ACTION_WIDTH * rightActions.length); break; default: break; } }, [leftActions.length, listItemXOffset, rightActions.length]); (0, _react2.useEffect)(() => { var _swipeableWrapperRef$; const width = (_swipeableWrapperRef$ = swipeableWrapperRef.current) === null || _swipeableWrapperRef$ === void 0 || (_swipeableWrapperRef$ = _swipeableWrapperRef$.parentElement) === null || _swipeableWrapperRef$ === void 0 ? void 0 : _swipeableWrapperRef$.offsetWidth; // This check was deliberately chosen because a width of 0 is also not permitted. if (width) { setLeftThreshold((0, _threshold.calcThreshold)({ actionCount: leftActions.length, direction: 'left', width })); setRightThreshold((0, _threshold.calcThreshold)({ actionCount: rightActions.length, direction: 'right', width })); } }, [leftActions.length, rightActions.length]); // Close an opened menu when anything outside it is tapped (0, _react2.useEffect)(() => { function closeCallback(event) { var _swipeableWrapperRef$2; const eventTarget = event.target; // @ts-expect-error: Pretty sure that the event target is always a Node. if (eventTarget && !((_swipeableWrapperRef$2 = swipeableWrapperRef.current) !== null && _swipeableWrapperRef$2 !== void 0 && _swipeableWrapperRef$2.contains(eventTarget))) { close(); } } document.addEventListener('mousedown', closeCallback); document.addEventListener('touchstart', closeCallback); return () => { document.removeEventListener('mousedown', closeCallback); document.removeEventListener('touchstart', closeCallback); }; }, [close]); // Vibrate when the threshold is passed (0, _react2.useEffect)(() => listItemXOffset.on('change', newValue => { const previous = listItemXOffset.getPrevious(); if (!previous) { return; } const hasCrossedLeftThreshold = previous < leftThreshold && newValue >= leftThreshold || previous > leftThreshold && newValue <= leftThreshold; const hasCrossedRightThreshold = previous < rightThreshold && newValue >= rightThreshold || previous > rightThreshold && newValue <= rightThreshold; if (hasCrossedLeftThreshold || hasCrossedRightThreshold) { void (0, _chaynsApi.vibrate)({ iOSFeedbackVibration: 6, pattern: [150] }); } }), [leftThreshold, listItemXOffset, rightThreshold]); const handlePan = (0, _react2.useCallback)((_, info) => { if (!isSwipingRef.current) { isSwipingRef.current = true; if (typeof onSwipeStart === 'function') { onSwipeStart(); } } const currentXOffset = listItemXOffset.get(); const dampingFactor = info.offset.x > 0 && leftActions.length > 0 || info.offset.x < 0 && rightActions.length > 0 || currentXOffset > 0 && info.delta.x < 0 || currentXOffset < 0 && info.delta.x > 0 ? 1 : 0.75 / (Math.abs(info.offset.x) / 9); if (Math.abs(info.offset.x) > 30 || currentXOffset > 0) { listItemXOffset.set(currentXOffset + info.delta.x * dampingFactor); } }, [leftActions.length, listItemXOffset, onSwipeStart, rightActions.length]); const handlePanEnd = (0, _react2.useCallback)(() => { if (typeof onSwipeEnd === 'function') { onSwipeEnd(); } isSwipingRef.current = false; const offset = listItemXOffset.get(); if (offset > leftThreshold) { var _leftActions$; (_leftActions$ = leftActions[0]) === null || _leftActions$ === void 0 || _leftActions$.action(); close(); } else if (offset < rightThreshold) { var _rightActions; (_rightActions = rightActions[rightActions.length - 1]) === null || _rightActions === void 0 || _rightActions.action(); close(); } else { let state; if (offset > 2) { state = 'left-open'; } else if (offset < -2) { state = 'right-open'; } else { state = 'closed'; } // eslint-disable-next-line default-case switch (state) { case 'left-open': if (offset < _SwipeableAction.SWIPEABLE_ACTION_WIDTH) { close(); } else { open('left'); } break; case 'right-open': if (offset > -_SwipeableAction.SWIPEABLE_ACTION_WIDTH) { close(); } else { open('right'); } break; case 'closed': if (offset > _SwipeableAction.SWIPEABLE_ACTION_WIDTH) { open('left'); } else if (offset < -_SwipeableAction.SWIPEABLE_ACTION_WIDTH) { open('right'); } else { close(); } } } }, [close, leftActions, leftThreshold, listItemXOffset, onSwipeEnd, open, rightActions, rightThreshold]); const leftActionElements = (0, _react2.useMemo)(() => Array.from(leftActions).reverse().map((item, index) => /*#__PURE__*/_react2.default.createElement(_SwipeableAction.default, { activationThreshold: leftThreshold, close: close, index: index, item: item, key: item.key, listItemXOffset: listItemXOffset, position: "left", shouldUseOpacityAnimation: shouldUseOpacityAnimation, totalActionCount: leftActions.length })), [close, leftActions, leftThreshold, listItemXOffset, shouldUseOpacityAnimation]); const rightActionElements = (0, _react2.useMemo)(() => rightActions.map((item, index) => /*#__PURE__*/_react2.default.createElement(_SwipeableAction.default, { activationThreshold: rightThreshold, close: close, index: index, item: item, key: item.key, listItemXOffset: listItemXOffset, position: "right", shouldUseOpacityAnimation: shouldUseOpacityAnimation, totalActionCount: rightActions.length })), [rightActions, rightThreshold, close, listItemXOffset, shouldUseOpacityAnimation]); return /*#__PURE__*/_react2.default.createElement(_SwipeableWrapper.StyledMotionSwipeableWrapper, { onPan: isDisabled ? undefined : handlePan, onPanEnd: isDisabled ? undefined : handlePanEnd, ref: swipeableWrapperRef, style: { x: listItemXOffset } }, leftActionElements, /*#__PURE__*/_react2.default.createElement(_SwipeableWrapper.StyledSwipeableWrapperContent, null, children), rightActionElements); }; SwipeableWrapper.displayName = 'SwipeableWrapper'; var _default = exports.default = SwipeableWrapper; //# sourceMappingURL=SwipeableWrapper.js.map