Skip to content

Commit 615c33f

Browse files
authored
Merge pull request #312 from SMAKSS/develop
[Release] Allow specifying a custom scrollable element
2 parents 3f57b75 + 459176f commit 615c33f

File tree

11 files changed

+822
-827
lines changed

11 files changed

+822
-827
lines changed

.eslintcache

Lines changed: 0 additions & 1 deletion
This file was deleted.

.eslintignore

Lines changed: 0 additions & 11 deletions
This file was deleted.

.eslintrc.json

Lines changed: 0 additions & 35 deletions
This file was deleted.

Readme.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import useDetectScroll, {
4343

4444
The `useDetectScroll` hook takes an options object with the following properties:
4545

46+
- `target`: The target scrollable element from which to detect scroll direction and position (default: `window`, must be an `HTMLDivElement`).
4647
- `thr`: Threshold for scroll direction change detection (default: `0`, accepts only positive values).
4748
- `axis`: Defines the scroll axis (`"y"` or `"x"`, default: `"y"`).
4849
- `scrollUp`: Value returned when scrolling up (y-axis) or left (x-axis) (default: `"up"` for y-axis, `"left"` for x-axis).
@@ -74,6 +75,21 @@ const { scrollDir, scrollPosition } = useDetectScroll({ axis: Axis.X });
7475
// scrollPosition: { top, bottom, left, right }
7576
```
7677

78+
To use a custom scrollable element as a target rather than the default window:
79+
80+
```js
81+
const customElementRef = useRef<HTMLDivElement>(null);
82+
const [customElement, setCustomElement] = useState<HTMLDivElement>();
83+
84+
const scrollDir = useDetectScroll({target: customElement});
85+
86+
useEffect(() => {
87+
if(customElementRef.current) {
88+
setHomepageElement(customElementRef.current);
89+
}
90+
}, [customElementRef])
91+
```
92+
7793
## Contributing
7894

7995
Interested in making contributions to this project? Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines and details.

eslint.config.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import tsParser from '@typescript-eslint/parser';
2+
import tsPlugin from '@typescript-eslint/eslint-plugin';
3+
import reactHooksPlugin from 'eslint-plugin-react-hooks';
4+
5+
export default [
6+
{
7+
files: ['**/*.{js,jsx,ts,tsx}'],
8+
ignores: [
9+
'dist/**',
10+
'prettier.config.cjs',
11+
'lint-staged.config.cjs',
12+
'commitlint.config.cjs'
13+
],
14+
languageOptions: {
15+
parser: tsParser,
16+
parserOptions: {
17+
ecmaFeatures: {
18+
jsx: true,
19+
modules: true
20+
},
21+
ecmaVersion: 'latest',
22+
sourceType: 'module',
23+
tsconfigRootDir: '.',
24+
project: ['./tsconfig.json']
25+
}
26+
},
27+
plugins: {
28+
'@typescript-eslint': tsPlugin,
29+
'react-hooks': reactHooksPlugin
30+
},
31+
rules: {
32+
'linebreak-style': 'off',
33+
'@typescript-eslint/no-explicit-any': 'warn',
34+
'@typescript-eslint/ban-types': [
35+
'error',
36+
{
37+
extendDefaults: true,
38+
types: {
39+
'{}': false
40+
}
41+
}
42+
],
43+
'react-hooks/rules-of-hooks': 'error',
44+
'react-hooks/exhaustive-deps': 'warn',
45+
'object-shorthand': 'error',
46+
'no-console': ['warn', { allow: ['warn', 'error', 'info'] }]
47+
}
48+
}
49+
];

package.json

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@
55
},
66
"description": "Enhance your React apps with advanced scroll detection using @smakss/react-scroll-direction. This powerful hook not only detects scroll direction but also provides scroll position information. Ideal for React, Remix, Next.js, and Gatsby projects, it comes with adjustable sensitivity and supports ES Modules.",
77
"devDependencies": {
8-
"@commitlint/cli": "^19.2.1",
9-
"@commitlint/config-conventional": "^19.1.0",
8+
"@commitlint/cli": "^19.3.0",
9+
"@commitlint/config-conventional": "^19.2.2",
1010
"@rollup/plugin-node-resolve": "^15.2.3",
1111
"@rollup/plugin-typescript": "^11.1.6",
12-
"@types/react": "^18.2.74",
13-
"@typescript-eslint/eslint-plugin": "^7.5.0",
14-
"@typescript-eslint/parser": "^7.5.0",
15-
"eslint": "^8.57.0",
12+
"@types/react": "^18.3.3",
13+
"@typescript-eslint/eslint-plugin": "^7.17.0",
14+
"@typescript-eslint/parser": "^7.17.0",
15+
"eslint": "^9.8.0",
1616
"eslint-config-prettier": "^9.1.0",
17-
"eslint-plugin-prettier": "^5.1.3",
18-
"eslint-plugin-react-hooks": "^4.6.0",
19-
"husky": "^9.0.11",
20-
"lint-staged": "^15.2.2",
21-
"prettier": "^3.2.5",
22-
"rollup": "^4.14.0",
23-
"typescript": "^5.4.4"
17+
"eslint-plugin-prettier": "^5.2.1",
18+
"eslint-plugin-react-hooks": "^4.6.2",
19+
"husky": "^9.1.3",
20+
"lint-staged": "^15.2.7",
21+
"prettier": "^3.3.3",
22+
"rollup": "^4.19.1",
23+
"typescript": "^5.5.4"
2424
},
2525
"engines": {
2626
"node": ">=18.0.0"
@@ -72,13 +72,13 @@
7272
"format": "prettier --write \"**/*.+(js|jsx|json|yml|yaml|css|ts|tsx|md|gql|graphql|mdx)\"",
7373
"format:check": "prettier -l \"**/*.+(js|jsx|json|yml|yaml|css|ts|tsx|md|gql|graphql|mdx)\"",
7474
"generate": "rollup -c",
75-
"lint": "eslint --cache --cache-location ./node_modules/.cache/.eslintcache --ext js,jsx,ts,tsx --max-warnings=0 .",
76-
"lint:fix": "eslint src/**/*.ts --fix",
75+
"lint": "npx eslint --cache --cache-location ./node_modules/.cache/.eslintcache --max-warnings=0 .",
76+
"lint:fix": "npx eslint --fix .",
7777
"setup": "yarn && husky install",
7878
"typecheck": "tsc -b .",
79-
"update:deps": "rm -rf node_modules yarn.lock && ncu -u && yarn"
79+
"update:deps": "rm -rf node_modules yarn.lock && npx npm-check-updates -u && yarn"
8080
},
8181
"type": "module",
8282
"types": "./dist/index.d.ts",
83-
"version": "4.1.0"
83+
"version": "4.2.0"
8484
}

src/index.ts

Lines changed: 2 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -1,200 +1,4 @@
1-
import { useState, useEffect, useCallback, useRef } from 'react';
2-
3-
/** Enumeration for axis values */
4-
export enum Axis {
5-
/**
6-
* The x-axis represents the horizontal direction.
7-
*/
8-
X = 'x',
9-
/**
10-
* The y-axis represents the vertical direction.
11-
*/
12-
Y = 'y'
13-
}
14-
15-
/** Enumeration for direction values */
16-
export enum Direction {
17-
/**
18-
* The up direction represents the scroll direction moving towards the top.
19-
*/
20-
Up = 'up',
21-
/**
22-
* The down direction represents the scroll direction moving towards the bottom.
23-
*/
24-
Down = 'down',
25-
/**
26-
* The left direction represents the scroll direction moving towards the left.
27-
*/
28-
Left = 'left',
29-
/**
30-
* The right direction represents the scroll direction moving towards the right.
31-
*/
32-
Right = 'right',
33-
/**
34-
* The still direction represents the scroll direction when the user is not scrolling.
35-
*/
36-
Still = 'still'
37-
}
38-
39-
type ScrollPosition = {
40-
/**
41-
* The top position represents the distance from the top edge of the page.
42-
*/
43-
top: number;
44-
/**
45-
* The bottom position represents the distance from the bottom edge of the page.
46-
*/
47-
bottom: number;
48-
/**
49-
* The left position represents the distance from the left edge of the page.
50-
*/
51-
left: number;
52-
/**
53-
* The right position represents the distance from the right edge of the page.
54-
*/
55-
right: number;
56-
};
57-
58-
/** Type declaration for the returned scroll information */
59-
type ScrollInfo = {
60-
/**
61-
* The scrollDir represents the current scroll direction.
62-
*/
63-
scrollDir: Direction;
64-
/**
65-
* The scrollPosition represents the current scroll position.
66-
*/
67-
scrollPosition: ScrollPosition;
68-
};
69-
70-
/** Type declaration for scroll properties */
71-
type ScrollProps = {
72-
/**
73-
* The thr represents the threshold value for scroll detection.
74-
*/
75-
thr?: number;
76-
/**
77-
* The axis represents the scroll axis (x or y).
78-
*/
79-
axis?: Axis;
80-
/**
81-
* The scrollUp represents the scroll direction when moving up.
82-
*/
83-
scrollUp?: Direction;
84-
/**
85-
* The scrollDown represents the scroll direction when moving down.
86-
*/
87-
scrollDown?: Direction;
88-
/**
89-
* The still represents the scroll direction when the user is not scrolling.
90-
*/
91-
still?: Direction;
92-
};
93-
94-
/**
95-
* useDetectScroll hook.
96-
*
97-
* This hook provides a mechanism to detect the scroll direction and position.
98-
* It will return the scroll direction as a string (up, down, left, right, or still) based on user scrolling,
99-
* as well as the scroll position from the top, bottom, left, and right edges of the page.
100-
*
101-
* @example
102-
*
103-
* import useDetectScroll, { Axis, Direction } from '@smakss/react-scroll-direction';
104-
*
105-
* function App() {
106-
* const { scrollDir, scrollPosition } = useDetectScroll({
107-
* thr: 100,
108-
* axis: Axis.Y,
109-
* scrollUp: Direction.Up,
110-
* scrollDown: Direction.Down,
111-
* still: Direction.Still
112-
* });
113-
*
114-
* return (
115-
* <div>
116-
* <p>Current scroll direction: {scrollDir}</p>
117-
* <p>Scroll position - Top: {scrollPosition.top}, Bottom: {scrollPosition.bottom},
118-
* Left: {scrollPosition.left}, Right: {scrollPosition.right}</p>
119-
* </div>
120-
* );
121-
* }
122-
*
123-
* @param {ScrollProps} props - The properties related to scrolling.
124-
* @returns {ScrollInfo} - The current direction and position of scrolling.
125-
*/
126-
function useDetectScroll(props: ScrollProps = {}): ScrollInfo {
127-
const {
128-
thr = 0,
129-
axis = Axis.Y,
130-
scrollUp = axis === Axis.Y ? Direction.Up : Direction.Left,
131-
scrollDown = axis === Axis.Y ? Direction.Down : Direction.Right,
132-
still = Direction.Still
133-
} = props;
134-
135-
const [scrollDir, setScrollDir] = useState<Direction>(still);
136-
const [scrollPosition, setScrollPosition] = useState<ScrollPosition>({
137-
top: 0,
138-
bottom: 0,
139-
left: 0,
140-
right: 0
141-
});
142-
143-
const threshold = Math.max(0, thr);
144-
const ticking = useRef(false);
145-
const lastScroll = useRef(0);
146-
147-
/** Function to update scroll direction */
148-
const updateScrollDir = useCallback(() => {
149-
const scroll = axis === Axis.Y ? window.scrollY : window.scrollX;
150-
151-
if (Math.abs(scroll - lastScroll.current) >= threshold) {
152-
setScrollDir(scroll > lastScroll.current ? scrollDown : scrollUp);
153-
lastScroll.current = Math.max(0, scroll);
154-
}
155-
ticking.current = false;
156-
}, [axis, threshold, scrollDown, scrollUp]);
157-
158-
useEffect(() => {
159-
/** Function to update scroll position */
160-
const updateScrollPosition = () => {
161-
const top = window.scrollY;
162-
const left = window.scrollX;
163-
const bottom =
164-
document.documentElement.scrollHeight - window.innerHeight - top;
165-
const right =
166-
document.documentElement.scrollWidth - window.innerWidth - left;
167-
168-
setScrollPosition({ top, bottom, left, right });
169-
};
170-
171-
/** Call the update function when the component mounts */
172-
updateScrollPosition();
173-
174-
window.addEventListener('scroll', updateScrollPosition);
175-
176-
return () => {
177-
window.removeEventListener('scroll', updateScrollPosition);
178-
};
179-
}, []);
180-
181-
useEffect(() => {
182-
lastScroll.current = axis === Axis.Y ? window.scrollY : window.scrollX;
183-
184-
/** Function to handle onScroll event */
185-
const onScroll = () => {
186-
if (!ticking.current) {
187-
window.requestAnimationFrame(updateScrollDir);
188-
ticking.current = true;
189-
}
190-
};
191-
192-
window.addEventListener('scroll', onScroll);
193-
194-
return () => window.removeEventListener('scroll', onScroll);
195-
}, [axis, updateScrollDir]);
196-
197-
return { scrollDir, scrollPosition };
198-
}
1+
import useDetectScroll from './useDetectScroll';
1992

2003
export default useDetectScroll;
4+
export { Axis, Direction } from './types';

0 commit comments

Comments
 (0)