-
Notifications
You must be signed in to change notification settings - Fork 95
/
index.ts
71 lines (61 loc) · 2.29 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import {type DependencyList, type RefObject, useMemo} from 'react';
import {useEventListener} from '../useEventListener/index.js';
import {useSyncedRef} from '../useSyncedRef/index.js';
import {isBrowser} from '../util/const.js';
import {yieldFalse, yieldTrue} from '../util/misc.js';
export type KeyboardEventPredicate = (event: KeyboardEvent) => boolean;
export type KeyboardEventFilter = null | string | boolean | KeyboardEventPredicate;
export type KeyboardEventHandler<T extends EventTarget> = (this: T, event: KeyboardEvent) => void;
export type UseKeyboardEventOptions<T extends EventTarget> = {
/**
* Keyboard event which triggers `callback`.
* @default `keydown`
*/
event?: 'keydown' | 'keypress' | 'keyup';
/**
* Target element that emits `event`.
* @default window
*/
target?: RefObject<T> | T | null;
/**
* Options passed to the underlying `useEventListener` hook.
*/
eventOptions?: boolean | AddEventListenerOptions;
};
const createKeyPredicate = (keyFilter: KeyboardEventFilter): KeyboardEventPredicate => {
if (typeof keyFilter === 'function') {
return keyFilter;
}
if (typeof keyFilter === 'string') {
return ev => ev.key === keyFilter;
}
return keyFilter ? yieldTrue : yieldFalse;
};
const WINDOW_OR_NULL = isBrowser ? globalThis : null;
/**
* Invokes a callback when a keyboard event occurs on the chosen target element.
*
* @param keyOrPredicate Filters key presses on which `callback` is invoked.
* @param callback Function to call when a key is pressed and `keyOrPredicate` matches positive.
* @param deps Dependencies list that is passed to the underlying `useMemo`.
* @param options Hook options.
*/
export function useKeyboardEvent<T extends EventTarget>(
keyOrPredicate: KeyboardEventFilter,
callback: KeyboardEventHandler<T>,
deps: DependencyList = [],
options: UseKeyboardEventOptions<T> = {},
): void {
const {event = 'keydown', target = WINDOW_OR_NULL, eventOptions} = options;
const cbRef = useSyncedRef(callback);
const handler = useMemo<KeyboardEventHandler<T>>(() => {
const predicate = createKeyPredicate(keyOrPredicate);
return function (this: T, ev) {
if (predicate(ev)) {
cbRef.current.call(this, ev);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
useEventListener(target, event, handler, eventOptions);
}