All files / web/src/composables/shared useKeyboardShortcuts.ts

0% Statements 0/57
0% Branches 0/1
0% Functions 0/1
0% Lines 0/57

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79                                                                                                                                                             
import { onMounted, onUnmounted } from 'vue'
 
export interface ShortcutConfig {
  key: string
  ctrl?: boolean
  meta?: boolean
  shift?: boolean
  alt?: boolean
  handler: () => void
  preventDefault?: boolean
}
 
export function useKeyboardShortcuts(shortcuts: Record<string, () => void> | ShortcutConfig[]) {
  const normalizedShortcuts: ShortcutConfig[] = []
 
  if (Array.isArray(shortcuts)) {
    normalizedShortcuts.push(...shortcuts)
  } else {
    for (const [combo, handler] of Object.entries(shortcuts)) {
      const parts = combo.toLowerCase().split('+')
      const lastPart = parts[parts.length - 1]
      if (!lastPart) continue  // Skip invalid shortcuts
 
      const config: ShortcutConfig = {
        key: lastPart,
        handler,
        preventDefault: true,
      }
 
      for (const part of parts.slice(0, -1)) {
        if (part === 'ctrl') config.ctrl = true
        if (part === 'cmd' || part === 'meta') config.meta = true
        if (part === 'shift') config.shift = true
        if (part === 'alt') config.alt = true
      }
 
      normalizedShortcuts.push(config)
    }
  }
 
  const handleKeyDown = (event: KeyboardEvent) => {
    for (const shortcut of normalizedShortcuts) {
      const ctrlOrMeta = shortcut.ctrl || shortcut.meta
      const isCtrlPressed = event.ctrlKey || event.metaKey
      const matchesShift = shortcut.shift ? event.shiftKey : !event.shiftKey
      const matchesAlt = shortcut.alt ? event.altKey : !event.altKey
 
      if (
        shortcut.key === event.key.toLowerCase() &&
        (!ctrlOrMeta || isCtrlPressed === true) &&
        matchesShift &&
        matchesAlt
      ) {
        if (shortcut.preventDefault !== false) {
          event.preventDefault()
        }
        shortcut.handler()
        break
      }
    }
  }
 
  const register = () => {
    document.addEventListener('keydown', handleKeyDown)
  }
 
  const unregister = () => {
    document.removeEventListener('keydown', handleKeyDown)
  }
 
  // Auto register/unregister with component lifecycle
  onMounted(register)
  onUnmounted(unregister)
 
  return {
    register,
    unregister,
  }
}