rx-hotkeys 3.0.0 → 4.0.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 +195 -42
- package/dist/{hotkeys.d.ts → core/hotkeys.d.ts} +35 -14
- package/dist/core/hotkeys.d.ts.map +1 -0
- package/dist/{hotkeys.js → core/hotkeys.js} +107 -42
- package/dist/core/hotkeys.js.map +1 -0
- package/dist/{hotkeys.test.d.ts.map → core/hotkeys.test.d.ts.map} +1 -1
- package/dist/{hotkeys.test.js → core/hotkeys.test.js} +201 -145
- package/dist/core/hotkeys.test.js.map +1 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/{index.js → core/index.js} +1 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/keys.d.ts.map +1 -0
- package/dist/{keys.js → core/keys.js} +1 -0
- package/dist/core/keys.js.map +1 -0
- package/dist/core/testutils.d.ts.map +1 -0
- package/dist/{testutils.js → core/testutils.js} +1 -0
- package/dist/core/testutils.js.map +1 -0
- package/dist/integrations/react/index.d.ts +3 -0
- package/dist/integrations/react/index.d.ts.map +1 -0
- package/dist/integrations/react/index.js +3 -0
- package/dist/integrations/react/index.js.map +1 -0
- package/dist/integrations/react/provider.d.ts +36 -0
- package/dist/integrations/react/provider.d.ts.map +1 -0
- package/dist/integrations/react/provider.js +85 -0
- package/dist/integrations/react/provider.js.map +1 -0
- package/dist/integrations/react/useEventCallback.d.ts +14 -0
- package/dist/integrations/react/useEventCallback.d.ts.map +1 -0
- package/dist/integrations/react/useEventCallback.js +32 -0
- package/dist/integrations/react/useEventCallback.js.map +1 -0
- package/dist/integrations/react/useHotkeys.d.ts +43 -0
- package/dist/integrations/react/useHotkeys.d.ts.map +1 -0
- package/dist/integrations/react/useHotkeys.js +76 -0
- package/dist/integrations/react/useHotkeys.js.map +1 -0
- package/dist/integrations/react/useIsomorphicLayoutEffect.d.ts +3 -0
- package/dist/integrations/react/useIsomorphicLayoutEffect.d.ts.map +1 -0
- package/dist/integrations/react/useIsomorphicLayoutEffect.js +3 -0
- package/dist/integrations/react/useIsomorphicLayoutEffect.js.map +1 -0
- package/package.json +19 -8
- package/dist/hotkeys.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/keys.d.ts.map +0 -1
- package/dist/testutils.d.ts.map +0 -1
- /package/dist/{hotkeys.test.d.ts → core/hotkeys.test.d.ts} +0 -0
- /package/dist/{index.d.ts → core/index.d.ts} +0 -0
- /package/dist/{keys.d.ts → core/keys.d.ts} +0 -0
- /package/dist/{testutils.d.ts → core/testutils.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -4,12 +4,15 @@ rx-hotkeys is a powerful and flexible TypeScript library for managing keyboard s
|
|
|
4
4
|
|
|
5
5
|
## ✨ Features
|
|
6
6
|
|
|
7
|
+
* **Official React Hooks**: Provides an official wrapper (`HotkeysProvider`, `useHotkeys`, `useScopedHotkeysContext`) for seamless, idiomatic integration with React.
|
|
7
8
|
* **Fully Observable API**: Returns an RxJS `Observable` for each shortcut, allowing for powerful stream manipulation like chaining, filtering, debouncing, and merging with other streams.
|
|
8
9
|
* **Flexible Shortcut Definitions**: Define shortcuts using simple, intuitive strings (e.g., `"ctrl+s"` or `"g -> i"`) in addition to the classic object-based configuration.
|
|
9
10
|
* **Element-Scoped Listeners**: Attach shortcuts to specific DOM elements, so they are only active within a certain component or area, not just on the global `document`.
|
|
10
11
|
* **`keyup` Event Support**: Trigger actions on key release (`keyup`) in addition to the default key press (`keydown`).
|
|
11
12
|
* **Key Combinations & Sequences**: Supports both simultaneous key presses (`Ctrl+S`) and ordered key sequences (`g` -> `c`).
|
|
12
13
|
* **Context Management**: Activate or deactivate groups of shortcuts based on the application's current state (e.g., "editor", "modal", "global").
|
|
14
|
+
* **Stack-Based Context Management**: Natively handles nested contexts with an `enter`/`leave` API, perfect for hierarchical UIs like pages, modals, and dropdowns.
|
|
15
|
+
* **Temporary Context Override**: Safely override all contexts with a high-priority temporary context, ideal for global application states like "saving" or "loading".
|
|
13
16
|
* **Strict Global Shortcuts**: Option to register global shortcuts that *only* fire when no other context is active.
|
|
14
17
|
* **Type-Safe Key Definitions**: Uses an exported `Keys` object based on standard `KeyboardEvent.key` values for a superior developer experience and fewer errors.
|
|
15
18
|
* **Sequence Timeouts**: Optional timeout between key presses in a sequence to prevent accidental triggers.
|
|
@@ -21,40 +24,17 @@ rx-hotkeys is a powerful and flexible TypeScript library for managing keyboard s
|
|
|
21
24
|
npm install rxjs rx-hotkeys
|
|
22
25
|
```
|
|
23
26
|
|
|
24
|
-
## ⚠️ Breaking Changes (
|
|
27
|
+
## ⚠️ Breaking Changes (v4.0+)
|
|
25
28
|
|
|
26
|
-
Starting with
|
|
29
|
+
Starting with v4.0, the context management API has been fundamentally redesigned into a more powerful and robust dual-mode system.
|
|
27
30
|
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
|
|
31
|
+
* The old `setContext` method (which returned a boolean) has been replaced.
|
|
32
|
+
* The library now offers two distinct ways to manage contexts:
|
|
33
|
+
1. **Context Stack (`enterContext`/`leaveContext`)**: For hierarchical UI states.
|
|
34
|
+
2. **Context Override (`setContext` returns a `restore` function)**: For temporary, global state overrides.
|
|
35
|
+
* `getContext` method rename to `getActiveContext`.
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
**Old (v1.x):**
|
|
35
|
-
```typescript
|
|
36
|
-
// The old way
|
|
37
|
-
keyManager.addCombination({
|
|
38
|
-
id: "save",
|
|
39
|
-
keys: { key: Keys.S, ctrlKey: true },
|
|
40
|
-
callback: () => console.log("File saved!"),
|
|
41
|
-
});
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
**New (v3.0+):**
|
|
45
|
-
```typescript
|
|
46
|
-
// The new, observable-based way
|
|
47
|
-
const save$ = keyManager.addCombination({
|
|
48
|
-
id: "save",
|
|
49
|
-
keys: { key: Keys.S, ctrlKey: true }
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const subscription = save$.subscribe(() => console.log("File saved!"));
|
|
53
|
-
|
|
54
|
-
// Don't forget to unsubscribe when your component is destroyed!
|
|
55
|
-
// The stream will also complete automatically if the shortcut is removed or keyManager.destroy() is called.
|
|
56
|
-
// subscription.unsubscribe();
|
|
57
|
-
```
|
|
37
|
+
Please review the "Context Management" section below for details.
|
|
58
38
|
|
|
59
39
|
## Basic Usage
|
|
60
40
|
|
|
@@ -142,16 +122,55 @@ const submit$ = keyManager.addCombination({
|
|
|
142
122
|
submit$.subscribe(() => console.log("Form submitted on Enter keyup!"));
|
|
143
123
|
```
|
|
144
124
|
|
|
145
|
-
### 6.
|
|
125
|
+
### 6. Context Management
|
|
126
|
+
|
|
127
|
+
You now have two powerful tools for managing contexts.
|
|
146
128
|
|
|
147
|
-
|
|
129
|
+
#### A) Context Stack (`enterContext` / `leaveContext`)
|
|
130
|
+
|
|
131
|
+
Use this for nested UI scopes that follow a clear hierarchy.
|
|
148
132
|
|
|
149
133
|
```typescript
|
|
150
|
-
//
|
|
151
|
-
keyManager.
|
|
134
|
+
// A shortcut with context: "editor" will NOT be active here.
|
|
135
|
+
console.log(keyManager.getActiveContext()); // null
|
|
136
|
+
|
|
137
|
+
// Activate the "editor" context
|
|
138
|
+
keyManager.enterContext("editor");
|
|
139
|
+
// Now, pressing Ctrl+S will trigger the "saveFile" shortcut.
|
|
140
|
+
console.log(keyManager.getActiveContext()); // 'editor'
|
|
152
141
|
|
|
153
|
-
//
|
|
154
|
-
keyManager.
|
|
142
|
+
// Imagine opening a dropdown menu inside the editor
|
|
143
|
+
keyManager.enterContext("dropdown-menu");
|
|
144
|
+
console.log(keyManager.getActiveContext()); // 'dropdown-menu'
|
|
145
|
+
|
|
146
|
+
// When the dropdown closes, leave its context
|
|
147
|
+
keyManager.leaveContext();
|
|
148
|
+
console.log(keyManager.getActiveContext()); // 'editor' (restored automatically)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### B) Context Override (`setContext` and `restore`)
|
|
152
|
+
|
|
153
|
+
Use this for temporary, high-priority states that should override everything else.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
async function performSave() {
|
|
157
|
+
// Set a temporary "saving" context that overrides the stack.
|
|
158
|
+
const restore = keyManager.setContext('saving');
|
|
159
|
+
|
|
160
|
+
// Any shortcuts with context: 'saving' are now active.
|
|
161
|
+
// All other shortcuts (editor, etc.) are inactive.
|
|
162
|
+
console.log(keyManager.getActiveContext()); // 'saving'
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
await someAsyncSaveOperation();
|
|
166
|
+
} finally {
|
|
167
|
+
// No matter what happens, call restore() to clear the override
|
|
168
|
+
// and return control to the context stack.
|
|
169
|
+
restore();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.log(keyManager.getActiveContext()); // e.g., 'editor' (restored from the stack)
|
|
173
|
+
}
|
|
155
174
|
```
|
|
156
175
|
|
|
157
176
|
### 7. Clean Up
|
|
@@ -163,6 +182,99 @@ When the Hotkeys instance is no longer needed (e.g., component unmount), call `d
|
|
|
163
182
|
keyManager.destroy();
|
|
164
183
|
```
|
|
165
184
|
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Usage with React
|
|
188
|
+
|
|
189
|
+
The library provides a dedicated React wrapper for the best developer experience.
|
|
190
|
+
|
|
191
|
+
### Step 1: Wrap Your App with `HotkeysProvider`
|
|
192
|
+
|
|
193
|
+
First, import `HotkeysProvider` and wrap your root application component with it. This creates a single, shared instance of the hotkeys manager for your entire app.
|
|
194
|
+
|
|
195
|
+
```jsx
|
|
196
|
+
// In your main App.js or index.js
|
|
197
|
+
import React from 'react';
|
|
198
|
+
import ReactDOM from 'react-dom/client';
|
|
199
|
+
import { HotkeysProvider } from 'rx-hotkeys/react';
|
|
200
|
+
import App from './App';
|
|
201
|
+
|
|
202
|
+
const root = ReactDOM.createRoot(document.getElementById('root'));
|
|
203
|
+
root.render(
|
|
204
|
+
<React.StrictMode>
|
|
205
|
+
<HotkeysProvider debugMode={true}>
|
|
206
|
+
<App />
|
|
207
|
+
</HotkeysProvider>
|
|
208
|
+
</React.StrictMode>
|
|
209
|
+
);
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Step 2: Use the `useHotkeys` Hook in Your Components
|
|
213
|
+
|
|
214
|
+
Now, you can use the `useHotkeys` and `useSequence` hooks anywhere in your component tree. The hook automatically handles registration, cleanup, and stale closures. You no longer need to provide a dependency array for your callback.
|
|
215
|
+
|
|
216
|
+
```jsx
|
|
217
|
+
// src/components/Counter.jsx
|
|
218
|
+
import React, { useState } from 'react';
|
|
219
|
+
import { useHotkeys, useSequence } from 'rx-hotkeys/react';
|
|
220
|
+
|
|
221
|
+
export function Counter() {
|
|
222
|
+
const [count, setCount] = useState(0);
|
|
223
|
+
|
|
224
|
+
// The callback can safely use the latest component state (like `count`)
|
|
225
|
+
// without you needing to worry about stale closures or dependency arrays.
|
|
226
|
+
const handleIncrement = () => {
|
|
227
|
+
console.log(`Incrementing from ${count}...`);
|
|
228
|
+
setCount(count + 1);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// Register '+' key to increment.
|
|
232
|
+
useHotkeys('+', handleIncrement);
|
|
233
|
+
|
|
234
|
+
// Register 'c' key to increment, with options.
|
|
235
|
+
useHotkeys('c', handleIncrement, { preventDefault: true });
|
|
236
|
+
|
|
237
|
+
// Register a sequence to reset the counter.
|
|
238
|
+
useSequence('r -> e -> s -> e -> t', () => {
|
|
239
|
+
console.log('Resetting counter!');
|
|
240
|
+
setCount(0);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
<div>
|
|
245
|
+
<h2>Count: {count}</h2>
|
|
246
|
+
<p>Press '+' or 'c' to increment. Type 'reset' to reset.</p>
|
|
247
|
+
</div>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Step 3: Manage Context with `useScopedHotkeysContext`
|
|
253
|
+
|
|
254
|
+
This hook allows a component (like a modal) to activate a specific context only while it is mounted.
|
|
255
|
+
|
|
256
|
+
```jsx
|
|
257
|
+
// src/components/MyModal.jsx
|
|
258
|
+
import { useHotkeys, useScopedHotkeysContext } from 'rx-hotkeys/react';
|
|
259
|
+
|
|
260
|
+
export function MyModal({ onClose }) {
|
|
261
|
+
// This activates the 'modal' context for all children of this component.
|
|
262
|
+
// When MyModal unmounts, this context is automatically removed from the stack.
|
|
263
|
+
useScopedHotkeysContext('modal');
|
|
264
|
+
|
|
265
|
+
// This hotkey will only be active when the 'modal' context is active.
|
|
266
|
+
useHotkeys('escape', onClose, { context: 'modal' });
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<div className="modal">
|
|
270
|
+
<p>This is a modal. Press ESC to close.</p>
|
|
271
|
+
{/* ... other modal content ... */}
|
|
272
|
+
</div>
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
166
278
|
|
|
167
279
|
## API Reference
|
|
168
280
|
|
|
@@ -171,7 +283,7 @@ keyManager.destroy();
|
|
|
171
283
|
* `Keys`: An exported constant object containing standard `KeyboardEvent.key` string values (e.g., `Keys.Enter`, `Keys.ArrowUp`, `Keys.A`). It's highly recommended to use these for type safety and to avoid typos.
|
|
172
284
|
* `StandardKey`: A TypeScript type representing any valid key string from the `Keys` object.
|
|
173
285
|
|
|
174
|
-
### `Hotkeys` Class
|
|
286
|
+
### `Hotkeys` Class (Core)
|
|
175
287
|
|
|
176
288
|
`constructor(initialContext?: string | null, debugMode?: boolean)`
|
|
177
289
|
|
|
@@ -189,13 +301,25 @@ Registers a key sequence shortcut.
|
|
|
189
301
|
* `config`: The `KeySequenceConfig` object.
|
|
190
302
|
* Returns an `Observable<KeyboardEvent>` that emits the final `KeyboardEvent` when the sequence is completed.
|
|
191
303
|
|
|
192
|
-
`
|
|
304
|
+
`enterContext(contextName: string | null): void`
|
|
305
|
+
|
|
306
|
+
Pushes a context onto the **context stack**. It becomes active if no override is set.
|
|
307
|
+
|
|
308
|
+
`leaveContext(): string | null | undefined`
|
|
193
309
|
|
|
194
|
-
|
|
310
|
+
Pops a context from the **context stack**, returning the context that was left.
|
|
195
311
|
|
|
196
|
-
`
|
|
312
|
+
`setContext(contextName: string | null): () => void`
|
|
197
313
|
|
|
198
|
-
|
|
314
|
+
Sets a temporary **override context**. Returns a `restore` function to clear the override.
|
|
315
|
+
|
|
316
|
+
`getActiveContext(): string | null`
|
|
317
|
+
|
|
318
|
+
Returns the current active context (checks for an override first, then the stack top).
|
|
319
|
+
|
|
320
|
+
`onContextChange$: Observable<string | null>`
|
|
321
|
+
|
|
322
|
+
A public `Observable` property that emits the active context whenever it changes.
|
|
199
323
|
|
|
200
324
|
`remove(id: string): boolean`
|
|
201
325
|
|
|
@@ -219,6 +343,33 @@ Enables or disables console logging for debug purposes.
|
|
|
219
343
|
|
|
220
344
|
Cleans up all subscriptions and resources. Essential to call to prevent memory leaks.
|
|
221
345
|
|
|
346
|
+
### React Hooks (`rx-hotkeys/react`)
|
|
347
|
+
|
|
348
|
+
`HotkeysProvider({ children, initialContext?, debugMode? })`
|
|
349
|
+
|
|
350
|
+
A React component that provides the Hotkeys instance to its children.
|
|
351
|
+
|
|
352
|
+
`useHotkeys(keys, callback, options?)`
|
|
353
|
+
|
|
354
|
+
A React hook to register a key combination.
|
|
355
|
+
* `keys: string | string[]`: The shortcut definition (e.g., `'ctrl+s'`).
|
|
356
|
+
* `callback: (event: KeyboardEvent) => void`: The function to execute.
|
|
357
|
+
* `options?: HotkeyHookOptions`: Optional config for `preventDefault`, `context`, `target`, etc.
|
|
358
|
+
|
|
359
|
+
`useSequence(sequence, callback, options?)`
|
|
360
|
+
|
|
361
|
+
A React hook to register a key sequence.
|
|
362
|
+
* `sequence: string | string[]`: The sequence definition (e.g., `'g -> i'`).
|
|
363
|
+
* `callback: (event: KeyboardEvent) => void`: The function to execute.
|
|
364
|
+
* `options?: SequenceHookOptions`: Optional config for `preventDefault`, `context`, etc.
|
|
365
|
+
|
|
366
|
+
`useScopedHotkeysContext(context, enabled: boolean = true)`
|
|
367
|
+
|
|
368
|
+
A React hook to apply a specific context for the lifetime of the component.
|
|
369
|
+
|
|
370
|
+
`useHotkeysManager(): Hotkeys`
|
|
371
|
+
A hook to get direct access to the `Hotkeys` manager instance.
|
|
372
|
+
|
|
222
373
|
### Configuration Interfaces
|
|
223
374
|
|
|
224
375
|
#### `KeyCombinationConfig`
|
|
@@ -252,6 +403,8 @@ Cleans up all subscriptions and resources. Essential to call to prevent memory l
|
|
|
252
403
|
* **Aliases**: Common aliases are supported in string definitions, such as `cmd` for `Meta`, `option` for `Alt`, and `esc` for `Escape`.
|
|
253
404
|
* **Special Keys**: For full type-safety, it is recommended to use the exported `Keys` object (e.g., `Keys.Enter`, `Keys.ArrowUp`).
|
|
254
405
|
|
|
406
|
+
---
|
|
407
|
+
|
|
255
408
|
## Contributing
|
|
256
409
|
|
|
257
410
|
Contributions are welcome! Please feel free to submit issues, fork the repository, and create pull requests.
|
|
@@ -111,18 +111,30 @@ export declare class Hotkeys {
|
|
|
111
111
|
private static readonly KEYDOWN_EVENT;
|
|
112
112
|
private static readonly KEYUP_EVENT;
|
|
113
113
|
private static readonly LOG_PREFIX;
|
|
114
|
+
private static readonly NO_OVERRIDE;
|
|
114
115
|
private keydownStreams;
|
|
115
116
|
private keyupStreams;
|
|
116
|
-
private activeContext$;
|
|
117
117
|
private activeShortcuts;
|
|
118
118
|
private debugMode;
|
|
119
|
+
private contextStack$;
|
|
120
|
+
private overrideContext$;
|
|
121
|
+
/**
|
|
122
|
+
* An Observable that emits the new active context name (or null) whenever it changes.
|
|
123
|
+
* The active context is the override context if one is set, otherwise it's the context
|
|
124
|
+
* from the top of the stack.
|
|
125
|
+
*/
|
|
126
|
+
private readonly activeContext$;
|
|
119
127
|
/**
|
|
120
128
|
* Creates an instance of Hotkeys.
|
|
121
|
-
* @param initialContext - Optional initial context name.
|
|
129
|
+
* @param initialContext - Optional initial context name. This forms the base of the context stack.
|
|
122
130
|
* @param debugMode - Optional. If true, debug messages will be logged to the console. Defaults to false.
|
|
123
131
|
* @throws Error if not in a browser environment (i.e., `document` or `performance` is undefined).
|
|
124
132
|
*/
|
|
125
133
|
constructor(initialContext?: string | null, debugMode?: boolean);
|
|
134
|
+
/**
|
|
135
|
+
* Helper method to determine the active context based on override and stack.
|
|
136
|
+
*/
|
|
137
|
+
private _resolveActiveContext;
|
|
126
138
|
/**
|
|
127
139
|
* Gets or creates a shared event stream for a given event type and target.
|
|
128
140
|
* @param eventType The type of event ("keydown" or "keyup").
|
|
@@ -131,19 +143,32 @@ export declare class Hotkeys {
|
|
|
131
143
|
*/
|
|
132
144
|
private _getEventStream;
|
|
133
145
|
/**
|
|
134
|
-
* Sets
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* @param contextName - The name of the context (e.g., "modal", "editor", "global").
|
|
138
|
-
* Pass `null` to activate shortcuts with no context or to deactivate context-specific shortcuts.
|
|
139
|
-
* @returns `true` if the context was changed, `false` if the new context was the same as the current one.
|
|
146
|
+
* Sets a temporary, high-priority override context that takes precedence over the context stack.
|
|
147
|
+
* @param contextName The override context to activate (can be a string or `null`).
|
|
148
|
+
* @returns A `restore` function that, when called, clears the override context, reverting to the stack.
|
|
140
149
|
*/
|
|
141
|
-
setContext(contextName: string | null):
|
|
150
|
+
setContext(contextName: string | null): () => void;
|
|
142
151
|
/**
|
|
143
|
-
*
|
|
152
|
+
* @deprecated Rename to `getActiveContext`
|
|
153
|
+
* Gets the current active context, considering any override.
|
|
144
154
|
* @returns The current context name as a string, or `null` if no context is set.
|
|
145
155
|
*/
|
|
146
156
|
getContext(): string | null;
|
|
157
|
+
/**
|
|
158
|
+
* Gets the current active context, considering any override.
|
|
159
|
+
* @returns The current context name as a string, or `null` if no context is set.
|
|
160
|
+
*/
|
|
161
|
+
getActiveContext(): string | null;
|
|
162
|
+
/**
|
|
163
|
+
* Pushes a new context onto the context stack. It will become active if no override context is set.
|
|
164
|
+
* @param contextName The name of the context to enter (e.g., "modal", "editor").
|
|
165
|
+
*/
|
|
166
|
+
enterContext(contextName: string | null): void;
|
|
167
|
+
/**
|
|
168
|
+
* Pops the current context from the stack.
|
|
169
|
+
* @returns The context that was just left from the stack, or `undefined` if at the base.
|
|
170
|
+
*/
|
|
171
|
+
leaveContext(): string | null | undefined;
|
|
147
172
|
/**
|
|
148
173
|
* Enables or disables debug logging for the Hotkeys instance.
|
|
149
174
|
* When enabled, various internal actions and shortcut triggers will be logged to the console.
|
|
@@ -158,10 +183,6 @@ export declare class Hotkeys {
|
|
|
158
183
|
hasShortcut(id: string): boolean;
|
|
159
184
|
/**
|
|
160
185
|
* An Observable that emits the new context name (or null) whenever the active context changes.
|
|
161
|
-
* This allows external parts of the application to react to context transitions.
|
|
162
|
-
*
|
|
163
|
-
* Note: This observable benefits from the distinct check within the `setContext` method,
|
|
164
|
-
* meaning it will only emit when the context value actually changes.
|
|
165
186
|
*
|
|
166
187
|
* @example
|
|
167
188
|
* ```typescript
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hotkeys.d.ts","sourceRoot":"","sources":["../../src/core/hotkeys.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fromEvent, BehaviorSubject, EMPTY, filter, map, bufferCount, withLatestFrom, tap, catchError, scan, merge, Subject, takeUntil, share, } from "rxjs";
|
|
1
|
+
import { fromEvent, BehaviorSubject, EMPTY, filter, map, bufferCount, withLatestFrom, tap, catchError, scan, merge, Subject, takeUntil, share, distinctUntilChanged, combineLatest, } from "rxjs";
|
|
2
2
|
import { Keys, KeyAliases } from "./keys.js";
|
|
3
3
|
// --- Enums, Interfaces and Types ---
|
|
4
4
|
export var ShortcutTypes;
|
|
@@ -59,30 +59,57 @@ export class Hotkeys {
|
|
|
59
59
|
static KEYDOWN_EVENT = "keydown";
|
|
60
60
|
static KEYUP_EVENT = "keyup";
|
|
61
61
|
static LOG_PREFIX = "Hotkeys:";
|
|
62
|
+
// --- Sentinel value for no override ---
|
|
63
|
+
static NO_OVERRIDE = Symbol("No Hotkey Override");
|
|
62
64
|
keydownStreams;
|
|
63
65
|
keyupStreams;
|
|
64
|
-
activeContext$;
|
|
65
66
|
activeShortcuts;
|
|
66
67
|
debugMode;
|
|
68
|
+
// --- Separate states for stack and override ---
|
|
69
|
+
contextStack$;
|
|
70
|
+
overrideContext$;
|
|
71
|
+
/**
|
|
72
|
+
* An Observable that emits the new active context name (or null) whenever it changes.
|
|
73
|
+
* The active context is the override context if one is set, otherwise it's the context
|
|
74
|
+
* from the top of the stack.
|
|
75
|
+
*/
|
|
76
|
+
activeContext$;
|
|
67
77
|
/**
|
|
68
78
|
* Creates an instance of Hotkeys.
|
|
69
|
-
* @param initialContext - Optional initial context name.
|
|
79
|
+
* @param initialContext - Optional initial context name. This forms the base of the context stack.
|
|
70
80
|
* @param debugMode - Optional. If true, debug messages will be logged to the console. Defaults to false.
|
|
71
81
|
* @throws Error if not in a browser environment (i.e., `document` or `performance` is undefined).
|
|
72
82
|
*/
|
|
73
83
|
constructor(initialContext = null, debugMode = false) {
|
|
74
84
|
this.debugMode = debugMode;
|
|
75
85
|
if (typeof document === "undefined" || typeof performance === "undefined") {
|
|
76
|
-
throw new Error(`${Hotkeys.LOG_PREFIX} Hotkeys can only be used in a browser environment
|
|
86
|
+
throw new Error(`${Hotkeys.LOG_PREFIX} Hotkeys can only be used in a browser environment.`);
|
|
77
87
|
}
|
|
78
88
|
this.keydownStreams = new WeakMap();
|
|
79
89
|
this.keyupStreams = new WeakMap();
|
|
80
|
-
this.activeContext$ = new BehaviorSubject(initialContext);
|
|
81
90
|
this.activeShortcuts = new Map();
|
|
91
|
+
// The context stack is the source of truth for the active context.
|
|
92
|
+
this.contextStack$ = new BehaviorSubject([initialContext]);
|
|
93
|
+
this.overrideContext$ = new BehaviorSubject(Hotkeys.NO_OVERRIDE);
|
|
94
|
+
// The public activeContext$ now correctly handles the sentinel value.
|
|
95
|
+
this.activeContext$ = combineLatest([
|
|
96
|
+
this.overrideContext$,
|
|
97
|
+
this.contextStack$.pipe(map(stack => stack.length > 0 ? stack[stack.length - 1] : null))
|
|
98
|
+
]).pipe(map(([overrideCtx, stackCtx]) => this._resolveActiveContext(overrideCtx, stackCtx)), distinctUntilChanged());
|
|
82
99
|
if (this.debugMode) {
|
|
83
100
|
console.log(`${Hotkeys.LOG_PREFIX} Library initialized. Initial context: "${initialContext}". Debug mode: ${debugMode}.`);
|
|
101
|
+
// Optional: Log context changes for debugging
|
|
102
|
+
this.activeContext$.subscribe(newContext => {
|
|
103
|
+
console.log(`${Hotkeys.LOG_PREFIX} Active context changed to: ${newContext}`);
|
|
104
|
+
});
|
|
84
105
|
}
|
|
85
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Helper method to determine the active context based on override and stack.
|
|
109
|
+
*/
|
|
110
|
+
_resolveActiveContext(overrideCtx, stackCtx) {
|
|
111
|
+
return overrideCtx !== Hotkeys.NO_OVERRIDE ? overrideCtx : stackCtx;
|
|
112
|
+
}
|
|
86
113
|
/**
|
|
87
114
|
* Gets or creates a shared event stream for a given event type and target.
|
|
88
115
|
* @param eventType The type of event ("keydown" or "keyup").
|
|
@@ -102,35 +129,78 @@ export class Hotkeys {
|
|
|
102
129
|
return streamCache.get(target);
|
|
103
130
|
}
|
|
104
131
|
/**
|
|
105
|
-
* Sets
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* @param contextName - The name of the context (e.g., "modal", "editor", "global").
|
|
109
|
-
* Pass `null` to activate shortcuts with no context or to deactivate context-specific shortcuts.
|
|
110
|
-
* @returns `true` if the context was changed, `false` if the new context was the same as the current one.
|
|
132
|
+
* Sets a temporary, high-priority override context that takes precedence over the context stack.
|
|
133
|
+
* @param contextName The override context to activate (can be a string or `null`).
|
|
134
|
+
* @returns A `restore` function that, when called, clears the override context, reverting to the stack.
|
|
111
135
|
*/
|
|
112
136
|
setContext(contextName) {
|
|
113
|
-
const currentContext = this.activeContext$.getValue();
|
|
114
|
-
if (currentContext === contextName) {
|
|
115
|
-
if (this.debugMode) {
|
|
116
|
-
// Optional: Log that no change is happening, or simply do nothing.
|
|
117
|
-
console.log(`${Hotkeys.LOG_PREFIX} setContext called with the same context "${contextName}". No change made.`);
|
|
118
|
-
}
|
|
119
|
-
return false; // Context was NOT updated
|
|
120
|
-
}
|
|
121
|
-
// If we reach here, the context is actually changing.
|
|
122
137
|
if (this.debugMode) {
|
|
123
|
-
console.log(`${Hotkeys.LOG_PREFIX}
|
|
124
|
-
}
|
|
125
|
-
this.
|
|
126
|
-
|
|
138
|
+
console.log(`${Hotkeys.LOG_PREFIX} Setting override context to: "${contextName}".`);
|
|
139
|
+
}
|
|
140
|
+
this.overrideContext$.next(contextName);
|
|
141
|
+
const restore = () => {
|
|
142
|
+
// Only clear the override if it's still the one we set.
|
|
143
|
+
if (this.overrideContext$.getValue() === contextName) {
|
|
144
|
+
if (this.debugMode) {
|
|
145
|
+
console.log(`${Hotkeys.LOG_PREFIX} Restoring/clearing override context from: "${contextName}".`);
|
|
146
|
+
}
|
|
147
|
+
// Restore now sets the special "NO_OVERRIDE" value.
|
|
148
|
+
this.overrideContext$.next(Hotkeys.NO_OVERRIDE);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
return restore;
|
|
127
152
|
}
|
|
128
153
|
/**
|
|
129
|
-
*
|
|
154
|
+
* @deprecated Rename to `getActiveContext`
|
|
155
|
+
* Gets the current active context, considering any override.
|
|
130
156
|
* @returns The current context name as a string, or `null` if no context is set.
|
|
131
157
|
*/
|
|
132
158
|
getContext() {
|
|
133
|
-
|
|
159
|
+
console.warn(`${Hotkeys.LOG_PREFIX} "getContext" is deprecated. Use "getActiveContext()" or subscribe to "onContextChange$" instead.`);
|
|
160
|
+
return this.getActiveContext();
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Gets the current active context, considering any override.
|
|
164
|
+
* @returns The current context name as a string, or `null` if no context is set.
|
|
165
|
+
*/
|
|
166
|
+
getActiveContext() {
|
|
167
|
+
const overrideCtx = this.overrideContext$.getValue();
|
|
168
|
+
const stack = this.contextStack$.getValue();
|
|
169
|
+
const stackCtx = stack.length > 0 ? stack[stack.length - 1] : null;
|
|
170
|
+
// Also uses the abstracted helper method.
|
|
171
|
+
return this._resolveActiveContext(overrideCtx, stackCtx);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Pushes a new context onto the context stack. It will become active if no override context is set.
|
|
175
|
+
* @param contextName The name of the context to enter (e.g., "modal", "editor").
|
|
176
|
+
*/
|
|
177
|
+
enterContext(contextName) {
|
|
178
|
+
const currentStack = this.contextStack$.getValue();
|
|
179
|
+
const newStack = [...currentStack, contextName];
|
|
180
|
+
if (this.debugMode) {
|
|
181
|
+
console.log(`${Hotkeys.LOG_PREFIX} Entering context: "${contextName}". New stack: [${newStack.join(", ")}]`);
|
|
182
|
+
}
|
|
183
|
+
this.contextStack$.next(newStack);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Pops the current context from the stack.
|
|
187
|
+
* @returns The context that was just left from the stack, or `undefined` if at the base.
|
|
188
|
+
*/
|
|
189
|
+
leaveContext() {
|
|
190
|
+
const currentStack = this.contextStack$.getValue();
|
|
191
|
+
if (currentStack.length <= 1) {
|
|
192
|
+
if (this.debugMode) {
|
|
193
|
+
console.log(`${Hotkeys.LOG_PREFIX} Attempted to leave the base stack context. No change made.`);
|
|
194
|
+
}
|
|
195
|
+
return undefined; // Nothing was left
|
|
196
|
+
}
|
|
197
|
+
const leavingContext = currentStack[currentStack.length - 1];
|
|
198
|
+
const newStack = currentStack.slice(0, -1);
|
|
199
|
+
if (this.debugMode) {
|
|
200
|
+
console.log(`${Hotkeys.LOG_PREFIX} Leaving context: "${leavingContext}". New stack: [${newStack.join(", ")}]`);
|
|
201
|
+
}
|
|
202
|
+
this.contextStack$.next(newStack);
|
|
203
|
+
return leavingContext;
|
|
134
204
|
}
|
|
135
205
|
/**
|
|
136
206
|
* Enables or disables debug logging for the Hotkeys instance.
|
|
@@ -159,10 +229,6 @@ export class Hotkeys {
|
|
|
159
229
|
}
|
|
160
230
|
/**
|
|
161
231
|
* An Observable that emits the new context name (or null) whenever the active context changes.
|
|
162
|
-
* This allows external parts of the application to react to context transitions.
|
|
163
|
-
*
|
|
164
|
-
* Note: This observable benefits from the distinct check within the `setContext` method,
|
|
165
|
-
* meaning it will only emit when the context value actually changes.
|
|
166
232
|
*
|
|
167
233
|
* @example
|
|
168
234
|
* ```typescript
|
|
@@ -176,7 +242,7 @@ export class Hotkeys {
|
|
|
176
242
|
* ```
|
|
177
243
|
*/
|
|
178
244
|
get onContextChange$() {
|
|
179
|
-
return this.activeContext
|
|
245
|
+
return this.activeContext$;
|
|
180
246
|
}
|
|
181
247
|
/**
|
|
182
248
|
* Compares two sequences of StandardKey arrays to see if they are identical.
|
|
@@ -440,19 +506,18 @@ export class Hotkeys {
|
|
|
440
506
|
return ctrlMatch && altMatch && shiftMatch && metaMatch;
|
|
441
507
|
}), filter(event => compareKey(event.key, configuredMainKey)),
|
|
442
508
|
// New filter for priority: Specific context > Global context
|
|
443
|
-
filter(event => {
|
|
509
|
+
withLatestFrom(this.activeContext$), filter(([event, activeCtx]) => {
|
|
444
510
|
if (context != null || strict) { // This shortcut is NOT global or strict
|
|
445
511
|
return true;
|
|
446
512
|
}
|
|
447
513
|
// This shortcut IS global. Check for specific overrides.
|
|
448
|
-
|
|
449
|
-
if (currentSpecificContext == null) { // No specific context active
|
|
514
|
+
if (activeCtx == null) { // No specific context active
|
|
450
515
|
return true;
|
|
451
516
|
}
|
|
452
517
|
for (const [, otherAS] of this.activeShortcuts) {
|
|
453
518
|
if (otherAS.config.id !== id &&
|
|
454
519
|
"keys" in otherAS.config &&
|
|
455
|
-
otherAS.config.context ===
|
|
520
|
+
otherAS.config.context === activeCtx &&
|
|
456
521
|
this._shortcutMatchesEvent(otherAS.config, event)) {
|
|
457
522
|
if (this.debugMode) {
|
|
458
523
|
console.log(`${Hotkeys.LOG_PREFIX} Global shortcut "${id}" (key: "${event.key}") suppressed by specific context shortcut "${otherAS.config.id}".`);
|
|
@@ -461,7 +526,7 @@ export class Hotkeys {
|
|
|
461
526
|
}
|
|
462
527
|
}
|
|
463
528
|
return true; // Global can proceed
|
|
464
|
-
}));
|
|
529
|
+
}), map(([event]) => event));
|
|
465
530
|
observables.push(stream);
|
|
466
531
|
}
|
|
467
532
|
if (observables.length === 0) {
|
|
@@ -601,19 +666,18 @@ export class Hotkeys {
|
|
|
601
666
|
}));
|
|
602
667
|
}
|
|
603
668
|
const terminator$ = new Subject();
|
|
604
|
-
const finalShortcutWithPriority$ = shortcut$.pipe(filter((
|
|
669
|
+
const finalShortcutWithPriority$ = shortcut$.pipe(withLatestFrom(this.activeContext$), filter(([_completedEvents, activeCtx]) => {
|
|
605
670
|
if (context != null || strict) { // This sequence is NOT global or strict
|
|
606
671
|
return true;
|
|
607
672
|
}
|
|
608
673
|
// This sequence IS global. Check for specific overrides.
|
|
609
|
-
|
|
610
|
-
if (currentSpecificContext == null) { // No specific context active
|
|
674
|
+
if (activeCtx == null) { // No specific context active
|
|
611
675
|
return true;
|
|
612
676
|
}
|
|
613
677
|
for (const [, otherAS] of this.activeShortcuts) {
|
|
614
678
|
if (otherAS.config.id !== id &&
|
|
615
679
|
"sequence" in otherAS.config &&
|
|
616
|
-
otherAS.config.context ===
|
|
680
|
+
otherAS.config.context === activeCtx &&
|
|
617
681
|
this._areSequencesIdentical(configuredSequence, typeof otherAS.config.sequence === "string" ? this._parseSequenceString(otherAS.config.sequence) : otherAS.config.sequence)) {
|
|
618
682
|
if (this.debugMode) {
|
|
619
683
|
console.log(`${Hotkeys.LOG_PREFIX} Global sequence shortcut "${id}" suppressed by identical specific-context shortcut "${otherAS.config.id}".`);
|
|
@@ -622,7 +686,7 @@ export class Hotkeys {
|
|
|
622
686
|
}
|
|
623
687
|
}
|
|
624
688
|
return true; // Global sequence can proceed
|
|
625
|
-
}), tap((events) => {
|
|
689
|
+
}), map(([events]) => events), tap((events) => {
|
|
626
690
|
if (this.debugMode) {
|
|
627
691
|
const timeoutInfo = (sequenceTimeoutMs && sequenceTimeoutMs > 0) ? ` (with timeout logic)` : ` (no timeout logic)`;
|
|
628
692
|
const preventAction = preventDefault ? ", preventing default for last event" : "";
|
|
@@ -692,8 +756,9 @@ export class Hotkeys {
|
|
|
692
756
|
shortcut.terminator$.complete();
|
|
693
757
|
});
|
|
694
758
|
this.activeShortcuts.clear();
|
|
695
|
-
this.
|
|
759
|
+
this.contextStack$.complete();
|
|
696
760
|
if (this.debugMode)
|
|
697
761
|
console.log(`${Hotkeys.LOG_PREFIX} Library destroyed.`);
|
|
698
762
|
}
|
|
699
763
|
}
|
|
764
|
+
//# sourceMappingURL=hotkeys.js.map
|