Global Shortcuts
Problem
You need document-level save, undo, and palette shortcuts. They must stop while a modal owns interaction without remounting listeners whenever modal state changes.
Solution
Use global when(event) to re-evaluate application policy for every keyboard event.
ts
import { createKeymap } from '@vielzeug/keymap';
let modalOpen = false;
const map = createKeymap(
[
{ id: 'palette', shortcut: 'ctrl+k', handler: () => openCommandPalette() },
{ id: 'save', shortcut: 'ctrl+s', handler: () => saveDocument() },
{ id: 'undo', shortcut: 'ctrl+z', handler: () => undo() },
{ id: 'redo', shortcut: 'ctrl+shift+z', handler: () => redo() },
{ id: 'sidebar', shortcut: 'ctrl+/', handler: () => toggleSidebar() },
],
{ when: () => !modalOpen },
);
const unmount = map.mount(document);
export function openModal() {
modalOpen = true;
showModal();
}
export function closeModal() {
modalOpen = false;
hideModal();
}
export function disposeShortcuts() {
unmount();
map.dispose();
}Preserve Native Text Editing
Inspect event.composedPath() when global undo and redo must yield to browser behavior inside editable fields. This matches CRM app shell policy.
ts
const isTypingInField = (event: KeyboardEvent): boolean =>
event.composedPath().some(
(target) =>
target instanceof HTMLElement &&
(target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target.isContentEditable),
);
const editingMap = createKeymap(
[
{ id: 'undo', shortcut: 'mod+z', handler: () => undo() },
{ id: 'redo', shortcut: 'mod+shift+z', handler: () => redo() },
],
{ when: (event) => !isTypingInField(event) },
);Pitfalls
when()runs for every event. Read current state inside callback instead of storing a stale boolean outside it.- A global guard blocks every binding owned by that map. Create another
createKeymap()owner with its own guard when some shortcuts remain active. - Call both the returned
unmount()callback andmap.dispose()during owner teardown.