react-hotkeys-hook 4.0.0-2 → 4.0.0-5

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.
@@ -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-2",
3
+ "version": "4.0.0-5",
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.21",
75
+ "lodash": ">=4.17.0",
73
76
  "react": ">=16.8.1",
74
77
  "react-dom": ">=16.8.1"
75
78
  }
@@ -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
package/src/index.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import useHotkeys from './useHotkeys'
2
2
  import type { Options } from './types'
3
3
  import { HotkeysProvider, useHotkeysContext } from './HotkeysProvider'
4
+ import { isHotkeyPressed } from './isHotkeyPressed'
4
5
 
5
6
  export {
6
7
  useHotkeys,
7
8
  useHotkeysContext,
9
+ isHotkeyPressed,
8
10
  HotkeysProvider,
9
11
  Options,
10
12
  }
@@ -14,7 +14,9 @@ export function parseHotkey(hotkey: string, combinationKey: string = '+'): Hotke
14
14
  const keys = hotkey
15
15
  .toLocaleLowerCase()
16
16
  .split(combinationKey)
17
- .map((k) => k.trim())
17
+ .map(k => k.trim())
18
+ .map(k => k === 'esc' ? 'escape' : k)
19
+ .map(k => k === 'return' ? 'enter' : k)
18
20
 
19
21
  const modifiers: KeyboardModifiers = {
20
22
  alt: keys.includes('alt'),
@@ -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,7 @@ import {
11
11
  } from './validators'
12
12
  import { useHotkeysContext } from './HotkeysProvider'
13
13
  import { useBoundHotkeysProxy } from './BoundHotkeysProxyProvider'
14
+ import useDeepEqualMemo from './useDeepEqualMemo'
14
15
 
15
16
  const stopPropagation = (e: KeyboardEvent): void => {
16
17
  e.stopPropagation()
@@ -31,37 +32,41 @@ export default function useHotkeys<T extends HTMLElement>(
31
32
  const _deps = options instanceof Array ? options : dependencies instanceof Array ? dependencies : []
32
33
 
33
34
  const cb = useCallback(callback, [..._deps])
34
- const ctx = useHotkeysContext()
35
+ const memoisedOptions = useDeepEqualMemo(_options)
35
36
 
37
+ const { activeScopes } = useHotkeysContext()
36
38
  const proxy = useBoundHotkeysProxy()
37
39
 
38
40
  useLayoutEffect(() => {
39
- if (_options?.enabled === false || !isScopeActive(ctx.activeScopes, _options?.scopes)) {
41
+ if (memoisedOptions?.enabled === false || !isScopeActive(activeScopes, memoisedOptions?.scopes)) {
40
42
  return
41
43
  }
42
44
 
43
45
  const listener = (e: KeyboardEvent) => {
44
- if (isKeyboardEventTriggeredByInput(e) && !isHotkeyEnabledOnTag(e, _options?.enableOnFormTags)) {
46
+ if (isKeyboardEventTriggeredByInput(e) && !isHotkeyEnabledOnTag(e, memoisedOptions?.enableOnFormTags)) {
45
47
  return
46
48
  }
47
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
+
48
53
  if (ref.current !== null && document.activeElement !== ref.current && !ref.current.contains(document.activeElement)) {
49
54
  stopPropagation(e)
50
55
 
51
56
  return
52
57
  }
53
58
 
54
- if (((e.target as HTMLElement)?.isContentEditable && !_options?.enableOnContentEditable)) {
59
+ if (((e.target as HTMLElement)?.isContentEditable && !memoisedOptions?.enableOnContentEditable)) {
55
60
  return
56
61
  }
57
62
 
58
- parseKeysHookInput(keys, _options?.splitKey).forEach((key) => {
59
- const hotkey = parseHotkey(key, _options?.combinationKey)
63
+ parseKeysHookInput(keys, memoisedOptions?.splitKey).forEach((key) => {
64
+ const hotkey = parseHotkey(key, memoisedOptions?.combinationKey)
60
65
 
61
66
  if (isHotkeyMatchingKeyboardEvent(e, hotkey, pressedDownKeys) || hotkey.keys?.includes('*')) {
62
- maybePreventDefault(e, hotkey, _options?.preventDefault)
67
+ maybePreventDefault(e, hotkey, memoisedOptions?.preventDefault)
63
68
 
64
- if (!isHotkeyEnabled(e, hotkey, _options?.enabled)) {
69
+ if (!isHotkeyEnabled(e, hotkey, memoisedOptions?.enabled)) {
65
70
  stopPropagation(e)
66
71
 
67
72
  return
@@ -75,15 +80,20 @@ export default function useHotkeys<T extends HTMLElement>(
75
80
  const handleKeyDown = (event: KeyboardEvent) => {
76
81
  pressedDownKeys.add(event.key.toLowerCase())
77
82
 
78
- if ((_options?.keydown === undefined && _options?.keyup !== true) || _options?.keydown) {
83
+ if ((memoisedOptions?.keydown === undefined && memoisedOptions?.keyup !== true) || memoisedOptions?.keydown) {
79
84
  listener(event)
80
85
  }
81
86
  }
82
87
 
83
88
  const handleKeyUp = (event: KeyboardEvent) => {
84
- pressedDownKeys.delete(event.key.toLowerCase())
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
+ }
85
95
 
86
- if (_options?.keyup) {
96
+ if (memoisedOptions?.keyup) {
87
97
  listener(event)
88
98
  }
89
99
  }
@@ -94,7 +104,7 @@ export default function useHotkeys<T extends HTMLElement>(
94
104
  (ref.current || document).addEventListener('keydown', handleKeyDown)
95
105
 
96
106
  if (proxy) {
97
- parseKeysHookInput(keys, _options?.splitKey).forEach((key) => proxy.addHotkey(parseHotkey(key, _options?.combinationKey)))
107
+ parseKeysHookInput(keys, memoisedOptions?.splitKey).forEach((key) => proxy.addHotkey(parseHotkey(key, memoisedOptions?.combinationKey)))
98
108
  }
99
109
 
100
110
  return () => {
@@ -104,10 +114,10 @@ export default function useHotkeys<T extends HTMLElement>(
104
114
  (ref.current || document).removeEventListener('keydown', handleKeyDown)
105
115
 
106
116
  if (proxy) {
107
- parseKeysHookInput(keys, _options?.splitKey).forEach((key) => proxy.removeHotkey(parseHotkey(key, _options?.combinationKey)))
117
+ parseKeysHookInput(keys, memoisedOptions?.splitKey).forEach((key) => proxy.removeHotkey(parseHotkey(key, memoisedOptions?.combinationKey)))
108
118
  }
109
119
  }
110
- }, [keys, cb, _options])
120
+ }, [keys, cb, memoisedOptions, activeScopes])
111
121
 
112
122
  return ref
113
123
  }