Skip to content

Commit 85b89ca

Browse files
committed
[IMPL] hotKeyHandler utility function
1 parent 9b32eac commit 85b89ca

7 files changed

Lines changed: 86 additions & 46 deletions

File tree

apps/react-tools-demo/src/constants/components.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ export const COMPONENTS = [
9898
"isTouchEvent",
9999
"isMouseEvent",
100100
"isClient",
101-
"isAsync"
101+
"isAsync",
102+
"hotKeyHandler"
102103
]
103104
] as const;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# hotKeyHandler
2+
Utility function for _onKeyDown_ and _onKeyUp_ events handler that supports keys combination.
3+
4+
## API
5+
6+
```tsx
7+
hotKeyHandler (hotKeys: `${string}` | `${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${string}` | `${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${string}`, listener: (evt: KeyboardEvent | KeyEvt<HTMLElement>) => void | Promise<void>): (evt: KeyboardEvent | KeyEvt<HTMLElement>) => void
8+
```
9+
10+
> ### Params
11+
>
12+
> - __hotKeys__: _`${string}` | `${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${string}` | `${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${string}`_
13+
hotKey string: _ctrlCommand_ indicates to listen __Ctrl__ (on Windows) or __Command__ (on Mac) keys.
14+
> - __listener__: _(evt: KeyboardEvent|React.KeyboardEvent<HTMLElement>) => void | Promise<void>_
15+
listener to be executed on specified event
16+
>
17+
18+
> ### Returns
19+
>
20+
>
21+
> - _(evt: KeyboardEvent|React.KeyboardEvent<HTMLElement>) => void_
22+
>

apps/react-tools-demo/src/markdown/useHotKeys.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# useHotKeys
2-
Hook to listen for the keyboard press, support key combinations.
2+
Hook to listen for the keyboard press, support key combinations, built on [hotKeyHandler](#/hotKeyHandler) utility function.
33

44
## Usage
55

packages/react-tools/src/hooks/useHotKeys.ts

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { KeyboardEvent as KeyEvt, RefObject, useCallback } from "react";
1+
import { KeyboardEvent as KeyEvt, RefObject, useMemo } from "react";
22
import { useEventListener } from ".";
3+
import { hotKeyHandler } from "../utils";
34

45
/**
5-
* __`useHotKeys`__: Hook to listen for the keyboard press, support key combinations.
6+
* __`useHotKeys`__: Hook to listen for the keyboard press, support key combinations, built on [hotKeyHandler](#/hotKeyHandler) utility function.
67
* @param {Object} options
78
* @param {`${string}` | `${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${string}` | `${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${string}`} options.hotKey - hotKey string: _ctrlCommand_ indicates to listen __Ctrl__ (on Windows) or __Command__ (on Mac) keys.
89
* @param {"keydown"|"keyup"} [options.type="keydown"] - event type
@@ -12,46 +13,9 @@ import { useEventListener } from ".";
1213
* @returns {()=>void} remove - used to manually remove the eventListener, otherwise is removed when component is unmounted.
1314
*/
1415
export const useHotKeys = ({ hotKey, type = "keydown", target = window, listener, listenerOpts }: { hotKey: `${string}` | `${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${string}` | `${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${string}`, type?: "keydown" | "keyup", target?: RefObject<HTMLElement> | Window, listener: (evt: KeyboardEvent | KeyEvt<HTMLElement>) => void | Promise<void>, listenerOpts?: boolean | AddEventListenerOptions }): (() => void) => {
15-
const handler = useCallback((evt: KeyboardEvent | KeyEvt<HTMLElement>) => {
16-
let keys = hotKey.toLowerCase().split("+").map(el => el.trim());
17-
const modifiers = {
18-
alt: keys.includes('alt'),
19-
ctrl: keys.includes('ctrl'),
20-
meta: keys.includes('meta'),
21-
ctrlCommand: keys.includes('ctrlcommand'),
22-
shift: keys.includes('shift'),
23-
};
24-
keys = keys.filter(el => !['alt', 'ctrl', 'meta', 'shift', 'ctrlCommand'].includes(el));
25-
for (let i = 0, size = keys.length; i < size; i++) {
26-
if (keys[i] === "" && (i + 1) < size && keys[i + 1] === "") {
27-
keys[i + 1] = "+";
28-
}
29-
}
30-
keys = keys.filter(el => el !== "")
31-
const { altKey, ctrlKey, metaKey, shiftKey, key: pressedKey } = evt;
32-
33-
if (modifiers.ctrlCommand) {
34-
if (!ctrlKey && !metaKey) {
35-
return false;
36-
}
37-
} else {
38-
if (ctrlKey !== modifiers.ctrl) {
39-
return false;
40-
}
41-
if (altKey !== modifiers.alt) {
42-
return false;
43-
}
44-
if (metaKey !== modifiers.meta) {
45-
return false;
46-
}
47-
if (shiftKey !== modifiers.shift) {
48-
return false;
49-
}
50-
}
51-
if (!(keys.includes(pressedKey.toLowerCase()) || keys.includes(evt.code.replace("Key", "")))) {
52-
return false
53-
}
54-
listener && listener(evt);
16+
const handler = useMemo(() => {
17+
const handler = hotKeyHandler(hotKey, listener);
18+
return handler;
5519
}, [hotKey, listener]);
5620

5721
const remove = useEventListener<KeyboardEvent>({

packages/react-tools/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,6 @@ export {
9797
isMouseEvent,
9898
isTouchEvent,
9999
isClient,
100-
isAsync
100+
isAsync,
101+
hotKeyHandler
101102
} from './utils'
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { KeyboardEvent as KeyEvt } from "react";
2+
3+
/**
4+
* **`hotKeyHandler`**: utility function for _onKeyDown_ and _onKeyUp_ events handler that supports keys combination.
5+
* @param {`${string}` | `${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${string}` | `${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${string}`} hotKeys - hotKey string: _ctrlCommand_ indicates to listen __Ctrl__ (on Windows) or __Command__ (on Mac) keys.
6+
* @param {(evt: KeyboardEvent|React.KeyboardEvent<HTMLElement>) => void | Promise<void>} listener - listener to be executed on specified event
7+
* @returns {(evt: KeyboardEvent|React.KeyboardEvent<HTMLElement>) => void}
8+
*/
9+
export const hotKeyHandler = (hotKeys: `${string}` | `${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${string}` | `${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${'alt' | 'ctrl' | 'meta' | 'shift' | 'ctrlCommand'}+${string}`, listener: (evt: KeyboardEvent | KeyEvt<HTMLElement>) => void | Promise<void>): (evt: KeyboardEvent | KeyEvt<HTMLElement>) => void => {
10+
return (evt: KeyboardEvent | KeyEvt<HTMLElement>) => {
11+
let keys = hotKeys.toLowerCase().split("+").map(el => el.trim());
12+
const modifiers = {
13+
alt: keys.includes('alt'),
14+
ctrl: keys.includes('ctrl'),
15+
meta: keys.includes('meta'),
16+
ctrlCommand: keys.includes('ctrlcommand'),
17+
shift: keys.includes('shift'),
18+
};
19+
keys = keys.filter(el => !['alt', 'ctrl', 'meta', 'shift', 'ctrlCommand'].includes(el));
20+
for (let i = 0, size = keys.length; i < size; i++) {
21+
if (keys[i] === "" && (i + 1) < size && keys[i + 1] === "") {
22+
keys[i + 1] = "+";
23+
}
24+
}
25+
keys = keys.filter(el => el !== "")
26+
const { altKey, ctrlKey, metaKey, shiftKey, key: pressedKey } = evt;
27+
28+
if (modifiers.ctrlCommand) {
29+
if (!ctrlKey && !metaKey) {
30+
return;
31+
}
32+
} else {
33+
if (ctrlKey !== modifiers.ctrl) {
34+
return;
35+
}
36+
if (altKey !== modifiers.alt) {
37+
return;
38+
}
39+
if (metaKey !== modifiers.meta) {
40+
return;
41+
}
42+
if (shiftKey !== modifiers.shift) {
43+
return;
44+
}
45+
}
46+
if (!(keys.includes(pressedKey.toLowerCase()) || keys.includes(evt.code.replace("Key", "")))) {
47+
return;
48+
}
49+
listener && listener(evt);
50+
}
51+
}

packages/react-tools/src/utils/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export { isTouchEvent } from "./isTouchEvent";
55
export { isClient } from './isClient';
66
export { PublishSubscribePattern } from './PublishSubscribePattern';
77
export { EventsPattern } from './EventsPattern';
8-
export { isAsync } from './isAsync';
8+
export { isAsync } from './isAsync';
9+
export { hotKeyHandler } from './hotKeyHandler';

0 commit comments

Comments
 (0)