Angular Example: InjectHotkey

ts
import { Component, ElementRef, signal, viewChild } from '@angular/core'
import {
  formatForDisplay,
  injectHotkey,
  type Hotkey,
} from '@tanstack/angular-hotkeys'

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [],
  templateUrl: './app.component.html',
})
export class AppComponent {
  protected readonly lastHotkey = signal<Hotkey | null>(null)
  protected readonly saveCount = signal(0)
  protected readonly incrementCount = signal(0)
  protected readonly enabled = signal(true)
  protected readonly activeTab = signal(1)
  protected readonly navigationCount = signal(0)
  protected readonly functionKeyCount = signal(0)
  protected readonly multiModifierCount = signal(0)
  protected readonly editingKeyCount = signal(0)

  protected readonly modalOpen = signal(false)
  protected readonly editorContent = signal('')
  protected readonly sidebarShortcutCount = signal(0)
  protected readonly modalShortcutCount = signal(0)
  protected readonly editorShortcutCount = signal(0)

  private readonly sidebarRef =
    viewChild<ElementRef<HTMLDivElement>>('sidebarRef')
  private readonly modalRef = viewChild<ElementRef<HTMLDivElement>>('modalRef')
  private readonly editorRef =
    viewChild<ElementRef<HTMLTextAreaElement>>('editorRef')

  constructor() {
    injectHotkey('Mod+S', (_event, { hotkey }) => {
      this.lastHotkey.set(hotkey)
      this.saveCount.update((c) => c + 1)
    })

    injectHotkey(
      'Mod+K',
      (_event, { hotkey }) => {
        this.lastHotkey.set(hotkey)
        this.incrementCount.update((c) => c + 1)
      },
      { requireReset: true },
    )

    injectHotkey(
      'Mod+E',
      (_event, { hotkey }) => {
        this.lastHotkey.set(hotkey)
        alert('This hotkey can be toggled!')
      },
      () => ({ enabled: this.enabled() }),
    )

    injectHotkey('Mod+1', () => {
      this.lastHotkey.set('Mod+1')
      this.activeTab.set(1)
    })
    injectHotkey('Mod+2', () => {
      this.lastHotkey.set('Mod+2')
      this.activeTab.set(2)
    })
    injectHotkey('Mod+3', () => {
      this.lastHotkey.set('Mod+3')
      this.activeTab.set(3)
    })
    injectHotkey('Mod+4', () => {
      this.lastHotkey.set('Mod+4')
      this.activeTab.set(4)
    })
    injectHotkey('Mod+5', () => {
      this.lastHotkey.set('Mod+5')
      this.activeTab.set(5)
    })

    injectHotkey('Shift+ArrowUp', () => {
      this.lastHotkey.set('Shift+ArrowUp')
      this.navigationCount.update((c) => c + 1)
    })
    injectHotkey('Shift+ArrowDown', () => {
      this.lastHotkey.set('Shift+ArrowDown')
      this.navigationCount.update((c) => c + 1)
    })
    injectHotkey('Alt+ArrowLeft', () => {
      this.lastHotkey.set('Alt+ArrowLeft')
      this.navigationCount.update((c) => c + 1)
    })
    injectHotkey('Alt+ArrowRight', () => {
      this.lastHotkey.set('Alt+ArrowRight')
      this.navigationCount.update((c) => c + 1)
    })
    injectHotkey('Mod+Home', () => {
      this.lastHotkey.set('Mod+Home')
      this.navigationCount.update((c) => c + 1)
    })
    injectHotkey('Mod+End', () => {
      this.lastHotkey.set('Mod+End')
      this.navigationCount.update((c) => c + 1)
    })
    injectHotkey('Control+PageUp', () => {
      this.lastHotkey.set('Control+PageUp')
      this.navigationCount.update((c) => c + 1)
    })
    injectHotkey('Control+PageDown', () => {
      this.lastHotkey.set('Control+PageDown')
      this.navigationCount.update((c) => c + 1)
    })

    injectHotkey('Meta+F4', () => {
      this.lastHotkey.set('Alt+F4')
      this.functionKeyCount.update((c) => c + 1)
      alert('Alt+F4 pressed (normally closes window)')
    })
    injectHotkey('Control+F5', () => {
      this.lastHotkey.set('Control+F5')
      this.functionKeyCount.update((c) => c + 1)
    })
    injectHotkey('Mod+F1', () => {
      this.lastHotkey.set('Mod+F1')
      this.functionKeyCount.update((c) => c + 1)
    })
    injectHotkey('Shift+F10', () => {
      this.lastHotkey.set('Shift+F10')
      this.functionKeyCount.update((c) => c + 1)
    })

    injectHotkey('Mod+Shift+S', () => {
      this.lastHotkey.set('Mod+Shift+S')
      this.multiModifierCount.update((c) => c + 1)
    })
    injectHotkey('Mod+Shift+Z', () => {
      this.lastHotkey.set('Mod+Shift+Z')
      this.multiModifierCount.update((c) => c + 1)
    })
    injectHotkey({ key: 'A', ctrl: true, alt: true }, () => {
      this.lastHotkey.set('Control+Alt+A')
      this.multiModifierCount.update((c) => c + 1)
    })
    injectHotkey('Control+Shift+N', () => {
      this.lastHotkey.set('Control+Shift+N')
      this.multiModifierCount.update((c) => c + 1)
    })
    injectHotkey('Mod+Alt+T', () => {
      this.lastHotkey.set('Mod+Alt+T')
      this.multiModifierCount.update((c) => c + 1)
    })
    injectHotkey('Control+Alt+Shift+X', () => {
      this.lastHotkey.set('Control+Alt+Shift+X')
      this.multiModifierCount.update((c) => c + 1)
    })

    injectHotkey('Mod+Enter', () => {
      this.lastHotkey.set('Mod+Enter')
      this.editingKeyCount.update((c) => c + 1)
    })
    injectHotkey('Shift+Enter', () => {
      this.lastHotkey.set('Shift+Enter')
      this.editingKeyCount.update((c) => c + 1)
    })
    injectHotkey('Mod+Backspace', () => {
      this.lastHotkey.set('Mod+Backspace')
      this.editingKeyCount.update((c) => c + 1)
    })
    injectHotkey('Mod+Delete', () => {
      this.lastHotkey.set('Mod+Delete')
      this.editingKeyCount.update((c) => c + 1)
    })
    injectHotkey('Control+Tab', () => {
      this.lastHotkey.set('Control+Tab')
      this.editingKeyCount.update((c) => c + 1)
    })
    injectHotkey('Shift+Tab', () => {
      this.lastHotkey.set('Shift+Tab')
      this.editingKeyCount.update((c) => c + 1)
    })
    injectHotkey('Mod+Space', () => {
      this.lastHotkey.set('Mod+Space')
      this.editingKeyCount.update((c) => c + 1)
    })

    injectHotkey({ key: 'Escape' }, () => {
      this.lastHotkey.set(null)
      this.saveCount.set(0)
      this.incrementCount.set(0)
      this.navigationCount.set(0)
      this.functionKeyCount.set(0)
      this.multiModifierCount.set(0)
      this.editingKeyCount.set(0)
      this.activeTab.set(1)
    })
    injectHotkey('F12', () => {
      this.lastHotkey.set('F12')
      this.functionKeyCount.update((c) => c + 1)
    })

    // Scoped: sidebar (Solid-style getter)
    injectHotkey(
      'Mod+B',
      () => {
        this.lastHotkey.set('Mod+B')
        this.sidebarShortcutCount.update((c) => c + 1)
        alert(
          'Sidebar shortcut triggered! This only works when the sidebar area is focused.',
        )
      },
      () => ({ target: this.sidebarRef()?.nativeElement ?? null }),
    )
    injectHotkey(
      'Mod+N',
      () => {
        this.lastHotkey.set('Mod+N')
        this.sidebarShortcutCount.update((c) => c + 1)
      },
      () => ({ target: this.sidebarRef()?.nativeElement ?? null }),
    )

    // Scoped: modal (Solid-style getter)
    injectHotkey(
      'Escape',
      () => {
        this.lastHotkey.set('Escape')
        this.modalShortcutCount.update((c) => c + 1)
        this.modalOpen.set(false)
      },
      () => ({
        target: this.modalRef()?.nativeElement ?? null,
        enabled: this.modalOpen(),
      }),
    )
    injectHotkey(
      'Mod+Enter',
      () => {
        this.lastHotkey.set('Mod+Enter')
        this.modalShortcutCount.update((c) => c + 1)
        alert('Modal submit shortcut!')
      },
      () => ({
        target: this.modalRef()?.nativeElement ?? null,
        enabled: this.modalOpen(),
      }),
    )

    // Scoped: editor (Solid-style getter)
    injectHotkey(
      'Mod+S',
      () => {
        this.lastHotkey.set('Mod+S')
        this.editorShortcutCount.update((c) => c + 1)
        const content = this.editorContent()
        alert(
          `Editor content saved: "${content.substring(0, 50)}${content.length > 50 ? '...' : ''}"`,
        )
      },
      () => ({ target: this.editorRef()?.nativeElement ?? null }),
    )
    injectHotkey(
      'Mod+/',
      () => {
        this.lastHotkey.set('Mod+/')
        this.editorShortcutCount.update((c) => c + 1)
        this.editorContent.update(
          (prev) => prev + '\n// Comment added via shortcut',
        )
      },
      () => ({ target: this.editorRef()?.nativeElement ?? null }),
    )
    injectHotkey(
      'Mod+K',
      () => {
        this.lastHotkey.set('Mod+K')
        this.editorShortcutCount.update((c) => c + 1)
        this.editorContent.set('')
      },
      () => ({ target: this.editorRef()?.nativeElement ?? null }),
    )
  }

  protected formatForDisplay = formatForDisplay

  protected openModal(): void {
    this.modalOpen.set(true)
  }

  protected closeModal(): void {
    this.modalOpen.set(false)
  }

  protected closeModalOverlay(): void {
    this.modalOpen.set(false)
  }

  protected onModalContentClick(event: Event): void {
    event.stopPropagation()
  }

  protected onEditorInput(event: Event): void {
    const el = event.target as HTMLTextAreaElement
    this.editorContent.set(el.value)
  }
}