@tanstack/hotkeys 0.13.0 → 0.14.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/_registration.js +2 -1
- package/dist/_target-listeners.js +72 -0
- package/dist/app-hotkeys.d.ts +1 -1
- package/dist/hotkey-manager.d.ts +13 -2
- package/dist/hotkey-manager.js +35 -57
- package/dist/key-state-tracker.js +35 -5
- package/package.json +1 -1
package/dist/_registration.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Omitted: platform, target (resolved at registration), requireReset (HotkeyManager only).
|
|
5
5
|
*/
|
|
6
6
|
const defaultHotkeyOptions = {
|
|
7
|
+
capture: false,
|
|
7
8
|
preventDefault: true,
|
|
8
9
|
stopPropagation: true,
|
|
9
10
|
eventType: "keydown",
|
|
@@ -31,7 +32,7 @@ function getDefaultIgnoreInputs(parsedHotkey) {
|
|
|
31
32
|
function handleConflict(conflictingId, keyDisplay, conflictBehavior, unregister) {
|
|
32
33
|
if (conflictBehavior === "allow") return;
|
|
33
34
|
if (conflictBehavior === "warn") {
|
|
34
|
-
console.warn(`'${keyDisplay}' is already registered. Multiple handlers
|
|
35
|
+
console.warn(`'${keyDisplay}' is already registered. Multiple handlers may be triggered. Use conflictBehavior: 'replace' to replace the existing handler, or conflictBehavior: 'allow' to suppress this warning.`);
|
|
35
36
|
return;
|
|
36
37
|
}
|
|
37
38
|
if (conflictBehavior === "error") throw new Error(`'${keyDisplay}' is already registered. Use conflictBehavior: 'replace' to replace the existing handler, or conflictBehavior: 'allow' to allow multiple registrations.`);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
//#region src/_target-listeners.ts
|
|
2
|
+
/** Shares keyboard listeners within each target and capture phase. */
|
|
3
|
+
var TargetListeners = class {
|
|
4
|
+
dispatch;
|
|
5
|
+
#targets = /* @__PURE__ */ new Map();
|
|
6
|
+
#processed = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
constructor(dispatch) {
|
|
8
|
+
this.dispatch = dispatch;
|
|
9
|
+
}
|
|
10
|
+
add(target, capture, id) {
|
|
11
|
+
if (typeof document === "undefined") return;
|
|
12
|
+
let phases = this.#targets.get(target);
|
|
13
|
+
if (!phases) {
|
|
14
|
+
phases = /* @__PURE__ */ new Map();
|
|
15
|
+
this.#targets.set(target, phases);
|
|
16
|
+
}
|
|
17
|
+
let group = phases.get(capture);
|
|
18
|
+
if (!group) {
|
|
19
|
+
const ids = /* @__PURE__ */ new Set();
|
|
20
|
+
const listener = (eventType) => (rawEvent) => {
|
|
21
|
+
const event = rawEvent;
|
|
22
|
+
let processed = this.#processed.get(event);
|
|
23
|
+
if (!processed || processed.groups.has(ids)) {
|
|
24
|
+
processed = {
|
|
25
|
+
ids: /* @__PURE__ */ new Set(),
|
|
26
|
+
groups: /* @__PURE__ */ new Set()
|
|
27
|
+
};
|
|
28
|
+
this.#processed.set(event, processed);
|
|
29
|
+
}
|
|
30
|
+
processed.groups.add(ids);
|
|
31
|
+
const pending = /* @__PURE__ */ new Set();
|
|
32
|
+
for (const registrationId of ids) if (!processed.ids.has(registrationId)) {
|
|
33
|
+
processed.ids.add(registrationId);
|
|
34
|
+
pending.add(registrationId);
|
|
35
|
+
}
|
|
36
|
+
this.dispatch(event, target, eventType, capture, pending);
|
|
37
|
+
};
|
|
38
|
+
group = {
|
|
39
|
+
ids,
|
|
40
|
+
keydown: listener("keydown"),
|
|
41
|
+
keyup: listener("keyup")
|
|
42
|
+
};
|
|
43
|
+
phases.set(capture, group);
|
|
44
|
+
target.addEventListener("keydown", group.keydown, capture);
|
|
45
|
+
target.addEventListener("keyup", group.keyup, capture);
|
|
46
|
+
}
|
|
47
|
+
group.ids.add(id);
|
|
48
|
+
}
|
|
49
|
+
remove(target, capture, id) {
|
|
50
|
+
const phases = this.#targets.get(target);
|
|
51
|
+
const group = phases?.get(capture);
|
|
52
|
+
if (!group) return;
|
|
53
|
+
group.ids.delete(id);
|
|
54
|
+
if (group.ids.size === 0) {
|
|
55
|
+
target.removeEventListener("keydown", group.keydown, capture);
|
|
56
|
+
target.removeEventListener("keyup", group.keyup, capture);
|
|
57
|
+
phases.delete(capture);
|
|
58
|
+
if (phases.size === 0) this.#targets.delete(target);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
destroy() {
|
|
62
|
+
for (const [target, phases] of this.#targets) for (const [capture, group] of phases) {
|
|
63
|
+
target.removeEventListener("keydown", group.keydown, capture);
|
|
64
|
+
target.removeEventListener("keyup", group.keyup, capture);
|
|
65
|
+
}
|
|
66
|
+
this.#targets.clear();
|
|
67
|
+
this.#processed = /* @__PURE__ */ new WeakMap();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
//#endregion
|
|
72
|
+
export { TargetListeners };
|
package/dist/app-hotkeys.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { HotkeyChordOptions, HotkeyRegistrationView, HotkeySequenceOptions } fro
|
|
|
3
3
|
import { Store } from "@tanstack/store";
|
|
4
4
|
//#region src/app-hotkeys.d.ts
|
|
5
5
|
type Platform = 'mac' | 'windows' | 'linux';
|
|
6
|
-
type DeclaredOptionKeys = 'conflictBehavior' | 'eventType' | 'ignoreInputs' | 'preventDefault' | 'stopPropagation';
|
|
6
|
+
type DeclaredOptionKeys = 'capture' | 'conflictBehavior' | 'eventType' | 'ignoreInputs' | 'preventDefault' | 'stopPropagation';
|
|
7
7
|
/** Declared options for an app hotkey whose default is a chord. */
|
|
8
8
|
export type AppHotkeyChordOptions = Pick<HotkeyChordOptions, DeclaredOptionKeys | 'requireReset'>;
|
|
9
9
|
/** Declared options for an app hotkey whose default is a sequence. */
|
package/dist/hotkey-manager.d.ts
CHANGED
|
@@ -8,6 +8,14 @@ export type Target = HTMLElement | Document | Window;
|
|
|
8
8
|
* Options shared by chords and sequences.
|
|
9
9
|
*/
|
|
10
10
|
export interface HotkeyOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Listen during capture, before descendant listeners. Defaults to `false` (bubble).
|
|
13
|
+
* With `stopPropagation: true`, a match on an ancestor prevents the event from
|
|
14
|
+
* reaching descendant widgets. Other listeners on the same target still run.
|
|
15
|
+
* Applies to both `keydown` and `keyup`, for chords and sequences.
|
|
16
|
+
* Changing this option preserves held-key reset state and sequence progress.
|
|
17
|
+
*/
|
|
18
|
+
capture?: boolean;
|
|
11
19
|
/** Behavior when this hotkey conflicts with an existing registration on the same target. Defaults to 'warn' */
|
|
12
20
|
conflictBehavior?: ConflictBehavior;
|
|
13
21
|
/**
|
|
@@ -156,6 +164,9 @@ export interface HotkeyRegistrationHandle<TOptions extends HotkeyOptions = Hotke
|
|
|
156
164
|
/**
|
|
157
165
|
* Update options (merged with existing options).
|
|
158
166
|
* Useful for updating `enabled`, `preventDefault`, etc. without re-registering.
|
|
167
|
+
* Changing `capture` moves the listener phase without clearing `requireReset`
|
|
168
|
+
* state or sequence progress. A registration already processed for an event
|
|
169
|
+
* cannot process it again in another phase.
|
|
159
170
|
*/
|
|
160
171
|
setOptions: (options: Partial<TOptions>) => void;
|
|
161
172
|
/** Unregister this hotkey */
|
|
@@ -165,8 +176,8 @@ export interface HotkeyRegistrationHandle<TOptions extends HotkeyOptions = Hotke
|
|
|
165
176
|
* Singleton manager for hotkey registrations, including sequences.
|
|
166
177
|
*
|
|
167
178
|
* This class provides a centralized way to register and manage keyboard
|
|
168
|
-
* hotkeys. It
|
|
169
|
-
*
|
|
179
|
+
* hotkeys. It shares one keydown and one keyup listener per target and
|
|
180
|
+
* capture phase across registrations.
|
|
170
181
|
*
|
|
171
182
|
* Sequences (Vim-style hotkeys such as `['G', 'G']`) match when every step is
|
|
172
183
|
* pressed within the timeout. Modifier-only events (Shift, Control, Alt, or
|
package/dist/hotkey-manager.js
CHANGED
|
@@ -6,6 +6,7 @@ import { formatHotkey } from "./format.js";
|
|
|
6
6
|
import { matchKeyboardEvent } from "./_match.js";
|
|
7
7
|
import { DEFAULT_SEQUENCE_TIMEOUT, areHotkeysEqual } from "./match.js";
|
|
8
8
|
import { isEventForTarget, shouldIgnoreInputEvent } from "./_event-target.js";
|
|
9
|
+
import { TargetListeners } from "./_target-listeners.js";
|
|
9
10
|
import { isRecordingEvent } from "./_recording-guard.js";
|
|
10
11
|
import { defaultHotkeyOptions, getDefaultIgnoreInputs, handleConflict, optionsEqual } from "./_registration.js";
|
|
11
12
|
import { Store } from "@tanstack/store";
|
|
@@ -73,8 +74,8 @@ function formatForMessage(hotkey) {
|
|
|
73
74
|
* Singleton manager for hotkey registrations, including sequences.
|
|
74
75
|
*
|
|
75
76
|
* This class provides a centralized way to register and manage keyboard
|
|
76
|
-
* hotkeys. It
|
|
77
|
-
*
|
|
77
|
+
* hotkeys. It shares one keydown and one keyup listener per target and
|
|
78
|
+
* capture phase across registrations.
|
|
78
79
|
*
|
|
79
80
|
* Sequences (Vim-style hotkeys such as `['G', 'G']`) match when every step is
|
|
80
81
|
* pressed within the timeout. Modifier-only events (Shift, Control, Alt, or
|
|
@@ -123,8 +124,7 @@ var HotkeyManager = class HotkeyManager {
|
|
|
123
124
|
*/
|
|
124
125
|
appHotkeys = new Store(null);
|
|
125
126
|
#platform;
|
|
126
|
-
#
|
|
127
|
-
#targetRegistrations = /* @__PURE__ */ new Map();
|
|
127
|
+
#listeners = new TargetListeners((...args) => this.#processTargetEvent(...args));
|
|
128
128
|
constructor() {
|
|
129
129
|
this.#platform = detectPlatform();
|
|
130
130
|
}
|
|
@@ -214,9 +214,7 @@ var HotkeyManager = class HotkeyManager {
|
|
|
214
214
|
const conflicting = this.#findConflictingRegistration(registration);
|
|
215
215
|
if (conflicting) handleConflict(conflicting.id, formatForMessage(registration.hotkey), options.conflictBehavior ?? "warn", (conflictId) => this.#unregister(conflictId));
|
|
216
216
|
this.registrations.setState((prev) => new Map(prev).set(id, registration));
|
|
217
|
-
|
|
218
|
-
this.#targetRegistrations.get(target).add(id);
|
|
219
|
-
this.#ensureListenersForTarget(target);
|
|
217
|
+
this.#listeners.add(target, !!registration.options.capture, id);
|
|
220
218
|
const manager = this;
|
|
221
219
|
return {
|
|
222
220
|
get id() {
|
|
@@ -241,6 +239,11 @@ var HotkeyManager = class HotkeyManager {
|
|
|
241
239
|
...optionsForKind(reg.kind, newOptions)
|
|
242
240
|
};
|
|
243
241
|
if (optionsEqual(reg.options, options)) return prev;
|
|
242
|
+
const capture = !!reg.options.capture;
|
|
243
|
+
if (capture !== !!options.capture) {
|
|
244
|
+
manager.#listeners.remove(reg.target, capture, id);
|
|
245
|
+
manager.#listeners.add(reg.target, !!options.capture, id);
|
|
246
|
+
}
|
|
244
247
|
return new Map(prev).set(id, {
|
|
245
248
|
...reg,
|
|
246
249
|
options
|
|
@@ -264,58 +267,31 @@ var HotkeyManager = class HotkeyManager {
|
|
|
264
267
|
next.delete(id);
|
|
265
268
|
return next;
|
|
266
269
|
});
|
|
267
|
-
|
|
268
|
-
if (targetRegs) {
|
|
269
|
-
targetRegs.delete(id);
|
|
270
|
-
if (targetRegs.size === 0) this.#removeListenersForTarget(target);
|
|
271
|
-
}
|
|
270
|
+
this.#listeners.remove(target, !!registration.options.capture, id);
|
|
272
271
|
}
|
|
273
|
-
/**
|
|
274
|
-
|
|
275
|
-
*/
|
|
276
|
-
#ensureListenersForTarget(target) {
|
|
277
|
-
if (typeof document === "undefined") return;
|
|
278
|
-
if (this.#targetListeners.has(target)) return;
|
|
279
|
-
const keydown = (event) => this.#processTargetEvent(event, target, "keydown");
|
|
280
|
-
const keyup = (event) => this.#processTargetEvent(event, target, "keyup");
|
|
281
|
-
target.addEventListener("keydown", keydown);
|
|
282
|
-
target.addEventListener("keyup", keyup);
|
|
283
|
-
this.#targetListeners.set(target, {
|
|
284
|
-
keydown,
|
|
285
|
-
keyup
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Removes event listeners for a specific target.
|
|
290
|
-
*/
|
|
291
|
-
#removeListenersForTarget(target) {
|
|
292
|
-
if (typeof document === "undefined") return;
|
|
293
|
-
const listeners = this.#targetListeners.get(target);
|
|
294
|
-
if (!listeners) return;
|
|
295
|
-
target.removeEventListener("keydown", listeners.keydown);
|
|
296
|
-
target.removeEventListener("keyup", listeners.keyup);
|
|
297
|
-
this.#targetListeners.delete(target);
|
|
298
|
-
this.#targetRegistrations.delete(target);
|
|
299
|
-
}
|
|
300
|
-
/** Registrations of one kind on a target, in registration order. */
|
|
301
|
-
#registrationsOn(target, kind) {
|
|
272
|
+
/** Registrations of one kind in the listener phase, in registration order. */
|
|
273
|
+
#registrationsIn(ids, capture, kind) {
|
|
302
274
|
const result = [];
|
|
303
|
-
for (const id of
|
|
275
|
+
for (const id of ids) {
|
|
304
276
|
const registration = this.registrations.state.get(id);
|
|
305
|
-
if (registration?.kind === kind) result.push(registration);
|
|
277
|
+
if (registration?.kind === kind && !!registration.options.capture === capture) result.push(registration);
|
|
306
278
|
}
|
|
307
279
|
return result;
|
|
308
280
|
}
|
|
281
|
+
/** The live registration, unless it was removed or moved to another phase. */
|
|
282
|
+
#currentIn(registration, capture) {
|
|
283
|
+
const current = this.registrations.state.get(registration.id);
|
|
284
|
+
return current && !!current.options.capture === capture ? current : void 0;
|
|
285
|
+
}
|
|
309
286
|
/**
|
|
310
287
|
* Processes keyboard events for a specific target and event type.
|
|
311
288
|
* Chords and sequences match independently; both may fire for one event.
|
|
312
289
|
*/
|
|
313
|
-
#processTargetEvent(event, target, eventType) {
|
|
314
|
-
this.#processChords(event, target, eventType);
|
|
315
|
-
this.#processSequences(event, target, eventType);
|
|
290
|
+
#processTargetEvent(event, target, eventType, capture, ids) {
|
|
291
|
+
this.#processChords(event, target, eventType, capture, this.#registrationsIn(ids, capture, "chord"));
|
|
292
|
+
this.#processSequences(event, target, eventType, capture, this.#registrationsIn(ids, capture, "sequence"));
|
|
316
293
|
}
|
|
317
|
-
#processChords(event, target, eventType) {
|
|
318
|
-
const registrations = this.#registrationsOn(target, "chord");
|
|
294
|
+
#processChords(event, target, eventType, capture, registrations) {
|
|
319
295
|
if (eventType === "keyup") {
|
|
320
296
|
for (const registration of registrations) if (registration.hasFired && this.#shouldResetRegistration(registration, event)) {
|
|
321
297
|
registration.hasFired = false;
|
|
@@ -333,9 +309,11 @@ var HotkeyManager = class HotkeyManager {
|
|
|
333
309
|
bestScore = Math.max(bestScore, result.score);
|
|
334
310
|
}
|
|
335
311
|
}
|
|
336
|
-
for (const
|
|
337
|
-
const match = matches.get(
|
|
312
|
+
for (const snapshot of registrations) {
|
|
313
|
+
const match = matches.get(snapshot.id);
|
|
338
314
|
if (!match?.matched || match.score !== bestScore) continue;
|
|
315
|
+
const registration = this.#currentIn(snapshot, capture);
|
|
316
|
+
if (!registration) continue;
|
|
339
317
|
if (eventType === "keyup") {
|
|
340
318
|
this.#executeCallback(registration, event);
|
|
341
319
|
continue;
|
|
@@ -343,16 +321,15 @@ var HotkeyManager = class HotkeyManager {
|
|
|
343
321
|
if (registration.options.preventDefault) event.preventDefault();
|
|
344
322
|
if (registration.options.stopPropagation) event.stopPropagation();
|
|
345
323
|
if (!registration.options.requireReset || !registration.hasFired) {
|
|
346
|
-
this.#executeCallback(registration, event);
|
|
347
324
|
if (registration.options.requireReset) {
|
|
348
325
|
registration.hasFired = true;
|
|
349
326
|
registration.activeMatch = match.identity;
|
|
350
327
|
}
|
|
328
|
+
this.#executeCallback(registration, event);
|
|
351
329
|
}
|
|
352
330
|
}
|
|
353
331
|
}
|
|
354
|
-
#processSequences(event, target, eventType) {
|
|
355
|
-
const registrations = this.#registrationsOn(target, "sequence");
|
|
332
|
+
#processSequences(event, target, eventType, capture, registrations) {
|
|
356
333
|
if (registrations.length === 0) return;
|
|
357
334
|
if (isRecordingEvent(event)) {
|
|
358
335
|
let changed = false;
|
|
@@ -410,7 +387,10 @@ var HotkeyManager = class HotkeyManager {
|
|
|
410
387
|
}
|
|
411
388
|
}
|
|
412
389
|
if (changed && completed.length === 0) this.#notify();
|
|
413
|
-
for (const
|
|
390
|
+
for (const snapshot of completed) {
|
|
391
|
+
const registration = this.#currentIn(snapshot, capture);
|
|
392
|
+
if (registration) this.#executeCallback(registration, event);
|
|
393
|
+
}
|
|
414
394
|
}
|
|
415
395
|
/**
|
|
416
396
|
* Executes a hotkey callback with proper event handling.
|
|
@@ -517,11 +497,9 @@ var HotkeyManager = class HotkeyManager {
|
|
|
517
497
|
* Destroys the manager and removes all listeners.
|
|
518
498
|
*/
|
|
519
499
|
destroy() {
|
|
520
|
-
|
|
500
|
+
this.#listeners.destroy();
|
|
521
501
|
this.registrations.setState(() => /* @__PURE__ */ new Map());
|
|
522
502
|
this.appHotkeys.setState(() => null);
|
|
523
|
-
this.#targetListeners.clear();
|
|
524
|
-
this.#targetRegistrations.clear();
|
|
525
503
|
}
|
|
526
504
|
};
|
|
527
505
|
/**
|
|
@@ -55,6 +55,7 @@ var KeyStateTracker = class KeyStateTracker {
|
|
|
55
55
|
#heldEntries = /* @__PURE__ */ new Map();
|
|
56
56
|
#keydownListener = null;
|
|
57
57
|
#keyupListener = null;
|
|
58
|
+
#mouseListener = null;
|
|
58
59
|
#blurListener = null;
|
|
59
60
|
constructor() {
|
|
60
61
|
this.#setupListeners();
|
|
@@ -81,6 +82,7 @@ var KeyStateTracker = class KeyStateTracker {
|
|
|
81
82
|
#setupListeners() {
|
|
82
83
|
if (typeof document === "undefined") return;
|
|
83
84
|
this.#keydownListener = (event) => {
|
|
85
|
+
let changed = this.#reconcileModifiers(event);
|
|
84
86
|
const key = normalizeKeyName(event.key);
|
|
85
87
|
const identity = event.code || `key:${key}`;
|
|
86
88
|
if (!this.#heldEntries.has(identity)) {
|
|
@@ -88,8 +90,9 @@ var KeyStateTracker = class KeyStateTracker {
|
|
|
88
90
|
key,
|
|
89
91
|
code: event.code
|
|
90
92
|
});
|
|
91
|
-
|
|
93
|
+
changed = true;
|
|
92
94
|
}
|
|
95
|
+
if (changed) this.#syncState();
|
|
93
96
|
};
|
|
94
97
|
this.#keyupListener = (event) => {
|
|
95
98
|
const key = normalizeKeyName(event.key);
|
|
@@ -98,22 +101,44 @@ var KeyStateTracker = class KeyStateTracker {
|
|
|
98
101
|
if (!event.code) {
|
|
99
102
|
for (const [entryIdentity, entry] of this.#heldEntries) if (entry.key === key) this.#heldEntries.delete(entryIdentity);
|
|
100
103
|
}
|
|
104
|
+
this.#reconcileModifiers(event);
|
|
101
105
|
if (MODIFIER_KEYS.has(key)) {
|
|
102
106
|
for (const [entryIdentity, entry] of this.#heldEntries) if (!MODIFIER_KEYS.has(entry.key)) this.#heldEntries.delete(entryIdentity);
|
|
103
107
|
}
|
|
104
108
|
this.#syncState();
|
|
105
109
|
};
|
|
110
|
+
this.#mouseListener = (event) => {
|
|
111
|
+
if (this.#reconcileModifiers(event)) this.#syncState();
|
|
112
|
+
};
|
|
106
113
|
this.#blurListener = () => {
|
|
107
114
|
if (this.#heldEntries.size > 0) {
|
|
108
115
|
this.#heldEntries.clear();
|
|
109
116
|
this.#syncState();
|
|
110
117
|
}
|
|
111
118
|
};
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
window.addEventListener("keydown", this.#keydownListener, true);
|
|
120
|
+
window.addEventListener("keyup", this.#keyupListener, true);
|
|
121
|
+
window.addEventListener("mousemove", this.#mouseListener, true);
|
|
122
|
+
window.addEventListener("mousedown", this.#mouseListener, true);
|
|
114
123
|
window.addEventListener("blur", this.#blurListener);
|
|
115
124
|
}
|
|
116
125
|
/**
|
|
126
|
+
* Removes modifiers whose release was missed without a window blur.
|
|
127
|
+
* Event modifier state is shared by left/right keys, so a true flag keeps
|
|
128
|
+
* both physical entries until their individual keyup events arrive.
|
|
129
|
+
* Only real user input counts: events dispatched by page code report no
|
|
130
|
+
* modifiers and would clear keys the user is still holding.
|
|
131
|
+
*/
|
|
132
|
+
#reconcileModifiers(event) {
|
|
133
|
+
if (!event.isTrusted) return false;
|
|
134
|
+
let changed = false;
|
|
135
|
+
for (const [identity, entry] of this.#heldEntries) if (entry.key === "Control" && !event.ctrlKey || entry.key === "Alt" && !event.altKey || entry.key === "Shift" && !event.shiftKey || entry.key === "Meta" && !event.metaKey) {
|
|
136
|
+
this.#heldEntries.delete(identity);
|
|
137
|
+
changed = true;
|
|
138
|
+
}
|
|
139
|
+
return changed;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
117
142
|
* Syncs the internal Set to the Store state.
|
|
118
143
|
*/
|
|
119
144
|
#syncState() {
|
|
@@ -130,13 +155,18 @@ var KeyStateTracker = class KeyStateTracker {
|
|
|
130
155
|
#removeListeners() {
|
|
131
156
|
if (typeof document === "undefined") return;
|
|
132
157
|
if (this.#keydownListener) {
|
|
133
|
-
|
|
158
|
+
window.removeEventListener("keydown", this.#keydownListener, true);
|
|
134
159
|
this.#keydownListener = null;
|
|
135
160
|
}
|
|
136
161
|
if (this.#keyupListener) {
|
|
137
|
-
|
|
162
|
+
window.removeEventListener("keyup", this.#keyupListener, true);
|
|
138
163
|
this.#keyupListener = null;
|
|
139
164
|
}
|
|
165
|
+
if (this.#mouseListener) {
|
|
166
|
+
window.removeEventListener("mousemove", this.#mouseListener, true);
|
|
167
|
+
window.removeEventListener("mousedown", this.#mouseListener, true);
|
|
168
|
+
this.#mouseListener = null;
|
|
169
|
+
}
|
|
140
170
|
if (this.#blurListener) {
|
|
141
171
|
window.removeEventListener("blur", this.#blurListener);
|
|
142
172
|
this.#blurListener = null;
|