rx-hotkeys 2.2.0 → 2.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/README.md +1 -1
- package/dist/hotkeys.d.ts +2 -1
- package/dist/hotkeys.d.ts.map +1 -1
- package/dist/hotkeys.js +15 -10
- package/dist/hotkeys.test.js +86 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -128,7 +128,7 @@ Registers a key sequence shortcut.
|
|
|
128
128
|
* `config`: The KeySequenceConfig object.
|
|
129
129
|
* Returns the shortcut ID if successful, undefined otherwise.
|
|
130
130
|
|
|
131
|
-
`setContext(contextName: string | null):
|
|
131
|
+
`setContext(contextName: string | null): boolean`
|
|
132
132
|
|
|
133
133
|
Sets the active context. Only shortcuts matching this context or global shortcuts (no context) will trigger.
|
|
134
134
|
|
package/dist/hotkeys.d.ts
CHANGED
|
@@ -84,8 +84,9 @@ export declare class Hotkeys {
|
|
|
84
84
|
* will be active and can be triggered.
|
|
85
85
|
* @param contextName - The name of the context (e.g., "modal", "editor", "global").
|
|
86
86
|
* Pass `null` to activate shortcuts with no context or to deactivate context-specific shortcuts.
|
|
87
|
+
* @returns boolean
|
|
87
88
|
*/
|
|
88
|
-
setContext(contextName: string | null):
|
|
89
|
+
setContext(contextName: string | null): boolean;
|
|
89
90
|
/**
|
|
90
91
|
* Gets the current active context.
|
|
91
92
|
* @returns The current context name as a string, or `null` if no context is set.
|
package/dist/hotkeys.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hotkeys.d.ts","sourceRoot":"","sources":["../src/hotkeys.ts"],"names":[],"mappings":"AAAA,OAAO,EACuC,UAAU,EAEvD,MAAM,MAAM,CAAC;AACd,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
|
|
1
|
+
{"version":3,"file":"hotkeys.d.ts","sourceRoot":"","sources":["../src/hotkeys.ts"],"names":[],"mappings":"AAAA,OAAO,EACuC,UAAU,EAEvD,MAAM,MAAM,CAAC;AACd,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;;;;;;;OAOG;IACI,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO;IAkBtD;;;OAGG;IACI,UAAU,IAAI,MAAM,GAAG,IAAI;IAIlC;;;;OAIG;IACI,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAa1C;;;;;;;;;;;;;;;;;OAiBG;IACH,IAAW,gBAAgB,IAAI,UAAU,CAAC,MAAM,GAAG,IAAI,CAAC,CAEvD;IAED;;;;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;IAgFvE;;;;;;;;;;;;;;;;;;;OAmBG;IACI,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,GAAG,SAAS;IAkHjE;;;;;;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
|
@@ -63,21 +63,23 @@ export class Hotkeys {
|
|
|
63
63
|
* will be active and can be triggered.
|
|
64
64
|
* @param contextName - The name of the context (e.g., "modal", "editor", "global").
|
|
65
65
|
* Pass `null` to activate shortcuts with no context or to deactivate context-specific shortcuts.
|
|
66
|
+
* @returns boolean
|
|
66
67
|
*/
|
|
67
68
|
setContext(contextName) {
|
|
68
|
-
|
|
69
|
+
const currentContext = this.activeContext$.getValue();
|
|
70
|
+
if (currentContext === contextName) {
|
|
69
71
|
if (this.debugMode) {
|
|
70
72
|
// Optional: Log that no change is happening, or simply do nothing.
|
|
71
73
|
console.log(`${Hotkeys.LOG_PREFIX} setContext called with the same context "${contextName}". No change made.`);
|
|
72
74
|
}
|
|
73
|
-
return; // Context is the same, so no further action is needed.
|
|
75
|
+
return false; // Context is the same, so no further action is needed.
|
|
74
76
|
}
|
|
75
77
|
// If we reach here, the context is actually changing.
|
|
76
|
-
const oldContext = this.activeContext$.getValue(); // For more informative logging
|
|
77
78
|
if (this.debugMode) {
|
|
78
|
-
console.log(`${Hotkeys.LOG_PREFIX} Context changed from "${
|
|
79
|
+
console.log(`${Hotkeys.LOG_PREFIX} Context changed from "${currentContext}" to "${contextName}".`);
|
|
79
80
|
}
|
|
80
81
|
this.activeContext$.next(contextName);
|
|
82
|
+
return true;
|
|
81
83
|
}
|
|
82
84
|
/**
|
|
83
85
|
* Gets the current active context.
|
|
@@ -184,8 +186,9 @@ export class Hotkeys {
|
|
|
184
186
|
let keyDetailsForLog;
|
|
185
187
|
if (typeof keys === "string") {
|
|
186
188
|
// Shorthand: keys is StandardKey, implying no modifiers should be active
|
|
187
|
-
|
|
188
|
-
|
|
189
|
+
// Corrected validation: Check for actual empty string, not a string that trims to empty.
|
|
190
|
+
if (keys === "") { // StandardKey type should prevent this, but check for robustness.
|
|
191
|
+
console.warn(`${Hotkeys.LOG_PREFIX} Invalid "keys" (shorthand) for combination shortcut "${id}". Key must not be an empty string. Shortcut not added.`);
|
|
189
192
|
return undefined;
|
|
190
193
|
}
|
|
191
194
|
configuredMainKey = keys;
|
|
@@ -197,8 +200,9 @@ export class Hotkeys {
|
|
|
197
200
|
}
|
|
198
201
|
else {
|
|
199
202
|
// Object form
|
|
200
|
-
|
|
201
|
-
|
|
203
|
+
// Corrected validation for keys.key
|
|
204
|
+
if (!keys || !keys.key || typeof keys.key !== "string" || keys.key === "") { // StandardKey type should prevent empty string for key.key
|
|
205
|
+
console.warn(`${Hotkeys.LOG_PREFIX} Invalid "keys.key" for combination shortcut "${id}". Key must be a non-empty string value from Keys. Shortcut not added.`);
|
|
202
206
|
return undefined;
|
|
203
207
|
}
|
|
204
208
|
configuredMainKey = keys.key;
|
|
@@ -270,8 +274,9 @@ export class Hotkeys {
|
|
|
270
274
|
console.warn(`${Hotkeys.LOG_PREFIX} Sequence for shortcut "${id}" is empty or invalid. Shortcut not added.`);
|
|
271
275
|
return undefined;
|
|
272
276
|
}
|
|
273
|
-
|
|
274
|
-
|
|
277
|
+
// Corrected validation: Check for actual empty string, not a string that trims to empty.
|
|
278
|
+
if (sequence.some(key => typeof key !== "string" || key === "")) { // StandardKey type should prevent empty strings.
|
|
279
|
+
console.warn(`${Hotkeys.LOG_PREFIX} Invalid key in sequence for shortcut "${id}". All keys must be non-empty string values from Keys. Shortcut not added.`);
|
|
275
280
|
return undefined;
|
|
276
281
|
}
|
|
277
282
|
const configuredSequence = sequence;
|
package/dist/hotkeys.test.js
CHANGED
|
@@ -152,16 +152,59 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
152
152
|
activeContextNextSpy.mock.restore();
|
|
153
153
|
consoleLogMock.mock.restore();
|
|
154
154
|
});
|
|
155
|
+
it(`should log "no change" if context is set to the same value (debug mode on)`, () => {
|
|
156
|
+
keyManager.setContext("sameCtx"); // Set initial context
|
|
157
|
+
const consoleLogMock = mock.method(console, "log");
|
|
158
|
+
keyManager.setDebugMode(true);
|
|
159
|
+
consoleLogMock.mock.resetCalls(); // Clear previous logs
|
|
160
|
+
keyManager.setContext("sameCtx"); // Attempt to set the same context
|
|
161
|
+
const noChangeLogCall = consoleLogMock.mock.calls.find(call => call.arguments[0].includes(`setContext called with the same context "sameCtx". No change made.`));
|
|
162
|
+
assert.ok(noChangeLogCall, "No-change log not found or incorrect for same context.");
|
|
163
|
+
const changedLogCall = consoleLogMock.mock.calls.find(call => call.arguments[0].includes("Context changed from"));
|
|
164
|
+
assert.strictEqual(changedLogCall, undefined, "Context changed log should not appear for same context.");
|
|
165
|
+
consoleLogMock.mock.restore();
|
|
166
|
+
});
|
|
155
167
|
it("should toggle debug mode and log its state", () => {
|
|
156
168
|
const consoleLogMock = mock.method(console, "log");
|
|
157
169
|
keyManager.setDebugMode(true);
|
|
158
170
|
assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes("Debug mode enabled")));
|
|
159
171
|
consoleLogMock.mock.resetCalls();
|
|
160
172
|
keyManager.setDebugMode(false);
|
|
161
|
-
console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>", consoleLogMock.mock.calls);
|
|
162
173
|
assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes("Debug mode disabled")));
|
|
163
174
|
consoleLogMock.mock.restore();
|
|
164
175
|
});
|
|
176
|
+
describe("setContext return value", () => {
|
|
177
|
+
it("should return true when context is changed from null to a new value", () => {
|
|
178
|
+
assert.strictEqual(keyManager.getContext(), null, "Initial context should be null");
|
|
179
|
+
const result = keyManager.setContext("newContext");
|
|
180
|
+
assert.strictEqual(result, true, "setContext should return true for a change from null");
|
|
181
|
+
assert.strictEqual(keyManager.getContext(), "newContext");
|
|
182
|
+
});
|
|
183
|
+
it("should return true when context is changed from one value to another", () => {
|
|
184
|
+
keyManager.setContext("initialContext"); // Set a non-null initial context
|
|
185
|
+
const result = keyManager.setContext("changedContext");
|
|
186
|
+
assert.strictEqual(result, true, "setContext should return true for a value-to-value change");
|
|
187
|
+
assert.strictEqual(keyManager.getContext(), "changedContext");
|
|
188
|
+
});
|
|
189
|
+
it("should return false when context is set to the same value (non-null)", () => {
|
|
190
|
+
keyManager.setContext("sameContext");
|
|
191
|
+
const result = keyManager.setContext("sameContext");
|
|
192
|
+
assert.strictEqual(result, false, "setContext should return false for no change (non-null)");
|
|
193
|
+
assert.strictEqual(keyManager.getContext(), "sameContext");
|
|
194
|
+
});
|
|
195
|
+
it("should return true when context is changed from a value to null", () => {
|
|
196
|
+
keyManager.setContext("initialContext");
|
|
197
|
+
const result = keyManager.setContext(null);
|
|
198
|
+
assert.strictEqual(result, true, "setContext should return true for a change to null");
|
|
199
|
+
assert.strictEqual(keyManager.getContext(), null);
|
|
200
|
+
});
|
|
201
|
+
it("should return false when context is set to null and already null", () => {
|
|
202
|
+
keyManager.setContext(null); // Ensure context is null
|
|
203
|
+
const result = keyManager.setContext(null);
|
|
204
|
+
assert.strictEqual(result, false, "setContext should return false for no change (already null)");
|
|
205
|
+
assert.strictEqual(keyManager.getContext(), null);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
165
208
|
describe("onContextChange$ observable", () => {
|
|
166
209
|
it("should emit initial context to new subscriber", () => {
|
|
167
210
|
const manager = new Hotkeys("initial", false);
|
|
@@ -176,14 +219,14 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
176
219
|
const spy = createMockFn();
|
|
177
220
|
const sub = keyManager.onContextChange$.subscribe(spy); // Subscribes, gets initial null
|
|
178
221
|
spy.mockClear(); // Clear initial emission
|
|
179
|
-
keyManager.setContext("newContext");
|
|
180
|
-
assert.strictEqual(spy.calledCount, 1);
|
|
222
|
+
assert.ok(keyManager.setContext("newContext"), "setContext should have returned true for change");
|
|
223
|
+
assert.strictEqual(spy.calledCount, 1, "onContextChange$ did not emit on first change");
|
|
181
224
|
assert.strictEqual(spy.lastArgs[0], "newContext");
|
|
182
|
-
keyManager.setContext("anotherContext");
|
|
183
|
-
assert.strictEqual(spy.calledCount, 2);
|
|
225
|
+
assert.ok(keyManager.setContext("anotherContext"), "setContext should have returned true for second change");
|
|
226
|
+
assert.strictEqual(spy.calledCount, 2, "onContextChange$ did not emit on second change");
|
|
184
227
|
assert.strictEqual(spy.lastArgs[0], "anotherContext");
|
|
185
|
-
keyManager.setContext(null);
|
|
186
|
-
assert.strictEqual(spy.calledCount, 3);
|
|
228
|
+
assert.ok(keyManager.setContext(null), "setContext should have returned true for change to null");
|
|
229
|
+
assert.strictEqual(spy.calledCount, 3, "onContextChange$ did not emit on change to null");
|
|
187
230
|
assert.strictEqual(spy.lastArgs[0], null);
|
|
188
231
|
sub.unsubscribe();
|
|
189
232
|
});
|
|
@@ -192,7 +235,7 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
192
235
|
const spy = createMockFn();
|
|
193
236
|
const sub = keyManager.onContextChange$.subscribe(spy); // Subscribes, gets "testContext"
|
|
194
237
|
spy.mockClear(); // Clear initial emission
|
|
195
|
-
keyManager.setContext("testContext"); // Set same context
|
|
238
|
+
assert.strictEqual(keyManager.setContext("testContext"), false, "setContext should have returned false for no change"); // Set same context
|
|
196
239
|
assert.strictEqual(spy.calledCount, 0, "Observable should not emit if context value is the same.");
|
|
197
240
|
sub.unsubscribe();
|
|
198
241
|
});
|
|
@@ -340,6 +383,18 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
340
383
|
consoleLogMock.mock.restore();
|
|
341
384
|
keyManager.setDebugMode(false);
|
|
342
385
|
});
|
|
386
|
+
it("should trigger callback for Keys.Space using shorthand", () => {
|
|
387
|
+
const config = { id: "spaceShorthand", keys: Keys.Space, callback: mockCallback };
|
|
388
|
+
keyManager.addCombination(config);
|
|
389
|
+
dispatchKeyEvent(" "); // Event key for space is " "
|
|
390
|
+
assert.strictEqual(mockCallback.calledCount, 1, "Callback for Keys.Space (shorthand) not called");
|
|
391
|
+
});
|
|
392
|
+
it("should trigger callback for Keys.Space using object form", () => {
|
|
393
|
+
const config = { id: "spaceObject", keys: { key: Keys.Space }, callback: mockCallback };
|
|
394
|
+
keyManager.addCombination(config);
|
|
395
|
+
dispatchKeyEvent(" ");
|
|
396
|
+
assert.strictEqual(mockCallback.calledCount, 1, "Callback for Keys.Space (object form) not called");
|
|
397
|
+
});
|
|
343
398
|
});
|
|
344
399
|
});
|
|
345
400
|
describe("addSequence", () => {
|
|
@@ -402,6 +457,29 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
402
457
|
assert.strictEqual(consoleErrorMock.mock.calls.length, 1);
|
|
403
458
|
assert.ok(consoleErrorMock.mock.calls[0].arguments[0].includes(`Error in user callback for sequence shortcut "errorSeq"`));
|
|
404
459
|
});
|
|
460
|
+
it("should trigger callback for a sequence including Keys.Space", () => {
|
|
461
|
+
const config = {
|
|
462
|
+
id: "seqWithSpace",
|
|
463
|
+
sequence: [Keys.G, Keys.Space, Keys.I],
|
|
464
|
+
callback: mockCallback
|
|
465
|
+
};
|
|
466
|
+
keyManager.addSequence(config);
|
|
467
|
+
dispatchKeyEvent(Keys.G);
|
|
468
|
+
dispatchKeyEvent(Keys.Space); // Dispatch " " for space
|
|
469
|
+
dispatchKeyEvent(Keys.I);
|
|
470
|
+
assert.strictEqual(mockCallback.calledCount, 1, "Callback for sequence with Keys.Space not called");
|
|
471
|
+
});
|
|
472
|
+
it("should trigger callback for a sequence starting or ending with Keys.Space", () => {
|
|
473
|
+
const config = {
|
|
474
|
+
id: "seqStartSpace",
|
|
475
|
+
sequence: [Keys.Space, Keys.A],
|
|
476
|
+
callback: mockCallback
|
|
477
|
+
};
|
|
478
|
+
keyManager.addSequence(config);
|
|
479
|
+
dispatchKeyEvent(Keys.Space);
|
|
480
|
+
dispatchKeyEvent(Keys.A);
|
|
481
|
+
assert.strictEqual(mockCallback.calledCount, 1, "Callback for sequence starting with Keys.Space not called");
|
|
482
|
+
});
|
|
405
483
|
describe("Sequence Contextual Triggering", () => {
|
|
406
484
|
let editorSequenceConfig;
|
|
407
485
|
beforeEach(() => {
|