react-hotkeys-hook 4.0.0-1 → 4.0.0-4
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/README.md +113 -40
- package/dist/HotkeysProvider.d.ts +1 -1
- package/dist/index.js +28 -1
- package/dist/index.js.map +3 -3
- package/dist/useDeepEqualMemo.d.ts +1 -0
- package/package.json +5 -2
- package/src/HotkeysProvider.tsx +2 -2
- package/src/parseHotkeys.ts +2 -1
- package/src/types.ts +1 -1
- package/src/useDeepEqualMemo.ts +12 -0
- package/src/useHotkeys.ts +34 -14
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function useDeepEqualMemo<T>(value: T): T | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-hotkeys-hook",
|
|
3
|
-
"version": "4.0.0-
|
|
3
|
+
"version": "4.0.0-4",
|
|
4
4
|
"repository": "https://JohannesKlauss@github.com/JohannesKlauss/react-keymap-hook.git",
|
|
5
5
|
"homepage": "https://johannesklauss.github.io/react-hotkeys-hook/",
|
|
6
6
|
"author": "Johannes Klauss",
|
|
@@ -68,8 +68,11 @@
|
|
|
68
68
|
"tslib": "2.4.0",
|
|
69
69
|
"typescript": "4.7.4"
|
|
70
70
|
},
|
|
71
|
+
"dependencies": {
|
|
72
|
+
"lodash": "4.17.11"
|
|
73
|
+
},
|
|
71
74
|
"peerDependencies": {
|
|
72
|
-
"lodash": ">=4.17.
|
|
75
|
+
"lodash": ">=4.17.0",
|
|
73
76
|
"react": ">=16.8.1",
|
|
74
77
|
"react-dom": ">=16.8.1"
|
|
75
78
|
}
|
package/src/HotkeysProvider.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { Hotkey } from './types'
|
|
|
2
2
|
import { createContext, ReactNode, useMemo, useState, useContext } from 'react'
|
|
3
3
|
import BoundHotkeysProxyProviderProvider from './BoundHotkeysProxyProvider'
|
|
4
4
|
|
|
5
|
-
type HotkeysContextType = {
|
|
5
|
+
export type HotkeysContextType = {
|
|
6
6
|
hotkeys: ReadonlyArray<Hotkey>
|
|
7
7
|
activeScopes: string[]
|
|
8
8
|
toggleScope: (scope: string) => void
|
|
@@ -13,7 +13,7 @@ type HotkeysContextType = {
|
|
|
13
13
|
// The context is only needed for special features like global scoping, so we use a graceful default fallback
|
|
14
14
|
const HotkeysContext = createContext<HotkeysContextType>({
|
|
15
15
|
hotkeys: [],
|
|
16
|
-
activeScopes: [], // This array has to be empty to check if the provider is set or not
|
|
16
|
+
activeScopes: [], // This array has to be empty instead of containing '*' as default, to check if the provider is set or not
|
|
17
17
|
toggleScope: () => {},
|
|
18
18
|
activateScope: () => {},
|
|
19
19
|
deactivateScope: () => {},
|
package/src/parseHotkeys.ts
CHANGED
|
@@ -14,7 +14,8 @@ export function parseHotkey(hotkey: string, combinationKey: string = '+'): Hotke
|
|
|
14
14
|
const keys = hotkey
|
|
15
15
|
.toLocaleLowerCase()
|
|
16
16
|
.split(combinationKey)
|
|
17
|
-
.map(
|
|
17
|
+
.map(k => k.trim())
|
|
18
|
+
.map(k => k === 'esc' ? 'escape' : k)
|
|
18
19
|
|
|
19
20
|
const modifiers: KeyboardModifiers = {
|
|
20
21
|
alt: keys.includes('alt'),
|
package/src/types.ts
CHANGED
|
@@ -34,7 +34,7 @@ export type Options = {
|
|
|
34
34
|
scopes?: Scopes // Scope
|
|
35
35
|
keyup?: boolean // Trigger on keyup event? (Default: undefined)
|
|
36
36
|
keydown?: boolean // Trigger on keydown event? (Default: true)
|
|
37
|
-
preventDefault?: Trigger // Prevent default browser behavior? (Default:
|
|
37
|
+
preventDefault?: Trigger // Prevent default browser behavior? (Default: false)
|
|
38
38
|
description?: string // Use this option to describe what the hotkey does. (Default: undefined)
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useRef } from 'react'
|
|
2
|
+
import { isEqual } from 'lodash'
|
|
3
|
+
|
|
4
|
+
export default function useDeepEqualMemo<T>(value: T) {
|
|
5
|
+
const ref = useRef<T | undefined>(undefined)
|
|
6
|
+
|
|
7
|
+
if (!isEqual(ref.current, value)) {
|
|
8
|
+
ref.current = value
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return ref.current
|
|
12
|
+
}
|
package/src/useHotkeys.ts
CHANGED
|
@@ -11,6 +11,13 @@ import {
|
|
|
11
11
|
} from './validators'
|
|
12
12
|
import { useHotkeysContext } from './HotkeysProvider'
|
|
13
13
|
import { useBoundHotkeysProxy } from './BoundHotkeysProxyProvider'
|
|
14
|
+
import useDeepEqualMemo from './useDeepEqualMemo'
|
|
15
|
+
|
|
16
|
+
const stopPropagation = (e: KeyboardEvent): void => {
|
|
17
|
+
e.stopPropagation()
|
|
18
|
+
e.preventDefault()
|
|
19
|
+
e.stopImmediatePropagation()
|
|
20
|
+
}
|
|
14
21
|
|
|
15
22
|
export default function useHotkeys<T extends HTMLElement>(
|
|
16
23
|
keys: Keys,
|
|
@@ -25,35 +32,43 @@ export default function useHotkeys<T extends HTMLElement>(
|
|
|
25
32
|
const _deps = options instanceof Array ? options : dependencies instanceof Array ? dependencies : []
|
|
26
33
|
|
|
27
34
|
const cb = useCallback(callback, [..._deps])
|
|
28
|
-
const
|
|
35
|
+
const memoisedOptions = useDeepEqualMemo(_options)
|
|
29
36
|
|
|
37
|
+
const { activeScopes } = useHotkeysContext()
|
|
30
38
|
const proxy = useBoundHotkeysProxy()
|
|
31
39
|
|
|
32
40
|
useLayoutEffect(() => {
|
|
33
|
-
if (
|
|
41
|
+
if (memoisedOptions?.enabled === false || !isScopeActive(activeScopes, memoisedOptions?.scopes)) {
|
|
34
42
|
return
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
const listener = (e: KeyboardEvent) => {
|
|
38
|
-
if (isKeyboardEventTriggeredByInput(e) && !isHotkeyEnabledOnTag(e,
|
|
46
|
+
if (isKeyboardEventTriggeredByInput(e) && !isHotkeyEnabledOnTag(e, memoisedOptions?.enableOnFormTags)) {
|
|
39
47
|
return
|
|
40
48
|
}
|
|
41
49
|
|
|
50
|
+
// 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
|
|
51
|
+
// REF IS THE ACTIVE ELEMENT. THIS IS A PROBLEM SINCE FOCUSED SUB COMPONENTS WONT TRIGGER THE HOTKEY.
|
|
52
|
+
|
|
42
53
|
if (ref.current !== null && document.activeElement !== ref.current && !ref.current.contains(document.activeElement)) {
|
|
54
|
+
stopPropagation(e)
|
|
55
|
+
|
|
43
56
|
return
|
|
44
57
|
}
|
|
45
58
|
|
|
46
|
-
if (((e.target as HTMLElement)?.isContentEditable && !
|
|
59
|
+
if (((e.target as HTMLElement)?.isContentEditable && !memoisedOptions?.enableOnContentEditable)) {
|
|
47
60
|
return
|
|
48
61
|
}
|
|
49
62
|
|
|
50
|
-
parseKeysHookInput(keys,
|
|
51
|
-
const hotkey = parseHotkey(key,
|
|
63
|
+
parseKeysHookInput(keys, memoisedOptions?.splitKey).forEach((key) => {
|
|
64
|
+
const hotkey = parseHotkey(key, memoisedOptions?.combinationKey)
|
|
52
65
|
|
|
53
66
|
if (isHotkeyMatchingKeyboardEvent(e, hotkey, pressedDownKeys) || hotkey.keys?.includes('*')) {
|
|
54
|
-
maybePreventDefault(e, hotkey,
|
|
67
|
+
maybePreventDefault(e, hotkey, memoisedOptions?.preventDefault)
|
|
68
|
+
|
|
69
|
+
if (!isHotkeyEnabled(e, hotkey, memoisedOptions?.enabled)) {
|
|
70
|
+
stopPropagation(e)
|
|
55
71
|
|
|
56
|
-
if (!isHotkeyEnabled(e, hotkey, _options?.enabled)) {
|
|
57
72
|
return
|
|
58
73
|
}
|
|
59
74
|
|
|
@@ -65,15 +80,20 @@ export default function useHotkeys<T extends HTMLElement>(
|
|
|
65
80
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
66
81
|
pressedDownKeys.add(event.key.toLowerCase())
|
|
67
82
|
|
|
68
|
-
if ((
|
|
83
|
+
if ((memoisedOptions?.keydown === undefined && memoisedOptions?.keyup !== true) || memoisedOptions?.keydown) {
|
|
69
84
|
listener(event)
|
|
70
85
|
}
|
|
71
86
|
}
|
|
72
87
|
|
|
73
88
|
const handleKeyUp = (event: KeyboardEvent) => {
|
|
74
|
-
|
|
89
|
+
if (event.key.toLowerCase() !== 'meta') {
|
|
90
|
+
pressedDownKeys.delete(event.key.toLowerCase())
|
|
91
|
+
} else {
|
|
92
|
+
// On macOS pressing down the meta key prevents triggering the keyup event for any other key https://stackoverflow.com/a/57153300/735226.
|
|
93
|
+
pressedDownKeys.clear()
|
|
94
|
+
}
|
|
75
95
|
|
|
76
|
-
if (
|
|
96
|
+
if (memoisedOptions?.keyup) {
|
|
77
97
|
listener(event)
|
|
78
98
|
}
|
|
79
99
|
}
|
|
@@ -84,7 +104,7 @@ export default function useHotkeys<T extends HTMLElement>(
|
|
|
84
104
|
(ref.current || document).addEventListener('keydown', handleKeyDown)
|
|
85
105
|
|
|
86
106
|
if (proxy) {
|
|
87
|
-
parseKeysHookInput(keys,
|
|
107
|
+
parseKeysHookInput(keys, memoisedOptions?.splitKey).forEach((key) => proxy.addHotkey(parseHotkey(key, memoisedOptions?.combinationKey)))
|
|
88
108
|
}
|
|
89
109
|
|
|
90
110
|
return () => {
|
|
@@ -94,10 +114,10 @@ export default function useHotkeys<T extends HTMLElement>(
|
|
|
94
114
|
(ref.current || document).removeEventListener('keydown', handleKeyDown)
|
|
95
115
|
|
|
96
116
|
if (proxy) {
|
|
97
|
-
parseKeysHookInput(keys,
|
|
117
|
+
parseKeysHookInput(keys, memoisedOptions?.splitKey).forEach((key) => proxy.removeHotkey(parseHotkey(key, memoisedOptions?.combinationKey)))
|
|
98
118
|
}
|
|
99
119
|
}
|
|
100
|
-
}, [keys, cb,
|
|
120
|
+
}, [keys, cb, memoisedOptions, activeScopes])
|
|
101
121
|
|
|
102
122
|
return ref
|
|
103
123
|
}
|