ngx-keys 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,13 +5,15 @@ A lightweight, reactive Angular service for managing keyboard shortcuts with sig
5
5
  ## Features
6
6
 
7
7
  - **ðŸŽŊ Service-Focused**: Clean, focused API without unnecessary UI components
8
+ - **📝 Declarative Directive**: Optional directive for template-based shortcut registration
8
9
  - **⚡ Reactive Signals**: Track active/inactive shortcuts with Angular signals
9
10
  - **🔧 UI-Agnostic**: Build your own UI using the provided reactive signals
10
11
  - **🌍 Cross-Platform**: Automatic Mac/PC key display formatting
11
12
  - **🔄 Dynamic Management**: Add, remove, activate/deactivate shortcuts at runtime
12
13
  - **📁 Group Management**: Organize shortcuts into logical groups
13
- - **ïŋ― Smart Conflict Detection**: Register multiple shortcuts with same keys when not simultaneously active
14
- - **ïŋ―ðŸŠķ Lightweight**: Zero dependencies, minimal bundle impact
14
+ - **⚙ïļ Configurable**: Customize sequence timeout and other behavior via dependency injection
15
+ - **🔍 Smart Conflict Detection**: Register multiple shortcuts with same keys when not simultaneously active
16
+ - **ðŸŠķ Lightweight**: Zero dependencies, minimal bundle impact
15
17
 
16
18
  ## Installation
17
19
 
@@ -84,7 +86,110 @@ export class MyComponent {
84
86
  }
85
87
  ```
86
88
 
89
+ ### Using the Directive (Declarative Approach)
87
90
 
91
+ For a more declarative approach, use the `ngxKeys` directive directly on your elements:
92
+
93
+ ```typescript
94
+ import { Component } from '@angular/core';
95
+ import { KeyboardShortcutDirective } from 'ngx-keys';
96
+
97
+ @Component({
98
+ standalone: true,
99
+ imports: [KeyboardShortcutDirective],
100
+ template: `
101
+ <h3>My App</h3>
102
+ <p>Last action: {{ lastAction }}</p>
103
+
104
+ <!-- Directive automatically registers and unregisters shortcuts -->
105
+ <button
106
+ ngxKeys
107
+ keys="ctrl,s"
108
+ macKeys="cmd,s"
109
+ description="Save document"
110
+ (click)="save()">
111
+ Save
112
+ </button>
113
+
114
+ <button
115
+ ngxKeys
116
+ keys="ctrl,h"
117
+ macKeys="cmd,h"
118
+ description="Show help"
119
+ (click)="showHelp()">
120
+ Help
121
+ </button>
122
+
123
+ <!-- Multi-step shortcuts work too -->
124
+ <button
125
+ ngxKeys
126
+ [steps]="[['ctrl', 'k'], ['ctrl', 'p']]"
127
+ [macSteps]="[['cmd', 'k'], ['cmd', 'p']]"
128
+ description="Open command palette (Ctrl+K then Ctrl+P)"
129
+ (click)="openCommandPalette()">
130
+ Command Palette
131
+ </button>
132
+
133
+ <!-- Use custom action instead of click -->
134
+ <div
135
+ ngxKeys
136
+ keys="?"
137
+ description="Show help overlay"
138
+ [action]="showHelpOverlay">
139
+ Press ? for help
140
+ </div>
141
+ `
142
+ })
143
+ export class MyComponent {
144
+ protected lastAction = 'Try pressing Ctrl+S or Ctrl+H';
145
+
146
+ protected readonly showHelpOverlay = () => {
147
+ this.lastAction = 'Help overlay displayed!';
148
+ };
149
+
150
+ protected save() {
151
+ this.lastAction = 'Document saved!';
152
+ }
153
+
154
+ protected showHelp() {
155
+ this.lastAction = 'Help displayed!';
156
+ }
157
+
158
+ protected openCommandPalette() {
159
+ this.lastAction = 'Command palette opened!';
160
+ }
161
+ }
162
+ ```
163
+
164
+ > [!TIP]
165
+ > The directive automatically:
166
+ > - Registers shortcuts when initialized
167
+ > - Triggers click events on the host element (or executes a custom action)
168
+ > - Unregisters shortcuts when destroyed
169
+ > - Adds a `data-keyboard-shortcut` attribute for styling/testing
170
+
171
+ **Directive Inputs:**
172
+
173
+ | Input | Type | Description |
174
+ |-------|------|-------------|
175
+ | `keys` | `string` | Comma-separated keys for single-step shortcut (e.g., `"ctrl,s"`) |
176
+ | `macKeys` | `string` | Comma-separated keys for Mac (e.g., `"cmd,s"`) |
177
+ | `steps` | `string[][]` | Multi-step sequence (e.g., `[['ctrl', 'k'], ['ctrl', 'd']]`) |
178
+ | `macSteps` | `string[][]` | Multi-step sequence for Mac |
179
+ | `description` | `string` | Required. Description of what the shortcut does |
180
+ | `action` | `() => void` | Optional custom action. If not provided, triggers click on host element |
181
+ | `shortcutId` | `string` | Optional custom ID. If not provided, generates unique ID |
182
+
183
+ **Directive Outputs:**
184
+
185
+ | Output | Type | Description |
186
+ |--------|------|-------------|
187
+ | `triggered` | `void` | Emitted when the keyboard shortcut is triggered |
188
+
189
+ **When to use the directive vs. the service:**
190
+
191
+ - **Use the directive** when shortcuts are tied to specific UI elements and their lifecycle
192
+ - **Use the service** when you need programmatic control, dynamic shortcuts, or shortcuts not tied to elements
88
193
 
89
194
  ## Explore the Demo
90
195
 
@@ -95,6 +200,7 @@ Want to see ngx-keys in action? Check out our comprehensive [demo application](/
95
200
  | [**App Component**](/projects/demo/src/app/app.ts) | Global shortcuts | Single & group registration, cleanup patterns |
96
201
  | [**Home Component**](/projects/demo/src/app/home/home.component.ts) | Reactive UI | Real-time status display, toggle controls |
97
202
  | [**Feature Component**](/projects/demo/src/app/feature/feature.component.ts) | Route-specific shortcuts | Scoped shortcuts, lifecycle management |
203
+ | [**Directive Demo**](/projects/demo/src/app/directive-demo/directive-demo.component.ts) | Declarative shortcuts | Directive usage, automatic lifecycle management |
98
204
  | [**Customize Component**](/projects/demo/src/app/customize/customize.component.ts) | Dynamic shortcut recording | Real-time key capture, shortcut customization |
99
205
 
100
206
  **Run the demo:**
@@ -177,23 +283,53 @@ this.keyboardService.activateGroup('editor'); // Re-enable all editor shortcut
177
283
 
178
284
  In addition to single-step shortcuts using `keys` / `macKeys`, ngx-keys supports ordered multi-step sequences using `steps` and `macSteps` on the `KeyboardShortcut` object. Each element in `steps` is itself an array of key tokens that must be pressed together for that step.
179
285
 
180
- Example: register a sequence that requires `Ctrl+K` followed by `S`:
286
+ Example: register a sequence that requires `Ctrl+K` followed by `Ctrl+D`:
181
287
 
182
288
  ```typescript
183
289
  this.keyboardService.register({
184
- id: 'open-settings-seq',
185
- steps: [['ctrl', 'k'], ['s']],
186
- macSteps: [['meta', 'k'], ['s']],
187
- action: () => this.openSettings(),
188
- description: 'Open settings (Ctrl+K then S)'
290
+ id: 'format-document-seq',
291
+ steps: [['ctrl', 'k'], ['ctrl', 'd']],
292
+ macSteps: [['meta', 'k'], ['meta', 'd']],
293
+ action: () => this.formatDocument(),
294
+ description: 'Format document (Ctrl+K then Ctrl+D)'
189
295
  });
190
296
  ```
191
297
 
298
+ **Configuring Sequence Timeout**
299
+
300
+ By default, multi-step shortcuts have **no timeout** - users can take as long as they need between steps. You can configure a timeout globally using a provider function:
301
+
302
+ ```typescript
303
+ import { ApplicationConfig } from '@angular/core';
304
+ import { provideKeyboardShortcutsConfig } from 'ngx-keys';
305
+
306
+ export const appConfig: ApplicationConfig = {
307
+ providers: [
308
+ provideKeyboardShortcutsConfig({ sequenceTimeoutMs: 2000 }) // 2 seconds
309
+ ]
310
+ };
311
+ ```
312
+
313
+ Alternatively, you can use the injection token directly:
314
+
315
+ ```typescript
316
+ import { ApplicationConfig } from '@angular/core';
317
+ import { KEYBOARD_SHORTCUTS_CONFIG } from 'ngx-keys';
318
+
319
+ export const appConfig: ApplicationConfig = {
320
+ providers: [
321
+ {
322
+ provide: KEYBOARD_SHORTCUTS_CONFIG,
323
+ useValue: { sequenceTimeoutMs: 2000 } // 2 seconds
324
+ }
325
+ ]
326
+ };
327
+ ```
328
+
192
329
  **Important behavior notes**
193
330
 
194
- - Default sequence timeout: the service requires the next step to be entered within 2000ms (2 seconds) of the previous step; otherwise the pending sequence is cleared. This timeout is intentionally conservative and can be changed in future releases or exposed per-shortcut if needed.
195
- - Steps are order-sensitive. `steps: [['ctrl','k'], ['s']]` is different from `steps: [['s'], ['ctrl','k']]`.
196
- - Existing single-step `keys` / `macKeys` remain supported and continue to work as before.
331
+ - **Sequence timeout**: Steps must be entered within the configured timeout (default 2000ms) or the sequence is cleared.
332
+ - **Order-sensitive**: Steps are order-sensitive. `steps: [['ctrl','k'], ['s']]` is different from `steps: [['s'], ['ctrl','k']]`.
197
333
 
198
334
 
199
335
  Use the `activate()` and `deactivate()` methods for dynamic control after registration:
@@ -208,6 +344,52 @@ this.keyboardService.activate('save');
208
344
 
209
345
  ## Advanced Usage
210
346
 
347
+ ### Type-Safe Action Functions
348
+
349
+ For better type safety and code reusability, use the exported `Action` type when defining action functions:
350
+
351
+ ```typescript
352
+ import { Component, inject } from '@angular/core';
353
+ import { KeyboardShortcuts, Action } from 'ngx-keys';
354
+
355
+ export class MyComponent {
356
+ private readonly keyboardService = inject(KeyboardShortcuts);
357
+
358
+ // Define reusable, type-safe action functions
359
+ private readonly saveAction: Action = () => {
360
+ console.log('Saving document...');
361
+ this.performSave();
362
+ };
363
+
364
+ private readonly undoAction: Action = () => {
365
+ console.log('Undoing...');
366
+ this.performUndo();
367
+ };
368
+
369
+ constructor() {
370
+ // Use the typed actions in shortcuts
371
+ this.keyboardService.register({
372
+ id: 'save',
373
+ keys: ['ctrl', 's'],
374
+ macKeys: ['meta', 's'],
375
+ action: this.saveAction,
376
+ description: 'Save document'
377
+ });
378
+
379
+ this.keyboardService.register({
380
+ id: 'undo',
381
+ keys: ['ctrl', 'z'],
382
+ macKeys: ['meta', 'z'],
383
+ action: this.undoAction,
384
+ description: 'Undo last action'
385
+ });
386
+ }
387
+
388
+ private performSave() { /* implementation */ }
389
+ private performUndo() { /* implementation */ }
390
+ }
391
+ ```
392
+
211
393
  ### Context-Specific Shortcuts
212
394
 
213
395
  Register different actions for the same keys in different UI contexts:
@@ -367,15 +549,38 @@ Conflicts are only checked among **active** shortcuts, not all registered shortc
367
549
 
368
550
  - `register(shortcut: KeyboardShortcut)` - Register and automatically activate a single shortcut *Throws error on conflicts with active shortcuts only*
369
551
  - `registerGroup(groupId: string, shortcuts: KeyboardShortcut[])` - Register and automatically activate a group of shortcuts *Throws error on conflicts with active shortcuts only*
552
+ - `registerMany(shortcuts: KeyboardShortcut[])` - Register multiple shortcuts in a single batch update
553
+
554
+ **Unregistration Methods:**
555
+ > [!NOTE]
556
+ > `unregister()` automatically removes shortcuts from all groups they belong to.
557
+
558
+ - `unregister(shortcutId: string)` - Remove a shortcut and its group associations *Throws error if not found*
559
+ - `unregisterGroup(groupId: string)` - Remove a group and all its shortcuts *Throws error if not found*
560
+ - `unregisterMany(ids: string[])` - Unregister multiple shortcuts in a single batch update
561
+ - `unregisterGroups(ids: string[])` - Unregister multiple groups in a single batch update
562
+ - `clearAll()` - Remove all shortcuts, groups, and filters (nuclear reset)
370
563
 
371
- **Management Methods:**
372
- - `unregister(shortcutId: string)` - Remove a shortcut *Throws error if not found*
373
- - `unregisterGroup(groupId: string)` - Remove a group *Throws error if not found*
564
+ **Activation Methods:**
374
565
  - `activate(shortcutId: string)` - Activate a shortcut *Throws error if not registered or would create conflicts*
375
566
  - `deactivate(shortcutId: string)` - Deactivate a shortcut *Throws error if not registered*
376
567
  - `activateGroup(groupId: string)` - Activate all shortcuts in a group *Throws error if not found or would create conflicts*
377
568
  - `deactivateGroup(groupId: string)` - Deactivate all shortcuts in a group *Throws error if not found*
378
569
 
570
+ **Filter Methods:**
571
+ - `addFilter(name: string, filter: Function)` - Add a named global filter
572
+ - `removeFilter(name: string)` - Remove a named global filter
573
+ - `clearFilters()` - Remove all global filters
574
+ - `hasFilter(name: string): boolean` - Check if a filter exists
575
+ - `getFilter(name: string)` - Get a filter function by name
576
+ - `getFilterNames(): string[]` - Get all filter names
577
+ - `removeGroupFilter(groupId: string)` - Remove filter from a group
578
+ - `removeShortcutFilter(shortcutId: string)` - Remove filter from a shortcut
579
+ - `clearAllGroupFilters()` - Remove all group filters
580
+ - `clearAllShortcutFilters()` - Remove all shortcut filters
581
+ - `hasGroupFilter(groupId: string): boolean` - Check if group has a filter
582
+ - `hasShortcutFilter(shortcutId: string): boolean` - Check if shortcut has a filter
583
+
379
584
  **Query Methods:**
380
585
  - `isActive(shortcutId: string): boolean` - Check if a shortcut is active
381
586
  - `isRegistered(shortcutId: string): boolean` - Check if a shortcut is registered
@@ -383,6 +588,7 @@ Conflicts are only checked among **active** shortcuts, not all registered shortc
383
588
  - `isGroupRegistered(groupId: string): boolean` - Check if a group is registered
384
589
  - `getShortcuts(): ReadonlyMap<string, KeyboardShortcut>` - Get all registered shortcuts
385
590
  - `getGroups(): ReadonlyMap<string, KeyboardShortcutGroup>` - Get all registered groups
591
+ - `getGroupShortcuts(groupId: string): KeyboardShortcut[]` - Get all shortcuts in a specific group
386
592
 
387
593
  **Utility Methods:**
388
594
  - `formatShortcutForUI(shortcut: KeyboardShortcut): KeyboardShortcutUI` - Format a shortcut for display
@@ -425,6 +631,9 @@ interface KeyboardShortcutUI {
425
631
  ### KeyboardShortcut Interface
426
632
 
427
633
  ```typescript
634
+ // Type for shortcut action functions
635
+ type Action = () => void;
636
+
428
637
  interface KeyboardShortcut {
429
638
  id: string; // Unique identifier
430
639
  // Single-step combinations (existing API)
@@ -435,7 +644,7 @@ interface KeyboardShortcut {
435
644
  // Each step is an array of keys pressed together. Example: steps: [['ctrl','k'], ['s']]
436
645
  steps?: string[][];
437
646
  macSteps?: string[][];
438
- action: () => void; // Function to execute
647
+ action: Action; // Function to execute
439
648
  description: string; // Human-readable description
440
649
  }
441
650
  ```
@@ -450,6 +659,108 @@ interface KeyboardShortcutGroup {
450
659
  }
451
660
  ```
452
661
 
662
+ ### KeyboardShortcutDirective
663
+
664
+ A declarative directive for registering keyboard shortcuts directly in templates.
665
+
666
+ #### Selector
667
+ ```typescript
668
+ [ngxKeys]
669
+ ```
670
+
671
+ #### Inputs
672
+
673
+ | Input | Type | Required | Description |
674
+ |-------|------|----------|-------------|
675
+ | `keys` | `string` | No* | Comma-separated keys for single-step shortcut (e.g., `"ctrl,s"`) |
676
+ | `macKeys` | `string` | No | Comma-separated keys for Mac (e.g., `"cmd,s"`) |
677
+ | `steps` | `string[][]` | No* | Multi-step sequence. Each inner array is one step (e.g., `[['ctrl', 'k'], ['ctrl', 'd']]`) |
678
+ | `macSteps` | `string[][]` | No | Multi-step sequence for Mac |
679
+ | `description` | `string` | **Yes** | Human-readable description of what the shortcut does |
680
+ | `action` | `() => void` | No | Custom action to execute. If not provided, triggers click on host element |
681
+ | `shortcutId` | `string` | No | Custom ID for the shortcut. If not provided, generates a unique ID |
682
+
683
+ \* Either `keys`/`macKeys` OR `steps`/`macSteps` must be provided (not both)
684
+
685
+ #### Outputs
686
+
687
+ | Output | Type | Description |
688
+ |--------|------|-------------|
689
+ | `triggered` | `void` | Emitted when the keyboard shortcut is triggered (in addition to the action) |
690
+
691
+ #### Host Bindings
692
+
693
+ The directive adds the following to the host element:
694
+ - `data-keyboard-shortcut` attribute with the shortcut ID (useful for styling or testing)
695
+
696
+ #### Behavior
697
+
698
+ - **Registration**: Automatically registers the shortcut when the directive initializes
699
+ - **Default Action**: Triggers a click event on the host element (unless custom `action` is provided)
700
+ - **Cleanup**: Automatically unregisters the shortcut when the directive is destroyed
701
+ - **Error Handling**: Throws errors for invalid input combinations or registration failures
702
+
703
+ #### Usage Examples
704
+
705
+ **Basic button with click trigger:**
706
+ ```html
707
+ <button
708
+ ngxKeys
709
+ keys="ctrl,s"
710
+ macKeys="cmd,s"
711
+ description="Save document"
712
+ (click)="save()">
713
+ Save
714
+ </button>
715
+ ```
716
+
717
+ **Multi-step shortcut:**
718
+ ```html
719
+ <button
720
+ ngxKeys
721
+ [steps]="[['ctrl', 'k'], ['ctrl', 'd']]"
722
+ [macSteps]="[['cmd', 'k'], ['cmd', 'd']]"
723
+ description="Format document (Ctrl+K then Ctrl+D)"
724
+ (click)="format()">
725
+ Format
726
+ </button>
727
+ ```
728
+
729
+ **Custom action on non-interactive element:**
730
+ ```html
731
+ <div
732
+ ngxKeys
733
+ keys="?"
734
+ description="Show help"
735
+ [action]="showHelp">
736
+ Press ? for help
737
+ </div>
738
+ ```
739
+
740
+ **Listen to triggered event:**
741
+ ```html
742
+ <button
743
+ ngxKeys
744
+ keys="ctrl,s"
745
+ description="Save document"
746
+ (triggered)="onShortcutTriggered()"
747
+ (click)="save()">
748
+ Save
749
+ </button>
750
+ ```
751
+
752
+ **Custom shortcut ID:**
753
+ ```html
754
+ <button
755
+ ngxKeys
756
+ keys="ctrl,s"
757
+ description="Save document"
758
+ shortcutId="my-custom-save-shortcut"
759
+ (click)="save()">
760
+ Save
761
+ </button>
762
+ ```
763
+
453
764
  ## Key Mapping Reference
454
765
 
455
766
  ### Modifier Keys
@@ -686,6 +997,130 @@ export class BatchUpdateComponent {
686
997
  }
687
998
  ```
688
999
 
1000
+ ### Bulk Registration and Unregistration
1001
+
1002
+ Register or unregister multiple shortcuts efficiently:
1003
+
1004
+ ```typescript
1005
+ import { Component, inject } from '@angular/core';
1006
+ import { KeyboardShortcuts } from 'ngx-keys';
1007
+
1008
+ export class MyComponent {
1009
+ private readonly keyboardService = inject(KeyboardShortcuts);
1010
+
1011
+ setupToolbarShortcuts() {
1012
+ // Register multiple shortcuts at once
1013
+ this.keyboardService.registerMany([
1014
+ {
1015
+ id: 'save',
1016
+ keys: ['ctrl', 's'],
1017
+ macKeys: ['meta', 's'],
1018
+ action: () => this.save(),
1019
+ description: 'Save document'
1020
+ },
1021
+ {
1022
+ id: 'save-as',
1023
+ keys: ['ctrl', 'shift', 's'],
1024
+ macKeys: ['meta', 'shift', 's'],
1025
+ action: () => this.saveAs(),
1026
+ description: 'Save as...'
1027
+ },
1028
+ {
1029
+ id: 'print',
1030
+ keys: ['ctrl', 'p'],
1031
+ macKeys: ['meta', 'p'],
1032
+ action: () => this.print(),
1033
+ description: 'Print document'
1034
+ }
1035
+ ]);
1036
+ }
1037
+
1038
+ cleanup() {
1039
+ // Unregister multiple shortcuts at once
1040
+ this.keyboardService.unregisterMany(['save', 'save-as', 'print']);
1041
+
1042
+ // Or unregister entire groups
1043
+ this.keyboardService.unregisterGroups(['toolbar', 'menu']);
1044
+
1045
+ // Or clear everything
1046
+ this.keyboardService.clearAll();
1047
+ }
1048
+
1049
+ private save() { /* implementation */ }
1050
+ private saveAs() { /* implementation */ }
1051
+ private print() { /* implementation */ }
1052
+ }
1053
+ ```
1054
+
1055
+ ### Managing Group Shortcuts
1056
+
1057
+ Remove individual shortcuts from groups and query group contents:
1058
+
1059
+ ```typescript
1060
+ import { Component, inject } from '@angular/core';
1061
+ import { KeyboardShortcuts } from 'ngx-keys';
1062
+
1063
+ export class MyComponent {
1064
+ private readonly keyboardService = inject(KeyboardShortcuts);
1065
+
1066
+ manageEditorGroup() {
1067
+ // Check if a shortcut is registered
1068
+ if (this.keyboardService.isRegistered('bold')) {
1069
+ // Remove a shortcut - automatically removes it from all groups
1070
+ this.keyboardService.unregister('bold');
1071
+ }
1072
+
1073
+ // Remove multiple shortcuts (automatically removes from groups)
1074
+ this.keyboardService.unregisterMany(['bold', 'italic', 'underline']);
1075
+
1076
+ // Get all shortcuts in a group
1077
+ const editorShortcuts = this.keyboardService.getGroupShortcuts('editor');
1078
+ console.log('Remaining editor shortcuts:', editorShortcuts);
1079
+
1080
+ // Or remove the entire group and all its shortcuts
1081
+ this.keyboardService.unregisterGroup('editor');
1082
+ }
1083
+ }
1084
+ ```
1085
+
1086
+ ### Filter Management
1087
+
1088
+ Remove filters when no longer needed:
1089
+
1090
+ ```typescript
1091
+ import { Component, inject } from '@angular/core';
1092
+ import { KeyboardShortcuts } from 'ngx-keys';
1093
+
1094
+ export class MyComponent {
1095
+ private readonly keyboardService = inject(KeyboardShortcuts);
1096
+
1097
+ setupModalFilters() {
1098
+ // Add filters for modal context
1099
+ this.keyboardService.addGroupFilter('navigation', () => this.isModalOpen);
1100
+ this.keyboardService.addShortcutFilter('global-search', () => this.isModalOpen);
1101
+ }
1102
+
1103
+ cleanupModalFilters() {
1104
+ // Check if filters exist
1105
+ if (this.keyboardService.hasGroupFilter('navigation')) {
1106
+ // Remove specific group filter
1107
+ this.keyboardService.removeGroupFilter('navigation');
1108
+ }
1109
+
1110
+ if (this.keyboardService.hasShortcutFilter('global-search')) {
1111
+ // Remove specific shortcut filter
1112
+ this.keyboardService.removeShortcutFilter('global-search');
1113
+ }
1114
+
1115
+ // Or clear all filters at once
1116
+ this.keyboardService.clearAllGroupFilters();
1117
+ this.keyboardService.clearAllShortcutFilters();
1118
+ }
1119
+
1120
+ private isModalOpen = false;
1121
+ }
1122
+ ```
1123
+
689
1124
  ### Checking Status
690
1125
 
691
1126
  > [!NOTE]