UNPKG

@chayns-components/swipeable-wrapper

Version:

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

107 lines (102 loc) 3.62 kB
import { useMotionValueEvent, useSpring, useTransform } from 'motion/react'; import React, { useEffect } from 'react'; import { StyledMotionSwipeableAction, StyledSwipeableActionButton } from './SwipeableAction.styles'; export const SWIPEABLE_ACTION_WIDTH = 72; const SwipeableAction = ({ activationThreshold, close, index, item, listItemXOffset, position, shouldUseOpacityAnimation, totalActionCount }) => { const [pointerEvents, setPointerEvents] = React.useState('none'); const handleButtonClick = () => { item.action(); close(); }; /** * By default, the action sticks to the content of the swipeable item. This * makes it move outwards to reveal the inner items. */ const actionOffset = useTransform(listItemXOffset, newValue => { const maxOffset = SWIPEABLE_ACTION_WIDTH * index; const fractionalOffset = -newValue / totalActionCount * index; switch (position) { case 'left': return Math.max(-maxOffset, fractionalOffset); case 'right': return Math.min(maxOffset, fractionalOffset); default: return 0; } }); /** * Brings the item in again if past the threshold. Only relevant for * outermost items. */ const actionOverlayOffset = useSpring(0, { bounce: 0 }); /** * Combines the two values above to create the correct X transform that has * to be applied to the action. */ const actionX = useTransform([actionOffset, actionOverlayOffset], ([x, y]) => { if (position === 'left') { return Math.min((x ?? 0) + (y ?? 0), 0); } return Math.max((x ?? 0) + (y ?? 0), 0); }); const opacity = useTransform(listItemXOffset, x => Math.max(0, Math.min(1, Math.abs(x) / 72))); useMotionValueEvent(opacity, 'change', value => { setPointerEvents(value < 0.5 ? 'none' : 'auto'); }); // Animate to the middle after passing threshold if outermost item useEffect(() => { const isOuterMost = index === totalActionCount - 1; if (!isOuterMost) return undefined; return listItemXOffset.onChange(newValue => { const lastValue = listItemXOffset.getPrevious(); if (!lastValue) { return; } // eslint-disable-next-line default-case switch (position) { case 'left': if (newValue > activationThreshold && lastValue <= activationThreshold) { actionOverlayOffset.set(SWIPEABLE_ACTION_WIDTH * index); } else if (newValue < activationThreshold && lastValue >= activationThreshold) { actionOverlayOffset.set(0); } break; case 'right': if (newValue < activationThreshold && lastValue >= activationThreshold) { actionOverlayOffset.set(SWIPEABLE_ACTION_WIDTH * -1 * index); } else if (newValue > activationThreshold && lastValue <= activationThreshold) { actionOverlayOffset.set(0); } } }); }, [actionOverlayOffset, activationThreshold, index, listItemXOffset, position, totalActionCount]); return /*#__PURE__*/React.createElement(StyledMotionSwipeableAction, { $backgroundColor: item.backgroundColor, $position: position, style: { x: actionX, opacity: shouldUseOpacityAnimation ? opacity : 1 } }, /*#__PURE__*/React.createElement(StyledSwipeableActionButton, { onClick: handleButtonClick, style: { pointerEvents }, $color: item.color, $width: `${SWIPEABLE_ACTION_WIDTH}px` }, item.icon, item.text)); }; SwipeableAction.displayName = 'SwipeableAction'; export default SwipeableAction; //# sourceMappingURL=SwipeableAction.js.map