1
1
mirror of https://github.com/kokonect-link/cherrypick synced 2024-12-17 08:09:14 +09:00
cherrypick/src/client/app/common/hotkey.ts

107 lines
2.5 KiB
TypeScript
Raw Normal View History

import keyCode from './keycode';
2018-09-18 12:34:41 +09:00
import { concat } from '../../../prelude/array';
2018-09-18 12:34:41 +09:00
type pattern = {
which: string[];
ctrl?: boolean;
shift?: boolean;
alt?: boolean;
};
type action = {
patterns: pattern[];
callback: Function;
};
const getKeyMap = keymap => Object.entries(keymap).map(([patterns, callback]): action => {
const result = {
patterns: [],
callback: callback
} as action;
result.patterns = patterns.split('|').map(part => {
const pattern = {
which: []
} as pattern;
part.trim().split('+').forEach(key => {
key = key.trim().toLowerCase();
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());
2018-09-18 09:11:52 +09:00
}
2018-09-18 12:34:41 +09:00
});
2018-09-18 12:34:41 +09:00
return pattern;
});
return result;
});
const ignoreElemens = ['input', 'textarea'];
export default {
install(Vue) {
Vue.directive('hotkey', {
bind(el, binding) {
el._hotkey_global = binding.modifiers.global === true;
2018-09-18 12:34:41 +09:00
const actions = getKeyMap(binding.value);
2018-09-18 12:42:56 +09:00
// flatten
const reservedKeys = concat(concat(actions.map(a => a.patterns.map(p => p.which))));
2018-09-18 12:34:41 +09:00
el.dataset.reservedKeys = reservedKeys.map(key => `'${key}'`).join(' ');
el._keyHandler = e => {
2018-09-18 12:34:41 +09:00
const key = e.code.toLowerCase();
const targetReservedKeys = document.activeElement ? ((document.activeElement as any).dataset || {}).reservedKeys || '' : '';
if (document.activeElement && ignoreElemens.some(el => document.activeElement.matches(el))) return;
2018-09-18 12:34:41 +09:00
for (const action of actions) {
if (el._hotkey_global && targetReservedKeys.includes(`'${key}'`)) break;
const matched = action.patterns.some(pattern => {
let matched = pattern.which.includes(key);
if (pattern.ctrl && !e.ctrlKey) matched = false;
if (pattern.shift && !e.shiftKey) matched = false;
if (pattern.alt && !e.altKey) matched = false;
2018-09-18 12:34:41 +09:00
if (matched) {
e.preventDefault();
e.stopPropagation();
action.callback(e);
return true;
} else {
return false;
}
});
2018-09-18 12:34:41 +09:00
if (matched) {
break;
}
}
};
if (el._hotkey_global) {
document.addEventListener('keydown', el._keyHandler);
} else {
el.addEventListener('keydown', el._keyHandler);
}
},
unbind(el) {
if (el._hotkey_global) {
document.removeEventListener('keydown', el._keyHandler);
} else {
el.removeEventListener('keydown', el._keyHandler);
}
}
});
}
};