rename: client -> frontend
This commit is contained in:
90
packages/frontend/src/scripts/hotkey.ts
Normal file
90
packages/frontend/src/scripts/hotkey.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import keyCode from './keycode';
|
||||
|
||||
type Callback = (ev: KeyboardEvent) => void;
|
||||
|
||||
type Keymap = Record<string, Callback>;
|
||||
|
||||
type Pattern = {
|
||||
which: string[];
|
||||
ctrl?: boolean;
|
||||
shift?: boolean;
|
||||
alt?: boolean;
|
||||
};
|
||||
|
||||
type Action = {
|
||||
patterns: Pattern[];
|
||||
callback: Callback;
|
||||
allowRepeat: boolean;
|
||||
};
|
||||
|
||||
const parseKeymap = (keymap: Keymap) => Object.entries(keymap).map(([patterns, callback]): Action => {
|
||||
const result = {
|
||||
patterns: [],
|
||||
callback,
|
||||
allowRepeat: true,
|
||||
} as Action;
|
||||
|
||||
if (patterns.match(/^\(.*\)$/) !== null) {
|
||||
result.allowRepeat = false;
|
||||
patterns = patterns.slice(1, -1);
|
||||
}
|
||||
|
||||
result.patterns = patterns.split('|').map(part => {
|
||||
const pattern = {
|
||||
which: [],
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
} as Pattern;
|
||||
|
||||
const keys = part.trim().split('+').map(x => x.trim().toLowerCase());
|
||||
for (const key of keys) {
|
||||
switch (key) {
|
||||
case 'ctrl': pattern.ctrl = true; break;
|
||||
case 'alt': pattern.alt = true; break;
|
||||
case 'shift': pattern.shift = true; break;
|
||||
default: pattern.which = keyCode(key).map(k => k.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
return pattern;
|
||||
});
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
const ignoreElemens = ['input', 'textarea'];
|
||||
|
||||
function match(ev: KeyboardEvent, patterns: Action['patterns']): boolean {
|
||||
const key = ev.code.toLowerCase();
|
||||
return patterns.some(pattern => pattern.which.includes(key) &&
|
||||
pattern.ctrl === ev.ctrlKey &&
|
||||
pattern.shift === ev.shiftKey &&
|
||||
pattern.alt === ev.altKey &&
|
||||
!ev.metaKey,
|
||||
);
|
||||
}
|
||||
|
||||
export const makeHotkey = (keymap: Keymap) => {
|
||||
const actions = parseKeymap(keymap);
|
||||
|
||||
return (ev: KeyboardEvent) => {
|
||||
if (document.activeElement) {
|
||||
if (ignoreElemens.some(el => document.activeElement!.matches(el))) return;
|
||||
if (document.activeElement.attributes['contenteditable']) return;
|
||||
}
|
||||
|
||||
for (const action of actions) {
|
||||
const matched = match(ev, action.patterns);
|
||||
|
||||
if (matched) {
|
||||
if (!action.allowRepeat && ev.repeat) return;
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
action.callback(ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
Reference in New Issue
Block a user