UNPKG

@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
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 };