|
@@ -0,0 +1,161 @@
|
|
|
|
+import React, {useRef, useEffect, useState} from 'react'
|
|
|
|
+
|
|
|
|
+export default ({images = [], className, onChange, onClick, ...props}) => {
|
|
|
|
+ const divRef = useRef()
|
|
|
|
+ const containerRef = useRef()
|
|
|
|
+ const autoscroll = useRef(false)
|
|
|
|
+ const inTouch = useRef(false)
|
|
|
|
+ const timeout = useRef(false)
|
|
|
|
+
|
|
|
|
+ const [imgs, setImgs] = useState([])
|
|
|
|
+ const [current, setCurrent] = useState(0)
|
|
|
|
+ const currentScrollPosition = useRef(0)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ if (divRef.current){
|
|
|
|
+ const div = divRef.current
|
|
|
|
+
|
|
|
|
+ const totalWidth = imgs.reduce((total, img) => total + (img?.getBoundingClientRect().width || 0),0)
|
|
|
|
+ div.style.minWidth = totalWidth + 'px'
|
|
|
|
+ }
|
|
|
|
+ }, [divRef, imgs])
|
|
|
|
+
|
|
|
|
+ const scrollToCurrent = () => {
|
|
|
|
+ if (autoscroll.current || !containerRef.current) return;
|
|
|
|
+
|
|
|
|
+ const el = containerRef.current
|
|
|
|
+
|
|
|
|
+ const step = () => {
|
|
|
|
+ if (!el) return;
|
|
|
|
+
|
|
|
|
+ autoscroll.current = true
|
|
|
|
+ const {scrollLeft} = el
|
|
|
|
+ const diff = (currentScrollPosition.current - scrollLeft) /5
|
|
|
|
+ if (Math.abs(diff) > 1){
|
|
|
|
+ el.scrollTo(scrollLeft + diff, 0)
|
|
|
|
+ setTimeout(step, 20)
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ el.scrollTo(currentScrollPosition.current, 0)
|
|
|
|
+ autoscroll.current = false
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ step()
|
|
|
|
+ //if (milaCount !== current +1) set(current +1)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const onTouchEnd = () => {
|
|
|
|
+ if (inTouch.current){
|
|
|
|
+ if (timeout.current) clearInterval(timeout.current)
|
|
|
|
+
|
|
|
|
+ timeout.current = setTimeout(onTouchEnd, 200)
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const {scrollLeft, scrollWidth} = containerRef.current;
|
|
|
|
+ const viewPortWidth = containerRef.current.getBoundingClientRect().width
|
|
|
|
+
|
|
|
|
+ let newCurrent;
|
|
|
|
+ if (scrollLeft === 0) {
|
|
|
|
+ newCurrent = 0
|
|
|
|
+ currentScrollPosition.current = 0
|
|
|
|
+ }
|
|
|
|
+ else if (scrollLeft >= scrollWidth - viewPortWidth) {
|
|
|
|
+ newCurrent = (images.length -1)
|
|
|
|
+ currentScrollPosition.current = scrollWidth - viewPortWidth
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ let imgsWidth = 0
|
|
|
|
+ let i = 0
|
|
|
|
+ for (const img of imgs){
|
|
|
|
+ const imgWidth = img?.getBoundingClientRect().width || 0
|
|
|
|
+ imgsWidth += imgWidth
|
|
|
|
+ if (imgsWidth > scrollLeft){
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ i++
|
|
|
|
+ }
|
|
|
|
+ const offset = imgsWidth - scrollLeft
|
|
|
|
+ newCurrent = i
|
|
|
|
+ currentScrollPosition.current = imgsWidth - (imgs?.[i].getBoundingClientRect().width || 0)
|
|
|
|
+ if (offset < viewPortWidth/2){
|
|
|
|
+ currentScrollPosition.current = imgsWidth
|
|
|
|
+ newCurrent++
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ setCurrent(newCurrent)
|
|
|
|
+ if (newCurrent === current) scrollToCurrent()
|
|
|
|
+ else if (typeof onChange === 'function') onChange(newCurrent)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const onScroll = e => {
|
|
|
|
+ autoscroll.current = false
|
|
|
|
+ //if (autoscroll.current) return;
|
|
|
|
+
|
|
|
|
+ if (timeout.current) clearInterval(timeout.current)
|
|
|
|
+
|
|
|
|
+ timeout.current = setTimeout(onTouchEnd, 200)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ useEffect(scrollToCurrent, [current])
|
|
|
|
+
|
|
|
|
+ const isTouchDevice = () => (navigator.maxTouchPoints || 'ontouchstart' in document.documentElement);
|
|
|
|
+
|
|
|
|
+ const onImgClick = (e, i) => {
|
|
|
|
+ const touchDevice = isTouchDevice()
|
|
|
|
+ if (touchDevice && typeof onClick === 'function'){
|
|
|
|
+ onClick(i)
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ const viewPortWidth = containerRef.current.getBoundingClientRect().width
|
|
|
|
+ const clickX = e.clientX -e.target.getBoundingClientRect().x
|
|
|
|
+ const {scrollLeft} = containerRef.current;
|
|
|
|
+ if (clickX > viewPortWidth * 0.75) {
|
|
|
|
+ if (i < images.length -1){
|
|
|
|
+ currentScrollPosition.current = scrollLeft + (imgs?.[i].getBoundingClientRect().width || 0)
|
|
|
|
+ setCurrent(i +1)
|
|
|
|
+ scrollToCurrent()
|
|
|
|
+ if (typeof onChange === 'function') onChange(i +1)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (clickX < viewPortWidth * 0.25) {
|
|
|
|
+ if (i > 0){
|
|
|
|
+ currentScrollPosition.current = scrollLeft - (imgs?.[i-1].getBoundingClientRect().width || 0)
|
|
|
|
+ setCurrent(i -1)
|
|
|
|
+ scrollToCurrent()
|
|
|
|
+ if (typeof onChange === 'function') onChange(i -1)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (typeof onClick === 'function'){
|
|
|
|
+ onClick(i)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ <div className={className}
|
|
|
|
+ style={{cssText: `overflow: auto; -ms-overflow-style: none; scrollbar-width: none; overscroll-behavior-x: none;`}}
|
|
|
|
+ onScroll={onScroll} //TODO: do it only on desktop, on touch devices use onTouchEnd
|
|
|
|
+ ref={containerRef}
|
|
|
|
+ {...isTouchDevice() ? {
|
|
|
|
+ onTouchStart(){
|
|
|
|
+ inTouch.current = true
|
|
|
|
+ autoscroll.current = false
|
|
|
|
+ },
|
|
|
|
+ onTouchEnd(){
|
|
|
|
+ inTouch.current = false
|
|
|
|
+ }
|
|
|
|
+ }: {}}
|
|
|
|
+ >
|
|
|
|
+ <div ref={divRef}>
|
|
|
|
+ {images.map((image, i) => <img src={image}
|
|
|
|
+ key={`image_${i}`}
|
|
|
|
+ alt={`image ${i}`}
|
|
|
|
+ onLoad={e => {imgs[i] = e.target; setImgs([...imgs]) }}
|
|
|
|
+ onClick={e => onImgClick(e, i)}
|
|
|
|
+ />)}
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ )
|
|
|
|
+}
|