rx-hotkeys 2.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/hotkeys.d.ts +30 -8
- package/dist/hotkeys.d.ts.map +1 -1
- package/dist/hotkeys.js +97 -38
- package/dist/hotkeys.test.js +89 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -165,7 +165,7 @@ Cleans up all subscriptions and resources. Essential to call to prevent memory l
|
|
|
165
165
|
`KeyCombinationConfig`
|
|
166
166
|
|
|
167
167
|
* `id: string` (required): Unique identifier for the shortcut.
|
|
168
|
-
* `keys: { key: StandardKey; ctrlKey?: boolean; altKey?: boolean; shiftKey?: boolean; metaKey?: boolean; }` (required): Defines the main key (from Keys) and optional modifier keys.
|
|
168
|
+
* `keys: { key: StandardKey; ctrlKey?: boolean; altKey?: boolean; shiftKey?: boolean; metaKey?: boolean; } | StandardKey` (required): Defines the main key (from Keys) and optional modifier keys.
|
|
169
169
|
* `callback: (event?: KeyboardEvent) => void` (required): Function to execute when the shortcut is triggered. The triggering `KeyboardEvent` is passed as an argument.
|
|
170
170
|
* `context?: string | null`: Specifies the context in which this shortcut is active. If `null` or `undefined`, it's a global shortcut.
|
|
171
171
|
* `preventDefault?: boolean`: If true, `event.preventDefault()` will be called when the shortcut triggers. Defaults to `false`.
|
package/dist/hotkeys.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { StandardKey } from "./keys.js";
|
|
2
|
+
export declare enum ShortcutTypes {
|
|
3
|
+
Combination = "combination",
|
|
4
|
+
Sequence = "sequence"
|
|
5
|
+
}
|
|
2
6
|
interface ShortcutConfigBase {
|
|
3
7
|
id: string;
|
|
4
8
|
callback: (event?: KeyboardEvent) => void;
|
|
@@ -7,6 +11,17 @@ interface ShortcutConfigBase {
|
|
|
7
11
|
description?: string;
|
|
8
12
|
}
|
|
9
13
|
export interface KeyCombinationConfig extends ShortcutConfigBase {
|
|
14
|
+
/**
|
|
15
|
+
* Defines the key or key combination.
|
|
16
|
+
* Can be an object specifying the main `key` (from `StandardKey`) and optional
|
|
17
|
+
* modifiers (`ctrlKey`, `altKey`, `shiftKey`, `metaKey`).
|
|
18
|
+
* Example: `{ key: Keys.S, ctrlKey: true }` for Ctrl+S.
|
|
19
|
+
*
|
|
20
|
+
* Alternatively, for a simple key press without any modifiers, this can be
|
|
21
|
+
* a `StandardKey` directly.
|
|
22
|
+
* Example: `Keys.Escape` for the Escape key. When using this shorthand,
|
|
23
|
+
* it implies that no modifier keys (Ctrl, Alt, Shift, Meta) should be active.
|
|
24
|
+
*/
|
|
10
25
|
keys: {
|
|
11
26
|
/**
|
|
12
27
|
* The main key for the combination.
|
|
@@ -23,7 +38,7 @@ export interface KeyCombinationConfig extends ShortcutConfigBase {
|
|
|
23
38
|
altKey?: boolean;
|
|
24
39
|
shiftKey?: boolean;
|
|
25
40
|
metaKey?: boolean;
|
|
26
|
-
};
|
|
41
|
+
} | StandardKey;
|
|
27
42
|
}
|
|
28
43
|
export interface KeySequenceConfig extends ShortcutConfigBase {
|
|
29
44
|
/**
|
|
@@ -90,22 +105,29 @@ export declare class Hotkeys {
|
|
|
90
105
|
private filterByContext;
|
|
91
106
|
private _registerShortcut;
|
|
92
107
|
/**
|
|
93
|
-
* Registers a key combination shortcut (e.g., Ctrl+S, Shift+Enter).
|
|
94
|
-
* The callback is triggered when the specified key and modifier keys are pressed
|
|
108
|
+
* Registers a key combination shortcut (e.g., Ctrl+S, Shift+Enter, or a single key like Escape).
|
|
109
|
+
* The callback is triggered when the specified key and modifier keys (if any) are pressed.
|
|
95
110
|
* @param config - Configuration object for the key combination.
|
|
96
111
|
* See {@link KeyCombinationConfig} for details.
|
|
97
|
-
* The `key` property
|
|
98
|
-
* @returns The ID of the registered shortcut if successful, or `undefined` if the configuration is invalid
|
|
112
|
+
* The `key` property (or the direct `StandardKey` if using shorthand) must be a value from the `Keys` object.
|
|
113
|
+
* @returns The ID of the registered shortcut if successful, or `undefined` if the configuration is invalid.
|
|
99
114
|
* A warning is logged to the console if the configuration is invalid or if a shortcut with the same ID is overwritten.
|
|
100
115
|
* @example
|
|
101
116
|
* ```typescript
|
|
102
|
-
* import { Keys } from
|
|
117
|
+
* import { Keys } from "./keys";
|
|
118
|
+
* // For Ctrl+S
|
|
103
119
|
* keyManager.addCombination({
|
|
104
120
|
* id: "saveFile",
|
|
105
121
|
* keys: { key: Keys.S, ctrlKey: true },
|
|
106
122
|
* callback: () => console.log("File saved!"),
|
|
107
123
|
* context: "editor"
|
|
108
124
|
* });
|
|
125
|
+
* // For just the Escape key
|
|
126
|
+
* keyManager.addCombination({
|
|
127
|
+
* id: "closeModal",
|
|
128
|
+
* keys: Keys.Escape, // Shorthand syntax
|
|
129
|
+
* callback: () => console.log("Modal closed!")
|
|
130
|
+
* });
|
|
109
131
|
* ```
|
|
110
132
|
*/
|
|
111
133
|
addCombination(config: KeyCombinationConfig): string | undefined;
|
|
@@ -120,7 +142,7 @@ export declare class Hotkeys {
|
|
|
120
142
|
* A warning is logged to the console if the configuration is invalid or if a shortcut with the same ID is overwritten.
|
|
121
143
|
* @example
|
|
122
144
|
* ```typescript
|
|
123
|
-
* import { Keys } from
|
|
145
|
+
* import { Keys } from "./keys";
|
|
124
146
|
* keyManager.addSequence({
|
|
125
147
|
* id: "konamiCode",
|
|
126
148
|
* sequence: [Keys.ArrowUp, Keys.ArrowUp, Keys.ArrowDown, Keys.ArrowDown, Keys.A, Keys.B],
|
|
@@ -149,7 +171,7 @@ export declare class Hotkeys {
|
|
|
149
171
|
id: string;
|
|
150
172
|
description?: string;
|
|
151
173
|
context?: string | null;
|
|
152
|
-
type:
|
|
174
|
+
type: ShortcutTypes;
|
|
153
175
|
}[];
|
|
154
176
|
/**
|
|
155
177
|
* Cleans up all active subscriptions and resources used by the Hotkeys instance.
|
package/dist/hotkeys.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hotkeys.d.ts","sourceRoot":"","sources":["../src/hotkeys.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAIxC,UAAU,kBAAkB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC5D,IAAI,EAAE;QACF;;;;;;;;;WASG;QACH,GAAG,EAAE,WAAW,CAAC;QACjB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,OAAO,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;
|
|
1
|
+
{"version":3,"file":"hotkeys.d.ts","sourceRoot":"","sources":["../src/hotkeys.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAIxC,oBAAY,aAAa;IACrB,WAAW,gBAAgB;IAC3B,QAAQ,aAAa;CACxB;AAED,UAAU,kBAAkB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC5D;;;;;;;;;;OAUG;IACH,IAAI,EAAE;QACF;;;;;;;;;WASG;QACH,GAAG,EAAE,WAAW,CAAC;QACjB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,OAAO,CAAC,EAAE,OAAO,CAAC;KACrB,GAAG,WAAW,CAAC;CACnB;AAED,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IACzD;;;;;;;;OAQG;IACH,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAyCD;;;;GAIG;AACH,qBAAa,OAAO;IAChB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAa;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAc;IAEhD,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,cAAc,CAAiC;IACvD,OAAO,CAAC,eAAe,CAA8B;IACrD,OAAO,CAAC,SAAS,CAAU;IAE3B;;;;;OAKG;gBACS,cAAc,GAAE,MAAM,GAAG,IAAW,EAAE,SAAS,GAAE,OAAe;IAe5E;;;;;;OAMG;IACI,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAOnD;;;OAGG;IACI,UAAU,IAAI,MAAM,GAAG,IAAI;IAIlC;;;;OAIG;IACI,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAO1C;;;;OAIG;IACI,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIvC,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,iBAAiB;IAkBzB;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACI,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS;IA6EvE;;;;;;;;;;;;;;;;;;;OAmBG;IACI,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,GAAG,SAAS;IAiHjE;;;;;;OAMG;IACI,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAYlC;;;;;;OAMG;IACI,kBAAkB,IAAI;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,aAAa,CAAA;KAAC,EAAE;IAa/G;;;;;OAKG;IACI,OAAO,IAAI,IAAI;CAOzB"}
|
package/dist/hotkeys.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { fromEvent, BehaviorSubject, EMPTY, filter, map, bufferCount, withLatestFrom, tap, catchError, scan, } from "rxjs";
|
|
2
|
+
// --- Enums, Interfaces and Types ---
|
|
3
|
+
export var ShortcutTypes;
|
|
4
|
+
(function (ShortcutTypes) {
|
|
5
|
+
ShortcutTypes["Combination"] = "combination";
|
|
6
|
+
ShortcutTypes["Sequence"] = "sequence";
|
|
7
|
+
})(ShortcutTypes || (ShortcutTypes = {}));
|
|
2
8
|
// --- Helper function to compare keys ---
|
|
3
9
|
/**
|
|
4
10
|
* Compares a browser event's key with a configured key.
|
|
@@ -15,6 +21,12 @@ function compareKey(eventKey, configuredKey) {
|
|
|
15
21
|
return eventKey === configuredKey;
|
|
16
22
|
}
|
|
17
23
|
// --- Hotkeys Library ---
|
|
24
|
+
var EmitStates;
|
|
25
|
+
(function (EmitStates) {
|
|
26
|
+
EmitStates[EmitStates["Emit"] = 0] = "Emit";
|
|
27
|
+
EmitStates[EmitStates["Ignore"] = 1] = "Ignore";
|
|
28
|
+
EmitStates[EmitStates["InProgress"] = 2] = "InProgress";
|
|
29
|
+
})(EmitStates || (EmitStates = {}));
|
|
18
30
|
/**
|
|
19
31
|
* Manages keyboard shortcuts for web applications.
|
|
20
32
|
* Allows registration of single key combinations (e.g., Ctrl+S) and key sequences (e.g., g -> i).
|
|
@@ -36,7 +48,7 @@ export class Hotkeys {
|
|
|
36
48
|
constructor(initialContext = null, debugMode = false) {
|
|
37
49
|
this.debugMode = debugMode;
|
|
38
50
|
if (typeof document === "undefined" || typeof performance === "undefined") {
|
|
39
|
-
throw new Error(`${Hotkeys.LOG_PREFIX} Hotkeys can only be used in a browser environment with global
|
|
51
|
+
throw new Error(`${Hotkeys.LOG_PREFIX} Hotkeys can only be used in a browser environment with global "document" and "performance" objects.`);
|
|
40
52
|
}
|
|
41
53
|
this.keydown$ = fromEvent(document, Hotkeys.KEYDOWN_EVENT);
|
|
42
54
|
this.activeContext$ = new BehaviorSubject(initialContext);
|
|
@@ -73,7 +85,7 @@ export class Hotkeys {
|
|
|
73
85
|
setDebugMode(enable) {
|
|
74
86
|
this.debugMode = enable;
|
|
75
87
|
if (this.debugMode) {
|
|
76
|
-
console.log(`${Hotkeys.LOG_PREFIX} Debug mode ${enable ?
|
|
88
|
+
console.log(`${Hotkeys.LOG_PREFIX} Debug mode ${enable ? "enabled" : "disabled"}.`);
|
|
77
89
|
}
|
|
78
90
|
}
|
|
79
91
|
/**
|
|
@@ -87,7 +99,8 @@ export class Hotkeys {
|
|
|
87
99
|
filterByContext(source$, context) {
|
|
88
100
|
return source$.pipe(withLatestFrom(this.activeContext$), filter(([/* event */ , activeCtx]) => context == null || context === activeCtx), map(([event, /* _activeCtx */]) => event));
|
|
89
101
|
}
|
|
90
|
-
_registerShortcut(config, subscription, type,
|
|
102
|
+
_registerShortcut(config, subscription, type, // Changed to use Enum
|
|
103
|
+
detailsForLog) {
|
|
91
104
|
const existingShortcut = this.activeShortcuts.get(config.id);
|
|
92
105
|
if (existingShortcut) {
|
|
93
106
|
console.warn(`${Hotkeys.LOG_PREFIX} Shortcut with ID "${config.id}" already exists. It will be overwritten.`);
|
|
@@ -100,35 +113,81 @@ export class Hotkeys {
|
|
|
100
113
|
return config.id;
|
|
101
114
|
}
|
|
102
115
|
/**
|
|
103
|
-
* Registers a key combination shortcut (e.g., Ctrl+S, Shift+Enter).
|
|
104
|
-
* The callback is triggered when the specified key and modifier keys are pressed
|
|
116
|
+
* Registers a key combination shortcut (e.g., Ctrl+S, Shift+Enter, or a single key like Escape).
|
|
117
|
+
* The callback is triggered when the specified key and modifier keys (if any) are pressed.
|
|
105
118
|
* @param config - Configuration object for the key combination.
|
|
106
119
|
* See {@link KeyCombinationConfig} for details.
|
|
107
|
-
* The `key` property
|
|
108
|
-
* @returns The ID of the registered shortcut if successful, or `undefined` if the configuration is invalid
|
|
120
|
+
* The `key` property (or the direct `StandardKey` if using shorthand) must be a value from the `Keys` object.
|
|
121
|
+
* @returns The ID of the registered shortcut if successful, or `undefined` if the configuration is invalid.
|
|
109
122
|
* A warning is logged to the console if the configuration is invalid or if a shortcut with the same ID is overwritten.
|
|
110
123
|
* @example
|
|
111
124
|
* ```typescript
|
|
112
|
-
* import { Keys } from
|
|
125
|
+
* import { Keys } from "./keys";
|
|
126
|
+
* // For Ctrl+S
|
|
113
127
|
* keyManager.addCombination({
|
|
114
128
|
* id: "saveFile",
|
|
115
129
|
* keys: { key: Keys.S, ctrlKey: true },
|
|
116
130
|
* callback: () => console.log("File saved!"),
|
|
117
131
|
* context: "editor"
|
|
118
132
|
* });
|
|
133
|
+
* // For just the Escape key
|
|
134
|
+
* keyManager.addCombination({
|
|
135
|
+
* id: "closeModal",
|
|
136
|
+
* keys: Keys.Escape, // Shorthand syntax
|
|
137
|
+
* callback: () => console.log("Modal closed!")
|
|
138
|
+
* });
|
|
119
139
|
* ```
|
|
120
140
|
*/
|
|
121
141
|
addCombination(config) {
|
|
122
142
|
const { keys, callback, context, preventDefault = false, id } = config;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
143
|
+
let configuredMainKey;
|
|
144
|
+
let ctrlKeyConfig;
|
|
145
|
+
let altKeyConfig;
|
|
146
|
+
let shiftKeyConfig;
|
|
147
|
+
let metaKeyConfig;
|
|
148
|
+
let keyDetailsForLog;
|
|
149
|
+
if (typeof keys === "string") {
|
|
150
|
+
// Shorthand: keys is StandardKey, implying no modifiers should be active
|
|
151
|
+
if (keys.trim() === "") {
|
|
152
|
+
console.warn(`${Hotkeys.LOG_PREFIX} Invalid "keys" (shorthand) for combination shortcut "${id}". Key string must not be empty. Shortcut not added.`);
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
configuredMainKey = keys;
|
|
156
|
+
ctrlKeyConfig = false; // Shorthand implies no modifiers
|
|
157
|
+
altKeyConfig = false;
|
|
158
|
+
shiftKeyConfig = false;
|
|
159
|
+
metaKeyConfig = false;
|
|
160
|
+
keyDetailsForLog = `key: "${keys}" (no modifiers implied)`;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
// Object form
|
|
164
|
+
if (!keys || !keys.key || typeof keys.key !== "string" || keys.key.trim() === "") {
|
|
165
|
+
console.warn(`${Hotkeys.LOG_PREFIX} Invalid "keys.key" for combination shortcut "${id}". Key must be a non-empty value from Keys. Shortcut not added.`);
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
configuredMainKey = keys.key;
|
|
169
|
+
ctrlKeyConfig = keys.ctrlKey;
|
|
170
|
+
altKeyConfig = keys.altKey;
|
|
171
|
+
shiftKeyConfig = keys.shiftKey;
|
|
172
|
+
metaKeyConfig = keys.metaKey;
|
|
173
|
+
keyDetailsForLog = `key: "${keys.key}"` +
|
|
174
|
+
(keys.ctrlKey !== undefined ? `, ctrlKey: ${keys.ctrlKey}` : "") +
|
|
175
|
+
(keys.altKey !== undefined ? `, altKey: ${keys.altKey}` : "") +
|
|
176
|
+
(keys.shiftKey !== undefined ? `, shiftKey: ${keys.shiftKey}` : "") +
|
|
177
|
+
(keys.metaKey !== undefined ? `, metaKey: ${keys.metaKey}` : "");
|
|
126
178
|
}
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
179
|
+
const shortcut$ = this.filterByContext(this.keydown$, context).pipe(filter(event => {
|
|
180
|
+
// For shorthand (where modifiers are explicitly false), we want an exact match.
|
|
181
|
+
// For object form:
|
|
182
|
+
// - if modifier is true, event.modifier must be true.
|
|
183
|
+
// - if modifier is false, event.modifier must be false.
|
|
184
|
+
// - if modifier is undefined, we don't care about event.modifier.
|
|
185
|
+
const ctrlMatch = (ctrlKeyConfig === undefined) ? true : (event.ctrlKey === ctrlKeyConfig);
|
|
186
|
+
const altMatch = (altKeyConfig === undefined) ? true : (event.altKey === altKeyConfig);
|
|
187
|
+
const shiftMatch = (shiftKeyConfig === undefined) ? true : (event.shiftKey === shiftKeyConfig);
|
|
188
|
+
const metaMatch = (metaKeyConfig === undefined) ? true : (event.metaKey === metaKeyConfig);
|
|
189
|
+
return ctrlMatch && altMatch && shiftMatch && metaMatch;
|
|
190
|
+
}), filter(event => compareKey(event.key, configuredMainKey)), tap(event => {
|
|
132
191
|
if (this.debugMode) {
|
|
133
192
|
const preventAction = preventDefault ? ", preventing default" : "";
|
|
134
193
|
console.log(`${Hotkeys.LOG_PREFIX} Combination "${id}" triggered${preventAction}.`);
|
|
@@ -147,12 +206,7 @@ export class Hotkeys {
|
|
|
147
206
|
console.error(`${Hotkeys.LOG_PREFIX} Error in user callback for combination shortcut "${id}":`, e);
|
|
148
207
|
}
|
|
149
208
|
});
|
|
150
|
-
|
|
151
|
-
(keys.ctrlKey !== undefined ? `, ctrlKey: ${keys.ctrlKey}` : "") +
|
|
152
|
-
(keys.altKey !== undefined ? `, altKey: ${keys.altKey}` : "") +
|
|
153
|
-
(keys.shiftKey !== undefined ? `, shiftKey: ${keys.shiftKey}` : "") +
|
|
154
|
-
(keys.metaKey !== undefined ? `, metaKey: ${keys.metaKey}` : "");
|
|
155
|
-
return this._registerShortcut(config, subscription, "Combination", `Keys: { ${keyDetails} }`);
|
|
209
|
+
return this._registerShortcut(config, subscription, ShortcutTypes.Combination, `Keys: { ${keyDetailsForLog} }`); // Use Enum
|
|
156
210
|
}
|
|
157
211
|
/**
|
|
158
212
|
* Registers a key sequence shortcut (e.g., g -> i, or ArrowUp -> ArrowUp -> ArrowDown).
|
|
@@ -165,7 +219,7 @@ export class Hotkeys {
|
|
|
165
219
|
* A warning is logged to the console if the configuration is invalid or if a shortcut with the same ID is overwritten.
|
|
166
220
|
* @example
|
|
167
221
|
* ```typescript
|
|
168
|
-
* import { Keys } from
|
|
222
|
+
* import { Keys } from "./keys";
|
|
169
223
|
* keyManager.addSequence({
|
|
170
224
|
* id: "konamiCode",
|
|
171
225
|
* sequence: [Keys.ArrowUp, Keys.ArrowUp, Keys.ArrowDown, Keys.ArrowDown, Keys.A, Keys.B],
|
|
@@ -180,7 +234,7 @@ export class Hotkeys {
|
|
|
180
234
|
console.warn(`${Hotkeys.LOG_PREFIX} Sequence for shortcut "${id}" is empty or invalid. Shortcut not added.`);
|
|
181
235
|
return undefined;
|
|
182
236
|
}
|
|
183
|
-
if (sequence.some(key => typeof key !==
|
|
237
|
+
if (sequence.some(key => typeof key !== "string" || key.trim() === "")) {
|
|
184
238
|
console.warn(`${Hotkeys.LOG_PREFIX} Invalid key in sequence for shortcut "${id}". All keys must be non-empty strings from Keys. Shortcut not added.`);
|
|
185
239
|
return undefined;
|
|
186
240
|
}
|
|
@@ -192,48 +246,52 @@ export class Hotkeys {
|
|
|
192
246
|
shortcut$ = baseKeydownStream$.pipe(scan((acc, event) => {
|
|
193
247
|
let { matchedEvents, lastEventTime } = acc;
|
|
194
248
|
const currentTime = performance.now();
|
|
195
|
-
if (acc.emitState ===
|
|
249
|
+
if (acc.emitState === EmitStates.Emit) {
|
|
196
250
|
matchedEvents = [];
|
|
197
251
|
lastEventTime = 0;
|
|
198
252
|
}
|
|
199
253
|
if (matchedEvents.length > 0 && (currentTime - lastEventTime > sequenceTimeoutMs)) {
|
|
200
254
|
if (this.debugMode) {
|
|
201
|
-
console.log(`${Hotkeys.LOG_PREFIX} Sequence "${id}" (timeout: ${sequenceTimeoutMs}ms) attempt timed out. Matched: ${matchedEvents.map(e => e.key).join(
|
|
255
|
+
console.log(`${Hotkeys.LOG_PREFIX} Sequence "${id}" (timeout: ${sequenceTimeoutMs}ms) attempt timed out. Matched: ${matchedEvents.map(e => e.key).join(",")}. Resetting.`);
|
|
202
256
|
}
|
|
203
257
|
matchedEvents = [];
|
|
204
258
|
}
|
|
205
259
|
const nextExpectedKeyIndex = matchedEvents.length;
|
|
206
260
|
if (nextExpectedKeyIndex >= sequenceLength) {
|
|
261
|
+
// Sequence was already emitted or buffer is too long (should not happen if reset correctly)
|
|
262
|
+
// Start new sequence if current key matches the first key of the sequence
|
|
207
263
|
if (sequenceLength > 0 && compareKey(event.key, configuredSequence[0])) {
|
|
208
|
-
return { matchedEvents: [event], lastEventTime: currentTime, emitState:
|
|
264
|
+
return { matchedEvents: [event], lastEventTime: currentTime, emitState: EmitStates.InProgress };
|
|
209
265
|
}
|
|
210
|
-
return { matchedEvents: [], lastEventTime: 0, emitState:
|
|
266
|
+
return { matchedEvents: [], lastEventTime: 0, emitState: EmitStates.Ignore };
|
|
211
267
|
}
|
|
212
268
|
if (compareKey(event.key, configuredSequence[nextExpectedKeyIndex])) {
|
|
213
269
|
const newMatchedEvents = [...matchedEvents, event];
|
|
214
270
|
if (newMatchedEvents.length === sequenceLength) {
|
|
215
|
-
if (this.debugMode)
|
|
271
|
+
if (this.debugMode && !acc.emitState)
|
|
216
272
|
console.log(`${Hotkeys.LOG_PREFIX} Sequence "${id}" (timeout: ${sequenceTimeoutMs}ms) matched.`);
|
|
217
|
-
return { matchedEvents: newMatchedEvents, lastEventTime: currentTime, emitState:
|
|
273
|
+
return { matchedEvents: newMatchedEvents, lastEventTime: currentTime, emitState: EmitStates.Emit };
|
|
218
274
|
}
|
|
219
275
|
else {
|
|
220
|
-
return { matchedEvents: newMatchedEvents, lastEventTime: currentTime, emitState:
|
|
276
|
+
return { matchedEvents: newMatchedEvents, lastEventTime: currentTime, emitState: EmitStates.InProgress };
|
|
221
277
|
}
|
|
222
278
|
}
|
|
223
279
|
else {
|
|
280
|
+
// If current key breaks sequence, check if it starts a new sequence
|
|
224
281
|
if (matchedEvents.length > 0 && this.debugMode) {
|
|
225
|
-
console.log(`${Hotkeys.LOG_PREFIX} Sequence "${id}" (timeout: ${sequenceTimeoutMs}ms) broken by key "${event.key}". Matched: ${matchedEvents.map(e => e.key).join(
|
|
282
|
+
console.log(`${Hotkeys.LOG_PREFIX} Sequence "${id}" (timeout: ${sequenceTimeoutMs}ms) broken by key "${event.key}". Matched: ${matchedEvents.map(e => e.key).join(",")}. Resetting.`);
|
|
226
283
|
}
|
|
227
284
|
if (sequenceLength > 0 && compareKey(event.key, configuredSequence[0])) {
|
|
228
|
-
return { matchedEvents: [event], lastEventTime: currentTime, emitState:
|
|
285
|
+
return { matchedEvents: [event], lastEventTime: currentTime, emitState: EmitStates.InProgress };
|
|
229
286
|
}
|
|
230
287
|
else {
|
|
231
|
-
return { matchedEvents: [], lastEventTime: 0, emitState:
|
|
288
|
+
return { matchedEvents: [], lastEventTime: 0, emitState: EmitStates.Ignore };
|
|
232
289
|
}
|
|
233
290
|
}
|
|
234
|
-
}, { matchedEvents: [], lastEventTime: 0, emitState:
|
|
291
|
+
}, { matchedEvents: [], lastEventTime: 0, emitState: EmitStates.Ignore }), filter(state => state.emitState === EmitStates.Emit), map(state => state.matchedEvents));
|
|
235
292
|
}
|
|
236
293
|
else {
|
|
294
|
+
// No timeout logic: simple buffer-based matching
|
|
237
295
|
shortcut$ = baseKeydownStream$.pipe(bufferCount(sequenceLength, 1), filter((events) => {
|
|
238
296
|
if (events.length < sequenceLength)
|
|
239
297
|
return false;
|
|
@@ -255,6 +313,7 @@ export class Hotkeys {
|
|
|
255
313
|
}));
|
|
256
314
|
const subscription = finalShortcut$.subscribe((events) => {
|
|
257
315
|
try {
|
|
316
|
+
// Ensure callback receives the last event of the sequence, similar to combination.
|
|
258
317
|
if (events.length > 0)
|
|
259
318
|
callback(events[events.length - 1]);
|
|
260
319
|
}
|
|
@@ -262,8 +321,8 @@ export class Hotkeys {
|
|
|
262
321
|
console.error(`${Hotkeys.LOG_PREFIX} Error in user callback for sequence shortcut "${id}":`, e);
|
|
263
322
|
}
|
|
264
323
|
});
|
|
265
|
-
const logDetails = `Sequence: ${sequence.join(" -> ")}${sequenceTimeoutMs && sequenceTimeoutMs > 0 ? ` (timeout: ${sequenceTimeoutMs}ms)` :
|
|
266
|
-
return this._registerShortcut(config, subscription,
|
|
324
|
+
const logDetails = `Sequence: ${sequence.join(" -> ")}${sequenceTimeoutMs && sequenceTimeoutMs > 0 ? ` (timeout: ${sequenceTimeoutMs}ms)` : ""}`;
|
|
325
|
+
return this._registerShortcut(config, subscription, ShortcutTypes.Sequence, logDetails); // Use Enum
|
|
267
326
|
}
|
|
268
327
|
/**
|
|
269
328
|
* Removes a registered shortcut by its ID.
|
|
@@ -298,7 +357,7 @@ export class Hotkeys {
|
|
|
298
357
|
id,
|
|
299
358
|
description: activeShortcut.config.description,
|
|
300
359
|
context: activeShortcut.config.context,
|
|
301
|
-
type:
|
|
360
|
+
type: ("sequence" in activeShortcut.config) ? ShortcutTypes.Sequence : ShortcutTypes.Combination // Use Enum values
|
|
302
361
|
});
|
|
303
362
|
}
|
|
304
363
|
return shortcuts;
|
package/dist/hotkeys.test.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { describe, it, before, beforeEach, afterEach, mock } from "node:test";
|
|
2
2
|
import assert from "node:assert";
|
|
3
|
-
|
|
4
|
-
import { Hotkeys } from "./hotkeys.js";
|
|
5
|
-
// Importing Keys and StandardKey from the separate keys.js file
|
|
3
|
+
import { Hotkeys, ShortcutTypes } from "./hotkeys.js";
|
|
6
4
|
import { Keys } from "./keys.js";
|
|
7
5
|
import { fromEvent, BehaviorSubject } from "rxjs";
|
|
8
6
|
import { createMockFn, dispatchKeyEvent } from "./testutils.js";
|
|
@@ -27,14 +25,14 @@ before(() => {
|
|
|
27
25
|
// @ts-ignore
|
|
28
26
|
global.fromEvent = fromEvent;
|
|
29
27
|
// @ts-ignore
|
|
30
|
-
if (typeof global.performance ===
|
|
28
|
+
if (typeof global.performance === "undefined") {
|
|
31
29
|
// @ts-ignore
|
|
32
30
|
global.performance = {};
|
|
33
31
|
}
|
|
34
32
|
// @ts-ignore
|
|
35
33
|
originalPerformanceNow = global.performance.now;
|
|
36
34
|
// @ts-ignore
|
|
37
|
-
if (typeof global.performance.now !==
|
|
35
|
+
if (typeof global.performance.now !== "function") {
|
|
38
36
|
// @ts-ignore
|
|
39
37
|
global.performance.now = (() => {
|
|
40
38
|
const start = Date.now();
|
|
@@ -70,6 +68,7 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
70
68
|
global.performance.now = originalPerformanceNow;
|
|
71
69
|
}
|
|
72
70
|
});
|
|
71
|
+
// ... (Initialization and Basic Context tests remain the same) ...
|
|
73
72
|
describe("Initialization and Basic Context", () => {
|
|
74
73
|
it("should initialize without errors", () => {
|
|
75
74
|
assert(keyManager instanceof Hotkeys);
|
|
@@ -92,11 +91,11 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
92
91
|
const consoleLogMock = mock.method(console, "log");
|
|
93
92
|
keyManager.setDebugMode(true);
|
|
94
93
|
keyManager.setContext("debug_test");
|
|
95
|
-
assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes(
|
|
94
|
+
assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes(`Context changed to "debug_test"`)));
|
|
96
95
|
consoleLogMock.mock.resetCalls();
|
|
97
96
|
keyManager.setDebugMode(false);
|
|
98
97
|
keyManager.setContext("no_debug_test");
|
|
99
|
-
assert.ok(!consoleLogMock.mock.calls.some(call => call.arguments[0].includes(
|
|
98
|
+
assert.ok(!consoleLogMock.mock.calls.some(call => call.arguments[0].includes(`Context changed to "no_debug_test"`)));
|
|
100
99
|
consoleLogMock.mock.restore();
|
|
101
100
|
});
|
|
102
101
|
});
|
|
@@ -111,13 +110,12 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
111
110
|
dispatchKeyEvent("A");
|
|
112
111
|
assert.strictEqual(mockCallback.calledCount, 1, "Callback for 'A' not called");
|
|
113
112
|
});
|
|
114
|
-
it("should return undefined and warn if keys.key is null or undefined (runtime check)", () => {
|
|
115
|
-
// This test checks runtime robustness if `any` is used to bypass StandardKey
|
|
113
|
+
it("should return undefined and warn if keys.key is null or undefined (runtime check in object form)", () => {
|
|
116
114
|
const config = { id: "nullKey", keys: { key: null }, callback: mockCallback };
|
|
117
115
|
const result = keyManager.addCombination(config);
|
|
118
116
|
assert.strictEqual(result, undefined, "Should return undefined for null key");
|
|
119
117
|
assert.strictEqual(consoleWarnMock.mock.calls.length, 1);
|
|
120
|
-
assert.ok(consoleWarnMock.mock.calls[0].arguments[0].includes(
|
|
118
|
+
assert.ok(consoleWarnMock.mock.calls[0].arguments[0].includes(`Invalid "keys.key" for combination shortcut "nullKey"`));
|
|
121
119
|
});
|
|
122
120
|
it("should pass the KeyboardEvent to the callback", () => {
|
|
123
121
|
const config = { id: "eventPass", keys: { key: Keys.E }, callback: mockCallback };
|
|
@@ -140,14 +138,14 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
140
138
|
dispatchKeyEvent("a", { ctrlKey: false });
|
|
141
139
|
assert.strictEqual(mockCallback.calledCount, 1);
|
|
142
140
|
});
|
|
143
|
-
it("should trigger for special keys like Escape", () => {
|
|
144
|
-
const config = { id: "
|
|
141
|
+
it("should trigger for special keys like Escape (object form)", () => {
|
|
142
|
+
const config = { id: "escapeKeyObj", keys: { key: Keys.Escape }, callback: mockCallback };
|
|
145
143
|
keyManager.addCombination(config);
|
|
146
144
|
dispatchKeyEvent("Escape"); // Event key matches Keys.Escape
|
|
147
145
|
assert.strictEqual(mockCallback.calledCount, 1);
|
|
148
146
|
});
|
|
149
|
-
it("should handle preventDefault correctly", () => {
|
|
150
|
-
const config = { id: "
|
|
147
|
+
it("should handle preventDefault correctly (object form)", () => {
|
|
148
|
+
const config = { id: "preventAObj", keys: { key: Keys.A }, callback: mockCallback, preventDefault: true };
|
|
151
149
|
keyManager.addCombination(config);
|
|
152
150
|
const event = dispatchKeyEvent("a");
|
|
153
151
|
assert.strictEqual(mockCallback.calledCount, 1);
|
|
@@ -173,25 +171,92 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
173
171
|
keyManager.addCombination({ id: "workingCombo", keys: { key: Keys.W }, callback: workingCallback });
|
|
174
172
|
dispatchKeyEvent("e");
|
|
175
173
|
assert.strictEqual(consoleErrorMock.mock.calls.length, 1);
|
|
176
|
-
assert.ok(consoleErrorMock.mock.calls[0].arguments[0].includes(
|
|
174
|
+
assert.ok(consoleErrorMock.mock.calls[0].arguments[0].includes(`Error in user callback for combination shortcut "errorCombo"`));
|
|
177
175
|
dispatchKeyEvent("w");
|
|
178
176
|
assert.strictEqual(workingCallback.calledCount, 1);
|
|
179
177
|
});
|
|
178
|
+
describe("addCombination - Shorthand Syntax", () => {
|
|
179
|
+
it("should trigger callback for a simple key using shorthand (e.g., Keys.X)", () => {
|
|
180
|
+
const config = { id: "shorthandX", keys: Keys.X, callback: mockCallback };
|
|
181
|
+
keyManager.addCombination(config);
|
|
182
|
+
dispatchKeyEvent(Keys.X.toLowerCase());
|
|
183
|
+
assert.strictEqual(mockCallback.calledCount, 1, "Callback for 'x' (shorthand) not called");
|
|
184
|
+
mockCallback.mockClear();
|
|
185
|
+
dispatchKeyEvent(Keys.X);
|
|
186
|
+
assert.strictEqual(mockCallback.calledCount, 1, "Callback for 'X' (shorthand) not called");
|
|
187
|
+
});
|
|
188
|
+
it("should NOT trigger callback for shorthand if modifier is pressed", () => {
|
|
189
|
+
const config = { id: "shorthandY", keys: Keys.Y, callback: mockCallback };
|
|
190
|
+
keyManager.addCombination(config);
|
|
191
|
+
dispatchKeyEvent(Keys.Y, { ctrlKey: true });
|
|
192
|
+
assert.strictEqual(mockCallback.calledCount, 0, "Callback for 'y' (shorthand) should not be called with Ctrl");
|
|
193
|
+
mockCallback.mockClear();
|
|
194
|
+
dispatchKeyEvent(Keys.Y, { altKey: true });
|
|
195
|
+
assert.strictEqual(mockCallback.calledCount, 0, "Callback for 'y' (shorthand) should not be called with Alt");
|
|
196
|
+
mockCallback.mockClear();
|
|
197
|
+
dispatchKeyEvent(Keys.Y, { shiftKey: true });
|
|
198
|
+
assert.strictEqual(mockCallback.calledCount, 0, "Callback for 'y' (shorthand) should not be called with Shift");
|
|
199
|
+
mockCallback.mockClear();
|
|
200
|
+
dispatchKeyEvent(Keys.Y, { metaKey: true });
|
|
201
|
+
assert.strictEqual(mockCallback.calledCount, 0, "Callback for 'y' (shorthand) should not be called with Meta");
|
|
202
|
+
});
|
|
203
|
+
it("should trigger callback for shorthand if ONLY the key is pressed (no modifiers)", () => {
|
|
204
|
+
const config = { id: "shorthandZ", keys: Keys.Z, callback: mockCallback };
|
|
205
|
+
keyManager.addCombination(config);
|
|
206
|
+
dispatchKeyEvent(Keys.Z, { ctrlKey: false, altKey: false, shiftKey: false, metaKey: false });
|
|
207
|
+
assert.strictEqual(mockCallback.calledCount, 1);
|
|
208
|
+
});
|
|
209
|
+
it("should handle preventDefault correctly for shorthand", () => {
|
|
210
|
+
const config = { id: "shorthandPrevent", keys: Keys.P, callback: mockCallback, preventDefault: true };
|
|
211
|
+
keyManager.addCombination(config);
|
|
212
|
+
const event = dispatchKeyEvent(Keys.P.toLowerCase());
|
|
213
|
+
assert.strictEqual(mockCallback.calledCount, 1);
|
|
214
|
+
assert.strictEqual(event.defaultPrevented, true);
|
|
215
|
+
});
|
|
216
|
+
it("should respect context for shorthand", () => {
|
|
217
|
+
const config = { id: "shorthandContext", keys: Keys.C, callback: mockCallback, context: "editor" };
|
|
218
|
+
keyManager.addCombination(config);
|
|
219
|
+
keyManager.setContext("other");
|
|
220
|
+
dispatchKeyEvent(Keys.C.toLowerCase());
|
|
221
|
+
assert.strictEqual(mockCallback.calledCount, 0);
|
|
222
|
+
keyManager.setContext("editor");
|
|
223
|
+
dispatchKeyEvent(Keys.C.toLowerCase());
|
|
224
|
+
assert.strictEqual(mockCallback.calledCount, 1);
|
|
225
|
+
});
|
|
226
|
+
it("should return undefined and warn if shorthand key is an empty string", () => {
|
|
227
|
+
const config = { id: "emptyShorthand", keys: "", callback: mockCallback };
|
|
228
|
+
const result = keyManager.addCombination(config);
|
|
229
|
+
assert.strictEqual(result, undefined);
|
|
230
|
+
assert.strictEqual(consoleWarnMock.mock.calls.length, 1);
|
|
231
|
+
assert.ok(consoleWarnMock.mock.calls[0].arguments[0].includes(`Invalid "keys" (shorthand) for combination shortcut "emptyShorthand"`));
|
|
232
|
+
});
|
|
233
|
+
it("should correctly log shorthand key details in debug mode", () => {
|
|
234
|
+
const consoleLogMock = mock.method(console, "log");
|
|
235
|
+
keyManager.setDebugMode(true);
|
|
236
|
+
const config = { id: "debugShorthand", keys: Keys.D, callback: mockCallback };
|
|
237
|
+
keyManager.addCombination(config);
|
|
238
|
+
const logMessage = consoleLogMock.mock.calls.find(call => call.arguments[0].includes(`combination shortcut "debugShorthand" added`));
|
|
239
|
+
assert.ok(logMessage, "Debug log for adding shortcut not found");
|
|
240
|
+
assert.ok(logMessage.arguments[0].includes(`Keys: { key: "D" (no modifiers implied) }`), `Log message content mismatch: ${logMessage.arguments[0]}`);
|
|
241
|
+
consoleLogMock.mock.restore();
|
|
242
|
+
keyManager.setDebugMode(false);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
180
245
|
});
|
|
181
246
|
describe("addSequence", () => {
|
|
182
247
|
it("should trigger callback for a simple key sequence", () => {
|
|
183
248
|
const config = { id: "seqGI", sequence: [Keys.G, Keys.I], callback: mockCallback };
|
|
184
249
|
const result = keyManager.addSequence(config);
|
|
185
250
|
assert.strictEqual(result, "seqGI");
|
|
186
|
-
dispatchKeyEvent("g"); // Dispatch
|
|
187
|
-
dispatchKeyEvent("i"); // Dispatch
|
|
251
|
+
dispatchKeyEvent("g"); // Dispatch "g" (lowercase)
|
|
252
|
+
dispatchKeyEvent("i"); // Dispatch "i" (lowercase)
|
|
188
253
|
assert.strictEqual(mockCallback.calledCount, 1);
|
|
189
254
|
});
|
|
190
255
|
it("should trigger callback for Konami code using Keys", () => {
|
|
191
256
|
const konamiSequence = [
|
|
192
257
|
Keys.ArrowUp, Keys.ArrowUp, Keys.ArrowDown, Keys.ArrowDown,
|
|
193
258
|
Keys.ArrowLeft, Keys.ArrowRight, Keys.ArrowLeft, Keys.ArrowRight,
|
|
194
|
-
Keys.B, Keys.A // Using
|
|
259
|
+
Keys.B, Keys.A // Using "B" and "A" from Keys
|
|
195
260
|
];
|
|
196
261
|
const config = { id: "konami", sequence: konamiSequence, callback: mockCallback };
|
|
197
262
|
keyManager.addSequence(config);
|
|
@@ -236,7 +301,7 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
236
301
|
dispatchKeyEvent("e");
|
|
237
302
|
dispatchKeyEvent("s");
|
|
238
303
|
assert.strictEqual(consoleErrorMock.mock.calls.length, 1);
|
|
239
|
-
assert.ok(consoleErrorMock.mock.calls[0].arguments[0].includes(
|
|
304
|
+
assert.ok(consoleErrorMock.mock.calls[0].arguments[0].includes(`Error in user callback for sequence shortcut "errorSeq"`));
|
|
240
305
|
});
|
|
241
306
|
describe("Sequence Contextual Triggering", () => {
|
|
242
307
|
let editorSequenceConfig;
|
|
@@ -317,17 +382,17 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
317
382
|
});
|
|
318
383
|
});
|
|
319
384
|
describe("getActiveShortcuts", () => {
|
|
320
|
-
it("should return active combination and sequence shortcuts", () => {
|
|
385
|
+
it("should return active combination and sequence shortcuts with enum types", () => {
|
|
321
386
|
keyManager.addCombination({ id: "combo1", keys: { key: Keys.A }, callback: createMockFn(), description: "Test A" });
|
|
322
387
|
keyManager.addSequence({ id: "seq1", sequence: [Keys.B, Keys.C], callback: createMockFn(), context: "modal", description: "Test BC" });
|
|
323
388
|
const active = keyManager.getActiveShortcuts();
|
|
324
389
|
assert.strictEqual(active.length, 2);
|
|
325
390
|
const combo = active.find(s => s.id === "combo1");
|
|
326
391
|
assert.ok(combo);
|
|
327
|
-
assert.strictEqual(combo.type,
|
|
392
|
+
assert.strictEqual(combo.type, ShortcutTypes.Combination); // Use Enum for comparison
|
|
328
393
|
const seq = active.find(s => s.id === "seq1");
|
|
329
394
|
assert.ok(seq);
|
|
330
|
-
assert.strictEqual(seq.type,
|
|
395
|
+
assert.strictEqual(seq.type, ShortcutTypes.Sequence); // Use Enum for comparison
|
|
331
396
|
});
|
|
332
397
|
});
|
|
333
398
|
describe("hasShortcut", () => {
|
|
@@ -347,10 +412,10 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
347
412
|
keyManager.addCombination({ id: "destroyTestCombo", keys: { key: Keys.D }, callback: mockCallback });
|
|
348
413
|
keyManager.addSequence({ id: "destroyTestSeq", sequence: [Keys.X, Keys.Y], callback: mockCallback });
|
|
349
414
|
// @ts-ignore
|
|
350
|
-
assert.strictEqual(keyManager[
|
|
415
|
+
assert.strictEqual(keyManager["activeShortcuts"].size, 2);
|
|
351
416
|
keyManager.destroy();
|
|
352
417
|
// @ts-ignore
|
|
353
|
-
assert.strictEqual(keyManager[
|
|
418
|
+
assert.strictEqual(keyManager["activeShortcuts"].size, 0);
|
|
354
419
|
dispatchKeyEvent(Keys.D);
|
|
355
420
|
dispatchKeyEvent(Keys.X);
|
|
356
421
|
dispatchKeyEvent(Keys.Y);
|