react-hotkeys-hook 4.3.4 → 4.3.6-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/react-hotkeys-hook.cjs.development.js +50 -41
- package/dist/react-hotkeys-hook.cjs.development.js.map +1 -1
- package/dist/react-hotkeys-hook.cjs.production.min.js +1 -1
- package/dist/react-hotkeys-hook.cjs.production.min.js.map +1 -1
- package/dist/react-hotkeys-hook.esm.js +50 -41
- package/dist/react-hotkeys-hook.esm.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +46 -9
- package/src/BoundHotkeysProxyProvider.tsx +7 -3
- package/src/HotkeysProvider.tsx +13 -9
- package/src/deepEqual.ts +5 -6
- package/src/index.ts +1 -8
- package/src/isHotkeyPressed.ts +31 -32
- package/src/parseHotkeys.ts +13 -11
- package/src/types.ts +1 -1
- package/src/useHotkeys.ts +38 -15
- package/src/useRecordHotkeys.ts +2 -2
- package/src/validators.ts +6 -4
package/src/parseHotkeys.ts
CHANGED
|
@@ -12,14 +12,16 @@ const mappedKeys: Record<string, string> = {
|
|
|
12
12
|
'`': 'backquote',
|
|
13
13
|
'#': 'backslash',
|
|
14
14
|
'+': 'bracketright',
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
ShiftLeft: 'shift',
|
|
16
|
+
ShiftRight: 'shift',
|
|
17
|
+
AltLeft: 'alt',
|
|
18
|
+
AltRight: 'alt',
|
|
19
|
+
MetaLeft: 'meta',
|
|
20
|
+
MetaRight: 'meta',
|
|
21
|
+
OSLeft: 'meta',
|
|
22
|
+
OSRight: 'meta',
|
|
23
|
+
ControlLeft: 'ctrl',
|
|
24
|
+
ControlRight: 'ctrl',
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export function mapKey(key: string): string {
|
|
@@ -36,7 +38,7 @@ export function isHotkeyModifier(key: string) {
|
|
|
36
38
|
return reservedModifierKeywords.includes(key)
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
export function parseKeysHookInput(keys: Keys, splitKey
|
|
41
|
+
export function parseKeysHookInput(keys: Keys, splitKey = ','): string[] {
|
|
40
42
|
if (typeof keys === 'string') {
|
|
41
43
|
return keys.split(splitKey)
|
|
42
44
|
}
|
|
@@ -44,11 +46,11 @@ export function parseKeysHookInput(keys: Keys, splitKey: string = ','): string[]
|
|
|
44
46
|
return keys
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
export function parseHotkey(hotkey: string, combinationKey
|
|
49
|
+
export function parseHotkey(hotkey: string, combinationKey = '+'): Hotkey {
|
|
48
50
|
const keys = hotkey
|
|
49
51
|
.toLocaleLowerCase()
|
|
50
52
|
.split(combinationKey)
|
|
51
|
-
.map(k => mapKey(k))
|
|
53
|
+
.map((k) => mapKey(k))
|
|
52
54
|
|
|
53
55
|
const modifiers: KeyboardModifiers = {
|
|
54
56
|
alt: keys.includes('alt'),
|
package/src/types.ts
CHANGED
package/src/useHotkeys.ts
CHANGED
|
@@ -26,15 +26,28 @@ export default function useHotkeys<T extends HTMLElement>(
|
|
|
26
26
|
keys: Keys,
|
|
27
27
|
callback: HotkeyCallback,
|
|
28
28
|
options?: OptionsOrDependencyArray,
|
|
29
|
-
dependencies?: OptionsOrDependencyArray
|
|
29
|
+
dependencies?: OptionsOrDependencyArray
|
|
30
30
|
) {
|
|
31
31
|
const ref = useRef<RefType<T>>(null)
|
|
32
32
|
const hasTriggeredRef = useRef(false)
|
|
33
33
|
|
|
34
|
-
const _options: Options | undefined = !(options instanceof Array)
|
|
35
|
-
|
|
34
|
+
const _options: Options | undefined = !(options instanceof Array)
|
|
35
|
+
? (options as Options)
|
|
36
|
+
: !(dependencies instanceof Array)
|
|
37
|
+
? (dependencies as Options)
|
|
38
|
+
: undefined
|
|
39
|
+
const _deps: DependencyList | undefined =
|
|
40
|
+
options instanceof Array ? options : dependencies instanceof Array ? dependencies : undefined
|
|
41
|
+
|
|
42
|
+
const memoisedCB = useCallback(callback, _deps ?? [])
|
|
43
|
+
const cbRef = useRef<HotkeyCallback>(memoisedCB)
|
|
44
|
+
|
|
45
|
+
if (_deps) {
|
|
46
|
+
cbRef.current = memoisedCB
|
|
47
|
+
} else {
|
|
48
|
+
cbRef.current = callback
|
|
49
|
+
}
|
|
36
50
|
|
|
37
|
-
const cb = useCallback(callback, [..._deps])
|
|
38
51
|
const memoisedOptions = useDeepEqualMemo(_options)
|
|
39
52
|
|
|
40
53
|
const { enabledScopes } = useHotkeysContext()
|
|
@@ -45,20 +58,24 @@ export default function useHotkeys<T extends HTMLElement>(
|
|
|
45
58
|
return
|
|
46
59
|
}
|
|
47
60
|
|
|
48
|
-
const listener = (e: KeyboardEvent, isKeyUp
|
|
61
|
+
const listener = (e: KeyboardEvent, isKeyUp = false) => {
|
|
49
62
|
if (isKeyboardEventTriggeredByInput(e) && !isHotkeyEnabledOnTag(e, memoisedOptions?.enableOnFormTags)) {
|
|
50
63
|
return
|
|
51
64
|
}
|
|
52
65
|
|
|
53
66
|
// TODO: SINCE THE EVENT IS NOW ATTACHED TO THE REF, THE ACTIVE ELEMENT CAN NEVER BE INSIDE THE REF. THE HOTKEY ONLY TRIGGERS IF THE
|
|
54
67
|
// REF IS THE ACTIVE ELEMENT. THIS IS A PROBLEM SINCE FOCUSED SUB COMPONENTS WON'T TRIGGER THE HOTKEY.
|
|
55
|
-
if (
|
|
68
|
+
if (
|
|
69
|
+
ref.current !== null &&
|
|
70
|
+
document.activeElement !== ref.current &&
|
|
71
|
+
!ref.current.contains(document.activeElement)
|
|
72
|
+
) {
|
|
56
73
|
stopPropagation(e)
|
|
57
74
|
|
|
58
75
|
return
|
|
59
76
|
}
|
|
60
77
|
|
|
61
|
-
if ((
|
|
78
|
+
if ((e.target as HTMLElement)?.isContentEditable && !memoisedOptions?.enableOnContentEditable) {
|
|
62
79
|
return
|
|
63
80
|
}
|
|
64
81
|
|
|
@@ -79,7 +96,7 @@ export default function useHotkeys<T extends HTMLElement>(
|
|
|
79
96
|
}
|
|
80
97
|
|
|
81
98
|
// Execute the user callback for that hotkey
|
|
82
|
-
|
|
99
|
+
cbRef.current(e, hotkey)
|
|
83
100
|
|
|
84
101
|
if (!isKeyUp) {
|
|
85
102
|
hasTriggeredRef.current = true
|
|
@@ -116,26 +133,32 @@ export default function useHotkeys<T extends HTMLElement>(
|
|
|
116
133
|
}
|
|
117
134
|
}
|
|
118
135
|
|
|
136
|
+
const domNode = ref.current || _options?.document || document
|
|
137
|
+
|
|
119
138
|
// @ts-ignore
|
|
120
|
-
|
|
139
|
+
domNode.addEventListener('keyup', handleKeyUp)
|
|
121
140
|
// @ts-ignore
|
|
122
|
-
|
|
141
|
+
domNode.addEventListener('keydown', handleKeyDown)
|
|
123
142
|
|
|
124
143
|
if (proxy) {
|
|
125
|
-
parseKeysHookInput(keys, memoisedOptions?.splitKey).forEach((key) =>
|
|
144
|
+
parseKeysHookInput(keys, memoisedOptions?.splitKey).forEach((key) =>
|
|
145
|
+
proxy.addHotkey(parseHotkey(key, memoisedOptions?.combinationKey))
|
|
146
|
+
)
|
|
126
147
|
}
|
|
127
148
|
|
|
128
149
|
return () => {
|
|
129
150
|
// @ts-ignore
|
|
130
|
-
|
|
151
|
+
domNode.removeEventListener('keyup', handleKeyUp)
|
|
131
152
|
// @ts-ignore
|
|
132
|
-
|
|
153
|
+
domNode.removeEventListener('keydown', handleKeyDown)
|
|
133
154
|
|
|
134
155
|
if (proxy) {
|
|
135
|
-
parseKeysHookInput(keys, memoisedOptions?.splitKey).forEach((key) =>
|
|
156
|
+
parseKeysHookInput(keys, memoisedOptions?.splitKey).forEach((key) =>
|
|
157
|
+
proxy.removeHotkey(parseHotkey(key, memoisedOptions?.combinationKey))
|
|
158
|
+
)
|
|
136
159
|
}
|
|
137
160
|
}
|
|
138
|
-
}, [keys,
|
|
161
|
+
}, [keys, memoisedOptions, enabledScopes])
|
|
139
162
|
|
|
140
163
|
return ref
|
|
141
164
|
}
|
package/src/useRecordHotkeys.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { mapKey } from './parseHotkeys'
|
|
|
3
3
|
|
|
4
4
|
export default function useRecordHotkeys() {
|
|
5
5
|
const [keys, setKeys] = useState(new Set<string>())
|
|
6
|
-
const [isRecording, setIsRecording] = useState(false)
|
|
6
|
+
const [isRecording, setIsRecording] = useState(false)
|
|
7
7
|
|
|
8
8
|
const handler = useCallback((event: KeyboardEvent) => {
|
|
9
9
|
if (event.key === undefined) {
|
|
@@ -14,7 +14,7 @@ export default function useRecordHotkeys() {
|
|
|
14
14
|
event.preventDefault()
|
|
15
15
|
event.stopPropagation()
|
|
16
16
|
|
|
17
|
-
setKeys(prev => {
|
|
17
|
+
setKeys((prev) => {
|
|
18
18
|
const newKeys = new Set(prev)
|
|
19
19
|
|
|
20
20
|
newKeys.add(mapKey(event.code))
|
package/src/validators.ts
CHANGED
|
@@ -24,7 +24,9 @@ export function isHotkeyEnabledOnTag({ target }: KeyboardEvent, enabledOnTags: F
|
|
|
24
24
|
const targetTagName = target && (target as HTMLElement).tagName
|
|
25
25
|
|
|
26
26
|
if (enabledOnTags instanceof Array) {
|
|
27
|
-
return Boolean(
|
|
27
|
+
return Boolean(
|
|
28
|
+
targetTagName && enabledOnTags && enabledOnTags.some((tag) => tag.toLowerCase() === targetTagName.toLowerCase())
|
|
29
|
+
)
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
return Boolean(targetTagName && enabledOnTags && enabledOnTags === true)
|
|
@@ -33,7 +35,7 @@ export function isHotkeyEnabledOnTag({ target }: KeyboardEvent, enabledOnTags: F
|
|
|
33
35
|
export function isScopeActive(activeScopes: string[], scopes?: Scopes): boolean {
|
|
34
36
|
if (activeScopes.length === 0 && scopes) {
|
|
35
37
|
console.warn(
|
|
36
|
-
'A hotkey has the "scopes" option set, however no active scopes were found. If you want to use the global scopes feature, you need to wrap your app in a <HotkeysProvider>'
|
|
38
|
+
'A hotkey has the "scopes" option set, however no active scopes were found. If you want to use the global scopes feature, you need to wrap your app in a <HotkeysProvider>'
|
|
37
39
|
)
|
|
38
40
|
|
|
39
41
|
return true
|
|
@@ -43,10 +45,10 @@ export function isScopeActive(activeScopes: string[], scopes?: Scopes): boolean
|
|
|
43
45
|
return true
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
return activeScopes.some(scope => scopes.includes(scope)) || activeScopes.includes('*')
|
|
48
|
+
return activeScopes.some((scope) => scopes.includes(scope)) || activeScopes.includes('*')
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
export const isHotkeyMatchingKeyboardEvent = (e: KeyboardEvent, hotkey: Hotkey, ignoreModifiers
|
|
51
|
+
export const isHotkeyMatchingKeyboardEvent = (e: KeyboardEvent, hotkey: Hotkey, ignoreModifiers = false): boolean => {
|
|
50
52
|
const { alt, meta, mod, shift, ctrl, keys } = hotkey
|
|
51
53
|
const { key: pressedKeyUppercase, code, ctrlKey, metaKey, shiftKey, altKey } = e
|
|
52
54
|
|