react-hotkeys-hook 5.2.4 → 5.3.0

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/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/package.json CHANGED
@@ -1,23 +1,19 @@
1
1
  {
2
2
  "name": "react-hotkeys-hook",
3
- "version": "5.2.4",
4
- "type": "module",
5
- "scripts": {
6
- "dev": "vite",
7
- "build": "tsc && vite build",
8
- "test": "vitest",
9
- "prepublishOnly": "vitest run && npm run build"
10
- },
3
+ "description": "React hook for handling keyboard shortcuts",
4
+ "version": "5.3.0",
5
+ "sideEffects": false,
11
6
  "repository": {
12
7
  "type": "git",
13
8
  "url": "https://github.com/JohannesKlauss/react-keymap-hook.git"
14
9
  },
15
10
  "homepage": "https://react-hotkeys-hook.vercel.app/",
16
11
  "author": "Johannes Klauss",
17
- "main": "dist/index.js",
18
- "types": "dist/index.d.ts",
12
+ "type": "module",
13
+ "main": "packages/react-hotkeys-hook/dist/index.js",
14
+ "types": "packages/react-hotkeys-hook/dist/index.d.ts",
19
15
  "files": [
20
- "dist"
16
+ "packages/react-hotkeys-hook/dist"
21
17
  ],
22
18
  "keywords": [
23
19
  "react",
@@ -32,24 +28,22 @@
32
28
  "hotkeys"
33
29
  ],
34
30
  "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
+ ],
35
42
  "peerDependencies": {
36
43
  "react": ">=16.8.0",
37
44
  "react-dom": ">=16.8.0"
38
45
  },
39
46
  "devDependencies": {
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"
47
+ "@biomejs/biome": "2.4.13"
54
48
  }
55
49
  }
@@ -0,0 +1,94 @@
1
+ import { DependencyList } from 'react';
2
+ import { JSX } from 'react/jsx-runtime';
3
+ import { ReactNode } from 'react';
4
+ import { RefCallback } from 'react';
5
+
6
+ declare type EventListenerOptions_2 = {
7
+ capture?: boolean;
8
+ once?: boolean;
9
+ passive?: boolean;
10
+ signal?: AbortSignal;
11
+ } | boolean;
12
+
13
+ declare type FormTags = 'input' | 'textarea' | 'select' | 'INPUT' | 'TEXTAREA' | 'SELECT' | 'searchbox' | 'slider' | 'spinbutton' | 'menuitem' | 'menuitemcheckbox' | 'menuitemradio' | 'option' | 'radio' | 'textbox';
14
+
15
+ declare type Hotkey = KeyboardModifiers & {
16
+ keys?: readonly string[];
17
+ scopes?: Scopes;
18
+ description?: string;
19
+ isSequence?: boolean;
20
+ hotkey: string;
21
+ metadata?: Record<string, unknown>;
22
+ };
23
+
24
+ export declare type HotkeyCallback = (keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => void;
25
+
26
+ declare type HotkeysContextType = {
27
+ hotkeys: ReadonlyArray<Hotkey>;
28
+ activeScopes: string[];
29
+ toggleScope: (scope: string) => void;
30
+ enableScope: (scope: string) => void;
31
+ disableScope: (scope: string) => void;
32
+ };
33
+
34
+ declare type HotkeysEvent = Hotkey;
35
+
36
+ export declare const HotkeysProvider: ({ initiallyActiveScopes, children }: Props) => JSX.Element;
37
+
38
+ export declare function isHotkeyPressed(key: string | readonly string[], delimiter?: string): boolean;
39
+
40
+ declare type KeyboardModifiers = {
41
+ alt?: boolean;
42
+ ctrl?: boolean;
43
+ meta?: boolean;
44
+ shift?: boolean;
45
+ mod?: boolean;
46
+ useKey?: boolean;
47
+ };
48
+
49
+ export declare type Keys = string | readonly string[];
50
+
51
+ export declare type Options = {
52
+ enabled?: Trigger;
53
+ enableOnFormTags?: readonly FormTags[] | boolean;
54
+ enableOnContentEditable?: boolean;
55
+ ignoreEventWhen?: (e: KeyboardEvent) => boolean;
56
+ splitKey?: string;
57
+ delimiter?: string;
58
+ scopes?: Scopes;
59
+ keyup?: boolean;
60
+ keydown?: boolean;
61
+ preventDefault?: Trigger;
62
+ description?: string;
63
+ document?: Document;
64
+ ignoreModifiers?: boolean;
65
+ eventListenerOptions?: EventListenerOptions_2;
66
+ useKey?: boolean;
67
+ sequenceTimeoutMs?: number;
68
+ sequenceSplitKey?: string;
69
+ metadata?: Record<string, unknown>;
70
+ };
71
+
72
+ declare type OptionsOrDependencyArray = Options | DependencyList;
73
+
74
+ declare interface Props {
75
+ initiallyActiveScopes?: string[];
76
+ children: ReactNode;
77
+ }
78
+
79
+ declare type Scopes = string | readonly string[];
80
+
81
+ declare type Trigger = boolean | ((keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => boolean);
82
+
83
+ export declare function useHotkeys<T extends HTMLElement>(keys: Keys, callback: HotkeyCallback, options?: OptionsOrDependencyArray, dependencies?: OptionsOrDependencyArray): RefCallback<T | null>;
84
+
85
+ export declare const useHotkeysContext: () => HotkeysContextType;
86
+
87
+ export declare function useRecordHotkeys(useKey?: boolean, blacklist?: string[]): readonly [Set<string>, {
88
+ readonly start: () => void;
89
+ readonly stop: () => void;
90
+ readonly resetKeys: () => void;
91
+ readonly isRecording: boolean;
92
+ }];
93
+
94
+ export { }
@@ -0,0 +1,313 @@
1
+ import { createContext as j, useContext as W, useState as A, useCallback as h, useRef as K, useLayoutEffect as X, useEffect as I, useMemo as Y } from "react";
2
+ import { jsx as x } from "react/jsx-runtime";
3
+ const F = ["shift", "alt", "meta", "mod", "ctrl", "control"];
4
+ function Z() {
5
+ return typeof navigator > "u" ? !1 : /mac/i.test(navigator.userAgent) && !/iphone|ipad|ipod/i.test(navigator.userAgent);
6
+ }
7
+ const ee = {
8
+ esc: "escape",
9
+ return: "enter",
10
+ left: "arrowleft",
11
+ right: "arrowright",
12
+ up: "arrowup",
13
+ down: "arrowdown",
14
+ ShiftLeft: "shift",
15
+ ShiftRight: "shift",
16
+ AltLeft: "alt",
17
+ AltRight: "alt",
18
+ MetaLeft: "meta",
19
+ MetaRight: "meta",
20
+ OSLeft: "meta",
21
+ OSRight: "meta",
22
+ ControlLeft: "ctrl",
23
+ ControlRight: "ctrl"
24
+ };
25
+ function L(e) {
26
+ return (ee[e.trim()] || e.trim()).toLowerCase().replace(/key|digit|numpad/, "");
27
+ }
28
+ function $(e) {
29
+ return F.includes(e);
30
+ }
31
+ function R(e, t = ",") {
32
+ return e.toLowerCase().split(t);
33
+ }
34
+ function O(e, t = "+", r = ">", i = !1, u, f) {
35
+ let d = [], l = !1;
36
+ e = e.trim(), e.includes(r) ? (l = !0, d = e.toLocaleLowerCase().split(r).map((y) => L(y))) : d = e.toLocaleLowerCase().split(t).map((y) => L(y));
37
+ const o = {
38
+ alt: d.includes("alt"),
39
+ ctrl: d.includes("ctrl") || d.includes("control"),
40
+ shift: d.includes("shift"),
41
+ meta: d.includes("meta"),
42
+ mod: d.includes("mod"),
43
+ useKey: i
44
+ }, p = d.filter((y) => !F.includes(y));
45
+ return {
46
+ ...o,
47
+ keys: p,
48
+ description: u,
49
+ isSequence: l,
50
+ hotkey: e,
51
+ metadata: f
52
+ };
53
+ }
54
+ typeof document < "u" && (document.addEventListener("keydown", (e) => {
55
+ e.code !== void 0 && z([L(e.code)]);
56
+ }), document.addEventListener("keyup", (e) => {
57
+ e.code !== void 0 && G([L(e.code)]);
58
+ })), typeof window < "u" && (window.addEventListener("blur", () => {
59
+ g.clear();
60
+ }), window.addEventListener("focus", () => {
61
+ g.clear();
62
+ }), window.addEventListener("contextmenu", () => {
63
+ setTimeout(() => {
64
+ g.clear();
65
+ }, 0);
66
+ })), typeof document < "u" && document.addEventListener("visibilitychange", () => {
67
+ g.clear();
68
+ });
69
+ const g = /* @__PURE__ */ new Set();
70
+ function D(e) {
71
+ return Array.isArray(e);
72
+ }
73
+ function te(e, t = ",") {
74
+ return (D(e) ? e : e.split(t)).every((i) => g.has(i.trim().toLowerCase()));
75
+ }
76
+ function z(e) {
77
+ const t = Array.isArray(e) ? e : [e];
78
+ g.has("meta") && g.forEach((r) => {
79
+ $(r) || g.delete(r.toLowerCase());
80
+ }), t.forEach((r) => {
81
+ g.add(r.toLowerCase());
82
+ });
83
+ }
84
+ function G(e) {
85
+ const t = Array.isArray(e) ? e : [e];
86
+ e === "meta" ? g.clear() : t.forEach((r) => {
87
+ g.delete(r.toLowerCase());
88
+ });
89
+ }
90
+ function re(e, t, r) {
91
+ (typeof r == "function" && r(e, t) || r === !0) && e.preventDefault();
92
+ }
93
+ function ne(e, t, r) {
94
+ return typeof r == "function" ? r(e, t) : r === !0 || r === void 0;
95
+ }
96
+ const oe = [
97
+ "input",
98
+ "textarea",
99
+ "select",
100
+ "searchbox",
101
+ "slider",
102
+ "spinbutton",
103
+ "menuitem",
104
+ "menuitemcheckbox",
105
+ "menuitemradio",
106
+ "option",
107
+ "radio",
108
+ "textbox"
109
+ ];
110
+ function se(e) {
111
+ return J(e, oe);
112
+ }
113
+ function J(e, t = !1) {
114
+ const { target: r, composed: i } = e;
115
+ let u, f;
116
+ return ie(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), D(t) ? !!(u && t?.some((d) => d.toLowerCase() === u.toLowerCase() || d === f)) : !!(u && t && t);
117
+ }
118
+ function ie(e) {
119
+ return !!e.tagName && !e.tagName.startsWith("-") && e.tagName.includes("-");
120
+ }
121
+ function ue(e, t) {
122
+ return e.length === 0 && t ? !1 : t ? e.some((r) => t.includes(r)) || e.includes("*") : !0;
123
+ }
124
+ const ce = (e, t, r = !1) => {
125
+ const { alt: i, meta: u, mod: f, shift: d, ctrl: l, keys: o, useKey: p } = t, { code: y, key: s, ctrlKey: c, metaKey: n, shiftKey: E, altKey: C } = e, m = L(y);
126
+ if (p && o?.length === 1 && o.includes(s.toLowerCase()))
127
+ return !0;
128
+ if (!o?.includes(m) && !["ctrl", "control", "unknown", "meta", "alt", "shift", "os"].includes(m))
129
+ return !1;
130
+ if (!r) {
131
+ if (i !== C && m !== "alt" || d !== E && m !== "shift")
132
+ return !1;
133
+ if (f) {
134
+ if (Z() ? !n : !c)
135
+ return !1;
136
+ } else if (u !== n && m !== "meta" && m !== "os" || l !== c && m !== "ctrl" && m !== "control")
137
+ return !1;
138
+ }
139
+ return o && o.length === 1 && o.includes(m) ? !0 : o && o.length > 0 ? o.includes(m) ? te(o) : !1 : !o || o.length === 0;
140
+ }, Q = j(void 0), ae = () => W(Q);
141
+ function de({ addHotkey: e, removeHotkey: t, children: r }) {
142
+ return /* @__PURE__ */ x(Q.Provider, { value: { addHotkey: e, removeHotkey: t }, children: r });
143
+ }
144
+ function M(e, t) {
145
+ return e && t && typeof e == "object" && typeof t == "object" ? Object.keys(e).length === Object.keys(t).length && // @ts-expect-error TS7053
146
+ Object.keys(e).reduce((r, i) => r && M(e[i], t[i]), !0) : e === t;
147
+ }
148
+ const U = j({
149
+ hotkeys: [],
150
+ activeScopes: [],
151
+ // This array has to be empty instead of containing '*' as default, to check if the provider is set or not
152
+ toggleScope: () => {
153
+ },
154
+ enableScope: () => {
155
+ },
156
+ disableScope: () => {
157
+ }
158
+ }), fe = () => W(U), ve = ({ initiallyActiveScopes: e = ["*"], children: t }) => {
159
+ const [r, i] = A(e), [u, f] = A([]), d = h((s) => {
160
+ i((c) => c.includes("*") ? [s] : Array.from(/* @__PURE__ */ new Set([...c, s])));
161
+ }, []), l = h((s) => {
162
+ i((c) => c.filter((n) => n !== s));
163
+ }, []), o = h((s) => {
164
+ i((c) => c.includes(s) ? c.filter((n) => n !== s) : c.includes("*") ? [s] : Array.from(/* @__PURE__ */ new Set([...c, s])));
165
+ }, []), p = h((s) => {
166
+ f((c) => [...c, s]);
167
+ }, []), y = h((s) => {
168
+ f((c) => c.filter((n) => !M(n, s)));
169
+ }, []);
170
+ return /* @__PURE__ */ x(
171
+ U.Provider,
172
+ {
173
+ value: { activeScopes: r, hotkeys: u, enableScope: d, disableScope: l, toggleScope: o },
174
+ children: /* @__PURE__ */ x(de, { addHotkey: p, removeHotkey: y, children: t })
175
+ }
176
+ );
177
+ };
178
+ function le(e) {
179
+ const t = K(void 0);
180
+ return M(t.current, e) || (t.current = e), t.current;
181
+ }
182
+ const ye = (e) => {
183
+ e.stopPropagation(), e.preventDefault(), e.stopImmediatePropagation();
184
+ }, me = typeof window < "u" ? X : I;
185
+ function pe(e) {
186
+ if (!e) return;
187
+ const { enabled: t, preventDefault: r, ignoreEventWhen: i, ...u } = e;
188
+ return u;
189
+ }
190
+ function we(e, t, r, i) {
191
+ const [u, f] = A(null), d = h((w) => (f(w), () => f(null)), []), l = K(!1), o = Array.isArray(r) ? Array.isArray(i) ? void 0 : i : r, p = D(e) ? e.join(o?.delimiter) : e, y = Array.isArray(r) ? r : Array.isArray(i) ? i : void 0, s = h(t, y ?? []), c = K(s);
192
+ y ? c.current = s : c.current = t;
193
+ const n = le(pe(o)), E = K(o?.enabled);
194
+ E.current = o?.enabled;
195
+ const C = K(o?.preventDefault);
196
+ C.current = o?.preventDefault;
197
+ const m = K(o?.ignoreEventWhen);
198
+ m.current = o?.ignoreEventWhen;
199
+ const { activeScopes: T } = fe(), b = ae();
200
+ return me(() => {
201
+ if (E.current === !1 || !ue(T, n?.scopes))
202
+ return;
203
+ let w = [], S;
204
+ const q = (a, _ = !1) => {
205
+ if (!(se(a) && !J(a, n?.enableOnFormTags))) {
206
+ if (u !== null) {
207
+ const k = u.getRootNode();
208
+ if ((k instanceof Document || k instanceof ShadowRoot) && k.activeElement !== u && !u.contains(k.activeElement)) {
209
+ ye(a);
210
+ return;
211
+ }
212
+ }
213
+ a.target?.isContentEditable && !n?.enableOnContentEditable || R(p, n?.delimiter).forEach((k) => {
214
+ if (k.includes(n?.splitKey ?? "+") && k.includes(n?.sequenceSplitKey ?? ">")) {
215
+ console.warn(
216
+ `Hotkey ${k} contains both ${n?.splitKey ?? "+"} and ${n?.sequenceSplitKey ?? ">"} which is not supported.`
217
+ );
218
+ return;
219
+ }
220
+ const v = O(
221
+ k,
222
+ n?.splitKey,
223
+ n?.sequenceSplitKey,
224
+ n?.useKey,
225
+ n?.description,
226
+ n?.metadata
227
+ );
228
+ if (v.isSequence) {
229
+ S = setTimeout(() => {
230
+ w = [];
231
+ }, n?.sequenceTimeoutMs ?? 1e3);
232
+ const P = v.useKey ? a.key : L(a.code);
233
+ if ($(P.toLowerCase()))
234
+ return;
235
+ w.push(P);
236
+ const V = v.keys?.[w.length - 1];
237
+ if (P !== V) {
238
+ w = [], S && clearTimeout(S);
239
+ return;
240
+ }
241
+ w.length === v.keys?.length && (c.current(a, v), S && clearTimeout(S), w = []);
242
+ } else if (ce(a, v, n?.ignoreModifiers) || v.keys?.includes("*")) {
243
+ if (m.current?.(a) || _ && l.current || (re(a, v, C.current), !ne(a, v, E.current)))
244
+ return;
245
+ c.current(a, v), _ || (l.current = !0);
246
+ }
247
+ });
248
+ }
249
+ }, B = (a) => {
250
+ a.code !== void 0 && (z(L(a.code)), (n?.keydown === void 0 && n?.keyup !== !0 || n?.keydown) && q(a));
251
+ }, N = (a) => {
252
+ a.code !== void 0 && (G(L(a.code)), l.current = !1, n?.keyup && q(a, !0));
253
+ }, H = u || o?.document || document;
254
+ return H.addEventListener("keyup", N, o?.eventListenerOptions), H.addEventListener("keydown", B, o?.eventListenerOptions), b && R(p, n?.delimiter).forEach((a) => {
255
+ b.addHotkey(
256
+ O(
257
+ a,
258
+ n?.splitKey,
259
+ n?.sequenceSplitKey,
260
+ n?.useKey,
261
+ n?.description,
262
+ n?.metadata
263
+ )
264
+ );
265
+ }), () => {
266
+ H.removeEventListener("keyup", N, o?.eventListenerOptions), H.removeEventListener("keydown", B, o?.eventListenerOptions), b && R(p, n?.delimiter).forEach((a) => {
267
+ b.removeHotkey(
268
+ O(
269
+ a,
270
+ n?.splitKey,
271
+ n?.sequenceSplitKey,
272
+ n?.useKey,
273
+ n?.description,
274
+ n?.metadata
275
+ )
276
+ );
277
+ }), w = [], S && clearTimeout(S);
278
+ };
279
+ }, [u, n, T, p]), d;
280
+ }
281
+ function ke(e = !1, t = []) {
282
+ const [r, i] = A(/* @__PURE__ */ new Set()), [u, f] = A(!1), d = Y(() => new Set(t.map((s) => s.toLowerCase())), [t]), l = h(
283
+ (s) => {
284
+ if (s.code === void 0)
285
+ return;
286
+ const c = L(e ? s.key : s.code).toLowerCase();
287
+ d.has(c) || (s.preventDefault(), s.stopPropagation(), i((n) => {
288
+ const E = new Set(n);
289
+ return E.add(c), E;
290
+ }));
291
+ },
292
+ [e, d]
293
+ ), o = h(() => {
294
+ f(!1);
295
+ }, []), p = h(() => {
296
+ i(/* @__PURE__ */ new Set()), f(!0);
297
+ }, []), y = h(() => {
298
+ i(/* @__PURE__ */ new Set());
299
+ }, []);
300
+ return I(() => {
301
+ if (typeof document < "u" && u)
302
+ return document.addEventListener("keydown", l), () => {
303
+ document.removeEventListener("keydown", l);
304
+ };
305
+ }, [u, l]), [r, { start: p, stop: o, resetKeys: y, isRecording: u }];
306
+ }
307
+ export {
308
+ ve as HotkeysProvider,
309
+ te as isHotkeyPressed,
310
+ we as useHotkeys,
311
+ fe as useHotkeysContext,
312
+ ke as useRecordHotkeys
313
+ };
@@ -1,14 +0,0 @@
1
- import { ReactNode } from 'react';
2
- import { Hotkey } from './types';
3
- type BoundHotkeysProxyProviderType = {
4
- addHotkey: (hotkey: Hotkey) => void;
5
- removeHotkey: (hotkey: Hotkey) => void;
6
- };
7
- export declare const useBoundHotkeysProxy: () => BoundHotkeysProxyProviderType | undefined;
8
- interface Props {
9
- children: ReactNode;
10
- addHotkey: (hotkey: Hotkey) => void;
11
- removeHotkey: (hotkey: Hotkey) => void;
12
- }
13
- export default function BoundHotkeysProxyProviderProvider({ addHotkey, removeHotkey, children }: Props): import("react/jsx-runtime").JSX.Element;
14
- export {};
@@ -1,16 +0,0 @@
1
- import { Hotkey } from './types';
2
- import { ReactNode } from 'react';
3
- export type HotkeysContextType = {
4
- hotkeys: ReadonlyArray<Hotkey>;
5
- activeScopes: string[];
6
- toggleScope: (scope: string) => void;
7
- enableScope: (scope: string) => void;
8
- disableScope: (scope: string) => void;
9
- };
10
- export declare const useHotkeysContext: () => HotkeysContextType;
11
- interface Props {
12
- initiallyActiveScopes?: string[];
13
- children: ReactNode;
14
- }
15
- export declare const HotkeysProvider: ({ initiallyActiveScopes, children }: Props) => import("react/jsx-runtime").JSX.Element;
16
- export {};
@@ -1 +0,0 @@
1
- export default function deepEqual(x?: unknown, y?: unknown): boolean;
package/dist/index.d.ts DELETED
@@ -1,6 +0,0 @@
1
- import { default as useHotkeys } from './useHotkeys';
2
- import { Options, Keys, HotkeyCallback } from './types';
3
- import { HotkeysProvider, useHotkeysContext } from './HotkeysProvider';
4
- import { isHotkeyPressed } from './isHotkeyPressed';
5
- import { default as useRecordHotkeys } from './useRecordHotkeys';
6
- export { useHotkeys, useRecordHotkeys, useHotkeysContext, isHotkeyPressed, HotkeysProvider, type Options, type Keys, type HotkeyCallback, };
package/dist/index.js DELETED
@@ -1,290 +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, c, a) {
31
- let n = [], y = !1;
32
- e = e.trim(), e.includes(o) ? (y = !0, n = e.toLocaleLowerCase().split(o).map((f) => K(f))) : n = e.toLocaleLowerCase().split(r).map((f) => K(f));
33
- const u = {
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
- }, l = n.filter((f) => !j.includes(f));
41
- return {
42
- ...u,
43
- keys: l,
44
- description: c,
45
- isSequence: y,
46
- hotkey: e,
47
- metadata: a
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, r = ",") {
66
- return (R(e) ? e : e.split(r)).every((i) => L.has(i.trim().toLowerCase()));
67
- }
68
- function _(e) {
69
- const r = Array.isArray(e) ? e : [e];
70
- L.has("meta") && L.forEach((o) => {
71
- D(o) || L.delete(o.toLowerCase());
72
- }), r.forEach((o) => {
73
- L.add(o.toLowerCase());
74
- });
75
- }
76
- function I(e) {
77
- const r = Array.isArray(e) ? e : [e];
78
- e === "meta" ? L.clear() : r.forEach((o) => {
79
- L.delete(o.toLowerCase());
80
- });
81
- }
82
- function V(e, r, o) {
83
- (typeof o == "function" && o(e, r) || o === !0) && e.preventDefault();
84
- }
85
- function X(e, r, o) {
86
- return typeof o == "function" ? o(e, r) : o === !0 || o === 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, r = !1) {
106
- const { target: o, composed: i } = e;
107
- let c, a;
108
- return ee(o) && i ? (c = e.composedPath()[0] && e.composedPath()[0].tagName, a = e.composedPath()[0] && e.composedPath()[0].role) : (c = o && o.tagName, a = o && o.role), R(r) ? !!(c && r && r.some((n) => n.toLowerCase() === c.toLowerCase() || n === a)) : !!(c && r && r);
109
- }
110
- function ee(e) {
111
- return !!e.tagName && !e.tagName.startsWith("-") && e.tagName.includes("-");
112
- }
113
- function te(e, r) {
114
- return e.length === 0 && r ? !1 : r ? e.some((o) => r.includes(o)) || e.includes("*") : !0;
115
- }
116
- const re = (e, r, o = !1) => {
117
- const { alt: i, meta: c, mod: a, shift: n, ctrl: y, keys: u, useKey: l } = r, { code: f, key: t, ctrlKey: d, metaKey: m, shiftKey: g, altKey: k } = e, p = K(f);
118
- if (l && u?.length === 1 && u.includes(t.toLowerCase()))
119
- return !0;
120
- if (!u?.includes(p) && !["ctrl", "control", "unknown", "meta", "alt", "shift", "os"].includes(p))
121
- return !1;
122
- if (!o) {
123
- if (i !== k && p !== "alt" || n !== g && p !== "shift")
124
- return !1;
125
- if (a) {
126
- if (!m && !d)
127
- return !1;
128
- } else if (c !== m && p !== "meta" && p !== "os" || y !== d && p !== "ctrl" && p !== "control")
129
- return !1;
130
- }
131
- return u && u.length === 1 && u.includes(p) ? !0 : u && u.length > 0 ? u.includes(p) ? U(u) : !1 : !u || u.length === 0;
132
- }, $ = T(void 0), oe = () => M($);
133
- function ne({ addHotkey: e, removeHotkey: r, children: o }) {
134
- return /* @__PURE__ */ b($.Provider, { value: { addHotkey: e, removeHotkey: r }, children: o });
135
- }
136
- function x(e, r) {
137
- return e && r && typeof e == "object" && typeof r == "object" ? Object.keys(e).length === Object.keys(r).length && // @ts-expect-error TS7053
138
- Object.keys(e).reduce((o, i) => o && x(e[i], r[i]), !0) : e === r;
139
- }
140
- const W = T({
141
- hotkeys: [],
142
- activeScopes: [],
143
- // This array has to be empty instead of containing '*' as default, to check if the provider is set or not
144
- toggleScope: () => {
145
- },
146
- enableScope: () => {
147
- },
148
- disableScope: () => {
149
- }
150
- }), se = () => M(W), de = ({ initiallyActiveScopes: e = ["*"], children: r }) => {
151
- const [o, i] = A(e), [c, a] = A([]), n = w((t) => {
152
- i((d) => d.includes("*") ? [t] : Array.from(/* @__PURE__ */ new Set([...d, t])));
153
- }, []), y = w((t) => {
154
- i((d) => d.filter((m) => m !== t));
155
- }, []), u = w((t) => {
156
- i((d) => d.includes(t) ? d.filter((m) => m !== t) : d.includes("*") ? [t] : Array.from(/* @__PURE__ */ new Set([...d, t])));
157
- }, []), l = w((t) => {
158
- a((d) => [...d, t]);
159
- }, []), f = w((t) => {
160
- a((d) => d.filter((m) => !x(m, t)));
161
- }, []);
162
- return /* @__PURE__ */ b(
163
- W.Provider,
164
- {
165
- value: { activeScopes: o, hotkeys: c, enableScope: n, disableScope: y, toggleScope: u },
166
- children: /* @__PURE__ */ b(ne, { addHotkey: l, removeHotkey: f, children: r })
167
- }
168
- );
169
- };
170
- function ie(e) {
171
- const r = S(void 0);
172
- return x(r.current, e) || (r.current = e), r.current;
173
- }
174
- const N = (e) => {
175
- e.stopPropagation(), e.preventDefault(), e.stopImmediatePropagation();
176
- }, ce = typeof window < "u" ? z : J;
177
- function fe(e, r, o, i) {
178
- const c = S(null), a = S(!1), n = Array.isArray(o) ? Array.isArray(i) ? void 0 : i : o, y = R(e) ? e.join(n?.delimiter) : e, u = Array.isArray(o) ? o : Array.isArray(i) ? i : void 0, l = w(r, u ?? []), f = S(l);
179
- u ? f.current = l : f.current = r;
180
- const t = ie(n), { activeScopes: d } = se(), m = oe();
181
- return ce(() => {
182
- if (t?.enabled === !1 || !te(d, t?.scopes))
183
- return;
184
- let g = [], k;
185
- const p = (s, B = !1) => {
186
- if (!(Z(s) && !F(s, t?.enableOnFormTags))) {
187
- if (c.current !== null) {
188
- const v = c.current.getRootNode();
189
- if ((v instanceof Document || v instanceof ShadowRoot) && v.activeElement !== c.current && !c.current.contains(v.activeElement)) {
190
- N(s);
191
- return;
192
- }
193
- }
194
- s.target?.isContentEditable && !t?.enableOnContentEditable || H(y, t?.delimiter).forEach((v) => {
195
- if (v.includes(t?.splitKey ?? "+") && v.includes(t?.sequenceSplitKey ?? ">")) {
196
- console.warn(
197
- `Hotkey ${v} contains both ${t?.splitKey ?? "+"} and ${t?.sequenceSplitKey ?? ">"} which is not supported.`
198
- );
199
- return;
200
- }
201
- const h = P(
202
- v,
203
- t?.splitKey,
204
- t?.sequenceSplitKey,
205
- t?.useKey,
206
- t?.description,
207
- t?.metadata
208
- );
209
- if (h.isSequence) {
210
- k = setTimeout(() => {
211
- g = [];
212
- }, t?.sequenceTimeoutMs ?? 1e3);
213
- const C = h.useKey ? s.key : K(s.code);
214
- if (D(C.toLowerCase()))
215
- return;
216
- g.push(C);
217
- const G = h.keys?.[g.length - 1];
218
- if (C !== G) {
219
- g = [], k && clearTimeout(k);
220
- return;
221
- }
222
- g.length === h.keys?.length && (f.current(s, h), k && clearTimeout(k), g = []);
223
- } else if (re(s, h, t?.ignoreModifiers) || h.keys?.includes("*")) {
224
- if (t?.ignoreEventWhen?.(s) || B && a.current)
225
- return;
226
- if (V(s, h, t?.preventDefault), !X(s, h, t?.enabled)) {
227
- N(s);
228
- return;
229
- }
230
- f.current(s, h), B || (a.current = !0);
231
- }
232
- });
233
- }
234
- }, O = (s) => {
235
- s.code !== void 0 && (_(K(s.code)), (t?.keydown === void 0 && t?.keyup !== !0 || t?.keydown) && p(s));
236
- }, q = (s) => {
237
- s.code !== void 0 && (I(K(s.code)), a.current = !1, t?.keyup && p(s, !0));
238
- }, E = c.current || n?.document || document;
239
- return E.addEventListener("keyup", q, n?.eventListenerOptions), E.addEventListener("keydown", O, n?.eventListenerOptions), m && H(y, t?.delimiter).forEach((s) => {
240
- m.addHotkey(
241
- P(
242
- s,
243
- t?.splitKey,
244
- t?.sequenceSplitKey,
245
- t?.useKey,
246
- t?.description,
247
- t?.metadata
248
- )
249
- );
250
- }), () => {
251
- E.removeEventListener("keyup", q, n?.eventListenerOptions), E.removeEventListener("keydown", O, n?.eventListenerOptions), m && H(y, t?.delimiter).forEach((s) => {
252
- m.removeHotkey(
253
- P(
254
- s,
255
- t?.splitKey,
256
- t?.sequenceSplitKey,
257
- t?.useKey,
258
- t?.description,
259
- t?.metadata
260
- )
261
- );
262
- }), g = [], k && clearTimeout(k);
263
- };
264
- }, [y, t, d]), c;
265
- }
266
- function le(e = !1) {
267
- const [r, o] = A(/* @__PURE__ */ new Set()), [i, c] = A(!1), a = w(
268
- (l) => {
269
- l.code !== void 0 && (l.preventDefault(), l.stopPropagation(), o((f) => {
270
- const t = new Set(f);
271
- return t.add(K(e ? l.key : l.code)), t;
272
- }));
273
- },
274
- [e]
275
- ), n = w(() => {
276
- typeof document < "u" && (document.removeEventListener("keydown", a), c(!1));
277
- }, [a]), y = w(() => {
278
- o(/* @__PURE__ */ new Set()), typeof document < "u" && (n(), document.addEventListener("keydown", a), c(!0));
279
- }, [a, n]), u = w(() => {
280
- o(/* @__PURE__ */ new Set());
281
- }, []);
282
- return [r, { start: y, stop: n, resetKeys: u, isRecording: i }];
283
- }
284
- export {
285
- de as HotkeysProvider,
286
- U as isHotkeyPressed,
287
- fe as useHotkeys,
288
- se as useHotkeysContext,
289
- le as useRecordHotkeys
290
- };
@@ -1,4 +0,0 @@
1
- export declare function isReadonlyArray(value: unknown): value is readonly unknown[];
2
- export declare function isHotkeyPressed(key: string | readonly string[], delimiter?: string): boolean;
3
- export declare function pushToCurrentlyPressedKeys(key: string | string[]): void;
4
- export declare function removeFromCurrentlyPressedKeys(key: string | string[]): void;
@@ -1,5 +0,0 @@
1
- import { Hotkey } from './types';
2
- export declare function mapCode(key: string): string;
3
- export declare function isHotkeyModifier(key: string): boolean;
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, metadata?: Record<string, unknown>): Hotkey;
package/dist/types.d.ts DELETED
@@ -1,50 +0,0 @@
1
- import { DependencyList } from 'react';
2
- export type FormTags = 'input' | 'textarea' | 'select' | 'INPUT' | 'TEXTAREA' | 'SELECT' | 'searchbox' | 'slider' | 'spinbutton' | 'menuitem' | 'menuitemcheckbox' | 'menuitemradio' | 'option' | 'radio' | 'textbox';
3
- export type Keys = string | readonly string[];
4
- export type Scopes = string | readonly string[];
5
- export type EventListenerOptions = {
6
- capture?: boolean;
7
- once?: boolean;
8
- passive?: boolean;
9
- signal?: AbortSignal;
10
- } | boolean;
11
- export type KeyboardModifiers = {
12
- alt?: boolean;
13
- ctrl?: boolean;
14
- meta?: boolean;
15
- shift?: boolean;
16
- mod?: boolean;
17
- useKey?: boolean;
18
- };
19
- export type Hotkey = KeyboardModifiers & {
20
- keys?: readonly string[];
21
- scopes?: Scopes;
22
- description?: string;
23
- isSequence?: boolean;
24
- hotkey: string;
25
- metadata?: Record<string, unknown>;
26
- };
27
- export type HotkeysEvent = Hotkey;
28
- export type HotkeyCallback = (keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => void;
29
- export type Trigger = boolean | ((keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => boolean);
30
- export type Options = {
31
- enabled?: Trigger;
32
- enableOnFormTags?: readonly FormTags[] | boolean;
33
- enableOnContentEditable?: boolean;
34
- ignoreEventWhen?: (e: KeyboardEvent) => boolean;
35
- splitKey?: string;
36
- delimiter?: string;
37
- scopes?: Scopes;
38
- keyup?: boolean;
39
- keydown?: boolean;
40
- preventDefault?: Trigger;
41
- description?: string;
42
- document?: Document;
43
- ignoreModifiers?: boolean;
44
- eventListenerOptions?: EventListenerOptions;
45
- useKey?: boolean;
46
- sequenceTimeoutMs?: number;
47
- sequenceSplitKey?: string;
48
- metadata?: Record<string, unknown>;
49
- };
50
- export type OptionsOrDependencyArray = Options | DependencyList;
@@ -1 +0,0 @@
1
- export default function useDeepEqualMemo<T>(value: T): T | undefined;
@@ -1,2 +0,0 @@
1
- import { HotkeyCallback, Keys, OptionsOrDependencyArray } from './types';
2
- export default function useHotkeys<T extends HTMLElement>(keys: Keys, callback: HotkeyCallback, options?: OptionsOrDependencyArray, dependencies?: OptionsOrDependencyArray): import('react').RefObject<T | null>;
@@ -1,6 +0,0 @@
1
- export default function useRecordHotkeys(useKey?: boolean): readonly [Set<string>, {
2
- readonly start: () => void;
3
- readonly stop: () => void;
4
- readonly resetKeys: () => void;
5
- readonly isRecording: boolean;
6
- }];
@@ -1,8 +0,0 @@
1
- import { FormTags, Hotkey, Scopes, Trigger } from './types';
2
- export declare function maybePreventDefault(e: KeyboardEvent, hotkey: Hotkey, preventDefault?: Trigger): void;
3
- export declare function isHotkeyEnabled(e: KeyboardEvent, hotkey: Hotkey, enabled?: Trigger): boolean;
4
- export declare function isKeyboardEventTriggeredByInput(ev: KeyboardEvent): boolean;
5
- export declare function isHotkeyEnabledOnTag(event: KeyboardEvent, enabledOnTags?: readonly FormTags[] | boolean): boolean;
6
- export declare function isCustomElement(element: HTMLElement): boolean;
7
- export declare function isScopeActive(activeScopes: string[], scopes?: Scopes): boolean;
8
- export declare const isHotkeyMatchingKeyboardEvent: (e: KeyboardEvent, hotkey: Hotkey, ignoreModifiers?: boolean) => boolean;