import React, {useRef, useEffect, startTransition} from 'react';
export default function SwipeRecognizer({
action,
children,
direction,
gesture,
}) {
if (direction == null) {
direction = 'left';
}
const axis = direction === 'left' || direction === 'right' ? 'x' : 'y';
const scrollRef = useRef(null);
const activeGesture = useRef(null);
function onScroll() {
if (activeGesture.current !== null) {
return;
}
if (typeof ScrollTimeline !== 'function') {
return;
}
const scrollTimeline = new ScrollTimeline({
source: scrollRef.current,
axis: axis,
});
activeGesture.current = gesture(scrollTimeline, {
range: [0, direction === 'left' || direction === 'up' ? 100 : 0, 100],
});
}
function onScrollEnd() {
let changed;
const scrollElement = scrollRef.current;
if (axis === 'x') {
const halfway =
(scrollElement.scrollWidth - scrollElement.clientWidth) / 2;
changed =
direction === 'left'
? scrollElement.scrollLeft < halfway
: scrollElement.scrollLeft > halfway;
} else {
const halfway =
(scrollElement.scrollHeight - scrollElement.clientHeight) / 2;
changed =
direction === 'up'
? scrollElement.scrollTop < halfway
: scrollElement.scrollTop > halfway;
}
if (changed) {
startTransition(action);
}
if (activeGesture.current !== null) {
const cancelGesture = activeGesture.current;
activeGesture.current = null;
cancelGesture();
}
}
useEffect(() => {
const scrollElement = scrollRef.current;
switch (direction) {
case 'left':
scrollElement.scrollLeft =
scrollElement.scrollWidth - scrollElement.clientWidth;
break;
case 'right':
scrollElement.scrollLeft = 0;
break;
case 'up':
scrollElement.scrollTop =
scrollElement.scrollHeight - scrollElement.clientHeight;
break;
case 'down':
scrollElement.scrollTop = 0;
break;
default:
break;
}
}, [direction]);
const scrollStyle = {
position: 'relative',
padding: '0px',
margin: '0px',
border: '0px',
width: axis === 'x' ? '100%' : null,
height: axis === 'y' ? '100%' : null,
overflow: 'scroll hidden',
touchAction: 'pan-' + direction,
overscrollBehaviorX: axis === 'x' ? 'none' : 'auto',
overscrollBehaviorY: axis === 'y' ? 'none' : 'auto',
scrollSnapType: axis + ' mandatory',
scrollbarWidth: 'none',
};
const overScrollStyle = {
position: 'relative',
padding: '0px',
margin: '0px',
border: '0px',
width: axis === 'x' ? '200%' : null,
height: axis === 'y' ? '200%' : null,
};
const snapStartStyle = {
position: 'absolute',
padding: '0px',
margin: '0px',
border: '0px',
width: axis === 'x' ? '50%' : '100%',
height: axis === 'y' ? '50%' : '100%',
left: '0px',
top: '0px',
scrollSnapAlign: 'center',
};
const snapEndStyle = {
position: 'absolute',
padding: '0px',
margin: '0px',
border: '0px',
width: axis === 'x' ? '50%' : '100%',
height: axis === 'y' ? '50%' : '100%',
right: '0px',
bottom: '0px',
scrollSnapAlign: 'center',
};
const stickyStyle = {
position: 'sticky',
padding: '0px',
margin: '0px',
border: '0px',
left: '0px',
top: '0px',
width: axis === 'x' ? '50%' : null,
height: axis === 'y' ? '50%' : null,
overflow: 'hidden',
};
return (
<div
style={scrollStyle}
onScroll={onScroll}
onScrollEnd={onScrollEnd}
ref={scrollRef}>
<div style={overScrollStyle}>
<div style={snapStartStyle} />
<div style={snapEndStyle} />
<div style={stickyStyle}>{children}</div>
</div>
</div>
);
}