@@ -2,22 +2,27 @@ import { mapKeys } from 'lodash';
2
2
import PropTypes from 'prop-types' ;
3
3
import { useCallback , useEffect , useRef } from 'react' ;
4
4
5
+ /**
6
+ * Function to call upon keydown
7
+ */
8
+ export type KeydownHandler = ( e : KeyboardEvent ) => void ;
9
+ /**
10
+ * An object mapping from keys like 'ctrl-s' or 'ctrl-shift-1' to handlers.
11
+ */
12
+ export type KeydownHandlerMap = Record < string , KeydownHandler > ;
13
+
5
14
/**
6
15
* Attaches keydown handlers to the global document.
7
- *
8
16
* Handles Mac/PC switching of Ctrl to Cmd.
9
17
*
10
- * @param {Record<string, (e: KeyboardEvent) => void> } keyHandlers - an object
11
- * which maps from the key to its event handler. The object keys are a combination
12
- * of the key and prefixes `ctrl-` `shift-` (ie. 'ctrl-f', 'ctrl-shift-f')
13
- * and the values are the function to call when that specific key is pressed.
18
+ * @param keyHandlers - an object which maps from the key to its event handler. The object keys are a combination of the key and prefixes `ctrl-` `shift-` (ie. 'ctrl-f', 'ctrl-shift-f') and the values are the function to call when that specific key is pressed.
14
19
*/
15
- export default function useKeyDownHandlers ( keyHandlers ) {
20
+ export default function useKeyDownHandlers ( keyHandlers : KeydownHandlerMap ) {
16
21
/**
17
22
* Instead of memoizing the handlers, use a ref and call the current
18
23
* handler at the time of the event.
19
24
*/
20
- const handlers = useRef ( keyHandlers ) ;
25
+ const handlers = useRef < KeydownHandlerMap > ( keyHandlers ) ;
21
26
22
27
useEffect ( ( ) => {
23
28
handlers . current = mapKeys ( keyHandlers , ( value , key ) => key ?. toLowerCase ( ) ) ;
@@ -26,47 +31,37 @@ export default function useKeyDownHandlers(keyHandlers) {
26
31
/**
27
32
* Will call all matching handlers, starting with the most specific: 'ctrl-shift-f' => 'ctrl-f' => 'f'.
28
33
* Can use e.stopPropagation() to prevent subsequent handlers.
29
- * @type {(function(KeyboardEvent): void) }
30
34
*/
31
- const handleEvent = useCallback ( ( e ) => {
32
- if ( ! e . key ) return ;
33
- const isMac = navigator . userAgent . toLowerCase ( ) . indexOf ( 'mac' ) !== - 1 ;
34
- const isCtrl = isMac ? e . metaKey : e . ctrlKey ;
35
- if ( e . shiftKey && isCtrl ) {
36
- handlers . current [
37
- `ctrl-shift-${
38
- / ^ \d + $ / . test ( e . code . at ( - 1 ) ) ? e . code . at ( - 1 ) : e . key . toLowerCase ( )
39
- } `
40
- ] ?.( e ) ;
41
- } else if ( isCtrl && e . altKey && e . code === 'KeyN' ) {
42
- // specifically for creating a new file
43
- handlers . current [ `ctrl-alt-n` ] ?.( e ) ;
44
- } else if ( isCtrl ) {
45
- handlers . current [ `ctrl-${ e . key . toLowerCase ( ) } ` ] ?.( e ) ;
46
- }
47
- handlers . current [ e . key ?. toLowerCase ( ) ] ?.( e ) ;
48
- } , [ ] ) ;
35
+ const handleEvent : ( e : KeyboardEvent ) => void = useCallback (
36
+ ( e : KeyboardEvent ) => {
37
+ if ( ! e . key ) return ;
38
+
39
+ const isMac = navigator . userAgent . toLowerCase ( ) . indexOf ( 'mac' ) !== - 1 ;
40
+ const isCtrl = isMac ? e . metaKey : e . ctrlKey ;
41
+
42
+ if ( e . shiftKey && isCtrl ) {
43
+ handlers . current [
44
+ `ctrl-shift-${
45
+ / ^ \d + $ / . test ( e . code . at ( - 1 ) || '' )
46
+ ? e . code . at ( - 1 )
47
+ : e . key . toLowerCase ( )
48
+ } `
49
+ ] ?.( e ) ;
50
+ } else if ( isCtrl && e . altKey && e . code === 'KeyN' ) {
51
+ // specifically for creating a new file
52
+ handlers . current [ `ctrl-alt-n` ] ?.( e ) ;
53
+ } else if ( isCtrl ) {
54
+ handlers . current [ `ctrl-${ e . key . toLowerCase ( ) } ` ] ?.( e ) ;
55
+ }
56
+
57
+ handlers . current [ e . key ?. toLowerCase ( ) ] ?.( e ) ;
58
+ } ,
59
+ [ ]
60
+ ) ;
49
61
50
62
useEffect ( ( ) => {
51
63
document . addEventListener ( 'keydown' , handleEvent ) ;
52
64
53
65
return ( ) => document . removeEventListener ( 'keydown' , handleEvent ) ;
54
66
} , [ handleEvent ] ) ;
55
67
}
56
-
57
- /**
58
- * Component version can be used in class components where hooks can't be used.
59
- *
60
- * @param {Record<string, (e: KeyboardEvent) => void> } handlers
61
- */
62
- export const DocumentKeyDown = ( { handlers } ) => {
63
- useKeyDownHandlers ( handlers ) ;
64
- return null ;
65
- } ;
66
- DocumentKeyDown . propTypes = {
67
- handlers : PropTypes . objectOf ( PropTypes . func )
68
- } ;
69
-
70
- DocumentKeyDown . defaultProps = {
71
- handlers : { }
72
- } ;
0 commit comments