UNPKG

kbind

Version:

Library for working with keybinds

679 lines (678 loc) 18.6 kB
var D = Object.defineProperty; var L = (e, i, a) => i in e ? D(e, i, { enumerable: !0, configurable: !0, writable: !0, value: a }) : e[i] = a; var p = (e, i, a) => L(e, typeof i != "symbol" ? i + "" : i, a); import { isUndefined as m, isArray as K, sortedIndex as A, isNil as S } from "lodash"; var t = /* @__PURE__ */ ((e) => (e.Alt = "Alt", e.AltLeft = "AltLeft", e.AltRight = "AltRight", e.ArrowDown = "ArrowDown", e.ArrowLeft = "ArrowLeft", e.ArrowRight = "ArrowRight", e.ArrowUp = "ArrowUp", e.Backquote = "Backquote", e.Backslash = "Backslash", e.Backspace = "Backspace", e.BracketLeft = "BracketLeft", e.BracketRight = "BracketRight", e.CapsLock = "CapsLock", e.Comma = "Comma", e.Control = "Control", e.ControlLeft = "ControlLeft", e.ControlRight = "ControlRight", e.Delete = "Delete", e.Digit0 = "Digit0", e.Digit1 = "Digit1", e.Digit2 = "Digit2", e.Digit3 = "Digit3", e.Digit4 = "Digit4", e.Digit5 = "Digit5", e.Digit6 = "Digit6", e.Digit7 = "Digit7", e.Digit8 = "Digit8", e.Digit9 = "Digit9", e.Minus = "Minus", e.Equal = "Equal", e.End = "End", e.Enter = "Enter", e.Escape = "Escape", e.Home = "Home", e.Insert = "Insert", e.IntlBackslash = "IntlBackslash", e.IntlRo = "IntlRo", e.IntlYen = "IntlYen", e.F1 = "F1", e.F2 = "F2", e.F3 = "F3", e.F4 = "F4", e.F5 = "F5", e.F6 = "F6", e.F7 = "F7", e.F8 = "F8", e.F9 = "F9", e.F10 = "F10", e.F11 = "F11", e.F12 = "F12", e.F13 = "F13", e.F14 = "F14", e.F15 = "F15", e.F16 = "F16", e.F17 = "F17", e.F18 = "F18", e.F19 = "F19", e.F20 = "F20", e.F21 = "F21", e.F22 = "F22", e.F23 = "F23", e.F24 = "F24", e.KeyA = "KeyA", e.KeyB = "KeyB", e.KeyC = "KeyC", e.KeyD = "KeyD", e.KeyE = "KeyE", e.KeyF = "KeyF", e.KeyG = "KeyG", e.KeyH = "KeyH", e.KeyI = "KeyI", e.KeyJ = "KeyJ", e.KeyK = "KeyK", e.KeyL = "KeyL", e.KeyM = "KeyM", e.KeyN = "KeyN", e.KeyO = "KeyO", e.KeyP = "KeyP", e.KeyQ = "KeyQ", e.KeyR = "KeyR", e.KeyS = "KeyS", e.KeyT = "KeyT", e.KeyU = "KeyU", e.KeyV = "KeyV", e.KeyW = "KeyW", e.KeyX = "KeyX", e.KeyY = "KeyY", e.KeyZ = "KeyZ", e.Numpad0 = "Numpad0", e.Numpad1 = "Numpad1", e.Numpad2 = "Numpad2", e.Numpad3 = "Numpad3", e.Numpad4 = "Numpad4", e.Numpad5 = "Numpad5", e.Numpad6 = "Numpad6", e.Numpad7 = "Numpad7", e.Numpad8 = "Numpad8", e.Numpad9 = "Numpad9", e.NumpadAdd = "NumpadAdd", e.NumpadSubtract = "NumpadSubtract", e.NumpadMultiply = "NumpadMultiply", e.NumpadDivide = "NumpadDivide", e.NumpadEqual = "NumpadEqual", e.NumpadDecimal = "NumpadDecimal", e.NumpadComma = "NumpadComma", e.NumpadEnter = "NumpadEnter", e.PageUp = "PageUp", e.PageDown = "PageDown", e.Pause = "Pause", e.Period = "Period", e.Quote = "Quote", e.ScrollLock = "ScrollLock", e.Semicolon = "Semicolon", e.Shift = "Shift", e.ShiftLeft = "ShiftLeft", e.ShiftRight = "ShiftRight", e.Slash = "Slash", e.Space = "Space", e.Tab = "Tab", e))(t || {}); const v = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: t }, Symbol.toStringTag, { value: "Module" })), R = [ t.AltLeft, t.AltRight, t.ControlLeft, t.ControlRight, t.ShiftLeft, t.ShiftRight ]; t.AltLeft, t.AltRight, t.ShiftLeft, t.ShiftRight, t.ControlLeft, t.ControlRight; const C = [ t.ArrowDown, t.ArrowLeft, t.ArrowRight, t.ArrowUp, t.Backquote, t.Backslash, t.Backspace, t.BracketLeft, t.BracketRight, t.CapsLock, t.Comma, t.Delete, t.Digit0, t.Digit1, t.Digit2, t.Digit3, t.Digit4, t.Digit5, t.Digit6, t.Digit7, t.Digit8, t.Digit9, t.Minus, t.Equal, t.End, t.Enter, t.Escape, t.Home, t.Insert, t.IntlBackslash, t.IntlRo, t.IntlYen, t.F1, t.F2, t.F3, t.F4, t.F5, t.F6, t.F7, t.F8, t.F9, t.F10, t.F11, t.F12, t.F13, t.F14, t.F15, t.F16, t.F17, t.F18, t.F19, t.F20, t.F21, t.F22, t.F23, t.F24, t.KeyA, t.KeyB, t.KeyC, t.KeyD, t.KeyE, t.KeyF, t.KeyG, t.KeyH, t.KeyI, t.KeyJ, t.KeyK, t.KeyL, t.KeyM, t.KeyN, t.KeyO, t.KeyP, t.KeyQ, t.KeyR, t.KeyS, t.KeyT, t.KeyU, t.KeyV, t.KeyW, t.KeyX, t.KeyY, t.KeyZ, t.Numpad0, t.Numpad1, t.Numpad2, t.Numpad3, t.Numpad4, t.Numpad5, t.Numpad6, t.Numpad7, t.Numpad8, t.Numpad9, t.NumpadAdd, t.NumpadSubtract, t.NumpadMultiply, t.NumpadDivide, t.NumpadEqual, t.NumpadDecimal, t.NumpadComma, t.NumpadEnter, t.PageUp, t.PageDown, t.Pause, t.Period, t.Quote, t.ScrollLock, t.Semicolon, t.Slash, t.Space, t.Tab ], k = { [t.Alt]: "Alt", [t.AltLeft]: "AltLeft", [t.AltRight]: "AltRight", [t.ArrowDown]: "ArrowDown", [t.ArrowLeft]: "ArrowLeft", [t.ArrowRight]: "ArrowRight", [t.ArrowUp]: "ArrowUp", [t.Backquote]: "`", [t.Backslash]: "\\", [t.Backspace]: "Backspace", [t.BracketLeft]: "[", [t.BracketRight]: "]", [t.CapsLock]: "CapsLock", [t.Comma]: ",", [t.Control]: "Ctrl", [t.ControlLeft]: "ControlLeft", [t.ControlRight]: "ControlRight", [t.Delete]: "Delete", [t.Digit0]: "0", [t.Digit1]: "1", [t.Digit2]: "2", [t.Digit3]: "3", [t.Digit4]: "4", [t.Digit5]: "5", [t.Digit6]: "6", [t.Digit7]: "7", [t.Digit8]: "8", [t.Digit9]: "9", [t.Minus]: "-", [t.Equal]: "=", [t.End]: "End", [t.Enter]: "Enter", [t.Escape]: "Escape", [t.Home]: "Home", [t.Insert]: "Insert", [t.IntlBackslash]: "IntlBackslash", [t.IntlRo]: "IntlRo", [t.IntlYen]: "IntlYen", [t.F1]: "F1", [t.F2]: "F2", [t.F3]: "F3", [t.F4]: "F4", [t.F5]: "F5", [t.F6]: "F6", [t.F7]: "F7", [t.F8]: "F8", [t.F9]: "F9", [t.F10]: "F10", [t.F11]: "F11", [t.F12]: "F12", [t.F13]: "F13", [t.F14]: "F14", [t.F15]: "F15", [t.F16]: "F16", [t.F17]: "F17", [t.F18]: "F18", [t.F19]: "F19", [t.F20]: "F20", [t.F21]: "F21", [t.F22]: "F22", [t.F23]: "F23", [t.F24]: "F24", [t.KeyA]: "A", [t.KeyB]: "B", [t.KeyC]: "C", [t.KeyD]: "D", [t.KeyE]: "E", [t.KeyF]: "F", [t.KeyG]: "G", [t.KeyH]: "H", [t.KeyI]: "I", [t.KeyJ]: "J", [t.KeyK]: "K", [t.KeyL]: "L", [t.KeyM]: "M", [t.KeyN]: "N", [t.KeyO]: "O", [t.KeyP]: "P", [t.KeyQ]: "Q", [t.KeyR]: "R", [t.KeyS]: "S", [t.KeyT]: "T", [t.KeyU]: "U", [t.KeyV]: "V", [t.KeyW]: "W", [t.KeyX]: "X", [t.KeyY]: "Y", [t.KeyZ]: "Z", [t.Numpad0]: "Numpad 0", [t.Numpad1]: "Numpad 1", [t.Numpad2]: "Numpad 2", [t.Numpad3]: "Numpad 3", [t.Numpad4]: "Numpad 4", [t.Numpad5]: "Numpad 5", [t.Numpad6]: "Numpad 6", [t.Numpad7]: "Numpad 7", [t.Numpad8]: "Numpad 8", [t.Numpad9]: "Numpad 9", [t.NumpadAdd]: "+", [t.NumpadSubtract]: "-", [t.NumpadMultiply]: "*", [t.NumpadDivide]: "/", [t.NumpadEqual]: "=", [t.NumpadDecimal]: ".", [t.NumpadComma]: ",", [t.NumpadEnter]: "Enter", [t.PageUp]: "PageUp", [t.PageDown]: "PageDown", [t.Pause]: "Pause", [t.Period]: ".", [t.Quote]: '"', [t.ScrollLock]: "ScrollLock", [t.Semicolon]: ";", [t.Shift]: "Shift", [t.ShiftLeft]: "ShiftLeft", [t.ShiftRight]: "ShiftRight", [t.Slash]: "/", [t.Space]: "Space", [t.Tab]: "Tab" }; function w(e, i) { const { target: a } = e; if (!(a instanceof HTMLInputElement || a instanceof HTMLTextAreaElement)) return !1; const s = a.getAttribute("type") ?? "text-area"; return m(i) ? !0 : K(i) ? (i == null ? void 0 : i.includes(s)) ?? !0 : s === i; } const y = [ t.Backquote, t.Backslash, t.BracketLeft, t.BracketRight, t.Comma, t.Digit0, t.Digit1, t.Digit2, t.Digit3, t.Digit4, t.Digit5, t.Digit6, t.Digit7, t.Digit8, t.Digit9, t.Minus, t.Equal, t.IntlBackslash, t.IntlRo, t.IntlYen, t.KeyA, t.KeyB, t.KeyC, t.KeyD, t.KeyE, t.KeyF, t.KeyG, t.KeyH, t.KeyI, t.KeyJ, t.KeyK, t.KeyL, t.KeyM, t.KeyN, t.KeyO, t.KeyP, t.KeyQ, t.KeyR, t.KeyS, t.KeyT, t.KeyU, t.KeyV, t.KeyW, t.KeyX, t.KeyY, t.KeyZ, t.Quote, t.Semicolon, t.Slash ], E = [ ...y, t.Backspace, t.Delete, t.Numpad0, t.Numpad1, t.Numpad2, t.Numpad3, t.Numpad4, t.Numpad5, t.Numpad6, t.Numpad7, t.Numpad8, t.Numpad9, t.NumpadAdd, t.NumpadSubtract, t.NumpadMultiply, t.NumpadDivide, t.NumpadEqual, t.NumpadDecimal, t.NumpadComma, t.Space ], I = [t.Home, t.End, t.PageUp, t.PageDown], P = [ t.ArrowLeft, t.ArrowRight, t.Backspace, t.Delete, t.Home, t.End, t.KeyA, t.KeyC, t.Insert, t.KeyX, t.KeyV, t.KeyZ, t.KeyY ], B = [ t.Numpad0, t.Numpad1, t.Numpad2, t.Numpad3, t.Numpad4, t.Numpad5, t.Numpad6, t.Numpad7, t.Numpad8, t.Numpad9 ], b = [ t.ArrowLeft, t.ArrowRight, t.Home, t.End, t.Delete, t.Insert ], M = [t.ArrowLeft, t.ArrowRight, t.Home, t.End]; function N(e) { if (!w(e, "text")) return !0; const { ctrlKey: i, shiftKey: a, altKey: s, code: n } = e; return !i && !a && !s ? !I.includes(n) && !E.includes(n) : i && !a && !s ? !P.includes(n) : !i && a && !s ? !b.includes(n) && !y.includes(n) : !i && !a && s ? !B.includes(n) : i && a && !s ? !M.includes(n) : !0; } const r = class r { /** * Checks if either the left or right Control key is currently pressed. * * @returns {boolean} `true` if either ControlLeft or ControlRight key is pressed, * otherwise `false`. */ static get isControlPressed() { return r.state.modifierKeys.includes(t.ControlLeft) || r.state.modifierKeys.includes(t.ControlRight); } /** * Checks if either the left or right Shift key is currently pressed. * * @returns {boolean} `true` if either ShiftLeft or ShiftRight key is pressed, * otherwise `false` */ static get isShiftPressed() { return r.state.modifierKeys.includes(t.ShiftLeft) || r.state.modifierKeys.includes(t.ShiftRight); } /** * Checks if either the left or right Alt key is currently pressed. * * @returns {boolean} `true` if either AltLeft or AltRight key is pressed, * otherwise `false`. */ static get isAltPressed() { return r.state.modifierKeys.includes(t.AltLeft) || r.state.modifierKeys.includes(t.AltRight); } /** * Gets the sorted list of layer ids based on their priority. * * NOTE: heavy operation, use it wisely. * * @returns {string[]} An array of layer IDs sorted in descending order of their priority. */ static get layerIds() { return Object.keys(r.state.layers).sort( (i, a) => { const s = r.state.layers[i]; return r.state.layers[a].priority - s.priority; } ); } /** * Gets the sorted list of listener ids based on their priority. * * NOTE: heavy operation, use it wisely. * * @returns {string[]} An array of listener IDs sorted in descending order of their priority. */ static get listenerIds() { return Object.keys(r.state.listeners).sort( (i, a) => { const s = r.state.listeners[i]; return r.state.listeners[a].priority - s.priority; } ); } /** * Gets the highest priority of the layer in the stack. * * NOTE: heavy operation, use it wisely. * * @returns {number | undefined} The highest priority of the layer in the stack. */ static get highestLayerPriority() { const i = r.layerIds[0]; if (!m(i)) return r.state.layers[i].priority; } /** * Mounts the KeyBinder event listeners to the window object. * This method attaches the following event listeners: * - 'blur': Calls the `blurHandler` method when the window loses focus. * - 'keydown': Calls the `keyDownHandler` method when a key is pressed down. * - 'keyup': Calls the `keyUpHandler` method when a key is released. */ static mount() { window.addEventListener("blur", r.blurHandler), window.addEventListener("keydown", r.keyDownHandler), window.addEventListener("keyup", r.keyUpHandler); } /** * Unmounts the KeyBinder by removing event listeners for 'blur', 'keydown', and 'keyup' events. * This method should be called to clean up event listeners when the KeyBinder is no longer * needed. */ static unmount() { window.removeEventListener("blur", r.blurHandler), window.removeEventListener("keydown", r.keyDownHandler), window.removeEventListener("keyup", r.keyUpHandler); } /** * Handles the blur event by resetting the state of modifier keys. * This method is called when the window loses focus. */ static blurHandler() { r.state.modifierKeys = []; } /** * Handles the key down event by processing the key code and updating the state of modifier * keys. * * @param event - The keyboard event triggered by a key press. */ static keyDownHandler(i) { if (i.code) { if (!r.isModifierKeyCode(i.code)) { r.emit(i); return; } r.state.modifierKeys.includes(i.code) || r.state.modifierKeys.splice( A(r.state.modifierKeys, i.code), 0, i.code ); } } /** * Handles the key up event for modifier keys. * * @param event - The keyboard event triggered on key up. */ static keyUpHandler(i) { if (!i.code || !r.isModifierKeyCode(i.code)) return; const a = r.state.modifierKeys.indexOf(i.code); a !== -1 && r.state.modifierKeys.splice(a, 1); } /** * Checks if the given key code is a modifier key. * * @param key - The key code to check. * @returns `true` if the key is a modifier key, otherwise `false`. */ static isModifierKeyCode(i) { return R.includes(i); } /** * Checks if the given key code is a primary key. * * @param key - The key code to check. * @returns `true` if the key is a primary key, otherwise `false`. */ static isPrimaryKeyCode(i) { return C.includes(i); } /** * Converts a given key code to its corresponding key name. * * @param key - The key code to convert. * @returns The key name corresponding to the key code, * or the key code itself if no key name is found. */ static normalizeKey(i) { return k[i] || i; } /** * Converts a key binding to an array of key names. * * @param keyBind - The key binding to convert. * @returns An array of key names corresponding to the key binding. */ static normalizeKeyBind(i) { return Array.isArray(i) ? i.map(r.normalizeKey).sort((a, s) => a.localeCompare(s)) : i ? [r.normalizeKey(i)] : []; } /** * Gets the highest priority of the listener in the stack. * * NOTE: heavy operation, use it wisely. * * @param layerId - The id of the layer to get the highest listener priority from. * @returns {number | undefined} The highest priority of the listener in the given layer stack. */ static getHighestListenerPriority(i) { const { listenerIds: a } = r, s = a.find((n) => r.state.listeners[n].layerId === i); if (!m(s)) return r.state.listeners[s].priority; } /** * Registers a listener with a specified id. * * @param id - The unique identifier for the listener. * @param listener - The listener metadata object containing the callback and other properties. */ static registerListener(i, a) { r.state.listeners[i] = { layerId: a.layerId ?? "default", ...a }; } /** * Removes a listener from the state by its unique id. * * @param id - The unique identifier for the listener. */ static unregisterListener(i) { delete r.state.listeners[i]; } /** * Registers a layer with a specified id. * * @param id - The unique identifier for the layer. * @param layer - The layer metadata object containing the priority and propagation properties */ static registerLayer(i, a) { r.state.layers[i] = a; } /** * Removes a layer from the state by its unique id. * * @param id - The unique identifier for the layer. */ static unregisterLayer(i) { delete r.state.layers[i]; } static isValidEvent(i) { return r.eventValidators.every( (a) => a(i) ); } static checkIfBindModifierKeysPressed(i) { const a = i, s = { Control: { Left: r.state.modifierKeys.includes(t.ControlLeft), Right: r.state.modifierKeys.includes(t.ControlRight) }, Shift: { Left: r.state.modifierKeys.includes(t.ShiftLeft), Right: r.state.modifierKeys.includes(t.ShiftRight) }, Alt: { Left: r.state.modifierKeys.includes(t.AltLeft), Right: r.state.modifierKeys.includes(t.AltRight) } }, n = { Control: { Left: a.includes(t.Control) || a.includes(t.ControlLeft), Right: a.includes(t.Control) || a.includes(t.ControlRight) }, Shift: { Left: a.includes(t.Shift) || a.includes(t.ShiftLeft), Right: a.includes(t.Shift) || a.includes(t.ShiftRight) }, Alt: { Left: a.includes(t.Alt) || a.includes(t.AltLeft), Right: a.includes(t.Alt) || a.includes(t.AltRight) } }; return Object.keys(s).every((l) => { const o = l, u = s[o], d = n[o]; return u.Left && d.Left || u.Right && d.Right || !u.Left && !u.Right && !d.Left && !d.Right; }); } static findListenerIdByPressedKey(i) { const { layerIds: a, listenerIds: s } = r; let n; if (a.some((o) => { const u = r.state.layers[o], d = s.find((h) => { const g = r.state.listeners[h]; if (g.layerId !== o) return !1; const { bind: c } = g; if (S(c)) return !1; if (r.checkIfBindModifierKeysPressed(c)) { const F = c[c.length - 1]; return F === i || F === "Enter" && i === "NumpadEnter"; } return !1; }); return n = d, !(!d && u.propagate); })) return n; } static emit(i) { var n, l, o; if (!r.isValidEvent(i)) return; const a = r.findListenerIdByPressedKey( i.code ); if (m(a)) return; const s = r.state.listeners[a]; (n = s.options) != null && n.preventDefault && i.preventDefault(), (l = s.options) != null && l.stopPropagation && i.stopPropagation(), (o = s.handler) == null || o.call(s, i); } }; p(r, "state", { modifierKeys: [], listeners: {}, layers: { default: { priority: 0, propagate: !0 } } }), /** * An array of event validator functions used to determine the validity of keyboard events. * * It's necessary in case you don't want to trigger some listeners under certain conditions, * e.g. `Ctrl+A` in a text field. * * @type {KeyBinder.EventValidator[]} */ p(r, "eventValidators", [ N ]); let f = r; const V = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, default: f }, Symbol.toStringTag, { value: "Module" })), q = { isTextInputEventValid: N }; export { v as Key, V as KeyBinder, f as default, q as defaultEventValidators };