@tanstack/hotkeys 0.12.1 → 0.13.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/dist/_event-target.js +156 -32
- package/dist/_registration.js +2 -2
- package/dist/adapter.js +3 -5
- package/dist/conflicts.js +2 -6
- package/dist/hotkey-manager.d.ts +11 -2
- package/dist/hotkey-manager.js +29 -26
- package/dist/hotkey-recorder.js +2 -2
- package/package.json +1 -1
package/dist/_event-target.js
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
|
+
import { normalizeKeyName } from "./constants.js";
|
|
2
|
+
|
|
1
3
|
//#region src/_event-target.ts
|
|
4
|
+
function isNode(target) {
|
|
5
|
+
return typeof target?.nodeType === "number";
|
|
6
|
+
}
|
|
7
|
+
function isElement(target) {
|
|
8
|
+
return isNode(target) && target.nodeType === 1;
|
|
9
|
+
}
|
|
10
|
+
function isWindow(target) {
|
|
11
|
+
return "document" in target;
|
|
12
|
+
}
|
|
13
|
+
/** Returns the document a listener target belongs to, in any frame. */
|
|
14
|
+
function getTargetDocument(target) {
|
|
15
|
+
if (isWindow(target)) return target.document;
|
|
16
|
+
if (target.nodeType === 9) return target;
|
|
17
|
+
return target.ownerDocument;
|
|
18
|
+
}
|
|
2
19
|
/**
|
|
3
20
|
* Checks if an element is an input-like element that should be ignored for hotkeys.
|
|
4
21
|
*
|
|
@@ -12,15 +29,13 @@
|
|
|
12
29
|
* Mod+S and Escape fire when the user has tabbed to a form button.
|
|
13
30
|
*/
|
|
14
31
|
function isInputElement(element) {
|
|
15
|
-
if (!element) return false;
|
|
16
|
-
if (element
|
|
32
|
+
if (!isElement(element)) return false;
|
|
33
|
+
if (element.localName === "input") {
|
|
17
34
|
const type = element.type.toLowerCase();
|
|
18
|
-
|
|
19
|
-
return true;
|
|
35
|
+
return type !== "button" && type !== "submit" && type !== "reset";
|
|
20
36
|
}
|
|
21
|
-
if (element
|
|
22
|
-
|
|
23
|
-
return false;
|
|
37
|
+
if (element.localName === "textarea" || element.localName === "select") return true;
|
|
38
|
+
return element.isContentEditable === true;
|
|
24
39
|
}
|
|
25
40
|
/**
|
|
26
41
|
* Returns the focused element for the document associated with where hotkey
|
|
@@ -30,41 +45,150 @@ function isInputElement(element) {
|
|
|
30
45
|
*/
|
|
31
46
|
function getActiveElementForListenerTarget(target) {
|
|
32
47
|
if (typeof document === "undefined") return null;
|
|
33
|
-
|
|
34
|
-
if (typeof HTMLElement !== "undefined" && target instanceof HTMLElement) return target.ownerDocument.activeElement ?? null;
|
|
35
|
-
return target.document.activeElement ?? null;
|
|
48
|
+
return getTargetDocument(target).activeElement;
|
|
36
49
|
}
|
|
50
|
+
const VERTICAL = ["ArrowUp", "ArrowDown"];
|
|
51
|
+
const HORIZONTAL = ["ArrowLeft", "ArrowRight"];
|
|
52
|
+
const ARROWS = [...VERTICAL, ...HORIZONTAL];
|
|
53
|
+
const EDGES = ["Home", "End"];
|
|
54
|
+
const PAGES = ["PageUp", "PageDown"];
|
|
55
|
+
const ACTIVATION = ["Enter", "Space"];
|
|
56
|
+
const TYPEAHEAD = "typeahead";
|
|
37
57
|
/**
|
|
38
|
-
* Returns
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* This checks:
|
|
42
|
-
* - the currently focused element for the listener target
|
|
43
|
-
* - the event's composed path (for shadow DOM)
|
|
44
|
-
* - the event target as a final fallback
|
|
58
|
+
* Returns the keys a control handles itself: activation for buttons and
|
|
59
|
+
* links, and navigation for ARIA widgets, following the WAI-ARIA Authoring
|
|
60
|
+
* Practices keyboard patterns (https://www.w3.org/WAI/ARIA/apg/patterns/).
|
|
45
61
|
*/
|
|
46
|
-
function
|
|
62
|
+
function getOwnedKeys(element) {
|
|
63
|
+
const orientation = element.getAttribute("aria-orientation");
|
|
64
|
+
switch (element.getAttribute("role")?.trim().split(/\s+/)[0]) {
|
|
65
|
+
case "button":
|
|
66
|
+
case "tab":
|
|
67
|
+
case "option":
|
|
68
|
+
case "treeitem":
|
|
69
|
+
case "menuitem":
|
|
70
|
+
case "menuitemcheckbox":
|
|
71
|
+
case "menuitemradio": return ACTIVATION;
|
|
72
|
+
case "link": return ["Enter"];
|
|
73
|
+
case "checkbox":
|
|
74
|
+
case "switch":
|
|
75
|
+
case "radio": return ["Space"];
|
|
76
|
+
case "menu":
|
|
77
|
+
case "menubar":
|
|
78
|
+
case "tree": return [
|
|
79
|
+
...ARROWS,
|
|
80
|
+
...EDGES,
|
|
81
|
+
...ACTIVATION,
|
|
82
|
+
TYPEAHEAD
|
|
83
|
+
];
|
|
84
|
+
case "listbox": return [
|
|
85
|
+
...orientation === "horizontal" ? HORIZONTAL : VERTICAL,
|
|
86
|
+
...EDGES,
|
|
87
|
+
...ACTIVATION,
|
|
88
|
+
TYPEAHEAD
|
|
89
|
+
];
|
|
90
|
+
case "combobox": return [
|
|
91
|
+
...VERTICAL,
|
|
92
|
+
...EDGES,
|
|
93
|
+
...ACTIVATION,
|
|
94
|
+
TYPEAHEAD
|
|
95
|
+
];
|
|
96
|
+
case "tablist": return [
|
|
97
|
+
...orientation === "vertical" ? VERTICAL : HORIZONTAL,
|
|
98
|
+
...EDGES,
|
|
99
|
+
...ACTIVATION
|
|
100
|
+
];
|
|
101
|
+
case "toolbar": return [...orientation === "vertical" ? VERTICAL : HORIZONTAL, ...EDGES];
|
|
102
|
+
case "grid":
|
|
103
|
+
case "treegrid": return [
|
|
104
|
+
...ARROWS,
|
|
105
|
+
...EDGES,
|
|
106
|
+
...PAGES,
|
|
107
|
+
...ACTIVATION
|
|
108
|
+
];
|
|
109
|
+
case "radiogroup": return [...ARROWS, "Space"];
|
|
110
|
+
case "slider": return [
|
|
111
|
+
...ARROWS,
|
|
112
|
+
...EDGES,
|
|
113
|
+
...PAGES
|
|
114
|
+
];
|
|
115
|
+
case "spinbutton": return [
|
|
116
|
+
...VERTICAL,
|
|
117
|
+
...EDGES,
|
|
118
|
+
...PAGES
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
switch (element.localName) {
|
|
122
|
+
case "button":
|
|
123
|
+
case "summary": return ACTIVATION;
|
|
124
|
+
case "input": {
|
|
125
|
+
const type = element.type;
|
|
126
|
+
return type === "button" || type === "submit" || type === "reset" ? ACTIVATION : [];
|
|
127
|
+
}
|
|
128
|
+
case "a":
|
|
129
|
+
case "area": return element.hasAttribute("href") ? ["Enter"] : [];
|
|
130
|
+
}
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Whether a control handles this key itself. Ctrl, Meta, and Alt combinations
|
|
135
|
+
* are never owned; Enter and Space are owned only without modifiers, while
|
|
136
|
+
* navigation and typeahead keys allow Shift (for example, to extend a selection).
|
|
137
|
+
*/
|
|
138
|
+
function ownsKey(element, event, key) {
|
|
139
|
+
if (event.ctrlKey || event.metaKey || event.altKey) return false;
|
|
140
|
+
const owned = getOwnedKeys(element);
|
|
141
|
+
if (key === "Enter" || key === "Space") return !event.shiftKey && owned.includes(key);
|
|
142
|
+
return owned.includes(key) || owned.includes(TYPEAHEAD) && /^\S$/u.test(event.key);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Whether the focused element or an element on the event's path passes `test`.
|
|
146
|
+
* Only elements inside the registration target count: walking outward from
|
|
147
|
+
* the event's origin stops at the registration target, so a hotkey targeted
|
|
148
|
+
* at a control, or at an element inside it, is not blocked by that control.
|
|
149
|
+
*/
|
|
150
|
+
function someElementInsideTarget(event, listenerTarget, registrationTarget, test) {
|
|
151
|
+
const scoped = !isWindow(registrationTarget) && registrationTarget.nodeType !== 9;
|
|
47
152
|
const focused = getActiveElementForListenerTarget(listenerTarget);
|
|
48
|
-
if (focused &&
|
|
49
|
-
|
|
50
|
-
|
|
153
|
+
if (focused && focused !== registrationTarget && (!scoped || registrationTarget.contains(focused)) && test(focused)) return true;
|
|
154
|
+
const path = event.composedPath();
|
|
155
|
+
for (const element of path.length > 0 ? path : [event.target]) {
|
|
156
|
+
if (element === registrationTarget) break;
|
|
157
|
+
if (isElement(element) && test(element)) return true;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Returns whether a hotkey should skip an event because a focused control
|
|
163
|
+
* inside the registration target handles the key itself:
|
|
164
|
+
* - text inputs, textareas, selects, and contenteditable elements (typing)
|
|
165
|
+
* - buttons, links, and `<summary>` elements (Enter and Space activation)
|
|
166
|
+
* - ARIA widgets such as menus, listboxes, tabs, and grids (navigation keys)
|
|
167
|
+
*/
|
|
168
|
+
function shouldIgnoreInputEvent(event, listenerTarget, registrationTarget) {
|
|
169
|
+
const key = normalizeKeyName(event.key);
|
|
170
|
+
return someElementInsideTarget(event, listenerTarget, registrationTarget, (element) => isInputElement(element) || ownsKey(element, event, key));
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Returns whether an event comes from a text input, textarea, select, or
|
|
174
|
+
* contenteditable element in the target's document. Unlike
|
|
175
|
+
* `shouldIgnoreInputEvent`, buttons, links, and ARIA widgets don't count.
|
|
176
|
+
*/
|
|
177
|
+
function isTypingEvent(event, target) {
|
|
178
|
+
return someElementInsideTarget(event, target, target, isInputElement);
|
|
51
179
|
}
|
|
52
180
|
/**
|
|
53
181
|
* Checks if an event is for the given target (originated from or bubbled to it).
|
|
54
182
|
*
|
|
55
|
-
* For document/window targets, also accepts document
|
|
56
|
-
* to handle Brave and other browsers where currentTarget may be
|
|
57
|
-
* instead of document when listeners are attached to document.
|
|
183
|
+
* For document/window targets, also accepts the document's root element as
|
|
184
|
+
* currentTarget to handle Brave and other browsers where currentTarget may be
|
|
185
|
+
* documentElement instead of document when listeners are attached to document.
|
|
58
186
|
*/
|
|
59
187
|
function isEventForTarget(event, target) {
|
|
60
|
-
if (
|
|
61
|
-
if (target
|
|
62
|
-
|
|
63
|
-
if (event.currentTarget === target) return true;
|
|
64
|
-
if (event.target instanceof Node && target.contains(event.target)) return true;
|
|
65
|
-
}
|
|
66
|
-
return false;
|
|
188
|
+
if (event.currentTarget === target) return true;
|
|
189
|
+
if (isWindow(target) || target.nodeType === 9) return event.currentTarget === getTargetDocument(target).documentElement;
|
|
190
|
+
return isNode(event.target) && target.contains(event.target);
|
|
67
191
|
}
|
|
68
192
|
|
|
69
193
|
//#endregion
|
|
70
|
-
export { getActiveElementForListenerTarget, isEventForTarget, isInputElement, shouldIgnoreInputEvent };
|
|
194
|
+
export { getActiveElementForListenerTarget, getTargetDocument, isEventForTarget, isInputElement, isTypingEvent, shouldIgnoreInputEvent };
|
package/dist/_registration.js
CHANGED
|
@@ -8,11 +8,11 @@ const defaultHotkeyOptions = {
|
|
|
8
8
|
stopPropagation: true,
|
|
9
9
|
eventType: "keydown",
|
|
10
10
|
enabled: true,
|
|
11
|
-
ignoreInputs:
|
|
11
|
+
ignoreInputs: "auto",
|
|
12
12
|
conflictBehavior: "warn"
|
|
13
13
|
};
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Resolves `ignoreInputs: 'auto'` from the hotkey (the first step of a sequence).
|
|
16
16
|
* Ctrl/Meta shortcuts and Escape fire in inputs; single keys and Shift/Alt combos are ignored.
|
|
17
17
|
*/
|
|
18
18
|
function getDefaultIgnoreInputs(parsedHotkey) {
|
package/dist/adapter.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { detectPlatform } from "./platform.js";
|
|
2
|
-
import { isHotkeySequence, normalizeRegisterableHotkey
|
|
3
|
-
import { defaultHotkeyOptions
|
|
2
|
+
import { isHotkeySequence, normalizeRegisterableHotkey } from "./parse.js";
|
|
3
|
+
import { defaultHotkeyOptions } from "./_registration.js";
|
|
4
4
|
import { getHotkeyManager } from "./hotkey-manager.js";
|
|
5
5
|
import { getAppHotkeyRegistrationOptions, isAppHotkey, resolveAppHotkey } from "./app-hotkeys.js";
|
|
6
6
|
|
|
@@ -23,14 +23,12 @@ function createHotkeyBindings() {
|
|
|
23
23
|
const options = {
|
|
24
24
|
...defaultHotkeyOptions,
|
|
25
25
|
...merged,
|
|
26
|
-
ignoreInputs: merged.ignoreInputs,
|
|
26
|
+
ignoreInputs: merged.ignoreInputs ?? "auto",
|
|
27
27
|
target: "target" in merged ? merged.target ?? null : typeof document === "undefined" ? null : document,
|
|
28
28
|
platform: merged.platform ?? detectPlatform()
|
|
29
29
|
};
|
|
30
30
|
if (!options.target || isHotkeySequence(definition.hotkey) && definition.hotkey.length === 0) return;
|
|
31
31
|
const hotkey = normalizeRegisterableHotkey(definition.hotkey, options.platform);
|
|
32
|
-
const first = isHotkeySequence(hotkey) ? hotkey[0] : hotkey;
|
|
33
|
-
options.ignoreInputs ??= getDefaultIgnoreInputs(parseHotkey(first, options.platform));
|
|
34
32
|
if (isHotkeySequence(hotkey)) delete options.requireReset;
|
|
35
33
|
else delete options.timeout;
|
|
36
34
|
prepared.push({
|
package/dist/conflicts.js
CHANGED
|
@@ -2,18 +2,14 @@ import { detectPlatform } from "./platform.js";
|
|
|
2
2
|
import { keysEqual } from "./_keyboard-event.js";
|
|
3
3
|
import { isHotkeySequence, parseRegisterableHotkey } from "./parse.js";
|
|
4
4
|
import { matchesKeyboardEvent } from "./match.js";
|
|
5
|
+
import { getTargetDocument } from "./_event-target.js";
|
|
5
6
|
import { getHotkeyManager, toHotkeyRegistrationView } from "./hotkey-manager.js";
|
|
6
7
|
|
|
7
8
|
//#region src/conflicts.ts
|
|
8
9
|
/** Whether two targets share a document and one contains the other (or is its window). */
|
|
9
10
|
function targetsOverlap(a, b) {
|
|
10
11
|
if (a === b) return true;
|
|
11
|
-
|
|
12
|
-
if ("document" in target) return target.document;
|
|
13
|
-
if (target.nodeType === 9) return target;
|
|
14
|
-
return target.ownerDocument;
|
|
15
|
-
};
|
|
16
|
-
if (documentOf(a) !== documentOf(b)) return false;
|
|
12
|
+
if (getTargetDocument(a) !== getTargetDocument(b)) return false;
|
|
17
13
|
if (!("contains" in a) || !("contains" in b)) return true;
|
|
18
14
|
return a.contains(b) || b.contains(a);
|
|
19
15
|
}
|
package/dist/hotkey-manager.d.ts
CHANGED
|
@@ -18,8 +18,17 @@ export interface HotkeyOptions {
|
|
|
18
18
|
enabled?: boolean;
|
|
19
19
|
/** The event type to listen for. Defaults to 'keydown' */
|
|
20
20
|
eventType?: 'keydown' | 'keyup';
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Skip keys that a focused control inside the target uses itself: typing in
|
|
23
|
+
* text inputs, textarea, select, and contenteditable; Enter/Space on buttons
|
|
24
|
+
* and summary elements; Enter on links; and navigation keys in ARIA widgets
|
|
25
|
+
* such as menus, listboxes, tabs, and grids. A hotkey targeted at the control
|
|
26
|
+
* itself still receives them. Defaults to `'auto'`, which chooses from the
|
|
27
|
+
* hotkey (the first step of a sequence): true for single keys and Shift/Alt
|
|
28
|
+
* combos; false for Ctrl/Meta shortcuts and Escape. Set false to handle keys
|
|
29
|
+
* even in these controls.
|
|
30
|
+
*/
|
|
31
|
+
ignoreInputs?: boolean | 'auto';
|
|
23
32
|
/** The target platform for resolving 'Mod' */
|
|
24
33
|
platform?: 'mac' | 'windows' | 'linux';
|
|
25
34
|
/** Prevent the default browser action when the hotkey matches. Defaults to true */
|
package/dist/hotkey-manager.js
CHANGED
|
@@ -5,9 +5,9 @@ import { isHotkeySequence, isModifierKey, parseHotkey, rawHotkeyToParsedHotkey,
|
|
|
5
5
|
import { formatHotkey } from "./format.js";
|
|
6
6
|
import { matchKeyboardEvent } from "./_match.js";
|
|
7
7
|
import { DEFAULT_SEQUENCE_TIMEOUT, areHotkeysEqual } from "./match.js";
|
|
8
|
+
import { isEventForTarget, shouldIgnoreInputEvent } from "./_event-target.js";
|
|
8
9
|
import { isRecordingEvent } from "./_recording-guard.js";
|
|
9
10
|
import { defaultHotkeyOptions, getDefaultIgnoreInputs, handleConflict, optionsEqual } from "./_registration.js";
|
|
10
|
-
import { isEventForTarget, shouldIgnoreInputEvent } from "./_event-target.js";
|
|
11
11
|
import { Store } from "@tanstack/store";
|
|
12
12
|
|
|
13
13
|
//#region src/hotkey-manager.ts
|
|
@@ -58,6 +58,11 @@ function optionsForKind(kind, options) {
|
|
|
58
58
|
function resolvedSteps(registration) {
|
|
59
59
|
return registration.kind === "chord" ? [registration.parsedHotkey] : registration.parsedSequence;
|
|
60
60
|
}
|
|
61
|
+
/** Whether a registration skips keys that focused controls use, resolving `'auto'`. */
|
|
62
|
+
function ignoresInputs(registration) {
|
|
63
|
+
const { ignoreInputs = "auto" } = registration.options;
|
|
64
|
+
return ignoreInputs === "auto" ? getDefaultIgnoreInputs(resolvedSteps(registration)[0]) : ignoreInputs;
|
|
65
|
+
}
|
|
61
66
|
function sameResolvedSteps(left, right) {
|
|
62
67
|
return left.length === right.length && left.every((step, index) => areHotkeysEqual(step, right[index]));
|
|
63
68
|
}
|
|
@@ -167,28 +172,26 @@ var HotkeyManager = class HotkeyManager {
|
|
|
167
172
|
const platform = options.platform ?? this.#platform;
|
|
168
173
|
const target = options.target ?? document;
|
|
169
174
|
let registration;
|
|
170
|
-
if (isHotkeySequence(hotkey)) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
};
|
|
191
|
-
} else {
|
|
175
|
+
if (isHotkeySequence(hotkey)) registration = {
|
|
176
|
+
kind: "sequence",
|
|
177
|
+
id,
|
|
178
|
+
hotkey,
|
|
179
|
+
parsedSequence: hotkey.map((step) => parseHotkey(step, platform)),
|
|
180
|
+
callback,
|
|
181
|
+
options: {
|
|
182
|
+
...defaultHotkeyOptions,
|
|
183
|
+
timeout: DEFAULT_SEQUENCE_TIMEOUT,
|
|
184
|
+
...optionsForKind("sequence", options),
|
|
185
|
+
platform,
|
|
186
|
+
ignoreInputs: options.ignoreInputs ?? "auto"
|
|
187
|
+
},
|
|
188
|
+
target,
|
|
189
|
+
currentIndex: 0,
|
|
190
|
+
lastKeyTime: 0,
|
|
191
|
+
triggerCount: 0,
|
|
192
|
+
hasFired: false
|
|
193
|
+
};
|
|
194
|
+
else {
|
|
192
195
|
const parsedHotkey = typeof hotkey === "string" ? parseHotkey(hotkey, platform) : rawHotkeyToParsedHotkey(hotkey, platform);
|
|
193
196
|
registration = {
|
|
194
197
|
kind: "chord",
|
|
@@ -201,7 +204,7 @@ var HotkeyManager = class HotkeyManager {
|
|
|
201
204
|
requireReset: false,
|
|
202
205
|
...optionsForKind("chord", options),
|
|
203
206
|
platform,
|
|
204
|
-
ignoreInputs: options.ignoreInputs ??
|
|
207
|
+
ignoreInputs: options.ignoreInputs ?? "auto"
|
|
205
208
|
},
|
|
206
209
|
target,
|
|
207
210
|
hasFired: false,
|
|
@@ -323,7 +326,7 @@ var HotkeyManager = class HotkeyManager {
|
|
|
323
326
|
const matches = /* @__PURE__ */ new Map();
|
|
324
327
|
let bestScore = 0;
|
|
325
328
|
for (const registration of registrations) {
|
|
326
|
-
if (!registration.options.enabled || registration.options.eventType !== eventType || !isEventForTarget(event, target) || registration
|
|
329
|
+
if (!registration.options.enabled || registration.options.eventType !== eventType || !isEventForTarget(event, target) || ignoresInputs(registration) && shouldIgnoreInputEvent(event, target, registration.target)) continue;
|
|
327
330
|
const result = matchKeyboardEvent(event, registration.parsedHotkey, registration.options.platform);
|
|
328
331
|
if (result.matched) {
|
|
329
332
|
matches.set(registration.id, result);
|
|
@@ -367,7 +370,7 @@ var HotkeyManager = class HotkeyManager {
|
|
|
367
370
|
const candidates = /* @__PURE__ */ new Map();
|
|
368
371
|
let bestScore = 0;
|
|
369
372
|
for (const registration of registrations) {
|
|
370
|
-
if (!isEventForTarget(event, target) || !registration.options.enabled || registration
|
|
373
|
+
if (!isEventForTarget(event, target) || !registration.options.enabled || ignoresInputs(registration) && shouldIgnoreInputEvent(event, target, registration.target) || registration.options.eventType !== eventType) continue;
|
|
371
374
|
const timeout = registration.options.timeout ?? 1e3;
|
|
372
375
|
if (registration.currentIndex > 0 && now - registration.lastKeyTime > timeout) {
|
|
373
376
|
registration.currentIndex = 0;
|
package/dist/hotkey-recorder.js
CHANGED
|
@@ -2,8 +2,8 @@ import { detectPlatform } from "./platform.js";
|
|
|
2
2
|
import { normalizeKeyboardEvent } from "./_keyboard-event.js";
|
|
3
3
|
import { isModifierKey, parseHotkey, simplifyHotkey } from "./parse.js";
|
|
4
4
|
import { validateHotkey } from "./validate.js";
|
|
5
|
+
import { isTypingEvent } from "./_event-target.js";
|
|
5
6
|
import { beginRecording, captureRecordingEvent, endRecording } from "./_recording-guard.js";
|
|
6
|
-
import { shouldIgnoreInputEvent } from "./_event-target.js";
|
|
7
7
|
import { findHotkeyConflicts } from "./conflicts.js";
|
|
8
8
|
import { chordRejection, hotkeyChordFromKeydown } from "./_recorder-chord.js";
|
|
9
9
|
import { Store } from "@tanstack/store";
|
|
@@ -111,7 +111,7 @@ var HotkeyRecorder = class {
|
|
|
111
111
|
}));
|
|
112
112
|
const handler = (event) => {
|
|
113
113
|
if (!this.#keydownHandler) return;
|
|
114
|
-
if (this.#options.ignoreInputs !== false && event.key !== "Escape" &&
|
|
114
|
+
if (this.#options.ignoreInputs !== false && event.key !== "Escape" && isTypingEvent(event, document)) return;
|
|
115
115
|
const platform = this.#options.platform ?? this.#platform;
|
|
116
116
|
if (normalizeKeyboardEvent(event, platform).isComposing) return;
|
|
117
117
|
captureRecordingEvent(event);
|