import { debounce } from 'lodash'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useSpring } from 'react-spring'; import TitleCard from '../TitleCard'; import { defineMessages, FormattedMessage } from 'react-intl'; const messages = defineMessages({ noresults: 'No Results', }); interface SliderProps { sliderKey: string; items?: JSX.Element[]; isLoading: boolean; isEmpty: boolean; emptyMessage?: string; } enum Direction { RIGHT, LEFT, } const Slider: React.FC = ({ sliderKey, items, isLoading, isEmpty, emptyMessage, }) => { const containerRef = useRef(null); const [scrollPos, setScrollPos] = useState({ isStart: true, isEnd: false }); const handleScroll = useCallback(() => { const scrollWidth = containerRef.current?.scrollWidth ?? 0; const clientWidth = containerRef.current?.getBoundingClientRect().width ?? 0; const scrollPosition = containerRef.current?.scrollLeft ?? 0; if (!items || items?.length === 0) { setScrollPos({ isStart: true, isEnd: true }); } else if (clientWidth >= scrollWidth) { setScrollPos({ isStart: true, isEnd: true }); } else if ( scrollPosition >= (containerRef.current?.scrollWidth ?? 0) - clientWidth ) { setScrollPos({ isStart: false, isEnd: true }); } else if (scrollPosition > 0) { setScrollPos({ isStart: false, isEnd: false }); } else { setScrollPos({ isStart: true, isEnd: false }); } }, [items]); // eslint-disable-next-line react-hooks/exhaustive-deps const debouncedScroll = useCallback( debounce(() => handleScroll(), 50), [handleScroll] ); useEffect(() => { const handleResize = () => { debouncedScroll(); }; window.addEventListener('resize', handleResize, { passive: true }); return () => { window.removeEventListener('resize', handleResize); }; }, [debouncedScroll]); useEffect(() => { handleScroll(); }, [items, handleScroll]); const onScroll = () => { debouncedScroll(); }; const [, setX] = useSpring(() => ({ from: { x: 0 }, to: { x: 0 }, onFrame: (props: { x: number }) => { if (containerRef.current) { containerRef.current.scrollLeft = props.x; } }, })); const slide = (direction: Direction) => { const clientWidth = containerRef.current?.getBoundingClientRect().width ?? 0; const cardWidth = containerRef.current?.firstElementChild?.getBoundingClientRect().width ?? 0; const scrollPosition = containerRef.current?.scrollLeft ?? 0; const visibleItems = Math.floor(clientWidth / cardWidth); const scrollOffset = scrollPosition % cardWidth; if (direction === Direction.LEFT) { const newX = Math.max( scrollPosition - scrollOffset - visibleItems * cardWidth, 0 ); setX({ from: { x: scrollPosition }, to: { x: newX, }, onFrame: (props) => { if (containerRef.current) { containerRef.current.scrollLeft = props.x; } }, reset: true, config: { friction: 60, tension: 500, velocity: 20 }, }); if (newX === 0) { setScrollPos({ isStart: true, isEnd: false }); } else { setScrollPos({ isStart: false, isEnd: false }); } } else if (direction === Direction.RIGHT) { const newX = Math.min( scrollPosition - scrollOffset + visibleItems * cardWidth, containerRef.current?.scrollWidth ?? 0 - clientWidth ); setX({ from: { x: scrollPosition }, to: { x: newX, }, onFrame: (props) => { if (containerRef.current) { containerRef.current.scrollLeft = props.x; } }, reset: true, config: { friction: 60, tension: 500, velocity: 20 }, }); if (newX >= (containerRef.current?.scrollWidth ?? 0) - clientWidth) { setScrollPos({ isStart: false, isEnd: true }); } else { setScrollPos({ isStart: false, isEnd: false }); } } }; return (
{items?.map((item, index) => (
{item}
))} {isLoading && [...Array(10)].map((_item, i) => (
))} {isEmpty && (
{emptyMessage ? ( emptyMessage ) : ( )}
)}
); }; export default Slider;