/* * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ /** * {@link KeyboardEvent.code} の値を表す文字列。不足分は適宜追加する * @see https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values */ export type KeyCode = ( | 'Backspace' | 'Tab' | 'Enter' | 'Shift' | 'Control' | 'Alt' | 'Pause' | 'CapsLock' | 'Escape' | 'Space' | 'PageUp' | 'PageDown' | 'End' | 'Home' | 'ArrowLeft' | 'ArrowUp' | 'ArrowRight' | 'ArrowDown' | 'Insert' | 'Delete' | 'Digit0' | 'Digit1' | 'Digit2' | 'Digit3' | 'Digit4' | 'Digit5' | 'Digit6' | 'Digit7' | 'Digit8' | 'Digit9' | 'KeyA' | 'KeyB' | 'KeyC' | 'KeyD' | 'KeyE' | 'KeyF' | 'KeyG' | 'KeyH' | 'KeyI' | 'KeyJ' | 'KeyK' | 'KeyL' | 'KeyM' | 'KeyN' | 'KeyO' | 'KeyP' | 'KeyQ' | 'KeyR' | 'KeyS' | 'KeyT' | 'KeyU' | 'KeyV' | 'KeyW' | 'KeyX' | 'KeyY' | 'KeyZ' | 'MetaLeft' | 'MetaRight' | 'ContextMenu' | 'F1' | 'F2' | 'F3' | 'F4' | 'F5' | 'F6' | 'F7' | 'F8' | 'F9' | 'F10' | 'F11' | 'F12' | 'NumLock' | 'ScrollLock' | 'Semicolon' | 'Equal' | 'Comma' | 'Minus' | 'Period' | 'Slash' | 'Backquote' | 'BracketLeft' | 'Backslash' | 'BracketRight' | 'Quote' | 'Meta' | 'AltGraph' ); /** * 修飾キーを表す文字列。不足分は適宜追加する。 */ export type KeyModifier = ( | 'Shift' | 'Control' | 'Alt' | 'Meta' ); /** * 押下されたキー以外の状態を表す文字列。不足分は適宜追加する。 */ export type KeyState = ( | 'composing' | 'repeat' ); export type KeyEventHandler = { modifiers?: KeyModifier[]; states?: KeyState[]; code: KeyCode | 'any'; handler: (event: KeyboardEvent) => void; }; export function handleKeyEvent(event: KeyboardEvent, handlers: KeyEventHandler[]) { function checkModifier(ev: KeyboardEvent, modifiers? : KeyModifier[]) { if (modifiers) { return modifiers.every(modifier => ev.getModifierState(modifier)); } return true; } function checkState(ev: KeyboardEvent, states?: KeyState[]) { if (states) { return states.every(state => ev.getModifierState(state)); } return true; } let hit = false; for (const handler of handlers.filter(it => it.code === event.code)) { if (checkModifier(event, handler.modifiers) && checkState(event, handler.states)) { handler.handler(event); hit = true; break; } } if (!hit) { for (const handler of handlers.filter(it => it.code === 'any')) { handler.handler(event); } } }