Skip to content

Commit 0d507fc

Browse files
committed
main 🧊 add use window focus, add use active element
1 parent 7fa1781 commit 0d507fc

File tree

7 files changed

+131
-14
lines changed

7 files changed

+131
-14
lines changed

‎src/hooks/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from './useActiveElement/useActiveElement';
12
export * from './useAsync/useAsync';
23
export * from './useBattery/useBattery';
34
export * from './useBoolean/useBoolean';
@@ -96,6 +97,7 @@ export * from './useToggle/useToggle';
9697
export * from './useUnmount/useUnmount';
9798
export * from './useWebSocket/useWebSocket';
9899
export * from './useWindowEvent/useWindowEvent';
100+
export * from './useWindowFocus/useWindowFocus';
99101
export * from './useWindowScroll/useWindowScroll';
100102
export * from './useWindowSize/useWindowSize';
101103
export * from './useWizard/useWizard';
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { useActiveElement } from './useActiveElement';
2+
3+
const Demo = () => {
4+
const activeElement = useActiveElement();
5+
const activeElementId = activeElement?.dataset?.id ?? 'null';
6+
7+
return (
8+
<>
9+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '5px' }}>
10+
{Array.from({ length: 6 }, (_, i) => i + 1).map((id) => (
11+
<input key={id} type='text' data-id={String(id)} placeholder={String(id)} />
12+
))}
13+
</div>
14+
15+
<p>
16+
current active element: <code>{activeElementId}</code>
17+
</p>
18+
</>
19+
);
20+
};
21+
22+
export default Demo;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { useEffect, useState } from 'react';
2+
3+
import { useMutationObserver } from '../useMutationObserver/useMutationObserver';
4+
5+
/**
6+
* @name useActiveElement
7+
* @description - Hook that returns the active element
8+
* @category Elements
9+
*
10+
* @returns {ActiveElement | null} The active element
11+
*
12+
* @example
13+
* const activeElement = useActiveElement();
14+
*/
15+
export const useActiveElement = <ActiveElement extends HTMLElement>() => {
16+
const [activeElement, setActiveElement] = useState<ActiveElement | null>(null);
17+
18+
useEffect(() => {
19+
const onActiveElementChange = () =>
20+
setActiveElement(document?.activeElement as ActiveElement | null);
21+
22+
window.addEventListener('focus', onActiveElementChange, true);
23+
window.addEventListener('blur', onActiveElementChange, true);
24+
return () => {
25+
window.removeEventListener('focus', onActiveElementChange);
26+
window.removeEventListener('blur', onActiveElementChange);
27+
};
28+
});
29+
30+
useMutationObserver(
31+
document as any,
32+
(mutations) => {
33+
mutations
34+
.filter((mutation) => mutation.removedNodes.length)
35+
.map((mutation) => Array.from(mutation.removedNodes))
36+
.flat()
37+
.forEach((node) => {
38+
if (node === activeElement)
39+
setActiveElement(document?.activeElement as ActiveElement | null);
40+
});
41+
},
42+
{
43+
childList: true,
44+
subtree: true
45+
}
46+
);
47+
48+
return activeElement;
49+
};
Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
1+
import { useEffect, useState } from 'react';
2+
13
import { useDidUpdate } from '../useDidUpdate/useDidUpdate';
4+
import { useTimer } from '../useTimer/useTimer';
25

36
import { useDocumentVisibility } from './useDocumentVisibility';
47

8+
const START_MESSAGE = '💡 Minimize the page or switch tab then return';
9+
510
const Demo = () => {
11+
const [message, setMessage] = useState(START_MESSAGE);
612
const documentVisibility = useDocumentVisibility();
713

8-
useDidUpdate(() => {
9-
console.log(`Current document visibility state: ${documentVisibility}`);
14+
const timer = useTimer(3000, () => {
15+
setMessage(START_MESSAGE);
16+
});
1017

18+
useDidUpdate(() => {
1119
if (documentVisibility === 'visible') {
12-
alert(`Current document visibility state: ${documentVisibility}`);
20+
setMessage('🎉 Welcome back!');
21+
timer.start();
1322
}
1423
}, [documentVisibility]);
1524

16-
return (
17-
<div>
18-
<p>
19-
Switch to another tab and then return here
20-
<br />
21-
<br />
22-
Visibility status: <code>{documentVisibility}</code>
23-
</p>
24-
</div>
25-
);
25+
return <p>{message}</p>;
2626
};
2727

2828
export default Demo;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { useWindowFocus } from './useWindowFocus';
2+
3+
const Demo = () => {
4+
const windowFocused = useWindowFocus();
5+
6+
return (
7+
<p>
8+
{windowFocused && '💡 Click somewhere outside of the document to unfocus.'}
9+
{!windowFocused && 'ℹ Tab is unfocused'}
10+
</p>
11+
);
12+
};
13+
14+
export default Demo;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { useEffect, useState } from 'react';
2+
3+
/**
4+
* @name useWindowFocus
5+
* @description - Hook that provides the current focus state of the window
6+
* @category Elements
7+
*
8+
* @returns {boolean} The current focus state of the window
9+
*
10+
* @example
11+
* const focused = useWindowFocus();
12+
*/
13+
export const useWindowFocus = () => {
14+
const [focused, setFocused] = useState(false);
15+
16+
useEffect(() => {
17+
const onFocus = () => setFocused(true);
18+
const onBlur = () => setFocused(false);
19+
20+
window.addEventListener('focus', onFocus);
21+
window.addEventListener('blur', onBlur);
22+
23+
return () => {
24+
window.removeEventListener('focus', onFocus);
25+
window.removeEventListener('blur', onBlur);
26+
};
27+
});
28+
29+
return focused;
30+
};

‎src/hooks/useWindowSize/useWindowSize.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export interface UseWindowSizeReturn {
1919
/**
2020
* @name useWindowSize
2121
* @description - Hook that manages a window size
22-
* @category Browser
22+
* @category Elements
2323
*
2424
* @param {number} [params.initialWidth=Number.POSITIVE_INFINITY] The initial window width
2525
* @param {number} [params.initialHeight=Number.POSITIVE_INFINITY] The initial window height

0 commit comments

Comments
 (0)