@ramstack/vue-hotkey
Version:
A lightweight package simplifies the handling of keyboard shortcuts within Vue applications. No external dependencies
129 lines (127 loc) • 3.74 kB
JavaScript
const aliases = {
"esc": "escape",
"ins": "insert",
"del": "delete",
"up": "arrowup",
"down": "arrowdown",
"right": "arrowright",
"left": "arrowleft",
"pgup": "pageup",
"pgdn": "pagedown",
"break": "pause",
"scroll": "scrolllock",
"scrlk": "scrolllock",
"prtscr": "printscreen",
"win": "meta",
"windows": "meta",
"cmd": "meta",
"command": "meta",
"comma": ",",
"period": ".",
"quote": "\"",
"singlequote": "'",
"colon": ":",
"semicolon": ";",
"plus": "+",
"minus": "-",
"tilde": "~",
"equal": "=",
"slash": "/"
};
const control_keys = [
"ctrlKey",
"altKey",
"shiftKey",
"metaKey"
];
function registerHotkey(target, hotkey, handler, eventName = "keydown", options) {
const info = describe(hotkey);
if (typeof target === "string") {
target = document.querySelector(target) ?? error(`No element found for selector '${target}'`);
}
return listen(target, eventName, function (e) {
if (!options?.trusted || e.isTrusted) {
if (!e.target?.closest("[data-hotkey-ignore]")) {
if (info.code === e.code.toUpperCase()) {
if (control_keys.every(n => info[n] === e[n])) {
handler.call(this, e);
}
}
}
}
}, options);
}
function describe(hotkey) {
const keys = hotkey.replace(/\s+/g, "").toLowerCase().split("+");
const info = keys.reduce((data, k) => {
k = aliases[k] ?? k;
switch (k) {
case "ctrl":
case "alt":
case "shift":
case "meta":
data[`${k}Key`] = true;
break;
default:
k.length || error_invalid_key(hotkey);
k = k.toUpperCase();
data.code = k.length === 1 && k >= 'A' && k <= 'Z' ? `KEY${k}` : k;
break;
}
return data;
}, {
code: "",
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false
});
info.code || error_invalid_key(hotkey);
return info;
}
function listen(target, type, callback, options) {
target.addEventListener(type, callback, options);
return () => target.removeEventListener(type, callback, options);
}
function error_invalid_key(hotkey) {
error(`Invalid hotkey: '${hotkey}'`);
}
function error(message) {
throw new Error(message);
}
const option_keys = ["stop", "passive", "prevent", "once", "capture", "trusted", "window", "document"];
const vHotkey = {
mounted(el, { arg, modifiers, value }) {
const { stop, passive, prevent, once, capture, trusted } = modifiers;
const target = modifiers.window ? window :
modifiers.document ? document : el;
el[create_key(modifiers)] = Object.keys(modifiers)
.filter(k => !option_keys.includes(k))
.map(hotkey => registerHotkey(target, hotkey, function (e) {
stop && e.stopPropagation();
prevent && e.preventDefault();
e["hotkey"] = hotkey;
value?.call(this, e);
}, arg ?? "keydown", {
capture,
passive,
once,
trusted
}));
},
unmounted(el, binding) {
const key = create_key(binding.modifiers);
const disposes = el[key];
disposes?.forEach(r => r());
el[key] = null;
}
};
const HotkeyPlugin = {
install(app) {
app.directive("hotkey", vHotkey);
},
};
function create_key(modifiers) {
return `__hotkey[${Object.keys(modifiers).join("+")}]`;
}
export { HotkeyPlugin, registerHotkey, vHotkey };