Skip to content

Commit

Permalink
A4A: Add swipe gesture on the carousel component. (#99039)
Browse files Browse the repository at this point in the history
* Add swipe gesture on the carousel component.

* Make the carousel swift gesture more immediate.

* Make the carousel feels like you are dragging the menu when swiping.

* Make it feel like the carousel snaps when swiping at certain threshold.
  • Loading branch information
jkguidaven authored Jan 31, 2025
1 parent 166d9e6 commit 01888d0
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 1 deletion.
42 changes: 41 additions & 1 deletion client/a8c-for-agencies/components/a4a-carousel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Props = {

export default function A4ACarousel( { children, className }: Props ) {
const [ offsetX, setOffsetX ] = useState( 0 );
const [ touchStart, setTouchStart ] = useState< number | null >( null );

const contentRef = useRef< HTMLDivElement >( null );
const containerRef = useRef< HTMLDivElement >( null );
Expand All @@ -31,9 +32,48 @@ export default function A4ACarousel( { children, className }: Props ) {
setOffsetX( Math.max( offsetX - offsetStep, -maxOffset ) );
}, [ offsetX, offsetStep, maxOffset ] );

const onTouchStart = ( e: React.TouchEvent ) => {
setTouchStart( e.targetTouches[ 0 ].clientX );
};

const onTouchMove = ( e: React.TouchEvent ) => {
if ( ! touchStart ) {
return;
}

const currentTouch = e.targetTouches[ 0 ].clientX;
const distance = touchStart - currentTouch;

// If distance is greater than threshold, snap to next/previous
const snapThreshold = containerWidth * 0.2; // 20% of container width
if ( Math.abs( distance ) > snapThreshold ) {
const newOffset =
distance > 0
? Math.max( offsetX - offsetStep, -maxOffset )
: Math.min( offsetX + offsetStep, 0 );
setOffsetX( newOffset );
} else {
// Otherwise do smooth tracking
const newOffset = Math.max( Math.min( offsetX - distance, 0 ), -maxOffset );
setOffsetX( newOffset );
}

setTouchStart( currentTouch );
};

const onTouchEnd = () => {
setTouchStart( null );
};

return (
<div className={ clsx( `a4a-carousel-wrapper`, className ) }>
<div className="a4a-carousel" ref={ containerRef }>
<div
className={ clsx( 'a4a-carousel', { 'is-touch-active': !! touchStart } ) }
ref={ containerRef }
onTouchStart={ onTouchStart }
onTouchMove={ onTouchMove }
onTouchEnd={ onTouchEnd }
>
<div className="a4a-carousel__navigation">
<Button
className="a4a-carousel__navigation-button"
Expand Down
4 changes: 4 additions & 0 deletions client/a8c-for-agencies/components/a4a-carousel/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,9 @@
gap: 16px;
width: max-content;
transform: translateX( 0 );
transition: transform .2s ease-out;
}

.a4a-carousel:not(.is-touch-active) .a4a-carousel__content {
transition: transform .75s cubic-bezier(.5,0,.5,1);
}

0 comments on commit 01888d0

Please sign in to comment.