Skip to content

Commit 525a4fb

Browse files
committed
[IMPL] useScrollIntoView hook
1 parent e91f2fd commit 525a4fb

16 files changed

Lines changed: 338 additions & 21 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useReducedMotion } from "../../../../../../packages/react-tools/src"
2+
3+
/**
4+
The component display user preferences about reducing motion.
5+
*/
6+
export const UseReducedMotion = () => {
7+
const value = useReducedMotion();
8+
9+
return <>
10+
<p
11+
style={{ color: value ? "red" : "rgb(152, 195, 121)"}}
12+
>
13+
{value ? 'You prefer to reduce motion' : 'You prefer not to reduce motion'}
14+
</p>
15+
</>
16+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useScrollIntoView } from "../../../../../../packages/react-tools/src";
2+
3+
export const UseScrollIntoView = () => {
4+
const { scroll, targetRef } = useScrollIntoView<HTMLParagraphElement, HTMLDivElement>({
5+
scrollableElement: ()=>document.querySelector('.container') as HTMLDivElement
6+
});
7+
8+
return (
9+
<div>
10+
<button
11+
onClick={()=>scroll('start')}
12+
>
13+
Scroll to target
14+
</button>
15+
<div
16+
style={{
17+
width: '100%',
18+
height: '50vh',
19+
}}
20+
/>
21+
<p ref={targetRef}>Hello there</p>
22+
</div>
23+
);
24+
}

apps/react-tools-demo/src/constants/components.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ export const COMPONENTS = [
5757
"useIntersectionObserver",
5858
"useMutationObserver",
5959
"useMeasure",
60-
"useVisible"
60+
"useVisible",
61+
"useScrollIntoView"
6162
],
6263
//API DOM
6364
[
@@ -72,6 +73,7 @@ export const COMPONENTS = [
7273
"useClipboard",
7374
"useMediaQuery",
7475
"useColorScheme",
76+
"useReducedMotion",
7577
"useTitle",
7678
"useIdle",
7779
"useFullscreen"
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# useReducedMotion
2+
Hook to detect if user prefers to reduce motion.
3+
4+
## Usage
5+
6+
```tsx
7+
export const UseReducedMotion = () => {
8+
const value = useReducedMotion();
9+
10+
return <>
11+
<p
12+
style={{ color: value ? "red" : "rgb(152, 195, 121)"}}
13+
>
14+
{value ? 'You prefer to reduce motion' : 'You prefer not to reduce motion'}
15+
</p>
16+
</>
17+
}
18+
```
19+
20+
> The component display user preferences about reducing motion.
21+
22+
23+
## API
24+
25+
```tsx
26+
useReducedMotion (): boolean
27+
```
28+
29+
> ### Params
30+
>
31+
>
32+
>
33+
34+
> ### Returns
35+
>
36+
> __result__: it is true if user prefers reduced motion, otherwise it is false.
37+
> - _boolean_
38+
>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# useScrollIntoView
2+
3+
4+
## Usage
5+
6+
```tsx
7+
8+
export const UseScrollIntoView = () => {
9+
const { scroll, targetRef } = useScrollIntoView<HTMLParagraphElement, HTMLDivElement>({
10+
scrollableElement: ()=>document.querySelector('.container') as HTMLDivElement
11+
});
12+
13+
return (
14+
<div>
15+
<button
16+
onClick={()=>scroll('start')}
17+
>
18+
Scroll to target
19+
</button>
20+
<div
21+
style={{
22+
width: '100%',
23+
height: '50vh',
24+
backgroundColor: 'lightblue',
25+
}}
26+
/>
27+
<p ref={targetRef}>Hello there</p>
28+
</div>
29+
);
30+
}
31+
```
32+
33+
34+
35+
36+
## API
37+
38+
```tsx
39+
useScrollIntoView <T extends Element, E extends Element | null = null>({ duration = 1000, axis = "y", animation = easeInOutSine, offset = 0, cancelable = false, onFinish, scrollableElement }: { duration?: number, axis?: "x" | "y", animation?: (t: number) => number, offset?: number, cancelable?: boolean, onFinish?: () => void, scrollableElement: (()=>E)|E|React.RefObject<E|null> }): { targetRef: React.MutableRefObject<T | null>, scroll: (alignment?: "start" | "center" | "end") => void, cancel: () => void }
40+
```
41+
42+
> ### Params
43+
>
44+
> - __param__: _Object_
45+
> - __param.duration=1000?__: _number_
46+
animation duration in milliseconds.
47+
> - __param.axis="x"?__: _"x"|"y"_
48+
scrolling axis.
49+
> - __param.animation=easeInOutSine?__: _(t:number)=>number_
50+
easing animation function. Refer to [https://easings.net/](https://easings.net/).
51+
> - __param.offset=0?__: _number_
52+
additional distance.
53+
> - __param.cancelable=false?__: _boolean_
54+
to establish if animation can be interrupted by user scroll.
55+
> - __param.onFinish?__: _()=>void_
56+
callback to be executed when animation ends.
57+
> - __param.scrollableElement?__: _(()=>E)|E|React.RefObject<E|null>_
58+
scrollable parent element, ref or function that returns element.
59+
>
60+
61+
> ### Returns
62+
>
63+
> __result__: a __targetRef__ to target element, a __scroll__ function to start scrolling, a __cancel__ function to cancel scrolling.
64+
> - __Object__:
65+
> - __targetRef__ : _React.MutableRefObject<T|null>_
66+
> - __scroll__ : _(alignment?: "start"|"center"|"end")=>void_
67+
> - __cancel__ : _()=>void_
68+
>

apps/react-tools-demo/src/markdown/useVisible.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,33 @@ Hook to know if an element is visible and optionally the visible area ration of
55

66
```tsx
77
export const UseVisible = () => {
8-
const [cbRef, isVisible, ratio] = useVisible({withRatio: true});
8+
const [cbRef, isVisible, ratio] = useVisible({
9+
threshold: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
10+
withRatio: true
11+
});
912
return (
1013
<div>
11-
<div style={{ width: 300, height: 300, overflow: 'scroll', border: '1px solid' }}>
12-
scroll here
14+
<div style={{ marginTop: 16, color: ratio === 1 ? '#98c379' : ratio === 0 ? '#e46962' : '#d2c04c' }}>
15+
<p>isVisible: {isVisible ? 'visible' : 'hidden'}</p>
16+
<p>ratio: {ratio}</p>
17+
</div>
18+
<div style={{ margin: 'auto', width: 300, height: 300, overflow: 'scroll', border: '1px solid' }}>
19+
<p>scrollable div</p>
1320
<div style={{ height: 800 }}>
1421
<div
1522
ref={cbRef}
1623
style={{
1724
border: '1px solid',
25+
margin: '320px auto 0px',
1826
height: 100,
1927
width: 100,
2028
textAlign: 'center',
21-
marginTop: 80,
2229
}}
2330
>
24-
observer dom
31+
bordered div
2532
</div>
2633
</div>
2734
</div>
28-
<div style={{ marginTop: 16, color: ratio ? '#87d068' : '#f50' }}>
29-
<p>isVisible: {isVisible ? 'visible' : 'hidden'}</p>
30-
<p>ratio: {ratio}</p>
31-
</div>
3235
</div>
3336
);
3437
}

packages/react-tools/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
- [x] useMutationObserver
6161
- [x] useMeasure
6262
- [x] useVisibile
63-
- [ ] useScrollIntoView
63+
- [x] useScrollIntoView
6464
- [ ] useMouse
6565
- [ ] useLongPress
6666
- [ ] useKeysEvents
@@ -84,6 +84,7 @@
8484
- [x] useClipboard
8585
- [x] useMediaQuery
8686
- [x] useColorScheme
87+
- [x] useReducedMotion
8788
- [x] useTitle (change document.title but also document.head.title nodeElement)
8889
- [x] useIdle
8990
- [x] useFullscreen (check browser compatibility)

packages/react-tools/src/hooks/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,6 @@ export { useIdle } from './useIdle';
5555
export { useRaf } from './useRaf';
5656
export { useMeasure } from './useMeasure';
5757
export { useFullscreen } from './useFullscreen';
58-
export { useVisible } from './useVisible';
58+
export { useVisible } from './useVisible';
59+
export { useReducedMotion } from './useReducedMotion';
60+
export { useScrollIntoView } from './useScrollIntoView';

packages/react-tools/src/hooks/useClickOutside.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const isClickInside = (node: HTMLElement | null, target: Node | null) => {
2424
*/
2525
export const useClickOutside = (target: RefObject<HTMLElement> | HTMLElement, handler: (evt: Event) => void):void => {
2626
const listener = useCallback((e: Event) => {
27-
const element = (target as RefObject<HTMLElement>).current
27+
const element = (target as RefObject<HTMLElement>)?.current
2828
? (target as RefObject<HTMLElement>).current
2929
: target as HTMLElement;
3030
if (!isClickInside(element, e.target as Node)) {

packages/react-tools/src/hooks/useClipboard.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ function useClipboard({ useValue, dataType, target }: { useValue: boolean, dataT
128128
useCallback(notif => {
129129
notifRef.current = notif;
130130
const element = target
131-
? (target as RefObject<HTMLElement>).current
131+
? (target as RefObject<HTMLElement>)?.current
132132
? (target as RefObject<HTMLElement>).current
133133
: target as HTMLElement
134134
: document;

0 commit comments

Comments
 (0)