import React, { FunctionComponent, useCallback, useState, MouseEvent, TouchEvent, } from 'react'; import { Item, SlideDirection } from '../../types/carousel'; import { getPageX } from '../../helpers'; import { useWindowWidthChange } from '../../hooks'; import styles from '../../styles/styles.module.css'; export const ItemProviderBase: FunctionComponent<ItemProviderProps> = ( props: ItemProviderProps, ) => { const [width, setWidth] = useState(200); const ref = useCallback( (node) => { if (node !== null) { const calculated = node.getBoundingClientRect().width / props.show; setWidth(calculated); props.widthCallBack(calculated, props.slide); } }, [width], ); // tslint:disable-next-line: no-unused-expression props.responsive && useWindowWidthChange((change: number) => { setWidth(width - change); }); const [drag, setDrag] = useState({ initial: props.transform, start: 0, isDown: false, drag: 0, finished: true, pointers: true, }); const handleDragStart = (e: MouseEvent | TouchEvent) => { e.persist(); setDrag({ ...drag, isDown: true, start: getPageX(e), initial: props.transform, finished: false, }); }; const handleDragFinish = (e: MouseEvent | TouchEvent) => { e.persist(); if (drag.finished) { return; } if (Math.abs(drag.drag) < width * props.swipeOn) { props.dragCallback(props.transform); return setDrag({ initial: props.transform, start: 0, isDown: false, drag: 0, finished: true, pointers: true, }); } props.slideCallback(drag.drag > 0 ? SlideDirection.Right : SlideDirection.Left); setDrag({ ...drag, drag: 0, isDown: false, finished: true, pointers: true }); return; }; const handleDragMove = (e: MouseEvent | TouchEvent) => { e.persist(); if (!drag.isDown) { return; } const pos = getPageX(e); setDrag({ ...drag, drag: drag.start - pos, pointers: false }); }; const swipeProps = props.swiping ? { onTouchCancel: handleDragFinish, onTouchEnd: handleDragFinish, onTouchMove: handleDragMove, onTouchStart: handleDragStart, onMouseDown: handleDragStart, onMouseLeave: handleDragFinish, onMouseUp: handleDragFinish, onMouseMove: handleDragMove, } : {}; return ( <div ref={ref} className={styles.itemProvider}> <div data-testid="trackList" {...swipeProps} className={styles.itemTracker} style={{ transform: `translateX(${props.transform - drag.drag}px)`, transition: `transform ${props.transition}s ease 0s`, width: width * props.items.length, }} > {props.items.map((item, i) => ( <div key={i} style={{ width, pointerEvents: drag.pointers ? 'all' : 'none' }} className={styles.itemContainer} > {item} </div> ))} </div> </div> ); }; export const ItemProvider = React.memo(ItemProviderBase); export interface ItemProviderProps { items: Item[]; show: number; slide: number; widthCallBack: (width: number, slide: number) => void; dragCallback: (transform: number) => void; slideCallback: (direction: SlideDirection) => void; transition: number; transform: number; swiping: boolean; swipeOn: number; responsive: boolean; infinite: boolean; }