113 lines
2.4 KiB
TypeScript
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)
|
|
}, [])
|
|
}
|