rx-hotkeys 2.6.1 → 3.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 +136 -98
- package/dist/hotkeys.d.ts +88 -37
- package/dist/hotkeys.d.ts.map +1 -1
- package/dist/hotkeys.js +228 -97
- package/dist/hotkeys.test.js +402 -238
- package/dist/keys.d.ts +6 -0
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +21 -0
- package/dist/testutils.d.ts +7 -4
- package/dist/testutils.d.ts.map +1 -1
- package/dist/testutils.js +9 -6
- package/package.json +1 -1
- package/dist/tt.d.ts +0 -125
- package/dist/tt.d.ts.map +0 -1
- package/dist/tt.js +0 -384
- package/dist/ttt.d.ts +0 -164
- package/dist/ttt.d.ts.map +0 -1
- package/dist/ttt.js +0 -439
- package/dist/tttt.d.ts +0 -67
- package/dist/tttt.d.ts.map +0 -1
- package/dist/tttt.js +0 -301
package/README.md
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
# rx-hotkeys: Advanced Keyboard Shortcut Management
|
|
1
|
+
# rx-hotkeys: Advanced Keyboard Shortcut Management with RxJS
|
|
2
2
|
|
|
3
|
-
rx-hotkeys is a powerful and flexible TypeScript library for managing keyboard shortcuts in web applications. It leverages RxJS to handle keyboard events, allowing for the registration of simple key combinations (e.g., `Ctrl+S`)
|
|
3
|
+
rx-hotkeys is a powerful and flexible TypeScript library for managing keyboard shortcuts in web applications. It leverages the full power of RxJS to handle keyboard events, allowing for the registration of simple key combinations (e.g., `Ctrl+S`), complex key sequences (e.g., `g` -> `i` for "go to inbox"), and much more. It supports contexts for enabling/disabling shortcuts based on application state, element-scoped listeners, and provides a type-safe API for defining shortcuts.
|
|
4
4
|
|
|
5
|
-
## Features
|
|
5
|
+
## ✨ Features
|
|
6
6
|
|
|
7
|
-
* **
|
|
8
|
-
* **
|
|
9
|
-
* **
|
|
10
|
-
*
|
|
11
|
-
* **
|
|
12
|
-
* **
|
|
13
|
-
* **
|
|
14
|
-
* **
|
|
15
|
-
* **
|
|
16
|
-
* **
|
|
7
|
+
* **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
|
+
* **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
|
+
* **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
|
+
* **`keyup` Event Support**: Trigger actions on key release (`keyup`) in addition to the default key press (`keydown`).
|
|
11
|
+
* **Key Combinations & Sequences**: Supports both simultaneous key presses (`Ctrl+S`) and ordered key sequences (`g` -> `c`).
|
|
12
|
+
* **Context Management**: Activate or deactivate groups of shortcuts based on the application's current state (e.g., "editor", "modal", "global").
|
|
13
|
+
* **Strict Global Shortcuts**: Option to register global shortcuts that *only* fire when no other context is active.
|
|
14
|
+
* **Type-Safe Key Definitions**: Uses an exported `Keys` object based on standard `KeyboardEvent.key` values for a superior developer experience and fewer errors.
|
|
15
|
+
* **Sequence Timeouts**: Optional timeout between key presses in a sequence to prevent accidental triggers.
|
|
16
|
+
* **Debug Mode**: Optional, detailed console logging for easier development and troubleshooting.
|
|
17
17
|
|
|
18
18
|
## Installation
|
|
19
19
|
|
|
@@ -21,13 +21,47 @@ rx-hotkeys is a powerful and flexible TypeScript library for managing keyboard s
|
|
|
21
21
|
npm install rxjs rx-hotkeys
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
## ⚠️ Breaking Changes (v3.0+)
|
|
25
|
+
|
|
26
|
+
Starting with v3.0, the API has been significantly updated for a more powerful and idiomatic RxJS experience. This is a major breaking change.
|
|
27
|
+
|
|
28
|
+
* `addCombination` and `addSequence` no longer accept a `callback` property in their configuration.
|
|
29
|
+
* They now return an **`Observable<KeyboardEvent>`**.
|
|
30
|
+
* You **must** now call `.subscribe()` on the returned Observable to execute your action.
|
|
31
|
+
|
|
32
|
+
**Migration Example:**
|
|
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
|
+
```
|
|
24
58
|
|
|
25
59
|
## Basic Usage
|
|
26
60
|
|
|
27
|
-
First, ensure you have the `
|
|
61
|
+
First, ensure you have the `Hotkeys` class and its helper `Keys` object imported:
|
|
28
62
|
|
|
29
63
|
```typescript
|
|
30
|
-
import { Hotkeys, Keys
|
|
64
|
+
import { Hotkeys, Keys } from "rx-hotkeys";
|
|
31
65
|
```
|
|
32
66
|
|
|
33
67
|
### 1. Initialize Hotkeys
|
|
@@ -37,54 +71,78 @@ Create an instance of the `Hotkeys` class. You can optionally provide an initial
|
|
|
37
71
|
```typescript
|
|
38
72
|
const keyManager = new Hotkeys(); // No initial context, debug mode off
|
|
39
73
|
|
|
40
|
-
//
|
|
41
|
-
// const keyManager = new Hotkeys(
|
|
74
|
+
// Or with an initial context and debug mode enabled:
|
|
75
|
+
// const keyManager = new Hotkeys("editor", true);
|
|
42
76
|
```
|
|
43
77
|
|
|
44
78
|
### 2. Add a Key Combination
|
|
45
79
|
|
|
46
|
-
Register a shortcut for a key combination, like Ctrl+S.
|
|
80
|
+
Register a shortcut for a key combination, like `Ctrl+S`, by subscribing to the returned Observable.
|
|
47
81
|
|
|
48
82
|
```typescript
|
|
49
|
-
const
|
|
83
|
+
const save$ = keyManager.addCombination({
|
|
50
84
|
id: "saveFile", // Unique ID for this shortcut
|
|
51
|
-
keys: { key: Keys.S, ctrlKey: true }, // Use Keys.S for
|
|
52
|
-
callback: () => {
|
|
53
|
-
console.log("Ctrl+S pressed: Save file action triggered!");
|
|
54
|
-
},
|
|
85
|
+
keys: { key: Keys.S, ctrlKey: true }, // Use Keys.S for "s" key
|
|
55
86
|
preventDefault: true, // Prevent browser's default save action
|
|
56
87
|
description: "Save the current file."
|
|
57
|
-
};
|
|
88
|
+
});
|
|
58
89
|
|
|
59
|
-
|
|
90
|
+
const saveSubscription = save$.subscribe((event) => {
|
|
91
|
+
console.log("Ctrl+S pressed: Save file action triggered!", event);
|
|
92
|
+
});
|
|
60
93
|
```
|
|
61
94
|
|
|
62
|
-
### 3.
|
|
95
|
+
### 3. Define Shortcuts with Strings (New)
|
|
96
|
+
|
|
97
|
+
You can also use more concise strings to define shortcuts.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// Combination
|
|
101
|
+
const open$ = keyManager.addCombination({ id: "openFile", keys: "ctrl+o" });
|
|
102
|
+
open$.subscribe(() => console.log("Opening file..."));
|
|
103
|
+
|
|
104
|
+
// Sequence
|
|
105
|
+
const command$ = keyManager.addSequence({ id: "showCommandPalette", sequence: "cmd+k" }); // Note: "cmd+k" is a combination, not a sequence. Let's fix this example.
|
|
106
|
+
const command$ = keyManager.addSequence({ id: "goToInbox", sequence: "g -> i" });
|
|
107
|
+
command$.subscribe(() => console.log("Navigating to Inbox..."));
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 4. Add a Key Sequence
|
|
63
111
|
|
|
64
112
|
Register a shortcut for a sequence of keys, like the Konami code.
|
|
65
113
|
|
|
66
114
|
```typescript
|
|
67
|
-
const
|
|
115
|
+
const konami$ = keyManager.addSequence({
|
|
68
116
|
id: "konamiCode",
|
|
69
|
-
sequence:
|
|
70
|
-
Keys.ArrowUp, Keys.ArrowUp,
|
|
71
|
-
Keys.ArrowDown, Keys.ArrowDown,
|
|
72
|
-
Keys.ArrowLeft, Keys.ArrowRight,
|
|
73
|
-
Keys.ArrowLeft, Keys.ArrowRight,
|
|
74
|
-
Keys.B, Keys.A // 'B' and 'A' from Keys
|
|
75
|
-
],
|
|
76
|
-
callback: (event) => { // The last KeyboardEvent of the sequence is passed
|
|
77
|
-
console.log("Konami code entered!");
|
|
78
|
-
// event.preventDefault(); // Can also be done here if not set in config
|
|
79
|
-
},
|
|
117
|
+
sequence: "up -> up -> down -> down -> left -> right -> left -> right -> b -> a",
|
|
80
118
|
sequenceTimeoutMs: 3000, // User has 3 seconds between each key press
|
|
81
119
|
description: "Unlock special features."
|
|
82
|
-
};
|
|
120
|
+
});
|
|
83
121
|
|
|
84
|
-
|
|
122
|
+
konami$.subscribe((event) => { // The last KeyboardEvent of the sequence is emitted
|
|
123
|
+
console.log("Konami code entered!");
|
|
124
|
+
});
|
|
85
125
|
```
|
|
86
126
|
|
|
87
|
-
###
|
|
127
|
+
### 5. Advanced Usage: Scopes and `keyup`
|
|
128
|
+
|
|
129
|
+
You can scope a shortcut to a specific element and trigger it on `keyup`.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const myInputField = document.getElementById("my-input");
|
|
133
|
+
|
|
134
|
+
const submit$ = keyManager.addCombination({
|
|
135
|
+
id: "submitOnEnter",
|
|
136
|
+
keys: Keys.Enter,
|
|
137
|
+
target: myInputField, // Only active on this element
|
|
138
|
+
event: "keyup", // Trigger on key release
|
|
139
|
+
preventDefault: true
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
submit$.subscribe(() => console.log("Form submitted on Enter keyup!"));
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 6. Manage Contexts
|
|
88
146
|
|
|
89
147
|
Control which shortcuts are active by setting the context.
|
|
90
148
|
|
|
@@ -96,27 +154,9 @@ keyManager.setContext("editor"); // Activates "editor" shortcuts and global shor
|
|
|
96
154
|
keyManager.setContext(null);
|
|
97
155
|
```
|
|
98
156
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
Global shortcuts (those without a `context` property) have two behaviors:
|
|
102
|
-
|
|
103
|
-
* **Default Global**: By default, a global shortcut will fire in *any* context, unless a more specific shortcut for the same key combination exists for that context.
|
|
104
|
-
|
|
105
|
-
```typescript
|
|
106
|
-
// This shortcut for Ctrl+P will fire in the "editor" context, "modal" context, or any other,
|
|
107
|
-
// unless a specific "editor" shortcut for Ctrl+P exists.
|
|
108
|
-
keyManager.addCombination({ id: 'globalPrint', keys: { key: Keys.P, ctrlKey: true }, callback: myCallback });
|
|
109
|
-
```
|
|
110
|
-
* **Strict Global**: By passing `true` as the second argument to `addCombination` or `addSequence`, you can register a "strict" global shortcut. This shortcut will **only** fire when no context is active (`keyManager.getContext()` returns `null`).
|
|
111
|
-
|
|
112
|
-
```typescript
|
|
113
|
-
// This help shortcut for "?" will ONLY fire when no other context is active.
|
|
114
|
-
keyManager.addCombination({ id: 'strictHelp', keys: Keys.QuestionMark, callback: openHelpModal, strict: true });
|
|
115
|
-
```
|
|
157
|
+
### 7. Clean Up
|
|
116
158
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
When the Hotkeys instance is no longer needed (e.g., component unmount), call `destroy()` to clean up subscriptions and prevent memory leaks.
|
|
159
|
+
When the Hotkeys instance is no longer needed (e.g., component unmount), call `destroy()` to clean up all internal streams and listeners, preventing memory leaks. This will also `complete` all active shortcut Observables.
|
|
120
160
|
|
|
121
161
|
```typescript
|
|
122
162
|
// In a component lifecycle cleanup method or similar:
|
|
@@ -128,8 +168,8 @@ keyManager.destroy();
|
|
|
128
168
|
|
|
129
169
|
### `Keys` Object & `StandardKey` Type
|
|
130
170
|
|
|
131
|
-
* `Keys`: An exported constant object containing standard KeyboardEvent.key string values (e.g., Keys.Enter
|
|
132
|
-
* `StandardKey`: A TypeScript type representing any valid key string from the Keys object.
|
|
171
|
+
* `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
|
+
* `StandardKey`: A TypeScript type representing any valid key string from the `Keys` object.
|
|
133
173
|
|
|
134
174
|
### `Hotkeys` Class
|
|
135
175
|
|
|
@@ -137,39 +177,35 @@ keyManager.destroy();
|
|
|
137
177
|
|
|
138
178
|
Creates a new Hotkeys instance.
|
|
139
179
|
|
|
140
|
-
`addCombination(config: KeyCombinationConfig):
|
|
180
|
+
`addCombination(config: KeyCombinationConfig): Observable<KeyboardEvent>`
|
|
141
181
|
|
|
142
182
|
Registers a key combination shortcut.
|
|
183
|
+
* `config`: The `KeyCombinationConfig` object.
|
|
184
|
+
* Returns an `Observable<KeyboardEvent>` that emits when the shortcut is triggered.
|
|
143
185
|
|
|
144
|
-
|
|
145
|
-
* Returns the shortcut ID if successful, undefined otherwise.
|
|
146
|
-
|
|
147
|
-
`addSequence(config: KeySequenceConfig): string | undefined`
|
|
186
|
+
`addSequence(config: KeySequenceConfig): Observable<KeyboardEvent>`
|
|
148
187
|
|
|
149
188
|
Registers a key sequence shortcut.
|
|
150
|
-
|
|
151
|
-
* `
|
|
152
|
-
* Returns the shortcut ID if successful, undefined otherwise.
|
|
189
|
+
* `config`: The `KeySequenceConfig` object.
|
|
190
|
+
* Returns an `Observable<KeyboardEvent>` that emits the final `KeyboardEvent` when the sequence is completed.
|
|
153
191
|
|
|
154
192
|
`setContext(contextName: string | null): boolean`
|
|
155
193
|
|
|
156
|
-
Sets the active context. Only shortcuts matching this context or global shortcuts
|
|
194
|
+
Sets the active context. Only shortcuts matching this context or global shortcuts will trigger.
|
|
157
195
|
|
|
158
196
|
`getContext(): string | null`
|
|
159
197
|
|
|
160
|
-
Returns the current active context name, or null
|
|
198
|
+
Returns the current active context name, or `null`.
|
|
161
199
|
|
|
162
200
|
`remove(id: string): boolean`
|
|
163
201
|
|
|
164
|
-
Removes a registered shortcut by its ID.
|
|
165
|
-
|
|
166
|
-
* Returns true if found and removed, false otherwise.
|
|
202
|
+
Removes a registered shortcut by its ID. This will cause the corresponding Observable to complete.
|
|
203
|
+
* Returns `true` if found and removed, `false` otherwise.
|
|
167
204
|
|
|
168
205
|
`hasShortcut(id: string): boolean`
|
|
169
206
|
|
|
170
207
|
Checks if a shortcut with the given ID is registered.
|
|
171
|
-
|
|
172
|
-
* Returns true if it exists, false otherwise.
|
|
208
|
+
* Returns `true` if it exists, `false` otherwise.
|
|
173
209
|
|
|
174
210
|
`getActiveShortcuts(): { id: string; description?: string; context?: string | null; type: "combination" | "sequence" }[]`
|
|
175
211
|
|
|
@@ -185,32 +221,36 @@ Cleans up all subscriptions and resources. Essential to call to prevent memory l
|
|
|
185
221
|
|
|
186
222
|
### Configuration Interfaces
|
|
187
223
|
|
|
188
|
-
`KeyCombinationConfig`
|
|
224
|
+
#### `KeyCombinationConfig`
|
|
189
225
|
|
|
190
226
|
* `id: string` (required): Unique identifier for the shortcut.
|
|
191
|
-
* `keys:
|
|
192
|
-
* `callback: (event: KeyboardEvent) => void` (required): Function to execute when the shortcut is triggered. The triggering `KeyboardEvent` is passed as an argument.
|
|
227
|
+
* `keys: string | KeyCombinationTrigger | KeyCombinationTrigger[]` (required): Defines the key(s). Can be a string (`"ctrl+s"`), a shorthand `StandardKey` (`Keys.Escape`), an object (`{ key: Keys.S, ctrlKey: true }`), or an array of these.
|
|
193
228
|
* `context?: string | null`: Specifies the context in which this shortcut is active. If `null` or `undefined`, it's a global shortcut.
|
|
194
|
-
* `preventDefault?: boolean`: If true
|
|
229
|
+
* `preventDefault?: boolean`: If `true`, `event.preventDefault()` will be called when the shortcut triggers. Defaults to `false`.
|
|
195
230
|
* `description?: string`: An optional description for the shortcut (e.g., for help menus).
|
|
196
231
|
* `strict?: boolean` (optional): If `true` and the shortcut has no `context`, it will only fire when no other context is active. Defaults to `false`.
|
|
232
|
+
* `target?: HTMLElement` (optional): The DOM element to attach the listener to. Defaults to `document`.
|
|
233
|
+
* `event?: "keydown" | "keyup"` (optional): The keyboard event to listen for. Defaults to `"keydown"`.
|
|
234
|
+
* `callback?: (event: KeyboardEvent) => void` (**@deprecated**): This property is deprecated. Subscribe to the `Observable` returned by `addCombination` instead.
|
|
197
235
|
|
|
198
|
-
`KeySequenceConfig`
|
|
236
|
+
#### `KeySequenceConfig`
|
|
199
237
|
|
|
200
238
|
* `id: string` (required): Unique identifier.
|
|
201
|
-
* `sequence: StandardKey[]` (required): An array of `StandardKey` values (
|
|
202
|
-
* `callback: (event: KeyboardEvent) => void` (required): Function to execute. The last `KeyboardEvent` of the sequence is passed.
|
|
239
|
+
* `sequence: string | StandardKey[]` (required): An array of `StandardKey` values or a string representation (e.g., `"g -> i"`).
|
|
203
240
|
* `context?: string | null`: Context for activation.
|
|
204
|
-
* `preventDefault?: boolean`: If true
|
|
241
|
+
* `preventDefault?: boolean`: If `true`, `event.preventDefault()` is called for the last event in the sequence. Defaults to `false`.
|
|
205
242
|
* `description?: string`: Optional description.
|
|
206
|
-
* `sequenceTimeoutMs?: number`: Optional. Maximum time (in milliseconds) allowed between consecutive key presses in the sequence.
|
|
207
|
-
* `strict?: boolean` (optional): If `true` and the shortcut has no `context`, it will only fire when no other context is active.
|
|
243
|
+
* `sequenceTimeoutMs?: number`: Optional. Maximum time (in milliseconds) allowed between consecutive key presses in the sequence.
|
|
244
|
+
* `strict?: boolean` (optional): If `true` and the shortcut has no `context`, it will only fire when no other context is active.
|
|
245
|
+
* `target?: HTMLElement` (optional): The DOM element to attach the listener to. Defaults to `document`.
|
|
246
|
+
* `event?: "keydown" | "keyup"` (optional): The keyboard event to listen for. Defaults to `"keydown"`.
|
|
247
|
+
* `callback?: (event: KeyboardEvent) => void` (**@deprecated**): This property is deprecated. Subscribe to the `Observable` returned by `addSequence` instead.
|
|
208
248
|
|
|
249
|
+
## Key Matching & Normalization
|
|
209
250
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
* **
|
|
213
|
-
* **Special Keys** (e.g., `Keys.Enter`, `Keys.ArrowUp`, `Keys.Escape`): These are multi-character `event.key` values. The library matches these case-sensitively against the `event.key`. Using the `Keys` object ensures you provide the correct, standard case-sensitive string.
|
|
251
|
+
* **Case Insensitivity**: The library automatically handles case for you. `keys: "a"` will match both "a" and "A" presses. `keys: "escape"` will match an event where `event.key` is `"Escape"`.
|
|
252
|
+
* **Aliases**: Common aliases are supported in string definitions, such as `cmd` for `Meta`, `option` for `Alt`, and `esc` for `Escape`.
|
|
253
|
+
* **Special Keys**: For full type-safety, it is recommended to use the exported `Keys` object (e.g., `Keys.Enter`, `Keys.ArrowUp`).
|
|
214
254
|
|
|
215
255
|
## Contributing
|
|
216
256
|
|
|
@@ -218,12 +258,10 @@ Contributions are welcome! Please feel free to submit issues, fork the repositor
|
|
|
218
258
|
|
|
219
259
|
## Development Setup
|
|
220
260
|
|
|
221
|
-
1.
|
|
222
|
-
2.
|
|
223
|
-
3.
|
|
261
|
+
1. Clone the repository.
|
|
262
|
+
2. Install dependencies: `npm install`.
|
|
263
|
+
3. Run tests: `npm test`.
|
|
224
264
|
|
|
225
|
-
|
|
265
|
+
## License
|
|
226
266
|
|
|
227
267
|
This project is licensed under the MIT License.
|
|
228
|
-
|
|
229
|
-
Powered by AI
|
package/dist/hotkeys.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Observable, Subject } from "rxjs";
|
|
2
2
|
import { type StandardKey } from "./keys.js";
|
|
3
3
|
export declare enum ShortcutTypes {
|
|
4
4
|
Combination = "combination",
|
|
@@ -6,7 +6,10 @@ export declare enum ShortcutTypes {
|
|
|
6
6
|
}
|
|
7
7
|
interface ShortcutConfigBase {
|
|
8
8
|
id: string;
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* @deprecated The callback property is deprecated. `addCombination` and `addSequence` now return an Observable. Please subscribe to it instead.
|
|
11
|
+
*/
|
|
12
|
+
callback?: (event: KeyboardEvent) => void;
|
|
10
13
|
context?: string | null;
|
|
11
14
|
preventDefault?: boolean;
|
|
12
15
|
description?: string;
|
|
@@ -19,6 +22,20 @@ interface ShortcutConfigBase {
|
|
|
19
22
|
* @default false
|
|
20
23
|
*/
|
|
21
24
|
strict?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The DOM element to which the event listener for this shortcut will be attached.
|
|
27
|
+
* If not provided, the listener will be attached to the `document`.
|
|
28
|
+
* Useful for creating shortcuts that are only active within a specific component or area.
|
|
29
|
+
* @default document
|
|
30
|
+
*/
|
|
31
|
+
target?: HTMLElement;
|
|
32
|
+
/**
|
|
33
|
+
* The type of keyboard event to listen for.
|
|
34
|
+
* Use "keydown" for actions that should happen immediately upon pressing a key.
|
|
35
|
+
* Use "keyup" for actions that should happen upon releasing a key.
|
|
36
|
+
* @default "keydown"
|
|
37
|
+
*/
|
|
38
|
+
event?: "keydown" | "keyup";
|
|
22
39
|
}
|
|
23
40
|
/**
|
|
24
41
|
* Defines a single key trigger, which can be a StandardKey (for simple presses like "Escape")
|
|
@@ -44,32 +61,34 @@ type KeyCombinationTrigger = {
|
|
|
44
61
|
export interface KeyCombinationConfig extends ShortcutConfigBase {
|
|
45
62
|
/**
|
|
46
63
|
* Defines the key or key combination(s) that trigger the shortcut.
|
|
47
|
-
* Can be a single trigger
|
|
48
|
-
*
|
|
64
|
+
* Can be a single trigger, an array of triggers, or a string representation.
|
|
65
|
+
*
|
|
66
|
+
* **Object/Array:** Each trigger can be an object specifying the main `key` (from `StandardKey`) and optional
|
|
49
67
|
* modifiers (`ctrlKey`, `altKey`, `shiftKey`, `metaKey`).
|
|
50
68
|
* Example: `{ key: Keys.S, ctrlKey: true }` for Ctrl+S.
|
|
51
69
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
70
|
+
* **Shorthand:** For a simple key press without modifiers, a trigger can be a `StandardKey` directly.
|
|
71
|
+
* Example: `Keys.Escape` for the Escape key.
|
|
72
|
+
*
|
|
73
|
+
* **String:** A human-readable string like `"ctrl+s"` or `"shift+alt+k"`. Modifiers are joined by `+`.
|
|
74
|
+
* Example: `"meta+k"`, `"ctrl+shift+?"`
|
|
56
75
|
*
|
|
57
76
|
* To define multiple triggers for the same action:
|
|
58
77
|
* Example: `keys: [Keys.Enter, { key: Keys.Space, ctrlKey: true }]`
|
|
59
78
|
*/
|
|
60
|
-
keys: KeyCombinationTrigger | KeyCombinationTrigger[];
|
|
79
|
+
keys: KeyCombinationTrigger | KeyCombinationTrigger[] | string;
|
|
61
80
|
}
|
|
62
81
|
export interface KeySequenceConfig extends ShortcutConfigBase {
|
|
63
82
|
/**
|
|
64
|
-
* An array of keys
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* Example:
|
|
83
|
+
* An array of keys or a string defining the sequence.
|
|
84
|
+
*
|
|
85
|
+
* **Array:** Each key in the sequence MUST be a value from `Keys`.
|
|
86
|
+
* Example: `[Keys.G, Keys.I]`
|
|
87
|
+
*
|
|
88
|
+
* **String:** A string where keys are separated by `->`.
|
|
89
|
+
* Example: `"g -> i"`, `"up -> up -> down -> down"`
|
|
71
90
|
*/
|
|
72
|
-
sequence: StandardKey[];
|
|
91
|
+
sequence: StandardKey[] | string;
|
|
73
92
|
/**
|
|
74
93
|
* Optional: Timeout in milliseconds between consecutive key presses in the sequence.
|
|
75
94
|
* If the time between two keys in the sequence exceeds this value, the sequence attempt is reset.
|
|
@@ -81,7 +100,7 @@ type ShortcutConfig = KeyCombinationConfig | KeySequenceConfig;
|
|
|
81
100
|
export interface ActiveShortcut {
|
|
82
101
|
id: string;
|
|
83
102
|
config: ShortcutConfig;
|
|
84
|
-
|
|
103
|
+
terminator$: Subject<void>;
|
|
85
104
|
}
|
|
86
105
|
/**
|
|
87
106
|
* Manages keyboard shortcuts for web applications.
|
|
@@ -90,8 +109,10 @@ export interface ActiveShortcut {
|
|
|
90
109
|
*/
|
|
91
110
|
export declare class Hotkeys {
|
|
92
111
|
private static readonly KEYDOWN_EVENT;
|
|
112
|
+
private static readonly KEYUP_EVENT;
|
|
93
113
|
private static readonly LOG_PREFIX;
|
|
94
|
-
private
|
|
114
|
+
private keydownStreams;
|
|
115
|
+
private keyupStreams;
|
|
95
116
|
private activeContext$;
|
|
96
117
|
private activeShortcuts;
|
|
97
118
|
private debugMode;
|
|
@@ -102,6 +123,13 @@ export declare class Hotkeys {
|
|
|
102
123
|
* @throws Error if not in a browser environment (i.e., `document` or `performance` is undefined).
|
|
103
124
|
*/
|
|
104
125
|
constructor(initialContext?: string | null, debugMode?: boolean);
|
|
126
|
+
/**
|
|
127
|
+
* Gets or creates a shared event stream for a given event type and target.
|
|
128
|
+
* @param eventType The type of event ("keydown" or "keyup").
|
|
129
|
+
* @param target The DOM element to attach the listener to.
|
|
130
|
+
* @returns A shared Observable for the specified event.
|
|
131
|
+
*/
|
|
132
|
+
private _getEventStream;
|
|
105
133
|
/**
|
|
106
134
|
* Sets the active context for shortcuts.
|
|
107
135
|
* Only shortcuts matching this context (or shortcuts with no specific context defined)
|
|
@@ -172,57 +200,80 @@ export declare class Hotkeys {
|
|
|
172
200
|
* @returns An object containing configuredMainKey and modifier states, or null if parsing fails.
|
|
173
201
|
*/
|
|
174
202
|
private _parseKeyTrigger;
|
|
203
|
+
private _parseCombinationString;
|
|
204
|
+
private _parseSequenceString;
|
|
175
205
|
/**
|
|
176
|
-
* Registers a key combination shortcut (e.g., Ctrl+S, Shift+Enter, or a single key like Escape)
|
|
177
|
-
*
|
|
206
|
+
* Registers a key combination shortcut (e.g., Ctrl+S, Shift+Enter, or a single key like Escape)
|
|
207
|
+
* and returns an Observable that emits the `KeyboardEvent` when the combination is triggered.
|
|
178
208
|
* @param config - Configuration object for the key combination.
|
|
179
209
|
* See {@link KeyCombinationConfig} for details.
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
210
|
+
* @returns An `Observable<KeyboardEvent>` that you can subscribe to. The stream will be automatically
|
|
211
|
+
* completed if the shortcut is removed via `remove(id)` or `destroy()`, or if it's overwritten.
|
|
212
|
+
* If the configuration is invalid, an empty Observable is returned and a warning is logged.
|
|
183
213
|
* @example
|
|
184
214
|
* ```typescript
|
|
185
215
|
* import { Keys } from "./keys";
|
|
186
216
|
* // For Ctrl+S
|
|
187
|
-
* keyManager.addCombination({
|
|
217
|
+
* const save$ = keyManager.addCombination({
|
|
188
218
|
* id: "saveFile",
|
|
189
219
|
* keys: { key: Keys.S, ctrlKey: true },
|
|
190
|
-
* callback: () => console.log("File saved!"),
|
|
191
220
|
* context: "editor"
|
|
192
221
|
* });
|
|
222
|
+
* save$.subscribe(event => console.log("File saved!", event));
|
|
223
|
+
*
|
|
224
|
+
* // For Ctrl+S using a string
|
|
225
|
+
* const save$ = keyManager.addCombination({ id: "saveFile", keys: "ctrl+s" });
|
|
226
|
+
* save$.subscribe(event => console.log("File saved!", event));
|
|
227
|
+
*
|
|
193
228
|
* // For just the Escape key, or Ctrl+Space
|
|
194
|
-
* keyManager.addCombination({
|
|
229
|
+
* const close$ = keyManager.addCombination({
|
|
195
230
|
* id: "closeModal",
|
|
196
231
|
* keys: [Keys.Escape, {key: Keys.Space, ctrlKey: true}],
|
|
197
|
-
* callback: () => console.log("Modal closed!")
|
|
198
232
|
* });
|
|
233
|
+
* close$.subscribe(() => console.log("Modal closed!"));
|
|
234
|
+
*
|
|
235
|
+
* // For the Escape key on a specific element
|
|
236
|
+
* const myModal = document.getElementById("my-modal");
|
|
237
|
+
* const close$ = keyManager.addCombination({ id: "closeModal", keys: Keys.Escape, target: myModal });
|
|
238
|
+
* close$.subscribe(() => console.log("Modal closed!"));
|
|
199
239
|
* ```
|
|
200
240
|
*/
|
|
201
|
-
addCombination(config: KeyCombinationConfig):
|
|
241
|
+
addCombination(config: KeyCombinationConfig): Observable<KeyboardEvent>;
|
|
202
242
|
/**
|
|
203
|
-
* Registers a key sequence shortcut (e.g., g -> i, or ArrowUp -> ArrowUp -> ArrowDown)
|
|
204
|
-
*
|
|
243
|
+
* Registers a key sequence shortcut (e.g., g -> i, or ArrowUp -> ArrowUp -> ArrowDown)
|
|
244
|
+
* and returns an Observable that emits the final `KeyboardEvent` of the sequence when it's completed.
|
|
205
245
|
* An optional timeout can be specified for the time allowed between key presses in the sequence.
|
|
206
246
|
* @param config - Configuration object for the key sequence.
|
|
207
247
|
* See {@link KeySequenceConfig} for details.
|
|
208
248
|
* Each key in the `sequence` array must be a value from the `Keys` object.
|
|
209
|
-
*
|
|
210
|
-
*
|
|
249
|
+
* Or using string for `sequence`.
|
|
250
|
+
* @returns An `Observable<KeyboardEvent>` that you can subscribe to. The stream will be automatically
|
|
251
|
+
* completed if the shortcut is removed via `remove(id)` or `destroy()`, or if it's overwritten.
|
|
252
|
+
* If the configuration is invalid, an empty Observable is returned and a warning is logged.
|
|
211
253
|
* @example
|
|
212
254
|
* ```typescript
|
|
213
255
|
* import { Keys } from "./keys";
|
|
214
|
-
* keyManager.addSequence({
|
|
256
|
+
* const konami$ = keyManager.addSequence({
|
|
215
257
|
* id: "konamiCode",
|
|
216
258
|
* sequence: [Keys.ArrowUp, Keys.ArrowUp, Keys.ArrowDown, Keys.ArrowDown, Keys.A, Keys.B],
|
|
217
|
-
* callback: () => console.log("Konami!"),
|
|
218
259
|
* sequenceTimeoutMs: 2000 // 2 seconds between keys
|
|
219
260
|
* });
|
|
261
|
+
* konami$.subscribe(event => console.log("Konami!", event));
|
|
262
|
+
* ```
|
|
263
|
+
* ```typescript
|
|
264
|
+
* // Using a string for the sequence
|
|
265
|
+
* const konami$ = keyManager.addSequence({
|
|
266
|
+
* id: "konamiCode",
|
|
267
|
+
* sequence: "up -> up -> down -> down -> a -> b",
|
|
268
|
+
* sequenceTimeoutMs: 2000
|
|
269
|
+
* });
|
|
270
|
+
* konami$.subscribe(event => console.log("Konami!", event));
|
|
220
271
|
* ```
|
|
221
272
|
*/
|
|
222
|
-
addSequence(config: KeySequenceConfig):
|
|
273
|
+
addSequence(config: KeySequenceConfig): Observable<KeyboardEvent>;
|
|
223
274
|
/**
|
|
224
275
|
* Removes a registered shortcut by its ID.
|
|
225
|
-
* This will
|
|
276
|
+
* This will complete the corresponding Observable stream for any subscribers.
|
|
226
277
|
* @param id - The unique ID of the shortcut to remove.
|
|
227
278
|
* @returns True if the shortcut was found and removed, false otherwise.
|
|
228
279
|
* A warning is logged to the console if no shortcut with the given ID is found.
|
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,
|
|
1
|
+
{"version":3,"file":"hotkeys.d.ts","sourceRoot":"","sources":["../src/hotkeys.ts"],"names":[],"mappings":"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"}
|