import { Arr, Optional } from '@ephox/katamari'; interface KeyPatternBase { shiftKey?: boolean; altKey?: boolean; ctrlKey?: boolean; metaKey?: boolean; keyCode?: number; } export interface KeyPattern extends KeyPatternBase { action: () => boolean; } export interface KeyPatternDelayed extends KeyPatternBase { action: () => Optional<() => void>; } const baseKeyPattern = { shiftKey: false, altKey: false, ctrlKey: false, metaKey: false, keyCode: 0 }; const defaultPatterns = (patterns: KeyPattern[]): Required[] => Arr.map(patterns, (pattern) => ({ ...baseKeyPattern, ...pattern })); const defaultDelayedPatterns = (patterns: KeyPatternDelayed[]): Required[] => Arr.map(patterns, (pattern) => ({ ...baseKeyPattern, ...pattern })); const matchesEvent = (pattern: T, evt: KeyboardEvent) => ( evt.keyCode === pattern.keyCode && evt.shiftKey === pattern.shiftKey && evt.altKey === pattern.altKey && evt.ctrlKey === pattern.ctrlKey && evt.metaKey === pattern.metaKey ); const match = (patterns: KeyPattern[], evt: KeyboardEvent): Required[] => Arr.bind(defaultPatterns(patterns), (pattern) => matchesEvent(pattern, evt) ? [ pattern ] : [ ]); const matchDelayed = (patterns: KeyPatternDelayed[], evt: KeyboardEvent): Required[] => Arr.bind(defaultDelayedPatterns(patterns), (pattern) => matchesEvent(pattern, evt) ? [ pattern ] : [ ]); const action = any>(f: T, ...x: Parameters) => (): ReturnType => f.apply(null, x); const execute = (patterns: KeyPattern[], evt: KeyboardEvent): Optional> => Arr.find(match(patterns, evt), (pattern) => pattern.action()); const executeWithDelayedAction = (patterns: KeyPatternDelayed[], evt: KeyboardEvent): Optional<() => void> => Arr.findMap(matchDelayed(patterns, evt), (pattern) => pattern.action()); export { match, action, execute, executeWithDelayedAction };