133 lines
3.2 KiB
133 lines
3.2 KiB
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; |
|
}
|
|
|