rx-hotkeys 2.1.1 → 2.2.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/hotkeys.d.ts +20 -0
- package/dist/hotkeys.d.ts.map +1 -1
- package/dist/hotkeys.js +40 -4
- package/dist/hotkeys.test.js +107 -8
- package/package.json +1 -1
package/dist/hotkeys.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
1
2
|
import { StandardKey } from "./keys.js";
|
|
2
3
|
export declare enum ShortcutTypes {
|
|
3
4
|
Combination = "combination",
|
|
@@ -96,6 +97,25 @@ export declare class Hotkeys {
|
|
|
96
97
|
* @param enable - True to enable debug logs, false to disable.
|
|
97
98
|
*/
|
|
98
99
|
setDebugMode(enable: boolean): void;
|
|
100
|
+
/**
|
|
101
|
+
* An Observable that emits the new context name (or null) whenever the active context changes.
|
|
102
|
+
* This allows external parts of the application to react to context transitions.
|
|
103
|
+
*
|
|
104
|
+
* Note: This observable benefits from the distinct check within the `setContext` method,
|
|
105
|
+
* meaning it will only emit when the context value actually changes.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* const hotkeys = new Hotkeys();
|
|
110
|
+
* const subscription = hotkeys.onContextChange$.subscribe(newContext => {
|
|
111
|
+
* console.log("Hotkey context changed to:", newContext);
|
|
112
|
+
* // Update UI or perform other actions
|
|
113
|
+
* });
|
|
114
|
+
* // To unsubscribe when no longer needed:
|
|
115
|
+
* // subscription.unsubscribe();
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
get onContextChange$(): Observable<string | null>;
|
|
99
119
|
/**
|
|
100
120
|
* Checks if a shortcut with the given ID is currently registered and active.
|
|
101
121
|
* @param id - The unique ID of the shortcut to check.
|
package/dist/hotkeys.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hotkeys.d.ts","sourceRoot":"","sources":["../src/hotkeys.ts"],"names":[],"mappings":"
|
|
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;;;;;;OAMG;IACI,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAiBnD;;;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;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
|
@@ -65,8 +65,17 @@ export class Hotkeys {
|
|
|
65
65
|
* Pass `null` to activate shortcuts with no context or to deactivate context-specific shortcuts.
|
|
66
66
|
*/
|
|
67
67
|
setContext(contextName) {
|
|
68
|
+
if (this.activeContext$.getValue() === contextName) {
|
|
69
|
+
if (this.debugMode) {
|
|
70
|
+
// Optional: Log that no change is happening, or simply do nothing.
|
|
71
|
+
console.log(`${Hotkeys.LOG_PREFIX} setContext called with the same context "${contextName}". No change made.`);
|
|
72
|
+
}
|
|
73
|
+
return; // Context is the same, so no further action is needed.
|
|
74
|
+
}
|
|
75
|
+
// If we reach here, the context is actually changing.
|
|
76
|
+
const oldContext = this.activeContext$.getValue(); // For more informative logging
|
|
68
77
|
if (this.debugMode) {
|
|
69
|
-
console.log(`${Hotkeys.LOG_PREFIX} Context changed to "${contextName}"
|
|
78
|
+
console.log(`${Hotkeys.LOG_PREFIX} Context changed from "${oldContext}" to "${contextName}".`);
|
|
70
79
|
}
|
|
71
80
|
this.activeContext$.next(contextName);
|
|
72
81
|
}
|
|
@@ -83,11 +92,38 @@ export class Hotkeys {
|
|
|
83
92
|
* @param enable - True to enable debug logs, false to disable.
|
|
84
93
|
*/
|
|
85
94
|
setDebugMode(enable) {
|
|
86
|
-
this.debugMode
|
|
87
|
-
|
|
88
|
-
|
|
95
|
+
if (this.debugMode === enable) { // Check if state is actually changing
|
|
96
|
+
return; // If no change, do nothing (no log)
|
|
97
|
+
}
|
|
98
|
+
this.debugMode = enable; // Set the new state
|
|
99
|
+
if (enable) { // Log based on the NEW state after a change
|
|
100
|
+
console.log(`${Hotkeys.LOG_PREFIX} Debug mode enabled.`);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
console.log(`${Hotkeys.LOG_PREFIX} Debug mode disabled.`);
|
|
89
104
|
}
|
|
90
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* An Observable that emits the new context name (or null) whenever the active context changes.
|
|
108
|
+
* This allows external parts of the application to react to context transitions.
|
|
109
|
+
*
|
|
110
|
+
* Note: This observable benefits from the distinct check within the `setContext` method,
|
|
111
|
+
* meaning it will only emit when the context value actually changes.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* const hotkeys = new Hotkeys();
|
|
116
|
+
* const subscription = hotkeys.onContextChange$.subscribe(newContext => {
|
|
117
|
+
* console.log("Hotkey context changed to:", newContext);
|
|
118
|
+
* // Update UI or perform other actions
|
|
119
|
+
* });
|
|
120
|
+
* // To unsubscribe when no longer needed:
|
|
121
|
+
* // subscription.unsubscribe();
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
get onContextChange$() {
|
|
125
|
+
return this.activeContext$.asObservable();
|
|
126
|
+
}
|
|
91
127
|
/**
|
|
92
128
|
* Checks if a shortcut with the given ID is currently registered and active.
|
|
93
129
|
* @param id - The unique ID of the shortcut to check.
|
package/dist/hotkeys.test.js
CHANGED
|
@@ -69,35 +69,134 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
|
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
71
|
// ... (Initialization and Basic Context tests remain the same) ...
|
|
72
|
-
describe("Initialization and
|
|
72
|
+
describe("Initialization and Context Management", () => {
|
|
73
73
|
it("should initialize without errors", () => {
|
|
74
74
|
assert(keyManager instanceof Hotkeys);
|
|
75
|
+
assert.strictEqual(keyManager.getContext(), null); // Default initial context
|
|
75
76
|
});
|
|
76
77
|
it("should initialize with a null context by default", () => {
|
|
77
78
|
assert.strictEqual(keyManager.getContext(), null);
|
|
78
79
|
});
|
|
79
|
-
it("should initialize with a given initial context", () => {
|
|
80
|
-
const manager = new Hotkeys("editor");
|
|
80
|
+
it("should initialize with a given initial context (debug off)", () => {
|
|
81
|
+
const manager = new Hotkeys("editor", false);
|
|
81
82
|
assert.strictEqual(manager.getContext(), "editor");
|
|
82
83
|
manager.destroy();
|
|
83
84
|
});
|
|
84
|
-
it("should
|
|
85
|
+
it("should log library initialization with context in debug mode", () => {
|
|
86
|
+
const consoleLogMock = mock.method(console, "log");
|
|
87
|
+
const manager = new Hotkeys("debugInitCtx", true);
|
|
88
|
+
const initLog = consoleLogMock.mock.calls.find(call => call.arguments[0].includes(`Library initialized. Initial context: "debugInitCtx"`));
|
|
89
|
+
assert.ok(initLog, "Library initialization log not found or incorrect.");
|
|
90
|
+
manager.destroy();
|
|
91
|
+
consoleLogMock.mock.restore();
|
|
92
|
+
});
|
|
93
|
+
it("should set and get context correctly", () => {
|
|
85
94
|
keyManager.setContext("modal");
|
|
86
95
|
assert.strictEqual(keyManager.getContext(), "modal");
|
|
87
96
|
keyManager.setContext(null);
|
|
88
97
|
assert.strictEqual(keyManager.getContext(), null);
|
|
89
98
|
});
|
|
90
|
-
it("should
|
|
99
|
+
it("should log context change correctly when context is different (debug mode on)", () => {
|
|
91
100
|
const consoleLogMock = mock.method(console, "log");
|
|
92
101
|
keyManager.setDebugMode(true);
|
|
102
|
+
// Initial context is null for keyManager
|
|
103
|
+
consoleLogMock.mock.resetCalls(); // Clear "Debug mode enabled" log
|
|
93
104
|
keyManager.setContext("debug_test");
|
|
94
|
-
assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes(`Context changed to "debug_test"`)));
|
|
105
|
+
assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes(`Context changed from "null" to "debug_test"`)), "Log for context change from null incorrect.");
|
|
106
|
+
assert.strictEqual(keyManager.getContext(), "debug_test");
|
|
107
|
+
consoleLogMock.mock.resetCalls();
|
|
108
|
+
keyManager.setContext("another_test");
|
|
109
|
+
assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes(`Context changed from "debug_test" to "another_test"`)), "Log for context change between non-null incorrect.");
|
|
110
|
+
assert.strictEqual(keyManager.getContext(), "another_test");
|
|
111
|
+
consoleLogMock.mock.restore();
|
|
112
|
+
});
|
|
113
|
+
it("should log context change from non-null to null (debug mode on)", () => {
|
|
114
|
+
const consoleLogMock = mock.method(console, "log");
|
|
115
|
+
keyManager.setContext("fromCtx"); // Initial context
|
|
116
|
+
keyManager.setDebugMode(true);
|
|
95
117
|
consoleLogMock.mock.resetCalls();
|
|
118
|
+
keyManager.setContext(null);
|
|
119
|
+
const logCall = consoleLogMock.mock.calls.find(call => call.arguments[0].includes(`Context changed from "fromCtx" to "null"`));
|
|
120
|
+
assert.ok(logCall, "Context change to null log not found or incorrect.");
|
|
121
|
+
assert.strictEqual(keyManager.getContext(), null);
|
|
122
|
+
consoleLogMock.mock.restore();
|
|
123
|
+
});
|
|
124
|
+
it(`should not call activeContext$.next and log "no change" if context is set to the same value (debug mode on)`, () => {
|
|
125
|
+
keyManager.setContext("sameCtx"); // Set initial context
|
|
126
|
+
const consoleLogMock = mock.method(console, "log");
|
|
127
|
+
keyManager.setDebugMode(true); // Enable debug for this test
|
|
128
|
+
consoleLogMock.mock.resetCalls(); // Clear "Debug mode enabled" log
|
|
129
|
+
// @ts-ignore: Accessing private member for test
|
|
130
|
+
const activeContextNextSpy = mock.method(keyManager["activeContext$"], "next");
|
|
131
|
+
keyManager.setContext("sameCtx"); // Attempt to set the same context
|
|
132
|
+
const noChangeLogCall = consoleLogMock.mock.calls.find(call => call.arguments[0].includes(`setContext called with the same context "sameCtx". No change made.`));
|
|
133
|
+
assert.ok(noChangeLogCall, "No-change log not found or incorrect for same context.");
|
|
134
|
+
const changedLogCall = consoleLogMock.mock.calls.find(call => call.arguments[0].includes(`Context changed from`));
|
|
135
|
+
assert.strictEqual(changedLogCall, undefined, "Context changed log should not appear for same context.");
|
|
136
|
+
assert.strictEqual(keyManager.getContext(), "sameCtx");
|
|
137
|
+
assert.strictEqual(activeContextNextSpy.mock.callCount(), 0, "activeContext$.next should not have been called.");
|
|
138
|
+
activeContextNextSpy.mock.restore();
|
|
139
|
+
consoleLogMock.mock.restore();
|
|
140
|
+
});
|
|
141
|
+
it("should not log or call next if context is set to the same value (debug mode off)", () => {
|
|
142
|
+
keyManager.setContext("sameCtxNoDebug"); // Set initial context
|
|
96
143
|
keyManager.setDebugMode(false);
|
|
97
|
-
|
|
98
|
-
|
|
144
|
+
// Ensure debug is off
|
|
145
|
+
const consoleLogMock = mock.method(console, "log");
|
|
146
|
+
// @ts-ignore: Accessing private member for test
|
|
147
|
+
const activeContextNextSpy = mock.method(keyManager["activeContext$"], "next");
|
|
148
|
+
keyManager.setContext("sameCtxNoDebug"); // Attempt to set the same context
|
|
149
|
+
assert.strictEqual(consoleLogMock.mock.callCount(), 0, "Console.log should not have been called with debug mode off.");
|
|
150
|
+
assert.strictEqual(activeContextNextSpy.mock.callCount(), 0, "activeContext$.next should not have been called.");
|
|
151
|
+
assert.strictEqual(keyManager.getContext(), "sameCtxNoDebug");
|
|
152
|
+
activeContextNextSpy.mock.restore();
|
|
99
153
|
consoleLogMock.mock.restore();
|
|
100
154
|
});
|
|
155
|
+
it("should toggle debug mode and log its state", () => {
|
|
156
|
+
const consoleLogMock = mock.method(console, "log");
|
|
157
|
+
keyManager.setDebugMode(true);
|
|
158
|
+
assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes("Debug mode enabled")));
|
|
159
|
+
consoleLogMock.mock.resetCalls();
|
|
160
|
+
keyManager.setDebugMode(false);
|
|
161
|
+
console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>", consoleLogMock.mock.calls);
|
|
162
|
+
assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes("Debug mode disabled")));
|
|
163
|
+
consoleLogMock.mock.restore();
|
|
164
|
+
});
|
|
165
|
+
describe("onContextChange$ observable", () => {
|
|
166
|
+
it("should emit initial context to new subscriber", () => {
|
|
167
|
+
const manager = new Hotkeys("initial", false);
|
|
168
|
+
const spy = createMockFn();
|
|
169
|
+
const sub = manager.onContextChange$.subscribe(spy);
|
|
170
|
+
assert.strictEqual(spy.calledCount, 1);
|
|
171
|
+
assert.strictEqual(spy.lastArgs[0], "initial");
|
|
172
|
+
sub.unsubscribe();
|
|
173
|
+
manager.destroy();
|
|
174
|
+
});
|
|
175
|
+
it("should emit when context changes", () => {
|
|
176
|
+
const spy = createMockFn();
|
|
177
|
+
const sub = keyManager.onContextChange$.subscribe(spy); // Subscribes, gets initial null
|
|
178
|
+
spy.mockClear(); // Clear initial emission
|
|
179
|
+
keyManager.setContext("newContext");
|
|
180
|
+
assert.strictEqual(spy.calledCount, 1);
|
|
181
|
+
assert.strictEqual(spy.lastArgs[0], "newContext");
|
|
182
|
+
keyManager.setContext("anotherContext");
|
|
183
|
+
assert.strictEqual(spy.calledCount, 2);
|
|
184
|
+
assert.strictEqual(spy.lastArgs[0], "anotherContext");
|
|
185
|
+
keyManager.setContext(null);
|
|
186
|
+
assert.strictEqual(spy.calledCount, 3);
|
|
187
|
+
assert.strictEqual(spy.lastArgs[0], null);
|
|
188
|
+
sub.unsubscribe();
|
|
189
|
+
});
|
|
190
|
+
it("should not emit if context is set to the same value", () => {
|
|
191
|
+
keyManager.setContext("testContext");
|
|
192
|
+
const spy = createMockFn();
|
|
193
|
+
const sub = keyManager.onContextChange$.subscribe(spy); // Subscribes, gets "testContext"
|
|
194
|
+
spy.mockClear(); // Clear initial emission
|
|
195
|
+
keyManager.setContext("testContext"); // Set same context
|
|
196
|
+
assert.strictEqual(spy.calledCount, 0, "Observable should not emit if context value is the same.");
|
|
197
|
+
sub.unsubscribe();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
101
200
|
});
|
|
102
201
|
describe("addCombination", () => {
|
|
103
202
|
it("should trigger callback for a simple key combination (e.g., 'A')", () => {
|