Skip to content

Commit 76ef7d6

Browse files
authored
Add support for x axis (#20)
1 parent c183ce7 commit 76ef7d6

File tree

8 files changed

+4429
-30454
lines changed

8 files changed

+4429
-30454
lines changed

package-lock.json

Lines changed: 4137 additions & 30392 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "1.0.0",
2+
"version": "1.1.0",
33
"description": "A simple, performant, and cross-browser hook for handling scroll in your next react app.",
44
"homepage": "https://andrzejsala.github.io/use-scroll-direction",
55
"keywords": [
@@ -76,11 +76,11 @@
7676
"@size-limit/preset-small-lib": "^5.0.3",
7777
"@storybook/addon-actions": "^6.3.12",
7878
"@storybook/addon-console": "^1.2.3",
79-
"@storybook/addon-essentials": "^6.3.12",
79+
"@storybook/addon-essentials": "^6.4.9",
8080
"@storybook/addon-info": "^5.3.21",
8181
"@storybook/addon-links": "^6.3.12",
8282
"@storybook/addons": "^6.3.12",
83-
"@storybook/react": "^6.3.12",
83+
"@storybook/react": "^6.4.9",
8484
"@types/lodash.debounce": "^4.0.6",
8585
"@types/lodash.throttle": "^4.1.6",
8686
"@types/react": "^17.0.21",

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { RefObject } from 'react';
22

3-
export type ScrollDirectionType = 'UP' | 'DOWN' | 'NONE';
3+
export type ScrollDirectionType = 'UP' | 'DOWN' | 'LEFT' | 'RIGHT' | 'NONE';
44

55
export type OptionsType = Readonly<{
66
wait?: number;

src/useScrollDirection.ts

Lines changed: 92 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -11,102 +11,138 @@ export const useScrollDirection = (options?: OptionsType) => {
1111
const wait = options?.wait || 50;
1212
const timeToReset = options?.timeToReset || 150;
1313
const ref = options?.ref || null;
14+
const throttleOptions = {
15+
trailing: false,
16+
};
1417

1518
const lastYPosition = useRef<number | null>(null);
19+
const lastXPosition = useRef<number | null>(null);
1620
const [direction, setDirection] = useState<ScrollDirectionType>('NONE');
1721

1822
const handleResetDirection = () => {
1923
lastYPosition.current = null;
24+
lastXPosition.current = null;
2025

2126
setDirection('NONE');
2227
};
2328

24-
const handleWindowScroll = () => {
25-
handleResetDirectionTimeout(timeToReset, handleResetDirection);
26-
27-
const currentYPosition = Math.round(window.pageYOffset);
28-
29-
// Skip first iteration
30-
if (lastYPosition.current === null) {
31-
lastYPosition.current = currentYPosition;
32-
33-
return;
34-
}
35-
36-
// Set NONE for the same position
37-
if (currentYPosition === lastYPosition.current) {
38-
setDirection('NONE');
39-
40-
return;
41-
}
42-
43-
const newDirection =
44-
lastYPosition.current < currentYPosition ? 'DOWN' : 'UP';
45-
46-
// If direction has changed, return it
47-
if (newDirection !== direction) {
48-
setDirection(newDirection);
49-
}
50-
51-
// Reassign lastYPosition
52-
lastYPosition.current = currentYPosition;
53-
54-
return;
55-
};
56-
57-
const handleWindowScrollThrottled = throttle(handleWindowScroll, wait, {
58-
trailing: false,
59-
});
60-
61-
const handleRefScroll = () => {
62-
handleResetDirectionTimeout(timeToReset, handleResetDirection);
63-
64-
const childRect = ref?.current?.children?.[0].getBoundingClientRect();
29+
const handleWindowScrollThrottled = throttle(
30+
() => {
31+
handleResetDirectionTimeout(timeToReset, handleResetDirection);
6532

66-
if (childRect) {
67-
const currentYPosition = Math.round(childRect.y);
33+
const currentYPosition = Math.round(window.pageYOffset);
34+
const currentXPosition = Math.round(window.pageXOffset);
6835

6936
// Skip first iteration
70-
if (lastYPosition.current === null) {
37+
if (lastYPosition.current === null || lastXPosition.current === null) {
7138
lastYPosition.current = currentYPosition;
39+
lastXPosition.current = currentXPosition;
7240

7341
return;
7442
}
7543

44+
const xNotChangedPosition = currentXPosition === lastXPosition.current;
45+
const yNotChangedPosition = currentYPosition === lastYPosition.current;
46+
const xyNotChangedPosition =
47+
currentYPosition === lastYPosition.current &&
48+
currentXPosition === lastXPosition.current;
49+
7650
// Set NONE for the same position
77-
if (currentYPosition === lastYPosition.current) {
51+
if (xyNotChangedPosition) {
7852
setDirection('NONE');
7953

8054
return;
8155
}
8256

83-
const newDirection =
84-
lastYPosition.current > currentYPosition ? 'DOWN' : 'UP';
57+
let newDirection: ScrollDirectionType = 'NONE';
58+
59+
// Check the new direction
60+
if (xNotChangedPosition) {
61+
newDirection = lastYPosition.current < currentYPosition ? 'DOWN' : 'UP';
62+
} else if (yNotChangedPosition) {
63+
newDirection =
64+
lastXPosition.current < currentXPosition ? 'RIGHT' : 'LEFT';
65+
}
8566

8667
// If direction has changed, return it
8768
if (newDirection !== direction) {
8869
setDirection(newDirection);
8970
}
9071

91-
// Reassign lastYPosition
72+
// Reassign lastPosition
9273
lastYPosition.current = currentYPosition;
74+
lastXPosition.current = currentXPosition;
9375

9476
return;
95-
}
96-
};
77+
},
78+
wait,
79+
throttleOptions
80+
);
81+
82+
const handleRefScroll = throttle(
83+
() => {
84+
handleResetDirectionTimeout(timeToReset, handleResetDirection);
85+
86+
const childRect = ref?.current?.children?.[0].getBoundingClientRect();
87+
88+
if (childRect) {
89+
const currentYPosition = Math.round(childRect.y);
90+
const currentXPosition = Math.round(childRect.x);
91+
92+
// Skip first iteration
93+
if (lastYPosition.current === null || lastXPosition.current === null) {
94+
lastYPosition.current = currentYPosition;
95+
lastXPosition.current = currentXPosition;
96+
97+
return;
98+
}
99+
100+
const xNotChangedPosition = currentXPosition === lastXPosition.current;
101+
const yNotChangedPosition = currentYPosition === lastYPosition.current;
102+
const xyNotChangedPosition =
103+
currentYPosition === lastYPosition.current &&
104+
currentXPosition === lastXPosition.current;
105+
106+
// Set NONE for the same position
107+
if (xyNotChangedPosition) {
108+
setDirection('NONE');
109+
110+
return;
111+
}
112+
113+
let newDirection: ScrollDirectionType = 'NONE';
114+
115+
if (xNotChangedPosition) {
116+
newDirection =
117+
lastYPosition.current > currentYPosition ? 'DOWN' : 'UP';
118+
} else if (yNotChangedPosition) {
119+
newDirection =
120+
lastXPosition.current > currentXPosition ? 'RIGHT' : 'LEFT';
121+
}
122+
123+
// If direction has changed, return it
124+
if (newDirection !== direction) {
125+
setDirection(newDirection);
126+
}
127+
128+
// Reassign lastYPosition
129+
lastYPosition.current = currentYPosition;
130+
lastXPosition.current = currentXPosition;
97131

98-
const handleRefScrollThrottled = throttle(handleRefScroll, wait, {
99-
trailing: false,
100-
});
132+
return;
133+
}
134+
},
135+
wait,
136+
throttleOptions
137+
);
101138

102139
useEffect(() => {
103140
initializeResetDirectionTimeout();
104141
const element = ref?.current;
105142

106143
if (element) {
107-
element.addEventListener('scroll', handleRefScrollThrottled);
108-
return () =>
109-
element.removeEventListener('scroll', handleRefScrollThrottled);
144+
element.addEventListener('scroll', handleRefScroll);
145+
return () => element.removeEventListener('scroll', handleRefScroll);
110146
} else {
111147
window.addEventListener('scroll', handleWindowScrollThrottled);
112148
return () =>

src/utils/getScrollDirectionBooleans.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@ export const getScrollDirectionBooleans = (
66
isScrolling: scrollDirection !== 'NONE',
77
isScrollingUp: scrollDirection === 'UP',
88
isScrollingDown: scrollDirection === 'DOWN',
9+
isScrollingLeft: scrollDirection === 'LEFT',
10+
isScrollingRight: scrollDirection === 'RIGHT',
911
scrollDirection,
1012
});

stories/basicHorizontal.stories.tsx

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import React, { useEffect, useRef } from 'react';
2+
import { useScrollDirection } from '../src/useScrollDirection';
3+
4+
export default {
5+
title: 'Basic usage X axis',
6+
component: useScrollDirection,
7+
};
8+
9+
export const WindowObject = () => {
10+
const scrollDirection = useScrollDirection();
11+
12+
useEffect(() => {
13+
console.log(scrollDirection);
14+
}, [scrollDirection]);
15+
16+
return (
17+
<div style={{ width: '2000px' }}>
18+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam et felis
19+
maximus magna placerat pellentesque. Aliquam eu tristique libero. Quisque
20+
aliquet dui neque, a lobortis mi posuere quis. In id purus et nisl
21+
ultrices bibendum quis a mi. Quisque sit amet justo erat. Cras condimentum
22+
pretium odio vitae interdum. Vestibulum sed felis iaculis, volutpat lacus
23+
sit amet, eleifend ex. Donec neque magna, rutrum quis scelerisque sit
24+
amet, maximus a tortor. Aenean a risus facilisis, facilisis nisl ut,
25+
luctus turpis. Curabitur laoreet gravida congue. Integer condimentum augue
26+
eu ante lobortis semper. Donec non odio eget justo sagittis fermentum a
27+
non nisl. Fusce ac orci massa. Ut egestas fringilla convallis. Nam non
28+
lorem lacinia, tincidunt eros efficitur, iaculis leo. Sed purus diam,
29+
interdum ac egestas vitae, pulvinar et ante. Nunc laoreet elementum urna
30+
ut luctus. Donec id purus sapien. Phasellus iaculis ipsum sapien, non
31+
ullamcorper mauris ultrices ac. Fusce sit amet semper dui. Sed aliquam,
32+
sem nec auctor eleifend, risus ante vehicula leo, mattis maximus massa
33+
ipsum quis leo. Vestibulum euismod cursus sapien in semper. Donec et purus
34+
sollicitudin, sagittis leo eu, feugiat sem. Curabitur euismod mi vel est
35+
pulvinar lobortis. Phasellus molestie nisl nec tincidunt suscipit. Mauris
36+
vitae iaculis urna. Sed viverra bibendum justo, at pulvinar sapien
37+
tincidunt at. Cras in tellus ligula. Aenean sed sem efficitur, aliquam
38+
turpis et, maximus neque. Phasellus imperdiet tortor odio, et porttitor
39+
orci porttitor nec. Curabitur sit amet risus ut metus imperdiet semper.
40+
Sed ornare, nulla ut suscipit feugiat, dolor libero aliquam ex, eu
41+
fringilla diam sapien sed diam. Vestibulum sed orci nec dolor semper
42+
varius. Maecenas vulputate gravida lacus quis rutrum. Donec magna lorem,
43+
efficitur nec euismod id, pharetra a neque. Donec vel erat sed libero
44+
cursus feugiat vel ut purus. Nunc luctus turpis et justo convallis
45+
viverra. Vestibulum id efficitur turpis. Orci varius natoque penatibus et
46+
magnis dis parturient montes, nascetur ridiculus mus. Nullam eu
47+
condimentum risus. Vestibulum posuere feugiat massa, quis lacinia turpis
48+
posuere at. Sed commodo congue diam, a venenatis nulla lacinia eu. Nam
49+
orci risus, imperdiet luctus massa et, fermentum accumsan erat.
50+
Suspendisse sagittis tempus risus vel condimentum. Praesent neque tellus,
51+
rutrum non lobortis lobortis, porta sit amet dolor. Lorem ipsum dolor sit
52+
amet, consectetur adipiscing elit. Nam et felis maximus magna placerat
53+
pellentesque. Aliquam eu tristique libero. Quisque aliquet dui neque, a
54+
lobortis mi posuere quis. In id purus et nisl ultrices bibendum quis a mi.
55+
Quisque sit amet justo erat. Cras condimentum pretium odio vitae interdum.
56+
Vestibulum sed felis iaculis, volutpat lacus sit amet, eleifend ex. Donec
57+
neque magna, rutrum quis scelerisque sit amet, maximus a tortor. Aenean a
58+
risus facilisis, facilisis nisl ut, luctus turpis. Curabitur laoreet
59+
gravida congue. Integer condimentum augue eu ante lobortis semper. Donec
60+
non odio eget justo sagittis fermentum a non nisl. Fusce ac orci massa. Ut
61+
egestas fringilla convallis. Nam non lorem lacinia, tincidunt eros
62+
efficitur, iaculis leo. Sed purus diam, interdum ac egestas vitae,
63+
pulvinar et ante. Nunc laoreet elementum urna ut luctus. Donec id purus
64+
sapien. Phasellus iaculis ipsum sapien, non ullamcorper mauris ultrices
65+
ac. Fusce sit amet semper dui. Sed aliquam, sem nec auctor eleifend, risus
66+
ante vehicula leo, mattis maximus massa ipsum quis leo. Vestibulum euismod
67+
cursus sapien in semper. Donec et purus sollicitudin, sagittis leo eu,
68+
feugiat sem. Curabitur euismod mi vel est pulvinar lobortis. Phasellus
69+
molestie nisl nec tincidunt suscipit.
70+
</div>
71+
);
72+
};
73+
74+
WindowObject.story = {
75+
name: 'On window object',
76+
};
77+
78+
export const RefObject = () => {
79+
const containerRef = useRef(null);
80+
const scrollDirection = useScrollDirection({ ref: containerRef });
81+
82+
useEffect(() => {
83+
console.log(scrollDirection);
84+
}, [scrollDirection]);
85+
86+
return (
87+
<div
88+
ref={containerRef}
89+
style={{
90+
width: '100%',
91+
height: '200px',
92+
overflowX: 'auto',
93+
whiteSpace: 'nowrap',
94+
}}
95+
>
96+
<div>
97+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam et felis
98+
maximus magna placerat pellentesque. Aliquam eu tristique libero.
99+
Quisque aliquet dui neque, a lobortis mi posuere quis. In id purus et
100+
nisl ultrices bibendum quis a mi. Quisque sit amet justo erat. Cras
101+
condimentum pretium odio vitae interdum. Vestibulum sed felis iaculis,
102+
volutpat lacus sit amet, eleifend ex. Donec neque magna, rutrum quis
103+
scelerisque sit amet, maximus a tortor. Aenean a risus facilisis,
104+
facilisis nisl ut, luctus turpis. Curabitur laoreet gravida congue.
105+
Integer condimentum augue eu ante lobortis semper. Donec non odio eget
106+
justo sagittis fermentum a non nisl. Fusce ac orci massa. Ut egestas
107+
fringilla convallis. Nam non lorem lacinia, tincidunt eros efficitur,
108+
iaculis leo. Sed purus diam, interdum ac egestas vitae, pulvinar et
109+
ante. Nunc laoreet elementum urna ut luctus. Donec id purus sapien.
110+
Phasellus iaculis ipsum sapien, non ullamcorper mauris ultrices ac.
111+
Fusce sit amet semper dui. Sed aliquam, sem nec auctor eleifend, risus
112+
ante vehicula leo, mattis maximus massa ipsum quis leo. Vestibulum
113+
euismod cursus sapien in semper. Donec et purus sollicitudin, sagittis
114+
leo eu, feugiat sem. Curabitur euismod mi vel est pulvinar lobortis.
115+
Phasellus molestie nisl nec tincidunt suscipit. Mauris vitae iaculis
116+
urna. Sed viverra bibendum justo, at pulvinar sapien tincidunt at. Cras
117+
in tellus ligula. Aenean sed sem efficitur, aliquam turpis et, maximus
118+
neque. Phasellus imperdiet tortor odio, et porttitor orci porttitor nec.
119+
Curabitur sit amet risus ut metus imperdiet semper. Sed ornare, nulla ut
120+
suscipit feugiat, dolor libero aliquam ex, eu fringilla diam sapien sed
121+
diam. Vestibulum sed orci nec dolor semper varius. Maecenas vulputate
122+
gravida lacus quis rutrum. Donec magna lorem, efficitur nec euismod id,
123+
pharetra a neque. Donec vel erat sed libero cursus feugiat vel ut purus.
124+
Nunc luctus turpis et justo convallis viverra. Vestibulum id efficitur
125+
turpis. Orci varius natoque penatibus et magnis dis parturient montes,
126+
nascetur ridiculus mus. Nullam eu condimentum risus. Vestibulum posuere
127+
feugiat massa, quis lacinia turpis posuere at. Sed commodo congue diam,
128+
a venenatis nulla lacinia eu. Nam orci risus, imperdiet luctus massa et,
129+
fermentum accumsan erat. Suspendisse sagittis tempus risus vel
130+
condimentum. Praesent neque tellus, rutrum non lobortis lobortis, porta
131+
sit amet dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
132+
Nam et felis maximus magna placerat pellentesque. Aliquam eu tristique
133+
libero. Quisque aliquet dui neque, a lobortis mi posuere quis. In id
134+
purus et nisl ultrices bibendum quis a mi. Quisque sit amet justo erat.
135+
Cras condimentum pretium odio vitae interdum. Vestibulum sed felis
136+
iaculis, volutpat lacus sit amet, eleifend ex. Donec neque magna, rutrum
137+
quis scelerisque sit amet, maximus a tortor. Aenean a risus facilisis,
138+
facilisis nisl ut, luctus turpis. Curabitur laoreet gravida congue.
139+
Integer condimentum augue eu ante lobortis semper. Donec non odio eget
140+
justo sagittis fermentum a non nisl. Fusce ac orci massa. Ut egestas
141+
fringilla convallis. Nam non lorem lacinia, tincidunt eros efficitur,
142+
iaculis leo. Sed purus diam, interdum ac egestas vitae, pulvinar et
143+
ante. Nunc laoreet elementum urna ut luctus. Donec id purus sapien.
144+
Phasellus iaculis ipsum sapien, non ullamcorper mauris ultrices ac.
145+
Fusce sit amet semper dui. Sed aliquam, sem nec auctor eleifend, risus
146+
ante vehicula leo, mattis maximus massa ipsum quis leo. Vestibulum
147+
euismod cursus sapien in semper. Donec et purus sollicitudin, sagittis
148+
leo eu, feugiat sem. Curabitur euismod mi vel est pulvinar lobortis.
149+
Phasellus molestie nisl nec tincidunt suscipit.
150+
</div>
151+
</div>
152+
);
153+
};
154+
155+
RefObject.story = {
156+
name: 'On ref object',
157+
};

0 commit comments

Comments
 (0)