Skip to content

Commit dc1bba6

Browse files
authored
Merge pull request #9 from faceless-ui/fix/drag-snap
Fix/drag snap
2 parents 6bceb82 + 2c02661 commit dc1bba6

File tree

8 files changed

+53
-26
lines changed

8 files changed

+53
-26
lines changed

demo/App.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React from 'react';
2-
import MarqueeSliderDemo from './MarqueeSliderDemo';
2+
// import MarqueeSliderDemo from './MarqueeSliderDemo';
33
// import FreeScrollSliderDemo from './FreeScrollSliderDemo';
44
// import ThumbnailSliderDemo from './ThumbnailSliderDemo';
5-
// import ScrollSnapSliderDemo from './ScrollSnapSliderDemo';
5+
import ScrollSnapSliderDemo from './ScrollSnapSliderDemo';
66

77
const App: React.FC = () => (
88
<div
@@ -15,18 +15,18 @@ const App: React.FC = () => (
1515
Free Scroll Slider:
1616
</h1> */}
1717
{/* <FreeScrollSliderDemo /> */}
18-
{/* <h1>
18+
<h1>
1919
Scroll Snap Slider:
20-
</h1> */}
21-
{/* <ScrollSnapSliderDemo /> */}
20+
</h1>
21+
<ScrollSnapSliderDemo />
2222
{/* <h1>
2323
Thumbnail Slider Demo:
2424
</h1>
2525
<ThumbnailSliderDemo /> */}
26-
<h1>
26+
{/* <h1>
2727
Marquee Slider Demo
2828
</h1>
29-
<MarqueeSliderDemo />
29+
<MarqueeSliderDemo /> */}
3030
</div>
3131
);
3232

demo/ScrollSnapSliderDemo/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ const ScrollSnapSliderDemo: React.FC = () => {
2828
</button>
2929
<SliderProvider
3030
slidesToShow={1}
31-
autoPlay={autoPlay}
32-
dragScroll
31+
autoPlay
32+
scrollSnap
3333
>
3434
<div
3535
style={{

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@faceless-ui/slider",
3-
"version": "1.1.12",
3+
"version": "1.1.13",
44
"main": "dist/index.js",
55
"types": "dist/index.d.ts",
66
"homepage:": "https://facelessui.com/docs/slider",

src/Slide/index.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ const Slide: React.FC<SlideProps> = (props) => {
2626
index,
2727
htmlElement = 'div',
2828
children,
29-
style: styleFromProps,
29+
style,
3030
onClick: onClickFromProps,
3131
...rest
3232
} = props;
3333

34-
const [style, setStyle] = useState<React.CSSProperties | undefined>();
34+
const [snapStyles, setSnapStyles] = useState<React.CSSProperties | undefined>();
3535
const slider = useSlider();
3636
const slideRef = useRef<HTMLElement | null>(null);
3737

@@ -70,14 +70,15 @@ const Slide: React.FC<SlideProps> = (props) => {
7070
index,
7171
]);
7272

73+
// here
7374
useEffect(() => {
7475
if (scrollSnap) {
75-
setStyle({
76+
setSnapStyles({
7677
scrollSnapStop: 'always',
7778
scrollSnapAlign: 'start',
7879
})
7980
} else {
80-
setStyle(undefined);
81+
setSnapStyles(undefined);
8182
}
8283
}, [scrollSnap]);
8384

@@ -101,7 +102,7 @@ const Slide: React.FC<SlideProps> = (props) => {
101102
flexShrink: 0,
102103
width: slideWidth,
103104
...style || {},
104-
...styleFromProps || {},
105+
...snapStyles || {},
105106
}
106107

107108
return (

src/SliderProvider/context.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface ISliderContext extends Omit<SliderProviderProps, 'children'> {
1919
slideWidth?: string
2020
isPaused?: boolean
2121
setIsPaused: (is: boolean) => void // eslint-disable-line no-unused-vars
22+
isDragging: boolean
2223
}
2324

2425
export const SliderContext = createContext<ISliderContext>({} as ISliderContext);

src/SliderProvider/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ const SliderProvider: React.FC<SliderProviderProps> = (props) => {
7979
const [isPaused, setIsPaused] = useState(false);
8080
const [isFullyScrolled, setIsFullyScrolled] = useState(false);
8181
const sliderTrackRef = useRef<HTMLDivElement>(null);
82+
const [isDragging, setIsDragging] = useState(false);
8283

8384
const [sliderState, dispatchSliderState] = useReducer(reducer, {
8485
currentSlideIndex: slideIndexFromProps,
@@ -89,7 +90,9 @@ const SliderProvider: React.FC<SliderProviderProps> = (props) => {
8990
useDragScroll({
9091
ref: sliderTrackRef,
9192
scrollYAxis: false,
92-
enable: dragScroll || (scrollable && dragScroll !== false)
93+
enable: dragScroll || (scrollable && dragScroll !== false),
94+
onDrag: () => { setIsDragging(true) },
95+
onDragEnd: () => { setIsDragging(false) },
9396
});
9497

9598
useMarquee({
@@ -206,7 +209,8 @@ const SliderProvider: React.FC<SliderProviderProps> = (props) => {
206209
setIsPaused,
207210
isPaused,
208211
pauseOnHover,
209-
alignLastSlide
212+
alignLastSlide,
213+
isDragging
210214
};
211215

212216
return (

src/SliderProvider/useDragScroll.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ type Args = {
55
scrollYAxis?: boolean
66
enable?: boolean
77
ref: React.MutableRefObject<HTMLDivElement | null>
8+
onDrag: () => void
9+
onDragEnd: () => void
810
}
911

1012
export type UseDraggable = (args?: Args) => null // eslint-disable-line no-unused-vars
@@ -14,7 +16,9 @@ export const useDraggable: UseDraggable = (args) => {
1416
buttons = [1, 4, 5], // See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
1517
scrollYAxis,
1618
enable,
17-
ref
19+
ref,
20+
onDrag,
21+
onDragEnd
1822
} = args || {};
1923

2024
// Position of the mouse on the page on mousedown
@@ -37,6 +41,9 @@ export const useDraggable: UseDraggable = (args) => {
3741
setStartY(e.pageY - ref.current.offsetTop);
3842
setStartScrollLeft(ref.current.scrollLeft);
3943
setStartScrollTop(ref.current.scrollTop);
44+
if (typeof onDrag === 'function') {
45+
onDrag();
46+
}
4047
}
4148
};
4249

@@ -57,15 +64,16 @@ export const useDraggable: UseDraggable = (args) => {
5764
}
5865

5966
e.preventDefault();
67+
6068
// Position of mouse on the page
6169
const mouseX = e.pageX - ref.current.offsetLeft;
6270
const mouseY = e.pageY - ref.current.offsetTop;
6371
// Distance of the mouse from the origin of the last mousedown event
64-
const walkX = mouseX - startX;
65-
const walkY = mouseY - startY;
66-
// Set element scroll
67-
ref.current.scrollLeft = startScrollLeft - walkX;
68-
const newScrollTop = startScrollTop - walkY;
72+
const xDisplacement = mouseX - startX;
73+
const yDisplacement = mouseY - startY;
74+
// Finally, set the element's scroll
75+
ref.current.scrollLeft = startScrollLeft - xDisplacement;
76+
const newScrollTop = startScrollTop - yDisplacement;
6977
if (scrollYAxis !== false) {
7078
ref.current.scrollTop = newScrollTop;
7179
}
@@ -80,6 +88,9 @@ export const useDraggable: UseDraggable = (args) => {
8088
const childAsElement = child as HTMLElement;
8189
childAsElement.style.removeProperty('pointer-events');
8290
});
91+
if (typeof onDragEnd === 'function') {
92+
onDragEnd();
93+
}
8394
}
8495
};
8596

@@ -101,7 +112,9 @@ export const useDraggable: UseDraggable = (args) => {
101112
startY,
102113
scrollYAxis,
103114
enable,
104-
ref
115+
ref,
116+
onDragEnd,
117+
onDrag
105118
]);
106119

107120
return null;

src/SliderTrack/index.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ const SliderTrack: React.FC<SliderTrackProps> = (props) => {
2525
scrollSnap,
2626
setIsPaused,
2727
pauseOnHover,
28-
alignLastSlide
28+
alignLastSlide,
29+
isDragging
2930
} = sliderContext;
3031

3132
const hasAddedScrollListener = useRef(false);
@@ -85,6 +86,7 @@ const SliderTrack: React.FC<SliderTrackProps> = (props) => {
8586
track.addEventListener('wheel', (e) => { e.preventDefault() })
8687
}
8788
}
89+
8890
return () => {
8991
if (track) {
9092
track.removeEventListener('scroll', onScroll);
@@ -98,6 +100,11 @@ const SliderTrack: React.FC<SliderTrackProps> = (props) => {
98100

99101
const ghostSlideWidth = getGhostSlideWidth(sliderContext);
100102

103+
let scrollSnapType;
104+
if (scrollSnap && slideWidth) {
105+
scrollSnapType = !isDragging ? 'x mandatory' : 'none';
106+
}
107+
101108
return (
102109
<Tag
103110
{...{
@@ -107,7 +114,8 @@ const SliderTrack: React.FC<SliderTrackProps> = (props) => {
107114
display: 'flex',
108115
overflowX: 'scroll', // NOTE: 'WebkitOverflowScrolling: touch' does not work when 'auto'
109116
WebkitOverflowScrolling: 'touch',
110-
scrollSnapType: (scrollSnap && slideWidth) ? 'x mandatory' : undefined, // NOTE: only apply after slide width has populated
117+
// NOTE: only apply after slide width has populated and while NOT dragging
118+
scrollSnapType,
111119
...style,
112120
},
113121
ref: sliderTrackRef,

0 commit comments

Comments
 (0)