-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Copy pathparse-hotkey.ts
99 lines (82 loc) · 2.23 KB
/
parse-hotkey.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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
export type KeyboardModifiers = {
alt: boolean;
ctrl: boolean;
meta: boolean;
mod: boolean;
shift: boolean;
plus: boolean;
};
export type Hotkey = KeyboardModifiers & {
key?: string;
};
type CheckHotkeyMatch = (event: KeyboardEvent) => boolean;
export function parseHotkey(hotkey: string): Hotkey {
const keys = hotkey
.toLowerCase()
.split('+')
.map((part) => part.trim());
const modifiers: KeyboardModifiers = {
alt: keys.includes('alt'),
ctrl: keys.includes('ctrl'),
meta: keys.includes('meta'),
mod: keys.includes('mod'),
shift: keys.includes('shift'),
plus: keys.includes('[plus]'),
};
const reservedKeys = ['alt', 'ctrl', 'meta', 'shift', 'mod'];
const freeKey = keys.find((key) => !reservedKeys.includes(key));
return {
...modifiers,
key: freeKey === '[plus]' ? '+' : freeKey,
};
}
function isExactHotkey(hotkey: Hotkey, event: KeyboardEvent): boolean {
const { alt, ctrl, meta, mod, shift, key } = hotkey;
const { altKey, ctrlKey, metaKey, shiftKey, key: pressedKey } = event;
if (alt !== altKey) {
return false;
}
if (mod) {
if (!ctrlKey && !metaKey) {
return false;
}
} else {
if (ctrl !== ctrlKey) {
return false;
}
if (meta !== metaKey) {
return false;
}
}
if (shift !== shiftKey) {
return false;
}
if (
key &&
(pressedKey.toLowerCase() === key.toLowerCase() ||
event.code.replace('Key', '').toLowerCase() === key.toLowerCase())
) {
return true;
}
return false;
}
export function getHotkeyMatcher(hotkey: string): CheckHotkeyMatch {
return (event) => isExactHotkey(parseHotkey(hotkey), event);
}
export interface HotkeyItemOptions {
preventDefault?: boolean;
}
type HotkeyItem = [string, (event: any) => void, HotkeyItemOptions?];
export function getHotkeyHandler(hotkeys: HotkeyItem[]) {
return (event: React.KeyboardEvent<HTMLElement> | KeyboardEvent) => {
const _event = 'nativeEvent' in event ? event.nativeEvent : event;
hotkeys.forEach(([hotkey, handler, options = { preventDefault: true }]) => {
if (getHotkeyMatcher(hotkey)(_event)) {
if (options.preventDefault) {
event.preventDefault();
}
handler(_event);
}
});
};
}