Skip to content

Commit 1ce04d5

Browse files
committed
[ADD] useHover hook
1 parent ea455c9 commit 1ce04d5

8 files changed

Lines changed: 128 additions & 6 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { useRef } from "react"
2+
import { useHover } from "../../../../../../packages/react-tools/src";
3+
4+
/**
5+
The component renders a paragraph element with some text to which is attacched a ref used for __useHover__ hook that returns if element is hovered or not. This value is rendered into another paragraph element.
6+
7+
When mouse is hovered paragraph element, status changes to true.
8+
*/
9+
export const UseHover = () => {
10+
const pRef = useRef<HTMLParagraphElement>(null);
11+
const hover = useHover(pRef);
12+
13+
return (
14+
<div>
15+
<p>Is hover: {hover ? "hover" : "not hover"}</p>
16+
<p ref={pRef}>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Id delectus deserunt atque! Est id voluptatem sint accusamus non doloribus totam nobis nostrum provident facilis eos eum, placeat consequatur minus quidem.</p>
17+
</div>
18+
);
19+
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ export const COMPONENTS = [
4242
],
4343
//EVENTS
4444
[
45-
"usePerformAction",
46-
"useEventListener",
4745
"useEvents",
46+
"useEventListener",
4847
"useEventDispatcher",
48+
"usePerformAction",
49+
"useHover"
4950
],
5051
//API DOM
5152
[
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# useHover
2+
Hook that determines whether the item is hovered or not and handles state hovers.
3+
4+
## Usage
5+
6+
```tsx
7+
export const UseHover = () => {
8+
const pRef = useRef<HTMLParagraphElement>(null);
9+
const hover = useHover(pRef);
10+
11+
return (
12+
<div>
13+
<p>Is hover: {hover ? "hover" : "not hover"}</p>
14+
<p ref={pRef}>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Id delectus deserunt atque! Est id voluptatem sint accusamus non doloribus totam nobis nostrum provident facilis eos eum, placeat consequatur minus quidem.</p>
15+
</div>
16+
);
17+
}
18+
```
19+
20+
> The component renders a paragraph element with some text to which is attacched a ref used for __useHover__ hook that returns if element is hovered or not. This value is rendered into another paragraph element.
21+
>
22+
> When mouse is hovered paragraph element, status changes to true.
23+
24+
25+
## API
26+
27+
```tsx
28+
useHover(target: RefObject<HTMLElement> | HTMLElement, opts?: { onEnter?: (evt: Event) => void, onChange?: (isHover: boolean) => void, onLeave?: (evt: Event) => void, returnValue?: boolean }): boolean | void
29+
```
30+
31+
> ### Params
32+
>
33+
> - __target__: _RefObject<HTMLElement> | HTMLElement_
34+
DOM element or ref
35+
> - __opts?__: _{ onEnter?: (evt: Event) => void, onChange?: (isHover: boolean) => void, onLeave?: (evt: Event) => void, returnValue?: boolean }_
36+
__onEnter__ function to be executed on starting hover, __onLeave__ function to be executed on hover finished, __onChange__ function to be executed when hover state changes, __return value__ boolean to return hover state value or not.
37+
>
38+
39+
> ### Returns
40+
>
41+
> __result__: if __returnValue__ option is true or not specified, hook return state hover value, otherwise returns nothing.
42+
> - _boolean|void_
43+
>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ _useId_ hook polyfilled for React versions below 18: __not use for key prop__.
44
## API
55

66
```tsx
7-
useId()
7+
useId(): string
88
```
99

1010
> ### Params

packages/react-tools/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
- [x] useEventListener
4444
- [x] useEventDispatcher
4545
- [x] usePerformAction
46-
- [ ] useHover
46+
- [x] useHover
4747
- [ ] useResponsive
4848
- [ ] useClickOutside
4949
- [ ] useSize (=useMeasuree?)

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,6 @@ export { useClipboard } from './useClipboard';
4141
export { useMediaQuery } from './useMediaQuery';
4242
export { useColorScheme } from './useColorScheme';
4343
export { useTitle } from './useTitle'
44-
export { useLazyRef } from './useLazyRef';
44+
export { useLazyRef } from './useLazyRef';
45+
export { useId } from './useId';
46+
export { useHover } from './useHover';
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { RefObject, useCallback, useMemo, useRef } from "react";
2+
import { useSyncExternalStore } from ".";
3+
4+
/**
5+
* **`useHover`**: Hook that determines whether the item is hovered or not and handles state hovers.
6+
* @param {RefObject<HTMLElement> | HTMLElement} target - DOM element or ref
7+
* @param {{ onEnter?: (evt: Event) => void, onChange?: (isHover: boolean) => void, onLeave?: (evt: Event) => void, returnValue?: boolean }} [opts] - __onEnter__ function to be executed on starting hover, __onLeave__ function to be executed on hover finished, __onChange__ function to be executed when hover state changes, __return value__ boolean to return hover state value or not.
8+
* @returns {boolean|void} result - if __returnValue__ option is true or not specified, hook return state hover value, otherwise returns nothing.
9+
*/
10+
function useHover(target: RefObject<HTMLElement> | HTMLElement, opts?: { onEnter?: (evt: Event) => void, onChange?: (isHover: boolean) => void, onLeave?: (evt: Event) => void, returnValue?: true }): boolean;
11+
function useHover(target: RefObject<HTMLElement> | HTMLElement, opts?: { onEnter?: (evt: Event) => void, onChange?: (isHover: boolean) => void, onLeave?: (evt: Event) => void, returnValue?: false }): void;
12+
function useHover(target: RefObject<HTMLElement> | HTMLElement, opts?: { onEnter?: (evt: Event) => void, onChange?: (isHover: boolean) => void, onLeave?: (evt: Event) => void, returnValue?: boolean }): boolean | void {
13+
const isHoverCached = useRef({ current: false, previous: false });
14+
const onEnt = useMemo(()=> opts?.onEnter, [opts?.onEnter])
15+
const onLeav = useMemo(()=> opts?.onLeave, [opts?.onLeave])
16+
const onCng = useMemo(()=> opts?.onChange, [opts?.onChange])
17+
const isHover = useSyncExternalStore(
18+
useCallback(notif => {
19+
const element = (target as RefObject<HTMLElement>).current
20+
? (target as RefObject<HTMLElement>).current
21+
: target as HTMLElement;
22+
const pointerEnter = (evt: Event) => {
23+
isHoverCached.current.current = true;
24+
onEnt && onEnt(evt);
25+
onCng && onCng(true);
26+
notif();
27+
}
28+
const pointerLeave = (evt: Event) => {
29+
isHoverCached.current.current = false;
30+
onLeav && onLeav(evt);
31+
onCng && onCng(false);
32+
notif();
33+
}
34+
element?.addEventListener("pointerenter", pointerEnter);
35+
element?.addEventListener("pointerleave", pointerLeave);
36+
37+
return () => {
38+
element?.removeEventListener("pointerenter", pointerEnter);
39+
element?.removeEventListener("pointerleave", pointerLeave);
40+
}
41+
}, [onCng, onEnt, onLeav, target]),
42+
useCallback(() => {
43+
if (isHoverCached.current.current !== isHoverCached.current.previous) {
44+
isHoverCached.current.previous = isHoverCached.current.current;
45+
}
46+
return isHoverCached.current.previous;
47+
}, [])
48+
)
49+
50+
if (!opts || opts.returnValue === true) {
51+
return isHover;
52+
}
53+
}
54+
55+
export {useHover}

packages/react-tools/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ export {
5050
useMediaQuery,
5151
useColorScheme,
5252
useTitle,
53-
useLazyRef
53+
useLazyRef,
54+
useId,
55+
useHover
5456
} from './hooks'
5557

5658
export {

0 commit comments

Comments
 (0)