Skip to content

Commit 45a0d9a

Browse files
committed
main 🧊 add use focus trap, use visibility
1 parent 5a67b18 commit 45a0d9a

File tree

20 files changed

+803
-209
lines changed

20 files changed

+803
-209
lines changed
Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,35 @@
11
export * from './useBreakpoints/useBreakpoints';
2-
// device
32
export * from './useDeviceMotion/useDeviceMotion';
43
export * from './useDeviceOrientation/useDeviceOrientation';
54
export * from './useDevicePixelRatio/useDevicePixelRatio';
65
export * from './useDocumentEvent/useDocumentEvent';
76
export * from './useDocumentVisibility/useDocumentVisibility';
8-
// size
97
export * from './useElementSize/useElementSize';
10-
// event
118
export * from './useEventListener/useEventListener';
129
export * from './useHotkeys/useHotkeys';
1310
export * from './useIdle/useIdle';
1411
export * from './useInfiniteScroll/useInfiniteScroll';
1512
export * from './useIntersectionObserver/useIntersectionObserver';
16-
// keyboard
1713
export * from './useKeyboard/useKeyboard';
1814
export * from './useKeyPress/useKeyPress';
1915
export * from './useKeyPressEvent/useKeyPressEvent';
2016
export * from './useKeysPressed/useKeysPressed';
21-
// scroll
2217
export * from './useLockScroll/useLockScroll';
2318
export * from './useMeasure/useMeasure';
2419
export * from './useMediaQuery/useMediaQuery';
25-
// mouse
2620
export * from './useMouse/useMouse';
2721
export * from './useMutationObserver/useMutationObserver';
2822
export * from './useOrientation/useOrientation';
2923
export * from './usePageLeave/usePageLeave';
3024
export * from './useParallax/useParallax';
3125
export * from './usePerformanceObserver/usePerformanceObserver';
32-
// observer
3326
export * from './useResizeObserver/useResizeObserver';
3427
export * from './useScroll/useScroll';
3528
export * from './useScrollIntoView/useScrollIntoView';
3629
export * from './useScrollTo/useScrollTo';
37-
// text
3830
export * from './useTextSelection/useTextSelection';
31+
export * from './useVisibility/useVisibility';
3932
export * from './useWindowEvent/useWindowEvent';
40-
// visibility
4133
export * from './useWindowFocus/useWindowFocus';
4234
export * from './useWindowScroll/useWindowScroll';
4335
export * from './useWindowSize/useWindowSize';

‎packages/core/src/bundle/hooks/useFocusTrap/useFocusTrap.js‎

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,19 @@ import { useState } from 'react';
22
import { getElement, isTarget } from '@/utils/helpers';
33
import { useIsomorphicLayoutEffect } from '../useIsomorphicLayoutEffect/useIsomorphicLayoutEffect';
44
import { useRefState } from '../useRefState/useRefState';
5-
const FOCUSABLE_ELEMENTS_SELECTOR = [
6-
'input:not([disabled])',
7-
'select:not([disabled])',
8-
'textarea:not([disabled])',
9-
'button:not([disabled])',
10-
'a[href]',
11-
'area[href]',
12-
'summary',
13-
'iframe',
14-
'object',
15-
'embed',
16-
'audio[controls]',
17-
'video[controls]',
18-
'[contenteditable]',
19-
'[tabindex]:not([tabindex^="-"])'
20-
].join(',');
5+
export const FOCUS_SELECTOR = 'a, input, select, textarea, button, object, [tabindex]';
216
const getFocusableElements = (element) => {
22-
const elements = Array.from(element.querySelectorAll(FOCUSABLE_ELEMENTS_SELECTOR));
23-
return elements.filter((el) => {
24-
const htmlEl = el;
7+
const elements = Array.from(element.querySelectorAll(FOCUS_SELECTOR));
8+
return elements.filter((element) => {
9+
const htmlEl = element;
2510
return htmlEl.tabIndex !== -1 && !htmlEl.hidden && htmlEl.style.display !== 'none';
2611
});
2712
};
2813
const focusElement = (element) => {
2914
const autofocusElement = element.querySelector('[data-autofocus]');
3015
if (autofocusElement) return autofocusElement.focus();
3116
const focusableElements = getFocusableElements(element);
32-
if (focusableElements.length > 0) {
33-
focusableElements[0].focus();
34-
}
17+
if (focusableElements.length) focusableElements[0].focus();
3518
};
3619
/**
3720
* @name useFocusTrap

‎packages/core/src/bundle/hooks/useIntersectionObserver/useIntersectionObserver.js‎

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { useRefState } from '../useRefState/useRefState';
1717
* @returns {UseIntersectionObserverReturn} An object containing the state
1818
*
1919
* @example
20-
* const { ref, entry, inView, observer } = useIntersectionObserver();
20+
* const { ref, entries, observer } = useIntersectionObserver();
2121
*
2222
* @overload
2323
* @template Target The target element
@@ -27,23 +27,23 @@ import { useRefState } from '../useRefState/useRefState';
2727
* @returns {UseIntersectionObserverReturn & { ref: StateRef<Target> }} A React ref to attach to the target element
2828
*
2929
* @example
30-
* const { entry, inView, observer } = useIntersectionObserver(ref);
30+
* const { entries, observer } = useIntersectionObserver(ref);
3131
*
3232
* @overload
3333
* @template Target The target element
3434
* @param {UseIntersectionObserverCallback} callback The callback to execute when intersection is detected
3535
* @returns {UseIntersectionObserverReturn & { ref: StateRef<Target> }} A React ref to attach to the target element
3636
*
3737
* @example
38-
* const { ref, entry, inView, observer } = useIntersectionObserver(() => console.log('callback'));
38+
* const { ref, entries, observer } = useIntersectionObserver(() => console.log('callback'));
3939
*
4040
* @overload
4141
* @param {UseIntersectionObserverCallback} callback The callback to execute when intersection is detected
4242
* @param {HookTarget} target The target element to detect intersection
4343
* @returns {UseIntersectionObserverReturn} An object containing the state
4444
*
4545
* @example
46-
* const { entry, inView, observer } = useIntersectionObserver(() => console.log('callback'), ref);
46+
* const { entries, observer } = useIntersectionObserver(ref, () => console.log('callback'));
4747
*/
4848
export const useIntersectionObserver = (...params) => {
4949
const target = isTarget(params[0]) ? params[0] : undefined;
@@ -57,7 +57,7 @@ export const useIntersectionObserver = (...params) => {
5757
const callback = options?.onChange;
5858
const enabled = options?.enabled ?? true;
5959
const [observer, setObserver] = useState();
60-
const [entry, setEntry] = useState();
60+
const [entries, setEntries] = useState();
6161
const internalRef = useRefState();
6262
const internalCallbackRef = useRef(callback);
6363
internalCallbackRef.current = callback;
@@ -66,9 +66,9 @@ export const useIntersectionObserver = (...params) => {
6666
const element = target ? getElement(target) : internalRef.current;
6767
if (!element) return;
6868
const observer = new IntersectionObserver(
69-
([entry], observer) => {
70-
setEntry(entry);
71-
internalCallbackRef.current?.(entry, observer);
69+
(entries, observer) => {
70+
setEntries(entries);
71+
internalCallbackRef.current?.(entries, observer);
7272
},
7373
{
7474
...options,
@@ -81,11 +81,10 @@ export const useIntersectionObserver = (...params) => {
8181
observer.disconnect();
8282
};
8383
}, [target, internalRef.state, options?.rootMargin, options?.threshold, options?.root, enabled]);
84-
if (target) return { observer, entry, inView: !!entry?.isIntersecting };
84+
if (target) return { observer, entries };
8585
return {
8686
observer,
8787
ref: internalRef,
88-
entry,
89-
inView: !!entry?.isIntersecting
88+
entries
9089
};
9190
};

‎packages/core/src/bundle/hooks/useUrlSearchParams/useUrlSearchParams.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export const useUrlSearchParams = (params) => {
4242
'mode' in params ||
4343
'write' in params)
4444
? params
45-
: undefined;
45+
: {};
4646
const initialValue = options ? options?.initialValue : params;
4747
const { mode = 'history', write: writeMode = 'replace' } = options;
4848
const serializer = (value) => {
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { useEffect, useRef, useState } from 'react';
2+
import { getElement, isTarget } from '@/utils/helpers';
3+
import { useRefState } from '../useRefState/useRefState';
4+
/**
5+
* @name useVisibility
6+
* @description - Hook that gives you visibility observer state
7+
* @category Sensors
8+
* @usage medium
9+
*
10+
* @browserapi IntersectionObserver https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver
11+
*
12+
* @overload
13+
* @param {HookTarget} target The target element to detect intersection
14+
* @param {boolean} [options.enabled=true] The Intersection options
15+
* @param {((entries: IntersectionEntry[], observer: Intersection) => void) | undefined} [options.onChange] The callback to execute when intersection is detected
16+
* @param {HookTarget} [options.root=document] The root element to observe
17+
* @returns {UseVisibilityReturn} An object containing the state
18+
*
19+
* @example
20+
* const { ref, entries, observer } = useVisibility();
21+
*
22+
* @overload
23+
* @template Target The target element
24+
* @param {boolean} [options.enabled=true] The Intersection options
25+
* @param {((entries: IntersectionEntry[], observer: Intersection) => void) | undefined} [options.onChange] The callback to execute when intersection is detected
26+
* @param {HookTarget} [options.root=document] The root element to observe
27+
* @returns {UseVisibilityReturn & { ref: StateRef<Target> }} A React ref to attach to the target element
28+
*
29+
* @example
30+
* const { entries, observer } = useVisibility(ref);
31+
*
32+
* @overload
33+
* @template Target The target element
34+
* @param {UseVisibilityCallback} callback The callback to execute when intersection is detected
35+
* @returns {UseVisibilityReturn & { ref: StateRef<Target> }} A React ref to attach to the target element
36+
*
37+
* @example
38+
* const { ref, entries, observer } = useVisibility(() => console.log('callback'));
39+
*
40+
* @overload
41+
* @param {UseVisibilityCallback} callback The callback to execute when intersection is detected
42+
* @param {HookTarget} target The target element to detect intersection
43+
* @returns {UseVisibilityReturn} An object containing the state
44+
*
45+
* @example
46+
* const { entries, observer } = useVisibility(ref, () => console.log('callback'));
47+
*/
48+
export const useVisibility = (...params) => {
49+
const target = isTarget(params[0]) ? params[0] : undefined;
50+
const options = target
51+
? typeof params[1] === 'object'
52+
? params[1]
53+
: { onChange: params[1] }
54+
: typeof params[0] === 'object'
55+
? params[0]
56+
: { onChange: params[0] };
57+
const callback = options?.onChange;
58+
const enabled = options?.enabled ?? true;
59+
const [observer, setObserver] = useState();
60+
const [entry, setEntry] = useState();
61+
const internalRef = useRefState();
62+
const internalCallbackRef = useRef(callback);
63+
internalCallbackRef.current = callback;
64+
useEffect(() => {
65+
if (!enabled || (!target && !internalRef.state)) return;
66+
const element = target ? getElement(target) : internalRef.current;
67+
if (!element) return;
68+
const observer = new IntersectionObserver(
69+
(entries, observer) => {
70+
const entry = entries.pop();
71+
setEntry(entry);
72+
internalCallbackRef.current?.(entry, observer);
73+
},
74+
{
75+
...options,
76+
root: options?.root ? getElement(options.root) : document
77+
}
78+
);
79+
setObserver(observer);
80+
observer.observe(element);
81+
return () => {
82+
observer.disconnect();
83+
};
84+
}, [target, internalRef.state, options?.rootMargin, options?.threshold, options?.root, enabled]);
85+
if (target) return { observer, entry, inView: !!entry?.isIntersecting };
86+
return {
87+
observer,
88+
ref: internalRef,
89+
entry,
90+
inView: !!entry?.isIntersecting
91+
};
92+
};

‎packages/core/src/hooks/elements.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export * from './useDropZone/useDropZone';
88
export * from './useFileDialog/useFileDialog';
99

1010
export * from './useFocus/useFocus';
11+
export * from './useFocusTrap/useFocusTrap';
1112
export * from './useHover/useHover';
1213
// ui
1314
export * from './useImage/useImage';
Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,35 @@
11
export * from './useBreakpoints/useBreakpoints';
2-
3-
// device
42
export * from './useDeviceMotion/useDeviceMotion';
53
export * from './useDeviceOrientation/useDeviceOrientation';
64
export * from './useDevicePixelRatio/useDevicePixelRatio';
75
export * from './useDocumentEvent/useDocumentEvent';
86
export * from './useDocumentVisibility/useDocumentVisibility';
9-
10-
// size
117
export * from './useElementSize/useElementSize';
12-
// event
138
export * from './useEventListener/useEventListener';
149
export * from './useHotkeys/useHotkeys';
15-
1610
export * from './useIdle/useIdle';
1711
export * from './useInfiniteScroll/useInfiniteScroll';
1812
export * from './useIntersectionObserver/useIntersectionObserver';
19-
// keyboard
2013
export * from './useKeyboard/useKeyboard';
2114
export * from './useKeyPress/useKeyPress';
22-
2315
export * from './useKeyPressEvent/useKeyPressEvent';
2416
export * from './useKeysPressed/useKeysPressed';
25-
// scroll
2617
export * from './useLockScroll/useLockScroll';
2718
export * from './useMeasure/useMeasure';
2819
export * from './useMediaQuery/useMediaQuery';
29-
// mouse
3020
export * from './useMouse/useMouse';
3121
export * from './useMutationObserver/useMutationObserver';
32-
3322
export * from './useOrientation/useOrientation';
3423
export * from './usePageLeave/usePageLeave';
3524
export * from './useParallax/useParallax';
3625
export * from './usePerformanceObserver/usePerformanceObserver';
37-
38-
// observer
3926
export * from './useResizeObserver/useResizeObserver';
4027
export * from './useScroll/useScroll';
4128
export * from './useScrollIntoView/useScrollIntoView';
4229
export * from './useScrollTo/useScrollTo';
43-
44-
// text
4530
export * from './useTextSelection/useTextSelection';
46-
31+
export * from './useVisibility/useVisibility';
4732
export * from './useWindowEvent/useWindowEvent';
48-
// visibility
4933
export * from './useWindowFocus/useWindowFocus';
5034
export * from './useWindowScroll/useWindowScroll';
5135
export * from './useWindowSize/useWindowSize';

0 commit comments

Comments
 (0)