mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2024-10-30 03:36:16 +00:00
aeb05336e9
This class registers shortcuts and processes key presses. It can also register multiple shortcuts and shortcuts with multiple keys pressed sequentially. Perhaps it should also support shortcuts with multiple keys pressed simultaneously for folks at https://klava.wiki ...
94 lines
2.7 KiB
JavaScript
94 lines
2.7 KiB
JavaScript
const $ = document.querySelector.bind(document);
|
|
const $$ = document.querySelectorAll.bind(document);
|
|
|
|
function keyEventToShortcut(event) {
|
|
let elideShift = event.key.toUpperCase() === event.key && event.shiftKey;
|
|
return (event.ctrlKey ? 'Ctrl+' : '') +
|
|
(event.altKey ? 'Alt+' : '') +
|
|
(event.metaKey ? 'Meta+' : '') +
|
|
(!elideShift && event.shiftKey ? 'Shift+' : '') +
|
|
event.key;
|
|
}
|
|
|
|
function isTextField(element) {
|
|
let name = element.nodeName.toLowerCase();
|
|
return name === 'textarea' ||
|
|
name === 'select' ||
|
|
(name === 'input' && !['submit', 'reset', 'checkbox', 'radio'].includes(element.type)) ||
|
|
element.isContentEditable;
|
|
}
|
|
|
|
class ShortcutHandler {
|
|
constructor(element, filter = () => {}) {
|
|
this.element = element;
|
|
this.map = {};
|
|
this.active = this.map;
|
|
this.filter = filter;
|
|
this.timeout = null;
|
|
|
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
|
this.resetActive = this.resetActive.bind(this);
|
|
this.addEventListeners();
|
|
}
|
|
|
|
addEventListeners() {
|
|
this.element.addEventListener('keydown', this.handleKeyDown);
|
|
}
|
|
|
|
add(text, action) {
|
|
let shortcuts = text.split(',').map(shortcut => shortcut.trim().split(' '));
|
|
|
|
for (let shortcut of shortcuts) {
|
|
let node = this.map;
|
|
for (let key of shortcut) {
|
|
if (!node[key]) {
|
|
node[key] = {};
|
|
}
|
|
node = node[key];
|
|
if (node.action) {
|
|
delete node.action;
|
|
}
|
|
}
|
|
|
|
node.action = action;
|
|
}
|
|
}
|
|
|
|
handleKeyDown(event) {
|
|
if (event.defaultPrevented) return;
|
|
if (['Control', 'Alt', 'Shift', 'Meta'].includes(event.key)) return;
|
|
if (!this.filter(event)) return;
|
|
|
|
let shortcut = keyEventToShortcut(event);
|
|
|
|
if (!this.active[shortcut]) {
|
|
this.resetActive();
|
|
return;
|
|
}
|
|
|
|
this.active = this.active[shortcut];
|
|
if (this.active.action) {
|
|
this.active.action(event);
|
|
this.resetActive();
|
|
return;
|
|
}
|
|
|
|
if (this.timeout) clearTimeout(this.timeout);
|
|
this.timeout = window.setTimeout(this.resetActive, 1500);
|
|
}
|
|
|
|
resetActive() {
|
|
this.active = this.map;
|
|
if (this.timeout) {
|
|
clearTimeout(this.timeout)
|
|
this.timeout = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
let notFormField = event => !(event.target instanceof Node && isTextField(event.target));
|
|
let globalShortcuts = new ShortcutHandler(document, notFormField);
|
|
|
|
globalShortcuts.add('p', () => alert('hello p'));
|
|
globalShortcuts.add('h', () => alert('hi h!'));
|
|
globalShortcuts.add('g h', () => alert('hi g h!!!')); |