UNPKG

2.28 kBJavaScriptView Raw
1import { useCallback, useMemo, useRef } from 'react';
2import useEventCallback from './useEventCallback';
3import useMounted from './useMounted';
4
5/**
6 * useFocusManager provides a way to track and manage focus as it moves around
7 * a container element. An `onChange` is fired when focus enters or leaves the
8 * element, but not when it moves around inside the element, similar to
9 * `pointerenter` and `pointerleave` DOM events.
10 *
11 * ```tsx
12 * const [focused, setFocusState] = useState(false)
13 *
14 * const { onBlur, onFocus } = useFocusManager({
15 * onChange: nextFocused => setFocusState(nextFocused)
16 * })
17 *
18 * return (
19 * <div tabIndex="-1" onFocus={onFocus} onBlur={onBlur}>
20 * {String(focused)}
21 * <input />
22 * <input />
23 *
24 * <button>A button</button>
25 * </div>
26 * ```
27 *
28 * @returns a memoized FocusController containing event handlers
29 */
30export default function useFocusManager(opts) {
31 var isMounted = useMounted();
32 var lastFocused = useRef();
33 var handle = useRef();
34 var willHandle = useEventCallback(opts.willHandle);
35 var didHandle = useEventCallback(opts.didHandle);
36 var onChange = useEventCallback(opts.onChange);
37 var isDisabled = useEventCallback(opts.isDisabled);
38 var handleFocusChange = useCallback(function (focused, event) {
39 if (event && event.persist) event.persist();
40 if (willHandle && willHandle(focused, event) === false) return;
41 clearTimeout(handle.current);
42 handle.current = window.setTimeout(function () {
43 if (focused !== lastFocused.current) {
44 if (didHandle) didHandle(focused, event); // only fire a change when unmounted if its a blur
45
46 if (isMounted() || !focused) {
47 lastFocused.current = focused;
48 onChange && onChange(focused, event);
49 }
50 }
51 });
52 }, [isMounted, willHandle, didHandle, onChange, lastFocused]);
53 var handleBlur = useCallback(function (event) {
54 if (!isDisabled()) handleFocusChange(false, event);
55 }, [handleFocusChange, isDisabled]);
56 var handleFocus = useCallback(function (event) {
57 if (!isDisabled()) handleFocusChange(true, event);
58 }, [handleFocusChange, isDisabled]);
59 return useMemo(function () {
60 return {
61 onBlur: handleBlur,
62 onFocus: handleFocus
63 };
64 }, [handleBlur, handleFocus]);
65}
\No newline at end of file