Keyboard shortcut manager
The keyboard shortcut manager utilities in @genesislcap/foundation-utils let you define, group, and execute keyboard shortcuts in a consistent way across your application.
They provide:
- A
ShortcutDefinitiontype to describe individual shortcuts - A
ShortcutManagerinterface andDefaultShortcutManagerimplementation - Helper types such as
ShortcutExecutionStatusandShortcutRegistrationResult
Use these building blocks when you want a central place to register and manage keyboard shortcuts for pages, layouts, and components.
Core concepts
ShortcutDefinition
A shortcut is described with a ShortcutDefinition:
import type { ShortcutDefinition } from '@genesislcap/foundation-utils';
const saveShortcut: ShortcutDefinition = {
id: 'save',
key: 's',
ctrlKey: true,
description: 'Save current form',
context: 'trade-form',
action: () => {
// trigger save logic
},
// Optional
shiftKey: false,
priority: 10,
canExecute: () => ({ disabled: false }),
};
Key fields:
id: Unique identifier for the shortcut within its context.key: The key to listen for (e.g.'s','F2','Escape').- Modifier keys:
ctrlKey,altKey,optionKey,shiftKey,metaKey(all optional). context: Logical group such as a page, layout, or component ('positions-grid','trade-form', etc.).description: Human‑readable text for help/menus.action: Function executed when the shortcut fires.priority(optional): Used to resolve conflicts when multiple shortcuts match.elementRef(optional): An element the shortcut is associated with.canExecute(optional): Returns aShortcutExecutionStatus(disabled,tooltip) used to control availability.
ShortcutManager
The ShortcutManager interface provides the operations for managing shortcuts:
- Register / unregister
registerShortcuts(shortcuts: ShortcutDefinition[]): voidregisterShortcut(shortcut: ShortcutDefinition): ShortcutRegistrationResultunregisterShortcut(context: string, id: string): voidunregisterShortcutsByContext(context: string): void
- Execute
executeShortcut(id: string): void(in the active context)executeShortcutByContext(context: string, id: string): void
- Query
getShortcuts(): ShortcutDefinition[]getShortcutsByContext(context: string): ShortcutDefinition[]getContexts(): string[]getShortcutsByContextMap(): Map<string, Map<string, ShortcutDefinition>>
- Context management
registerContext(context: string): voidsetActiveContext(context: string): voidgetActiveContext(): string | undefinedclearActiveContext(): void
- Key matching
findShortcutByKeyCombination(key: string, ctrlKey?: boolean, altKey?: boolean, shiftKey?: boolean, metaKey?: boolean): ShortcutDefinition | undefined
- Pause / resume
pause(): voidresume(): voidisPaused: booleanisPaused$: Observable<boolean>isPausedSubject: BehaviorSubject<boolean>
The default implementation is provided by the DefaultShortcutManager class.
Basic usage
Creating a manager and registering shortcuts
import {
DefaultShortcutManager,
type ShortcutDefinition,
} from '@genesislcap/foundation-utils';
// In a shared place (e.g. app shell, layout, or a service)
const shortcutManager = new DefaultShortcutManager();
const shortcuts: ShortcutDefinition[] = [
{
id: 'positions-refresh',
key: 'r',
ctrlKey: true,
description: 'Refresh positions grid',
context: 'positions-grid',
action: () => {
// refresh logic here
},
},
{
id: 'positions-export',
key: 'e',
ctrlKey: true,
description: 'Export positions to CSV',
context: 'positions-grid',
action: () => {
// export logic here
},
},
];
// Register once when the screen/layout is initialised
shortcutManager.registerShortcuts(shortcuts);
// Set the active context when the screen becomes active
shortcutManager.setActiveContext('positions-grid');
Wiring into key events
ShortcutManager does not add DOM listeners itself. A common pattern is to listen for keydown and delegate to the manager:
window.addEventListener('keydown', (event) => {
if (shortcutManager.isPaused) {
return;
}
const shortcut = shortcutManager.findShortcutByKeyCombination(
event.key,
event.ctrlKey,
event.altKey,
event.shiftKey,
event.metaKey,
);
if (!shortcut) {
return;
}
const status = shortcut.canExecute?.() ?? {};
if (status.disabled) {
if (status.tooltip) {
// Optionally show tooltip / toast here
}
return;
}
event.preventDefault();
shortcut.action();
});
This approach lets you centralise key handling while still keeping shortcuts context‑aware.
Contexts and active context
Contexts allow you to scope shortcuts to particular screens or components:
shortcutManager.registerContext('trade-form');
shortcutManager.registerContext('positions-grid');
// When user navigates to trade form
shortcutManager.setActiveContext('trade-form');
// Later, when switching to positions grid
shortcutManager.setActiveContext('positions-grid');
// To clear any active context
shortcutManager.clearActiveContext();
executeShortcut(id) always uses the active context, while executeShortcutByContext(context, id) lets you target a specific context explicitly.
You can inspect what is registered:
const allContexts = shortcutManager.getContexts();
const tradeShortcuts = shortcutManager.getShortcutsByContext('trade-form');
This is useful for building help dialogs or “keyboard shortcuts” overlays.
Enabling / disabling shortcuts
Use canExecute and the ShortcutExecutionStatus type to control availability:
const saveShortcut: ShortcutDefinition = {
id: 'save',
key: 's',
ctrlKey: true,
description: 'Save current form',
context: 'trade-form',
action: () => {
// save logic
},
canExecute: () => {
const hasErrors = /* validate current form */;
return {
disabled: hasErrors,
tooltip: hasErrors ? 'Fix validation errors before saving' : undefined,
};
},
};
shortcutManager.registerShortcut(saveShortcut);
In your key handler, the canExecute function is evaluated before running action. You can also expose disabled and tooltip in your UI (for example, to show the same state in a “Shortcuts” menu).
Pausing shortcuts
Sometimes you want to temporarily disable global shortcuts (for example, when a modal or input field has focus).
// Pause all shortcuts
shortcutManager.pause();
// Resume again
shortcutManager.resume();
// Reactively monitor pause state
shortcutManager.isPaused$.subscribe((isPaused) => {
console.log('Shortcuts paused:', isPaused);
});
This is typically used in combination with focus/blur handlers on inputs or dialogs.
Summary
Use the shortcut manager utilities when you need:
- Central, testable definitions of keyboard shortcuts
- Context‑aware behaviour across different screens
- Consistent enable/disable logic and tooltips
- A single place to wire keyboard events to application actions
For the full type surface, refer to the TypeScript definitions in @genesislcap/foundation-utils (especially ShortcutDefinition, ShortcutManager, DefaultShortcutManager, and ShortcutRegistrationResult).