soapbox/app/soapbox/components/ui/carousel/carousel.tsx

109 wiersze
3.2 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { useDimensions } from 'soapbox/hooks';
import HStack from '../hstack/hstack';
import Icon from '../icon/icon';
interface ICarousel {
children: any
/** Optional height to force on controls */
controlsHeight?: number
/** How many items in the carousel */
itemCount: number
/** The minimum width per item */
itemWidth: number
}
/**
* Carousel
*/
const Carousel: React.FC<ICarousel> = (props): JSX.Element => {
const { children, controlsHeight, itemCount, itemWidth } = props;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_ref, setContainerRef, { width: containerWidth }] = useDimensions();
const [pageSize, setPageSize] = useState<number>(0);
const [currentPage, setCurrentPage] = useState<number>(1);
const numberOfPages = Math.ceil(itemCount / pageSize);
const width = containerWidth / (Math.floor(containerWidth / itemWidth));
const hasNextPage = currentPage < numberOfPages && numberOfPages > 1;
const hasPrevPage = currentPage > 1 && numberOfPages > 1;
const handleNextPage = () => setCurrentPage((prevPage) => prevPage + 1);
const handlePrevPage = () => setCurrentPage((prevPage) => prevPage - 1);
const renderChildren = () => {
if (typeof children === 'function') {
return children({ width: width || 'auto' });
}
return children;
};
useEffect(() => {
if (containerWidth) {
setPageSize(Math.round(containerWidth / width));
}
}, [containerWidth, width]);
return (
<HStack alignItems='stretch'>
<div
className='z-10 flex w-5 items-center justify-center self-stretch rounded-l-xl bg-white dark:bg-primary-900'
style={{
height: controlsHeight || 'auto',
}}
>
<button
data-testid='prev-page'
onClick={handlePrevPage}
className='flex h-full w-7 items-center justify-center transition-opacity duration-500 disabled:opacity-25'
disabled={!hasPrevPage}
>
<Icon
src={require('@tabler/icons/chevron-left.svg')}
className='h-5 w-5 text-black dark:text-white'
/>
</button>
</div>
<div className='relative w-full overflow-hidden'>
<HStack
alignItems='center'
style={{
transform: `translateX(-${(currentPage - 1) * 100}%)`,
}}
className='transition-all duration-500 ease-out'
ref={setContainerRef}
>
{renderChildren()}
</HStack>
</div>
<div
className='z-10 flex w-5 items-center justify-center self-stretch rounded-r-xl bg-white dark:bg-primary-900'
style={{
height: controlsHeight || 'auto',
}}
>
<button
data-testid='next-page'
onClick={handleNextPage}
className='flex h-full w-7 items-center justify-center transition-opacity duration-500 disabled:opacity-25'
disabled={!hasNextPage}
>
<Icon
src={require('@tabler/icons/chevron-right.svg')}
className='h-5 w-5 text-black dark:text-white'
/>
</button>
</div>
</HStack>
);
};
export default Carousel;