import React, { useState, FunctionComponent, KeyboardEvent } from 'react'; import { Arrow } from '../arrow'; import { ItemProvider } from '../item'; import { rotateItems, getTransformAmount, getCurrent, initItems, getShowArrow, cleanItems, updateNodes, } from '../../helpers'; import { SlideDirection, Item, ArrowKeys } from '../../types/carousel'; import { defaultProps } from './defaultProps'; import styles from '../../styles/styles.module.css'; export const Carousel: FunctionComponent = (userProps: CarouselProps) => { const props: Required = { ...defaultProps, ...userProps }; const [items, setItems] = useState( initItems(props.children, props.slide, props.infinite), ); const [width, setWidth] = useState(0); const [animation, setAnimation] = useState({ transform: 0, transition: 0, isSliding: false, }); const [current, setCurrent] = useState(0); const [showArrow, setShowArrow] = useState( getShowArrow(props.children.length, props.show, props.infinite, current), ); if (props.dynamic) { React.useEffect(() => { const newItems = updateNodes(items, userProps.children); setItems(newItems); }, userProps.children); } const slide = (direction: SlideDirection, slide: number): void => { if ( animation.isSliding || (direction === SlideDirection.Right && !showArrow.right) || (direction === SlideDirection.Left && !showArrow.left) ) { return; } const next = getCurrent(current, slide, props.children.length, direction); const rotated = props.infinite ? rotateItems(props.children, items, next, props.show, slide, direction) : items; if (props.infinite && direction === SlideDirection.Right) { setItems(rotated); } setAnimation({ transform: animation.transform + getTransformAmount(width, slide, direction), transition: props.transition, isSliding: true, }); setCurrent(next); setShowArrow( getShowArrow(props.children.length, props.show, props.infinite, next), ); setTimeout(() => { if (props.infinite) { setItems(cleanItems(rotated, slide, direction)); } setAnimation({ transform: props.infinite ? getTransformAmount(width, slide, SlideDirection.Right) : animation.transform + getTransformAmount(width, slide, direction), transition: 0, isSliding: false, }); }, props.transition * 1_0_0_0); }; const widthCallBack = (calculatedWidth: number) => { setWidth(calculatedWidth); setAnimation({ transform: props.infinite ? getTransformAmount(calculatedWidth, props.slide, SlideDirection.Right) : 0, transition: 0, isSliding: false, }); }; const dragCallback = (translateX: number) => { setAnimation({ transform: translateX, transition: props.transition, isSliding: false, }); setTimeout( () => setAnimation({ ...animation, transition: 0 }), props.transition * 1_0_0_0, ); }; const slideCallback = (direction: SlideDirection) => { slide(direction, props.slide); }; const handleOnKeyDown = (e: KeyboardEvent) => { if (e.keyCode === ArrowKeys.Left) { slide(SlideDirection.Left, props.slide); } else if (e.keyCode === ArrowKeys.Right) { slide(SlideDirection.Right, props.slide); } }; return (
{showArrow.left && ( slide(SlideDirection.Left, props.slide)} /> )} {showArrow.right && ( slide(SlideDirection.Right, props.slide)} /> )}
); }; export interface CarouselProps { children: Item[]; show: number; slide: number; transition?: number; swiping?: boolean; swipeOn?: number; responsive?: boolean; infinite?: boolean; className?: string; useArrowKeys?: boolean; a11y?: { [key: string]: string }; dynamic: boolean; } export interface CarouselState { items: Item[]; width: number; transform: number; transition: number; isSliding: boolean; current: number; }