Skip to content

Commit 70f5a2b

Browse files
Hook: useScrollTop (#8254)
* feat: New useScrollTop hook * feat: useScrollTop docs added
1 parent 091296c commit 70f5a2b

6 files changed

Lines changed: 181 additions & 0 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { useScrollTop } from '@primereact/hooks';
2+
import { Button } from 'primereact/button';
3+
4+
export default function BasicDemo() {
5+
const { scrollToTop, visible } = useScrollTop();
6+
7+
return (
8+
<div className="card flex flex-col items-center">
9+
<p>Scroll down the page to display the ScrollTop component.</p>
10+
<i className="pi pi-angle-down animate-fadeout animate-duration-1000 animate-infinite text-[2rem] mb-[30rem]"></i>
11+
{visible && (
12+
<Button onClick={scrollToTop} iconOnly rounded className="fixed [inset-block-end:20px] [inset-inline-end:20px]">
13+
<i className="pi pi-chevron-up" />
14+
</Button>
15+
)}
16+
</div>
17+
);
18+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { useScrollTop } from '@primereact/hooks';
2+
import { Button } from 'primereact/button';
3+
import * as React from 'react';
4+
5+
export default function ElementDemo() {
6+
const [target, setTarget] = React.useState<HTMLDivElement | null>(null);
7+
8+
const { scrollToTop, visible } = useScrollTop({
9+
target,
10+
threshold: 100
11+
});
12+
13+
return (
14+
<div className="card flex justify-center">
15+
<div ref={setTarget} className="overflow-scroll" style={{ width: '250px', height: '200px', scrollbarWidth: 'thin' }}>
16+
<p>
17+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vitae et leo duis ut diam. Ultricies mi quis hendrerit dolor magna eget est lorem. Amet consectetur
18+
adipiscing elit ut. Nam libero justo laoreet sit amet. Pharetra massa massa ultricies mi quis hendrerit dolor magna. Est ultricies integer quis auctor elit sed vulputate. Consequat ac felis donec et. Tellus orci ac auctor augue
19+
mauris. Semper feugiat nibh sed pulvinar proin gravida hendrerit lectus a. Tincidunt arcu non sodales neque sodales. Metus aliquam eleifend mi in nulla posuere sollicitudin aliquam ultrices. Sodales ut etiam sit amet nisl purus.
20+
Cursus sit amet dictum sit amet. Tristique senectus et netus et malesuada fames ac turpis egestas. Et tortor consequat id porta nibh venenatis cras sed. Diam maecenas ultricies mi eget mauris. Eget egestas purus viverra accumsan
21+
in nisl nisi. Suscipit adipiscing bibendum est ultricies integer. Mattis aliquam faucibus purus in massa tempor nec.
22+
</p>
23+
{visible && (
24+
<Button onClick={scrollToTop} iconOnly rounded className="sticky flex ms-auto [inset-block-end:20px] [inset-inline-end:20px]">
25+
<i className="pi pi-arrow-up" />
26+
</Button>
27+
)}
28+
</div>
29+
</div>
30+
);
31+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
title: useScrollTop
3+
description: useScrollTop is a hook that provides a way to scroll to the top of a container.
4+
component: usescrolltop
5+
---
6+
7+
## Usage
8+
9+
```tsx
10+
import { useScrollTop } from '@primereact/hooks';
11+
```
12+
13+
```tsx
14+
const scrollTop = useScrollTop();
15+
```
16+
17+
## Examples
18+
19+
### Basic
20+
21+
useScrollTop listens window scroll by default.
22+
23+
<DocDemoViewer name="usescrolltop:basic-demo" />
24+
25+
Setting the _target_ option binds useScrollTop to this element that has scrolling content.
26+
27+
### Element
28+
29+
<DocDemoViewer name="usescrolltop:element-demo" />

packages/hooks/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export * from './use-match-media';
88
export * from './use-mount-effect';
99
export * from './use-previous';
1010
export * from './use-props';
11+
export * from './use-scrolltop';
1112
export * from './use-unmount-effect';
1213
export * from './use-update-effect';
1314
export * from './use-view-transition';

packages/hooks/src/use-scrolltop/index.test.ts

Whitespace-only changes.
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { getWindowScrollTop } from '@primeuix/utils';
2+
import * as React from 'react';
3+
4+
/**
5+
* The options for the `useScrollTop` hook.
6+
*/
7+
export interface UseScrollTopOptions {
8+
/**
9+
* Reference to the target element.
10+
* @default window
11+
*/
12+
target?: Window | Element | null;
13+
/**
14+
* Defines the threshold value of the vertical scroll position of the target to toggle the visibility.
15+
* @default 400
16+
*/
17+
threshold?: number;
18+
/**
19+
* Defines the scrolling behaviour, 'smooth' adds an animation and 'auto' scrolls with a jump.
20+
* @default smooth
21+
*/
22+
behavior?: ScrollBehavior;
23+
}
24+
25+
/**
26+
* The exposes for the `useScrollTop` hook.
27+
*/
28+
export interface UseScrollTopExposes {
29+
/**
30+
* Current visible state as a boolean.
31+
* @default false
32+
*/
33+
visible: boolean;
34+
/**
35+
* Scrolls the target element to the top.
36+
*/
37+
scrollToTop: () => void;
38+
}
39+
40+
/**
41+
* useScrollTop hook is used to enter input in a certain format such as numeric, date, currency, email and phone.
42+
*
43+
* @param {UseScrollTopOptions} options - The options for the scroll top behavior.
44+
* @returns {UseScrollTopExposes} - The exposed methods for the scroll top behavior.
45+
*
46+
* @example
47+
* ```tsx
48+
* const { scrollToTop, visible } = useScrollTop({
49+
* target: elementRef.current,
50+
* threshold: 400,
51+
* behavior: 'smooth'
52+
* });
53+
*
54+
* return (
55+
* <div ref={elementRef}>
56+
* {visible && (
57+
* <Button onClick={scrollToTop}>
58+
* <i className="pi pi-arrow-up" />
59+
* </Button>
60+
* )}
61+
* </div>
62+
* );
63+
*/
64+
export function useScrollTop(options?: UseScrollTopOptions): UseScrollTopExposes {
65+
const { target = typeof window !== 'undefined' ? window : null, threshold = 400, behavior = 'smooth' } = options || {};
66+
const [visible, setVisible] = React.useState(false);
67+
68+
const scrollToTop = () => {
69+
target?.scroll({
70+
top: 0,
71+
behavior: behavior
72+
});
73+
};
74+
75+
React.useEffect(() => {
76+
if (!target) return;
77+
78+
const checkVisibility = (scrollY: number) => {
79+
if (scrollY > threshold) setVisible(true);
80+
else setVisible(false);
81+
};
82+
83+
const onScroll = () => {
84+
const scrollY = target === window ? getWindowScrollTop() : (target as Element).scrollTop;
85+
86+
checkVisibility(scrollY);
87+
};
88+
89+
target.addEventListener('scroll', onScroll);
90+
91+
return () => {
92+
target.removeEventListener('scroll', onScroll);
93+
};
94+
}, [target, threshold]);
95+
96+
return {
97+
//state
98+
visible,
99+
//methods
100+
scrollToTop
101+
};
102+
}

0 commit comments

Comments
 (0)