@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
JavaScript
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