app/launch/src/hooks/useKeyboardShortcuts.js (57 lines of code) (raw):
import { useCallback, useEffect, useRef } from 'react'
const IGNORE_TAGS = ['INPUT', 'TEXTAREA']
function shouldIgnore(target) {
return IGNORE_TAGS.includes(target.tagName)
}
const DISABLED_FN = () => {}
export default function useKeyboardShortcuts(
shortcutKeys = [],
callback = () => {},
disabled = false
) {
const activeKeys = useRef(new Set())
const watchableCallback = useRef()
useEffect(() => {
watchableCallback.current = disabled === true ? DISABLED_FN : callback
}, [callback, disabled])
const onKeyDown = useCallback(
(event) => {
const { repeat, keyCode, target } = event
if (repeat || shouldIgnore(target)) {
return
}
activeKeys.current.add(keyCode)
// If more keys are pressed than requested return
if (activeKeys.current.size !== shortcutKeys.length) {
return
}
const difference = new Set(
[...shortcutKeys].filter((x) => !activeKeys.current.has(x))
)
if (difference.size === 0) {
watchableCallback.current()
}
},
[shortcutKeys, activeKeys, watchableCallback]
)
const onKeyUp = useCallback(
(event) => {
const { keyCode, repeat, target } = event
if (repeat || shouldIgnore(target) || !activeKeys.current.has(keyCode)) {
return
}
activeKeys.current.delete(keyCode)
},
[activeKeys]
)
useEffect(() => {
activeKeys.current.clear()
}, [shortcutKeys, activeKeys])
useEffect(() => {
window.addEventListener('keydown', onKeyDown, true)
return () => window.removeEventListener('keydown', onKeyDown, true)
}, [onKeyDown, shortcutKeys])
useEffect(() => {
window.addEventListener('keyup', onKeyUp, true)
return () => window.removeEventListener('keyup', onKeyUp, true)
}, [onKeyUp, shortcutKeys])
}