127 lines
3.6 KiB
TypeScript
127 lines
3.6 KiB
TypeScript
import { isMac } from '@common/utils'
|
|
|
|
const downKeys: Set<string> = new Set()
|
|
|
|
export declare type KeyActionType = LX.KeyDownEevent['type']
|
|
export declare type Keys = LX.KeyDownEevent['keys']
|
|
export declare type Key = LX.KeyDownEevent['key']
|
|
export declare type EventKey = LX.KeyDownEevent['eventKey']
|
|
export declare type Event = LX.KeyDownEevent['event']
|
|
|
|
const handleEvent = (type: KeyActionType, event: LX.KeyEvent, keys: Keys, isEditing: boolean) => {
|
|
let eventKey = event.key
|
|
if (isMac) {
|
|
let index = keys.indexOf('meta')
|
|
if (index > -1) keys.splice(index, 1, 'mod')
|
|
if (eventKey == 'Meta') eventKey = 'mod'
|
|
} else {
|
|
let index = keys.indexOf('ctrl')
|
|
if (index > -1) keys.splice(index, 1, 'mod')
|
|
if (eventKey == 'Control') eventKey = 'mod'
|
|
}
|
|
let key = keys.join('+')
|
|
|
|
switch (type) {
|
|
case 'down':
|
|
downKeys.add(key)
|
|
break
|
|
case 'up':
|
|
downKeys.delete(key)
|
|
break
|
|
}
|
|
handleSendEvent(key, eventKey, type, event, keys, isEditing)
|
|
}
|
|
|
|
// 修饰键处理
|
|
const eventModifiers = (event: LX.KeyEvent): string[] => {
|
|
let modifiers: string[] = []
|
|
if (event.ctrlKey) modifiers.push('ctrl')
|
|
if (event.shiftKey) modifiers.push('shift')
|
|
if (event.altKey) modifiers.push('alt')
|
|
if (event.metaKey) modifiers.push('meta')
|
|
|
|
return modifiers
|
|
}
|
|
|
|
// 是否忽略事件(表单元素等默认忽略)
|
|
const assertStopCallback = (element: HTMLElement) => {
|
|
// if the element has the class "keybind" then no need to stop
|
|
if (element.classList.contains('key-bind')) return false
|
|
|
|
// stop for input, select, and textarea
|
|
switch (element.tagName) {
|
|
case 'INPUT':
|
|
case 'SELECT':
|
|
case 'TEXTAREA':
|
|
return true
|
|
default:
|
|
return !!element.isContentEditable
|
|
}
|
|
}
|
|
|
|
const handleKeyDown = (event: LX.KeyEvent) => {
|
|
// if (assertStopCallback(event.target)) return
|
|
// event.preventDefault()
|
|
let keys = eventModifiers(event)
|
|
switch (event.key) {
|
|
case 'Control':
|
|
case 'Alt':
|
|
case 'Meta':
|
|
case 'Shift':
|
|
break
|
|
case ' ':
|
|
keys.push('space')
|
|
break
|
|
default:
|
|
keys.push((event.code.includes('Numpad') ? event.code.replace(/^Numpad(\w{1,3})\w*$/i, 'num$1') : event.key).toLowerCase())
|
|
break
|
|
}
|
|
handleEvent('down', event, keys, event.target ? assertStopCallback(event.target as HTMLElement) : false)
|
|
}
|
|
|
|
const handleKeyUp = (event: LX.KeyEvent) => {
|
|
// if (assertStopCallback(event.target)) return
|
|
event.preventDefault()
|
|
let keys = eventModifiers(event)
|
|
switch (event.key) {
|
|
case 'Control':
|
|
keys.push('ctrl')
|
|
break
|
|
case ' ':
|
|
keys.push('space')
|
|
break
|
|
default:
|
|
keys.push((event.code.includes('Numpad') ? event.code.replace(/^Numpad(\w{1,3})\w*$/i, 'num$1') : event.key).toLowerCase())
|
|
break
|
|
}
|
|
handleEvent('up', event, keys, event.target ? assertStopCallback(event.target as HTMLElement) : false)
|
|
}
|
|
|
|
type HandleSendEvent = (key: Key, eventKey: EventKey, type: KeyActionType, event: Event, keys: Keys, isEditing: boolean) => void
|
|
let handleSendEvent: HandleSendEvent
|
|
|
|
const bindKey = (handle: HandleSendEvent = () => {}) => {
|
|
handleSendEvent = handle
|
|
document.addEventListener('keydown', handleKeyDown)
|
|
document.addEventListener('keyup', handleKeyUp)
|
|
}
|
|
|
|
const unbindKey = () => {
|
|
document.removeEventListener('keydown', handleKeyDown)
|
|
document.removeEventListener('keyup', handleKeyUp)
|
|
}
|
|
|
|
const clearDownKeys = () => {
|
|
let keys = Array.from(downKeys)
|
|
for (let i = keys.length - 1; i > -1; i--) {
|
|
handleSendEvent(keys[i], keys[i], 'up', null, [keys[i]], false)
|
|
}
|
|
downKeys.clear()
|
|
}
|
|
|
|
export default {
|
|
bindKey,
|
|
unbindKey,
|
|
clearDownKeys,
|
|
}
|