Skip to content

Commit

Permalink
Merge pull request #312 from SMAKSS/develop
Browse files Browse the repository at this point in the history
[Release] Allow specifying a custom scrollable element
  • Loading branch information
SMAKSS authored Jul 27, 2024
2 parents 3f57b75 + 459176f commit 615c33f
Show file tree
Hide file tree
Showing 11 changed files with 822 additions and 827 deletions.
1 change: 0 additions & 1 deletion .eslintcache

This file was deleted.

11 changes: 0 additions & 11 deletions .eslintignore

This file was deleted.

35 changes: 0 additions & 35 deletions .eslintrc.json

This file was deleted.

16 changes: 16 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import useDetectScroll, {

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

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

To use a custom scrollable element as a target rather than the default window:

```js
const customElementRef = useRef<HTMLDivElement>(null);
const [customElement, setCustomElement] = useState<HTMLDivElement>();

const scrollDir = useDetectScroll({target: customElement});

useEffect(() => {
if(customElementRef.current) {
setHomepageElement(customElementRef.current);
}
}, [customElementRef])
```

## Contributing

Interested in making contributions to this project? Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines and details.
Expand Down
49 changes: 49 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import tsParser from '@typescript-eslint/parser';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import reactHooksPlugin from 'eslint-plugin-react-hooks';

export default [
{
files: ['**/*.{js,jsx,ts,tsx}'],
ignores: [
'dist/**',
'prettier.config.cjs',
'lint-staged.config.cjs',
'commitlint.config.cjs'
],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaFeatures: {
jsx: true,
modules: true
},
ecmaVersion: 'latest',
sourceType: 'module',
tsconfigRootDir: '.',
project: ['./tsconfig.json']
}
},
plugins: {
'@typescript-eslint': tsPlugin,
'react-hooks': reactHooksPlugin
},
rules: {
'linebreak-style': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/ban-types': [
'error',
{
extendDefaults: true,
types: {
'{}': false
}
}
],
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'object-shorthand': 'error',
'no-console': ['warn', { allow: ['warn', 'error', 'info'] }]
}
}
];
34 changes: 17 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@
},
"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.",
"devDependencies": {
"@commitlint/cli": "^19.2.1",
"@commitlint/config-conventional": "^19.1.0",
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.6",
"@types/react": "^18.2.74",
"@typescript-eslint/eslint-plugin": "^7.5.0",
"@typescript-eslint/parser": "^7.5.0",
"eslint": "^8.57.0",
"@types/react": "^18.3.3",
"@typescript-eslint/eslint-plugin": "^7.17.0",
"@typescript-eslint/parser": "^7.17.0",
"eslint": "^9.8.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react-hooks": "^4.6.0",
"husky": "^9.0.11",
"lint-staged": "^15.2.2",
"prettier": "^3.2.5",
"rollup": "^4.14.0",
"typescript": "^5.4.4"
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react-hooks": "^4.6.2",
"husky": "^9.1.3",
"lint-staged": "^15.2.7",
"prettier": "^3.3.3",
"rollup": "^4.19.1",
"typescript": "^5.5.4"
},
"engines": {
"node": ">=18.0.0"
Expand Down Expand Up @@ -72,13 +72,13 @@
"format": "prettier --write \"**/*.+(js|jsx|json|yml|yaml|css|ts|tsx|md|gql|graphql|mdx)\"",
"format:check": "prettier -l \"**/*.+(js|jsx|json|yml|yaml|css|ts|tsx|md|gql|graphql|mdx)\"",
"generate": "rollup -c",
"lint": "eslint --cache --cache-location ./node_modules/.cache/.eslintcache --ext js,jsx,ts,tsx --max-warnings=0 .",
"lint:fix": "eslint src/**/*.ts --fix",
"lint": "npx eslint --cache --cache-location ./node_modules/.cache/.eslintcache --max-warnings=0 .",
"lint:fix": "npx eslint --fix .",
"setup": "yarn && husky install",
"typecheck": "tsc -b .",
"update:deps": "rm -rf node_modules yarn.lock && ncu -u && yarn"
"update:deps": "rm -rf node_modules yarn.lock && npx npm-check-updates -u && yarn"
},
"type": "module",
"types": "./dist/index.d.ts",
"version": "4.1.0"
"version": "4.2.0"
}
200 changes: 2 additions & 198 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,200 +1,4 @@
import { useState, useEffect, useCallback, useRef } from 'react';

/** Enumeration for axis values */
export enum Axis {
/**
* The x-axis represents the horizontal direction.
*/
X = 'x',
/**
* The y-axis represents the vertical direction.
*/
Y = 'y'
}

/** Enumeration for direction values */
export enum Direction {
/**
* The up direction represents the scroll direction moving towards the top.
*/
Up = 'up',
/**
* The down direction represents the scroll direction moving towards the bottom.
*/
Down = 'down',
/**
* The left direction represents the scroll direction moving towards the left.
*/
Left = 'left',
/**
* The right direction represents the scroll direction moving towards the right.
*/
Right = 'right',
/**
* The still direction represents the scroll direction when the user is not scrolling.
*/
Still = 'still'
}

type ScrollPosition = {
/**
* The top position represents the distance from the top edge of the page.
*/
top: number;
/**
* The bottom position represents the distance from the bottom edge of the page.
*/
bottom: number;
/**
* The left position represents the distance from the left edge of the page.
*/
left: number;
/**
* The right position represents the distance from the right edge of the page.
*/
right: number;
};

/** Type declaration for the returned scroll information */
type ScrollInfo = {
/**
* The scrollDir represents the current scroll direction.
*/
scrollDir: Direction;
/**
* The scrollPosition represents the current scroll position.
*/
scrollPosition: ScrollPosition;
};

/** Type declaration for scroll properties */
type ScrollProps = {
/**
* The thr represents the threshold value for scroll detection.
*/
thr?: number;
/**
* The axis represents the scroll axis (x or y).
*/
axis?: Axis;
/**
* The scrollUp represents the scroll direction when moving up.
*/
scrollUp?: Direction;
/**
* The scrollDown represents the scroll direction when moving down.
*/
scrollDown?: Direction;
/**
* The still represents the scroll direction when the user is not scrolling.
*/
still?: Direction;
};

/**
* useDetectScroll hook.
*
* This hook provides a mechanism to detect the scroll direction and position.
* It will return the scroll direction as a string (up, down, left, right, or still) based on user scrolling,
* as well as the scroll position from the top, bottom, left, and right edges of the page.
*
* @example
*
* import useDetectScroll, { Axis, Direction } from '@smakss/react-scroll-direction';
*
* function App() {
* const { scrollDir, scrollPosition } = useDetectScroll({
* thr: 100,
* axis: Axis.Y,
* scrollUp: Direction.Up,
* scrollDown: Direction.Down,
* still: Direction.Still
* });
*
* return (
* <div>
* <p>Current scroll direction: {scrollDir}</p>
* <p>Scroll position - Top: {scrollPosition.top}, Bottom: {scrollPosition.bottom},
* Left: {scrollPosition.left}, Right: {scrollPosition.right}</p>
* </div>
* );
* }
*
* @param {ScrollProps} props - The properties related to scrolling.
* @returns {ScrollInfo} - The current direction and position of scrolling.
*/
function useDetectScroll(props: ScrollProps = {}): ScrollInfo {
const {
thr = 0,
axis = Axis.Y,
scrollUp = axis === Axis.Y ? Direction.Up : Direction.Left,
scrollDown = axis === Axis.Y ? Direction.Down : Direction.Right,
still = Direction.Still
} = props;

const [scrollDir, setScrollDir] = useState<Direction>(still);
const [scrollPosition, setScrollPosition] = useState<ScrollPosition>({
top: 0,
bottom: 0,
left: 0,
right: 0
});

const threshold = Math.max(0, thr);
const ticking = useRef(false);
const lastScroll = useRef(0);

/** Function to update scroll direction */
const updateScrollDir = useCallback(() => {
const scroll = axis === Axis.Y ? window.scrollY : window.scrollX;

if (Math.abs(scroll - lastScroll.current) >= threshold) {
setScrollDir(scroll > lastScroll.current ? scrollDown : scrollUp);
lastScroll.current = Math.max(0, scroll);
}
ticking.current = false;
}, [axis, threshold, scrollDown, scrollUp]);

useEffect(() => {
/** Function to update scroll position */
const updateScrollPosition = () => {
const top = window.scrollY;
const left = window.scrollX;
const bottom =
document.documentElement.scrollHeight - window.innerHeight - top;
const right =
document.documentElement.scrollWidth - window.innerWidth - left;

setScrollPosition({ top, bottom, left, right });
};

/** Call the update function when the component mounts */
updateScrollPosition();

window.addEventListener('scroll', updateScrollPosition);

return () => {
window.removeEventListener('scroll', updateScrollPosition);
};
}, []);

useEffect(() => {
lastScroll.current = axis === Axis.Y ? window.scrollY : window.scrollX;

/** Function to handle onScroll event */
const onScroll = () => {
if (!ticking.current) {
window.requestAnimationFrame(updateScrollDir);
ticking.current = true;
}
};

window.addEventListener('scroll', onScroll);

return () => window.removeEventListener('scroll', onScroll);
}, [axis, updateScrollDir]);

return { scrollDir, scrollPosition };
}
import useDetectScroll from './useDetectScroll';

export default useDetectScroll;
export { Axis, Direction } from './types';
Loading

0 comments on commit 615c33f

Please sign in to comment.