import { DefaultMagicKeysAliasMap, tryOnScopeDispose, useEventListener } from '@vueuse/core' import { isEqual, isMatch } from 'lodash-es' import { reactive } from 'vue' import useLogger from './useLogger' type KeyFilter = string | string[] interface Entry { handler: () => unknown, prevent: boolean, __location?: string } const logger = useLogger() const combinations = reactive(new Map()) const current = new Set() useEventListener(window, 'keydown', (event) => { if (!event.key) return const target = event.target as HTMLElement if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') return current.add(event.key.toLowerCase()) const currentArray = [...current] for (const [requiredKeys, { handler, prevent }] of combinations.entries()) { if (isEqual(currentArray, requiredKeys)) { if (prevent) event.preventDefault() handler() } } }) useEventListener(window, 'keyup', (event) => { if (event.key) { current.delete(event.key.toLowerCase()) } }) export default (key: KeyFilter, handler: () => unknown, prevent = false) => { const combination = (Array.isArray(key) ? key : [key as string]).map(key => { return DefaultMagicKeysAliasMap[key] ?? key }) const entry: Entry = { prevent, handler } if (import.meta.env.DEV) { entry.__location = new Error().stack?.split('\n', 2).pop() // TODO: Get correct line number somehow? // Currently $3 is a line number that should work in .ts files, // though in .vue files we need to get the line of the script plus // the position of