Skip to content

Commit 0bc2248

Browse files
committed
[IMPL] useActiveElement hook
1 parent 46cd7ba commit 0bc2248

4 files changed

Lines changed: 31 additions & 11 deletions

File tree

apps/react-tools-demo/src/components/hooks/useActiveElement/UseActiveElement.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import { useActiveElement } from "../../../../../../packages/react-tools/src"
22

33
/**
4-
DEMO
4+
The component has 3 HTML elements: a _span_ element with __tabindex__ attribute, a _p_ element with __tabindex__ attribute and a _button_ element. It renders also the current _active element_. If element focused changes, _active element_ displayed will change.
55
*/
66
export const UseActiveElement = () => {
77
const activeElement = useActiveElement();
8-
console.log("render", activeElement?.tagName)
98

109
return (<>
1110
<p>Active element is: {activeElement?.tagName}</p>
1211
<div style={{ display: "grid", gridTemplateColumns: "auto auto auto", justifyContent: "center", gap: 50 }}>
13-
<span tabIndex={0}>Span</span>
14-
<p tabIndex={0}>P</p>
12+
<span tabIndex={0} style={{backgroundColor: '#1A1A1A', borderRadius: 8, cursor: "pointer", padding: "15px 25px", margin: 'auto'}}>Span</span>
13+
<p tabIndex={0} style={{backgroundColor: '#1A1A1A', borderRadius: 8, cursor: "pointer", padding: "15px 25px", margin: 0}}>P</p>
1514
<button>Button</button>
1615
</div>
1716
</>)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ export const UseActiveElement = () => {
1010
return (<>
1111
<p>Active element is: {activeElement?.tagName}</p>
1212
<div style={{ display: "grid", gridTemplateColumns: "auto auto auto", justifyContent: "center", gap: 50 }}>
13-
<span tabIndex={0}>Span</span>
14-
<p tabIndex={0}>P</p>
13+
<span tabIndex={0} style={{backgroundColor: '#1A1A1A', borderRadius: 8, cursor: "pointer", padding: "15px 25px", margin: 'auto'}}>Span</span>
14+
<p tabIndex={0} style={{backgroundColor: '#1A1A1A', borderRadius: 8, cursor: "pointer", padding: "15px 25px", margin: 0}}>P</p>
1515
<button>Button</button>
1616
</div>
1717
</>)
1818
}
1919
```
2020

21-
> DEMO
21+
> The component has 3 HTML elements: a _span_ element with __tabindex__ attribute, a _p_ element with __tabindex__ attribute and a _button_ element. It renders also the current active element. If element focused changes, active element displayed will change.
2222
2323

2424
## API

packages/react-tools/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
- [x] useScript
6161
- [x] useDebounce
6262
- [x] useThrottle
63-
- [-] useActiveElement
63+
- [x] useActiveElement
6464
- [ ] useTimeout
6565
- [ ] useInterval
6666
- [ ] useTextSelection

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,34 @@ export const useActiveElement = ():Element|null => {
1111
return useSyncExternalStore(
1212
useCallback(notif => {
1313
previousElem.current = document.activeElement;
14-
const listener = () => notif();
14+
const idTimeout:{id: number | NodeJS.Timeout} = { id: 0 };
15+
const listener = (e: Event) => {
16+
/**
17+
* INFO
18+
* When switching from one active element, other than the body,
19+
* to another, there is a first rerender due to the focusout event
20+
* which returns the body as the active element. To overcome this,
21+
* the execution of the notify callback during the focusout event
22+
* is delayed by one iteration: if the focusout event is followed
23+
* by a focusin event, this will cancel the previous execution
24+
* of the notify callback and will only rerender the new active
25+
* element.
26+
*/
27+
if (e.type === "focusout") {
28+
idTimeout.id = setTimeout(() => {
29+
notif()
30+
}, 0);
31+
} else {
32+
clearTimeout(idTimeout.id);
33+
notif();
34+
}
35+
}
1536
addEventListener("focusin", listener, true);
1637
addEventListener("focusout", listener, true);
1738

1839
return () => {
19-
addEventListener("focusin", listener, true);
20-
addEventListener("focusout", listener, true);
40+
removeEventListener("focusin", listener, true);
41+
removeEventListener("focusout", listener, true);
2142
previousElem.current = null;
2243
}
2344
}, []),

0 commit comments

Comments
 (0)