You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
4.3 KiB
169 lines
4.3 KiB
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<CarouselProps> = (userProps: CarouselProps) => { |
|
const props: Required<CarouselProps> = { ...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 ( |
|
<div |
|
{...props.a11y} |
|
data-testid="carousel" |
|
tabIndex={0} |
|
{...(props.useArrowKeys ? { onKeyDown: handleOnKeyDown } : {})} |
|
className={`${styles.carouselBase} ${props.className}`} |
|
> |
|
{showArrow.left && ( |
|
<Arrow |
|
direction="left" |
|
onClick={() => slide(SlideDirection.Left, props.slide)} |
|
/> |
|
)} |
|
<ItemProvider |
|
{...props} |
|
transition={animation.transition} |
|
items={items} |
|
transform={animation.transform} |
|
slideCallback={slideCallback} |
|
dragCallback={dragCallback} |
|
widthCallBack={widthCallBack} |
|
/> |
|
{showArrow.right && ( |
|
<Arrow |
|
direction="right" |
|
onClick={() => slide(SlideDirection.Right, props.slide)} |
|
/> |
|
)} |
|
</div> |
|
); |
|
}; |
|
|
|
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; |
|
}
|
|
|