keybuddy 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/index.d.mts +25 -1
- package/index.d.ts +25 -1
- package/index.global.js +1 -1
- package/index.global.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/index.mjs +1 -1
- package/index.mjs.map +1 -1
- package/package.json +1 -1
- package/keybuddy.d.mts +0 -16
- package/keybuddy.d.ts +0 -16
- package/keybuddy.global.js +0 -3
- package/keybuddy.global.js.map +0 -1
- package/keybuddy.js +0 -3
- package/keybuddy.js.map +0 -1
- package/keybuddy.mjs +0 -3
- package/keybuddy.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# keybuddy ⌨️
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/js/keybuddy)
|
|
4
|
-
[](https://jsr.io/@keybuddy/core)
|
|
5
4
|
[](https://www.typescriptlang.org/)
|
|
6
|
-
[](https://deno.land/)
|
|
7
5
|
|
|
8
6
|
Define and dispatch shortcuts with easy using keybuddy.
|
|
9
7
|
|
package/index.d.mts
CHANGED
|
@@ -3,6 +3,30 @@ declare const KeyStringBrand: unique symbol;
|
|
|
3
3
|
type KeyString = string & {
|
|
4
4
|
readonly [KeyStringBrand]: true;
|
|
5
5
|
};
|
|
6
|
+
declare const MODS: {
|
|
7
|
+
readonly SHIFT: 1;
|
|
8
|
+
readonly ALT: 2;
|
|
9
|
+
readonly CTRL: 4;
|
|
10
|
+
readonly META: 8;
|
|
11
|
+
};
|
|
12
|
+
declare const SPECIAL: {
|
|
13
|
+
[key: string]: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type noop = (e: KeyboardEvent) => void;
|
|
17
|
+
type FilterFn = (el: KeyboardEvent) => boolean;
|
|
18
|
+
declare function createKeybuddy(doc: Document, filterFn?: FilterFn): {
|
|
19
|
+
bind: (keysStr: string, scopeOrMethod: string | noop, methodOrNull?: noop, { skipOther, }?: {
|
|
20
|
+
skipOther: boolean;
|
|
21
|
+
}) => void;
|
|
22
|
+
unbind: (keysStr: string, scopeOrMethod: string | noop, methodOrNull?: noop) => void;
|
|
23
|
+
unsafeUnbind: (keysStr: string, scope?: string) => void;
|
|
24
|
+
unbindScope: (deleteScope: string) => void;
|
|
25
|
+
setScope: (scope: string) => void;
|
|
26
|
+
unbindAll: () => void;
|
|
27
|
+
getScope: () => string;
|
|
28
|
+
destroy: () => void;
|
|
29
|
+
};
|
|
6
30
|
|
|
7
31
|
declare const bind: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void, { skipOther, }?: {
|
|
8
32
|
skipOther: boolean;
|
|
@@ -18,4 +42,4 @@ declare const bindKey: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEv
|
|
|
18
42
|
declare const unbindKey: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void) => void;
|
|
19
43
|
declare const unsafeUnbindKey: (keysStr: string, scope?: string) => void;
|
|
20
44
|
|
|
21
|
-
export { DEFAULT_SCOPE, type KeyString, bindKey, bind as default, destroy, getScope, setScope, unbindAll, unbindKey, unbindScope, unsafeUnbindKey };
|
|
45
|
+
export { DEFAULT_SCOPE, type KeyString, MODS, SPECIAL, bindKey, createKeybuddy, bind as default, destroy, getScope, setScope, unbindAll, unbindKey, unbindScope, unsafeUnbindKey };
|
package/index.d.ts
CHANGED
|
@@ -3,6 +3,30 @@ declare const KeyStringBrand: unique symbol;
|
|
|
3
3
|
type KeyString = string & {
|
|
4
4
|
readonly [KeyStringBrand]: true;
|
|
5
5
|
};
|
|
6
|
+
declare const MODS: {
|
|
7
|
+
readonly SHIFT: 1;
|
|
8
|
+
readonly ALT: 2;
|
|
9
|
+
readonly CTRL: 4;
|
|
10
|
+
readonly META: 8;
|
|
11
|
+
};
|
|
12
|
+
declare const SPECIAL: {
|
|
13
|
+
[key: string]: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type noop = (e: KeyboardEvent) => void;
|
|
17
|
+
type FilterFn = (el: KeyboardEvent) => boolean;
|
|
18
|
+
declare function createKeybuddy(doc: Document, filterFn?: FilterFn): {
|
|
19
|
+
bind: (keysStr: string, scopeOrMethod: string | noop, methodOrNull?: noop, { skipOther, }?: {
|
|
20
|
+
skipOther: boolean;
|
|
21
|
+
}) => void;
|
|
22
|
+
unbind: (keysStr: string, scopeOrMethod: string | noop, methodOrNull?: noop) => void;
|
|
23
|
+
unsafeUnbind: (keysStr: string, scope?: string) => void;
|
|
24
|
+
unbindScope: (deleteScope: string) => void;
|
|
25
|
+
setScope: (scope: string) => void;
|
|
26
|
+
unbindAll: () => void;
|
|
27
|
+
getScope: () => string;
|
|
28
|
+
destroy: () => void;
|
|
29
|
+
};
|
|
6
30
|
|
|
7
31
|
declare const bind: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void, { skipOther, }?: {
|
|
8
32
|
skipOther: boolean;
|
|
@@ -18,4 +42,4 @@ declare const bindKey: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEv
|
|
|
18
42
|
declare const unbindKey: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void) => void;
|
|
19
43
|
declare const unsafeUnbindKey: (keysStr: string, scope?: string) => void;
|
|
20
44
|
|
|
21
|
-
export { DEFAULT_SCOPE, type KeyString, bindKey, bind as default, destroy, getScope, setScope, unbindAll, unbindKey, unbindScope, unsafeUnbindKey };
|
|
45
|
+
export { DEFAULT_SCOPE, type KeyString, MODS, SPECIAL, bindKey, createKeybuddy, bind as default, destroy, getScope, setScope, unbindAll, unbindKey, unbindScope, unsafeUnbindKey };
|
package/index.global.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/* keybuddy - Modern keyboard shortcuts library */
|
|
2
|
-
"use strict";var keybuddy=(()=>{var
|
|
2
|
+
"use strict";var keybuddy=(()=>{var b=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var Y=(e,r)=>{for(var t in r)b(e,t,{get:r[t],enumerable:!0})},j=(e,r,t,s)=>{if(r&&typeof r=="object"||typeof r=="function")for(let c of $(r))!V.call(e,c)&&c!==t&&b(e,c,{get:()=>r[c],enumerable:!(s=q(r,c))||s.enumerable});return e};var J=e=>j(b({},"__esModule",{value:!0}),e);var de={};Y(de,{DEFAULT_SCOPE:()=>u,MODS:()=>i,SPECIAL:()=>g,bindKey:()=>ie,createKeybuddy:()=>L,default:()=>ce,destroy:()=>oe,getScope:()=>ee,setScope:()=>te,unbindAll:()=>re,unbindKey:()=>se,unbindScope:()=>ne,unsafeUnbindKey:()=>ae});var u="all",i={SHIFT:1,ALT:2,CTRL:4,META:8},S={"\u21E7":i.SHIFT,shift:i.SHIFT,"\u2325":i.ALT,alt:i.ALT,option:i.ALT,"\u2303":i.CTRL,ctrl:i.CTRL,control:i.CTRL,"\u2318":i.META,cmd:i.META,command:i.META},g={backspace:"Backspace",tab:"Tab",clear:"Clear",enter:"Enter",return:"Enter",esc:"Escape",escape:"Escape",space:" ",left:"ArrowLeft",up:"ArrowUp",right:"ArrowRight",down:"ArrowDown",del:"Delete",delete:"Delete",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",comma:",",".":".","/":"/","`":"`","-":"-","=":"=",";":";","'":"'","[":"[","]":"]","\\":"\\",Meta:"Meta",MetaLeft:"Meta",MetaRight:"Meta",OS:"Meta",ContextMenu:"Meta",ArrowLeft:"ArrowLeft",ArrowUp:"ArrowUp",ArrowRight:"ArrowRight",ArrowDown:"ArrowDown",Backspace:"Backspace",Tab:"Tab",Clear:"Clear",Enter:"Enter",Escape:"Escape",Delete:"Delete",Home:"Home",End:"End",PageUp:"PageUp",PageDown:"PageDown"},P="CapsLock";var K=e=>g[e]||e.toUpperCase(),D=e=>{let r=0;return e.shiftKey&&(r|=i.SHIFT),e.altKey&&(r|=i.ALT),e.ctrlKey&&(r|=i.CTRL),e.metaKey&&(r|=i.META),r};var W=e=>g[e]||e.toUpperCase(),X=e=>e.reduce((r,t)=>(t in S?r.mods|=S[t]:r.special.push(g[t]||t.toUpperCase()),r),{mods:0,special:[]}),z=e=>{let t=e.replace(/\s/g,"").split(",");return t[t.length-1]===""&&(t[t.length-2]+=","),t},h=e=>z(e).map(t=>{let s=t.split("+"),c=s[s.length-1];return{key:W(c),shortcut:X(s)}});var I=navigator.userAgent.includes("Firefox"),U=e=>e.isContentEditable||e.tagName==="INPUT"||e.tagName==="SELECT"||e.tagName==="TEXTAREA",A=(e,r)=>{if(e.length!==r.length)return!1;for(let t=0;t<e.length;t++)if(e[t]!==r[t])return!1;return!0};var G=e=>e&&!U(e.target),v=new WeakMap;function L(e,r=G){let t={},s=new Set,c="all",m=0,H=(o,n,d=()=>{},{skipOther:a}={skipOther:!1})=>{let p=typeof n=="function"?"all":n,l=typeof n=="function"?n:d;h(o).forEach(({key:f,shortcut:y})=>{t[f]||(t[f]=[]),t[f].push({scope:p,method:l,shortcut:y,skipOther:a})})},T=(o,n,d="all")=>{h(o).forEach(({key:a,shortcut:p})=>{let l=t[a];if(Array.isArray(l)){let f=t[a].filter(({scope:y,method:k,shortcut:E})=>!(y===d&&E.mods===p.mods&&A(E.special,p.special)&&(n===null||k===n)));f.length?t[a]=f:delete t[a]}})},O=(o,n,d=()=>{})=>T(o,typeof n=="function"?n:d,typeof n=="function"?"all":n),N=(o,n)=>T(o,null,n),w=o=>{let n=K(o.key);if(!r(o)||I&&n===P||(m=D(o),!(n==="SHIFT"||n==="ALT"||n==="CTRL"||n==="META")&&!s.has(n)&&s.add(n),!(n in t)))return;let a=t[n].filter(({scope:l,shortcut:{special:f,mods:y}})=>l!==c?!1:A(f,Array.from(s))&&y===m),p=a.find(l=>l.skipOther);p?p.method(o):a.forEach(({method:l})=>{l(o)})},M=o=>{let n=K(o.key);o.key&&o.key.toLowerCase()==="meta"?s.clear():s.delete(n)},R=o=>{Object.keys(t).forEach(n=>{let d=n,a=t[d].filter(({scope:p})=>p!==o);a.length?t[d]=a:delete t[d]})},_=o=>{c=o},B=()=>{t={},s.clear()},x=()=>{s.clear()},C=()=>{if(s.clear(),t={},e){let o=v.get(e);o&&(e.removeEventListener("keydown",o.dispatch),e.removeEventListener("keyup",o.cleanUp),window.removeEventListener("focus",o.reset),v.delete(e))}};return C(),v.set(e,{dispatch:w,cleanUp:M,reset:x}),e.addEventListener("keydown",w),e.addEventListener("keyup",M),window.addEventListener("focus",x),{bind:H,unbind:O,unsafeUnbind:N,unbindScope:R,setScope:_,unbindAll:B,getScope:()=>c,destroy:C}}var{bind:F,unbind:Q,unsafeUnbind:Z,getScope:ee,setScope:te,unbindScope:ne,unbindAll:re,destroy:oe}=L(document),ie=F,se=Q,ae=Z;var ce=F;return J(de);})();
|
|
3
3
|
//# sourceMappingURL=index.global.js.map
|
package/index.global.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/helpers/keyboard.ts","../src/helpers/keymap.ts","../src/helpers/utils.ts","../src/keybuddy.ts"],"sourcesContent":["import { DEFAULT_SCOPE } from './constants';\nimport { createKeybuddy } from './keybuddy';\n\nconst {\n bind,\n unbind,\n unsafeUnbind,\n getScope,\n setScope,\n unbindScope,\n unbindAll,\n destroy,\n} = createKeybuddy(document);\n\nexport const bindKey = bind;\nexport const unbindKey = unbind;\nexport const unsafeUnbindKey = unsafeUnbind;\nexport { setScope, unbindScope, unbindAll, getScope, destroy, DEFAULT_SCOPE };\nexport type { KeyString } from './constants';\nexport default bind;\n","\nexport const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\n// Bitwise flags for modifiers - much faster than object-based tracking\nexport const MODS = {\n SHIFT: 0b0001, // 1\n ALT: 0b0010, // 2\n CTRL: 0b0100, // 4\n META: 0b1000, // 8\n} as const;\n\n// Map string modifier names to bitwise flags for parsing\nexport type ModifierNames = {\n '⇧': number;\n shift: number;\n '⌥': number;\n alt: number;\n option: number;\n '⌃': number;\n ctrl: number;\n control: number;\n '⌘': number;\n cmd: number;\n command: number;\n};\n\nexport const MODIFIERS: ModifierNames = {\n '⇧': MODS.SHIFT,\n shift: MODS.SHIFT,\n '⌥': MODS.ALT,\n alt: MODS.ALT,\n option: MODS.ALT,\n '⌃': MODS.CTRL,\n ctrl: MODS.CTRL,\n control: MODS.CTRL,\n '⌘': MODS.META,\n cmd: MODS.META,\n command: MODS.META,\n};\n\n// Modern key mapping using KeyboardEvent.key values\nexport const SPECIAL: { [key: string]: string } = {\n backspace: 'Backspace',\n tab: 'Tab',\n clear: 'Clear',\n enter: 'Enter',\n return: 'Enter',\n esc: 'Escape',\n escape: 'Escape',\n space: ' ',\n left: 'ArrowLeft',\n up: 'ArrowUp',\n right: 'ArrowRight',\n down: 'ArrowDown',\n del: 'Delete',\n delete: 'Delete',\n home: 'Home',\n end: 'End',\n pageup: 'PageUp',\n pagedown: 'PageDown',\n comma: ',',\n '.': '.',\n '/': '/',\n '`': '`',\n '-': '-',\n '=': '=',\n ';': ';',\n \"'\": \"'\",\n '[': '[',\n ']': ']',\n '\\\\': '\\\\',\n // Normalize Meta key variants\n 'Meta': 'Meta',\n 'MetaLeft': 'Meta',\n 'MetaRight': 'Meta', \n 'OS': 'Meta', // Some browsers use OS instead of Meta\n 'ContextMenu': 'Meta', // Right-click context menu key sometimes acts as Meta\n // Add identity mappings for already-normalized keys\n 'ArrowLeft': 'ArrowLeft',\n 'ArrowUp': 'ArrowUp',\n 'ArrowRight': 'ArrowRight',\n 'ArrowDown': 'ArrowDown',\n 'Backspace': 'Backspace',\n 'Tab': 'Tab',\n 'Clear': 'Clear',\n 'Enter': 'Enter',\n 'Escape': 'Escape',\n 'Delete': 'Delete',\n 'Home': 'Home',\n 'End': 'End',\n 'PageUp': 'PageUp',\n 'PageDown': 'PageDown',\n};\n\nexport const CAPS_LOCK_KEY = 'CapsLock';\n","import { KeyString, MODS, SPECIAL } from '../constants';\n\nexport const getKeyIdentifier = (key: string): KeyString => {\n return (SPECIAL[key] || key.toUpperCase()) as KeyString;\n};\n\nexport const updateModifiers = (e: KeyboardEvent): number => {\n let modifiers = 0;\n if (e.shiftKey) modifiers |= MODS.SHIFT;\n if (e.altKey) modifiers |= MODS.ALT;\n if (e.ctrlKey) modifiers |= MODS.CTRL;\n if (e.metaKey) modifiers |= MODS.META;\n return modifiers;\n};\n","import { KeyString, MODIFIERS, ModifierNames, SPECIAL } from '../constants';\n\nexport interface ParsedShortcut {\n mods: number; // Bitwise flag for modifiers\n special: string[];\n}\nexport interface KeyMap {\n key: KeyString;\n shortcut: ParsedShortcut;\n}\n\nexport const getKeyIdentifier = (key: string): KeyString =>\n (SPECIAL[key] || key.toUpperCase()) as KeyString;\n\nconst getMods = (keys: string[]): ParsedShortcut =>\n keys.reduce(\n (acc, key) => {\n if (key in MODIFIERS) {\n acc.mods |= MODIFIERS[key as keyof ModifierNames];\n } else {\n acc.special.push(SPECIAL[key] || key.toUpperCase());\n }\n return acc;\n },\n {\n mods: 0, // Start with no modifiers\n special: [],\n } as ParsedShortcut,\n );\n\nconst getCombinations = (keysStr: string): string[] => {\n const cleanKeys = keysStr.replace(/\\s/g, '');\n const keys = cleanKeys.split(',');\n if (keys[keys.length - 1] === '') {\n keys[keys.length - 2] += ',';\n }\n\n return keys;\n};\n\nexport const getKeyMap = (keysStr: string): KeyMap[] => {\n const keymap = getCombinations(keysStr);\n return keymap.map((keyCmd) => {\n const keys = keyCmd.split('+');\n const key = keys[keys.length - 1];\n const keyIdentifier = getKeyIdentifier(key);\n\n return {\n key: keyIdentifier,\n shortcut: getMods(keys),\n };\n });\n};\n","export const invariant = (\n condition: boolean,\n message: string,\n ...args: unknown[]\n) => {\n if (\n typeof process !== 'undefined' &&\n process.env &&\n process.env.NODE_ENV === 'development' &&\n !condition\n ) {\n throw new Error(\n `Invariant failed: ${message}${args.length ? ` ${JSON.stringify(args)}` : ''}`,\n );\n }\n};\n\nexport const isFirefox = navigator.userAgent.includes('Firefox');\n\nexport const isEditable = (el: HTMLElement): boolean =>\n el.isContentEditable ||\n el.tagName === 'INPUT' ||\n el.tagName === 'SELECT' ||\n el.tagName === 'TEXTAREA';\n\nexport const isEqArray = (\n arr1: (string | number)[],\n arr2: (string | number)[],\n): boolean => {\n if (arr1.length !== arr2.length) return false;\n\n for (let i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) return false;\n }\n\n return true;\n};\n","import { CAPS_LOCK_KEY, DEFAULT_SCOPE, KeyString } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype noop = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: noop;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\n// WeakMap to track event listener references per document to prevent memory leaks\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n }\n>();\n\nexport function createKeybuddy(\n doc: Document,\n filterFn: FilterFn = defaultFilter,\n) {\n let handlers: { [key: KeyString]: Handler[] } = {};\n const downKeys: Set<KeyString> = new Set();\n let activeScope = DEFAULT_SCOPE;\n\n let modifiers = 0; // Bitwise flag for active modifiers\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n {\n skipOther,\n }: {\n skipOther: boolean;\n } = {\n skipOther: false,\n },\n ): void => {\n const scope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const method: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n if (!handlers[key]) {\n handlers[key] = [];\n }\n const handler = handlers[key];\n if (process.env.NODE_ENV === 'development') {\n if (skipOther) {\n const action = handler.find((i) => i.skipOther);\n invariant(\n !action,\n \"Conflicting 'skipOther' property with action\",\n action,\n );\n }\n }\n\n handler.push({\n scope,\n method,\n shortcut,\n skipOther,\n });\n });\n };\n\n const unbindKeyProcess = (\n keysStr: string,\n deleteMethod: null | noop,\n deleteScope: string = DEFAULT_SCOPE,\n ): void => {\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n const handler = handlers[key];\n if (Array.isArray(handler)) {\n const handler = handlers[key].filter(\n ({ scope, method, shortcut: methodShortcut }: Handler) =>\n !(\n scope === deleteScope &&\n methodShortcut.mods === shortcut.mods &&\n isEqArray(methodShortcut.special, shortcut.special) &&\n (deleteMethod === null ? true : method === deleteMethod)\n ),\n );\n if (handler.length) {\n handlers[key] = handler;\n } else {\n delete handlers[key];\n }\n }\n });\n };\n\n const unbindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n return unbindKeyProcess(keysStr, deleteMethod, deleteScope);\n };\n\n const unsafeUnbindKey = (keysStr: string, scope?: string) =>\n unbindKeyProcess(keysStr, null, scope);\n\n const dispatch = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n if (!filterFn(e)) {\n return;\n }\n\n // fix firefox behavior when caps lock fires three times onkeydown\n // and don't fire at all onkeyup (Firefox 72)\n if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\n // Check if key is not a modifier\n const isModifierKey =\n key === ('SHIFT' as KeyString) ||\n key === ('ALT' as KeyString) ||\n key === ('CTRL' as KeyString) ||\n key === ('META' as KeyString);\n if (!isModifierKey && !downKeys.has(key)) {\n downKeys.add(key);\n }\n // See if we need to ignore the keypress (filter() can can be overridden)\n // by default ignore key presses if a select, textarea, or input is focused\n // if (!assignKey.filter.call(this, event)) return;\n\n // abort if no potentially matching shortcuts found\n if (!(key in handlers)) {\n return;\n }\n\n const currentHandlers = handlers[key].filter(\n ({ scope, shortcut: { special, mods } }) => {\n if (scope !== activeScope) {\n return false;\n }\n\n return isEqArray(special, Array.from(downKeys)) && mods === modifiers;\n },\n );\n\n const primaryAction: Handler | undefined = currentHandlers.find(\n (action) => action.skipOther,\n );\n if (primaryAction) {\n primaryAction.method(e);\n } else {\n currentHandlers.forEach(({ method }) => {\n method(e);\n });\n }\n };\n\n const cleanUp = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n // clean all for meta.\n // Main reason is ctrl+z (or any other native command not fires letter keyup on editable inputs)\n if (e.key && e.key.toLowerCase() === 'meta') {\n downKeys.clear();\n } else {\n downKeys.delete(key);\n }\n };\n\n const unbindScope = (deleteScope: string): void => {\n Object.keys(handlers).forEach((key) => {\n const keyString = key as KeyString;\n const handler = handlers[keyString].filter(\n ({ scope }: Handler) => scope !== deleteScope,\n );\n if (handler.length) {\n handlers[keyString] = handler;\n } else {\n delete handlers[keyString];\n }\n });\n };\n\n const setScope = (scope: string): void => {\n activeScope = scope;\n };\n\n const unbindAll = (): void => {\n handlers = {};\n downKeys.clear();\n };\n\n const reset = (): void => {\n downKeys.clear();\n };\n\n const destroy = (): void => {\n downKeys.clear();\n handlers = {};\n if (doc) {\n const listeners = documentListeners.get(doc);\n if (listeners) {\n doc.removeEventListener('keydown', listeners.dispatch);\n doc.removeEventListener('keyup', listeners.cleanUp);\n window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n // Remove old listeners if they exist\n destroy();\n\n // Store and add new listeners\n documentListeners.set(doc, { dispatch, cleanUp, reset });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n window.addEventListener('focus', reset);\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope: () => activeScope,\n destroy,\n };\n}\n"],"mappings":"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","names":["src_exports","__export","DEFAULT_SCOPE","bindKey","src_default","destroy","getScope","setScope","unbindAll","unbindKey","unbindScope","unsafeUnbindKey","DEFAULT_SCOPE","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getKeyIdentifier","key","SPECIAL","getMods","keys","acc","MODIFIERS","getCombinations","keysStr","getKeyMap","keyCmd","isFirefox","isEditable","el","isEqArray","arr1","arr2","i","defaultFilter","isEditable","documentListeners","createKeybuddy","doc","filterFn","handlers","downKeys","activeScope","modifiers","bindKey","keysStr","scopeOrMethod","methodOrNull","skipOther","scope","method","getKeyMap","key","shortcut","unbindKeyProcess","deleteMethod","deleteScope","handler","methodShortcut","isEqArray","unbindKey","unsafeUnbindKey","dispatch","e","getKeyIdentifier","isFirefox","CAPS_LOCK_KEY","updateModifiers","currentHandlers","special","mods","primaryAction","action","cleanUp","unbindScope","keyString","setScope","unbindAll","reset","destroy","listeners","bind","unbind","unsafeUnbind","getScope","setScope","unbindScope","unbindAll","destroy","createKeybuddy","bindKey","unbindKey","unsafeUnbindKey","src_default","bind"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/helpers/keyboard.ts","../src/helpers/keymap.ts","../src/helpers/utils.ts","../src/keybuddy.ts"],"sourcesContent":["import { DEFAULT_SCOPE } from './constants';\nimport { createKeybuddy } from './keybuddy';\n\nexport { createKeybuddy };\n\nconst {\n bind,\n unbind,\n unsafeUnbind,\n getScope,\n setScope,\n unbindScope,\n unbindAll,\n destroy,\n} = createKeybuddy(document);\n\nexport const bindKey = bind;\nexport const unbindKey = unbind;\nexport const unsafeUnbindKey = unsafeUnbind;\nexport { setScope, unbindScope, unbindAll, getScope, destroy, DEFAULT_SCOPE };\nexport type { KeyString } from './constants';\nexport { MODS, SPECIAL } from './constants';\nexport default bind;\n","\nexport const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\n// Bitwise flags for modifiers - much faster than object-based tracking\nexport const MODS = {\n SHIFT: 0b0001, // 1\n ALT: 0b0010, // 2\n CTRL: 0b0100, // 4\n META: 0b1000, // 8\n} as const;\n\n// Map string modifier names to bitwise flags for parsing\nexport type ModifierNames = {\n '⇧': number;\n shift: number;\n '⌥': number;\n alt: number;\n option: number;\n '⌃': number;\n ctrl: number;\n control: number;\n '⌘': number;\n cmd: number;\n command: number;\n};\n\nexport const MODIFIERS: ModifierNames = {\n '⇧': MODS.SHIFT,\n shift: MODS.SHIFT,\n '⌥': MODS.ALT,\n alt: MODS.ALT,\n option: MODS.ALT,\n '⌃': MODS.CTRL,\n ctrl: MODS.CTRL,\n control: MODS.CTRL,\n '⌘': MODS.META,\n cmd: MODS.META,\n command: MODS.META,\n};\n\n// Modern key mapping using KeyboardEvent.key values\nexport const SPECIAL: { [key: string]: string } = {\n backspace: 'Backspace',\n tab: 'Tab',\n clear: 'Clear',\n enter: 'Enter',\n return: 'Enter',\n esc: 'Escape',\n escape: 'Escape',\n space: ' ',\n left: 'ArrowLeft',\n up: 'ArrowUp',\n right: 'ArrowRight',\n down: 'ArrowDown',\n del: 'Delete',\n delete: 'Delete',\n home: 'Home',\n end: 'End',\n pageup: 'PageUp',\n pagedown: 'PageDown',\n comma: ',',\n '.': '.',\n '/': '/',\n '`': '`',\n '-': '-',\n '=': '=',\n ';': ';',\n \"'\": \"'\",\n '[': '[',\n ']': ']',\n '\\\\': '\\\\',\n // Normalize Meta key variants\n 'Meta': 'Meta',\n 'MetaLeft': 'Meta',\n 'MetaRight': 'Meta', \n 'OS': 'Meta', // Some browsers use OS instead of Meta\n 'ContextMenu': 'Meta', // Right-click context menu key sometimes acts as Meta\n // Add identity mappings for already-normalized keys\n 'ArrowLeft': 'ArrowLeft',\n 'ArrowUp': 'ArrowUp',\n 'ArrowRight': 'ArrowRight',\n 'ArrowDown': 'ArrowDown',\n 'Backspace': 'Backspace',\n 'Tab': 'Tab',\n 'Clear': 'Clear',\n 'Enter': 'Enter',\n 'Escape': 'Escape',\n 'Delete': 'Delete',\n 'Home': 'Home',\n 'End': 'End',\n 'PageUp': 'PageUp',\n 'PageDown': 'PageDown',\n};\n\nexport const CAPS_LOCK_KEY = 'CapsLock';\n","import { KeyString, MODS, SPECIAL } from '../constants';\n\nexport const getKeyIdentifier = (key: string): KeyString => {\n return (SPECIAL[key] || key.toUpperCase()) as KeyString;\n};\n\nexport const updateModifiers = (e: KeyboardEvent): number => {\n let modifiers = 0;\n if (e.shiftKey) modifiers |= MODS.SHIFT;\n if (e.altKey) modifiers |= MODS.ALT;\n if (e.ctrlKey) modifiers |= MODS.CTRL;\n if (e.metaKey) modifiers |= MODS.META;\n return modifiers;\n};\n","import { KeyString, MODIFIERS, ModifierNames, SPECIAL } from '../constants';\n\nexport interface ParsedShortcut {\n mods: number; // Bitwise flag for modifiers\n special: string[];\n}\nexport interface KeyMap {\n key: KeyString;\n shortcut: ParsedShortcut;\n}\n\nexport const getKeyIdentifier = (key: string): KeyString =>\n (SPECIAL[key] || key.toUpperCase()) as KeyString;\n\nconst getMods = (keys: string[]): ParsedShortcut =>\n keys.reduce(\n (acc, key) => {\n if (key in MODIFIERS) {\n acc.mods |= MODIFIERS[key as keyof ModifierNames];\n } else {\n acc.special.push(SPECIAL[key] || key.toUpperCase());\n }\n return acc;\n },\n {\n mods: 0, // Start with no modifiers\n special: [],\n } as ParsedShortcut,\n );\n\nconst getCombinations = (keysStr: string): string[] => {\n const cleanKeys = keysStr.replace(/\\s/g, '');\n const keys = cleanKeys.split(',');\n if (keys[keys.length - 1] === '') {\n keys[keys.length - 2] += ',';\n }\n\n return keys;\n};\n\nexport const getKeyMap = (keysStr: string): KeyMap[] => {\n const keymap = getCombinations(keysStr);\n return keymap.map((keyCmd) => {\n const keys = keyCmd.split('+');\n const key = keys[keys.length - 1];\n const keyIdentifier = getKeyIdentifier(key);\n\n return {\n key: keyIdentifier,\n shortcut: getMods(keys),\n };\n });\n};\n","export const invariant = (\n condition: boolean,\n message: string,\n ...args: unknown[]\n) => {\n if (\n typeof process !== 'undefined' &&\n process.env &&\n process.env.NODE_ENV === 'development' &&\n !condition\n ) {\n throw new Error(\n `Invariant failed: ${message}${args.length ? ` ${JSON.stringify(args)}` : ''}`,\n );\n }\n};\n\nexport const isFirefox = navigator.userAgent.includes('Firefox');\n\nexport const isEditable = (el: HTMLElement): boolean =>\n el.isContentEditable ||\n el.tagName === 'INPUT' ||\n el.tagName === 'SELECT' ||\n el.tagName === 'TEXTAREA';\n\nexport const isEqArray = (\n arr1: (string | number)[],\n arr2: (string | number)[],\n): boolean => {\n if (arr1.length !== arr2.length) return false;\n\n for (let i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) return false;\n }\n\n return true;\n};\n","import { CAPS_LOCK_KEY, DEFAULT_SCOPE, KeyString } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype noop = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: noop;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\n// WeakMap to track event listener references per document to prevent memory leaks\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n }\n>();\n\nexport function createKeybuddy(\n doc: Document,\n filterFn: FilterFn = defaultFilter,\n) {\n let handlers: { [key: KeyString]: Handler[] } = {};\n const downKeys: Set<KeyString> = new Set();\n let activeScope = DEFAULT_SCOPE;\n\n let modifiers = 0; // Bitwise flag for active modifiers\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n {\n skipOther,\n }: {\n skipOther: boolean;\n } = {\n skipOther: false,\n },\n ): void => {\n const scope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const method: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n if (!handlers[key]) {\n handlers[key] = [];\n }\n const handler = handlers[key];\n if (process.env.NODE_ENV === 'development') {\n if (skipOther) {\n const action = handler.find((i) => i.skipOther);\n invariant(\n !action,\n \"Conflicting 'skipOther' property with action\",\n action,\n );\n }\n }\n\n handler.push({\n scope,\n method,\n shortcut,\n skipOther,\n });\n });\n };\n\n const unbindKeyProcess = (\n keysStr: string,\n deleteMethod: null | noop,\n deleteScope: string = DEFAULT_SCOPE,\n ): void => {\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n const handler = handlers[key];\n if (Array.isArray(handler)) {\n const handler = handlers[key].filter(\n ({ scope, method, shortcut: methodShortcut }: Handler) =>\n !(\n scope === deleteScope &&\n methodShortcut.mods === shortcut.mods &&\n isEqArray(methodShortcut.special, shortcut.special) &&\n (deleteMethod === null ? true : method === deleteMethod)\n ),\n );\n if (handler.length) {\n handlers[key] = handler;\n } else {\n delete handlers[key];\n }\n }\n });\n };\n\n const unbindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n return unbindKeyProcess(keysStr, deleteMethod, deleteScope);\n };\n\n const unsafeUnbindKey = (keysStr: string, scope?: string) =>\n unbindKeyProcess(keysStr, null, scope);\n\n const dispatch = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n if (!filterFn(e)) {\n return;\n }\n\n // fix firefox behavior when caps lock fires three times onkeydown\n // and don't fire at all onkeyup (Firefox 72)\n if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\n // Check if key is not a modifier\n const isModifierKey =\n key === ('SHIFT' as KeyString) ||\n key === ('ALT' as KeyString) ||\n key === ('CTRL' as KeyString) ||\n key === ('META' as KeyString);\n if (!isModifierKey && !downKeys.has(key)) {\n downKeys.add(key);\n }\n // See if we need to ignore the keypress (filter() can can be overridden)\n // by default ignore key presses if a select, textarea, or input is focused\n // if (!assignKey.filter.call(this, event)) return;\n\n // abort if no potentially matching shortcuts found\n if (!(key in handlers)) {\n return;\n }\n\n const currentHandlers = handlers[key].filter(\n ({ scope, shortcut: { special, mods } }) => {\n if (scope !== activeScope) {\n return false;\n }\n\n return isEqArray(special, Array.from(downKeys)) && mods === modifiers;\n },\n );\n\n const primaryAction: Handler | undefined = currentHandlers.find(\n (action) => action.skipOther,\n );\n if (primaryAction) {\n primaryAction.method(e);\n } else {\n currentHandlers.forEach(({ method }) => {\n method(e);\n });\n }\n };\n\n const cleanUp = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n // clean all for meta.\n // Main reason is ctrl+z (or any other native command not fires letter keyup on editable inputs)\n if (e.key && e.key.toLowerCase() === 'meta') {\n downKeys.clear();\n } else {\n downKeys.delete(key);\n }\n };\n\n const unbindScope = (deleteScope: string): void => {\n Object.keys(handlers).forEach((key) => {\n const keyString = key as KeyString;\n const handler = handlers[keyString].filter(\n ({ scope }: Handler) => scope !== deleteScope,\n );\n if (handler.length) {\n handlers[keyString] = handler;\n } else {\n delete handlers[keyString];\n }\n });\n };\n\n const setScope = (scope: string): void => {\n activeScope = scope;\n };\n\n const unbindAll = (): void => {\n handlers = {};\n downKeys.clear();\n };\n\n const reset = (): void => {\n downKeys.clear();\n };\n\n const destroy = (): void => {\n downKeys.clear();\n handlers = {};\n if (doc) {\n const listeners = documentListeners.get(doc);\n if (listeners) {\n doc.removeEventListener('keydown', listeners.dispatch);\n doc.removeEventListener('keyup', listeners.cleanUp);\n window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n // Remove old listeners if they exist\n destroy();\n\n // Store and add new listeners\n documentListeners.set(doc, { dispatch, cleanUp, reset });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n window.addEventListener('focus', reset);\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope: () => activeScope,\n destroy,\n };\n}\n"],"mappings":"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","names":["src_exports","__export","DEFAULT_SCOPE","MODS","SPECIAL","bindKey","createKeybuddy","src_default","destroy","getScope","setScope","unbindAll","unbindKey","unbindScope","unsafeUnbindKey","DEFAULT_SCOPE","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getKeyIdentifier","key","SPECIAL","getMods","keys","acc","MODIFIERS","getCombinations","keysStr","getKeyMap","keyCmd","isFirefox","isEditable","el","isEqArray","arr1","arr2","i","defaultFilter","isEditable","documentListeners","createKeybuddy","doc","filterFn","handlers","downKeys","activeScope","modifiers","bindKey","keysStr","scopeOrMethod","methodOrNull","skipOther","scope","method","getKeyMap","key","shortcut","unbindKeyProcess","deleteMethod","deleteScope","handler","methodShortcut","isEqArray","unbindKey","unsafeUnbindKey","dispatch","e","getKeyIdentifier","isFirefox","CAPS_LOCK_KEY","updateModifiers","currentHandlers","special","mods","primaryAction","action","cleanUp","unbindScope","keyString","setScope","unbindAll","reset","destroy","listeners","bind","unbind","unsafeUnbind","getScope","setScope","unbindScope","unbindAll","destroy","createKeybuddy","bindKey","unbindKey","unsafeUnbindKey","src_default","bind"]}
|
package/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/* keybuddy - Modern keyboard shortcuts library */
|
|
2
|
-
"use strict";var
|
|
2
|
+
"use strict";var b=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var Y=(e,r)=>{for(var t in r)b(e,t,{get:r[t],enumerable:!0})},j=(e,r,t,s)=>{if(r&&typeof r=="object"||typeof r=="function")for(let c of $(r))!V.call(e,c)&&c!==t&&b(e,c,{get:()=>r[c],enumerable:!(s=q(r,c))||s.enumerable});return e};var J=e=>j(b({},"__esModule",{value:!0}),e);var de={};Y(de,{DEFAULT_SCOPE:()=>u,MODS:()=>i,SPECIAL:()=>g,bindKey:()=>ie,createKeybuddy:()=>L,default:()=>ce,destroy:()=>oe,getScope:()=>ee,setScope:()=>te,unbindAll:()=>re,unbindKey:()=>se,unbindScope:()=>ne,unsafeUnbindKey:()=>ae});module.exports=J(de);var u="all",i={SHIFT:1,ALT:2,CTRL:4,META:8},S={"\u21E7":i.SHIFT,shift:i.SHIFT,"\u2325":i.ALT,alt:i.ALT,option:i.ALT,"\u2303":i.CTRL,ctrl:i.CTRL,control:i.CTRL,"\u2318":i.META,cmd:i.META,command:i.META},g={backspace:"Backspace",tab:"Tab",clear:"Clear",enter:"Enter",return:"Enter",esc:"Escape",escape:"Escape",space:" ",left:"ArrowLeft",up:"ArrowUp",right:"ArrowRight",down:"ArrowDown",del:"Delete",delete:"Delete",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",comma:",",".":".","/":"/","`":"`","-":"-","=":"=",";":";","'":"'","[":"[","]":"]","\\":"\\",Meta:"Meta",MetaLeft:"Meta",MetaRight:"Meta",OS:"Meta",ContextMenu:"Meta",ArrowLeft:"ArrowLeft",ArrowUp:"ArrowUp",ArrowRight:"ArrowRight",ArrowDown:"ArrowDown",Backspace:"Backspace",Tab:"Tab",Clear:"Clear",Enter:"Enter",Escape:"Escape",Delete:"Delete",Home:"Home",End:"End",PageUp:"PageUp",PageDown:"PageDown"},P="CapsLock";var K=e=>g[e]||e.toUpperCase(),D=e=>{let r=0;return e.shiftKey&&(r|=i.SHIFT),e.altKey&&(r|=i.ALT),e.ctrlKey&&(r|=i.CTRL),e.metaKey&&(r|=i.META),r};var W=e=>g[e]||e.toUpperCase(),X=e=>e.reduce((r,t)=>(t in S?r.mods|=S[t]:r.special.push(g[t]||t.toUpperCase()),r),{mods:0,special:[]}),z=e=>{let t=e.replace(/\s/g,"").split(",");return t[t.length-1]===""&&(t[t.length-2]+=","),t},h=e=>z(e).map(t=>{let s=t.split("+"),c=s[s.length-1];return{key:W(c),shortcut:X(s)}});var I=navigator.userAgent.includes("Firefox"),U=e=>e.isContentEditable||e.tagName==="INPUT"||e.tagName==="SELECT"||e.tagName==="TEXTAREA",A=(e,r)=>{if(e.length!==r.length)return!1;for(let t=0;t<e.length;t++)if(e[t]!==r[t])return!1;return!0};var G=e=>e&&!U(e.target),v=new WeakMap;function L(e,r=G){let t={},s=new Set,c="all",m=0,H=(o,n,d=()=>{},{skipOther:a}={skipOther:!1})=>{let p=typeof n=="function"?"all":n,l=typeof n=="function"?n:d;h(o).forEach(({key:f,shortcut:y})=>{t[f]||(t[f]=[]),t[f].push({scope:p,method:l,shortcut:y,skipOther:a})})},T=(o,n,d="all")=>{h(o).forEach(({key:a,shortcut:p})=>{let l=t[a];if(Array.isArray(l)){let f=t[a].filter(({scope:y,method:k,shortcut:E})=>!(y===d&&E.mods===p.mods&&A(E.special,p.special)&&(n===null||k===n)));f.length?t[a]=f:delete t[a]}})},O=(o,n,d=()=>{})=>T(o,typeof n=="function"?n:d,typeof n=="function"?"all":n),N=(o,n)=>T(o,null,n),w=o=>{let n=K(o.key);if(!r(o)||I&&n===P||(m=D(o),!(n==="SHIFT"||n==="ALT"||n==="CTRL"||n==="META")&&!s.has(n)&&s.add(n),!(n in t)))return;let a=t[n].filter(({scope:l,shortcut:{special:f,mods:y}})=>l!==c?!1:A(f,Array.from(s))&&y===m),p=a.find(l=>l.skipOther);p?p.method(o):a.forEach(({method:l})=>{l(o)})},M=o=>{let n=K(o.key);o.key&&o.key.toLowerCase()==="meta"?s.clear():s.delete(n)},R=o=>{Object.keys(t).forEach(n=>{let d=n,a=t[d].filter(({scope:p})=>p!==o);a.length?t[d]=a:delete t[d]})},_=o=>{c=o},B=()=>{t={},s.clear()},x=()=>{s.clear()},C=()=>{if(s.clear(),t={},e){let o=v.get(e);o&&(e.removeEventListener("keydown",o.dispatch),e.removeEventListener("keyup",o.cleanUp),window.removeEventListener("focus",o.reset),v.delete(e))}};return C(),v.set(e,{dispatch:w,cleanUp:M,reset:x}),e.addEventListener("keydown",w),e.addEventListener("keyup",M),window.addEventListener("focus",x),{bind:H,unbind:O,unsafeUnbind:N,unbindScope:R,setScope:_,unbindAll:B,getScope:()=>c,destroy:C}}var{bind:F,unbind:Q,unsafeUnbind:Z,getScope:ee,setScope:te,unbindScope:ne,unbindAll:re,destroy:oe}=L(document),ie=F,se=Q,ae=Z;var ce=F;0&&(module.exports={DEFAULT_SCOPE,MODS,SPECIAL,bindKey,createKeybuddy,destroy,getScope,setScope,unbindAll,unbindKey,unbindScope,unsafeUnbindKey});
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/helpers/keyboard.ts","../src/helpers/keymap.ts","../src/helpers/utils.ts","../src/keybuddy.ts"],"sourcesContent":["import { DEFAULT_SCOPE } from './constants';\nimport { createKeybuddy } from './keybuddy';\n\nconst {\n bind,\n unbind,\n unsafeUnbind,\n getScope,\n setScope,\n unbindScope,\n unbindAll,\n destroy,\n} = createKeybuddy(document);\n\nexport const bindKey = bind;\nexport const unbindKey = unbind;\nexport const unsafeUnbindKey = unsafeUnbind;\nexport { setScope, unbindScope, unbindAll, getScope, destroy, DEFAULT_SCOPE };\nexport type { KeyString } from './constants';\nexport default bind;\n","\nexport const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\n// Bitwise flags for modifiers - much faster than object-based tracking\nexport const MODS = {\n SHIFT: 0b0001, // 1\n ALT: 0b0010, // 2\n CTRL: 0b0100, // 4\n META: 0b1000, // 8\n} as const;\n\n// Map string modifier names to bitwise flags for parsing\nexport type ModifierNames = {\n '⇧': number;\n shift: number;\n '⌥': number;\n alt: number;\n option: number;\n '⌃': number;\n ctrl: number;\n control: number;\n '⌘': number;\n cmd: number;\n command: number;\n};\n\nexport const MODIFIERS: ModifierNames = {\n '⇧': MODS.SHIFT,\n shift: MODS.SHIFT,\n '⌥': MODS.ALT,\n alt: MODS.ALT,\n option: MODS.ALT,\n '⌃': MODS.CTRL,\n ctrl: MODS.CTRL,\n control: MODS.CTRL,\n '⌘': MODS.META,\n cmd: MODS.META,\n command: MODS.META,\n};\n\n// Modern key mapping using KeyboardEvent.key values\nexport const SPECIAL: { [key: string]: string } = {\n backspace: 'Backspace',\n tab: 'Tab',\n clear: 'Clear',\n enter: 'Enter',\n return: 'Enter',\n esc: 'Escape',\n escape: 'Escape',\n space: ' ',\n left: 'ArrowLeft',\n up: 'ArrowUp',\n right: 'ArrowRight',\n down: 'ArrowDown',\n del: 'Delete',\n delete: 'Delete',\n home: 'Home',\n end: 'End',\n pageup: 'PageUp',\n pagedown: 'PageDown',\n comma: ',',\n '.': '.',\n '/': '/',\n '`': '`',\n '-': '-',\n '=': '=',\n ';': ';',\n \"'\": \"'\",\n '[': '[',\n ']': ']',\n '\\\\': '\\\\',\n // Normalize Meta key variants\n 'Meta': 'Meta',\n 'MetaLeft': 'Meta',\n 'MetaRight': 'Meta', \n 'OS': 'Meta', // Some browsers use OS instead of Meta\n 'ContextMenu': 'Meta', // Right-click context menu key sometimes acts as Meta\n // Add identity mappings for already-normalized keys\n 'ArrowLeft': 'ArrowLeft',\n 'ArrowUp': 'ArrowUp',\n 'ArrowRight': 'ArrowRight',\n 'ArrowDown': 'ArrowDown',\n 'Backspace': 'Backspace',\n 'Tab': 'Tab',\n 'Clear': 'Clear',\n 'Enter': 'Enter',\n 'Escape': 'Escape',\n 'Delete': 'Delete',\n 'Home': 'Home',\n 'End': 'End',\n 'PageUp': 'PageUp',\n 'PageDown': 'PageDown',\n};\n\nexport const CAPS_LOCK_KEY = 'CapsLock';\n","import { KeyString, MODS, SPECIAL } from '../constants';\n\nexport const getKeyIdentifier = (key: string): KeyString => {\n return (SPECIAL[key] || key.toUpperCase()) as KeyString;\n};\n\nexport const updateModifiers = (e: KeyboardEvent): number => {\n let modifiers = 0;\n if (e.shiftKey) modifiers |= MODS.SHIFT;\n if (e.altKey) modifiers |= MODS.ALT;\n if (e.ctrlKey) modifiers |= MODS.CTRL;\n if (e.metaKey) modifiers |= MODS.META;\n return modifiers;\n};\n","import { KeyString, MODIFIERS, ModifierNames, SPECIAL } from '../constants';\n\nexport interface ParsedShortcut {\n mods: number; // Bitwise flag for modifiers\n special: string[];\n}\nexport interface KeyMap {\n key: KeyString;\n shortcut: ParsedShortcut;\n}\n\nexport const getKeyIdentifier = (key: string): KeyString =>\n (SPECIAL[key] || key.toUpperCase()) as KeyString;\n\nconst getMods = (keys: string[]): ParsedShortcut =>\n keys.reduce(\n (acc, key) => {\n if (key in MODIFIERS) {\n acc.mods |= MODIFIERS[key as keyof ModifierNames];\n } else {\n acc.special.push(SPECIAL[key] || key.toUpperCase());\n }\n return acc;\n },\n {\n mods: 0, // Start with no modifiers\n special: [],\n } as ParsedShortcut,\n );\n\nconst getCombinations = (keysStr: string): string[] => {\n const cleanKeys = keysStr.replace(/\\s/g, '');\n const keys = cleanKeys.split(',');\n if (keys[keys.length - 1] === '') {\n keys[keys.length - 2] += ',';\n }\n\n return keys;\n};\n\nexport const getKeyMap = (keysStr: string): KeyMap[] => {\n const keymap = getCombinations(keysStr);\n return keymap.map((keyCmd) => {\n const keys = keyCmd.split('+');\n const key = keys[keys.length - 1];\n const keyIdentifier = getKeyIdentifier(key);\n\n return {\n key: keyIdentifier,\n shortcut: getMods(keys),\n };\n });\n};\n","export const invariant = (\n condition: boolean,\n message: string,\n ...args: unknown[]\n) => {\n if (\n typeof process !== 'undefined' &&\n process.env &&\n process.env.NODE_ENV === 'development' &&\n !condition\n ) {\n throw new Error(\n `Invariant failed: ${message}${args.length ? ` ${JSON.stringify(args)}` : ''}`,\n );\n }\n};\n\nexport const isFirefox = navigator.userAgent.includes('Firefox');\n\nexport const isEditable = (el: HTMLElement): boolean =>\n el.isContentEditable ||\n el.tagName === 'INPUT' ||\n el.tagName === 'SELECT' ||\n el.tagName === 'TEXTAREA';\n\nexport const isEqArray = (\n arr1: (string | number)[],\n arr2: (string | number)[],\n): boolean => {\n if (arr1.length !== arr2.length) return false;\n\n for (let i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) return false;\n }\n\n return true;\n};\n","import { CAPS_LOCK_KEY, DEFAULT_SCOPE, KeyString } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype noop = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: noop;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\n// WeakMap to track event listener references per document to prevent memory leaks\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n }\n>();\n\nexport function createKeybuddy(\n doc: Document,\n filterFn: FilterFn = defaultFilter,\n) {\n let handlers: { [key: KeyString]: Handler[] } = {};\n const downKeys: Set<KeyString> = new Set();\n let activeScope = DEFAULT_SCOPE;\n\n let modifiers = 0; // Bitwise flag for active modifiers\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n {\n skipOther,\n }: {\n skipOther: boolean;\n } = {\n skipOther: false,\n },\n ): void => {\n const scope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const method: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n if (!handlers[key]) {\n handlers[key] = [];\n }\n const handler = handlers[key];\n if (process.env.NODE_ENV === 'development') {\n if (skipOther) {\n const action = handler.find((i) => i.skipOther);\n invariant(\n !action,\n \"Conflicting 'skipOther' property with action\",\n action,\n );\n }\n }\n\n handler.push({\n scope,\n method,\n shortcut,\n skipOther,\n });\n });\n };\n\n const unbindKeyProcess = (\n keysStr: string,\n deleteMethod: null | noop,\n deleteScope: string = DEFAULT_SCOPE,\n ): void => {\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n const handler = handlers[key];\n if (Array.isArray(handler)) {\n const handler = handlers[key].filter(\n ({ scope, method, shortcut: methodShortcut }: Handler) =>\n !(\n scope === deleteScope &&\n methodShortcut.mods === shortcut.mods &&\n isEqArray(methodShortcut.special, shortcut.special) &&\n (deleteMethod === null ? true : method === deleteMethod)\n ),\n );\n if (handler.length) {\n handlers[key] = handler;\n } else {\n delete handlers[key];\n }\n }\n });\n };\n\n const unbindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n return unbindKeyProcess(keysStr, deleteMethod, deleteScope);\n };\n\n const unsafeUnbindKey = (keysStr: string, scope?: string) =>\n unbindKeyProcess(keysStr, null, scope);\n\n const dispatch = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n if (!filterFn(e)) {\n return;\n }\n\n // fix firefox behavior when caps lock fires three times onkeydown\n // and don't fire at all onkeyup (Firefox 72)\n if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\n // Check if key is not a modifier\n const isModifierKey =\n key === ('SHIFT' as KeyString) ||\n key === ('ALT' as KeyString) ||\n key === ('CTRL' as KeyString) ||\n key === ('META' as KeyString);\n if (!isModifierKey && !downKeys.has(key)) {\n downKeys.add(key);\n }\n // See if we need to ignore the keypress (filter() can can be overridden)\n // by default ignore key presses if a select, textarea, or input is focused\n // if (!assignKey.filter.call(this, event)) return;\n\n // abort if no potentially matching shortcuts found\n if (!(key in handlers)) {\n return;\n }\n\n const currentHandlers = handlers[key].filter(\n ({ scope, shortcut: { special, mods } }) => {\n if (scope !== activeScope) {\n return false;\n }\n\n return isEqArray(special, Array.from(downKeys)) && mods === modifiers;\n },\n );\n\n const primaryAction: Handler | undefined = currentHandlers.find(\n (action) => action.skipOther,\n );\n if (primaryAction) {\n primaryAction.method(e);\n } else {\n currentHandlers.forEach(({ method }) => {\n method(e);\n });\n }\n };\n\n const cleanUp = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n // clean all for meta.\n // Main reason is ctrl+z (or any other native command not fires letter keyup on editable inputs)\n if (e.key && e.key.toLowerCase() === 'meta') {\n downKeys.clear();\n } else {\n downKeys.delete(key);\n }\n };\n\n const unbindScope = (deleteScope: string): void => {\n Object.keys(handlers).forEach((key) => {\n const keyString = key as KeyString;\n const handler = handlers[keyString].filter(\n ({ scope }: Handler) => scope !== deleteScope,\n );\n if (handler.length) {\n handlers[keyString] = handler;\n } else {\n delete handlers[keyString];\n }\n });\n };\n\n const setScope = (scope: string): void => {\n activeScope = scope;\n };\n\n const unbindAll = (): void => {\n handlers = {};\n downKeys.clear();\n };\n\n const reset = (): void => {\n downKeys.clear();\n };\n\n const destroy = (): void => {\n downKeys.clear();\n handlers = {};\n if (doc) {\n const listeners = documentListeners.get(doc);\n if (listeners) {\n doc.removeEventListener('keydown', listeners.dispatch);\n doc.removeEventListener('keyup', listeners.cleanUp);\n window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n // Remove old listeners if they exist\n destroy();\n\n // Store and add new listeners\n documentListeners.set(doc, { dispatch, cleanUp, reset });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n window.addEventListener('focus', reset);\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope: () => activeScope,\n destroy,\n };\n}\n"],"mappings":"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","names":["src_exports","__export","DEFAULT_SCOPE","bindKey","src_default","destroy","getScope","setScope","unbindAll","unbindKey","unbindScope","unsafeUnbindKey","__toCommonJS","DEFAULT_SCOPE","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getKeyIdentifier","key","SPECIAL","getMods","keys","acc","MODIFIERS","getCombinations","keysStr","getKeyMap","keyCmd","isFirefox","isEditable","el","isEqArray","arr1","arr2","i","defaultFilter","isEditable","documentListeners","createKeybuddy","doc","filterFn","handlers","downKeys","activeScope","modifiers","bindKey","keysStr","scopeOrMethod","methodOrNull","skipOther","scope","method","getKeyMap","key","shortcut","unbindKeyProcess","deleteMethod","deleteScope","handler","methodShortcut","isEqArray","unbindKey","unsafeUnbindKey","dispatch","e","getKeyIdentifier","isFirefox","CAPS_LOCK_KEY","updateModifiers","currentHandlers","special","mods","primaryAction","action","cleanUp","unbindScope","keyString","setScope","unbindAll","reset","destroy","listeners","bind","unbind","unsafeUnbind","getScope","setScope","unbindScope","unbindAll","destroy","createKeybuddy","bindKey","unbindKey","unsafeUnbindKey","src_default","bind"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/helpers/keyboard.ts","../src/helpers/keymap.ts","../src/helpers/utils.ts","../src/keybuddy.ts"],"sourcesContent":["import { DEFAULT_SCOPE } from './constants';\nimport { createKeybuddy } from './keybuddy';\n\nexport { createKeybuddy };\n\nconst {\n bind,\n unbind,\n unsafeUnbind,\n getScope,\n setScope,\n unbindScope,\n unbindAll,\n destroy,\n} = createKeybuddy(document);\n\nexport const bindKey = bind;\nexport const unbindKey = unbind;\nexport const unsafeUnbindKey = unsafeUnbind;\nexport { setScope, unbindScope, unbindAll, getScope, destroy, DEFAULT_SCOPE };\nexport type { KeyString } from './constants';\nexport { MODS, SPECIAL } from './constants';\nexport default bind;\n","\nexport const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\n// Bitwise flags for modifiers - much faster than object-based tracking\nexport const MODS = {\n SHIFT: 0b0001, // 1\n ALT: 0b0010, // 2\n CTRL: 0b0100, // 4\n META: 0b1000, // 8\n} as const;\n\n// Map string modifier names to bitwise flags for parsing\nexport type ModifierNames = {\n '⇧': number;\n shift: number;\n '⌥': number;\n alt: number;\n option: number;\n '⌃': number;\n ctrl: number;\n control: number;\n '⌘': number;\n cmd: number;\n command: number;\n};\n\nexport const MODIFIERS: ModifierNames = {\n '⇧': MODS.SHIFT,\n shift: MODS.SHIFT,\n '⌥': MODS.ALT,\n alt: MODS.ALT,\n option: MODS.ALT,\n '⌃': MODS.CTRL,\n ctrl: MODS.CTRL,\n control: MODS.CTRL,\n '⌘': MODS.META,\n cmd: MODS.META,\n command: MODS.META,\n};\n\n// Modern key mapping using KeyboardEvent.key values\nexport const SPECIAL: { [key: string]: string } = {\n backspace: 'Backspace',\n tab: 'Tab',\n clear: 'Clear',\n enter: 'Enter',\n return: 'Enter',\n esc: 'Escape',\n escape: 'Escape',\n space: ' ',\n left: 'ArrowLeft',\n up: 'ArrowUp',\n right: 'ArrowRight',\n down: 'ArrowDown',\n del: 'Delete',\n delete: 'Delete',\n home: 'Home',\n end: 'End',\n pageup: 'PageUp',\n pagedown: 'PageDown',\n comma: ',',\n '.': '.',\n '/': '/',\n '`': '`',\n '-': '-',\n '=': '=',\n ';': ';',\n \"'\": \"'\",\n '[': '[',\n ']': ']',\n '\\\\': '\\\\',\n // Normalize Meta key variants\n 'Meta': 'Meta',\n 'MetaLeft': 'Meta',\n 'MetaRight': 'Meta', \n 'OS': 'Meta', // Some browsers use OS instead of Meta\n 'ContextMenu': 'Meta', // Right-click context menu key sometimes acts as Meta\n // Add identity mappings for already-normalized keys\n 'ArrowLeft': 'ArrowLeft',\n 'ArrowUp': 'ArrowUp',\n 'ArrowRight': 'ArrowRight',\n 'ArrowDown': 'ArrowDown',\n 'Backspace': 'Backspace',\n 'Tab': 'Tab',\n 'Clear': 'Clear',\n 'Enter': 'Enter',\n 'Escape': 'Escape',\n 'Delete': 'Delete',\n 'Home': 'Home',\n 'End': 'End',\n 'PageUp': 'PageUp',\n 'PageDown': 'PageDown',\n};\n\nexport const CAPS_LOCK_KEY = 'CapsLock';\n","import { KeyString, MODS, SPECIAL } from '../constants';\n\nexport const getKeyIdentifier = (key: string): KeyString => {\n return (SPECIAL[key] || key.toUpperCase()) as KeyString;\n};\n\nexport const updateModifiers = (e: KeyboardEvent): number => {\n let modifiers = 0;\n if (e.shiftKey) modifiers |= MODS.SHIFT;\n if (e.altKey) modifiers |= MODS.ALT;\n if (e.ctrlKey) modifiers |= MODS.CTRL;\n if (e.metaKey) modifiers |= MODS.META;\n return modifiers;\n};\n","import { KeyString, MODIFIERS, ModifierNames, SPECIAL } from '../constants';\n\nexport interface ParsedShortcut {\n mods: number; // Bitwise flag for modifiers\n special: string[];\n}\nexport interface KeyMap {\n key: KeyString;\n shortcut: ParsedShortcut;\n}\n\nexport const getKeyIdentifier = (key: string): KeyString =>\n (SPECIAL[key] || key.toUpperCase()) as KeyString;\n\nconst getMods = (keys: string[]): ParsedShortcut =>\n keys.reduce(\n (acc, key) => {\n if (key in MODIFIERS) {\n acc.mods |= MODIFIERS[key as keyof ModifierNames];\n } else {\n acc.special.push(SPECIAL[key] || key.toUpperCase());\n }\n return acc;\n },\n {\n mods: 0, // Start with no modifiers\n special: [],\n } as ParsedShortcut,\n );\n\nconst getCombinations = (keysStr: string): string[] => {\n const cleanKeys = keysStr.replace(/\\s/g, '');\n const keys = cleanKeys.split(',');\n if (keys[keys.length - 1] === '') {\n keys[keys.length - 2] += ',';\n }\n\n return keys;\n};\n\nexport const getKeyMap = (keysStr: string): KeyMap[] => {\n const keymap = getCombinations(keysStr);\n return keymap.map((keyCmd) => {\n const keys = keyCmd.split('+');\n const key = keys[keys.length - 1];\n const keyIdentifier = getKeyIdentifier(key);\n\n return {\n key: keyIdentifier,\n shortcut: getMods(keys),\n };\n });\n};\n","export const invariant = (\n condition: boolean,\n message: string,\n ...args: unknown[]\n) => {\n if (\n typeof process !== 'undefined' &&\n process.env &&\n process.env.NODE_ENV === 'development' &&\n !condition\n ) {\n throw new Error(\n `Invariant failed: ${message}${args.length ? ` ${JSON.stringify(args)}` : ''}`,\n );\n }\n};\n\nexport const isFirefox = navigator.userAgent.includes('Firefox');\n\nexport const isEditable = (el: HTMLElement): boolean =>\n el.isContentEditable ||\n el.tagName === 'INPUT' ||\n el.tagName === 'SELECT' ||\n el.tagName === 'TEXTAREA';\n\nexport const isEqArray = (\n arr1: (string | number)[],\n arr2: (string | number)[],\n): boolean => {\n if (arr1.length !== arr2.length) return false;\n\n for (let i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) return false;\n }\n\n return true;\n};\n","import { CAPS_LOCK_KEY, DEFAULT_SCOPE, KeyString } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype noop = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: noop;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\n// WeakMap to track event listener references per document to prevent memory leaks\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n }\n>();\n\nexport function createKeybuddy(\n doc: Document,\n filterFn: FilterFn = defaultFilter,\n) {\n let handlers: { [key: KeyString]: Handler[] } = {};\n const downKeys: Set<KeyString> = new Set();\n let activeScope = DEFAULT_SCOPE;\n\n let modifiers = 0; // Bitwise flag for active modifiers\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n {\n skipOther,\n }: {\n skipOther: boolean;\n } = {\n skipOther: false,\n },\n ): void => {\n const scope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const method: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n if (!handlers[key]) {\n handlers[key] = [];\n }\n const handler = handlers[key];\n if (process.env.NODE_ENV === 'development') {\n if (skipOther) {\n const action = handler.find((i) => i.skipOther);\n invariant(\n !action,\n \"Conflicting 'skipOther' property with action\",\n action,\n );\n }\n }\n\n handler.push({\n scope,\n method,\n shortcut,\n skipOther,\n });\n });\n };\n\n const unbindKeyProcess = (\n keysStr: string,\n deleteMethod: null | noop,\n deleteScope: string = DEFAULT_SCOPE,\n ): void => {\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n const handler = handlers[key];\n if (Array.isArray(handler)) {\n const handler = handlers[key].filter(\n ({ scope, method, shortcut: methodShortcut }: Handler) =>\n !(\n scope === deleteScope &&\n methodShortcut.mods === shortcut.mods &&\n isEqArray(methodShortcut.special, shortcut.special) &&\n (deleteMethod === null ? true : method === deleteMethod)\n ),\n );\n if (handler.length) {\n handlers[key] = handler;\n } else {\n delete handlers[key];\n }\n }\n });\n };\n\n const unbindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n return unbindKeyProcess(keysStr, deleteMethod, deleteScope);\n };\n\n const unsafeUnbindKey = (keysStr: string, scope?: string) =>\n unbindKeyProcess(keysStr, null, scope);\n\n const dispatch = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n if (!filterFn(e)) {\n return;\n }\n\n // fix firefox behavior when caps lock fires three times onkeydown\n // and don't fire at all onkeyup (Firefox 72)\n if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\n // Check if key is not a modifier\n const isModifierKey =\n key === ('SHIFT' as KeyString) ||\n key === ('ALT' as KeyString) ||\n key === ('CTRL' as KeyString) ||\n key === ('META' as KeyString);\n if (!isModifierKey && !downKeys.has(key)) {\n downKeys.add(key);\n }\n // See if we need to ignore the keypress (filter() can can be overridden)\n // by default ignore key presses if a select, textarea, or input is focused\n // if (!assignKey.filter.call(this, event)) return;\n\n // abort if no potentially matching shortcuts found\n if (!(key in handlers)) {\n return;\n }\n\n const currentHandlers = handlers[key].filter(\n ({ scope, shortcut: { special, mods } }) => {\n if (scope !== activeScope) {\n return false;\n }\n\n return isEqArray(special, Array.from(downKeys)) && mods === modifiers;\n },\n );\n\n const primaryAction: Handler | undefined = currentHandlers.find(\n (action) => action.skipOther,\n );\n if (primaryAction) {\n primaryAction.method(e);\n } else {\n currentHandlers.forEach(({ method }) => {\n method(e);\n });\n }\n };\n\n const cleanUp = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n // clean all for meta.\n // Main reason is ctrl+z (or any other native command not fires letter keyup on editable inputs)\n if (e.key && e.key.toLowerCase() === 'meta') {\n downKeys.clear();\n } else {\n downKeys.delete(key);\n }\n };\n\n const unbindScope = (deleteScope: string): void => {\n Object.keys(handlers).forEach((key) => {\n const keyString = key as KeyString;\n const handler = handlers[keyString].filter(\n ({ scope }: Handler) => scope !== deleteScope,\n );\n if (handler.length) {\n handlers[keyString] = handler;\n } else {\n delete handlers[keyString];\n }\n });\n };\n\n const setScope = (scope: string): void => {\n activeScope = scope;\n };\n\n const unbindAll = (): void => {\n handlers = {};\n downKeys.clear();\n };\n\n const reset = (): void => {\n downKeys.clear();\n };\n\n const destroy = (): void => {\n downKeys.clear();\n handlers = {};\n if (doc) {\n const listeners = documentListeners.get(doc);\n if (listeners) {\n doc.removeEventListener('keydown', listeners.dispatch);\n doc.removeEventListener('keyup', listeners.cleanUp);\n window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n // Remove old listeners if they exist\n destroy();\n\n // Store and add new listeners\n documentListeners.set(doc, { dispatch, cleanUp, reset });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n window.addEventListener('focus', reset);\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope: () => activeScope,\n destroy,\n };\n}\n"],"mappings":"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","names":["src_exports","__export","DEFAULT_SCOPE","MODS","SPECIAL","bindKey","createKeybuddy","src_default","destroy","getScope","setScope","unbindAll","unbindKey","unbindScope","unsafeUnbindKey","__toCommonJS","DEFAULT_SCOPE","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getKeyIdentifier","key","SPECIAL","getMods","keys","acc","MODIFIERS","getCombinations","keysStr","getKeyMap","keyCmd","isFirefox","isEditable","el","isEqArray","arr1","arr2","i","defaultFilter","isEditable","documentListeners","createKeybuddy","doc","filterFn","handlers","downKeys","activeScope","modifiers","bindKey","keysStr","scopeOrMethod","methodOrNull","skipOther","scope","method","getKeyMap","key","shortcut","unbindKeyProcess","deleteMethod","deleteScope","handler","methodShortcut","isEqArray","unbindKey","unsafeUnbindKey","dispatch","e","getKeyIdentifier","isFirefox","CAPS_LOCK_KEY","updateModifiers","currentHandlers","special","mods","primaryAction","action","cleanUp","unbindScope","keyString","setScope","unbindAll","reset","destroy","listeners","bind","unbind","unsafeUnbind","getScope","setScope","unbindScope","unbindAll","destroy","createKeybuddy","bindKey","unbindKey","unsafeUnbindKey","src_default","bind"]}
|
package/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/* keybuddy - Modern keyboard shortcuts library */
|
|
2
|
-
var f="all",
|
|
2
|
+
var f="all",o={SHIFT:1,ALT:2,CTRL:4,META:8},b={"\u21E7":o.SHIFT,shift:o.SHIFT,"\u2325":o.ALT,alt:o.ALT,option:o.ALT,"\u2303":o.CTRL,ctrl:o.CTRL,control:o.CTRL,"\u2318":o.META,cmd:o.META,command:o.META},u={backspace:"Backspace",tab:"Tab",clear:"Clear",enter:"Enter",return:"Enter",esc:"Escape",escape:"Escape",space:" ",left:"ArrowLeft",up:"ArrowUp",right:"ArrowRight",down:"ArrowDown",del:"Delete",delete:"Delete",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",comma:",",".":".","/":"/","`":"`","-":"-","=":"=",";":";","'":"'","[":"[","]":"]","\\":"\\",Meta:"Meta",MetaLeft:"Meta",MetaRight:"Meta",OS:"Meta",ContextMenu:"Meta",ArrowLeft:"ArrowLeft",ArrowUp:"ArrowUp",ArrowRight:"ArrowRight",ArrowDown:"ArrowDown",Backspace:"Backspace",Tab:"Tab",Clear:"Clear",Enter:"Enter",Escape:"Escape",Delete:"Delete",Home:"Home",End:"End",PageUp:"PageUp",PageDown:"PageDown"},C="CapsLock";var S=e=>u[e]||e.toUpperCase(),k=e=>{let i=0;return e.shiftKey&&(i|=o.SHIFT),e.altKey&&(i|=o.ALT),e.ctrlKey&&(i|=o.CTRL),e.metaKey&&(i|=o.META),i};var B=e=>u[e]||e.toUpperCase(),q=e=>e.reduce((i,t)=>(t in b?i.mods|=b[t]:i.special.push(u[t]||t.toUpperCase()),i),{mods:0,special:[]}),$=e=>{let t=e.replace(/\s/g,"").split(",");return t[t.length-1]===""&&(t[t.length-2]+=","),t},K=e=>$(e).map(t=>{let a=t.split("+"),g=a[a.length-1];return{key:B(g),shortcut:q(a)}});var P=navigator.userAgent.includes("Firefox"),D=e=>e.isContentEditable||e.tagName==="INPUT"||e.tagName==="SELECT"||e.tagName==="TEXTAREA",h=(e,i)=>{if(e.length!==i.length)return!1;for(let t=0;t<e.length;t++)if(e[t]!==i[t])return!1;return!0};var V=e=>e&&!D(e.target),A=new WeakMap;function I(e,i=V){let t={},a=new Set,g="all",m=0,F=(r,n,c=()=>{},{skipOther:s}={skipOther:!1})=>{let d=typeof n=="function"?"all":n,p=typeof n=="function"?n:c;K(r).forEach(({key:l,shortcut:y})=>{t[l]||(t[l]=[]),t[l].push({scope:d,method:p,shortcut:y,skipOther:s})})},v=(r,n,c="all")=>{K(r).forEach(({key:s,shortcut:d})=>{let p=t[s];if(Array.isArray(p)){let l=t[s].filter(({scope:y,method:x,shortcut:E})=>!(y===c&&E.mods===d.mods&&h(E.special,d.special)&&(n===null||x===n)));l.length?t[s]=l:delete t[s]}})},H=(r,n,c=()=>{})=>v(r,typeof n=="function"?n:c,typeof n=="function"?"all":n),O=(r,n)=>v(r,null,n),L=r=>{let n=S(r.key);if(!i(r)||P&&n===C||(m=k(r),!(n==="SHIFT"||n==="ALT"||n==="CTRL"||n==="META")&&!a.has(n)&&a.add(n),!(n in t)))return;let s=t[n].filter(({scope:p,shortcut:{special:l,mods:y}})=>p!==g?!1:h(l,Array.from(a))&&y===m),d=s.find(p=>p.skipOther);d?d.method(r):s.forEach(({method:p})=>{p(r)})},T=r=>{let n=S(r.key);r.key&&r.key.toLowerCase()==="meta"?a.clear():a.delete(n)},N=r=>{Object.keys(t).forEach(n=>{let c=n,s=t[c].filter(({scope:d})=>d!==r);s.length?t[c]=s:delete t[c]})},R=r=>{g=r},_=()=>{t={},a.clear()},w=()=>{a.clear()},M=()=>{if(a.clear(),t={},e){let r=A.get(e);r&&(e.removeEventListener("keydown",r.dispatch),e.removeEventListener("keyup",r.cleanUp),window.removeEventListener("focus",r.reset),A.delete(e))}};return M(),A.set(e,{dispatch:L,cleanUp:T,reset:w}),e.addEventListener("keydown",L),e.addEventListener("keyup",T),window.addEventListener("focus",w),{bind:F,unbind:H,unsafeUnbind:O,unbindScope:N,setScope:R,unbindAll:_,getScope:()=>g,destroy:M}}var{bind:U,unbind:Y,unsafeUnbind:j,getScope:ue,setScope:ge,unbindScope:ye,unbindAll:me,destroy:Ee}=I(document),be=U,Se=Y,Ke=j;var he=U;export{f as DEFAULT_SCOPE,o as MODS,u as SPECIAL,be as bindKey,I as createKeybuddy,he as default,Ee as destroy,ue as getScope,ge as setScope,me as unbindAll,Se as unbindKey,ye as unbindScope,Ke as unsafeUnbindKey};
|
|
3
3
|
//# sourceMappingURL=index.mjs.map
|
package/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/helpers/keyboard.ts","../src/helpers/keymap.ts","../src/helpers/utils.ts","../src/keybuddy.ts","../src/index.ts"],"sourcesContent":["\nexport const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\n// Bitwise flags for modifiers - much faster than object-based tracking\nexport const MODS = {\n SHIFT: 0b0001, // 1\n ALT: 0b0010, // 2\n CTRL: 0b0100, // 4\n META: 0b1000, // 8\n} as const;\n\n// Map string modifier names to bitwise flags for parsing\nexport type ModifierNames = {\n '⇧': number;\n shift: number;\n '⌥': number;\n alt: number;\n option: number;\n '⌃': number;\n ctrl: number;\n control: number;\n '⌘': number;\n cmd: number;\n command: number;\n};\n\nexport const MODIFIERS: ModifierNames = {\n '⇧': MODS.SHIFT,\n shift: MODS.SHIFT,\n '⌥': MODS.ALT,\n alt: MODS.ALT,\n option: MODS.ALT,\n '⌃': MODS.CTRL,\n ctrl: MODS.CTRL,\n control: MODS.CTRL,\n '⌘': MODS.META,\n cmd: MODS.META,\n command: MODS.META,\n};\n\n// Modern key mapping using KeyboardEvent.key values\nexport const SPECIAL: { [key: string]: string } = {\n backspace: 'Backspace',\n tab: 'Tab',\n clear: 'Clear',\n enter: 'Enter',\n return: 'Enter',\n esc: 'Escape',\n escape: 'Escape',\n space: ' ',\n left: 'ArrowLeft',\n up: 'ArrowUp',\n right: 'ArrowRight',\n down: 'ArrowDown',\n del: 'Delete',\n delete: 'Delete',\n home: 'Home',\n end: 'End',\n pageup: 'PageUp',\n pagedown: 'PageDown',\n comma: ',',\n '.': '.',\n '/': '/',\n '`': '`',\n '-': '-',\n '=': '=',\n ';': ';',\n \"'\": \"'\",\n '[': '[',\n ']': ']',\n '\\\\': '\\\\',\n // Normalize Meta key variants\n 'Meta': 'Meta',\n 'MetaLeft': 'Meta',\n 'MetaRight': 'Meta', \n 'OS': 'Meta', // Some browsers use OS instead of Meta\n 'ContextMenu': 'Meta', // Right-click context menu key sometimes acts as Meta\n // Add identity mappings for already-normalized keys\n 'ArrowLeft': 'ArrowLeft',\n 'ArrowUp': 'ArrowUp',\n 'ArrowRight': 'ArrowRight',\n 'ArrowDown': 'ArrowDown',\n 'Backspace': 'Backspace',\n 'Tab': 'Tab',\n 'Clear': 'Clear',\n 'Enter': 'Enter',\n 'Escape': 'Escape',\n 'Delete': 'Delete',\n 'Home': 'Home',\n 'End': 'End',\n 'PageUp': 'PageUp',\n 'PageDown': 'PageDown',\n};\n\nexport const CAPS_LOCK_KEY = 'CapsLock';\n","import { KeyString, MODS, SPECIAL } from '../constants';\n\nexport const getKeyIdentifier = (key: string): KeyString => {\n return (SPECIAL[key] || key.toUpperCase()) as KeyString;\n};\n\nexport const updateModifiers = (e: KeyboardEvent): number => {\n let modifiers = 0;\n if (e.shiftKey) modifiers |= MODS.SHIFT;\n if (e.altKey) modifiers |= MODS.ALT;\n if (e.ctrlKey) modifiers |= MODS.CTRL;\n if (e.metaKey) modifiers |= MODS.META;\n return modifiers;\n};\n","import { KeyString, MODIFIERS, ModifierNames, SPECIAL } from '../constants';\n\nexport interface ParsedShortcut {\n mods: number; // Bitwise flag for modifiers\n special: string[];\n}\nexport interface KeyMap {\n key: KeyString;\n shortcut: ParsedShortcut;\n}\n\nexport const getKeyIdentifier = (key: string): KeyString =>\n (SPECIAL[key] || key.toUpperCase()) as KeyString;\n\nconst getMods = (keys: string[]): ParsedShortcut =>\n keys.reduce(\n (acc, key) => {\n if (key in MODIFIERS) {\n acc.mods |= MODIFIERS[key as keyof ModifierNames];\n } else {\n acc.special.push(SPECIAL[key] || key.toUpperCase());\n }\n return acc;\n },\n {\n mods: 0, // Start with no modifiers\n special: [],\n } as ParsedShortcut,\n );\n\nconst getCombinations = (keysStr: string): string[] => {\n const cleanKeys = keysStr.replace(/\\s/g, '');\n const keys = cleanKeys.split(',');\n if (keys[keys.length - 1] === '') {\n keys[keys.length - 2] += ',';\n }\n\n return keys;\n};\n\nexport const getKeyMap = (keysStr: string): KeyMap[] => {\n const keymap = getCombinations(keysStr);\n return keymap.map((keyCmd) => {\n const keys = keyCmd.split('+');\n const key = keys[keys.length - 1];\n const keyIdentifier = getKeyIdentifier(key);\n\n return {\n key: keyIdentifier,\n shortcut: getMods(keys),\n };\n });\n};\n","export const invariant = (\n condition: boolean,\n message: string,\n ...args: unknown[]\n) => {\n if (\n typeof process !== 'undefined' &&\n process.env &&\n process.env.NODE_ENV === 'development' &&\n !condition\n ) {\n throw new Error(\n `Invariant failed: ${message}${args.length ? ` ${JSON.stringify(args)}` : ''}`,\n );\n }\n};\n\nexport const isFirefox = navigator.userAgent.includes('Firefox');\n\nexport const isEditable = (el: HTMLElement): boolean =>\n el.isContentEditable ||\n el.tagName === 'INPUT' ||\n el.tagName === 'SELECT' ||\n el.tagName === 'TEXTAREA';\n\nexport const isEqArray = (\n arr1: (string | number)[],\n arr2: (string | number)[],\n): boolean => {\n if (arr1.length !== arr2.length) return false;\n\n for (let i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) return false;\n }\n\n return true;\n};\n","import { CAPS_LOCK_KEY, DEFAULT_SCOPE, KeyString } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype noop = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: noop;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\n// WeakMap to track event listener references per document to prevent memory leaks\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n }\n>();\n\nexport function createKeybuddy(\n doc: Document,\n filterFn: FilterFn = defaultFilter,\n) {\n let handlers: { [key: KeyString]: Handler[] } = {};\n const downKeys: Set<KeyString> = new Set();\n let activeScope = DEFAULT_SCOPE;\n\n let modifiers = 0; // Bitwise flag for active modifiers\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n {\n skipOther,\n }: {\n skipOther: boolean;\n } = {\n skipOther: false,\n },\n ): void => {\n const scope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const method: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n if (!handlers[key]) {\n handlers[key] = [];\n }\n const handler = handlers[key];\n if (process.env.NODE_ENV === 'development') {\n if (skipOther) {\n const action = handler.find((i) => i.skipOther);\n invariant(\n !action,\n \"Conflicting 'skipOther' property with action\",\n action,\n );\n }\n }\n\n handler.push({\n scope,\n method,\n shortcut,\n skipOther,\n });\n });\n };\n\n const unbindKeyProcess = (\n keysStr: string,\n deleteMethod: null | noop,\n deleteScope: string = DEFAULT_SCOPE,\n ): void => {\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n const handler = handlers[key];\n if (Array.isArray(handler)) {\n const handler = handlers[key].filter(\n ({ scope, method, shortcut: methodShortcut }: Handler) =>\n !(\n scope === deleteScope &&\n methodShortcut.mods === shortcut.mods &&\n isEqArray(methodShortcut.special, shortcut.special) &&\n (deleteMethod === null ? true : method === deleteMethod)\n ),\n );\n if (handler.length) {\n handlers[key] = handler;\n } else {\n delete handlers[key];\n }\n }\n });\n };\n\n const unbindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n return unbindKeyProcess(keysStr, deleteMethod, deleteScope);\n };\n\n const unsafeUnbindKey = (keysStr: string, scope?: string) =>\n unbindKeyProcess(keysStr, null, scope);\n\n const dispatch = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n if (!filterFn(e)) {\n return;\n }\n\n // fix firefox behavior when caps lock fires three times onkeydown\n // and don't fire at all onkeyup (Firefox 72)\n if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\n // Check if key is not a modifier\n const isModifierKey =\n key === ('SHIFT' as KeyString) ||\n key === ('ALT' as KeyString) ||\n key === ('CTRL' as KeyString) ||\n key === ('META' as KeyString);\n if (!isModifierKey && !downKeys.has(key)) {\n downKeys.add(key);\n }\n // See if we need to ignore the keypress (filter() can can be overridden)\n // by default ignore key presses if a select, textarea, or input is focused\n // if (!assignKey.filter.call(this, event)) return;\n\n // abort if no potentially matching shortcuts found\n if (!(key in handlers)) {\n return;\n }\n\n const currentHandlers = handlers[key].filter(\n ({ scope, shortcut: { special, mods } }) => {\n if (scope !== activeScope) {\n return false;\n }\n\n return isEqArray(special, Array.from(downKeys)) && mods === modifiers;\n },\n );\n\n const primaryAction: Handler | undefined = currentHandlers.find(\n (action) => action.skipOther,\n );\n if (primaryAction) {\n primaryAction.method(e);\n } else {\n currentHandlers.forEach(({ method }) => {\n method(e);\n });\n }\n };\n\n const cleanUp = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n // clean all for meta.\n // Main reason is ctrl+z (or any other native command not fires letter keyup on editable inputs)\n if (e.key && e.key.toLowerCase() === 'meta') {\n downKeys.clear();\n } else {\n downKeys.delete(key);\n }\n };\n\n const unbindScope = (deleteScope: string): void => {\n Object.keys(handlers).forEach((key) => {\n const keyString = key as KeyString;\n const handler = handlers[keyString].filter(\n ({ scope }: Handler) => scope !== deleteScope,\n );\n if (handler.length) {\n handlers[keyString] = handler;\n } else {\n delete handlers[keyString];\n }\n });\n };\n\n const setScope = (scope: string): void => {\n activeScope = scope;\n };\n\n const unbindAll = (): void => {\n handlers = {};\n downKeys.clear();\n };\n\n const reset = (): void => {\n downKeys.clear();\n };\n\n const destroy = (): void => {\n downKeys.clear();\n handlers = {};\n if (doc) {\n const listeners = documentListeners.get(doc);\n if (listeners) {\n doc.removeEventListener('keydown', listeners.dispatch);\n doc.removeEventListener('keyup', listeners.cleanUp);\n window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n // Remove old listeners if they exist\n destroy();\n\n // Store and add new listeners\n documentListeners.set(doc, { dispatch, cleanUp, reset });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n window.addEventListener('focus', reset);\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope: () => activeScope,\n destroy,\n };\n}\n","import { DEFAULT_SCOPE } from './constants';\nimport { createKeybuddy } from './keybuddy';\n\nconst {\n bind,\n unbind,\n unsafeUnbind,\n getScope,\n setScope,\n unbindScope,\n unbindAll,\n destroy,\n} = createKeybuddy(document);\n\nexport const bindKey = bind;\nexport const unbindKey = unbind;\nexport const unsafeUnbindKey = unsafeUnbind;\nexport { setScope, unbindScope, unbindAll, getScope, destroy, DEFAULT_SCOPE };\nexport type { KeyString } from './constants';\nexport default bind;\n"],"mappings":"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","names":["DEFAULT_SCOPE","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getKeyIdentifier","key","SPECIAL","getMods","keys","acc","MODIFIERS","getCombinations","keysStr","getKeyMap","keyCmd","isFirefox","isEditable","el","isEqArray","arr1","arr2","i","defaultFilter","isEditable","documentListeners","createKeybuddy","doc","filterFn","handlers","downKeys","activeScope","modifiers","bindKey","keysStr","scopeOrMethod","methodOrNull","skipOther","scope","method","getKeyMap","key","shortcut","unbindKeyProcess","deleteMethod","deleteScope","handler","methodShortcut","isEqArray","unbindKey","unsafeUnbindKey","dispatch","e","getKeyIdentifier","isFirefox","CAPS_LOCK_KEY","updateModifiers","currentHandlers","special","mods","primaryAction","action","cleanUp","unbindScope","keyString","setScope","unbindAll","reset","destroy","listeners","bind","unbind","unsafeUnbind","getScope","setScope","unbindScope","unbindAll","destroy","createKeybuddy","bindKey","unbindKey","unsafeUnbindKey","src_default","bind"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/helpers/keyboard.ts","../src/helpers/keymap.ts","../src/helpers/utils.ts","../src/keybuddy.ts","../src/index.ts"],"sourcesContent":["\nexport const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\n// Bitwise flags for modifiers - much faster than object-based tracking\nexport const MODS = {\n SHIFT: 0b0001, // 1\n ALT: 0b0010, // 2\n CTRL: 0b0100, // 4\n META: 0b1000, // 8\n} as const;\n\n// Map string modifier names to bitwise flags for parsing\nexport type ModifierNames = {\n '⇧': number;\n shift: number;\n '⌥': number;\n alt: number;\n option: number;\n '⌃': number;\n ctrl: number;\n control: number;\n '⌘': number;\n cmd: number;\n command: number;\n};\n\nexport const MODIFIERS: ModifierNames = {\n '⇧': MODS.SHIFT,\n shift: MODS.SHIFT,\n '⌥': MODS.ALT,\n alt: MODS.ALT,\n option: MODS.ALT,\n '⌃': MODS.CTRL,\n ctrl: MODS.CTRL,\n control: MODS.CTRL,\n '⌘': MODS.META,\n cmd: MODS.META,\n command: MODS.META,\n};\n\n// Modern key mapping using KeyboardEvent.key values\nexport const SPECIAL: { [key: string]: string } = {\n backspace: 'Backspace',\n tab: 'Tab',\n clear: 'Clear',\n enter: 'Enter',\n return: 'Enter',\n esc: 'Escape',\n escape: 'Escape',\n space: ' ',\n left: 'ArrowLeft',\n up: 'ArrowUp',\n right: 'ArrowRight',\n down: 'ArrowDown',\n del: 'Delete',\n delete: 'Delete',\n home: 'Home',\n end: 'End',\n pageup: 'PageUp',\n pagedown: 'PageDown',\n comma: ',',\n '.': '.',\n '/': '/',\n '`': '`',\n '-': '-',\n '=': '=',\n ';': ';',\n \"'\": \"'\",\n '[': '[',\n ']': ']',\n '\\\\': '\\\\',\n // Normalize Meta key variants\n 'Meta': 'Meta',\n 'MetaLeft': 'Meta',\n 'MetaRight': 'Meta', \n 'OS': 'Meta', // Some browsers use OS instead of Meta\n 'ContextMenu': 'Meta', // Right-click context menu key sometimes acts as Meta\n // Add identity mappings for already-normalized keys\n 'ArrowLeft': 'ArrowLeft',\n 'ArrowUp': 'ArrowUp',\n 'ArrowRight': 'ArrowRight',\n 'ArrowDown': 'ArrowDown',\n 'Backspace': 'Backspace',\n 'Tab': 'Tab',\n 'Clear': 'Clear',\n 'Enter': 'Enter',\n 'Escape': 'Escape',\n 'Delete': 'Delete',\n 'Home': 'Home',\n 'End': 'End',\n 'PageUp': 'PageUp',\n 'PageDown': 'PageDown',\n};\n\nexport const CAPS_LOCK_KEY = 'CapsLock';\n","import { KeyString, MODS, SPECIAL } from '../constants';\n\nexport const getKeyIdentifier = (key: string): KeyString => {\n return (SPECIAL[key] || key.toUpperCase()) as KeyString;\n};\n\nexport const updateModifiers = (e: KeyboardEvent): number => {\n let modifiers = 0;\n if (e.shiftKey) modifiers |= MODS.SHIFT;\n if (e.altKey) modifiers |= MODS.ALT;\n if (e.ctrlKey) modifiers |= MODS.CTRL;\n if (e.metaKey) modifiers |= MODS.META;\n return modifiers;\n};\n","import { KeyString, MODIFIERS, ModifierNames, SPECIAL } from '../constants';\n\nexport interface ParsedShortcut {\n mods: number; // Bitwise flag for modifiers\n special: string[];\n}\nexport interface KeyMap {\n key: KeyString;\n shortcut: ParsedShortcut;\n}\n\nexport const getKeyIdentifier = (key: string): KeyString =>\n (SPECIAL[key] || key.toUpperCase()) as KeyString;\n\nconst getMods = (keys: string[]): ParsedShortcut =>\n keys.reduce(\n (acc, key) => {\n if (key in MODIFIERS) {\n acc.mods |= MODIFIERS[key as keyof ModifierNames];\n } else {\n acc.special.push(SPECIAL[key] || key.toUpperCase());\n }\n return acc;\n },\n {\n mods: 0, // Start with no modifiers\n special: [],\n } as ParsedShortcut,\n );\n\nconst getCombinations = (keysStr: string): string[] => {\n const cleanKeys = keysStr.replace(/\\s/g, '');\n const keys = cleanKeys.split(',');\n if (keys[keys.length - 1] === '') {\n keys[keys.length - 2] += ',';\n }\n\n return keys;\n};\n\nexport const getKeyMap = (keysStr: string): KeyMap[] => {\n const keymap = getCombinations(keysStr);\n return keymap.map((keyCmd) => {\n const keys = keyCmd.split('+');\n const key = keys[keys.length - 1];\n const keyIdentifier = getKeyIdentifier(key);\n\n return {\n key: keyIdentifier,\n shortcut: getMods(keys),\n };\n });\n};\n","export const invariant = (\n condition: boolean,\n message: string,\n ...args: unknown[]\n) => {\n if (\n typeof process !== 'undefined' &&\n process.env &&\n process.env.NODE_ENV === 'development' &&\n !condition\n ) {\n throw new Error(\n `Invariant failed: ${message}${args.length ? ` ${JSON.stringify(args)}` : ''}`,\n );\n }\n};\n\nexport const isFirefox = navigator.userAgent.includes('Firefox');\n\nexport const isEditable = (el: HTMLElement): boolean =>\n el.isContentEditable ||\n el.tagName === 'INPUT' ||\n el.tagName === 'SELECT' ||\n el.tagName === 'TEXTAREA';\n\nexport const isEqArray = (\n arr1: (string | number)[],\n arr2: (string | number)[],\n): boolean => {\n if (arr1.length !== arr2.length) return false;\n\n for (let i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) return false;\n }\n\n return true;\n};\n","import { CAPS_LOCK_KEY, DEFAULT_SCOPE, KeyString } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype noop = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: noop;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\n// WeakMap to track event listener references per document to prevent memory leaks\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n }\n>();\n\nexport function createKeybuddy(\n doc: Document,\n filterFn: FilterFn = defaultFilter,\n) {\n let handlers: { [key: KeyString]: Handler[] } = {};\n const downKeys: Set<KeyString> = new Set();\n let activeScope = DEFAULT_SCOPE;\n\n let modifiers = 0; // Bitwise flag for active modifiers\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n {\n skipOther,\n }: {\n skipOther: boolean;\n } = {\n skipOther: false,\n },\n ): void => {\n const scope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const method: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n if (!handlers[key]) {\n handlers[key] = [];\n }\n const handler = handlers[key];\n if (process.env.NODE_ENV === 'development') {\n if (skipOther) {\n const action = handler.find((i) => i.skipOther);\n invariant(\n !action,\n \"Conflicting 'skipOther' property with action\",\n action,\n );\n }\n }\n\n handler.push({\n scope,\n method,\n shortcut,\n skipOther,\n });\n });\n };\n\n const unbindKeyProcess = (\n keysStr: string,\n deleteMethod: null | noop,\n deleteScope: string = DEFAULT_SCOPE,\n ): void => {\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n const handler = handlers[key];\n if (Array.isArray(handler)) {\n const handler = handlers[key].filter(\n ({ scope, method, shortcut: methodShortcut }: Handler) =>\n !(\n scope === deleteScope &&\n methodShortcut.mods === shortcut.mods &&\n isEqArray(methodShortcut.special, shortcut.special) &&\n (deleteMethod === null ? true : method === deleteMethod)\n ),\n );\n if (handler.length) {\n handlers[key] = handler;\n } else {\n delete handlers[key];\n }\n }\n });\n };\n\n const unbindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n return unbindKeyProcess(keysStr, deleteMethod, deleteScope);\n };\n\n const unsafeUnbindKey = (keysStr: string, scope?: string) =>\n unbindKeyProcess(keysStr, null, scope);\n\n const dispatch = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n if (!filterFn(e)) {\n return;\n }\n\n // fix firefox behavior when caps lock fires three times onkeydown\n // and don't fire at all onkeyup (Firefox 72)\n if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\n // Check if key is not a modifier\n const isModifierKey =\n key === ('SHIFT' as KeyString) ||\n key === ('ALT' as KeyString) ||\n key === ('CTRL' as KeyString) ||\n key === ('META' as KeyString);\n if (!isModifierKey && !downKeys.has(key)) {\n downKeys.add(key);\n }\n // See if we need to ignore the keypress (filter() can can be overridden)\n // by default ignore key presses if a select, textarea, or input is focused\n // if (!assignKey.filter.call(this, event)) return;\n\n // abort if no potentially matching shortcuts found\n if (!(key in handlers)) {\n return;\n }\n\n const currentHandlers = handlers[key].filter(\n ({ scope, shortcut: { special, mods } }) => {\n if (scope !== activeScope) {\n return false;\n }\n\n return isEqArray(special, Array.from(downKeys)) && mods === modifiers;\n },\n );\n\n const primaryAction: Handler | undefined = currentHandlers.find(\n (action) => action.skipOther,\n );\n if (primaryAction) {\n primaryAction.method(e);\n } else {\n currentHandlers.forEach(({ method }) => {\n method(e);\n });\n }\n };\n\n const cleanUp = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n // clean all for meta.\n // Main reason is ctrl+z (or any other native command not fires letter keyup on editable inputs)\n if (e.key && e.key.toLowerCase() === 'meta') {\n downKeys.clear();\n } else {\n downKeys.delete(key);\n }\n };\n\n const unbindScope = (deleteScope: string): void => {\n Object.keys(handlers).forEach((key) => {\n const keyString = key as KeyString;\n const handler = handlers[keyString].filter(\n ({ scope }: Handler) => scope !== deleteScope,\n );\n if (handler.length) {\n handlers[keyString] = handler;\n } else {\n delete handlers[keyString];\n }\n });\n };\n\n const setScope = (scope: string): void => {\n activeScope = scope;\n };\n\n const unbindAll = (): void => {\n handlers = {};\n downKeys.clear();\n };\n\n const reset = (): void => {\n downKeys.clear();\n };\n\n const destroy = (): void => {\n downKeys.clear();\n handlers = {};\n if (doc) {\n const listeners = documentListeners.get(doc);\n if (listeners) {\n doc.removeEventListener('keydown', listeners.dispatch);\n doc.removeEventListener('keyup', listeners.cleanUp);\n window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n // Remove old listeners if they exist\n destroy();\n\n // Store and add new listeners\n documentListeners.set(doc, { dispatch, cleanUp, reset });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n window.addEventListener('focus', reset);\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope: () => activeScope,\n destroy,\n };\n}\n","import { DEFAULT_SCOPE } from './constants';\nimport { createKeybuddy } from './keybuddy';\n\nexport { createKeybuddy };\n\nconst {\n bind,\n unbind,\n unsafeUnbind,\n getScope,\n setScope,\n unbindScope,\n unbindAll,\n destroy,\n} = createKeybuddy(document);\n\nexport const bindKey = bind;\nexport const unbindKey = unbind;\nexport const unsafeUnbindKey = unsafeUnbind;\nexport { setScope, unbindScope, unbindAll, getScope, destroy, DEFAULT_SCOPE };\nexport type { KeyString } from './constants';\nexport { MODS, SPECIAL } from './constants';\nexport default bind;\n"],"mappings":"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","names":["DEFAULT_SCOPE","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getKeyIdentifier","key","SPECIAL","getMods","keys","acc","MODIFIERS","getCombinations","keysStr","getKeyMap","keyCmd","isFirefox","isEditable","el","isEqArray","arr1","arr2","i","defaultFilter","isEditable","documentListeners","createKeybuddy","doc","filterFn","handlers","downKeys","activeScope","modifiers","bindKey","keysStr","scopeOrMethod","methodOrNull","skipOther","scope","method","getKeyMap","key","shortcut","unbindKeyProcess","deleteMethod","deleteScope","handler","methodShortcut","isEqArray","unbindKey","unsafeUnbindKey","dispatch","e","getKeyIdentifier","isFirefox","CAPS_LOCK_KEY","updateModifiers","currentHandlers","special","mods","primaryAction","action","cleanUp","unbindScope","keyString","setScope","unbindAll","reset","destroy","listeners","bind","unbind","unsafeUnbind","getScope","setScope","unbindScope","unbindAll","destroy","createKeybuddy","bindKey","unbindKey","unsafeUnbindKey","src_default","bind"]}
|
package/package.json
CHANGED
package/keybuddy.d.mts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
type noop = (e: KeyboardEvent) => void;
|
|
2
|
-
type FilterFn = (el: KeyboardEvent) => boolean;
|
|
3
|
-
declare function createKeybuddy(doc: Document, filterFn?: FilterFn): {
|
|
4
|
-
bind: (keysStr: string, scopeOrMethod: string | noop, methodOrNull?: noop, { skipOther, }?: {
|
|
5
|
-
skipOther: boolean;
|
|
6
|
-
}) => void;
|
|
7
|
-
unbind: (keysStr: string, scopeOrMethod: string | noop, methodOrNull?: noop) => void;
|
|
8
|
-
unsafeUnbind: (keysStr: string, scope?: string) => void;
|
|
9
|
-
unbindScope: (deleteScope: string) => void;
|
|
10
|
-
setScope: (scope: string) => void;
|
|
11
|
-
unbindAll: () => void;
|
|
12
|
-
getScope: () => string;
|
|
13
|
-
destroy: () => void;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export { createKeybuddy };
|
package/keybuddy.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
type noop = (e: KeyboardEvent) => void;
|
|
2
|
-
type FilterFn = (el: KeyboardEvent) => boolean;
|
|
3
|
-
declare function createKeybuddy(doc: Document, filterFn?: FilterFn): {
|
|
4
|
-
bind: (keysStr: string, scopeOrMethod: string | noop, methodOrNull?: noop, { skipOther, }?: {
|
|
5
|
-
skipOther: boolean;
|
|
6
|
-
}) => void;
|
|
7
|
-
unbind: (keysStr: string, scopeOrMethod: string | noop, methodOrNull?: noop) => void;
|
|
8
|
-
unsafeUnbind: (keysStr: string, scope?: string) => void;
|
|
9
|
-
unbindScope: (deleteScope: string) => void;
|
|
10
|
-
setScope: (scope: string) => void;
|
|
11
|
-
unbindAll: () => void;
|
|
12
|
-
getScope: () => string;
|
|
13
|
-
destroy: () => void;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export { createKeybuddy };
|
package/keybuddy.global.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
/* keybuddy - Modern keyboard shortcuts library */
|
|
2
|
-
"use strict";var keybuddy=(()=>{var b=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var q=Object.prototype.hasOwnProperty;var $=(e,r)=>{for(var t in r)b(e,t,{get:r[t],enumerable:!0})},V=(e,r,t,s)=>{if(r&&typeof r=="object"||typeof r=="function")for(let c of B(r))!q.call(e,c)&&c!==t&&b(e,c,{get:()=>r[c],enumerable:!(s=_(r,c))||s.enumerable});return e};var Y=e=>V(b({},"__esModule",{value:!0}),e);var G={};$(G,{createKeybuddy:()=>z});var i={SHIFT:1,ALT:2,CTRL:4,META:8},K={"\u21E7":i.SHIFT,shift:i.SHIFT,"\u2325":i.ALT,alt:i.ALT,option:i.ALT,"\u2303":i.CTRL,ctrl:i.CTRL,control:i.CTRL,"\u2318":i.META,cmd:i.META,command:i.META},u={backspace:"Backspace",tab:"Tab",clear:"Clear",enter:"Enter",return:"Enter",esc:"Escape",escape:"Escape",space:" ",left:"ArrowLeft",up:"ArrowUp",right:"ArrowRight",down:"ArrowDown",del:"Delete",delete:"Delete",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",comma:",",".":".","/":"/","`":"`","-":"-","=":"=",";":";","'":"'","[":"[","]":"]","\\":"\\",Meta:"Meta",MetaLeft:"Meta",MetaRight:"Meta",OS:"Meta",ContextMenu:"Meta",ArrowLeft:"ArrowLeft",ArrowUp:"ArrowUp",ArrowRight:"ArrowRight",ArrowDown:"ArrowDown",Backspace:"Backspace",Tab:"Tab",Clear:"Clear",Enter:"Enter",Escape:"Escape",Delete:"Delete",Home:"Home",End:"End",PageUp:"PageUp",PageDown:"PageDown"},x="CapsLock";var S=e=>u[e]||e.toUpperCase(),P=e=>{let r=0;return e.shiftKey&&(r|=i.SHIFT),e.altKey&&(r|=i.ALT),e.ctrlKey&&(r|=i.CTRL),e.metaKey&&(r|=i.META),r};var j=e=>u[e]||e.toUpperCase(),J=e=>e.reduce((r,t)=>(t in K?r.mods|=K[t]:r.special.push(u[t]||t.toUpperCase()),r),{mods:0,special:[]}),W=e=>{let t=e.replace(/\s/g,"").split(",");return t[t.length-1]===""&&(t[t.length-2]+=","),t},h=e=>W(e).map(t=>{let s=t.split("+"),c=s[s.length-1];return{key:j(c),shortcut:J(s)}});var D=navigator.userAgent.includes("Firefox"),I=e=>e.isContentEditable||e.tagName==="INPUT"||e.tagName==="SELECT"||e.tagName==="TEXTAREA",A=(e,r)=>{if(e.length!==r.length)return!1;for(let t=0;t<e.length;t++)if(e[t]!==r[t])return!1;return!0};var X=e=>e&&!I(e.target),v=new WeakMap;function z(e,r=X){let t={},s=new Set,c="all",m=0,F=(o,n,d=()=>{},{skipOther:a}={skipOther:!1})=>{let p=typeof n=="function"?"all":n,l=typeof n=="function"?n:d;h(o).forEach(({key:f,shortcut:g})=>{t[f]||(t[f]=[]),t[f].push({scope:p,method:l,shortcut:g,skipOther:a})})},L=(o,n,d="all")=>{h(o).forEach(({key:a,shortcut:p})=>{let l=t[a];if(Array.isArray(l)){let f=t[a].filter(({scope:g,method:C,shortcut:E})=>!(g===d&&E.mods===p.mods&&A(E.special,p.special)&&(n===null||C===n)));f.length?t[a]=f:delete t[a]}})},U=(o,n,d=()=>{})=>L(o,typeof n=="function"?n:d,typeof n=="function"?"all":n),H=(o,n)=>L(o,null,n),w=o=>{let n=S(o.key);if(!r(o)||D&&n===x||(m=P(o),!(n==="SHIFT"||n==="ALT"||n==="CTRL"||n==="META")&&!s.has(n)&&s.add(n),!(n in t)))return;let a=t[n].filter(({scope:l,shortcut:{special:f,mods:g}})=>l!==c?!1:A(f,Array.from(s))&&g===m),p=a.find(l=>l.skipOther);p?p.method(o):a.forEach(({method:l})=>{l(o)})},T=o=>{let n=S(o.key);o.key&&o.key.toLowerCase()==="meta"?s.clear():s.delete(n)},O=o=>{Object.keys(t).forEach(n=>{let d=n,a=t[d].filter(({scope:p})=>p!==o);a.length?t[d]=a:delete t[d]})},N=o=>{c=o},R=()=>{t={},s.clear()},M=()=>{s.clear()},k=()=>{if(s.clear(),t={},e){let o=v.get(e);o&&(e.removeEventListener("keydown",o.dispatch),e.removeEventListener("keyup",o.cleanUp),window.removeEventListener("focus",o.reset),v.delete(e))}};return k(),v.set(e,{dispatch:w,cleanUp:T,reset:M}),e.addEventListener("keydown",w),e.addEventListener("keyup",T),window.addEventListener("focus",M),{bind:F,unbind:U,unsafeUnbind:H,unbindScope:O,setScope:N,unbindAll:R,getScope:()=>c,destroy:k}}return Y(G);})();
|
|
3
|
-
//# sourceMappingURL=keybuddy.global.js.map
|
package/keybuddy.global.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/keybuddy.ts","../src/constants.ts","../src/helpers/keyboard.ts","../src/helpers/keymap.ts","../src/helpers/utils.ts"],"sourcesContent":["import { CAPS_LOCK_KEY, DEFAULT_SCOPE, KeyString } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype noop = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: noop;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\n// WeakMap to track event listener references per document to prevent memory leaks\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n }\n>();\n\nexport function createKeybuddy(\n doc: Document,\n filterFn: FilterFn = defaultFilter,\n) {\n let handlers: { [key: KeyString]: Handler[] } = {};\n const downKeys: Set<KeyString> = new Set();\n let activeScope = DEFAULT_SCOPE;\n\n let modifiers = 0; // Bitwise flag for active modifiers\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n {\n skipOther,\n }: {\n skipOther: boolean;\n } = {\n skipOther: false,\n },\n ): void => {\n const scope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const method: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n if (!handlers[key]) {\n handlers[key] = [];\n }\n const handler = handlers[key];\n if (process.env.NODE_ENV === 'development') {\n if (skipOther) {\n const action = handler.find((i) => i.skipOther);\n invariant(\n !action,\n \"Conflicting 'skipOther' property with action\",\n action,\n );\n }\n }\n\n handler.push({\n scope,\n method,\n shortcut,\n skipOther,\n });\n });\n };\n\n const unbindKeyProcess = (\n keysStr: string,\n deleteMethod: null | noop,\n deleteScope: string = DEFAULT_SCOPE,\n ): void => {\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n const handler = handlers[key];\n if (Array.isArray(handler)) {\n const handler = handlers[key].filter(\n ({ scope, method, shortcut: methodShortcut }: Handler) =>\n !(\n scope === deleteScope &&\n methodShortcut.mods === shortcut.mods &&\n isEqArray(methodShortcut.special, shortcut.special) &&\n (deleteMethod === null ? true : method === deleteMethod)\n ),\n );\n if (handler.length) {\n handlers[key] = handler;\n } else {\n delete handlers[key];\n }\n }\n });\n };\n\n const unbindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n return unbindKeyProcess(keysStr, deleteMethod, deleteScope);\n };\n\n const unsafeUnbindKey = (keysStr: string, scope?: string) =>\n unbindKeyProcess(keysStr, null, scope);\n\n const dispatch = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n if (!filterFn(e)) {\n return;\n }\n\n // fix firefox behavior when caps lock fires three times onkeydown\n // and don't fire at all onkeyup (Firefox 72)\n if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\n // Check if key is not a modifier\n const isModifierKey =\n key === ('SHIFT' as KeyString) ||\n key === ('ALT' as KeyString) ||\n key === ('CTRL' as KeyString) ||\n key === ('META' as KeyString);\n if (!isModifierKey && !downKeys.has(key)) {\n downKeys.add(key);\n }\n // See if we need to ignore the keypress (filter() can can be overridden)\n // by default ignore key presses if a select, textarea, or input is focused\n // if (!assignKey.filter.call(this, event)) return;\n\n // abort if no potentially matching shortcuts found\n if (!(key in handlers)) {\n return;\n }\n\n const currentHandlers = handlers[key].filter(\n ({ scope, shortcut: { special, mods } }) => {\n if (scope !== activeScope) {\n return false;\n }\n\n return isEqArray(special, Array.from(downKeys)) && mods === modifiers;\n },\n );\n\n const primaryAction: Handler | undefined = currentHandlers.find(\n (action) => action.skipOther,\n );\n if (primaryAction) {\n primaryAction.method(e);\n } else {\n currentHandlers.forEach(({ method }) => {\n method(e);\n });\n }\n };\n\n const cleanUp = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n // clean all for meta.\n // Main reason is ctrl+z (or any other native command not fires letter keyup on editable inputs)\n if (e.key && e.key.toLowerCase() === 'meta') {\n downKeys.clear();\n } else {\n downKeys.delete(key);\n }\n };\n\n const unbindScope = (deleteScope: string): void => {\n Object.keys(handlers).forEach((key) => {\n const keyString = key as KeyString;\n const handler = handlers[keyString].filter(\n ({ scope }: Handler) => scope !== deleteScope,\n );\n if (handler.length) {\n handlers[keyString] = handler;\n } else {\n delete handlers[keyString];\n }\n });\n };\n\n const setScope = (scope: string): void => {\n activeScope = scope;\n };\n\n const unbindAll = (): void => {\n handlers = {};\n downKeys.clear();\n };\n\n const reset = (): void => {\n downKeys.clear();\n };\n\n const destroy = (): void => {\n downKeys.clear();\n handlers = {};\n if (doc) {\n const listeners = documentListeners.get(doc);\n if (listeners) {\n doc.removeEventListener('keydown', listeners.dispatch);\n doc.removeEventListener('keyup', listeners.cleanUp);\n window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n // Remove old listeners if they exist\n destroy();\n\n // Store and add new listeners\n documentListeners.set(doc, { dispatch, cleanUp, reset });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n window.addEventListener('focus', reset);\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope: () => activeScope,\n destroy,\n };\n}\n","\nexport const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\n// Bitwise flags for modifiers - much faster than object-based tracking\nexport const MODS = {\n SHIFT: 0b0001, // 1\n ALT: 0b0010, // 2\n CTRL: 0b0100, // 4\n META: 0b1000, // 8\n} as const;\n\n// Map string modifier names to bitwise flags for parsing\nexport type ModifierNames = {\n '⇧': number;\n shift: number;\n '⌥': number;\n alt: number;\n option: number;\n '⌃': number;\n ctrl: number;\n control: number;\n '⌘': number;\n cmd: number;\n command: number;\n};\n\nexport const MODIFIERS: ModifierNames = {\n '⇧': MODS.SHIFT,\n shift: MODS.SHIFT,\n '⌥': MODS.ALT,\n alt: MODS.ALT,\n option: MODS.ALT,\n '⌃': MODS.CTRL,\n ctrl: MODS.CTRL,\n control: MODS.CTRL,\n '⌘': MODS.META,\n cmd: MODS.META,\n command: MODS.META,\n};\n\n// Modern key mapping using KeyboardEvent.key values\nexport const SPECIAL: { [key: string]: string } = {\n backspace: 'Backspace',\n tab: 'Tab',\n clear: 'Clear',\n enter: 'Enter',\n return: 'Enter',\n esc: 'Escape',\n escape: 'Escape',\n space: ' ',\n left: 'ArrowLeft',\n up: 'ArrowUp',\n right: 'ArrowRight',\n down: 'ArrowDown',\n del: 'Delete',\n delete: 'Delete',\n home: 'Home',\n end: 'End',\n pageup: 'PageUp',\n pagedown: 'PageDown',\n comma: ',',\n '.': '.',\n '/': '/',\n '`': '`',\n '-': '-',\n '=': '=',\n ';': ';',\n \"'\": \"'\",\n '[': '[',\n ']': ']',\n '\\\\': '\\\\',\n // Normalize Meta key variants\n 'Meta': 'Meta',\n 'MetaLeft': 'Meta',\n 'MetaRight': 'Meta', \n 'OS': 'Meta', // Some browsers use OS instead of Meta\n 'ContextMenu': 'Meta', // Right-click context menu key sometimes acts as Meta\n // Add identity mappings for already-normalized keys\n 'ArrowLeft': 'ArrowLeft',\n 'ArrowUp': 'ArrowUp',\n 'ArrowRight': 'ArrowRight',\n 'ArrowDown': 'ArrowDown',\n 'Backspace': 'Backspace',\n 'Tab': 'Tab',\n 'Clear': 'Clear',\n 'Enter': 'Enter',\n 'Escape': 'Escape',\n 'Delete': 'Delete',\n 'Home': 'Home',\n 'End': 'End',\n 'PageUp': 'PageUp',\n 'PageDown': 'PageDown',\n};\n\nexport const CAPS_LOCK_KEY = 'CapsLock';\n","import { KeyString, MODS, SPECIAL } from '../constants';\n\nexport const getKeyIdentifier = (key: string): KeyString => {\n return (SPECIAL[key] || key.toUpperCase()) as KeyString;\n};\n\nexport const updateModifiers = (e: KeyboardEvent): number => {\n let modifiers = 0;\n if (e.shiftKey) modifiers |= MODS.SHIFT;\n if (e.altKey) modifiers |= MODS.ALT;\n if (e.ctrlKey) modifiers |= MODS.CTRL;\n if (e.metaKey) modifiers |= MODS.META;\n return modifiers;\n};\n","import { KeyString, MODIFIERS, ModifierNames, SPECIAL } from '../constants';\n\nexport interface ParsedShortcut {\n mods: number; // Bitwise flag for modifiers\n special: string[];\n}\nexport interface KeyMap {\n key: KeyString;\n shortcut: ParsedShortcut;\n}\n\nexport const getKeyIdentifier = (key: string): KeyString =>\n (SPECIAL[key] || key.toUpperCase()) as KeyString;\n\nconst getMods = (keys: string[]): ParsedShortcut =>\n keys.reduce(\n (acc, key) => {\n if (key in MODIFIERS) {\n acc.mods |= MODIFIERS[key as keyof ModifierNames];\n } else {\n acc.special.push(SPECIAL[key] || key.toUpperCase());\n }\n return acc;\n },\n {\n mods: 0, // Start with no modifiers\n special: [],\n } as ParsedShortcut,\n );\n\nconst getCombinations = (keysStr: string): string[] => {\n const cleanKeys = keysStr.replace(/\\s/g, '');\n const keys = cleanKeys.split(',');\n if (keys[keys.length - 1] === '') {\n keys[keys.length - 2] += ',';\n }\n\n return keys;\n};\n\nexport const getKeyMap = (keysStr: string): KeyMap[] => {\n const keymap = getCombinations(keysStr);\n return keymap.map((keyCmd) => {\n const keys = keyCmd.split('+');\n const key = keys[keys.length - 1];\n const keyIdentifier = getKeyIdentifier(key);\n\n return {\n key: keyIdentifier,\n shortcut: getMods(keys),\n };\n });\n};\n","export const invariant = (\n condition: boolean,\n message: string,\n ...args: unknown[]\n) => {\n if (\n typeof process !== 'undefined' &&\n process.env &&\n process.env.NODE_ENV === 'development' &&\n !condition\n ) {\n throw new Error(\n `Invariant failed: ${message}${args.length ? ` ${JSON.stringify(args)}` : ''}`,\n );\n }\n};\n\nexport const isFirefox = navigator.userAgent.includes('Firefox');\n\nexport const isEditable = (el: HTMLElement): boolean =>\n el.isContentEditable ||\n el.tagName === 'INPUT' ||\n el.tagName === 'SELECT' ||\n el.tagName === 'TEXTAREA';\n\nexport const isEqArray = (\n arr1: (string | number)[],\n arr2: (string | number)[],\n): boolean => {\n if (arr1.length !== arr2.length) return false;\n\n for (let i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) return false;\n }\n\n return true;\n};\n"],"mappings":"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","names":["keybuddy_exports","__export","createKeybuddy","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getKeyIdentifier","key","SPECIAL","getMods","keys","acc","MODIFIERS","getCombinations","keysStr","getKeyMap","keyCmd","isFirefox","isEditable","el","isEqArray","arr1","arr2","i","defaultFilter","isEditable","documentListeners","createKeybuddy","doc","filterFn","handlers","downKeys","activeScope","modifiers","bindKey","keysStr","scopeOrMethod","methodOrNull","skipOther","scope","method","getKeyMap","key","shortcut","unbindKeyProcess","deleteMethod","deleteScope","handler","methodShortcut","isEqArray","unbindKey","unsafeUnbindKey","dispatch","e","getKeyIdentifier","isFirefox","CAPS_LOCK_KEY","updateModifiers","currentHandlers","special","mods","primaryAction","action","cleanUp","unbindScope","keyString","setScope","unbindAll","reset","destroy","listeners"]}
|
package/keybuddy.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
/* keybuddy - Modern keyboard shortcuts library */
|
|
2
|
-
"use strict";var b=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var q=Object.prototype.hasOwnProperty;var $=(e,r)=>{for(var t in r)b(e,t,{get:r[t],enumerable:!0})},V=(e,r,t,s)=>{if(r&&typeof r=="object"||typeof r=="function")for(let c of B(r))!q.call(e,c)&&c!==t&&b(e,c,{get:()=>r[c],enumerable:!(s=_(r,c))||s.enumerable});return e};var Y=e=>V(b({},"__esModule",{value:!0}),e);var G={};$(G,{createKeybuddy:()=>z});module.exports=Y(G);var i={SHIFT:1,ALT:2,CTRL:4,META:8},K={"\u21E7":i.SHIFT,shift:i.SHIFT,"\u2325":i.ALT,alt:i.ALT,option:i.ALT,"\u2303":i.CTRL,ctrl:i.CTRL,control:i.CTRL,"\u2318":i.META,cmd:i.META,command:i.META},u={backspace:"Backspace",tab:"Tab",clear:"Clear",enter:"Enter",return:"Enter",esc:"Escape",escape:"Escape",space:" ",left:"ArrowLeft",up:"ArrowUp",right:"ArrowRight",down:"ArrowDown",del:"Delete",delete:"Delete",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",comma:",",".":".","/":"/","`":"`","-":"-","=":"=",";":";","'":"'","[":"[","]":"]","\\":"\\",Meta:"Meta",MetaLeft:"Meta",MetaRight:"Meta",OS:"Meta",ContextMenu:"Meta",ArrowLeft:"ArrowLeft",ArrowUp:"ArrowUp",ArrowRight:"ArrowRight",ArrowDown:"ArrowDown",Backspace:"Backspace",Tab:"Tab",Clear:"Clear",Enter:"Enter",Escape:"Escape",Delete:"Delete",Home:"Home",End:"End",PageUp:"PageUp",PageDown:"PageDown"},x="CapsLock";var S=e=>u[e]||e.toUpperCase(),P=e=>{let r=0;return e.shiftKey&&(r|=i.SHIFT),e.altKey&&(r|=i.ALT),e.ctrlKey&&(r|=i.CTRL),e.metaKey&&(r|=i.META),r};var j=e=>u[e]||e.toUpperCase(),J=e=>e.reduce((r,t)=>(t in K?r.mods|=K[t]:r.special.push(u[t]||t.toUpperCase()),r),{mods:0,special:[]}),W=e=>{let t=e.replace(/\s/g,"").split(",");return t[t.length-1]===""&&(t[t.length-2]+=","),t},h=e=>W(e).map(t=>{let s=t.split("+"),c=s[s.length-1];return{key:j(c),shortcut:J(s)}});var D=navigator.userAgent.includes("Firefox"),I=e=>e.isContentEditable||e.tagName==="INPUT"||e.tagName==="SELECT"||e.tagName==="TEXTAREA",A=(e,r)=>{if(e.length!==r.length)return!1;for(let t=0;t<e.length;t++)if(e[t]!==r[t])return!1;return!0};var X=e=>e&&!I(e.target),v=new WeakMap;function z(e,r=X){let t={},s=new Set,c="all",m=0,F=(o,n,d=()=>{},{skipOther:a}={skipOther:!1})=>{let p=typeof n=="function"?"all":n,l=typeof n=="function"?n:d;h(o).forEach(({key:f,shortcut:g})=>{t[f]||(t[f]=[]),t[f].push({scope:p,method:l,shortcut:g,skipOther:a})})},L=(o,n,d="all")=>{h(o).forEach(({key:a,shortcut:p})=>{let l=t[a];if(Array.isArray(l)){let f=t[a].filter(({scope:g,method:C,shortcut:E})=>!(g===d&&E.mods===p.mods&&A(E.special,p.special)&&(n===null||C===n)));f.length?t[a]=f:delete t[a]}})},U=(o,n,d=()=>{})=>L(o,typeof n=="function"?n:d,typeof n=="function"?"all":n),H=(o,n)=>L(o,null,n),w=o=>{let n=S(o.key);if(!r(o)||D&&n===x||(m=P(o),!(n==="SHIFT"||n==="ALT"||n==="CTRL"||n==="META")&&!s.has(n)&&s.add(n),!(n in t)))return;let a=t[n].filter(({scope:l,shortcut:{special:f,mods:g}})=>l!==c?!1:A(f,Array.from(s))&&g===m),p=a.find(l=>l.skipOther);p?p.method(o):a.forEach(({method:l})=>{l(o)})},T=o=>{let n=S(o.key);o.key&&o.key.toLowerCase()==="meta"?s.clear():s.delete(n)},O=o=>{Object.keys(t).forEach(n=>{let d=n,a=t[d].filter(({scope:p})=>p!==o);a.length?t[d]=a:delete t[d]})},N=o=>{c=o},R=()=>{t={},s.clear()},M=()=>{s.clear()},k=()=>{if(s.clear(),t={},e){let o=v.get(e);o&&(e.removeEventListener("keydown",o.dispatch),e.removeEventListener("keyup",o.cleanUp),window.removeEventListener("focus",o.reset),v.delete(e))}};return k(),v.set(e,{dispatch:w,cleanUp:T,reset:M}),e.addEventListener("keydown",w),e.addEventListener("keyup",T),window.addEventListener("focus",M),{bind:F,unbind:U,unsafeUnbind:H,unbindScope:O,setScope:N,unbindAll:R,getScope:()=>c,destroy:k}}0&&(module.exports={createKeybuddy});
|
|
3
|
-
//# sourceMappingURL=keybuddy.js.map
|
package/keybuddy.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/keybuddy.ts","../src/constants.ts","../src/helpers/keyboard.ts","../src/helpers/keymap.ts","../src/helpers/utils.ts"],"sourcesContent":["import { CAPS_LOCK_KEY, DEFAULT_SCOPE, KeyString } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype noop = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: noop;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\n// WeakMap to track event listener references per document to prevent memory leaks\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n }\n>();\n\nexport function createKeybuddy(\n doc: Document,\n filterFn: FilterFn = defaultFilter,\n) {\n let handlers: { [key: KeyString]: Handler[] } = {};\n const downKeys: Set<KeyString> = new Set();\n let activeScope = DEFAULT_SCOPE;\n\n let modifiers = 0; // Bitwise flag for active modifiers\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n {\n skipOther,\n }: {\n skipOther: boolean;\n } = {\n skipOther: false,\n },\n ): void => {\n const scope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const method: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n if (!handlers[key]) {\n handlers[key] = [];\n }\n const handler = handlers[key];\n if (process.env.NODE_ENV === 'development') {\n if (skipOther) {\n const action = handler.find((i) => i.skipOther);\n invariant(\n !action,\n \"Conflicting 'skipOther' property with action\",\n action,\n );\n }\n }\n\n handler.push({\n scope,\n method,\n shortcut,\n skipOther,\n });\n });\n };\n\n const unbindKeyProcess = (\n keysStr: string,\n deleteMethod: null | noop,\n deleteScope: string = DEFAULT_SCOPE,\n ): void => {\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n const handler = handlers[key];\n if (Array.isArray(handler)) {\n const handler = handlers[key].filter(\n ({ scope, method, shortcut: methodShortcut }: Handler) =>\n !(\n scope === deleteScope &&\n methodShortcut.mods === shortcut.mods &&\n isEqArray(methodShortcut.special, shortcut.special) &&\n (deleteMethod === null ? true : method === deleteMethod)\n ),\n );\n if (handler.length) {\n handlers[key] = handler;\n } else {\n delete handlers[key];\n }\n }\n });\n };\n\n const unbindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n return unbindKeyProcess(keysStr, deleteMethod, deleteScope);\n };\n\n const unsafeUnbindKey = (keysStr: string, scope?: string) =>\n unbindKeyProcess(keysStr, null, scope);\n\n const dispatch = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n if (!filterFn(e)) {\n return;\n }\n\n // fix firefox behavior when caps lock fires three times onkeydown\n // and don't fire at all onkeyup (Firefox 72)\n if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\n // Check if key is not a modifier\n const isModifierKey =\n key === ('SHIFT' as KeyString) ||\n key === ('ALT' as KeyString) ||\n key === ('CTRL' as KeyString) ||\n key === ('META' as KeyString);\n if (!isModifierKey && !downKeys.has(key)) {\n downKeys.add(key);\n }\n // See if we need to ignore the keypress (filter() can can be overridden)\n // by default ignore key presses if a select, textarea, or input is focused\n // if (!assignKey.filter.call(this, event)) return;\n\n // abort if no potentially matching shortcuts found\n if (!(key in handlers)) {\n return;\n }\n\n const currentHandlers = handlers[key].filter(\n ({ scope, shortcut: { special, mods } }) => {\n if (scope !== activeScope) {\n return false;\n }\n\n return isEqArray(special, Array.from(downKeys)) && mods === modifiers;\n },\n );\n\n const primaryAction: Handler | undefined = currentHandlers.find(\n (action) => action.skipOther,\n );\n if (primaryAction) {\n primaryAction.method(e);\n } else {\n currentHandlers.forEach(({ method }) => {\n method(e);\n });\n }\n };\n\n const cleanUp = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n // clean all for meta.\n // Main reason is ctrl+z (or any other native command not fires letter keyup on editable inputs)\n if (e.key && e.key.toLowerCase() === 'meta') {\n downKeys.clear();\n } else {\n downKeys.delete(key);\n }\n };\n\n const unbindScope = (deleteScope: string): void => {\n Object.keys(handlers).forEach((key) => {\n const keyString = key as KeyString;\n const handler = handlers[keyString].filter(\n ({ scope }: Handler) => scope !== deleteScope,\n );\n if (handler.length) {\n handlers[keyString] = handler;\n } else {\n delete handlers[keyString];\n }\n });\n };\n\n const setScope = (scope: string): void => {\n activeScope = scope;\n };\n\n const unbindAll = (): void => {\n handlers = {};\n downKeys.clear();\n };\n\n const reset = (): void => {\n downKeys.clear();\n };\n\n const destroy = (): void => {\n downKeys.clear();\n handlers = {};\n if (doc) {\n const listeners = documentListeners.get(doc);\n if (listeners) {\n doc.removeEventListener('keydown', listeners.dispatch);\n doc.removeEventListener('keyup', listeners.cleanUp);\n window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n // Remove old listeners if they exist\n destroy();\n\n // Store and add new listeners\n documentListeners.set(doc, { dispatch, cleanUp, reset });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n window.addEventListener('focus', reset);\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope: () => activeScope,\n destroy,\n };\n}\n","\nexport const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\n// Bitwise flags for modifiers - much faster than object-based tracking\nexport const MODS = {\n SHIFT: 0b0001, // 1\n ALT: 0b0010, // 2\n CTRL: 0b0100, // 4\n META: 0b1000, // 8\n} as const;\n\n// Map string modifier names to bitwise flags for parsing\nexport type ModifierNames = {\n '⇧': number;\n shift: number;\n '⌥': number;\n alt: number;\n option: number;\n '⌃': number;\n ctrl: number;\n control: number;\n '⌘': number;\n cmd: number;\n command: number;\n};\n\nexport const MODIFIERS: ModifierNames = {\n '⇧': MODS.SHIFT,\n shift: MODS.SHIFT,\n '⌥': MODS.ALT,\n alt: MODS.ALT,\n option: MODS.ALT,\n '⌃': MODS.CTRL,\n ctrl: MODS.CTRL,\n control: MODS.CTRL,\n '⌘': MODS.META,\n cmd: MODS.META,\n command: MODS.META,\n};\n\n// Modern key mapping using KeyboardEvent.key values\nexport const SPECIAL: { [key: string]: string } = {\n backspace: 'Backspace',\n tab: 'Tab',\n clear: 'Clear',\n enter: 'Enter',\n return: 'Enter',\n esc: 'Escape',\n escape: 'Escape',\n space: ' ',\n left: 'ArrowLeft',\n up: 'ArrowUp',\n right: 'ArrowRight',\n down: 'ArrowDown',\n del: 'Delete',\n delete: 'Delete',\n home: 'Home',\n end: 'End',\n pageup: 'PageUp',\n pagedown: 'PageDown',\n comma: ',',\n '.': '.',\n '/': '/',\n '`': '`',\n '-': '-',\n '=': '=',\n ';': ';',\n \"'\": \"'\",\n '[': '[',\n ']': ']',\n '\\\\': '\\\\',\n // Normalize Meta key variants\n 'Meta': 'Meta',\n 'MetaLeft': 'Meta',\n 'MetaRight': 'Meta', \n 'OS': 'Meta', // Some browsers use OS instead of Meta\n 'ContextMenu': 'Meta', // Right-click context menu key sometimes acts as Meta\n // Add identity mappings for already-normalized keys\n 'ArrowLeft': 'ArrowLeft',\n 'ArrowUp': 'ArrowUp',\n 'ArrowRight': 'ArrowRight',\n 'ArrowDown': 'ArrowDown',\n 'Backspace': 'Backspace',\n 'Tab': 'Tab',\n 'Clear': 'Clear',\n 'Enter': 'Enter',\n 'Escape': 'Escape',\n 'Delete': 'Delete',\n 'Home': 'Home',\n 'End': 'End',\n 'PageUp': 'PageUp',\n 'PageDown': 'PageDown',\n};\n\nexport const CAPS_LOCK_KEY = 'CapsLock';\n","import { KeyString, MODS, SPECIAL } from '../constants';\n\nexport const getKeyIdentifier = (key: string): KeyString => {\n return (SPECIAL[key] || key.toUpperCase()) as KeyString;\n};\n\nexport const updateModifiers = (e: KeyboardEvent): number => {\n let modifiers = 0;\n if (e.shiftKey) modifiers |= MODS.SHIFT;\n if (e.altKey) modifiers |= MODS.ALT;\n if (e.ctrlKey) modifiers |= MODS.CTRL;\n if (e.metaKey) modifiers |= MODS.META;\n return modifiers;\n};\n","import { KeyString, MODIFIERS, ModifierNames, SPECIAL } from '../constants';\n\nexport interface ParsedShortcut {\n mods: number; // Bitwise flag for modifiers\n special: string[];\n}\nexport interface KeyMap {\n key: KeyString;\n shortcut: ParsedShortcut;\n}\n\nexport const getKeyIdentifier = (key: string): KeyString =>\n (SPECIAL[key] || key.toUpperCase()) as KeyString;\n\nconst getMods = (keys: string[]): ParsedShortcut =>\n keys.reduce(\n (acc, key) => {\n if (key in MODIFIERS) {\n acc.mods |= MODIFIERS[key as keyof ModifierNames];\n } else {\n acc.special.push(SPECIAL[key] || key.toUpperCase());\n }\n return acc;\n },\n {\n mods: 0, // Start with no modifiers\n special: [],\n } as ParsedShortcut,\n );\n\nconst getCombinations = (keysStr: string): string[] => {\n const cleanKeys = keysStr.replace(/\\s/g, '');\n const keys = cleanKeys.split(',');\n if (keys[keys.length - 1] === '') {\n keys[keys.length - 2] += ',';\n }\n\n return keys;\n};\n\nexport const getKeyMap = (keysStr: string): KeyMap[] => {\n const keymap = getCombinations(keysStr);\n return keymap.map((keyCmd) => {\n const keys = keyCmd.split('+');\n const key = keys[keys.length - 1];\n const keyIdentifier = getKeyIdentifier(key);\n\n return {\n key: keyIdentifier,\n shortcut: getMods(keys),\n };\n });\n};\n","export const invariant = (\n condition: boolean,\n message: string,\n ...args: unknown[]\n) => {\n if (\n typeof process !== 'undefined' &&\n process.env &&\n process.env.NODE_ENV === 'development' &&\n !condition\n ) {\n throw new Error(\n `Invariant failed: ${message}${args.length ? ` ${JSON.stringify(args)}` : ''}`,\n );\n }\n};\n\nexport const isFirefox = navigator.userAgent.includes('Firefox');\n\nexport const isEditable = (el: HTMLElement): boolean =>\n el.isContentEditable ||\n el.tagName === 'INPUT' ||\n el.tagName === 'SELECT' ||\n el.tagName === 'TEXTAREA';\n\nexport const isEqArray = (\n arr1: (string | number)[],\n arr2: (string | number)[],\n): boolean => {\n if (arr1.length !== arr2.length) return false;\n\n for (let i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) return false;\n }\n\n return true;\n};\n"],"mappings":"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","names":["keybuddy_exports","__export","createKeybuddy","__toCommonJS","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getKeyIdentifier","key","SPECIAL","getMods","keys","acc","MODIFIERS","getCombinations","keysStr","getKeyMap","keyCmd","isFirefox","isEditable","el","isEqArray","arr1","arr2","i","defaultFilter","isEditable","documentListeners","createKeybuddy","doc","filterFn","handlers","downKeys","activeScope","modifiers","bindKey","keysStr","scopeOrMethod","methodOrNull","skipOther","scope","method","getKeyMap","key","shortcut","unbindKeyProcess","deleteMethod","deleteScope","handler","methodShortcut","isEqArray","unbindKey","unsafeUnbindKey","dispatch","e","getKeyIdentifier","isFirefox","CAPS_LOCK_KEY","updateModifiers","currentHandlers","special","mods","primaryAction","action","cleanUp","unbindScope","keyString","setScope","unbindAll","reset","destroy","listeners"]}
|
package/keybuddy.mjs
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
/* keybuddy - Modern keyboard shortcuts library */
|
|
2
|
-
var i={SHIFT:1,ALT:2,CTRL:4,META:8},b={"\u21E7":i.SHIFT,shift:i.SHIFT,"\u2325":i.ALT,alt:i.ALT,option:i.ALT,"\u2303":i.CTRL,ctrl:i.CTRL,control:i.CTRL,"\u2318":i.META,cmd:i.META,command:i.META},u={backspace:"Backspace",tab:"Tab",clear:"Clear",enter:"Enter",return:"Enter",esc:"Escape",escape:"Escape",space:" ",left:"ArrowLeft",up:"ArrowUp",right:"ArrowRight",down:"ArrowDown",del:"Delete",delete:"Delete",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",comma:",",".":".","/":"/","`":"`","-":"-","=":"=",";":";","'":"'","[":"[","]":"]","\\":"\\",Meta:"Meta",MetaLeft:"Meta",MetaRight:"Meta",OS:"Meta",ContextMenu:"Meta",ArrowLeft:"ArrowLeft",ArrowUp:"ArrowUp",ArrowRight:"ArrowRight",ArrowDown:"ArrowDown",Backspace:"Backspace",Tab:"Tab",Clear:"Clear",Enter:"Enter",Escape:"Escape",Delete:"Delete",Home:"Home",End:"End",PageUp:"PageUp",PageDown:"PageDown"},C="CapsLock";var K=e=>u[e]||e.toUpperCase(),x=e=>{let o=0;return e.shiftKey&&(o|=i.SHIFT),e.altKey&&(o|=i.ALT),e.ctrlKey&&(o|=i.CTRL),e.metaKey&&(o|=i.META),o};var R=e=>u[e]||e.toUpperCase(),_=e=>e.reduce((o,t)=>(t in b?o.mods|=b[t]:o.special.push(u[t]||t.toUpperCase()),o),{mods:0,special:[]}),B=e=>{let t=e.replace(/\s/g,"").split(",");return t[t.length-1]===""&&(t[t.length-2]+=","),t},S=e=>B(e).map(t=>{let a=t.split("+"),f=a[a.length-1];return{key:R(f),shortcut:_(a)}});var P=navigator.userAgent.includes("Firefox"),D=e=>e.isContentEditable||e.tagName==="INPUT"||e.tagName==="SELECT"||e.tagName==="TEXTAREA",h=(e,o)=>{if(e.length!==o.length)return!1;for(let t=0;t<e.length;t++)if(e[t]!==o[t])return!1;return!0};var q=e=>e&&!D(e.target),A=new WeakMap;function se(e,o=q){let t={},a=new Set,f="all",m=0,I=(r,n,c=()=>{},{skipOther:s}={skipOther:!1})=>{let d=typeof n=="function"?"all":n,p=typeof n=="function"?n:c;S(r).forEach(({key:l,shortcut:g})=>{t[l]||(t[l]=[]),t[l].push({scope:d,method:p,shortcut:g,skipOther:s})})},v=(r,n,c="all")=>{S(r).forEach(({key:s,shortcut:d})=>{let p=t[s];if(Array.isArray(p)){let l=t[s].filter(({scope:g,method:k,shortcut:E})=>!(g===c&&E.mods===d.mods&&h(E.special,d.special)&&(n===null||k===n)));l.length?t[s]=l:delete t[s]}})},F=(r,n,c=()=>{})=>v(r,typeof n=="function"?n:c,typeof n=="function"?"all":n),U=(r,n)=>v(r,null,n),L=r=>{let n=K(r.key);if(!o(r)||P&&n===C||(m=x(r),!(n==="SHIFT"||n==="ALT"||n==="CTRL"||n==="META")&&!a.has(n)&&a.add(n),!(n in t)))return;let s=t[n].filter(({scope:p,shortcut:{special:l,mods:g}})=>p!==f?!1:h(l,Array.from(a))&&g===m),d=s.find(p=>p.skipOther);d?d.method(r):s.forEach(({method:p})=>{p(r)})},w=r=>{let n=K(r.key);r.key&&r.key.toLowerCase()==="meta"?a.clear():a.delete(n)},H=r=>{Object.keys(t).forEach(n=>{let c=n,s=t[c].filter(({scope:d})=>d!==r);s.length?t[c]=s:delete t[c]})},O=r=>{f=r},N=()=>{t={},a.clear()},T=()=>{a.clear()},M=()=>{if(a.clear(),t={},e){let r=A.get(e);r&&(e.removeEventListener("keydown",r.dispatch),e.removeEventListener("keyup",r.cleanUp),window.removeEventListener("focus",r.reset),A.delete(e))}};return M(),A.set(e,{dispatch:L,cleanUp:w,reset:T}),e.addEventListener("keydown",L),e.addEventListener("keyup",w),window.addEventListener("focus",T),{bind:I,unbind:F,unsafeUnbind:U,unbindScope:H,setScope:O,unbindAll:N,getScope:()=>f,destroy:M}}export{se as createKeybuddy};
|
|
3
|
-
//# sourceMappingURL=keybuddy.mjs.map
|
package/keybuddy.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/helpers/keyboard.ts","../src/helpers/keymap.ts","../src/helpers/utils.ts","../src/keybuddy.ts"],"sourcesContent":["\nexport const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\n// Bitwise flags for modifiers - much faster than object-based tracking\nexport const MODS = {\n SHIFT: 0b0001, // 1\n ALT: 0b0010, // 2\n CTRL: 0b0100, // 4\n META: 0b1000, // 8\n} as const;\n\n// Map string modifier names to bitwise flags for parsing\nexport type ModifierNames = {\n '⇧': number;\n shift: number;\n '⌥': number;\n alt: number;\n option: number;\n '⌃': number;\n ctrl: number;\n control: number;\n '⌘': number;\n cmd: number;\n command: number;\n};\n\nexport const MODIFIERS: ModifierNames = {\n '⇧': MODS.SHIFT,\n shift: MODS.SHIFT,\n '⌥': MODS.ALT,\n alt: MODS.ALT,\n option: MODS.ALT,\n '⌃': MODS.CTRL,\n ctrl: MODS.CTRL,\n control: MODS.CTRL,\n '⌘': MODS.META,\n cmd: MODS.META,\n command: MODS.META,\n};\n\n// Modern key mapping using KeyboardEvent.key values\nexport const SPECIAL: { [key: string]: string } = {\n backspace: 'Backspace',\n tab: 'Tab',\n clear: 'Clear',\n enter: 'Enter',\n return: 'Enter',\n esc: 'Escape',\n escape: 'Escape',\n space: ' ',\n left: 'ArrowLeft',\n up: 'ArrowUp',\n right: 'ArrowRight',\n down: 'ArrowDown',\n del: 'Delete',\n delete: 'Delete',\n home: 'Home',\n end: 'End',\n pageup: 'PageUp',\n pagedown: 'PageDown',\n comma: ',',\n '.': '.',\n '/': '/',\n '`': '`',\n '-': '-',\n '=': '=',\n ';': ';',\n \"'\": \"'\",\n '[': '[',\n ']': ']',\n '\\\\': '\\\\',\n // Normalize Meta key variants\n 'Meta': 'Meta',\n 'MetaLeft': 'Meta',\n 'MetaRight': 'Meta', \n 'OS': 'Meta', // Some browsers use OS instead of Meta\n 'ContextMenu': 'Meta', // Right-click context menu key sometimes acts as Meta\n // Add identity mappings for already-normalized keys\n 'ArrowLeft': 'ArrowLeft',\n 'ArrowUp': 'ArrowUp',\n 'ArrowRight': 'ArrowRight',\n 'ArrowDown': 'ArrowDown',\n 'Backspace': 'Backspace',\n 'Tab': 'Tab',\n 'Clear': 'Clear',\n 'Enter': 'Enter',\n 'Escape': 'Escape',\n 'Delete': 'Delete',\n 'Home': 'Home',\n 'End': 'End',\n 'PageUp': 'PageUp',\n 'PageDown': 'PageDown',\n};\n\nexport const CAPS_LOCK_KEY = 'CapsLock';\n","import { KeyString, MODS, SPECIAL } from '../constants';\n\nexport const getKeyIdentifier = (key: string): KeyString => {\n return (SPECIAL[key] || key.toUpperCase()) as KeyString;\n};\n\nexport const updateModifiers = (e: KeyboardEvent): number => {\n let modifiers = 0;\n if (e.shiftKey) modifiers |= MODS.SHIFT;\n if (e.altKey) modifiers |= MODS.ALT;\n if (e.ctrlKey) modifiers |= MODS.CTRL;\n if (e.metaKey) modifiers |= MODS.META;\n return modifiers;\n};\n","import { KeyString, MODIFIERS, ModifierNames, SPECIAL } from '../constants';\n\nexport interface ParsedShortcut {\n mods: number; // Bitwise flag for modifiers\n special: string[];\n}\nexport interface KeyMap {\n key: KeyString;\n shortcut: ParsedShortcut;\n}\n\nexport const getKeyIdentifier = (key: string): KeyString =>\n (SPECIAL[key] || key.toUpperCase()) as KeyString;\n\nconst getMods = (keys: string[]): ParsedShortcut =>\n keys.reduce(\n (acc, key) => {\n if (key in MODIFIERS) {\n acc.mods |= MODIFIERS[key as keyof ModifierNames];\n } else {\n acc.special.push(SPECIAL[key] || key.toUpperCase());\n }\n return acc;\n },\n {\n mods: 0, // Start with no modifiers\n special: [],\n } as ParsedShortcut,\n );\n\nconst getCombinations = (keysStr: string): string[] => {\n const cleanKeys = keysStr.replace(/\\s/g, '');\n const keys = cleanKeys.split(',');\n if (keys[keys.length - 1] === '') {\n keys[keys.length - 2] += ',';\n }\n\n return keys;\n};\n\nexport const getKeyMap = (keysStr: string): KeyMap[] => {\n const keymap = getCombinations(keysStr);\n return keymap.map((keyCmd) => {\n const keys = keyCmd.split('+');\n const key = keys[keys.length - 1];\n const keyIdentifier = getKeyIdentifier(key);\n\n return {\n key: keyIdentifier,\n shortcut: getMods(keys),\n };\n });\n};\n","export const invariant = (\n condition: boolean,\n message: string,\n ...args: unknown[]\n) => {\n if (\n typeof process !== 'undefined' &&\n process.env &&\n process.env.NODE_ENV === 'development' &&\n !condition\n ) {\n throw new Error(\n `Invariant failed: ${message}${args.length ? ` ${JSON.stringify(args)}` : ''}`,\n );\n }\n};\n\nexport const isFirefox = navigator.userAgent.includes('Firefox');\n\nexport const isEditable = (el: HTMLElement): boolean =>\n el.isContentEditable ||\n el.tagName === 'INPUT' ||\n el.tagName === 'SELECT' ||\n el.tagName === 'TEXTAREA';\n\nexport const isEqArray = (\n arr1: (string | number)[],\n arr2: (string | number)[],\n): boolean => {\n if (arr1.length !== arr2.length) return false;\n\n for (let i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) return false;\n }\n\n return true;\n};\n","import { CAPS_LOCK_KEY, DEFAULT_SCOPE, KeyString } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype noop = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: noop;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\n// WeakMap to track event listener references per document to prevent memory leaks\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n }\n>();\n\nexport function createKeybuddy(\n doc: Document,\n filterFn: FilterFn = defaultFilter,\n) {\n let handlers: { [key: KeyString]: Handler[] } = {};\n const downKeys: Set<KeyString> = new Set();\n let activeScope = DEFAULT_SCOPE;\n\n let modifiers = 0; // Bitwise flag for active modifiers\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n {\n skipOther,\n }: {\n skipOther: boolean;\n } = {\n skipOther: false,\n },\n ): void => {\n const scope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const method: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n if (!handlers[key]) {\n handlers[key] = [];\n }\n const handler = handlers[key];\n if (process.env.NODE_ENV === 'development') {\n if (skipOther) {\n const action = handler.find((i) => i.skipOther);\n invariant(\n !action,\n \"Conflicting 'skipOther' property with action\",\n action,\n );\n }\n }\n\n handler.push({\n scope,\n method,\n shortcut,\n skipOther,\n });\n });\n };\n\n const unbindKeyProcess = (\n keysStr: string,\n deleteMethod: null | noop,\n deleteScope: string = DEFAULT_SCOPE,\n ): void => {\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n const handler = handlers[key];\n if (Array.isArray(handler)) {\n const handler = handlers[key].filter(\n ({ scope, method, shortcut: methodShortcut }: Handler) =>\n !(\n scope === deleteScope &&\n methodShortcut.mods === shortcut.mods &&\n isEqArray(methodShortcut.special, shortcut.special) &&\n (deleteMethod === null ? true : method === deleteMethod)\n ),\n );\n if (handler.length) {\n handlers[key] = handler;\n } else {\n delete handlers[key];\n }\n }\n });\n };\n\n const unbindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n return unbindKeyProcess(keysStr, deleteMethod, deleteScope);\n };\n\n const unsafeUnbindKey = (keysStr: string, scope?: string) =>\n unbindKeyProcess(keysStr, null, scope);\n\n const dispatch = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n if (!filterFn(e)) {\n return;\n }\n\n // fix firefox behavior when caps lock fires three times onkeydown\n // and don't fire at all onkeyup (Firefox 72)\n if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\n // Check if key is not a modifier\n const isModifierKey =\n key === ('SHIFT' as KeyString) ||\n key === ('ALT' as KeyString) ||\n key === ('CTRL' as KeyString) ||\n key === ('META' as KeyString);\n if (!isModifierKey && !downKeys.has(key)) {\n downKeys.add(key);\n }\n // See if we need to ignore the keypress (filter() can can be overridden)\n // by default ignore key presses if a select, textarea, or input is focused\n // if (!assignKey.filter.call(this, event)) return;\n\n // abort if no potentially matching shortcuts found\n if (!(key in handlers)) {\n return;\n }\n\n const currentHandlers = handlers[key].filter(\n ({ scope, shortcut: { special, mods } }) => {\n if (scope !== activeScope) {\n return false;\n }\n\n return isEqArray(special, Array.from(downKeys)) && mods === modifiers;\n },\n );\n\n const primaryAction: Handler | undefined = currentHandlers.find(\n (action) => action.skipOther,\n );\n if (primaryAction) {\n primaryAction.method(e);\n } else {\n currentHandlers.forEach(({ method }) => {\n method(e);\n });\n }\n };\n\n const cleanUp = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n // clean all for meta.\n // Main reason is ctrl+z (or any other native command not fires letter keyup on editable inputs)\n if (e.key && e.key.toLowerCase() === 'meta') {\n downKeys.clear();\n } else {\n downKeys.delete(key);\n }\n };\n\n const unbindScope = (deleteScope: string): void => {\n Object.keys(handlers).forEach((key) => {\n const keyString = key as KeyString;\n const handler = handlers[keyString].filter(\n ({ scope }: Handler) => scope !== deleteScope,\n );\n if (handler.length) {\n handlers[keyString] = handler;\n } else {\n delete handlers[keyString];\n }\n });\n };\n\n const setScope = (scope: string): void => {\n activeScope = scope;\n };\n\n const unbindAll = (): void => {\n handlers = {};\n downKeys.clear();\n };\n\n const reset = (): void => {\n downKeys.clear();\n };\n\n const destroy = (): void => {\n downKeys.clear();\n handlers = {};\n if (doc) {\n const listeners = documentListeners.get(doc);\n if (listeners) {\n doc.removeEventListener('keydown', listeners.dispatch);\n doc.removeEventListener('keyup', listeners.cleanUp);\n window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n // Remove old listeners if they exist\n destroy();\n\n // Store and add new listeners\n documentListeners.set(doc, { dispatch, cleanUp, reset });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n window.addEventListener('focus', reset);\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope: () => activeScope,\n destroy,\n };\n}\n"],"mappings":"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","names":["MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getKeyIdentifier","key","SPECIAL","getMods","keys","acc","MODIFIERS","getCombinations","keysStr","getKeyMap","keyCmd","isFirefox","isEditable","el","isEqArray","arr1","arr2","i","defaultFilter","isEditable","documentListeners","createKeybuddy","doc","filterFn","handlers","downKeys","activeScope","modifiers","bindKey","keysStr","scopeOrMethod","methodOrNull","skipOther","scope","method","getKeyMap","key","shortcut","unbindKeyProcess","deleteMethod","deleteScope","handler","methodShortcut","isEqArray","unbindKey","unsafeUnbindKey","dispatch","e","getKeyIdentifier","isFirefox","CAPS_LOCK_KEY","updateModifiers","currentHandlers","special","mods","primaryAction","action","cleanUp","unbindScope","keyString","setScope","unbindAll","reset","destroy","listeners"]}
|