-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(useKeyboardEvent): improve the code and change signature (#248)
Also improve docs and arguments description. BREAKING CHANGE: hook call signature has changed.
- Loading branch information
Showing
6 changed files
with
63 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,64 @@ | ||
import { DependencyList, RefObject, useMemo } from 'react'; | ||
import { useEventListener } from '..'; | ||
import { noop, isBrowser } from '../util/const'; | ||
import { yieldTrue, yieldFalse } from '../util/misc'; | ||
import { useEventListener, useSyncedRef } from '..'; | ||
import { isBrowser } from '../util/const'; | ||
import { yieldFalse, yieldTrue } from '../util/misc'; | ||
|
||
export type IKeyboardEventPredicate = (event: KeyboardEvent) => boolean; | ||
export type IKeyboardEventFilter = null | undefined | string | IKeyboardEventPredicate; | ||
export type IKeyboardEventHandler = (event: KeyboardEvent) => void; | ||
export type IKeyboardEventFilter = null | undefined | string | boolean | IKeyboardEventPredicate; | ||
export type IKeyboardEventHandler<T extends EventTarget> = (this: T, event: KeyboardEvent) => void; | ||
|
||
export type IUseKeyboardEventOptions<T extends EventTarget> = { | ||
/** | ||
* Event name that triggers handler. | ||
* @default `keydown` | ||
*/ | ||
event?: 'keydown' | 'keypress' | 'keyup'; | ||
/** | ||
* Target that should emit event. | ||
* @default window | ||
*/ | ||
target?: RefObject<T> | T | null; | ||
/** | ||
* Options that will be passed to underlying `useEventListener` hook. | ||
*/ | ||
eventOptions?: boolean | AddEventListenerOptions; | ||
}; | ||
|
||
const createKeyPredicate = (keyFilter: IKeyboardEventFilter): IKeyboardEventPredicate => { | ||
if (typeof keyFilter === 'function') return keyFilter; | ||
if (typeof keyFilter === 'string') return (event: KeyboardEvent) => event.key === keyFilter; | ||
if (typeof keyFilter === 'string') return (ev) => ev.key === keyFilter; | ||
return keyFilter ? yieldTrue : yieldFalse; | ||
}; | ||
|
||
const WINDOW_OR_NULL = isBrowser ? window : null; | ||
|
||
/** | ||
* React hook to execute a handler when a key is used | ||
* it on unmount. | ||
* Executes callback when keyboard event occurred on target (window by default). | ||
* | ||
* @param keyOrPredicate Key filter can be `string` or callback function accept a KeyboardEvent and return a boolean. | ||
* @param callback callback function to call when key is used accept a KeyboardEvent | ||
* @param deps Dependencies list that will be passed to underlying `useMemo` | ||
* @param options some options passed to addEventListener, event can be `[keydown, keyup, keypress]`, target is the event target default `window`, eventOptions is the third parameter of `addEventListener`. | ||
* @param keyOrPredicate Filters keypresses on which `callback` will be executed. | ||
* @param callback Function to call when key is pressed and `keyOrPredicate` matches positive. | ||
* @param deps Dependencies list that will be passed to underlying `useMemo`. | ||
* @param options Hook options. | ||
*/ | ||
export function useKeyboardEvent<T extends EventTarget>( | ||
keyOrPredicate: IKeyboardEventFilter, | ||
callback: IKeyboardEventHandler = noop, | ||
deps: DependencyList = [keyOrPredicate], | ||
callback: IKeyboardEventHandler<T>, | ||
deps?: DependencyList, | ||
options: IUseKeyboardEventOptions<T> = {} | ||
): void { | ||
const windowOrNull = isBrowser ? window : null; | ||
const { event = 'keydown', target = windowOrNull, eventOptions } = options; | ||
const memoHandler = useMemo(() => { | ||
const predicate: IKeyboardEventPredicate = createKeyPredicate(keyOrPredicate); | ||
// eslint-disable-next-line func-names | ||
const handler: IKeyboardEventHandler = function (handlerEvent) { | ||
if (predicate(handlerEvent)) { | ||
// @ts-expect-error useEventListener will handle this reference to target | ||
callback.call(this, handlerEvent); | ||
const { event = 'keydown', target = WINDOW_OR_NULL, eventOptions } = options; | ||
const cbRef = useSyncedRef(callback); | ||
|
||
const handler = useMemo<IKeyboardEventHandler<T>>(() => { | ||
const predicate = createKeyPredicate(keyOrPredicate); | ||
|
||
return function kbEventHandler(this: T, ev) { | ||
if (predicate(ev)) { | ||
cbRef.current.call(this, ev); | ||
} | ||
}; | ||
return handler; | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, deps); | ||
useEventListener(target, event, memoHandler, eventOptions); | ||
|
||
useEventListener(target, event, handler, eventOptions); | ||
} |