diff --git a/demo/App.tsx b/demo/App.tsx index 6232e96..3eb9d4f 100644 --- a/demo/App.tsx +++ b/demo/App.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import MarqueeSliderDemo from './MarqueeSliderDemo'; +// import MarqueeSliderDemo from './MarqueeSliderDemo'; // import FreeScrollSliderDemo from './FreeScrollSliderDemo'; // import ThumbnailSliderDemo from './ThumbnailSliderDemo'; -// import ScrollSnapSliderDemo from './ScrollSnapSliderDemo'; +import ScrollSnapSliderDemo from './ScrollSnapSliderDemo'; const App: React.FC = () => (
( Free Scroll Slider: */} {/* */} - {/*

+

Scroll Snap Slider: -

*/} - {/* */} + + {/*

Thumbnail Slider Demo:

*/} -

+ {/*

Marquee Slider Demo

- + */}
); diff --git a/demo/ScrollSnapSliderDemo/index.tsx b/demo/ScrollSnapSliderDemo/index.tsx index 97d69ac..b38000d 100644 --- a/demo/ScrollSnapSliderDemo/index.tsx +++ b/demo/ScrollSnapSliderDemo/index.tsx @@ -28,8 +28,8 @@ const ScrollSnapSliderDemo: React.FC = () => {
= (props) => { index, htmlElement = 'div', children, - style: styleFromProps, + style, onClick: onClickFromProps, ...rest } = props; - const [style, setStyle] = useState(); + const [snapStyles, setSnapStyles] = useState(); const slider = useSlider(); const slideRef = useRef(null); @@ -70,14 +70,15 @@ const Slide: React.FC = (props) => { index, ]); + // here useEffect(() => { if (scrollSnap) { - setStyle({ + setSnapStyles({ scrollSnapStop: 'always', scrollSnapAlign: 'start', }) } else { - setStyle(undefined); + setSnapStyles(undefined); } }, [scrollSnap]); @@ -101,7 +102,7 @@ const Slide: React.FC = (props) => { flexShrink: 0, width: slideWidth, ...style || {}, - ...styleFromProps || {}, + ...snapStyles || {}, } return ( diff --git a/src/SliderProvider/context.tsx b/src/SliderProvider/context.tsx index 27085ad..2cff568 100644 --- a/src/SliderProvider/context.tsx +++ b/src/SliderProvider/context.tsx @@ -19,6 +19,7 @@ export interface ISliderContext extends Omit { slideWidth?: string isPaused?: boolean setIsPaused: (is: boolean) => void // eslint-disable-line no-unused-vars + isDragging: boolean } export const SliderContext = createContext({} as ISliderContext); diff --git a/src/SliderProvider/index.tsx b/src/SliderProvider/index.tsx index 3871368..efde438 100644 --- a/src/SliderProvider/index.tsx +++ b/src/SliderProvider/index.tsx @@ -79,6 +79,7 @@ const SliderProvider: React.FC = (props) => { const [isPaused, setIsPaused] = useState(false); const [isFullyScrolled, setIsFullyScrolled] = useState(false); const sliderTrackRef = useRef(null); + const [isDragging, setIsDragging] = useState(false); const [sliderState, dispatchSliderState] = useReducer(reducer, { currentSlideIndex: slideIndexFromProps, @@ -89,7 +90,9 @@ const SliderProvider: React.FC = (props) => { useDragScroll({ ref: sliderTrackRef, scrollYAxis: false, - enable: dragScroll || (scrollable && dragScroll !== false) + enable: dragScroll || (scrollable && dragScroll !== false), + onDrag: () => { setIsDragging(true) }, + onDragEnd: () => { setIsDragging(false) }, }); useMarquee({ @@ -206,7 +209,8 @@ const SliderProvider: React.FC = (props) => { setIsPaused, isPaused, pauseOnHover, - alignLastSlide + alignLastSlide, + isDragging }; return ( diff --git a/src/SliderProvider/useDragScroll.ts b/src/SliderProvider/useDragScroll.ts index adfbb77..dc786de 100644 --- a/src/SliderProvider/useDragScroll.ts +++ b/src/SliderProvider/useDragScroll.ts @@ -5,6 +5,8 @@ type Args = { scrollYAxis?: boolean enable?: boolean ref: React.MutableRefObject + onDrag: () => void + onDragEnd: () => void } export type UseDraggable = (args?: Args) => null // eslint-disable-line no-unused-vars @@ -14,7 +16,9 @@ export const useDraggable: UseDraggable = (args) => { buttons = [1, 4, 5], // See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons scrollYAxis, enable, - ref + ref, + onDrag, + onDragEnd } = args || {}; // Position of the mouse on the page on mousedown @@ -37,6 +41,9 @@ export const useDraggable: UseDraggable = (args) => { setStartY(e.pageY - ref.current.offsetTop); setStartScrollLeft(ref.current.scrollLeft); setStartScrollTop(ref.current.scrollTop); + if (typeof onDrag === 'function') { + onDrag(); + } } }; @@ -57,15 +64,16 @@ export const useDraggable: UseDraggable = (args) => { } e.preventDefault(); + // Position of mouse on the page const mouseX = e.pageX - ref.current.offsetLeft; const mouseY = e.pageY - ref.current.offsetTop; // Distance of the mouse from the origin of the last mousedown event - const walkX = mouseX - startX; - const walkY = mouseY - startY; - // Set element scroll - ref.current.scrollLeft = startScrollLeft - walkX; - const newScrollTop = startScrollTop - walkY; + const xDisplacement = mouseX - startX; + const yDisplacement = mouseY - startY; + // Finally, set the element's scroll + ref.current.scrollLeft = startScrollLeft - xDisplacement; + const newScrollTop = startScrollTop - yDisplacement; if (scrollYAxis !== false) { ref.current.scrollTop = newScrollTop; } @@ -80,6 +88,9 @@ export const useDraggable: UseDraggable = (args) => { const childAsElement = child as HTMLElement; childAsElement.style.removeProperty('pointer-events'); }); + if (typeof onDragEnd === 'function') { + onDragEnd(); + } } }; @@ -101,7 +112,9 @@ export const useDraggable: UseDraggable = (args) => { startY, scrollYAxis, enable, - ref + ref, + onDragEnd, + onDrag ]); return null; diff --git a/src/SliderTrack/index.tsx b/src/SliderTrack/index.tsx index 9806666..709e8a4 100644 --- a/src/SliderTrack/index.tsx +++ b/src/SliderTrack/index.tsx @@ -25,7 +25,8 @@ const SliderTrack: React.FC = (props) => { scrollSnap, setIsPaused, pauseOnHover, - alignLastSlide + alignLastSlide, + isDragging } = sliderContext; const hasAddedScrollListener = useRef(false); @@ -85,6 +86,7 @@ const SliderTrack: React.FC = (props) => { track.addEventListener('wheel', (e) => { e.preventDefault() }) } } + return () => { if (track) { track.removeEventListener('scroll', onScroll); @@ -98,6 +100,11 @@ const SliderTrack: React.FC = (props) => { const ghostSlideWidth = getGhostSlideWidth(sliderContext); + let scrollSnapType; + if (scrollSnap && slideWidth) { + scrollSnapType = !isDragging ? 'x mandatory' : 'none'; + } + return ( = (props) => { display: 'flex', overflowX: 'scroll', // NOTE: 'WebkitOverflowScrolling: touch' does not work when 'auto' WebkitOverflowScrolling: 'touch', - scrollSnapType: (scrollSnap && slideWidth) ? 'x mandatory' : undefined, // NOTE: only apply after slide width has populated + // NOTE: only apply after slide width has populated and while NOT dragging + scrollSnapType, ...style, }, ref: sliderTrackRef,