createKeyboardShortcuts

Declarative keyboard shortcut registration replacing switch statements.

Install

npx shadcn@latest add https://substrate.georgedrury.co.uk/r/keyboard-shortcuts

Usage

import {
  createKeyboardShortcuts,
  formatShortcut,
} from "@/lib/substrate/keyboard-shortcuts"

// Register shortcuts – returns a dispose function
const dispose = createKeyboardShortcuts([
  { key: "z", meta: true, handler: undo, label: "Undo", group: "Edit" },
  { key: "z", meta: true, shift: true, handler: redo, label: "Redo", group: "Edit" },
  { key: "Delete", handler: deleteSelected, label: "Delete", group: "Edit" },
  { key: "a", meta: true, handler: selectAll, label: "Select all", group: "Selection" },
  { key: "0", meta: true, handler: resetZoom, label: "Reset zoom", group: "View" },
])

// Later, when unmounting:
dispose()

Features

Each shortcut entry declares the key and modifiers it responds to. The first matching shortcut wins – so more specific shortcuts (e.g. ⌘+Shift+Z) should be listed before less specific ones (⌘+Z).

By default, shortcuts are ignored when focus is inside an <input>, <textarea>, or contentEditable element. Set ignoreInputs: false on a specific entry to override this.

The formatShortcut helper converts an entry into a human-readable string for display in toolbars and overlays – e.g. { key: "z", meta: true, shift: true } becomes "⌘+Shift+Z".

API

createKeyboardShortcuts(shortcuts, options?)

Returns a dispose function that removes the event listener.

ShortcutEntry

PropertyTypeDefaultDescription
keystring—The key value (e.g. "z", "Delete", "ArrowUp")
metabooleanfalseRequire ⌘/Ctrl
ctrlbooleanfalseRequire Ctrl specifically
shiftbooleanfalseRequire Shift
altbooleanfalseRequire Alt/Option
handler(e: KeyboardEvent) => void—Callback
preventDefaultbooleantrueCall e.preventDefault()
ignoreInputsbooleantrueSkip when focus is in an input
labelstring—Human-readable label for UI
groupstring—Group name for UI

formatShortcut(shortcut)

Returns a formatted string like "⌘+Shift+Z" or "Alt+↑".