Skip to content

Commit

Permalink
feat: supports scrollable, dragScroll, and scrollSnap props
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobsfletch committed Aug 23, 2022
1 parent 22d8215 commit e8f2884
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 26 deletions.
7 changes: 5 additions & 2 deletions demo/FreeScrollSliderDemo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ const FreeScrollSliderDemo: React.FC = () => (
<pre>
slidesToShow: 2
<br />
useFreeScroll: true
scrollable: true
</pre>
</code>
<SliderProvider
slidesToShow={2}
useFreeScroll
// scrollable
dragScroll
autoPlay
pauseOnHover={false}
>
<div
style={{
Expand Down
1 change: 1 addition & 0 deletions demo/ScrollSnapSliderDemo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const ScrollSnapSliderDemo: React.FC = () => {
<SliderProvider
slidesToShow={1}
autoPlay={autoPlay}
dragScroll
>
<div
style={{
Expand Down
4 changes: 2 additions & 2 deletions src/Slide/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const Slide: React.FC<Props> = (props) => {
goToSlideIndex,
slideWidth,
slideOnSelect,
useFreeScroll,
scrollSnap,
scrollOffset,
} = slider;

Expand Down Expand Up @@ -94,7 +94,7 @@ const Slide: React.FC<Props> = (props) => {
style: {
flexShrink: 0,
width: slideWidth,
scrollSnapAlign: !useFreeScroll ? 'start' : undefined,
scrollSnapAlign: scrollSnap ? 'start' : undefined,
scrollSnapStop: 'always',
...style,
},
Expand Down
37 changes: 26 additions & 11 deletions src/SliderProvider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ export type Props = {
onSlide?: (index: number) => void // eslint-disable-line no-unused-vars
slidesToShow?: number
slideOnSelect?: boolean
useFreeScroll?: boolean
scrollable?: boolean
useFreeScroll?: boolean // IMPORTANT: being deprecated, use `scrollable` instead
dragScroll?: boolean
scrollSnap?: boolean
scrollOffset?: number
autoPlay?: boolean
autoplaySpeed?: number
Expand All @@ -33,18 +36,32 @@ const SliderProvider: React.FC<Props> = (props) => {
onSlide,
slidesToShow = 3,
slideOnSelect,
scrollable: scrollableFromProps = true,
useFreeScroll,
dragScroll,
scrollSnap,
scrollOffset = 0,
autoPlay,
autoplaySpeed = 2000,
pauseOnHover = true,
pause,
useGhostSlide,
currentSlideIndex: slideIndexFromProps = 0
currentSlideIndex: slideIndexFromProps = 0,
} = props;

const sliderTrackRef = useDragScroll({
if (useFreeScroll !== undefined) {
console.warn('`useFreeScroll` prop will be deprecated in the next major release, use `scrollable` instead (`true` by default)');
}

// NOTE: this this only while `useFreeScroll` is still supported, see warning above
const scrollable = scrollableFromProps === undefined ? useFreeScroll : scrollableFromProps;

const sliderTrackRef = useRef<HTMLDivElement>(null);

useDragScroll({
ref: sliderTrackRef,
scrollYAxis: false,
enable: dragScroll || (scrollable && dragScroll !== false)
});

const [scrollRatio, setScrollRatio] = useState(0);
Expand Down Expand Up @@ -118,7 +135,7 @@ const SliderProvider: React.FC<Props> = (props) => {
type: 'GO_TO_NEXT_SLIDE',
payload: {
loop: true,
isFullyScrolled
isFullyScrolled,
},
});
}, autoplaySpeed);
Expand Down Expand Up @@ -175,7 +192,6 @@ const SliderProvider: React.FC<Props> = (props) => {
type: 'GO_TO_SLIDE_INDEX',
payload: {
index: slideIndexFromProps,
scrollToIndex
},
});
}
Expand All @@ -192,17 +208,15 @@ const SliderProvider: React.FC<Props> = (props) => {
dispatchSliderState({
type: 'GO_TO_NEXT_SLIDE',
payload: {
loop: !useFreeScroll,
scrollToIndex
loop: !scrollable,
},
});
},
goToPrevSlide: () => {
dispatchSliderState({
type: 'GO_TO_PREV_SLIDE',
payload: {
loop: !useFreeScroll,
scrollToIndex
loop: !scrollable,
},
});
},
Expand All @@ -211,7 +225,6 @@ const SliderProvider: React.FC<Props> = (props) => {
type: 'GO_TO_SLIDE_INDEX',
payload: {
index,
scrollToIndex
}
});
},
Expand All @@ -226,7 +239,9 @@ const SliderProvider: React.FC<Props> = (props) => {
slideWidth,
slidesToShow,
slideOnSelect,
useFreeScroll,
scrollable,
dragScroll,
scrollSnap,
scrollOffset,
setIsPaused,
isPaused,
Expand Down
19 changes: 11 additions & 8 deletions src/SliderProvider/useDragScroll.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useState } from 'react';

type Options = {
buttons?: number[]
scrollYAxis?: boolean
enable?: boolean
ref: React.MutableRefObject<HTMLDivElement | null>
}

type UseDraggable = (options?: Options) => React.MutableRefObject<HTMLDivElement | null> // eslint-disable-line no-unused-vars
type UseDraggable = (options?: Options) => null // eslint-disable-line no-unused-vars

/**
* Make an element scrollable by dragging
Expand All @@ -16,10 +18,10 @@ export const useDraggable: UseDraggable = (options) => {
const {
buttons = [1, 4, 5],
scrollYAxis,
enable,
ref
} = options || {};

// Ref to be attached to the element we want to drag
const ref = useRef<HTMLDivElement>(null);
// Position of the mouse on the page on mousedown
const [startX, setStartX] = useState(0);
const [startY, setStartY] = useState(0);
Expand All @@ -29,7 +31,7 @@ export const useDraggable: UseDraggable = (options) => {

useEffect(() => {
const handleDown = (e: MouseEvent) => {
if (ref.current) {
if (ref?.current && enable) {
// Only allow dragging inside of target element
if (!ref.current.contains(e.target as Node)) {
return;
Expand All @@ -44,7 +46,7 @@ export const useDraggable: UseDraggable = (options) => {
};

const handleMove = (e: MouseEvent) => {
if (ref.current) {
if (ref?.current && enable) {
// Don't fire if other buttons are pressed
if (!buttons.includes(e.buttons) || !ref.current.contains(e.target as Node)) {
return;
Expand Down Expand Up @@ -77,7 +79,7 @@ export const useDraggable: UseDraggable = (options) => {

const handleUp = () => {
// reenable nested anchor links after dragging
if (ref.current?.children) {
if (ref?.current?.children && enable) {
const childrenAsArray = Array.from(ref.current.children);
childrenAsArray.forEach((child) => {
const childAsElement = child as HTMLElement;
Expand All @@ -103,9 +105,10 @@ export const useDraggable: UseDraggable = (options) => {
startX,
startY,
scrollYAxis,
enable
]);

return ref;
return null;
};

export default useDraggable;
24 changes: 21 additions & 3 deletions src/SliderTrack/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ const SliderTrack: React.FC<SliderTrackProps> = (props) => {
setScrollRatio,
slideWidth,
slidesToShow,
useFreeScroll,
scrollable,
scrollSnap,
setIsPaused,
pauseOnHover,
useGhostSlide
Expand Down Expand Up @@ -53,6 +54,7 @@ const SliderTrack: React.FC<SliderTrackProps> = (props) => {
getScrollRatio,
]);

// NOTE: handle updates to the track's current scroll, which could originate from either the user or the program
useEffect(() => {
const track = sliderTrackRef.current;

Expand All @@ -72,16 +74,32 @@ const SliderTrack: React.FC<SliderTrackProps> = (props) => {
onScroll,
]);

// NOTE: if the user does not want scroll enabled, we need to remove the event listener without canceling programmatic scroll
useEffect(() => {
const track = sliderTrackRef.current;

if (track) {
if (!scrollable) {
track.addEventListener('wheel', (e) => { e.preventDefault() })
}
}
return () => {
if (track) {
track.removeEventListener('scroll', onScroll);
}
}
}, [scrollable])

return (
<Tag
{...{
...rest,
style: {
position: 'relative',
display: 'flex',
overflowX: 'scroll', // 'overflow: touch' does not work when 'auto'
overflowX: 'scroll', // NOTE: 'WebkitOverflowScrolling: touch' does not work when 'auto'
WebkitOverflowScrolling: 'touch',
scrollSnapType: (slideWidth && !useFreeScroll) ? 'x mandatory' : undefined, // only apply after slide width has populated
scrollSnapType: (scrollSnap && slideWidth) ? 'x mandatory' : undefined, // NOTE: only apply after slide width has populated
...style,
},
ref: sliderTrackRef,
Expand Down

0 comments on commit e8f2884

Please sign in to comment.