react-hotkeys-hook 3.4.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,97 @@
1
+ ## [2.2.1] - 23-Jul-2020
2
+ * Remove deprecated findDOMNode
3
+ * Added useIsHotkeyPressed hook.
4
+
5
+ ## [2.2.0] - 23-Jul-2020
6
+ * Return ref to scope hotkey
7
+ * Fixed test for scopable ref feature
8
+
9
+ ## [2.1.4] - 30-Jun-2020
10
+ * added updated readme to docz
11
+ * Update README to reflect lack of filter scoping
12
+
13
+ ## [2.1.3] - 16-Apr-2020
14
+ * Code cleanup
15
+ * Overloaded function type, so that options does not have to be set when deps is used only.
16
+
17
+ ## [2.1.2] - 13-Apr-2020
18
+ * Added tests
19
+
20
+ ## [2.1.1] - 05-Apr-2020
21
+ * Fixed bug in 2.1.0 that every filter returns false that has no available tags given.
22
+
23
+ ## [2.1.0] - 04-Apr-2020
24
+ * Added filter implementation
25
+
26
+ ## [2.0.1] - 04-Apr-2020
27
+ * Update readme
28
+ * Fixed Option Type
29
+
30
+ ## [2.0.0] - 04-Apr-2020
31
+ * BC: Added more options Swapped deps and options param. Updated docz.
32
+
33
+ ## [1.6.1] - 27-Mar-2020
34
+ * Directly use KeyHandler type from hotkeys-js
35
+
36
+ ## [1.6.0] - 09-Mar-2020
37
+ * Add options parameter to useHotkeys hook
38
+
39
+ ## [1.5.4] - 09-Dec-2019
40
+ * Updated docz.
41
+
42
+ ## [1.5.3] - 09-Sep-2019
43
+ * make sure only memoisedCallback is unbound
44
+
45
+ ## [1.5.2] - 24-Aug-2019
46
+ * Update readme.
47
+ * Bump hotkeys version to 3.7.1
48
+
49
+ ## [1.5.1] - 21-Jul-2019
50
+ * Update readme.
51
+
52
+ ## [1.5.0] - 21-Jul-2019
53
+ * Callback gets memoised inside hook by default
54
+ * Add memo deps array as third argument to hook
55
+
56
+ ## [1.4.0] - 03-Jun-2019
57
+ * Add callback to useEffect deps to allow update of hotkeys when callback changes
58
+ * This also fixes the stale state bug
59
+ * Bump hotkeys version to 3.6.11
60
+
61
+ ## [1.3.4] - 11-May-2019
62
+ * Bump hotkeys version to 3.6.8
63
+
64
+ ## [1.3.3] - 02-May-2019
65
+ * Removed console.logs
66
+ * tightened source
67
+ * Updated docz
68
+
69
+ ## [1.3.0] - 06-Apr-2019
70
+ * Fixed bind and unbind on every render
71
+ * Updated docz to make it work with the new deps array
72
+
73
+ ## [1.2.0] - 30-Mar-2019
74
+ * Updated hotkeys
75
+ * Support for hotkeys 3.6
76
+ * Fixed typos in docz
77
+
78
+ ## [1.1.1] - 20-Feb-2019
79
+ * Updated examples in readme.md
80
+
81
+ ## [1.1.0] - 20-Feb-2019
82
+ * BC: Renamed `useHotKeys` to `useHotkeys` to keep it identical with the hotkeys package
83
+ * Switched to docz for documentation
84
+ * Switched to pika for packaging and publishing
85
+
86
+ ## [1.0.3] - 13-Feb-2019
87
+ * Bumped up hotkeys-js version to 3.4.4
88
+
89
+ ## [1.0.2] - 07-Feb-2019
90
+ * Bumped up hotkeys-js version
91
+
92
+ ## [1.0.1] - 07-Feb-2019
93
+ * Bumped peerDependencies for react and react-dom
94
+ * Cleaned up repository
95
+
96
+ ## [0.1.2] - 15-Jan-2019
97
+ * Initial release
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Johannes Klauss
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # react-hotkeys-hook
2
+ React hook for using keyboard shortcuts in components.
3
+ This is a hook version for the [hotkeys] package.
4
+
5
+ ## [Join the discussion for version 4!](https://github.com/JohannesKlauss/react-hotkeys-hook/issues/574)
6
+
7
+ If you use this package please share your thoughts on how we can improve this hook with version 4.
8
+ Please engage at the corresponding [Github issue](https://github.com/JohannesKlauss/react-hotkeys-hook/issues/574).
9
+
10
+ ## [Documentation & Live Examples](https://react-hotkeys-hook.vercel.app/)
11
+
12
+ ## [Support](https://github.com/JohannesKlauss/react-hotkeys-hook/discussions)
13
+
14
+ ### Installation
15
+
16
+ ```shell
17
+ npm install react-hotkeys-hook
18
+ ```
19
+
20
+ or
21
+
22
+ ```shell
23
+ yarn add react-hotkeys-hook
24
+ ```
25
+
26
+ Make sure that you have at least version 16.8 of `react` and `react-dom` installed, or otherwise hooks won't work for you.
27
+
28
+ ### Usage
29
+ ```js
30
+ export const ExampleComponent = () => {
31
+ const [count, setCount] = useState(0);
32
+ useHotkeys('ctrl+k', () => setCount(prevCount => prevCount + 1));
33
+
34
+ return (
35
+ <p>
36
+ Pressed {count} times.
37
+ </p>
38
+ );
39
+ };
40
+ ```
41
+
42
+ The hook takes care of all the binding and unbinding for you.
43
+ As soon as the component mounts into the DOM, the key stroke will be
44
+ listened to. When the component unmounts it will stop listening.
45
+
46
+ ### Call Signature
47
+
48
+ ```typescript
49
+ useHotkeys(keys: string, callback: (event: KeyboardEvent, handler: HotkeysEvent) => void, options: Options = {}, deps: any[] = [])
50
+ ```
51
+
52
+ ### Parameters
53
+ - `keys: string`: Here you can set the key strokes you want the hook to listen to. You can use single or multiple keys,
54
+ modifier combination, etc. See [this](https://github.com/jaywcjlove/hotkeys/#defining-shortcuts)
55
+ section on the hotkeys documentation for more info.
56
+ - `callback: (event: KeyboardEvent, handler: HotkeysEvent) => void`: Gets executed when the defined keystroke
57
+ gets hit by the user. **Important:** Since version 1.5.0 this callback gets memoised inside the hook. So you don't have
58
+ to do this anymore by yourself.
59
+ - `options: Options = {}`
60
+ - `filter: (event: KeyboardEvent): boolean` is used to filter if a callback gets triggered depending on the keyboard event.
61
+ **Breaking Change in `3.0.0`!** Prior to version `3.0.0` the filter settings was one global setting that applied to every
62
+ hook. Since `3.0.0` this behavior changed. The `filter` option is now locally scoped to each call of `useHotkeys`.
63
+ - `filterPreventDefault: boolean` is used to prevent/allow the default browser behavior for the keystroke when the filter return false (default value: `true`)
64
+ - `enableOnTags: string[]` is used to enable listening to hotkeys in form fields. Available values are `INPUT`, `TEXTAREA` and `SELECT`.
65
+ - `splitKey: string` is used to change the splitting character inside the keys argument. Default is `+`, but if you want
66
+ to listen to the `+` character, you can set `splitKey` to i.e. `-` and listen for `ctrl-+`
67
+ - `keyup: boolean` Determine if you want to listen on the keyup event
68
+ - `keydown: boolean` Determine if want to listen on the keydown event
69
+ - `enabled: boolean` is used to prevent installation of the hotkey when set to false (default value: `true`)
70
+ - `deps: any[] = []`: The dependency array that gets appended to the memoisation of the callback. Here you define the inner
71
+ dependencies of your callback. If for example your callback actions depend on a referentially unstable value or a value
72
+ that will change over time, you should add this value to your deps array. Since most of the time your callback won't
73
+ depend on any unstable callbacks or changing values over time you can leave this value alone since it will be set to an
74
+ empty array by default. See the [Memoisation](#memoisation) section to
75
+ learn more and see an example where you have to set this array.
76
+
77
+ ### `useIsHotkeyPressed` hook
78
+
79
+ The `useIsHotkeyPressed` hook just returns the `hotkeys.isPressed` function and works exactly the same.
80
+
81
+ ```ts
82
+ const isPressed = useIsHotkeyPressed();
83
+
84
+ isPressed('return'); // Returns true if Return key is pressed down.
85
+ ```
86
+
87
+ ### Found an issue or have a feature request?
88
+
89
+ Open up an issue or pull request and participate.
90
+
91
+ ### Local Development
92
+
93
+ Checkout this repo, run `yarn` or `npm i` and then run the `docz:dev` script.
94
+ You can use the `docs/useHotkeys.mdx` to test the behavior of the hook. It directly imports the
95
+ `src/index.ts` file and transpiles it automatically. So you don't have to worry about. For more info
96
+ on .mdx files, check out the docz documentation: https://www.docz.site/docs/writing-mdx
97
+
98
+ ### Authors
99
+
100
+ * Johannes Klauss
101
+
102
+ ### Contributors
103
+
104
+ * [bernatmv](https://github.com/bernatmv)
105
+ * [wheeler](https://github.com/wheeler)
106
+ * [louisrli](https://github.com/louisrli)
107
+ * [jameschao](https://github.com/jameschao)
108
+ * [hmafzal](https://github.com/hmafzal)
109
+ * [godspeedelbow](https://github.com/godspeedelbow)
110
+ * [JoshuaKGoldberg](https://github.com/JoshuaKGoldberg)
111
+ * [ggascoigne](https://github.com/ggascoigne)
112
+
113
+ ---
114
+
115
+ MIT License.
116
+
117
+ ---
118
+
119
+ [hotkeys]: https://github.com/jaywcjlove/hotkeys
@@ -0,0 +1,7 @@
1
+ import { useIsHotkeyPressed } from './useIsHotkeyPressed';
2
+ import { useHotkeys, Options } from './useHotkeys';
3
+ declare const isHotkeyPressed: {
4
+ (keyCode: number): boolean;
5
+ (keyCode: string): boolean;
6
+ };
7
+ export { useHotkeys, useIsHotkeyPressed, isHotkeyPressed, Options };
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+
2
+ 'use strict'
3
+
4
+ if (process.env.NODE_ENV === 'production') {
5
+ module.exports = require('./react-hotkeys-hook.cjs.production.min.js')
6
+ } else {
7
+ module.exports = require('./react-hotkeys-hook.cjs.development.js')
8
+ }
@@ -0,0 +1,92 @@
1
+ 'use strict';
2
+
3
+ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
4
+
5
+ var hotkeys = _interopDefault(require('hotkeys-js'));
6
+ var react = require('react');
7
+
8
+ /**
9
+ * @deprecated Use isHotkeyPressed instead. Will be removed version 4.
10
+ */
11
+
12
+ function useIsHotkeyPressed() {
13
+ return hotkeys.isPressed;
14
+ }
15
+
16
+ hotkeys.filter = function () {
17
+ return true;
18
+ };
19
+
20
+ var tagFilter = function tagFilter(_ref, enableOnTags) {
21
+ var target = _ref.target;
22
+ var targetTagName = target && target.tagName;
23
+ return Boolean(targetTagName && enableOnTags && enableOnTags.includes(targetTagName));
24
+ };
25
+
26
+ var isKeyboardEventTriggeredByInput = function isKeyboardEventTriggeredByInput(ev) {
27
+ return tagFilter(ev, ['INPUT', 'TEXTAREA', 'SELECT']);
28
+ };
29
+
30
+ function useHotkeys(keys, callback, options, deps) {
31
+ if (options instanceof Array) {
32
+ deps = options;
33
+ options = undefined;
34
+ }
35
+
36
+ var _ref2 = options || {},
37
+ enableOnTags = _ref2.enableOnTags,
38
+ filter = _ref2.filter,
39
+ keyup = _ref2.keyup,
40
+ keydown = _ref2.keydown,
41
+ _ref2$filterPreventDe = _ref2.filterPreventDefault,
42
+ filterPreventDefault = _ref2$filterPreventDe === void 0 ? true : _ref2$filterPreventDe,
43
+ _ref2$enabled = _ref2.enabled,
44
+ enabled = _ref2$enabled === void 0 ? true : _ref2$enabled,
45
+ _ref2$enableOnContent = _ref2.enableOnContentEditable,
46
+ enableOnContentEditable = _ref2$enableOnContent === void 0 ? false : _ref2$enableOnContent;
47
+
48
+ var ref = react.useRef(null); // The return value of this callback determines if the browsers default behavior is prevented.
49
+
50
+ var memoisedCallback = react.useCallback(function (keyboardEvent, hotkeysEvent) {
51
+ var _keyboardEvent$target;
52
+
53
+ if (filter && !filter(keyboardEvent)) {
54
+ return !filterPreventDefault;
55
+ } // Check whether the hotkeys was triggered inside an input and that input is enabled or if it was triggered by a content editable tag and it is enabled.
56
+
57
+
58
+ if (isKeyboardEventTriggeredByInput(keyboardEvent) && !tagFilter(keyboardEvent, enableOnTags) || (_keyboardEvent$target = keyboardEvent.target) != null && _keyboardEvent$target.isContentEditable && !enableOnContentEditable) {
59
+ return true;
60
+ }
61
+
62
+ if (ref.current === null || document.activeElement === ref.current) {
63
+ callback(keyboardEvent, hotkeysEvent);
64
+ return true;
65
+ }
66
+
67
+ return false;
68
+ }, deps ? [ref, enableOnTags, filter].concat(deps) : [ref, enableOnTags, filter]);
69
+ react.useEffect(function () {
70
+ if (!enabled) {
71
+ return;
72
+ } // In this case keydown is likely undefined, so we set it to false, since hotkeys needs the `keydown` key to have a value.
73
+
74
+
75
+ if (keyup && keydown !== true) {
76
+ options.keydown = false;
77
+ }
78
+
79
+ hotkeys(keys, options || {}, memoisedCallback);
80
+ return function () {
81
+ return hotkeys.unbind(keys, memoisedCallback);
82
+ };
83
+ }, [memoisedCallback, keys, enabled]);
84
+ return ref;
85
+ }
86
+
87
+ var isHotkeyPressed = hotkeys.isPressed;
88
+
89
+ exports.isHotkeyPressed = isHotkeyPressed;
90
+ exports.useHotkeys = useHotkeys;
91
+ exports.useIsHotkeyPressed = useIsHotkeyPressed;
92
+ //# sourceMappingURL=react-hotkeys-hook.cjs.development.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-hotkeys-hook.cjs.development.js","sources":["../src/useIsHotkeyPressed.ts","../src/useHotkeys.ts","../src/index.ts"],"sourcesContent":["import hotkeys from 'hotkeys-js';\n\n/**\n * @deprecated Use isHotkeyPressed instead. Will be removed version 4.\n */\nexport function useIsHotkeyPressed() {\n return hotkeys.isPressed;\n}","import hotkeys, { HotkeysEvent, KeyHandler } from 'hotkeys-js';\nimport React, { useCallback, useEffect, useRef } from 'react';\n\ntype AvailableTags = 'INPUT' | 'TEXTAREA' | 'SELECT';\n\n// We implement our own custom filter system.\nhotkeys.filter = () => true;\n\nconst tagFilter = ({ target }: KeyboardEvent, enableOnTags?: AvailableTags[]) => {\n const targetTagName = target && (target as HTMLElement).tagName;\n\n return Boolean((targetTagName && enableOnTags && enableOnTags.includes(targetTagName as AvailableTags)));\n};\n\nconst isKeyboardEventTriggeredByInput = (ev: KeyboardEvent) => {\n return tagFilter(ev, ['INPUT', 'TEXTAREA', 'SELECT']);\n};\n\nexport type Options = {\n enabled?: boolean; // Main setting that determines if the hotkey is enabled or not. (Default: true)\n filter?: typeof hotkeys.filter; // A filter function returning whether the callback should get triggered or not. (Default: undefined)\n filterPreventDefault?: boolean; // Prevent default browser behavior if the filter function returns false. (Default: true)\n enableOnTags?: AvailableTags[]; // Enable hotkeys on a list of tags. (Default: [])\n enableOnContentEditable?: boolean; // Enable hotkeys on tags with contentEditable props. (Default: false)\n splitKey?: string; // Character to split keys in hotkeys combinations. (Default +)\n scope?: string; // Scope. Currently not doing anything.\n keyup?: boolean; // Trigger on keyup event? (Default: undefined)\n keydown?: boolean; // Trigger on keydown event? (Default: true)\n};\n\nexport function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: Options): React.MutableRefObject<T | null>;\nexport function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, deps?: any[]): React.MutableRefObject<T | null>;\nexport function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: Options, deps?: any[]): React.MutableRefObject<T | null>;\nexport function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: any[] | Options, deps?: any[]): React.MutableRefObject<T | null> {\n if (options instanceof Array) {\n deps = options;\n options = undefined;\n }\n\n const {\n enableOnTags,\n filter,\n keyup,\n keydown,\n filterPreventDefault = true,\n enabled = true,\n enableOnContentEditable = false,\n } = options as Options || {};\n const ref = useRef<T | null>(null);\n\n // The return value of this callback determines if the browsers default behavior is prevented.\n const memoisedCallback = useCallback((keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => {\n if (filter && !filter(keyboardEvent)) {\n return !filterPreventDefault;\n }\n\n // Check whether the hotkeys was triggered inside an input and that input is enabled or if it was triggered by a content editable tag and it is enabled.\n if (\n (isKeyboardEventTriggeredByInput(keyboardEvent) && !tagFilter(keyboardEvent, enableOnTags))\n || ((keyboardEvent.target as HTMLElement)?.isContentEditable && !enableOnContentEditable)\n ) {\n return true;\n }\n\n if (ref.current === null || document.activeElement === ref.current) {\n callback(keyboardEvent, hotkeysEvent);\n return true;\n }\n\n return false;\n }, deps ? [ref, enableOnTags, filter, ...deps] : [ref, enableOnTags, filter]);\n\n useEffect(() => {\n if (!enabled) {\n return;\n }\n\n // In this case keydown is likely undefined, so we set it to false, since hotkeys needs the `keydown` key to have a value.\n if (keyup && keydown !== true) {\n (options as Options).keydown = false;\n }\n\n hotkeys(keys, (options as Options) || {}, memoisedCallback);\n\n return () => hotkeys.unbind(keys, memoisedCallback);\n }, [memoisedCallback, keys, enabled]);\n\n return ref;\n}\n","import { useIsHotkeyPressed } from './useIsHotkeyPressed';\nimport { useHotkeys, Options } from './useHotkeys';\nimport hotkeys from 'hotkeys-js';\n\nconst isHotkeyPressed = hotkeys.isPressed;\n\nexport { useHotkeys, useIsHotkeyPressed, isHotkeyPressed, Options };"],"names":["useIsHotkeyPressed","hotkeys","isPressed","filter","tagFilter","enableOnTags","target","targetTagName","tagName","Boolean","includes","isKeyboardEventTriggeredByInput","ev","useHotkeys","keys","callback","options","deps","Array","undefined","keyup","keydown","filterPreventDefault","enabled","enableOnContentEditable","ref","useRef","memoisedCallback","useCallback","keyboardEvent","hotkeysEvent","isContentEditable","current","document","activeElement","useEffect","unbind","isHotkeyPressed"],"mappings":";;;;;;;AAEA;;;;SAGgBA;AACd,SAAOC,OAAO,CAACC,SAAf;AACD;;ACDDD,OAAO,CAACE,MAAR,GAAiB;AAAA,SAAM,IAAN;AAAA,CAAjB;;AAEA,IAAMC,SAAS,GAAG,SAAZA,SAAY,OAA4BC,YAA5B;MAAGC,cAAAA;AACnB,MAAMC,aAAa,GAAGD,MAAM,IAAKA,MAAsB,CAACE,OAAxD;AAEA,SAAOC,OAAO,CAAEF,aAAa,IAAIF,YAAjB,IAAiCA,YAAY,CAACK,QAAb,CAAsBH,aAAtB,CAAnC,CAAd;AACD,CAJD;;AAMA,IAAMI,+BAA+B,GAAG,SAAlCA,+BAAkC,CAACC,EAAD;AACtC,SAAOR,SAAS,CAACQ,EAAD,EAAK,CAAC,OAAD,EAAU,UAAV,EAAsB,QAAtB,CAAL,CAAhB;AACD,CAFD;;AAmBA,SAAgBC,WAA8BC,MAAcC,UAAsBC,SAA2BC;AAC3G,MAAID,OAAO,YAAYE,KAAvB,EAA8B;AAC5BD,IAAAA,IAAI,GAAGD,OAAP;AACAA,IAAAA,OAAO,GAAGG,SAAV;AACD;;AAED,cAQIH,OAAkB,IAAI,EAR1B;AAAA,MACEX,YADF,SACEA,YADF;AAAA,MAEEF,MAFF,SAEEA,MAFF;AAAA,MAGEiB,KAHF,SAGEA,KAHF;AAAA,MAIEC,OAJF,SAIEA,OAJF;AAAA,oCAKEC,oBALF;AAAA,MAKEA,oBALF,sCAKyB,IALzB;AAAA,4BAMEC,OANF;AAAA,MAMEA,OANF,8BAMY,IANZ;AAAA,oCAOEC,uBAPF;AAAA,MAOEA,uBAPF,sCAO4B,KAP5B;;AASA,MAAMC,GAAG,GAAGC,YAAM,CAAW,IAAX,CAAlB;;AAGA,MAAMC,gBAAgB,GAAGC,iBAAW,CAAC,UAACC,aAAD,EAA+BC,YAA/B;;;AACnC,QAAI3B,MAAM,IAAI,CAACA,MAAM,CAAC0B,aAAD,CAArB,EAAsC;AACpC,aAAO,CAACP,oBAAR;AACD;;;AAGD,QACGX,+BAA+B,CAACkB,aAAD,CAA/B,IAAkD,CAACzB,SAAS,CAACyB,aAAD,EAAgBxB,YAAhB,CAA7D,IACK,yBAAAwB,aAAa,CAACvB,MAAd,mCAAsCyB,iBAAtC,IAA2D,CAACP,uBAFnE,EAGE;AACA,aAAO,IAAP;AACD;;AAED,QAAIC,GAAG,CAACO,OAAJ,KAAgB,IAAhB,IAAwBC,QAAQ,CAACC,aAAT,KAA2BT,GAAG,CAACO,OAA3D,EAAoE;AAClEjB,MAAAA,QAAQ,CAACc,aAAD,EAAgBC,YAAhB,CAAR;AACA,aAAO,IAAP;AACD;;AAED,WAAO,KAAP;AACD,GAnBmC,EAmBjCb,IAAI,IAAIQ,GAAJ,EAASpB,YAAT,EAAuBF,MAAvB,SAAkCc,IAAlC,IAA0C,CAACQ,GAAD,EAAMpB,YAAN,EAAoBF,MAApB,CAnBb,CAApC;AAqBAgC,EAAAA,eAAS,CAAC;AACR,QAAI,CAACZ,OAAL,EAAc;AACZ;AACD;;;AAGD,QAAIH,KAAK,IAAIC,OAAO,KAAK,IAAzB,EAA+B;AAC5BL,MAAAA,OAAmB,CAACK,OAApB,GAA8B,KAA9B;AACF;;AAEDpB,IAAAA,OAAO,CAACa,IAAD,EAAQE,OAAmB,IAAI,EAA/B,EAAmCW,gBAAnC,CAAP;AAEA,WAAO;AAAA,aAAM1B,OAAO,CAACmC,MAAR,CAAetB,IAAf,EAAqBa,gBAArB,CAAN;AAAA,KAAP;AACD,GAbQ,EAaN,CAACA,gBAAD,EAAmBb,IAAnB,EAAyBS,OAAzB,CAbM,CAAT;AAeA,SAAOE,GAAP;AACD;;ICpFKY,eAAe,GAAGpC,OAAO,CAACC,SAAhC;;;;;;"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e,t=(e=require("hotkeys-js"))&&"object"==typeof e&&"default"in e?e.default:e,n=require("react");t.filter=function(){return!0};var r=function(e,t){var n=e.target,r=n&&n.tagName;return Boolean(r&&t&&t.includes(r))};exports.isHotkeyPressed=t.isPressed,exports.useHotkeys=function(e,u,i,o){i instanceof Array&&(o=i,i=void 0);var s=i||{},a=s.enableOnTags,l=s.filter,c=s.keyup,f=s.keydown,d=s.filterPreventDefault,v=void 0===d||d,y=s.enabled,b=void 0===y||y,k=s.enableOnContentEditable,E=void 0!==k&&k,P=n.useRef(null),p=n.useCallback((function(e,t){var n;return l&&!l(e)?!v:!!(r(e,["INPUT","TEXTAREA","SELECT"])&&!r(e,a)||null!=(n=e.target)&&n.isContentEditable&&!E)||(null===P.current||document.activeElement===P.current)&&(u(e,t),!0)}),o?[P,a,l].concat(o):[P,a,l]);return n.useEffect((function(){if(b)return c&&!0!==f&&(i.keydown=!1),t(e,i||{},p),function(){return t.unbind(e,p)}}),[p,e,b]),P},exports.useIsHotkeyPressed=function(){return t.isPressed};
2
+ //# sourceMappingURL=react-hotkeys-hook.cjs.production.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-hotkeys-hook.cjs.production.min.js","sources":["../src/useHotkeys.ts","../src/index.ts","../src/useIsHotkeyPressed.ts"],"sourcesContent":["import hotkeys, { HotkeysEvent, KeyHandler } from 'hotkeys-js';\nimport React, { useCallback, useEffect, useRef } from 'react';\n\ntype AvailableTags = 'INPUT' | 'TEXTAREA' | 'SELECT';\n\n// We implement our own custom filter system.\nhotkeys.filter = () => true;\n\nconst tagFilter = ({ target }: KeyboardEvent, enableOnTags?: AvailableTags[]) => {\n const targetTagName = target && (target as HTMLElement).tagName;\n\n return Boolean((targetTagName && enableOnTags && enableOnTags.includes(targetTagName as AvailableTags)));\n};\n\nconst isKeyboardEventTriggeredByInput = (ev: KeyboardEvent) => {\n return tagFilter(ev, ['INPUT', 'TEXTAREA', 'SELECT']);\n};\n\nexport type Options = {\n enabled?: boolean; // Main setting that determines if the hotkey is enabled or not. (Default: true)\n filter?: typeof hotkeys.filter; // A filter function returning whether the callback should get triggered or not. (Default: undefined)\n filterPreventDefault?: boolean; // Prevent default browser behavior if the filter function returns false. (Default: true)\n enableOnTags?: AvailableTags[]; // Enable hotkeys on a list of tags. (Default: [])\n enableOnContentEditable?: boolean; // Enable hotkeys on tags with contentEditable props. (Default: false)\n splitKey?: string; // Character to split keys in hotkeys combinations. (Default +)\n scope?: string; // Scope. Currently not doing anything.\n keyup?: boolean; // Trigger on keyup event? (Default: undefined)\n keydown?: boolean; // Trigger on keydown event? (Default: true)\n};\n\nexport function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: Options): React.MutableRefObject<T | null>;\nexport function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, deps?: any[]): React.MutableRefObject<T | null>;\nexport function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: Options, deps?: any[]): React.MutableRefObject<T | null>;\nexport function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: any[] | Options, deps?: any[]): React.MutableRefObject<T | null> {\n if (options instanceof Array) {\n deps = options;\n options = undefined;\n }\n\n const {\n enableOnTags,\n filter,\n keyup,\n keydown,\n filterPreventDefault = true,\n enabled = true,\n enableOnContentEditable = false,\n } = options as Options || {};\n const ref = useRef<T | null>(null);\n\n // The return value of this callback determines if the browsers default behavior is prevented.\n const memoisedCallback = useCallback((keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => {\n if (filter && !filter(keyboardEvent)) {\n return !filterPreventDefault;\n }\n\n // Check whether the hotkeys was triggered inside an input and that input is enabled or if it was triggered by a content editable tag and it is enabled.\n if (\n (isKeyboardEventTriggeredByInput(keyboardEvent) && !tagFilter(keyboardEvent, enableOnTags))\n || ((keyboardEvent.target as HTMLElement)?.isContentEditable && !enableOnContentEditable)\n ) {\n return true;\n }\n\n if (ref.current === null || document.activeElement === ref.current) {\n callback(keyboardEvent, hotkeysEvent);\n return true;\n }\n\n return false;\n }, deps ? [ref, enableOnTags, filter, ...deps] : [ref, enableOnTags, filter]);\n\n useEffect(() => {\n if (!enabled) {\n return;\n }\n\n // In this case keydown is likely undefined, so we set it to false, since hotkeys needs the `keydown` key to have a value.\n if (keyup && keydown !== true) {\n (options as Options).keydown = false;\n }\n\n hotkeys(keys, (options as Options) || {}, memoisedCallback);\n\n return () => hotkeys.unbind(keys, memoisedCallback);\n }, [memoisedCallback, keys, enabled]);\n\n return ref;\n}\n","import { useIsHotkeyPressed } from './useIsHotkeyPressed';\nimport { useHotkeys, Options } from './useHotkeys';\nimport hotkeys from 'hotkeys-js';\n\nconst isHotkeyPressed = hotkeys.isPressed;\n\nexport { useHotkeys, useIsHotkeyPressed, isHotkeyPressed, Options };","import hotkeys from 'hotkeys-js';\n\n/**\n * @deprecated Use isHotkeyPressed instead. Will be removed version 4.\n */\nexport function useIsHotkeyPressed() {\n return hotkeys.isPressed;\n}"],"names":["hotkeys","filter","tagFilter","enableOnTags","target","targetTagName","tagName","Boolean","includes","isPressed","keys","callback","options","deps","Array","undefined","keyup","keydown","filterPreventDefault","enabled","enableOnContentEditable","ref","useRef","memoisedCallback","useCallback","keyboardEvent","hotkeysEvent","isContentEditable","current","document","activeElement","useEffect","unbind"],"mappings":"iHAMAA,EAAQC,OAAS,kBAAM,GAEvB,IAAMC,EAAY,WAA4BC,OAAzBC,IAAAA,OACbC,EAAgBD,GAAWA,EAAuBE,eAEjDC,QAASF,GAAiBF,GAAgBA,EAAaK,SAASH,6BCPjDL,EAAQS,6BD6BhC,SAA8CC,EAAcC,EAAsBC,EAA2BC,GACvGD,aAAmBE,QACrBD,EAAOD,EACPA,OAAUG,SAWRH,GAAsB,GAPxBT,IAAAA,aACAF,IAAAA,OACAe,IAAAA,MACAC,IAAAA,YACAC,qBAAAA,oBACAC,QAAAA,oBACAC,wBAAAA,gBAEIC,EAAMC,SAAiB,MAGvBC,EAAmBC,eAAY,SAACC,EAA8BC,gBAC9DzB,IAAWA,EAAOwB,IACZP,KAtCLhB,EA2C8BuB,EA3ChB,CAAC,QAAS,WAAY,aA2CavB,EAAUuB,EAAetB,aACxEsB,EAAcrB,WAAwBuB,oBAAsBP,KAK/C,OAAhBC,EAAIO,SAAoBC,SAASC,gBAAkBT,EAAIO,WACzDjB,EAASc,EAAeC,IACjB,KAIRb,GAAQQ,EAAKlB,EAAcF,UAAWY,GAAQ,CAACQ,EAAKlB,EAAcF,WAErE8B,aAAU,cACHZ,SAKDH,IAAqB,IAAZC,IACVL,EAAoBK,SAAU,GAGjCjB,EAAQU,EAAOE,GAAuB,GAAIW,GAEnC,kBAAMvB,EAAQgC,OAAOtB,EAAMa,MACjC,CAACA,EAAkBb,EAAMS,IAErBE,gDEjFArB,EAAQS"}
@@ -0,0 +1,86 @@
1
+ import hotkeys from 'hotkeys-js';
2
+ import { useRef, useCallback, useEffect } from 'react';
3
+
4
+ /**
5
+ * @deprecated Use isHotkeyPressed instead. Will be removed version 4.
6
+ */
7
+
8
+ function useIsHotkeyPressed() {
9
+ return hotkeys.isPressed;
10
+ }
11
+
12
+ hotkeys.filter = function () {
13
+ return true;
14
+ };
15
+
16
+ var tagFilter = function tagFilter(_ref, enableOnTags) {
17
+ var target = _ref.target;
18
+ var targetTagName = target && target.tagName;
19
+ return Boolean(targetTagName && enableOnTags && enableOnTags.includes(targetTagName));
20
+ };
21
+
22
+ var isKeyboardEventTriggeredByInput = function isKeyboardEventTriggeredByInput(ev) {
23
+ return tagFilter(ev, ['INPUT', 'TEXTAREA', 'SELECT']);
24
+ };
25
+
26
+ function useHotkeys(keys, callback, options, deps) {
27
+ if (options instanceof Array) {
28
+ deps = options;
29
+ options = undefined;
30
+ }
31
+
32
+ var _ref2 = options || {},
33
+ enableOnTags = _ref2.enableOnTags,
34
+ filter = _ref2.filter,
35
+ keyup = _ref2.keyup,
36
+ keydown = _ref2.keydown,
37
+ _ref2$filterPreventDe = _ref2.filterPreventDefault,
38
+ filterPreventDefault = _ref2$filterPreventDe === void 0 ? true : _ref2$filterPreventDe,
39
+ _ref2$enabled = _ref2.enabled,
40
+ enabled = _ref2$enabled === void 0 ? true : _ref2$enabled,
41
+ _ref2$enableOnContent = _ref2.enableOnContentEditable,
42
+ enableOnContentEditable = _ref2$enableOnContent === void 0 ? false : _ref2$enableOnContent;
43
+
44
+ var ref = useRef(null); // The return value of this callback determines if the browsers default behavior is prevented.
45
+
46
+ var memoisedCallback = useCallback(function (keyboardEvent, hotkeysEvent) {
47
+ var _keyboardEvent$target;
48
+
49
+ if (filter && !filter(keyboardEvent)) {
50
+ return !filterPreventDefault;
51
+ } // Check whether the hotkeys was triggered inside an input and that input is enabled or if it was triggered by a content editable tag and it is enabled.
52
+
53
+
54
+ if (isKeyboardEventTriggeredByInput(keyboardEvent) && !tagFilter(keyboardEvent, enableOnTags) || (_keyboardEvent$target = keyboardEvent.target) != null && _keyboardEvent$target.isContentEditable && !enableOnContentEditable) {
55
+ return true;
56
+ }
57
+
58
+ if (ref.current === null || document.activeElement === ref.current) {
59
+ callback(keyboardEvent, hotkeysEvent);
60
+ return true;
61
+ }
62
+
63
+ return false;
64
+ }, deps ? [ref, enableOnTags, filter].concat(deps) : [ref, enableOnTags, filter]);
65
+ useEffect(function () {
66
+ if (!enabled) {
67
+ return;
68
+ } // In this case keydown is likely undefined, so we set it to false, since hotkeys needs the `keydown` key to have a value.
69
+
70
+
71
+ if (keyup && keydown !== true) {
72
+ options.keydown = false;
73
+ }
74
+
75
+ hotkeys(keys, options || {}, memoisedCallback);
76
+ return function () {
77
+ return hotkeys.unbind(keys, memoisedCallback);
78
+ };
79
+ }, [memoisedCallback, keys, enabled]);
80
+ return ref;
81
+ }
82
+
83
+ var isHotkeyPressed = hotkeys.isPressed;
84
+
85
+ export { isHotkeyPressed, useHotkeys, useIsHotkeyPressed };
86
+ //# sourceMappingURL=react-hotkeys-hook.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-hotkeys-hook.esm.js","sources":["../src/useIsHotkeyPressed.ts","../src/useHotkeys.ts","../src/index.ts"],"sourcesContent":["import hotkeys from 'hotkeys-js';\n\n/**\n * @deprecated Use isHotkeyPressed instead. Will be removed version 4.\n */\nexport function useIsHotkeyPressed() {\n return hotkeys.isPressed;\n}","import hotkeys, { HotkeysEvent, KeyHandler } from 'hotkeys-js';\nimport React, { useCallback, useEffect, useRef } from 'react';\n\ntype AvailableTags = 'INPUT' | 'TEXTAREA' | 'SELECT';\n\n// We implement our own custom filter system.\nhotkeys.filter = () => true;\n\nconst tagFilter = ({ target }: KeyboardEvent, enableOnTags?: AvailableTags[]) => {\n const targetTagName = target && (target as HTMLElement).tagName;\n\n return Boolean((targetTagName && enableOnTags && enableOnTags.includes(targetTagName as AvailableTags)));\n};\n\nconst isKeyboardEventTriggeredByInput = (ev: KeyboardEvent) => {\n return tagFilter(ev, ['INPUT', 'TEXTAREA', 'SELECT']);\n};\n\nexport type Options = {\n enabled?: boolean; // Main setting that determines if the hotkey is enabled or not. (Default: true)\n filter?: typeof hotkeys.filter; // A filter function returning whether the callback should get triggered or not. (Default: undefined)\n filterPreventDefault?: boolean; // Prevent default browser behavior if the filter function returns false. (Default: true)\n enableOnTags?: AvailableTags[]; // Enable hotkeys on a list of tags. (Default: [])\n enableOnContentEditable?: boolean; // Enable hotkeys on tags with contentEditable props. (Default: false)\n splitKey?: string; // Character to split keys in hotkeys combinations. (Default +)\n scope?: string; // Scope. Currently not doing anything.\n keyup?: boolean; // Trigger on keyup event? (Default: undefined)\n keydown?: boolean; // Trigger on keydown event? (Default: true)\n};\n\nexport function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: Options): React.MutableRefObject<T | null>;\nexport function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, deps?: any[]): React.MutableRefObject<T | null>;\nexport function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: Options, deps?: any[]): React.MutableRefObject<T | null>;\nexport function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: any[] | Options, deps?: any[]): React.MutableRefObject<T | null> {\n if (options instanceof Array) {\n deps = options;\n options = undefined;\n }\n\n const {\n enableOnTags,\n filter,\n keyup,\n keydown,\n filterPreventDefault = true,\n enabled = true,\n enableOnContentEditable = false,\n } = options as Options || {};\n const ref = useRef<T | null>(null);\n\n // The return value of this callback determines if the browsers default behavior is prevented.\n const memoisedCallback = useCallback((keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => {\n if (filter && !filter(keyboardEvent)) {\n return !filterPreventDefault;\n }\n\n // Check whether the hotkeys was triggered inside an input and that input is enabled or if it was triggered by a content editable tag and it is enabled.\n if (\n (isKeyboardEventTriggeredByInput(keyboardEvent) && !tagFilter(keyboardEvent, enableOnTags))\n || ((keyboardEvent.target as HTMLElement)?.isContentEditable && !enableOnContentEditable)\n ) {\n return true;\n }\n\n if (ref.current === null || document.activeElement === ref.current) {\n callback(keyboardEvent, hotkeysEvent);\n return true;\n }\n\n return false;\n }, deps ? [ref, enableOnTags, filter, ...deps] : [ref, enableOnTags, filter]);\n\n useEffect(() => {\n if (!enabled) {\n return;\n }\n\n // In this case keydown is likely undefined, so we set it to false, since hotkeys needs the `keydown` key to have a value.\n if (keyup && keydown !== true) {\n (options as Options).keydown = false;\n }\n\n hotkeys(keys, (options as Options) || {}, memoisedCallback);\n\n return () => hotkeys.unbind(keys, memoisedCallback);\n }, [memoisedCallback, keys, enabled]);\n\n return ref;\n}\n","import { useIsHotkeyPressed } from './useIsHotkeyPressed';\nimport { useHotkeys, Options } from './useHotkeys';\nimport hotkeys from 'hotkeys-js';\n\nconst isHotkeyPressed = hotkeys.isPressed;\n\nexport { useHotkeys, useIsHotkeyPressed, isHotkeyPressed, Options };"],"names":["useIsHotkeyPressed","hotkeys","isPressed","filter","tagFilter","enableOnTags","target","targetTagName","tagName","Boolean","includes","isKeyboardEventTriggeredByInput","ev","useHotkeys","keys","callback","options","deps","Array","undefined","keyup","keydown","filterPreventDefault","enabled","enableOnContentEditable","ref","useRef","memoisedCallback","useCallback","keyboardEvent","hotkeysEvent","isContentEditable","current","document","activeElement","useEffect","unbind","isHotkeyPressed"],"mappings":";;;AAEA;;;;SAGgBA;AACd,SAAOC,OAAO,CAACC,SAAf;AACD;;ACDDD,OAAO,CAACE,MAAR,GAAiB;AAAA,SAAM,IAAN;AAAA,CAAjB;;AAEA,IAAMC,SAAS,GAAG,SAAZA,SAAY,OAA4BC,YAA5B;MAAGC,cAAAA;AACnB,MAAMC,aAAa,GAAGD,MAAM,IAAKA,MAAsB,CAACE,OAAxD;AAEA,SAAOC,OAAO,CAAEF,aAAa,IAAIF,YAAjB,IAAiCA,YAAY,CAACK,QAAb,CAAsBH,aAAtB,CAAnC,CAAd;AACD,CAJD;;AAMA,IAAMI,+BAA+B,GAAG,SAAlCA,+BAAkC,CAACC,EAAD;AACtC,SAAOR,SAAS,CAACQ,EAAD,EAAK,CAAC,OAAD,EAAU,UAAV,EAAsB,QAAtB,CAAL,CAAhB;AACD,CAFD;;AAmBA,SAAgBC,WAA8BC,MAAcC,UAAsBC,SAA2BC;AAC3G,MAAID,OAAO,YAAYE,KAAvB,EAA8B;AAC5BD,IAAAA,IAAI,GAAGD,OAAP;AACAA,IAAAA,OAAO,GAAGG,SAAV;AACD;;AAED,cAQIH,OAAkB,IAAI,EAR1B;AAAA,MACEX,YADF,SACEA,YADF;AAAA,MAEEF,MAFF,SAEEA,MAFF;AAAA,MAGEiB,KAHF,SAGEA,KAHF;AAAA,MAIEC,OAJF,SAIEA,OAJF;AAAA,oCAKEC,oBALF;AAAA,MAKEA,oBALF,sCAKyB,IALzB;AAAA,4BAMEC,OANF;AAAA,MAMEA,OANF,8BAMY,IANZ;AAAA,oCAOEC,uBAPF;AAAA,MAOEA,uBAPF,sCAO4B,KAP5B;;AASA,MAAMC,GAAG,GAAGC,MAAM,CAAW,IAAX,CAAlB;;AAGA,MAAMC,gBAAgB,GAAGC,WAAW,CAAC,UAACC,aAAD,EAA+BC,YAA/B;;;AACnC,QAAI3B,MAAM,IAAI,CAACA,MAAM,CAAC0B,aAAD,CAArB,EAAsC;AACpC,aAAO,CAACP,oBAAR;AACD;;;AAGD,QACGX,+BAA+B,CAACkB,aAAD,CAA/B,IAAkD,CAACzB,SAAS,CAACyB,aAAD,EAAgBxB,YAAhB,CAA7D,IACK,yBAAAwB,aAAa,CAACvB,MAAd,mCAAsCyB,iBAAtC,IAA2D,CAACP,uBAFnE,EAGE;AACA,aAAO,IAAP;AACD;;AAED,QAAIC,GAAG,CAACO,OAAJ,KAAgB,IAAhB,IAAwBC,QAAQ,CAACC,aAAT,KAA2BT,GAAG,CAACO,OAA3D,EAAoE;AAClEjB,MAAAA,QAAQ,CAACc,aAAD,EAAgBC,YAAhB,CAAR;AACA,aAAO,IAAP;AACD;;AAED,WAAO,KAAP;AACD,GAnBmC,EAmBjCb,IAAI,IAAIQ,GAAJ,EAASpB,YAAT,EAAuBF,MAAvB,SAAkCc,IAAlC,IAA0C,CAACQ,GAAD,EAAMpB,YAAN,EAAoBF,MAApB,CAnBb,CAApC;AAqBAgC,EAAAA,SAAS,CAAC;AACR,QAAI,CAACZ,OAAL,EAAc;AACZ;AACD;;;AAGD,QAAIH,KAAK,IAAIC,OAAO,KAAK,IAAzB,EAA+B;AAC5BL,MAAAA,OAAmB,CAACK,OAApB,GAA8B,KAA9B;AACF;;AAEDpB,IAAAA,OAAO,CAACa,IAAD,EAAQE,OAAmB,IAAI,EAA/B,EAAmCW,gBAAnC,CAAP;AAEA,WAAO;AAAA,aAAM1B,OAAO,CAACmC,MAAR,CAAetB,IAAf,EAAqBa,gBAArB,CAAN;AAAA,KAAP;AACD,GAbQ,EAaN,CAACA,gBAAD,EAAmBb,IAAnB,EAAyBS,OAAzB,CAbM,CAAT;AAeA,SAAOE,GAAP;AACD;;ICpFKY,eAAe,GAAGpC,OAAO,CAACC,SAAhC;;;;"}
@@ -0,0 +1,18 @@
1
+ import hotkeys, { KeyHandler } from 'hotkeys-js';
2
+ import React from 'react';
3
+ declare type AvailableTags = 'INPUT' | 'TEXTAREA' | 'SELECT';
4
+ export declare type Options = {
5
+ enabled?: boolean;
6
+ filter?: typeof hotkeys.filter;
7
+ filterPreventDefault?: boolean;
8
+ enableOnTags?: AvailableTags[];
9
+ enableOnContentEditable?: boolean;
10
+ splitKey?: string;
11
+ scope?: string;
12
+ keyup?: boolean;
13
+ keydown?: boolean;
14
+ };
15
+ export declare function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: Options): React.MutableRefObject<T | null>;
16
+ export declare function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, deps?: any[]): React.MutableRefObject<T | null>;
17
+ export declare function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: Options, deps?: any[]): React.MutableRefObject<T | null>;
18
+ export {};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @deprecated Use isHotkeyPressed instead. Will be removed version 4.
3
+ */
4
+ export declare function useIsHotkeyPressed(): {
5
+ (keyCode: number): boolean;
6
+ (keyCode: string): boolean;
7
+ };
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "react-hotkeys-hook",
3
+ "version": "3.4.1",
4
+ "repository": "https://JohannesKlauss@github.com/JohannesKlauss/react-keymap-hook.git",
5
+ "homepage": "https://johannesklauss.github.io/react-hotkeys-hook/",
6
+ "author": "Johannes Klauss",
7
+ "main": "dist/index.js",
8
+ "typings": "dist/index.d.ts",
9
+ "module": "dist/react-hotkeys-hook.esm.js",
10
+ "files": [
11
+ "dist",
12
+ "src"
13
+ ],
14
+ "keywords": [
15
+ "react",
16
+ "hook",
17
+ "hooks",
18
+ "component",
19
+ "hotkey",
20
+ "shortcut",
21
+ "keyboard",
22
+ "shortcuts",
23
+ "keypress",
24
+ "hotkeys"
25
+ ],
26
+ "license": "MIT",
27
+ "scripts": {
28
+ "start": "tsdx watch",
29
+ "build": "tsdx build",
30
+ "test": "jest",
31
+ "publish": "np"
32
+ },
33
+ "babel": {
34
+ "presets": [
35
+ "@babel/preset-env",
36
+ "@babel/preset-typescript",
37
+ "@babel/preset-react"
38
+ ]
39
+ },
40
+ "jest": {
41
+ "setupFilesAfterEnv": [
42
+ "./setupTests.js"
43
+ ],
44
+ "testPathIgnorePatterns": [
45
+ "pkg",
46
+ ".docz",
47
+ "docs"
48
+ ]
49
+ },
50
+ "prettier": {
51
+ "printWidth": 80,
52
+ "semi": true,
53
+ "singleQuote": true,
54
+ "trailingComma": "es5"
55
+ },
56
+ "dependencies": {
57
+ "hotkeys-js": "3.8.7"
58
+ },
59
+ "devDependencies": {
60
+ "@babel/core": "7.15.5",
61
+ "@babel/plugin-proposal-class-properties": "7.14.5",
62
+ "@babel/preset-env": "7.15.6",
63
+ "@babel/preset-react": "7.14.5",
64
+ "@babel/preset-typescript": "7.15.0",
65
+ "@testing-library/react": "12.1.0",
66
+ "@testing-library/react-hooks": "7.0.2",
67
+ "@testing-library/user-event": "13.2.1",
68
+ "@types/jest": "27.0.1",
69
+ "@types/react": "17.0.24",
70
+ "@types/react-dom": "17.0.9",
71
+ "eslint-plugin-prettier": "4.0.0",
72
+ "jest": "26.6.3",
73
+ "prettier": "2.4.1",
74
+ "react": "17.0.2",
75
+ "react-dom": "17.0.2",
76
+ "react-test-renderer": "17.0.2",
77
+ "tsdx": "0.14.1",
78
+ "tslib": "2.3.1",
79
+ "typescript": "4.4.3"
80
+ },
81
+ "peerDependencies": {
82
+ "react": ">=16.8.1",
83
+ "react-dom": ">=16.8.1"
84
+ }
85
+ }
@@ -0,0 +1,230 @@
1
+ import React from 'react';
2
+ import { useHotkeys } from './index';
3
+ import { renderHook } from '@testing-library/react-hooks';
4
+ import { fireEvent, render, screen } from '@testing-library/react';
5
+ import userEvent from '@testing-library/user-event';
6
+ import hotkeys from 'hotkeys-js';
7
+
8
+ const HotkeysOnInput = ({ onPress, useTags }: { onPress: () => void, useTags?: boolean }) => {
9
+ useHotkeys('a', onPress, { enableOnTags: useTags ? ['INPUT'] : undefined });
10
+
11
+ return (
12
+ <input type='text' data-testid={'input'} />
13
+ );
14
+ };
15
+
16
+ const HotkeysFilteredOnInput = ({ onPress, useTags }: { onPress: () => void, useTags?: boolean }) => {
17
+ useHotkeys('a', onPress, { enableOnTags: useTags ? ['TEXTAREA'] : undefined });
18
+
19
+ return (
20
+ <input type='text' data-testid={'input'} />
21
+ );
22
+ };
23
+
24
+ const HotkeysOnKeyup = ({ onPress, keyup, keydown }: { onPress: () => void, keyup?: boolean, keydown?: boolean }) => {
25
+ useHotkeys('a', onPress, { keyup, keydown });
26
+
27
+ return (
28
+ <input type='text' data-testid={'input'} />
29
+ );
30
+ };
31
+
32
+ const HotkeysWithRef = ({ onPress }: { onPress: () => void }) => {
33
+ const ref = useHotkeys<HTMLElement>('a', onPress);
34
+
35
+ return (
36
+ <section ref={ref} tabIndex={0} data-testid={'container'}>
37
+ <input type='text' data-testid={'input'} />
38
+ </section>
39
+ );
40
+ };
41
+
42
+ test('useHotkeys should only fire when element is focused if a ref is set.', () => {
43
+ const onPress = jest.fn();
44
+
45
+ render(<HotkeysWithRef onPress={onPress} />);
46
+
47
+ userEvent.keyboard('A');
48
+
49
+ expect(onPress).not.toBeCalled();
50
+
51
+ userEvent.click(screen.getByTestId('container'));
52
+ userEvent.keyboard('A');
53
+
54
+ expect(onPress).toBeCalled();
55
+ });
56
+
57
+ test('useHotkeys should listen to key presses', () => {
58
+ const callback = jest.fn();
59
+
60
+ renderHook(() => useHotkeys('a', callback));
61
+
62
+ userEvent.keyboard('A');
63
+
64
+ expect(callback).toHaveBeenCalledTimes(1);
65
+ });
66
+
67
+ test('useHotkeys correctly assign deps when used as third argument and options being omitted', async () => {
68
+ let count = 0;
69
+ const callback = jest.fn();
70
+
71
+ renderHook(() => useHotkeys('a', () => callback(++count), [count]));
72
+
73
+ userEvent.keyboard('A');
74
+
75
+ expect(callback).toHaveBeenCalledTimes(1);
76
+ expect(callback.mock.calls[0][0]).toEqual(1);
77
+
78
+ userEvent.keyboard('A');
79
+
80
+ expect(callback).toHaveBeenCalledTimes(2);
81
+ expect(callback.mock.calls[1][0]).toEqual(2);
82
+ });
83
+
84
+ test('useHotkeys should use correct char to split combinations', () => {
85
+ const callback = jest.fn();
86
+
87
+ renderHook(() => useHotkeys('Shift-A', callback, { splitKey: '-' }));
88
+
89
+ userEvent.keyboard('{Shift>}A{/Shift}');
90
+
91
+ expect(callback).toHaveBeenCalledTimes(1);
92
+
93
+ userEvent.keyboard('{Shift>}A{/Shift}');
94
+
95
+ expect(callback).toHaveBeenCalledTimes(2);
96
+ });
97
+
98
+ test('useHotkeys should use correctly assign options and deps argument when using all four arguments', () => {
99
+ const callback = jest.fn();
100
+
101
+ renderHook(() => useHotkeys('shift-a', callback, { splitKey: '-' }, []));
102
+
103
+ userEvent.keyboard('{Shift>}A{/Shift}');
104
+
105
+ expect(callback).toHaveBeenCalledTimes(1);
106
+
107
+ userEvent.keyboard('{Shift>}A{/Shift}');
108
+
109
+ expect(callback).toHaveBeenCalledTimes(2);
110
+
111
+ userEvent.keyboard('{Shift>}A{/Shift}');
112
+
113
+ expect(callback).toHaveBeenCalledTimes(3);
114
+ });
115
+
116
+ test('useHotkeys should only trigger once if neither keyup nor keydown are set', () => {
117
+ const onPress = jest.fn();
118
+
119
+ render(<HotkeysOnKeyup onPress={onPress} />);
120
+
121
+ fireEvent.keyUp(document.body, { key: 'a', keyCode: 65 });
122
+
123
+ expect(onPress).not.toHaveBeenCalled();
124
+
125
+ fireEvent.keyDown(document.body, { key: 'a', keyCode: 65 });
126
+
127
+ expect(onPress).toHaveBeenCalled();
128
+ });
129
+
130
+ test('useHotkeys should only trigger once if keyup is set and keydown is not', () => {
131
+ const onPress = jest.fn();
132
+
133
+ render(<HotkeysOnKeyup onPress={onPress} keyup={true} />);
134
+
135
+ fireEvent.keyDown(document.body, { key: 'a', keyCode: 65 });
136
+
137
+ expect(onPress).not.toHaveBeenCalled();
138
+
139
+ fireEvent.keyUp(document.body, { key: 'a', keyCode: 65 });
140
+
141
+ expect(onPress).toHaveBeenCalled();
142
+ });
143
+
144
+ test('useHotkeys should trigger twice if keyup and keydown is set to true', () => {
145
+ let called = false;
146
+
147
+ render(<HotkeysOnKeyup onPress={() => called = true} keyup={true} keydown={true} />);
148
+
149
+ userEvent.keyboard('A');
150
+
151
+ expect(called).toBe(true);
152
+
153
+ userEvent.keyboard('A');
154
+
155
+ expect(called).toBe(true);
156
+ });
157
+
158
+ test('useHotkeys should be enabled on given form tags', async () => {
159
+ const onPress = jest.fn();
160
+ render(<HotkeysOnInput onPress={onPress} useTags={true} />);
161
+
162
+ const input = document.querySelector('input');
163
+
164
+ expect(input).not.toBe(null);
165
+
166
+ userEvent.keyboard('A');
167
+
168
+ expect(onPress).toHaveBeenCalled();
169
+ });
170
+
171
+ test('useHotkeys should not be enabled on given form tags when filter specifies different input field', async () => {
172
+ const onPress = jest.fn();
173
+ render(<HotkeysFilteredOnInput onPress={onPress} useTags={true} />);
174
+
175
+ userEvent.type(screen.getByRole('textbox'), 'A');
176
+
177
+ expect(onPress).toHaveBeenCalledTimes(0);
178
+ });
179
+
180
+ test('useHotkeys should not be enabled on given form tags when tags is not set', async () => {
181
+ const onPress = jest.fn();
182
+ render(<HotkeysFilteredOnInput onPress={onPress} useTags={false} />);
183
+
184
+ userEvent.type(screen.getByRole('textbox'), 'A');
185
+
186
+ expect(onPress).toHaveBeenCalledTimes(0);
187
+ });
188
+
189
+ test('useHotkeys should use its own custom filter system instead of the global hotkeys one', () => {
190
+ const callback = jest.fn();
191
+ const { rerender } = renderHook((returnFilterVal: boolean = false) => useHotkeys('a', callback, { filter: () => returnFilterVal }));
192
+
193
+ userEvent.keyboard('A');
194
+
195
+ expect(callback).not.toHaveBeenCalled();
196
+
197
+ rerender(true);
198
+
199
+ userEvent.keyboard('A');
200
+
201
+ expect(callback).toHaveBeenCalledTimes(1);
202
+ });
203
+
204
+ test('useHotkeys should not be enabled when enabled flag is set to false', () => {
205
+ const callback = jest.fn();
206
+
207
+ const { rerender } = renderHook((enabled: boolean = false) => useHotkeys('a', callback, { enabled }));
208
+
209
+ userEvent.keyboard('A');
210
+
211
+ expect(callback).not.toHaveBeenCalled();
212
+
213
+ rerender(true);
214
+
215
+ userEvent.keyboard('A');
216
+
217
+ expect(callback).toHaveBeenCalledTimes(1);
218
+ });
219
+
220
+ test('useHotkeys should unbind the hotkey when enabled is set from true to false', () => {
221
+ hotkeys.unbind = jest.fn();
222
+
223
+ const { rerender } = renderHook((enabled: boolean = true) => useHotkeys('a', () => true, { enabled }));
224
+
225
+ expect(hotkeys.unbind).not.toHaveBeenCalled()
226
+
227
+ rerender(false);
228
+
229
+ expect(hotkeys.unbind).toHaveBeenCalledTimes(2)
230
+ })
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { useIsHotkeyPressed } from './useIsHotkeyPressed';
2
+ import { useHotkeys, Options } from './useHotkeys';
3
+ import hotkeys from 'hotkeys-js';
4
+
5
+ const isHotkeyPressed = hotkeys.isPressed;
6
+
7
+ export { useHotkeys, useIsHotkeyPressed, isHotkeyPressed, Options };
@@ -0,0 +1,91 @@
1
+ import hotkeys, { HotkeysEvent, KeyHandler } from 'hotkeys-js';
2
+ import React, { useCallback, useEffect, useRef } from 'react';
3
+
4
+ type AvailableTags = 'INPUT' | 'TEXTAREA' | 'SELECT';
5
+
6
+ // We implement our own custom filter system.
7
+ hotkeys.filter = () => true;
8
+
9
+ const tagFilter = ({ target }: KeyboardEvent, enableOnTags?: AvailableTags[]) => {
10
+ const targetTagName = target && (target as HTMLElement).tagName;
11
+
12
+ return Boolean((targetTagName && enableOnTags && enableOnTags.includes(targetTagName as AvailableTags)));
13
+ };
14
+
15
+ const isKeyboardEventTriggeredByInput = (ev: KeyboardEvent) => {
16
+ return tagFilter(ev, ['INPUT', 'TEXTAREA', 'SELECT']);
17
+ };
18
+
19
+ export type Options = {
20
+ enabled?: boolean; // Main setting that determines if the hotkey is enabled or not. (Default: true)
21
+ filter?: typeof hotkeys.filter; // A filter function returning whether the callback should get triggered or not. (Default: undefined)
22
+ filterPreventDefault?: boolean; // Prevent default browser behavior if the filter function returns false. (Default: true)
23
+ enableOnTags?: AvailableTags[]; // Enable hotkeys on a list of tags. (Default: [])
24
+ enableOnContentEditable?: boolean; // Enable hotkeys on tags with contentEditable props. (Default: false)
25
+ splitKey?: string; // Character to split keys in hotkeys combinations. (Default +)
26
+ scope?: string; // Scope. Currently not doing anything.
27
+ keyup?: boolean; // Trigger on keyup event? (Default: undefined)
28
+ keydown?: boolean; // Trigger on keydown event? (Default: true)
29
+ };
30
+
31
+ export function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: Options): React.MutableRefObject<T | null>;
32
+ export function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, deps?: any[]): React.MutableRefObject<T | null>;
33
+ export function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: Options, deps?: any[]): React.MutableRefObject<T | null>;
34
+ export function useHotkeys<T extends Element>(keys: string, callback: KeyHandler, options?: any[] | Options, deps?: any[]): React.MutableRefObject<T | null> {
35
+ if (options instanceof Array) {
36
+ deps = options;
37
+ options = undefined;
38
+ }
39
+
40
+ const {
41
+ enableOnTags,
42
+ filter,
43
+ keyup,
44
+ keydown,
45
+ filterPreventDefault = true,
46
+ enabled = true,
47
+ enableOnContentEditable = false,
48
+ } = options as Options || {};
49
+ const ref = useRef<T | null>(null);
50
+
51
+ // The return value of this callback determines if the browsers default behavior is prevented.
52
+ const memoisedCallback = useCallback((keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => {
53
+ if (filter && !filter(keyboardEvent)) {
54
+ return !filterPreventDefault;
55
+ }
56
+
57
+ // Check whether the hotkeys was triggered inside an input and that input is enabled or if it was triggered by a content editable tag and it is enabled.
58
+ if (
59
+ (isKeyboardEventTriggeredByInput(keyboardEvent) && !tagFilter(keyboardEvent, enableOnTags))
60
+ || ((keyboardEvent.target as HTMLElement)?.isContentEditable && !enableOnContentEditable)
61
+ ) {
62
+ return true;
63
+ }
64
+
65
+ if (ref.current === null || document.activeElement === ref.current) {
66
+ callback(keyboardEvent, hotkeysEvent);
67
+ return true;
68
+ }
69
+
70
+ return false;
71
+ }, deps ? [ref, enableOnTags, filter, ...deps] : [ref, enableOnTags, filter]);
72
+
73
+ useEffect(() => {
74
+ if (!enabled) {
75
+ hotkeys.unbind(keys, memoisedCallback);
76
+
77
+ return;
78
+ }
79
+
80
+ // In this case keydown is likely undefined, so we set it to false, since hotkeys needs the `keydown` key to have a value.
81
+ if (keyup && keydown !== true) {
82
+ (options as Options).keydown = false;
83
+ }
84
+
85
+ hotkeys(keys, (options as Options) || {}, memoisedCallback);
86
+
87
+ return () => hotkeys.unbind(keys, memoisedCallback);
88
+ }, [memoisedCallback, keys, enabled]);
89
+
90
+ return ref;
91
+ }
@@ -0,0 +1,8 @@
1
+ import hotkeys from 'hotkeys-js';
2
+
3
+ /**
4
+ * @deprecated Use isHotkeyPressed instead. Will be removed version 4.
5
+ */
6
+ export function useIsHotkeyPressed() {
7
+ return hotkeys.isPressed;
8
+ }