rx-hotkeys 1.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 ADDED
@@ -0,0 +1,205 @@
1
+ # rx-hotkeys: Advanced Keyboard Shortcut Management using rxjs
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`) and complex key sequences (e.g., `g` -> `i` for "go to inbox"). It supports contexts for enabling/disabling shortcuts based on application state, and provides a type-safe way to define keys using standard `KeyboardEvent.key` values.
4
+
5
+ ## Features
6
+
7
+ * **Key Combinations:** Define shortcuts that trigger when a specific key and modifier keys (Ctrl, Alt, Shift, Meta) are pressed simultaneously.
8
+ * **Key Sequences:** Define shortcuts that trigger when a series of keys are pressed in a specific order.
9
+ * **Sequence Timeouts:** Optional timeout between key presses in a sequence to prevent accidental triggers or indefinite waiting.
10
+ * **Context Management:** Activate or deactivate groups of shortcuts based on the application's current state (e.g., "editor", "modal", "global").
11
+ * **Type-Safe Key Definitions:** Uses an exported `Keys` object based on standard `KeyboardEvent.key` values for improved developer experience and fewer errors.
12
+ * **RxJS Powered:** Built on RxJS for robust and efficient event handling.
13
+ * **Prevent Default:** Option to prevent the default browser action for a triggered shortcut.
14
+ * **Debug Mode:** Optional logging for easier development and troubleshooting.
15
+ * **Clean API:** Simple and intuitive methods for adding, removing, and managing shortcuts.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install rxjs rx-hotkeys
21
+ ```
22
+
23
+
24
+ ## Basic Usage
25
+
26
+ First, ensure you have the `rx-hotkeys` library and its helper Keys imported:
27
+
28
+ ```typescript
29
+ import { HotKeys, Keys, KeyCombinationConfig, KeySequenceConfig } from 'rx-hotkeys';
30
+ ```
31
+
32
+ 1. Initialize HotKeys
33
+
34
+ Create an instance of the `Hotkeys` class. You can optionally provide an initial context and enable debug mode.
35
+
36
+ ```typescript
37
+ const keyManager = new HotKeys(); // No initial context, debug mode off
38
+
39
+ // With an initial context and debug mode enabled:
40
+ // const keyManager = new HotKeys('editor', true);
41
+ ```
42
+
43
+ 2. Add a Key Combination
44
+
45
+ Register a shortcut for a key combination, like Ctrl+S.
46
+
47
+ ```typescript
48
+ const saveConfig: KeyCombinationConfig = {
49
+ id: "saveFile", // Unique ID for this shortcut
50
+ keys: { key: Keys.S, ctrlKey: true }, // Use Keys.S for 's' key
51
+ callback: () => {
52
+ console.log("Ctrl+S pressed: Save file action triggered!");
53
+ },
54
+ preventDefault: true, // Prevent browser's default save action
55
+ description: "Save the current file."
56
+ };
57
+
58
+ keyManager.addCombination(saveConfig);
59
+ ```
60
+
61
+ 3. Add a Key SequenceRegister a shortcut for a sequence of keys, like the Konami code.
62
+
63
+ ```typescript
64
+ const konamiConfig: KeySequenceConfig = {
65
+ id: "konamiCode",
66
+ sequence: [
67
+ Keys.ArrowUp, Keys.ArrowUp,
68
+ Keys.ArrowDown, Keys.ArrowDown,
69
+ Keys.ArrowLeft, Keys.ArrowRight,
70
+ Keys.ArrowLeft, Keys.ArrowRight,
71
+ Keys.B, Keys.A // 'B' and 'A' from Keys
72
+ ],
73
+ callback: (event) => { // The last KeyboardEvent of the sequence is passed
74
+ console.log("Konami code entered!");
75
+ // event.preventDefault(); // Can also be done here if not set in config
76
+ },
77
+ sequenceTimeoutMs: 3000, // User has 3 seconds between each key press
78
+ description: "Unlock special features."
79
+ };
80
+
81
+ keyManager.addSequence(konamiConfig);
82
+ ```
83
+
84
+ 4. Manage ContextsControl which shortcuts are active by setting the context.
85
+
86
+ ```typescript
87
+ // Assuming some shortcuts are configured with context: "editor"
88
+ keyManager.setContext("editor"); // Activates "editor" shortcuts and global shortcuts
89
+
90
+ // To activate only global shortcuts (those with no context or context: null)
91
+ keyManager.setContext(null);
92
+ ```
93
+
94
+ 5. Clean Up
95
+
96
+ When the HotKeys instance is no longer needed (e.g., component unmount), call `destroy()` to clean up subscriptions and prevent memory leaks.
97
+
98
+ ```typescript
99
+ // In a component lifecycle cleanup method or similar:
100
+ keyManager.destroy();
101
+ ```
102
+
103
+
104
+ ## API Reference
105
+
106
+ ### `Keys` Object & `StandardKey` Type
107
+
108
+ * `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 when defining key in `KeyCombinationConfig` or keys in the sequence array of `KeySequenceConfig`.
109
+ * `StandardKey`: A TypeScript type representing any valid key string from the Keys object.
110
+
111
+ ### `HotKeys` Class
112
+
113
+ `constructor(initialContext?: string | null, debugMode?: boolean)`
114
+
115
+ Creates a new HotKeys instance.
116
+
117
+ `addCombination(config: KeyCombinationConfig): string | undefined`
118
+
119
+ Registers a key combination shortcut.
120
+
121
+ * `config`: The KeyCombinationConfig object.
122
+ * Returns the shortcut ID if successful, undefined otherwise.
123
+
124
+ `addSequence(config: KeySequenceConfig): string | undefined`
125
+
126
+ Registers a key sequence shortcut.
127
+
128
+ * `config`: The KeySequenceConfig object.
129
+ * Returns the shortcut ID if successful, undefined otherwise.
130
+
131
+ `setContext(contextName: string | null): void`
132
+
133
+ Sets the active context. Only shortcuts matching this context or global shortcuts (no context) will trigger.
134
+
135
+ `getContext(): string | null`
136
+
137
+ Returns the current active context name, or null.
138
+
139
+ `remove(id: string): boolean`
140
+
141
+ Removes a registered shortcut by its ID.
142
+
143
+ * Returns true if found and removed, false otherwise.
144
+
145
+ `hasShortcut(id: string): boolean`
146
+
147
+ Checks if a shortcut with the given ID is registered.
148
+
149
+ * Returns true if it exists, false otherwise.
150
+
151
+ `getActiveShortcuts(): { id: string; description?: string; context?: string | null; type: "combination" | "sequence" }[]`
152
+
153
+ Returns an array of all currently registered shortcuts with their basic information.
154
+
155
+ `setDebugMode(enable: boolean): void`
156
+
157
+ Enables or disables console logging for debug purposes.
158
+
159
+ `destroy(): void`
160
+
161
+ Cleans up all subscriptions and resources. Essential to call to prevent memory leaks.
162
+
163
+ ### Configuration Interfaces
164
+
165
+ `KeyCombinationConfig`
166
+
167
+ * `id: string` (required): Unique identifier for the shortcut.
168
+ * `keys: { key: StandardKey; ctrlKey?: boolean; altKey?: boolean; shiftKey?: boolean; metaKey?: boolean; }` (required): Defines the main key (from Keys) and optional modifier keys.
169
+ * `callback: (event?: KeyboardEvent) => void` (required): Function to execute when the shortcut is triggered. The triggering `KeyboardEvent` is passed as an argument.
170
+ * `context?: string | null`: Specifies the context in which this shortcut is active. If `null` or `undefined`, it's a global shortcut.
171
+ * `preventDefault?: boolean`: If true, `event.preventDefault()` will be called when the shortcut triggers. Defaults to `false`.
172
+ * `description?: string`: An optional description for the shortcut (e.g., for help menus).
173
+
174
+ `KeySequenceConfig`
175
+
176
+ * `id: string` (required): Unique identifier.
177
+ * `sequence: StandardKey[]` (required): An array of `StandardKey` values (from `Keys`) representing the key sequence.
178
+ * `callback: (event?: KeyboardEvent) => void` (required): Function to execute. The last `KeyboardEvent` of the sequence is passed.
179
+ * `context?: string | null`: Context for activation.
180
+ * `preventDefault?: boolean`: If true, `event.preventDefault()` is called for the last event in the sequence. Defaults to `false`.
181
+ * `description?: string`: Optional description.
182
+ * `sequenceTimeoutMs?: number`: Optional. Maximum time (in milliseconds) allowed between consecutive key presses in the sequence. If exceeded, the sequence resets. If `0` or `undefined`, no inter-key timeout is applied (uses simpler buffer-based matching).
183
+
184
+
185
+ ## Key Matching Logic
186
+
187
+ * Single Character Keys (e.g., `Keys.A`, `Keys.Digit7`): When you configure a shortcut with a single character key from `Keys`, the library matches it case-insensitively against the `event.key` from the browser. For example, if you configure `Keys.A`, it will trigger for both "a" and "A" key presses (assuming Shift isn't a required modifier).
188
+ * 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.
189
+
190
+
191
+ ## Contributing
192
+
193
+ Contributions are welcome! Please feel free to submit issues, fork the repository, and create pull requests.
194
+
195
+ ## Development Setup
196
+
197
+ 1. Clone the repository.
198
+ 2. Install dependencies: `npm install`.
199
+ 3. Run tests: `npm test`.
200
+
201
+ # License
202
+
203
+ This project is licensed under the MIT License.
204
+
205
+ Powered by AI
@@ -0,0 +1,163 @@
1
+ import { StandardKey } from "./keys.js";
2
+ interface ShortcutConfigBase {
3
+ id: string;
4
+ callback: (event?: KeyboardEvent) => void;
5
+ context?: string | null;
6
+ preventDefault?: boolean;
7
+ description?: string;
8
+ }
9
+ export interface KeyCombinationConfig extends ShortcutConfigBase {
10
+ keys: {
11
+ /**
12
+ * The main key for the combination.
13
+ * This MUST be a value from the exported `Keys` object
14
+ * (e.g., `Keys.A`, `Keys.Enter`, `Keys.Escape`).
15
+ * The library handles case-insensitivity for single character keys (like A-Z, 0-9)
16
+ * automatically when comparing with the actual browser event's `event.key`.
17
+ * For special, multi-character keys (e.g. "ArrowUp", "Escape"), the value from
18
+ * `Keys` ensures the correct case-sensitive string is used.
19
+ * Refer to: https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values
20
+ */
21
+ key: StandardKey;
22
+ ctrlKey?: boolean;
23
+ altKey?: boolean;
24
+ shiftKey?: boolean;
25
+ metaKey?: boolean;
26
+ };
27
+ }
28
+ export interface KeySequenceConfig extends ShortcutConfigBase {
29
+ /**
30
+ * An array of keys that form the sequence.
31
+ * Each key in the sequence MUST be a value from the exported `Keys` object
32
+ * (e.g., `Keys.ArrowUp`, `Keys.G`, `Keys.Digit1`).
33
+ * The library handles case-insensitivity for single character keys automatically
34
+ * when comparing with the actual browser event's `event.key`.
35
+ * Refer to: https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values
36
+ * Example: [Keys.Control, Keys.Alt, Keys.Delete] or [Keys.G, Keys.I]
37
+ */
38
+ sequence: StandardKey[];
39
+ /**
40
+ * Optional: Timeout in milliseconds between consecutive key presses in the sequence.
41
+ * If the time between two keys in the sequence exceeds this value, the sequence attempt is reset.
42
+ * Set to 0 or undefined to disable inter-key timeout behavior (uses simpler buffer-based matching).
43
+ */
44
+ sequenceTimeoutMs?: number;
45
+ }
46
+ /**
47
+ * Manages keyboard shortcuts for web applications.
48
+ * Allows registration of single key combinations (e.g., Ctrl+S) and key sequences (e.g., g -> i).
49
+ * Supports contexts to enable/disable shortcuts based on application state.
50
+ */
51
+ export declare class HotKeys {
52
+ private static readonly KEYDOWN_EVENT;
53
+ private static readonly LOG_PREFIX;
54
+ private keydown$;
55
+ private activeContext$;
56
+ private activeShortcuts;
57
+ private debugMode;
58
+ /**
59
+ * Creates an instance of Hotkeys.
60
+ * @param initialContext - Optional initial context name. Shortcuts will only trigger if their context matches this, or if they have no context defined.
61
+ * @param debugMode - Optional. If true, debug messages will be logged to the console. Defaults to false.
62
+ * @throws Error if not in a browser environment (i.e., `document` or `performance` is undefined).
63
+ */
64
+ constructor(initialContext?: string | null, debugMode?: boolean);
65
+ /**
66
+ * Sets the active context for shortcuts.
67
+ * Only shortcuts matching this context (or shortcuts with no specific context defined)
68
+ * will be active and can be triggered.
69
+ * @param contextName - The name of the context (e.g., "modal", "editor", "global").
70
+ * Pass `null` to activate shortcuts with no context or to deactivate context-specific shortcuts.
71
+ */
72
+ setContext(contextName: string | null): void;
73
+ /**
74
+ * Gets the current active context.
75
+ * @returns The current context name as a string, or `null` if no context is set.
76
+ */
77
+ getContext(): string | null;
78
+ /**
79
+ * Enables or disables debug logging for the Hotkeys instance.
80
+ * When enabled, various internal actions and shortcut triggers will be logged to the console.
81
+ * @param enable - True to enable debug logs, false to disable.
82
+ */
83
+ setDebugMode(enable: boolean): void;
84
+ /**
85
+ * Checks if a shortcut with the given ID is currently registered and active.
86
+ * @param id - The unique ID of the shortcut to check.
87
+ * @returns True if a shortcut with the specified ID exists, false otherwise.
88
+ */
89
+ hasShortcut(id: string): boolean;
90
+ private filterByContext;
91
+ private _registerShortcut;
92
+ /**
93
+ * Registers a key combination shortcut (e.g., Ctrl+S, Shift+Enter).
94
+ * The callback is triggered when the specified key and modifier keys are pressed simultaneously.
95
+ * @param config - Configuration object for the key combination.
96
+ * See {@link KeyCombinationConfig} for details.
97
+ * The `key` property within `config.keys` must be a value from the `Keys` object.
98
+ * @returns The ID of the registered shortcut if successful, or `undefined` if the configuration is invalid (e.g., empty key).
99
+ * A warning is logged to the console if the configuration is invalid or if a shortcut with the same ID is overwritten.
100
+ * @example
101
+ * ```typescript
102
+ * import { Keys } from './keys';
103
+ * keyManager.addCombination({
104
+ * id: "saveFile",
105
+ * keys: { key: Keys.S, ctrlKey: true },
106
+ * callback: () => console.log("File saved!"),
107
+ * context: "editor"
108
+ * });
109
+ * ```
110
+ */
111
+ addCombination(config: KeyCombinationConfig): string | undefined;
112
+ /**
113
+ * Registers a key sequence shortcut (e.g., g -> i, or ArrowUp -> ArrowUp -> ArrowDown).
114
+ * The callback is triggered when the specified keys are pressed in order.
115
+ * An optional timeout can be specified for the time allowed between key presses in the sequence.
116
+ * @param config - Configuration object for the key sequence.
117
+ * See {@link KeySequenceConfig} for details.
118
+ * Each key in the `sequence` array must be a value from the `Keys` object.
119
+ * @returns The ID of the registered shortcut if successful, or `undefined` if the configuration is invalid (e.g., empty sequence or invalid keys).
120
+ * A warning is logged to the console if the configuration is invalid or if a shortcut with the same ID is overwritten.
121
+ * @example
122
+ * ```typescript
123
+ * import { Keys } from './keys';
124
+ * keyManager.addSequence({
125
+ * id: "konamiCode",
126
+ * sequence: [Keys.ArrowUp, Keys.ArrowUp, Keys.ArrowDown, Keys.ArrowDown, Keys.A, Keys.B],
127
+ * callback: () => console.log("Konami!"),
128
+ * sequenceTimeoutMs: 2000 // 2 seconds between keys
129
+ * });
130
+ * ```
131
+ */
132
+ addSequence(config: KeySequenceConfig): string | undefined;
133
+ /**
134
+ * Removes a registered shortcut by its ID.
135
+ * This will unsubscribe from the underlying keyboard event stream for that shortcut.
136
+ * @param id - The unique ID of the shortcut to remove.
137
+ * @returns True if the shortcut was found and removed, false otherwise.
138
+ * A warning is logged to the console if no shortcut with the given ID is found.
139
+ */
140
+ remove(id: string): boolean;
141
+ /**
142
+ * Retrieves a list of all currently active (registered) shortcut configurations.
143
+ * This can be useful for displaying available shortcuts to the user or for debugging.
144
+ * @returns An array of objects, where each object represents an active shortcut
145
+ * and includes its `id`, `description` (if provided), `context` (if any),
146
+ * and `type` ("combination" or "sequence").
147
+ */
148
+ getActiveShortcuts(): {
149
+ id: string;
150
+ description?: string;
151
+ context?: string | null;
152
+ type: "combination" | "sequence";
153
+ }[];
154
+ /**
155
+ * Cleans up all active subscriptions and resources used by the Hotkeys instance.
156
+ * This method should be called when the Hotkeys instance is no longer needed
157
+ * (e.g., when a component unmounts or the application is shutting down) to prevent memory leaks.
158
+ * After calling `destroy()`, the instance should not be used further.
159
+ */
160
+ destroy(): void;
161
+ }
162
+ export {};
163
+ //# sourceMappingURL=hotkeys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hotkeys.d.ts","sourceRoot":"","sources":["../src/hotkeys.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAIxC,UAAU,kBAAkB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC5D,IAAI,EAAE;QACF;;;;;;;;;WASG;QACH,GAAG,EAAE,WAAW,CAAC;QACjB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,OAAO,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACL;AAED,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IACzD;;;;;;;;OAQG;IACH,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC9B;AA6BD;;;;GAIG;AACH,qBAAa,OAAO;IAChB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAa;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAc;IAEhD,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,cAAc,CAAiC;IACvD,OAAO,CAAC,eAAe,CAA8B;IACrD,OAAO,CAAC,SAAS,CAAU;IAE3B;;;;;OAKG;gBACS,cAAc,GAAE,MAAM,GAAG,IAAW,EAAE,SAAS,GAAE,OAAe;IAe5E;;;;;;OAMG;IACI,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAOnD;;;OAGG;IACI,UAAU,IAAI,MAAM,GAAG,IAAI;IAIlC;;;;OAIG;IACI,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAO1C;;;;OAIG;IACI,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIvC,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,iBAAiB;IAkBzB;;;;;;;;;;;;;;;;;;OAkBG;IACI,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS;IA+CvE;;;;;;;;;;;;;;;;;;;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,GAAG,UAAU,CAAA;KAAC,EAAE;IAa5H;;;;;OAKG;IACI,OAAO,IAAI,IAAI;CAOzB"}