react-hotkeys-hook 5.2.1 → 5.2.2
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 +53 -222
- package/dist/index.js +300 -0
- package/{packages/react-hotkeys-hook/dist → dist}/parseHotkeys.d.ts +1 -1
- package/{packages/react-hotkeys-hook/dist → dist}/types.d.ts +2 -0
- package/package.json +26 -20
- package/LICENSE +0 -21
- package/packages/react-hotkeys-hook/dist/index.js +0 -282
- /package/{packages/react-hotkeys-hook/dist → dist}/BoundHotkeysProxyProvider.d.ts +0 -0
- /package/{packages/react-hotkeys-hook/dist → dist}/HotkeysProvider.d.ts +0 -0
- /package/{packages/react-hotkeys-hook/dist → dist}/deepEqual.d.ts +0 -0
- /package/{packages/react-hotkeys-hook/dist → dist}/index.d.ts +0 -0
- /package/{packages/react-hotkeys-hook/dist → dist}/isHotkeyPressed.d.ts +0 -0
- /package/{packages/react-hotkeys-hook/dist → dist}/useDeepEqualMemo.d.ts +0 -0
- /package/{packages/react-hotkeys-hook/dist → dist}/useHotkeys.d.ts +0 -0
- /package/{packages/react-hotkeys-hook/dist → dist}/useRecordHotkeys.d.ts +0 -0
- /package/{packages/react-hotkeys-hook/dist → dist}/validators.d.ts +0 -0
package/README.md
CHANGED
|
@@ -1,223 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
# React + TypeScript + Vite
|
|
2
|
+
|
|
3
|
+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
|
4
|
+
|
|
5
|
+
Currently, two official plugins are available:
|
|
6
|
+
|
|
7
|
+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
|
8
|
+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
|
9
|
+
|
|
10
|
+
## Expanding the ESLint configuration
|
|
11
|
+
|
|
12
|
+
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
export default tseslint.config({
|
|
16
|
+
extends: [
|
|
17
|
+
// Remove ...tseslint.configs.recommended and replace with this
|
|
18
|
+
...tseslint.configs.recommendedTypeChecked,
|
|
19
|
+
// Alternatively, use this for stricter rules
|
|
20
|
+
...tseslint.configs.strictTypeChecked,
|
|
21
|
+
// Optionally, add this for stylistic rules
|
|
22
|
+
...tseslint.configs.stylisticTypeChecked,
|
|
23
|
+
],
|
|
24
|
+
languageOptions: {
|
|
25
|
+
// other options...
|
|
26
|
+
parserOptions: {
|
|
27
|
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
|
28
|
+
tsconfigRootDir: import.meta.dirname,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
// eslint.config.js
|
|
38
|
+
import reactX from 'eslint-plugin-react-x'
|
|
39
|
+
import reactDom from 'eslint-plugin-react-dom'
|
|
40
|
+
|
|
41
|
+
export default tseslint.config({
|
|
42
|
+
plugins: {
|
|
43
|
+
// Add the react-x and react-dom plugins
|
|
44
|
+
'react-x': reactX,
|
|
45
|
+
'react-dom': reactDom,
|
|
46
|
+
},
|
|
47
|
+
rules: {
|
|
48
|
+
// other rules...
|
|
49
|
+
// Enable its recommended typescript rules
|
|
50
|
+
...reactX.configs['recommended-typescript'].rules,
|
|
51
|
+
...reactDom.configs.recommended.rules,
|
|
52
|
+
},
|
|
53
|
+
})
|
|
29
54
|
```
|
|
30
|
-
npm i react-hotkeys-hook
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
<p align="center">
|
|
34
|
-
A React hook for using keyboard shortcuts in components in a declarative way.
|
|
35
|
-
</p>
|
|
36
|
-
|
|
37
|
-
<hr>
|
|
38
|
-
|
|
39
|
-
## Quick Start
|
|
40
|
-
|
|
41
|
-
The easiest way to use the hook.
|
|
42
|
-
|
|
43
|
-
```jsx harmony
|
|
44
|
-
import { useHotkeys } from 'react-hotkeys-hook'
|
|
45
|
-
|
|
46
|
-
export const ExampleComponent = () => {
|
|
47
|
-
const [count, setCount] = useState(0)
|
|
48
|
-
useHotkeys('ctrl+k', () => setCount(count + 1), [count])
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<p>
|
|
52
|
-
Pressed {count} times.
|
|
53
|
-
</p>
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Scopes
|
|
59
|
-
|
|
60
|
-
Scopes allow you to group hotkeys together. You can use scopes to prevent hotkeys from colliding with each other.
|
|
61
|
-
|
|
62
|
-
```jsx harmony
|
|
63
|
-
const App = () => {
|
|
64
|
-
return (
|
|
65
|
-
<HotkeysProvider initiallyActiveScopes={['settings']}>
|
|
66
|
-
<ExampleComponent />
|
|
67
|
-
</HotkeysProvider>
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export const ExampleComponent = () => {
|
|
72
|
-
const [count, setCount] = useState(0)
|
|
73
|
-
useHotkeys('ctrl+k', () => setCount(prevCount => prevCount + 1), { scopes: ['settings'] })
|
|
74
|
-
|
|
75
|
-
return (
|
|
76
|
-
<p>
|
|
77
|
-
Pressed {count} times.
|
|
78
|
-
</p>
|
|
79
|
-
)
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
#### Changing a scope's active state
|
|
84
|
-
|
|
85
|
-
You can change the active state of a scope using the `disableScope`, `enableScope` and `toggleScope` functions
|
|
86
|
-
returned by the `useHotkeysContext()` hook. Note that you have to have your app wrapped in a `<HotkeysProvider>` component.
|
|
87
|
-
|
|
88
|
-
```jsx harmony
|
|
89
|
-
const App = () => {
|
|
90
|
-
return (
|
|
91
|
-
<HotkeysProvider initiallyActiveScopes={['settings']}>
|
|
92
|
-
<ExampleComponent />
|
|
93
|
-
</HotkeysProvider>
|
|
94
|
-
)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export const ExampleComponent = () => {
|
|
98
|
-
const { toggleScope } = useHotkeysContext()
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
<button onClick={() => toggleScope('settings')}>
|
|
102
|
-
Change scope active state
|
|
103
|
-
</button>
|
|
104
|
-
)
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Focus trap
|
|
109
|
-
|
|
110
|
-
This will only trigger the hotkey if the component is focused.
|
|
111
|
-
|
|
112
|
-
```tsx harmony
|
|
113
|
-
export const ExampleComponent = () => {
|
|
114
|
-
const [count, setCount] = useState(0)
|
|
115
|
-
const ref = useHotkeys<HTMLParagraphElement>('ctrl+k', () => setCount(prevCount => prevCount + 1))
|
|
116
|
-
|
|
117
|
-
return (
|
|
118
|
-
<p tabIndex={-1} ref={ref}>
|
|
119
|
-
Pressed {count} times.
|
|
120
|
-
</p>
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## Documentation & Live Examples
|
|
126
|
-
|
|
127
|
-
* [Quick Start](https://react-hotkeys-hook.vercel.app/docs/intro)
|
|
128
|
-
* [Documentation](https://react-hotkeys-hook.vercel.app/docs/documentation/installation)
|
|
129
|
-
* [API](https://react-hotkeys-hook.vercel.app/docs/api/use-hotkeys)
|
|
130
|
-
|
|
131
|
-
## API
|
|
132
|
-
|
|
133
|
-
### useHotkeys(keys, callback)
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
useHotkeys(keys: string | string[], callback: (event: KeyboardEvent, handler: HotkeysEvent) => void, options: Options = {}, deps: DependencyList = [])
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
| Parameter | Type | Required? | Default value | Description |
|
|
140
|
-
|---------------|---------------------------------------------------------|-----------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
141
|
-
| `keys` | `string` or `string[]` | required | - | set the hotkeys you want the hook to listen to. You can use single or multiple keys, modifier combinations, etc. This will either be a string or an array of strings. To separate multiple keys, use a comma. This split key value can be overridden with the `splitKey` option. |
|
|
142
|
-
| `callback` | `(event: KeyboardEvent, handler: HotkeysEvent) => void` | required | - | This is the callback function that will be called when the hotkey is pressed. The callback will receive the browsers native `KeyboardEvent` and the libraries `HotkeysEvent`. |
|
|
143
|
-
| `options` | `Options` | optional | `{}` | Object to modify the behavior of the hook. Default options are given below. |
|
|
144
|
-
| `dependencies` | `DependencyList` | optional | `[]` | The given callback will always be memoised inside the hook. So if you reference any outside variables, you need to set them here for the callback to get updated (Much like `useCallback` works in React). |
|
|
145
|
-
|
|
146
|
-
### Options
|
|
147
|
-
|
|
148
|
-
All options are optional and have a default value which you can override to change the behavior of the hook.
|
|
149
|
-
|
|
150
|
-
| Option | Type | Default value | Description |
|
|
151
|
-
|--------------------------|--------------------------------------------------------------------------------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
152
|
-
| `enabled` | `boolean` or `(keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => boolean` | `true` | This option determines whether the hotkey is active or not. It can take a boolean (for example a flag from a state outside) or a function which gets executed once the hotkey is pressed. If the function returns `false` the hotkey won't get executed and all browser events are prevented. |
|
|
153
|
-
| `enableOnFormTags` | `boolean` or `FormTags[]` | `false` | By default hotkeys are not registered if a focus focuses on an input field. This will prevent accidental triggering of hotkeys when the user is typing. If you want to enable hotkeys, use this option. Setting it to true will enable on all form tags, otherwise you can give an array of form tags to enable the hotkey on (possible options are: `['input', 'textarea', 'select']`) |
|
|
154
|
-
| `enableOnContentEditable` | `boolean` | `false` | Set this option to enable hotkeys on tags that have set the `contentEditable` prop to `true` |
|
|
155
|
-
| `combinationKey` | `string` | `+` | Character to indicate keystrokes like `shift+c`. You might want to change this if you want to listen to the `+` character like `ctrl-+`. |
|
|
156
|
-
| `splitKey` | `string` | `,` | Character to separate different keystrokes like `ctrl+a, ctrl+b`. |
|
|
157
|
-
| `scopes` | `string` or `string[]` | `*` | With scopes you can group hotkeys together. The default scope is the wildcard `*` which matches all hotkeys. Use the `<HotkeysProvider>` component to change active scopes. |
|
|
158
|
-
| `keyup` | `boolean` | `false` | Determines whether to listen to the browsers `keyup` event for triggering the callback. |
|
|
159
|
-
| `keydown` | `boolean` | `true` | Determines whether to listen to the browsers `keydown` event for triggering the callback. If you set both `keyup`and `keydown` to true, the callback will trigger on both events. |
|
|
160
|
-
| `preventDefault` | `boolean` or `(keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => boolean` | `false` | Set this to a `true` if you want the hook to prevent the browsers default behavior on certain keystrokes like `meta+s` to save a page. NOTE: Certain keystrokes are not preventable, like `meta+w` to close a tab in chrome. |
|
|
161
|
-
| `description` | `string` | `undefined` | Use this option to describe what the hotkey does. this is helpful if you want to display a list of active hotkeys to the user. |
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
#### Overloads
|
|
165
|
-
|
|
166
|
-
The hooks call signature is very flexible. For example if you don't need to set any special options you can use the dependency
|
|
167
|
-
array as your third parameter:
|
|
168
|
-
|
|
169
|
-
`useHotkeys('ctrl+k', () => console.log(counter + 1), [counter])`
|
|
170
|
-
|
|
171
|
-
### `isHotkeyPressed(keys: string | string[], splitKey?: string = ',')`
|
|
172
|
-
|
|
173
|
-
This function allows us to check if the user is currently pressing down a key.
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
import { isHotkeyPressed } from 'react-hotkeys-hook'
|
|
177
|
-
|
|
178
|
-
isHotkeyPressed('esc') // Returns true if Escape key is pressed down.
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
You can also check for multiple keys at the same time:
|
|
182
|
-
|
|
183
|
-
```ts
|
|
184
|
-
isHotkeyPressed(['esc', 'ctrl+s']) // Returns true if Escape or Ctrl+S are pressed down.
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## Support
|
|
188
|
-
|
|
189
|
-
* Ask your question in the [Github Discussions]([Support](https://github.com/JohannesKlauss/react-hotkeys-hook/discussions))
|
|
190
|
-
* Ask your question on [StackOverflow](https://stackoverflow.com/search?page=1&tab=Relevance&q=react-hotkeys-hook)
|
|
191
|
-
|
|
192
|
-
## Found an issue or have a feature request?
|
|
193
|
-
|
|
194
|
-
Open up an [issue](https://github.com/JohannesKlauss/react-hotkeys-hook/issues/new)
|
|
195
|
-
or [pull request](https://github.com/JohannesKlauss/react-hotkeys-hook/compare) and participate.
|
|
196
|
-
|
|
197
|
-
## Local Development
|
|
198
|
-
|
|
199
|
-
Checkout this repo, run `yarn` or `npm i` and then run the `test` script to test the behavior of the hook.
|
|
200
|
-
|
|
201
|
-
## Contributing
|
|
202
|
-
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
|
|
203
|
-
|
|
204
|
-
1. Fork the Project
|
|
205
|
-
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
|
|
206
|
-
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
|
|
207
|
-
4. Push to the Branch (`git push origin feature/AmazingFeature`)
|
|
208
|
-
5. Open a Pull Request
|
|
209
|
-
|
|
210
|
-
## License
|
|
211
|
-
Distributed under the MIT License. See `LICENSE` for more information.
|
|
212
|
-
|
|
213
|
-
## Contact
|
|
214
|
-
|
|
215
|
-
Johannes Klauss - [@JohannesKlauss](https://github.com/JohannesKlauss) - klauss.johannes@gmail.com
|
|
216
|
-
|
|
217
|
-
Project Link: [https://github.com/JohannesKlauss/react-hotkeys-hook](https://github.com/JohannesKlauss/react-hotkeys-hook)
|
|
218
|
-
|
|
219
|
-
## Contributors
|
|
220
|
-
|
|
221
|
-
<a href="https://github.com/johannesklauss/react-hotkeys-hook/graphs/contributors">
|
|
222
|
-
<img src="https://contrib.rocks/image?repo=johannesklauss/react-hotkeys-hook" />
|
|
223
|
-
</a>
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { createContext as T, useContext as M, useState as A, useCallback as k, useRef as S, useLayoutEffect as z, useEffect as J } from "react";
|
|
2
|
+
import { jsx as b } from "react/jsx-runtime";
|
|
3
|
+
const j = ["shift", "alt", "meta", "mod", "ctrl", "control"], Q = {
|
|
4
|
+
esc: "escape",
|
|
5
|
+
return: "enter",
|
|
6
|
+
left: "arrowleft",
|
|
7
|
+
right: "arrowright",
|
|
8
|
+
up: "arrowup",
|
|
9
|
+
down: "arrowdown",
|
|
10
|
+
ShiftLeft: "shift",
|
|
11
|
+
ShiftRight: "shift",
|
|
12
|
+
AltLeft: "alt",
|
|
13
|
+
AltRight: "alt",
|
|
14
|
+
MetaLeft: "meta",
|
|
15
|
+
MetaRight: "meta",
|
|
16
|
+
OSLeft: "meta",
|
|
17
|
+
OSRight: "meta",
|
|
18
|
+
ControlLeft: "ctrl",
|
|
19
|
+
ControlRight: "ctrl"
|
|
20
|
+
};
|
|
21
|
+
function K(e) {
|
|
22
|
+
return (Q[e.trim()] || e.trim()).toLowerCase().replace(/key|digit|numpad/, "");
|
|
23
|
+
}
|
|
24
|
+
function D(e) {
|
|
25
|
+
return j.includes(e);
|
|
26
|
+
}
|
|
27
|
+
function H(e, n = ",") {
|
|
28
|
+
return e.toLowerCase().split(n);
|
|
29
|
+
}
|
|
30
|
+
function P(e, n = "+", r = ">", i = !1, u, f) {
|
|
31
|
+
let o = [], y = !1;
|
|
32
|
+
e = e.trim(), e.includes(r) ? (y = !0, o = e.toLocaleLowerCase().split(r).map((d) => K(d))) : o = e.toLocaleLowerCase().split(n).map((d) => K(d));
|
|
33
|
+
const c = {
|
|
34
|
+
alt: o.includes("alt"),
|
|
35
|
+
ctrl: o.includes("ctrl") || o.includes("control"),
|
|
36
|
+
shift: o.includes("shift"),
|
|
37
|
+
meta: o.includes("meta"),
|
|
38
|
+
mod: o.includes("mod"),
|
|
39
|
+
useKey: i
|
|
40
|
+
}, m = o.filter((d) => !j.includes(d));
|
|
41
|
+
return {
|
|
42
|
+
...c,
|
|
43
|
+
keys: m,
|
|
44
|
+
description: u,
|
|
45
|
+
isSequence: y,
|
|
46
|
+
hotkey: e,
|
|
47
|
+
metadata: f
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
typeof document < "u" && (document.addEventListener("keydown", (e) => {
|
|
51
|
+
e.code !== void 0 && _([K(e.code)]);
|
|
52
|
+
}), document.addEventListener("keyup", (e) => {
|
|
53
|
+
e.code !== void 0 && I([K(e.code)]);
|
|
54
|
+
})), typeof window < "u" && (window.addEventListener("blur", () => {
|
|
55
|
+
L.clear();
|
|
56
|
+
}), window.addEventListener("contextmenu", () => {
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
L.clear();
|
|
59
|
+
}, 0);
|
|
60
|
+
}));
|
|
61
|
+
const L = /* @__PURE__ */ new Set();
|
|
62
|
+
function R(e) {
|
|
63
|
+
return Array.isArray(e);
|
|
64
|
+
}
|
|
65
|
+
function U(e, n = ",") {
|
|
66
|
+
return (R(e) ? e : e.split(n)).every((i) => L.has(i.trim().toLowerCase()));
|
|
67
|
+
}
|
|
68
|
+
function _(e) {
|
|
69
|
+
const n = Array.isArray(e) ? e : [e];
|
|
70
|
+
L.has("meta") && L.forEach((r) => {
|
|
71
|
+
D(r) || L.delete(r.toLowerCase());
|
|
72
|
+
}), n.forEach((r) => {
|
|
73
|
+
L.add(r.toLowerCase());
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function I(e) {
|
|
77
|
+
const n = Array.isArray(e) ? e : [e];
|
|
78
|
+
e === "meta" ? L.clear() : n.forEach((r) => {
|
|
79
|
+
L.delete(r.toLowerCase());
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function V(e, n, r) {
|
|
83
|
+
(typeof r == "function" && r(e, n) || r === !0) && e.preventDefault();
|
|
84
|
+
}
|
|
85
|
+
function X(e, n, r) {
|
|
86
|
+
return typeof r == "function" ? r(e, n) : r === !0 || r === void 0;
|
|
87
|
+
}
|
|
88
|
+
const Y = [
|
|
89
|
+
"input",
|
|
90
|
+
"textarea",
|
|
91
|
+
"select",
|
|
92
|
+
"searchbox",
|
|
93
|
+
"slider",
|
|
94
|
+
"spinbutton",
|
|
95
|
+
"menuitem",
|
|
96
|
+
"menuitemcheckbox",
|
|
97
|
+
"menuitemradio",
|
|
98
|
+
"option",
|
|
99
|
+
"radio",
|
|
100
|
+
"textbox"
|
|
101
|
+
];
|
|
102
|
+
function Z(e) {
|
|
103
|
+
return F(e, Y);
|
|
104
|
+
}
|
|
105
|
+
function F(e, n = !1) {
|
|
106
|
+
const { target: r, composed: i } = e;
|
|
107
|
+
let u, f;
|
|
108
|
+
return ee(r) && i ? (u = e.composedPath()[0] && e.composedPath()[0].tagName, f = e.composedPath()[0] && e.composedPath()[0].role) : (u = r && r.tagName, f = r && r.role), R(n) ? !!(u && n && n.some((o) => o.toLowerCase() === u.toLowerCase() || o === f)) : !!(u && n && n);
|
|
109
|
+
}
|
|
110
|
+
function ee(e) {
|
|
111
|
+
return !!e.tagName && !e.tagName.startsWith("-") && e.tagName.includes("-");
|
|
112
|
+
}
|
|
113
|
+
function te(e, n) {
|
|
114
|
+
return e.length === 0 && n ? !1 : n ? e.some((r) => n.includes(r)) || e.includes("*") : !0;
|
|
115
|
+
}
|
|
116
|
+
const re = (e, n, r = !1) => {
|
|
117
|
+
const { alt: i, meta: u, mod: f, shift: o, ctrl: y, keys: c, useKey: m } = n, { code: d, key: t, ctrlKey: a, metaKey: l, shiftKey: h, altKey: w } = e, p = K(d);
|
|
118
|
+
if (m) {
|
|
119
|
+
if (c?.length === 1 && c.includes(t.toLowerCase())) {
|
|
120
|
+
if (!r) {
|
|
121
|
+
if (i !== w || o !== h) return !1;
|
|
122
|
+
if (f) {
|
|
123
|
+
if (!l && !a) return !1;
|
|
124
|
+
} else if (u !== l || y !== a) return !1;
|
|
125
|
+
}
|
|
126
|
+
return !0;
|
|
127
|
+
}
|
|
128
|
+
return !1;
|
|
129
|
+
}
|
|
130
|
+
if (!c?.includes(p) && !["ctrl", "control", "unknown", "meta", "alt", "shift", "os"].includes(p))
|
|
131
|
+
return !1;
|
|
132
|
+
if (!r) {
|
|
133
|
+
if (i !== w && p !== "alt" || o !== h && p !== "shift")
|
|
134
|
+
return !1;
|
|
135
|
+
if (f) {
|
|
136
|
+
if (!l && !a)
|
|
137
|
+
return !1;
|
|
138
|
+
} else if (u !== l && p !== "meta" && p !== "os" || y !== a && p !== "ctrl" && p !== "control")
|
|
139
|
+
return !1;
|
|
140
|
+
}
|
|
141
|
+
return c && c.length === 1 && c.includes(p) ? !0 : c && c.length > 0 ? c.includes(p) ? U(c) : !1 : !c || c.length === 0;
|
|
142
|
+
}, $ = T(void 0), ne = () => M($);
|
|
143
|
+
function oe({ addHotkey: e, removeHotkey: n, children: r }) {
|
|
144
|
+
return /* @__PURE__ */ b($.Provider, { value: { addHotkey: e, removeHotkey: n }, children: r });
|
|
145
|
+
}
|
|
146
|
+
function x(e, n) {
|
|
147
|
+
return e && n && typeof e == "object" && typeof n == "object" ? Object.keys(e).length === Object.keys(n).length && // @ts-expect-error TS7053
|
|
148
|
+
Object.keys(e).reduce((r, i) => r && x(e[i], n[i]), !0) : e === n;
|
|
149
|
+
}
|
|
150
|
+
const W = T({
|
|
151
|
+
hotkeys: [],
|
|
152
|
+
activeScopes: [],
|
|
153
|
+
// This array has to be empty instead of containing '*' as default, to check if the provider is set or not
|
|
154
|
+
toggleScope: () => {
|
|
155
|
+
},
|
|
156
|
+
enableScope: () => {
|
|
157
|
+
},
|
|
158
|
+
disableScope: () => {
|
|
159
|
+
}
|
|
160
|
+
}), se = () => M(W), fe = ({ initiallyActiveScopes: e = ["*"], children: n }) => {
|
|
161
|
+
const [r, i] = A(e), [u, f] = A([]), o = k((t) => {
|
|
162
|
+
i((a) => a.includes("*") ? [t] : Array.from(/* @__PURE__ */ new Set([...a, t])));
|
|
163
|
+
}, []), y = k((t) => {
|
|
164
|
+
i((a) => a.filter((l) => l !== t));
|
|
165
|
+
}, []), c = k((t) => {
|
|
166
|
+
i((a) => a.includes(t) ? a.filter((l) => l !== t) : a.includes("*") ? [t] : Array.from(/* @__PURE__ */ new Set([...a, t])));
|
|
167
|
+
}, []), m = k((t) => {
|
|
168
|
+
f((a) => [...a, t]);
|
|
169
|
+
}, []), d = k((t) => {
|
|
170
|
+
f((a) => a.filter((l) => !x(l, t)));
|
|
171
|
+
}, []);
|
|
172
|
+
return /* @__PURE__ */ b(
|
|
173
|
+
W.Provider,
|
|
174
|
+
{
|
|
175
|
+
value: { activeScopes: r, hotkeys: u, enableScope: o, disableScope: y, toggleScope: c },
|
|
176
|
+
children: /* @__PURE__ */ b(oe, { addHotkey: m, removeHotkey: d, children: n })
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
};
|
|
180
|
+
function ie(e) {
|
|
181
|
+
const n = S(void 0);
|
|
182
|
+
return x(n.current, e) || (n.current = e), n.current;
|
|
183
|
+
}
|
|
184
|
+
const N = (e) => {
|
|
185
|
+
e.stopPropagation(), e.preventDefault(), e.stopImmediatePropagation();
|
|
186
|
+
}, ue = typeof window < "u" ? z : J;
|
|
187
|
+
function de(e, n, r, i) {
|
|
188
|
+
const u = S(null), f = S(!1), o = Array.isArray(r) ? Array.isArray(i) ? void 0 : i : r, y = R(e) ? e.join(o?.delimiter) : e, c = Array.isArray(r) ? r : Array.isArray(i) ? i : void 0, m = k(n, c ?? []), d = S(m);
|
|
189
|
+
c ? d.current = m : d.current = n;
|
|
190
|
+
const t = ie(o), { activeScopes: a } = se(), l = ne();
|
|
191
|
+
return ue(() => {
|
|
192
|
+
if (t?.enabled === !1 || !te(a, t?.scopes))
|
|
193
|
+
return;
|
|
194
|
+
let h = [], w;
|
|
195
|
+
const p = (s, B = !1) => {
|
|
196
|
+
if (!(Z(s) && !F(s, t?.enableOnFormTags))) {
|
|
197
|
+
if (u.current !== null) {
|
|
198
|
+
const v = u.current.getRootNode();
|
|
199
|
+
if ((v instanceof Document || v instanceof ShadowRoot) && v.activeElement !== u.current && !u.current.contains(v.activeElement)) {
|
|
200
|
+
N(s);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
s.target?.isContentEditable && !t?.enableOnContentEditable || H(y, t?.delimiter).forEach((v) => {
|
|
205
|
+
if (v.includes(t?.splitKey ?? "+") && v.includes(t?.sequenceSplitKey ?? ">")) {
|
|
206
|
+
console.warn(
|
|
207
|
+
`Hotkey ${v} contains both ${t?.splitKey ?? "+"} and ${t?.sequenceSplitKey ?? ">"} which is not supported.`
|
|
208
|
+
);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const g = P(
|
|
212
|
+
v,
|
|
213
|
+
t?.splitKey,
|
|
214
|
+
t?.sequenceSplitKey,
|
|
215
|
+
t?.useKey,
|
|
216
|
+
t?.description,
|
|
217
|
+
t?.metadata
|
|
218
|
+
);
|
|
219
|
+
if (g.isSequence) {
|
|
220
|
+
w = setTimeout(() => {
|
|
221
|
+
h = [];
|
|
222
|
+
}, t?.sequenceTimeoutMs ?? 1e3);
|
|
223
|
+
const C = g.useKey ? s.key : K(s.code);
|
|
224
|
+
if (D(C.toLowerCase()))
|
|
225
|
+
return;
|
|
226
|
+
h.push(C);
|
|
227
|
+
const G = g.keys?.[h.length - 1];
|
|
228
|
+
if (C !== G) {
|
|
229
|
+
h = [], w && clearTimeout(w);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
h.length === g.keys?.length && (d.current(s, g), w && clearTimeout(w), h = []);
|
|
233
|
+
} else if (re(s, g, t?.ignoreModifiers) || g.keys?.includes("*")) {
|
|
234
|
+
if (t?.ignoreEventWhen?.(s) || B && f.current)
|
|
235
|
+
return;
|
|
236
|
+
if (V(s, g, t?.preventDefault), !X(s, g, t?.enabled)) {
|
|
237
|
+
N(s);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
d.current(s, g), B || (f.current = !0);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}, O = (s) => {
|
|
245
|
+
s.code !== void 0 && (_(K(s.code)), (t?.keydown === void 0 && t?.keyup !== !0 || t?.keydown) && p(s));
|
|
246
|
+
}, q = (s) => {
|
|
247
|
+
s.code !== void 0 && (I(K(s.code)), f.current = !1, t?.keyup && p(s, !0));
|
|
248
|
+
}, E = u.current || o?.document || document;
|
|
249
|
+
return E.addEventListener("keyup", q, o?.eventListenerOptions), E.addEventListener("keydown", O, o?.eventListenerOptions), l && H(y, t?.delimiter).forEach((s) => {
|
|
250
|
+
l.addHotkey(
|
|
251
|
+
P(
|
|
252
|
+
s,
|
|
253
|
+
t?.splitKey,
|
|
254
|
+
t?.sequenceSplitKey,
|
|
255
|
+
t?.useKey,
|
|
256
|
+
t?.description,
|
|
257
|
+
t?.metadata
|
|
258
|
+
)
|
|
259
|
+
);
|
|
260
|
+
}), () => {
|
|
261
|
+
E.removeEventListener("keyup", q, o?.eventListenerOptions), E.removeEventListener("keydown", O, o?.eventListenerOptions), l && H(y, t?.delimiter).forEach((s) => {
|
|
262
|
+
l.removeHotkey(
|
|
263
|
+
P(
|
|
264
|
+
s,
|
|
265
|
+
t?.splitKey,
|
|
266
|
+
t?.sequenceSplitKey,
|
|
267
|
+
t?.useKey,
|
|
268
|
+
t?.description,
|
|
269
|
+
t?.metadata
|
|
270
|
+
)
|
|
271
|
+
);
|
|
272
|
+
}), h = [], w && clearTimeout(w);
|
|
273
|
+
};
|
|
274
|
+
}, [y, t, a]), u;
|
|
275
|
+
}
|
|
276
|
+
function le(e = !1) {
|
|
277
|
+
const [n, r] = A(/* @__PURE__ */ new Set()), [i, u] = A(!1), f = k(
|
|
278
|
+
(m) => {
|
|
279
|
+
m.code !== void 0 && (m.preventDefault(), m.stopPropagation(), r((d) => {
|
|
280
|
+
const t = new Set(d);
|
|
281
|
+
return t.add(K(e ? m.key : m.code)), t;
|
|
282
|
+
}));
|
|
283
|
+
},
|
|
284
|
+
[e]
|
|
285
|
+
), o = k(() => {
|
|
286
|
+
typeof document < "u" && (document.removeEventListener("keydown", f), u(!1));
|
|
287
|
+
}, [f]), y = k(() => {
|
|
288
|
+
r(/* @__PURE__ */ new Set()), typeof document < "u" && (o(), document.addEventListener("keydown", f), u(!0));
|
|
289
|
+
}, [f, o]), c = k(() => {
|
|
290
|
+
r(/* @__PURE__ */ new Set());
|
|
291
|
+
}, []);
|
|
292
|
+
return [n, { start: y, stop: o, resetKeys: c, isRecording: i }];
|
|
293
|
+
}
|
|
294
|
+
export {
|
|
295
|
+
fe as HotkeysProvider,
|
|
296
|
+
U as isHotkeyPressed,
|
|
297
|
+
de as useHotkeys,
|
|
298
|
+
se as useHotkeysContext,
|
|
299
|
+
le as useRecordHotkeys
|
|
300
|
+
};
|
|
@@ -2,4 +2,4 @@ import { Hotkey } from './types';
|
|
|
2
2
|
export declare function mapCode(key: string): string;
|
|
3
3
|
export declare function isHotkeyModifier(key: string): boolean;
|
|
4
4
|
export declare function parseKeysHookInput(keys: string, delimiter?: string): string[];
|
|
5
|
-
export declare function parseHotkey(hotkey: string, splitKey?: string, sequenceSplitKey?: string, useKey?: boolean, description?: string): Hotkey;
|
|
5
|
+
export declare function parseHotkey(hotkey: string, splitKey?: string, sequenceSplitKey?: string, useKey?: boolean, description?: string, metadata?: Record<string, unknown>): Hotkey;
|
|
@@ -22,6 +22,7 @@ export type Hotkey = KeyboardModifiers & {
|
|
|
22
22
|
description?: string;
|
|
23
23
|
isSequence?: boolean;
|
|
24
24
|
hotkey: string;
|
|
25
|
+
metadata?: Record<string, unknown>;
|
|
25
26
|
};
|
|
26
27
|
export type HotkeysEvent = Hotkey;
|
|
27
28
|
export type HotkeyCallback = (keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => void;
|
|
@@ -44,5 +45,6 @@ export type Options = {
|
|
|
44
45
|
useKey?: boolean;
|
|
45
46
|
sequenceTimeoutMs?: number;
|
|
46
47
|
sequenceSplitKey?: string;
|
|
48
|
+
metadata?: Record<string, unknown>;
|
|
47
49
|
};
|
|
48
50
|
export type OptionsOrDependencyArray = Options | DependencyList;
|
package/package.json
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-hotkeys-hook",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "5.2.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "vite",
|
|
7
|
+
"build": "tsc && vite build",
|
|
8
|
+
"test": "vitest",
|
|
9
|
+
"prepublishOnly": "vitest run && npm run build"
|
|
10
|
+
},
|
|
6
11
|
"repository": {
|
|
7
12
|
"type": "git",
|
|
8
13
|
"url": "https://github.com/JohannesKlauss/react-keymap-hook.git"
|
|
9
14
|
},
|
|
10
|
-
"homepage": "https://
|
|
15
|
+
"homepage": "https://react-hotkeys-hook.vercel.app/",
|
|
11
16
|
"author": "Johannes Klauss",
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"types": "packages/react-hotkeys-hook/dist/index.d.ts",
|
|
17
|
+
"main": "dist/index.js",
|
|
18
|
+
"types": "dist/index.d.ts",
|
|
15
19
|
"files": [
|
|
16
|
-
"
|
|
20
|
+
"dist"
|
|
17
21
|
],
|
|
18
22
|
"keywords": [
|
|
19
23
|
"react",
|
|
@@ -28,22 +32,24 @@
|
|
|
28
32
|
"hotkeys"
|
|
29
33
|
],
|
|
30
34
|
"license": "MIT",
|
|
31
|
-
"scripts": {
|
|
32
|
-
"build": "npm run -w packages/react-hotkeys-hook build",
|
|
33
|
-
"build:documentation": "npm run -w packages/documentation build",
|
|
34
|
-
"test": "npm run -w packages/react-hotkeys-hook test",
|
|
35
|
-
"prepublishOnly": "npm run test && npm run lint && npm run build",
|
|
36
|
-
"format": "npx @biomejs/biome format --write packages",
|
|
37
|
-
"lint": "npx @biomejs/biome lint --write packages"
|
|
38
|
-
},
|
|
39
|
-
"workspaces": [
|
|
40
|
-
"packages/*"
|
|
41
|
-
],
|
|
42
35
|
"peerDependencies": {
|
|
43
36
|
"react": ">=16.8.0",
|
|
44
37
|
"react-dom": ">=16.8.0"
|
|
45
38
|
},
|
|
46
39
|
"devDependencies": {
|
|
47
|
-
"@
|
|
40
|
+
"@eslint/js": "9.39.2",
|
|
41
|
+
"@types/react": "19.2.8",
|
|
42
|
+
"@types/react-dom": "19.2.3",
|
|
43
|
+
"@vitejs/plugin-react": "5.1.2",
|
|
44
|
+
"@testing-library/jest-dom": "6.9.1",
|
|
45
|
+
"@testing-library/react": "16.3.1",
|
|
46
|
+
"@testing-library/user-event": "14.6.1",
|
|
47
|
+
"@types/node": "24.10.8",
|
|
48
|
+
"globals": "16.5.0",
|
|
49
|
+
"jsdom": "27.4.0",
|
|
50
|
+
"typescript": "5.9.3",
|
|
51
|
+
"vite": "7.3.1",
|
|
52
|
+
"vitest": "4.0.17",
|
|
53
|
+
"vite-plugin-dts": "4.5.4"
|
|
48
54
|
}
|
|
49
55
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
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.
|
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
import { createContext as T, useContext as M, useState as A, useCallback as w, useRef as S, useLayoutEffect as z, useEffect as J } from "react";
|
|
2
|
-
import { jsx as b } from "react/jsx-runtime";
|
|
3
|
-
const j = ["shift", "alt", "meta", "mod", "ctrl", "control"], Q = {
|
|
4
|
-
esc: "escape",
|
|
5
|
-
return: "enter",
|
|
6
|
-
left: "arrowleft",
|
|
7
|
-
right: "arrowright",
|
|
8
|
-
up: "arrowup",
|
|
9
|
-
down: "arrowdown",
|
|
10
|
-
ShiftLeft: "shift",
|
|
11
|
-
ShiftRight: "shift",
|
|
12
|
-
AltLeft: "alt",
|
|
13
|
-
AltRight: "alt",
|
|
14
|
-
MetaLeft: "meta",
|
|
15
|
-
MetaRight: "meta",
|
|
16
|
-
OSLeft: "meta",
|
|
17
|
-
OSRight: "meta",
|
|
18
|
-
ControlLeft: "ctrl",
|
|
19
|
-
ControlRight: "ctrl"
|
|
20
|
-
};
|
|
21
|
-
function K(e) {
|
|
22
|
-
return (Q[e.trim()] || e.trim()).toLowerCase().replace(/key|digit|numpad/, "");
|
|
23
|
-
}
|
|
24
|
-
function D(e) {
|
|
25
|
-
return j.includes(e);
|
|
26
|
-
}
|
|
27
|
-
function H(e, r = ",") {
|
|
28
|
-
return e.toLowerCase().split(r);
|
|
29
|
-
}
|
|
30
|
-
function P(e, r = "+", o = ">", i = !1, u) {
|
|
31
|
-
let n = [], c = !1;
|
|
32
|
-
e = e.trim(), e.includes(o) ? (c = !0, n = e.toLocaleLowerCase().split(o).map((f) => K(f))) : n = e.toLocaleLowerCase().split(r).map((f) => K(f));
|
|
33
|
-
const y = {
|
|
34
|
-
alt: n.includes("alt"),
|
|
35
|
-
ctrl: n.includes("ctrl") || n.includes("control"),
|
|
36
|
-
shift: n.includes("shift"),
|
|
37
|
-
meta: n.includes("meta"),
|
|
38
|
-
mod: n.includes("mod"),
|
|
39
|
-
useKey: i
|
|
40
|
-
}, d = n.filter((f) => !j.includes(f));
|
|
41
|
-
return {
|
|
42
|
-
...y,
|
|
43
|
-
keys: d,
|
|
44
|
-
description: u,
|
|
45
|
-
isSequence: c,
|
|
46
|
-
hotkey: e
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
typeof document < "u" && (document.addEventListener("keydown", (e) => {
|
|
50
|
-
e.code !== void 0 && I([K(e.code)]);
|
|
51
|
-
}), document.addEventListener("keyup", (e) => {
|
|
52
|
-
e.code !== void 0 && _([K(e.code)]);
|
|
53
|
-
})), typeof window < "u" && (window.addEventListener("blur", () => {
|
|
54
|
-
L.clear();
|
|
55
|
-
}), window.addEventListener("contextmenu", () => {
|
|
56
|
-
setTimeout(() => {
|
|
57
|
-
L.clear();
|
|
58
|
-
}, 0);
|
|
59
|
-
}));
|
|
60
|
-
const L = /* @__PURE__ */ new Set();
|
|
61
|
-
function R(e) {
|
|
62
|
-
return Array.isArray(e);
|
|
63
|
-
}
|
|
64
|
-
function U(e, r = ",") {
|
|
65
|
-
return (R(e) ? e : e.split(r)).every((i) => L.has(i.trim().toLowerCase()));
|
|
66
|
-
}
|
|
67
|
-
function I(e) {
|
|
68
|
-
const r = Array.isArray(e) ? e : [e];
|
|
69
|
-
L.has("meta") && L.forEach((o) => !D(o) && L.delete(o.toLowerCase())), r.forEach((o) => L.add(o.toLowerCase()));
|
|
70
|
-
}
|
|
71
|
-
function _(e) {
|
|
72
|
-
const r = Array.isArray(e) ? e : [e];
|
|
73
|
-
e === "meta" ? L.clear() : r.forEach((o) => L.delete(o.toLowerCase()));
|
|
74
|
-
}
|
|
75
|
-
function V(e, r, o) {
|
|
76
|
-
(typeof o == "function" && o(e, r) || o === !0) && e.preventDefault();
|
|
77
|
-
}
|
|
78
|
-
function X(e, r, o) {
|
|
79
|
-
return typeof o == "function" ? o(e, r) : o === !0 || o === void 0;
|
|
80
|
-
}
|
|
81
|
-
const Y = [
|
|
82
|
-
"input",
|
|
83
|
-
"textarea",
|
|
84
|
-
"select",
|
|
85
|
-
"searchbox",
|
|
86
|
-
"slider",
|
|
87
|
-
"spinbutton",
|
|
88
|
-
"menuitem",
|
|
89
|
-
"menuitemcheckbox",
|
|
90
|
-
"menuitemradio",
|
|
91
|
-
"option",
|
|
92
|
-
"radio",
|
|
93
|
-
"textbox"
|
|
94
|
-
];
|
|
95
|
-
function Z(e) {
|
|
96
|
-
return F(e, Y);
|
|
97
|
-
}
|
|
98
|
-
function F(e, r = !1) {
|
|
99
|
-
const { target: o, composed: i } = e;
|
|
100
|
-
let u, n;
|
|
101
|
-
return ee(o) && i ? (u = e.composedPath()[0] && e.composedPath()[0].tagName, n = e.composedPath()[0] && e.composedPath()[0].role) : (u = o && o.tagName, n = o && o.role), R(r) ? !!(u && r && r.some((c) => c.toLowerCase() === u.toLowerCase() || c === n)) : !!(u && r && r);
|
|
102
|
-
}
|
|
103
|
-
function ee(e) {
|
|
104
|
-
return !!e.tagName && !e.tagName.startsWith("-") && e.tagName.includes("-");
|
|
105
|
-
}
|
|
106
|
-
function te(e, r) {
|
|
107
|
-
return e.length === 0 && r ? (console.warn(
|
|
108
|
-
'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>'
|
|
109
|
-
), !0) : r ? e.some((o) => r.includes(o)) || e.includes("*") : !0;
|
|
110
|
-
}
|
|
111
|
-
const re = (e, r, o = !1) => {
|
|
112
|
-
const { alt: i, meta: u, mod: n, shift: c, ctrl: y, keys: d, useKey: f } = r, { code: p, key: t, ctrlKey: a, metaKey: l, shiftKey: g, altKey: k } = e, m = K(p);
|
|
113
|
-
if (f && d?.length === 1 && d.includes(t))
|
|
114
|
-
return !0;
|
|
115
|
-
if (!d?.includes(m) && !["ctrl", "control", "unknown", "meta", "alt", "shift", "os"].includes(m))
|
|
116
|
-
return !1;
|
|
117
|
-
if (!o) {
|
|
118
|
-
if (i !== k && m !== "alt" || c !== g && m !== "shift")
|
|
119
|
-
return !1;
|
|
120
|
-
if (n) {
|
|
121
|
-
if (!l && !a)
|
|
122
|
-
return !1;
|
|
123
|
-
} else if (u !== l && m !== "meta" && m !== "os" || y !== a && m !== "ctrl" && m !== "control")
|
|
124
|
-
return !1;
|
|
125
|
-
}
|
|
126
|
-
return d && d.length === 1 && d.includes(m) ? !0 : d ? U(d) : !d;
|
|
127
|
-
}, $ = T(void 0), oe = () => M($);
|
|
128
|
-
function ne({ addHotkey: e, removeHotkey: r, children: o }) {
|
|
129
|
-
return /* @__PURE__ */ b($.Provider, { value: { addHotkey: e, removeHotkey: r }, children: o });
|
|
130
|
-
}
|
|
131
|
-
function x(e, r) {
|
|
132
|
-
return e && r && typeof e == "object" && typeof r == "object" ? Object.keys(e).length === Object.keys(r).length && // @ts-expect-error TS7053
|
|
133
|
-
Object.keys(e).reduce((o, i) => o && x(e[i], r[i]), !0) : e === r;
|
|
134
|
-
}
|
|
135
|
-
const W = T({
|
|
136
|
-
hotkeys: [],
|
|
137
|
-
activeScopes: [],
|
|
138
|
-
// This array has to be empty instead of containing '*' as default, to check if the provider is set or not
|
|
139
|
-
toggleScope: () => {
|
|
140
|
-
},
|
|
141
|
-
enableScope: () => {
|
|
142
|
-
},
|
|
143
|
-
disableScope: () => {
|
|
144
|
-
}
|
|
145
|
-
}), se = () => M(W), de = ({ initiallyActiveScopes: e = ["*"], children: r }) => {
|
|
146
|
-
const [o, i] = A(e), [u, n] = A([]), c = w((t) => {
|
|
147
|
-
i((a) => a.includes("*") ? [t] : Array.from(/* @__PURE__ */ new Set([...a, t])));
|
|
148
|
-
}, []), y = w((t) => {
|
|
149
|
-
i((a) => a.filter((l) => l !== t));
|
|
150
|
-
}, []), d = w((t) => {
|
|
151
|
-
i((a) => a.includes(t) ? a.filter((l) => l !== t) : a.includes("*") ? [t] : Array.from(/* @__PURE__ */ new Set([...a, t])));
|
|
152
|
-
}, []), f = w((t) => {
|
|
153
|
-
n((a) => [...a, t]);
|
|
154
|
-
}, []), p = w((t) => {
|
|
155
|
-
n((a) => a.filter((l) => !x(l, t)));
|
|
156
|
-
}, []);
|
|
157
|
-
return /* @__PURE__ */ b(
|
|
158
|
-
W.Provider,
|
|
159
|
-
{
|
|
160
|
-
value: { activeScopes: o, hotkeys: u, enableScope: c, disableScope: y, toggleScope: d },
|
|
161
|
-
children: /* @__PURE__ */ b(ne, { addHotkey: f, removeHotkey: p, children: r })
|
|
162
|
-
}
|
|
163
|
-
);
|
|
164
|
-
};
|
|
165
|
-
function ie(e) {
|
|
166
|
-
const r = S(void 0);
|
|
167
|
-
return x(r.current, e) || (r.current = e), r.current;
|
|
168
|
-
}
|
|
169
|
-
const N = (e) => {
|
|
170
|
-
e.stopPropagation(), e.preventDefault(), e.stopImmediatePropagation();
|
|
171
|
-
}, ue = typeof window < "u" ? z : J;
|
|
172
|
-
function fe(e, r, o, i) {
|
|
173
|
-
const u = S(null), n = S(!1), c = Array.isArray(o) ? Array.isArray(i) ? void 0 : i : o, y = R(e) ? e.join(c?.delimiter) : e, d = Array.isArray(o) ? o : Array.isArray(i) ? i : void 0, f = w(r, d ?? []), p = S(f);
|
|
174
|
-
d ? p.current = f : p.current = r;
|
|
175
|
-
const t = ie(c), { activeScopes: a } = se(), l = oe();
|
|
176
|
-
return ue(() => {
|
|
177
|
-
if (t?.enabled === !1 || !te(a, t?.scopes))
|
|
178
|
-
return;
|
|
179
|
-
let g = [], k;
|
|
180
|
-
const m = (s, B = !1) => {
|
|
181
|
-
if (!(Z(s) && !F(s, t?.enableOnFormTags))) {
|
|
182
|
-
if (u.current !== null) {
|
|
183
|
-
const v = u.current.getRootNode();
|
|
184
|
-
if ((v instanceof Document || v instanceof ShadowRoot) && v.activeElement !== u.current && !u.current.contains(v.activeElement)) {
|
|
185
|
-
N(s);
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
s.target?.isContentEditable && !t?.enableOnContentEditable || H(y, t?.delimiter).forEach((v) => {
|
|
190
|
-
if (v.includes(t?.splitKey ?? "+") && v.includes(t?.sequenceSplitKey ?? ">")) {
|
|
191
|
-
console.warn(
|
|
192
|
-
`Hotkey ${v} contains both ${t?.splitKey ?? "+"} and ${t?.sequenceSplitKey ?? ">"} which is not supported.`
|
|
193
|
-
);
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
const h = P(
|
|
197
|
-
v,
|
|
198
|
-
t?.splitKey,
|
|
199
|
-
t?.sequenceSplitKey,
|
|
200
|
-
t?.useKey,
|
|
201
|
-
t?.description
|
|
202
|
-
);
|
|
203
|
-
if (h.isSequence) {
|
|
204
|
-
k = setTimeout(() => {
|
|
205
|
-
g = [];
|
|
206
|
-
}, t?.sequenceTimeoutMs ?? 1e3);
|
|
207
|
-
const C = h.useKey ? s.key : K(s.code);
|
|
208
|
-
if (D(C.toLowerCase()))
|
|
209
|
-
return;
|
|
210
|
-
g.push(C);
|
|
211
|
-
const G = h.keys?.[g.length - 1];
|
|
212
|
-
if (C !== G) {
|
|
213
|
-
g = [], k && clearTimeout(k);
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
g.length === h.keys?.length && (p.current(s, h), k && clearTimeout(k), g = []);
|
|
217
|
-
} else if (re(s, h, t?.ignoreModifiers) || h.keys?.includes("*")) {
|
|
218
|
-
if (t?.ignoreEventWhen?.(s) || B && n.current)
|
|
219
|
-
return;
|
|
220
|
-
if (V(s, h, t?.preventDefault), !X(s, h, t?.enabled)) {
|
|
221
|
-
N(s);
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
p.current(s, h), B || (n.current = !0);
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
}, O = (s) => {
|
|
229
|
-
s.code !== void 0 && (I(K(s.code)), (t?.keydown === void 0 && t?.keyup !== !0 || t?.keydown) && m(s));
|
|
230
|
-
}, q = (s) => {
|
|
231
|
-
s.code !== void 0 && (_(K(s.code)), n.current = !1, t?.keyup && m(s, !0));
|
|
232
|
-
}, E = u.current || c?.document || document;
|
|
233
|
-
return E.addEventListener("keyup", q, c?.eventListenerOptions), E.addEventListener("keydown", O, c?.eventListenerOptions), l && H(y, t?.delimiter).forEach(
|
|
234
|
-
(s) => l.addHotkey(
|
|
235
|
-
P(
|
|
236
|
-
s,
|
|
237
|
-
t?.splitKey,
|
|
238
|
-
t?.sequenceSplitKey,
|
|
239
|
-
t?.useKey,
|
|
240
|
-
t?.description
|
|
241
|
-
)
|
|
242
|
-
)
|
|
243
|
-
), () => {
|
|
244
|
-
E.removeEventListener("keyup", q, c?.eventListenerOptions), E.removeEventListener("keydown", O, c?.eventListenerOptions), l && H(y, t?.delimiter).forEach(
|
|
245
|
-
(s) => l.removeHotkey(
|
|
246
|
-
P(
|
|
247
|
-
s,
|
|
248
|
-
t?.splitKey,
|
|
249
|
-
t?.sequenceSplitKey,
|
|
250
|
-
t?.useKey,
|
|
251
|
-
t?.description
|
|
252
|
-
)
|
|
253
|
-
)
|
|
254
|
-
), g = [], k && clearTimeout(k);
|
|
255
|
-
};
|
|
256
|
-
}, [y, t, a]), u;
|
|
257
|
-
}
|
|
258
|
-
function le(e = !1) {
|
|
259
|
-
const [r, o] = A(/* @__PURE__ */ new Set()), [i, u] = A(!1), n = w(
|
|
260
|
-
(f) => {
|
|
261
|
-
f.code !== void 0 && (f.preventDefault(), f.stopPropagation(), o((p) => {
|
|
262
|
-
const t = new Set(p);
|
|
263
|
-
return t.add(K(e ? f.key : f.code)), t;
|
|
264
|
-
}));
|
|
265
|
-
},
|
|
266
|
-
[e]
|
|
267
|
-
), c = w(() => {
|
|
268
|
-
typeof document < "u" && (document.removeEventListener("keydown", n), u(!1));
|
|
269
|
-
}, [n]), y = w(() => {
|
|
270
|
-
o(/* @__PURE__ */ new Set()), typeof document < "u" && (c(), document.addEventListener("keydown", n), u(!0));
|
|
271
|
-
}, [n, c]), d = w(() => {
|
|
272
|
-
o(/* @__PURE__ */ new Set());
|
|
273
|
-
}, []);
|
|
274
|
-
return [r, { start: y, stop: c, resetKeys: d, isRecording: i }];
|
|
275
|
-
}
|
|
276
|
-
export {
|
|
277
|
-
de as HotkeysProvider,
|
|
278
|
-
U as isHotkeyPressed,
|
|
279
|
-
fe as useHotkeys,
|
|
280
|
-
se as useHotkeysContext,
|
|
281
|
-
le as useRecordHotkeys
|
|
282
|
-
};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|