Skip to content

Instantly share code, notes, and snippets.

@abuduba
Created August 14, 2019 13:02
Show Gist options
  • Save abuduba/caa38fc41332582a3682686d602c8e2c to your computer and use it in GitHub Desktop.
Save abuduba/caa38fc41332582a3682686d602c8e2c to your computer and use it in GitHub Desktop.
'use strict';
const isEqual = (buff, hotkey) => {
const buffKeys = Object.keys(buff);
if (buffKeys.length !== Object.keys(hotkey).length) {
return false;
}
return buffKeys.every(
(k) => hotkey.hasOwnProperty(k) && buff[k] === hotkey[k]
);
}
export const matchHotkey = (buffer, hotkey) => {
if (buffer.length < hotkey.length) {
return false
}
const indexDiff = buffer.length - hotkey.length;
for(let i = hotkey.length - 1; i >= 0; i--) {
if(!isEqual(buffer[indexDiff + i], hotkey[i])) {
return false;
}
}
return true;
}
const allModifiers = ['ctrl', 'shift', 'alt', 'meta'];
const isHotkeyValid = (hotkey) => {
const allKeys = Object.keys(hotkey);
if (allKeys.length === 0) {
return;
}
const modifiers = allKeys.filter(
(k) => allModifiers.includes(k)
);
return allKeys.length - modifiers.length <= 1;
}
export const normalizeHotkey = (hotkey) =>
hotkey.split(' ').map(
(part) => {
const res = part.split('+').reduce(
(obj, key) => ({ ...obj, [key]: true }),
{},
);
if (!isHotkeyValid(res)) {
throw new Error(`Invalid hotkey combination: "${hotkey}"`)
}
return res;
}
);
const isArrayEqual = (a, b) =>
a.length === b.length && a.every((v, i) => isEqual(v, b[i]));
export const createHotkeyContext = ({ debounceTime = 300 } = {}) => {
let buffer = [];
const clearBuffer = () => { buffer = []; };
let bufferClearTimeout = null;
const listeners = [];
const keyDownListener = (event) => {
clearTimeout(bufferClearTimeout);
bufferClearTimeout = setTimeout(clearBuffer, debounceTime);
const description = {
[event.key.toLowerCase()]: true,
};
allModifiers.forEach((m) => {
if (event[`${m}Key`]) {
description[m] = true;
}
});
buffer.push(description);
listeners.forEach((listener) => {
if (matchHotkey(buffer, listener.hotkey)) {
listener.callback(event);
}
});
};
const register = (hotkey, callback) => {
if (typeof hotkey !== 'string' ) {
throw new Error(
`The hotkey must be a string; given ${typeof hotkey}`
);
}
if (typeof callback !== 'function' ) {
throw new Error(
`The callback must be a function; given ${typeof callback}`
);
}
listeners.push({ hotkey: normalizeHotkey(hotkey), callback });
}
const unregister = (hotkeyString, callback) => {
const normalized = normalizeHotkey(hotkeyString);
const index = listeners.findIndex(
(l) => l.callback === callback
&& isArrayEqual(normalized, l.hotkey)
);
if (index !== -1) {
listeners.splice(index, 1);
}
}
document.addEventListener('keydown', keyDownListener);
return { register, unregister }
};
let modifier = 0;
let keys = 0;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment