Files
bruitiste/src/hooks/useKeyboardShortcuts.ts
2025-10-06 14:31:05 +02:00

113 lines
2.4 KiB
TypeScript

import { useEffect, useRef } from 'react'
export interface KeyboardShortcutHandlers {
onSpace?: () => void
onArrowUp?: (shift: boolean) => void
onArrowDown?: (shift: boolean) => void
onArrowLeft?: (shift: boolean) => void
onArrowRight?: (shift: boolean) => void
onEnter?: () => void
onDoubleEnter?: () => void
onR?: () => void
onShiftR?: () => void
onC?: () => void
onShiftC?: () => void
onI?: () => void
onEscape?: () => void
}
const DOUBLE_ENTER_THRESHOLD = 300
export function useKeyboardShortcuts(handlers: KeyboardShortcutHandlers) {
const handlersRef = useRef(handlers)
useEffect(() => {
handlersRef.current = handlers
}, [handlers])
useEffect(() => {
let lastEnterTime = 0
const handleKeyDown = (e: KeyboardEvent) => {
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {
return
}
const h = handlersRef.current
switch (e.key) {
case ' ':
e.preventDefault()
h.onSpace?.()
break
case 'ArrowUp':
e.preventDefault()
h.onArrowUp?.(e.shiftKey)
break
case 'ArrowDown':
e.preventDefault()
h.onArrowDown?.(e.shiftKey)
break
case 'ArrowLeft':
e.preventDefault()
h.onArrowLeft?.(e.shiftKey)
break
case 'ArrowRight':
e.preventDefault()
h.onArrowRight?.(e.shiftKey)
break
case 'Enter': {
e.preventDefault()
const now = Date.now()
if (now - lastEnterTime < DOUBLE_ENTER_THRESHOLD) {
h.onDoubleEnter?.()
} else {
h.onEnter?.()
}
lastEnterTime = now
break
}
case 'r':
case 'R':
e.preventDefault()
if (e.shiftKey) {
h.onShiftR?.()
} else {
h.onR?.()
}
break
case 'c':
case 'C':
e.preventDefault()
if (e.shiftKey) {
h.onShiftC?.()
} else {
h.onC?.()
}
break
case 'i':
case 'I':
e.preventDefault()
h.onI?.()
break
case 'Escape':
e.preventDefault()
h.onEscape?.()
break
}
}
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [])
}