shortcutter
Version:
Tiny, dependency-free library to manage keyboard shortcuts in your application.
83 lines (82 loc) • 3.25 kB
JavaScript
import { useContexts } from './contexts';
import { useController } from './controller';
import { normalizeKeyCode } from './helpers';
const DEFAULT_CONFIG = {
eventTarget: window,
defaultContext: 'default',
};
export function useShortcutter(customConfig = {}) {
const CONFIG = {
...DEFAULT_CONFIG,
...customConfig,
};
const { eventTarget, defaultContext } = CONFIG;
const contexts = useContexts();
const keysRecorder = useController();
contexts.add(defaultContext);
contexts.setActive(defaultContext);
eventTarget.addEventListener('keydown', onkeydown);
eventTarget.addEventListener('keyup', onkeyup);
eventTarget.addEventListener('blur', onblur);
eventTarget.addEventListener('unload', onunload);
const invokeClbck = (keys, event, phase) => {
contexts.getActive().forEach(context => {
const ctx = contexts.get(context);
if (ctx?.has(keys, phase)) {
ctx.get(keys, phase)(event, phase);
}
});
};
function onkeydown(event) {
const previousCombination = keysRecorder.getPressed().join('+');
keysRecorder.press(normalizeKeyCode(event.code));
const nextCombination = keysRecorder.getPressed().join('+');
if (previousCombination !== nextCombination) {
if (previousCombination.length) {
invokeClbck(previousCombination.split('+'), event, "up");
}
invokeClbck(nextCombination.split('+'), event, "down");
}
else {
invokeClbck(previousCombination.split('+'), event, "press");
}
}
function onkeyup(event) {
const previousCombination = keysRecorder.getPressed().join('+');
keysRecorder.release(normalizeKeyCode(event.code));
const nextCombination = keysRecorder.getPressed().join('+');
if (previousCombination.length) {
invokeClbck(previousCombination.split('+'), event, "up");
}
if (nextCombination.length) {
invokeClbck(nextCombination.split('+'), event, "down");
}
}
function onblur(event) {
const previousCombination = keysRecorder.getPressed().join('+');
if (previousCombination.length) {
invokeClbck(previousCombination.split('+'), event, "up");
}
keysRecorder.releaseAll();
}
function onunload() {
eventTarget.removeEventListener('keydown', onkeydown);
eventTarget.removeEventListener('keyup', onkeyup);
eventTarget.removeEventListener('blur', onblur);
eventTarget.removeEventListener('unload', onunload);
}
return {
listen: (context, keys, callback, phases) => {
const ctx = contexts.has(context) ? contexts.get(context) : contexts.add(context);
ctx?.add(keys, callback, phases);
return () => ctx?.remove(keys, phases);
},
unlisten: (context, keys, phases) => {
const ctx = contexts.get(context);
ctx?.remove(keys, phases);
},
hasContext: (name) => contexts.has(name),
getActiveContext: () => contexts.getActive(),
setActiveContext: (name) => contexts.setActive(name),
};
}