keybuddy 0.5.0 → 0.7.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2025 Elvin Dzhavadov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,9 +1,7 @@
1
1
  # keybuddy ⌨️
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/keybuddy.svg)](https://badge.fury.io/js/keybuddy)
4
- [![JSR](https://jsr.io/badges/@keybuddy/core)](https://jsr.io/@keybuddy/core)
5
4
  [![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
6
- [![Deno](https://img.shields.io/badge/Deno-000000?logo=deno&logoColor=white)](https://deno.land/)
7
5
 
8
6
  Define and dispatch shortcuts with easy using keybuddy.
9
7
 
@@ -20,7 +18,7 @@ import key from 'keybuddy';
20
18
 
21
19
  key('a', e => handleKeyPress('a'))
22
20
  key('shift+r', e => handleKeyPress('shift+r'))
23
- key('⌘+shift+r, ctrl+shit+r', e => handleKeyPress('ctrl+shift+r'))
21
+ key('⌘+shift+r, ctrl+shift+r', e => handleKeyPress('ctrl+shift+r'))
24
22
 
25
23
  ```
26
24
 
@@ -32,10 +30,17 @@ Differences:
32
30
  1. Support multiple keystrokes
33
31
  1. Custom scope not conflicting with default one
34
32
  1. Unbind requires an action (unsafeUnbindKey for backward compatibility)
35
- 1. Creator instance to replace document with any other DOM element
33
+ 1. Creator instance to replace document with any other DOM element
36
34
  1. More explicit API
37
35
  1. Provides new fixes and maintaining
38
36
 
37
+ ## Migrating from keymaster
38
+
39
+ **Key differences:**
40
+ - Unbinding requires the handler function: `unbindKey('ctrl+s', handler)` instead of `key.unbind('ctrl+s')`
41
+ - Use `unsafeUnbindKey('ctrl+s')` if you need the old behavior (removes all handlers)
42
+ - Import what you need: `import { bindKey, setScope } from 'keybuddy'` instead of global `key`
43
+ - Modern browsers only (no IE11)
39
44
 
40
45
  ## Supported keys
41
46
 
@@ -124,11 +129,13 @@ const myKeybuddy = createKeybuddy(iframe, filterFn?)
124
129
  myKeybuddy.bind('alt+b', action);
125
130
  ```
126
131
 
132
+ For iframe usage examples, see [cypress/component/iframe-bindings.spec.ts](cypress/component/iframe-bindings.spec.ts).
133
+
127
134
  ### Deno
128
135
  ```typescript
129
136
  import key from "jsr:@keybuddy/core";
130
137
 
131
138
  key('a', e => handleKeyPress('a'))
132
139
  key('shift+r', e => handleKeyPress('shift+r'))
133
- key('⌘+shift+r, ctrl+shit+r', e => handleKeyPress('ctrl+shift+r'))
140
+ key('⌘+shift+r, ctrl+shift+r', e => handleKeyPress('ctrl+shift+r'))
134
141
  ```
package/index.d.mts CHANGED
@@ -3,6 +3,39 @@ 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 KeyHandler = (e: KeyboardEvent) => void;
17
+ type FilterFn = (el: KeyboardEvent) => boolean;
18
+ declare function createKeybuddy(doc: Document, filterFn?: FilterFn): {
19
+ bind: (keysStr: string, scopeOrMethod: string | KeyHandler, methodOrNull?: KeyHandler, { skipOther, }?: {
20
+ skipOther: boolean;
21
+ }) => void;
22
+ unbind: (keysStr: string, scopeOrMethod: string | KeyHandler, methodOrNull?: KeyHandler) => 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
+ isBound: (keysStr: string, options?: {
30
+ scope?: string;
31
+ }) => boolean;
32
+ getBoundKeys: (options?: {
33
+ scope?: string;
34
+ }) => string[];
35
+ getHandlers: (keysStr: string, options?: {
36
+ scope?: string;
37
+ }) => KeyHandler[];
38
+ };
6
39
 
7
40
  declare const bind: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void, { skipOther, }?: {
8
41
  skipOther: boolean;
@@ -12,10 +45,19 @@ declare const setScope: (scope: string) => void;
12
45
  declare const unbindScope: (deleteScope: string) => void;
13
46
  declare const unbindAll: () => void;
14
47
  declare const destroy: () => void;
48
+ declare const isBound: (keysStr: string, options?: {
49
+ scope?: string;
50
+ }) => boolean;
51
+ declare const getBoundKeys: (options?: {
52
+ scope?: string;
53
+ }) => string[];
54
+ declare const getHandlers: (keysStr: string, options?: {
55
+ scope?: string;
56
+ }) => ((e: KeyboardEvent) => void)[];
15
57
  declare const bindKey: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void, { skipOther, }?: {
16
58
  skipOther: boolean;
17
59
  }) => void;
18
60
  declare const unbindKey: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void) => void;
19
61
  declare const unsafeUnbindKey: (keysStr: string, scope?: string) => void;
20
62
 
21
- export { DEFAULT_SCOPE, type KeyString, bindKey, bind as default, destroy, getScope, setScope, unbindAll, unbindKey, unbindScope, unsafeUnbindKey };
63
+ export { DEFAULT_SCOPE, type KeyString, MODS, SPECIAL, bindKey, createKeybuddy, bind as default, destroy, getBoundKeys, getHandlers, getScope, isBound, setScope, unbindAll, unbindKey, unbindScope, unsafeUnbindKey };
package/index.d.ts CHANGED
@@ -3,6 +3,39 @@ 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 KeyHandler = (e: KeyboardEvent) => void;
17
+ type FilterFn = (el: KeyboardEvent) => boolean;
18
+ declare function createKeybuddy(doc: Document, filterFn?: FilterFn): {
19
+ bind: (keysStr: string, scopeOrMethod: string | KeyHandler, methodOrNull?: KeyHandler, { skipOther, }?: {
20
+ skipOther: boolean;
21
+ }) => void;
22
+ unbind: (keysStr: string, scopeOrMethod: string | KeyHandler, methodOrNull?: KeyHandler) => 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
+ isBound: (keysStr: string, options?: {
30
+ scope?: string;
31
+ }) => boolean;
32
+ getBoundKeys: (options?: {
33
+ scope?: string;
34
+ }) => string[];
35
+ getHandlers: (keysStr: string, options?: {
36
+ scope?: string;
37
+ }) => KeyHandler[];
38
+ };
6
39
 
7
40
  declare const bind: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void, { skipOther, }?: {
8
41
  skipOther: boolean;
@@ -12,10 +45,19 @@ declare const setScope: (scope: string) => void;
12
45
  declare const unbindScope: (deleteScope: string) => void;
13
46
  declare const unbindAll: () => void;
14
47
  declare const destroy: () => void;
48
+ declare const isBound: (keysStr: string, options?: {
49
+ scope?: string;
50
+ }) => boolean;
51
+ declare const getBoundKeys: (options?: {
52
+ scope?: string;
53
+ }) => string[];
54
+ declare const getHandlers: (keysStr: string, options?: {
55
+ scope?: string;
56
+ }) => ((e: KeyboardEvent) => void)[];
15
57
  declare const bindKey: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void, { skipOther, }?: {
16
58
  skipOther: boolean;
17
59
  }) => void;
18
60
  declare const unbindKey: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void) => void;
19
61
  declare const unsafeUnbindKey: (keysStr: string, scope?: string) => void;
20
62
 
21
- export { DEFAULT_SCOPE, type KeyString, bindKey, bind as default, destroy, getScope, setScope, unbindAll, unbindKey, unbindScope, unsafeUnbindKey };
63
+ export { DEFAULT_SCOPE, type KeyString, MODS, SPECIAL, bindKey, createKeybuddy, bind as default, destroy, getBoundKeys, getHandlers, getScope, isBound, 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 E=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var Y=(e,r)=>{for(var t in r)E(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&&E(e,c,{get:()=>r[c],enumerable:!(s=q(r,c))||s.enumerable});return e};var J=e=>j(E({},"__esModule",{value:!0}),e);var de={};Y(de,{DEFAULT_SCOPE:()=>u,bindKey:()=>ie,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},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},y={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=>y[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 W=e=>y[e]||e.toUpperCase(),X=e=>e.reduce((r,t)=>(t in K?r.mods|=K[t]:r.special.push(y[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 D=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 I(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: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:k,shortcut:b})=>!(g===d&&b.mods===p.mods&&A(b.special,p.special)&&(n===null||k===n)));f.length?t[a]=f:delete t[a]}})},O=(o,n,d=()=>{})=>L(o,typeof n=="function"?n:d,typeof n=="function"?"all":n),N=(o,n)=>L(o,null,n),T=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)})},w=o=>{let n=S(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()},M=()=>{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:T,cleanUp:w,reset:M}),e.addEventListener("keydown",T),e.addEventListener("keyup",w),window.addEventListener("focus",M),{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}=I(document),ie=F,se=Q,ae=Z;var ce=F;return J(de);})();
2
+ "use strict";var keybuddy=(()=>{var A=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var W=Object.prototype.hasOwnProperty;var $=(e,o)=>{for(var t in o)A(e,t,{get:o[t],enumerable:!0})},Y=(e,o,t,p)=>{if(o&&typeof o=="object"||typeof o=="function")for(let u of V(o))!W.call(e,u)&&u!==t&&A(e,u,{get:()=>o[u],enumerable:!(p=q(o,u))||p.enumerable});return e};var J=e=>Y(A({},"__esModule",{value:!0}),e);var fe={};$(fe,{DEFAULT_SCOPE:()=>g,MODS:()=>s,SPECIAL:()=>m,bindKey:()=>ce,createKeybuddy:()=>T,default:()=>pe,destroy:()=>oe,getBoundKeys:()=>ie,getHandlers:()=>ae,getScope:()=>ee,isBound:()=>se,setScope:()=>te,unbindAll:()=>re,unbindKey:()=>de,unbindScope:()=>ne,unsafeUnbindKey:()=>le});var g="all",s={SHIFT:1,ALT:2,CTRL:4,META:8},w={"\u21E7":s.SHIFT,shift:s.SHIFT,"\u2325":s.ALT,alt:s.ALT,option:s.ALT,"\u2303":s.CTRL,ctrl:s.CTRL,control:s.CTRL,"\u2318":s.META,cmd:s.META,command:s.META},m={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 E=e=>m[e]||e.toUpperCase(),D=e=>{let o=0;return e.shiftKey&&(o|=s.SHIFT),e.altKey&&(o|=s.ALT),e.ctrlKey&&(o|=s.CTRL),e.metaKey&&(o|=s.META),o};var X=e=>e.reduce((o,t)=>(t in w?o.mods|=w[t]:o.special.push(m[t]||t.toUpperCase()),o),{mods:0,special:[]}),z=e=>{let t=e.replace(/\s/g,"").split(",");return t[t.length-1]===""&&(t[t.length-2]+=","),t},K=e=>z(e).map(t=>{let p=t.split("+"),u=p[p.length-1];return{key:E(u),shortcut:X(p)}});var I=typeof navigator<"u"&&navigator.userAgent.includes("Firefox"),F=e=>e.isContentEditable||e.tagName==="INPUT"||e.tagName==="SELECT"||e.tagName==="TEXTAREA",b=(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 G=e=>e&&!F(e.target),v=new WeakMap;function T(e,o=G){let t={},p=new Set,u="all",h=0,O=(r,n,d=()=>{},{skipOther:c}={skipOther:!1})=>{let i=typeof n=="function"?"all":n,a=typeof n=="function"?n:d;K(r).forEach(({key:l,shortcut:f})=>{t[l]||(t[l]=[]),t[l].push({scope:i,method:a,shortcut:f,skipOther:c})})},L=(r,n,d="all")=>{K(r).forEach(({key:c,shortcut:i})=>{let a=t[c];if(Array.isArray(a)){let l=t[c].filter(({scope:f,method:y,shortcut:S})=>!(f===d&&S.mods===i.mods&&b(S.special,i.special)&&(n===null||y===n)));l.length?t[c]=l:delete t[c]}})},R=(r,n,d=()=>{})=>{L(r,typeof n=="function"?n:d,typeof n=="function"?"all":n)},N=(r,n)=>L(r,null,n),k=r=>{let n=E(r.key);if(!o(r)||I&&n===P||(h=D(r),!(n==="SHIFT"||n==="ALT"||n==="CTRL"||n==="META")&&!p.has(n)&&p.add(n),!(n in t)))return;let c=t[n].filter(({scope:a,shortcut:{special:l,mods:f}})=>a!==u?!1:b(l,Array.from(p))&&f===h),i=c.find(a=>a.skipOther);i?i.method(r):c.forEach(({method:a})=>{a(r)})},M=r=>{let n=E(r.key);r.key&&r.key.toLowerCase()==="meta"?p.clear():p.delete(n)},B=r=>{Object.keys(t).forEach(n=>{let d=n,c=t[d].filter(({scope:i})=>i!==r);c.length?t[d]=c:delete t[d]})},_=r=>{u=r},j=()=>{t={},p.clear()},H=()=>{p.clear()},C=()=>{if(p.clear(),t={},e){let r=v.get(e);r&&(e.removeEventListener("keydown",r.dispatch),e.removeEventListener("keyup",r.cleanUp),r.window.removeEventListener("focus",r.reset),v.delete(e))}};C();let x=e.defaultView||window;return v.set(e,{dispatch:k,cleanUp:M,reset:H,window:x}),e.addEventListener("keydown",k),e.addEventListener("keyup",M),x.addEventListener("focus",H),{bind:O,unbind:R,unsafeUnbind:N,unbindScope:B,setScope:_,unbindAll:j,getScope:()=>u,destroy:C,isBound:(r,n)=>{let d=n?.scope||u;return K(r).some(({key:i,shortcut:a})=>{let l=t[i];return l?l.some(f=>f.scope===d&&f.shortcut.mods===a.mods&&b(f.shortcut.special,a.special)):!1})},getBoundKeys:r=>{let n=r?.scope||u,d=new Set;return Object.values(t).forEach(c=>{c.forEach(i=>{if(i.scope===n){let a=[];i.shortcut.mods&s.CTRL&&a.push("ctrl"),i.shortcut.mods&s.ALT&&a.push("alt"),i.shortcut.mods&s.SHIFT&&a.push("shift"),i.shortcut.mods&s.META&&a.push("cmd");let l=i.shortcut.special.join("+")||"",f=[...a,l].filter(Boolean).join("+");f&&d.add(f)}})}),Array.from(d)},getHandlers:(r,n)=>{let d=n?.scope||u,c=K(r),i=[];return c.forEach(({key:a,shortcut:l})=>{let f=t[a];f&&f.forEach(y=>{y.scope===d&&y.shortcut.mods===l.mods&&b(y.shortcut.special,l.special)&&i.push(y.method)})}),i}}}var{bind:U,unbind:Q,unsafeUnbind:Z,getScope:ee,setScope:te,unbindScope:ne,unbindAll:re,destroy:oe,isBound:se,getBoundKeys:ie,getHandlers:ae}=T(document),ce=U,de=Q,le=Z;var pe=U;return J(fe);})();
3
3
  //# sourceMappingURL=index.global.js.map
@@ -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":";4bAAA,IAAAA,GAAA,GAAAC,EAAAD,GAAA,mBAAAE,EAAA,YAAAC,GAAA,YAAAC,GAAA,YAAAC,GAAA,aAAAC,GAAA,aAAAC,GAAA,cAAAC,GAAA,cAAAC,GAAA,gBAAAC,GAAA,oBAAAC,KCCO,IAAMC,EAAgB,MAOhBC,EAAO,CAClB,MAAO,EACP,IAAK,EACL,KAAM,EACN,KAAM,CACR,EAiBaC,EAA2B,CACtC,SAAKD,EAAK,MACV,MAAOA,EAAK,MACZ,SAAKA,EAAK,IACV,IAAKA,EAAK,IACV,OAAQA,EAAK,IACb,SAAKA,EAAK,KACV,KAAMA,EAAK,KACX,QAASA,EAAK,KACd,SAAKA,EAAK,KACV,IAAKA,EAAK,KACV,QAASA,EAAK,IAChB,EAGaE,EAAqC,CAChD,UAAW,YACX,IAAK,MACL,MAAO,QACP,MAAO,QACP,OAAQ,QACR,IAAK,SACL,OAAQ,SACR,MAAO,IACP,KAAM,YACN,GAAI,UACJ,MAAO,aACP,KAAM,YACN,IAAK,SACL,OAAQ,SACR,KAAM,OACN,IAAK,MACL,OAAQ,SACR,SAAU,WACV,MAAO,IACP,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,KAAM,KAEN,KAAQ,OACR,SAAY,OACZ,UAAa,OACb,GAAM,OACN,YAAe,OAEf,UAAa,YACb,QAAW,UACX,WAAc,aACd,UAAa,YACb,UAAa,YACb,IAAO,MACP,MAAS,QACT,MAAS,QACT,OAAU,SACV,OAAU,SACV,KAAQ,OACR,IAAO,MACP,OAAU,SACV,SAAY,UACd,EAEaC,EAAgB,WChGtB,IAAMC,EAAoBC,GACvBC,EAAQD,CAAG,GAAKA,EAAI,YAAY,EAG7BE,EAAmB,GAA6B,CAC3D,IAAIC,EAAY,EAChB,OAAI,EAAE,WAAUA,GAAaC,EAAK,OAC9B,EAAE,SAAQD,GAAaC,EAAK,KAC5B,EAAE,UAASD,GAAaC,EAAK,MAC7B,EAAE,UAASD,GAAaC,EAAK,MAC1BD,CACT,ECFO,IAAME,EAAoBC,GAC9BC,EAAQD,CAAG,GAAKA,EAAI,YAAY,EAE7BE,EAAWC,GACfA,EAAK,OACH,CAACC,EAAKJ,KACAA,KAAOK,EACTD,EAAI,MAAQC,EAAUL,CAA0B,EAEhDI,EAAI,QAAQ,KAAKH,EAAQD,CAAG,GAAKA,EAAI,YAAY,CAAC,EAE7CI,GAET,CACE,KAAM,EACN,QAAS,CAAC,CACZ,CACF,EAEIE,EAAmBC,GAA8B,CAErD,IAAMJ,EADYI,EAAQ,QAAQ,MAAO,EAAE,EACpB,MAAM,GAAG,EAChC,OAAIJ,EAAKA,EAAK,OAAS,CAAC,IAAM,KAC5BA,EAAKA,EAAK,OAAS,CAAC,GAAK,KAGpBA,CACT,EAEaK,EAAaD,GACTD,EAAgBC,CAAO,EACxB,IAAKE,GAAW,CAC5B,IAAMN,EAAOM,EAAO,MAAM,GAAG,EACvBT,EAAMG,EAAKA,EAAK,OAAS,CAAC,EAGhC,MAAO,CACL,IAHoBJ,EAAiBC,CAAG,EAIxC,SAAUE,EAAQC,CAAI,CACxB,CACF,CAAC,EClCI,IAAMO,EAAY,UAAU,UAAU,SAAS,SAAS,EAElDC,EAAcC,GACzBA,EAAG,mBACHA,EAAG,UAAY,SACfA,EAAG,UAAY,UACfA,EAAG,UAAY,WAEJC,EAAY,CACvBC,EACAC,IACY,CACZ,GAAID,EAAK,SAAWC,EAAK,OAAQ,MAAO,GAExC,QAASC,EAAI,EAAGA,EAAIF,EAAK,OAAQE,IAC/B,GAAIF,EAAKE,CAAC,IAAMD,EAAKC,CAAC,EAAG,MAAO,GAGlC,MAAO,EACT,ECrBA,IAAMC,EAAiB,GACrB,GAAK,CAACC,EAAW,EAAE,MAAqB,EAGpCC,EAAoB,IAAI,QASvB,SAASC,EACdC,EACAC,EAAqBL,EACrB,CACA,IAAIM,EAA4C,CAAC,EAC3CC,EAA2B,IAAI,IACjCC,EAAc,MAEdC,EAAY,EAEVC,EAAU,CACdC,EACAC,EACAC,EAAqB,IAAM,CAAC,EAC5B,CACE,UAAAC,CACF,EAEI,CACF,UAAW,EACb,IACS,CACT,IAAMC,EACJ,OAAOH,GAAkB,WAAa,MAAgBA,EAClDI,EACJ,OAAOJ,GAAkB,WAAaA,EAAgBC,EAExDI,EAAUN,CAAO,EAAE,QAAQ,CAAC,CAAE,IAAAO,EAAK,SAAAC,CAAS,IAAM,CAC3Cb,EAASY,CAAG,IACfZ,EAASY,CAAG,EAAI,CAAC,GAEHZ,EAASY,CAAG,EAYpB,KAAK,CACX,MAAAH,EACA,OAAAC,EACA,SAAAG,EACA,UAAAL,CACF,CAAC,CACH,CAAC,CACH,EAEMM,EAAmB,CACvBT,EACAU,EACAC,EAAsB,QACb,CACTL,EAAUN,CAAO,EAAE,QAAQ,CAAC,CAAE,IAAAO,EAAK,SAAAC,CAAS,IAAM,CAChD,IAAMI,EAAUjB,EAASY,CAAG,EAC5B,GAAI,MAAM,QAAQK,CAAO,EAAG,CAC1B,IAAMA,EAAUjB,EAASY,CAAG,EAAE,OAC5B,CAAC,CAAE,MAAAH,EAAO,OAAAC,EAAQ,SAAUQ,CAAe,IACzC,EACET,IAAUO,GACVE,EAAe,OAASL,EAAS,MACjCM,EAAUD,EAAe,QAASL,EAAS,OAAO,IACjDE,IAAiB,MAAcL,IAAWK,GAEjD,EACIE,EAAQ,OACVjB,EAASY,CAAG,EAAIK,EAEhB,OAAOjB,EAASY,CAAG,CAEvB,CACF,CAAC,CACH,EAEMQ,EAAY,CAChBf,EACAC,EACAC,EAAqB,IAAM,CAAC,IAMrBO,EAAiBT,EADtB,OAAOC,GAAkB,WAAaA,EAAgBC,EAFtD,OAAOD,GAAkB,WAAa,MAAgBA,CAGE,EAGtDe,EAAkB,CAAChB,EAAiBI,IACxCK,EAAiBT,EAAS,KAAMI,CAAK,EAEjCa,EAAYC,GAAqB,CACrC,IAAMX,EAAMY,EAAiBD,EAAE,GAAG,EA4BlC,GA1BI,CAACxB,EAASwB,CAAC,GAMXE,GAAab,IAAQc,IAIzBvB,EAAYwB,EAAgBJ,CAAC,EAQzB,EAJFX,IAAS,SACTA,IAAS,OACTA,IAAS,QACTA,IAAS,SACW,CAACX,EAAS,IAAIW,CAAG,GACrCX,EAAS,IAAIW,CAAG,EAOd,EAAEA,KAAOZ,IACX,OAGF,IAAM4B,EAAkB5B,EAASY,CAAG,EAAE,OACpC,CAAC,CAAE,MAAAH,EAAO,SAAU,CAAE,QAAAoB,EAAS,KAAAC,CAAK,CAAE,IAChCrB,IAAUP,EACL,GAGFiB,EAAUU,EAAS,MAAM,KAAK5B,CAAQ,CAAC,GAAK6B,IAAS3B,CAEhE,EAEM4B,EAAqCH,EAAgB,KACxDI,GAAWA,EAAO,SACrB,EACID,EACFA,EAAc,OAAOR,CAAC,EAEtBK,EAAgB,QAAQ,CAAC,CAAE,OAAAlB,CAAO,IAAM,CACtCA,EAAOa,CAAC,CACV,CAAC,CAEL,EAEMU,EAAWV,GAAqB,CACpC,IAAMX,EAAMY,EAAiBD,EAAE,GAAG,EAI9BA,EAAE,KAAOA,EAAE,IAAI,YAAY,IAAM,OACnCtB,EAAS,MAAM,EAEfA,EAAS,OAAOW,CAAG,CAEvB,EAEMsB,EAAelB,GAA8B,CACjD,OAAO,KAAKhB,CAAQ,EAAE,QAASY,GAAQ,CACrC,IAAMuB,EAAYvB,EACZK,EAAUjB,EAASmC,CAAS,EAAE,OAClC,CAAC,CAAE,MAAA1B,CAAM,IAAeA,IAAUO,CACpC,EACIC,EAAQ,OACVjB,EAASmC,CAAS,EAAIlB,EAEtB,OAAOjB,EAASmC,CAAS,CAE7B,CAAC,CACH,EAEMC,EAAY3B,GAAwB,CACxCP,EAAcO,CAChB,EAEM4B,EAAY,IAAY,CAC5BrC,EAAW,CAAC,EACZC,EAAS,MAAM,CACjB,EAEMqC,EAAQ,IAAY,CACxBrC,EAAS,MAAM,CACjB,EAEMsC,EAAU,IAAY,CAG1B,GAFAtC,EAAS,MAAM,EACfD,EAAW,CAAC,EACRF,EAAK,CACP,IAAM0C,EAAY5C,EAAkB,IAAIE,CAAG,EACvC0C,IACF1C,EAAI,oBAAoB,UAAW0C,EAAU,QAAQ,EACrD1C,EAAI,oBAAoB,QAAS0C,EAAU,OAAO,EAClD,OAAO,oBAAoB,QAASA,EAAU,KAAK,EACnD5C,EAAkB,OAAOE,CAAG,EAEhC,CACF,EAGA,OAAAyC,EAAQ,EAGR3C,EAAkB,IAAIE,EAAK,CAAE,SAAAwB,EAAU,QAAAW,EAAS,MAAAK,CAAM,CAAC,EACvDxC,EAAI,iBAAiB,UAAWwB,CAAQ,EACxCxB,EAAI,iBAAiB,QAASmC,CAAO,EACrC,OAAO,iBAAiB,QAASK,CAAK,EAE/B,CACL,KAAMlC,EACN,OAAQgB,EACR,aAAcC,EACd,YAAAa,EACA,SAAAE,EACA,UAAAC,EACA,SAAU,IAAMnC,EAChB,QAAAqC,CACF,CACF,CLrPA,GAAM,CACJ,KAAAE,EACA,OAAAC,EACA,aAAAC,EACA,SAAAC,GACA,SAAAC,GACA,YAAAC,GACA,UAAAC,GACA,QAAAC,EACF,EAAIC,EAAe,QAAQ,EAEdC,GAAUT,EACVU,GAAYT,EACZU,GAAkBT,EAG/B,IAAOU,GAAQC","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 isBound,\n getBoundKeys,\n getHandlers,\n} = createKeybuddy(document);\n\nexport const bindKey = bind;\nexport const unbindKey = unbind;\nexport const unsafeUnbindKey = unsafeUnbind;\n\nexport {\n setScope,\n unbindScope,\n unbindAll,\n getScope,\n destroy,\n isBound,\n getBoundKeys,\n getHandlers,\n DEFAULT_SCOPE,\n};\nexport type { KeyString } from './constants';\nexport { MODS, SPECIAL } from './constants';\n\nexport default bind;\n","export const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\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\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';\nimport { getKeyIdentifier } from './keyboard';\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\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 =\n typeof navigator !== 'undefined' && 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, MODS } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype KeyHandler = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: KeyHandler;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n window: Window & typeof globalThis;\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;\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | KeyHandler,\n methodOrNull: KeyHandler = () => {},\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: KeyHandler =\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 | KeyHandler,\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 | KeyHandler,\n methodOrNull: KeyHandler = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: KeyHandler =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n 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 if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\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\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 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 listeners.window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n destroy();\n\n const win = (doc.defaultView || window) as Window & typeof globalThis;\n\n documentListeners.set(doc, { dispatch, cleanUp, reset, window: win });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n win.addEventListener('focus', reset);\n\n const getScope = () => activeScope;\n\n const isBound = (keysStr: string, options?: { scope?: string }): boolean => {\n const scope = options?.scope || activeScope;\n const keyMaps = getKeyMap(keysStr);\n\n return keyMaps.some(({ key, shortcut }) => {\n const keyHandlers = handlers[key];\n if (!keyHandlers) return false;\n\n return keyHandlers.some(\n (h) =>\n h.scope === scope &&\n h.shortcut.mods === shortcut.mods &&\n isEqArray(h.shortcut.special, shortcut.special),\n );\n });\n };\n\n const getBoundKeys = (options?: { scope?: string }): string[] => {\n const scope = options?.scope || activeScope;\n const keys = new Set<string>();\n\n Object.values(handlers).forEach((handlerList) => {\n handlerList.forEach((handler) => {\n if (handler.scope === scope) {\n const modParts: string[] = [];\n if (handler.shortcut.mods & MODS.CTRL) modParts.push('ctrl');\n if (handler.shortcut.mods & MODS.ALT) modParts.push('alt');\n if (handler.shortcut.mods & MODS.SHIFT) modParts.push('shift');\n if (handler.shortcut.mods & MODS.META) modParts.push('cmd');\n\n const keyPart = handler.shortcut.special.join('+') || '';\n const fullKey = [...modParts, keyPart].filter(Boolean).join('+');\n if (fullKey) keys.add(fullKey);\n }\n });\n });\n\n return Array.from(keys);\n };\n\n const getHandlers = (\n keysStr: string,\n options?: { scope?: string },\n ): KeyHandler[] => {\n const scope = options?.scope || activeScope;\n const keyMaps = getKeyMap(keysStr);\n const result: KeyHandler[] = [];\n\n keyMaps.forEach(({ key, shortcut }) => {\n const keyHandlers = handlers[key];\n if (!keyHandlers) return;\n\n keyHandlers.forEach((h) => {\n if (\n h.scope === scope &&\n h.shortcut.mods === shortcut.mods &&\n isEqArray(h.shortcut.special, shortcut.special)\n ) {\n result.push(h.method);\n }\n });\n });\n\n return result;\n };\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope,\n destroy,\n isBound,\n getBoundKeys,\n getHandlers,\n };\n}\n"],"mappings":"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","names":["src_exports","__export","DEFAULT_SCOPE","MODS","SPECIAL","bindKey","createKeybuddy","src_default","destroy","getBoundKeys","getHandlers","getScope","isBound","setScope","unbindAll","unbindKey","unbindScope","unsafeUnbindKey","DEFAULT_SCOPE","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getMods","keys","acc","key","MODIFIERS","SPECIAL","getCombinations","keysStr","getKeyMap","keyCmd","getKeyIdentifier","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","win","options","keyHandlers","h","keys","handlerList","modParts","MODS","keyPart","fullKey","keyMaps","result","bind","unbind","unsafeUnbind","getScope","setScope","unbindScope","unbindAll","destroy","isBound","getBoundKeys","getHandlers","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 E=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var Y=(e,r)=>{for(var t in r)E(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&&E(e,c,{get:()=>r[c],enumerable:!(s=q(r,c))||s.enumerable});return e};var J=e=>j(E({},"__esModule",{value:!0}),e);var de={};Y(de,{DEFAULT_SCOPE:()=>u,bindKey:()=>ie,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},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},y={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=>y[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 W=e=>y[e]||e.toUpperCase(),X=e=>e.reduce((r,t)=>(t in K?r.mods|=K[t]:r.special.push(y[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 D=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 I(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: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:k,shortcut:b})=>!(g===d&&b.mods===p.mods&&A(b.special,p.special)&&(n===null||k===n)));f.length?t[a]=f:delete t[a]}})},O=(o,n,d=()=>{})=>L(o,typeof n=="function"?n:d,typeof n=="function"?"all":n),N=(o,n)=>L(o,null,n),T=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)})},w=o=>{let n=S(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()},M=()=>{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:T,cleanUp:w,reset:M}),e.addEventListener("keydown",T),e.addEventListener("keyup",w),window.addEventListener("focus",M),{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}=I(document),ie=F,se=Q,ae=Z;var ce=F;0&&(module.exports={DEFAULT_SCOPE,bindKey,destroy,getScope,setScope,unbindAll,unbindKey,unbindScope,unsafeUnbindKey});
2
+ "use strict";var A=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var W=Object.prototype.hasOwnProperty;var $=(e,o)=>{for(var t in o)A(e,t,{get:o[t],enumerable:!0})},Y=(e,o,t,p)=>{if(o&&typeof o=="object"||typeof o=="function")for(let u of V(o))!W.call(e,u)&&u!==t&&A(e,u,{get:()=>o[u],enumerable:!(p=q(o,u))||p.enumerable});return e};var J=e=>Y(A({},"__esModule",{value:!0}),e);var fe={};$(fe,{DEFAULT_SCOPE:()=>g,MODS:()=>s,SPECIAL:()=>m,bindKey:()=>ce,createKeybuddy:()=>T,default:()=>pe,destroy:()=>oe,getBoundKeys:()=>ie,getHandlers:()=>ae,getScope:()=>ee,isBound:()=>se,setScope:()=>te,unbindAll:()=>re,unbindKey:()=>de,unbindScope:()=>ne,unsafeUnbindKey:()=>le});module.exports=J(fe);var g="all",s={SHIFT:1,ALT:2,CTRL:4,META:8},w={"\u21E7":s.SHIFT,shift:s.SHIFT,"\u2325":s.ALT,alt:s.ALT,option:s.ALT,"\u2303":s.CTRL,ctrl:s.CTRL,control:s.CTRL,"\u2318":s.META,cmd:s.META,command:s.META},m={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 E=e=>m[e]||e.toUpperCase(),D=e=>{let o=0;return e.shiftKey&&(o|=s.SHIFT),e.altKey&&(o|=s.ALT),e.ctrlKey&&(o|=s.CTRL),e.metaKey&&(o|=s.META),o};var X=e=>e.reduce((o,t)=>(t in w?o.mods|=w[t]:o.special.push(m[t]||t.toUpperCase()),o),{mods:0,special:[]}),z=e=>{let t=e.replace(/\s/g,"").split(",");return t[t.length-1]===""&&(t[t.length-2]+=","),t},K=e=>z(e).map(t=>{let p=t.split("+"),u=p[p.length-1];return{key:E(u),shortcut:X(p)}});var I=typeof navigator<"u"&&navigator.userAgent.includes("Firefox"),F=e=>e.isContentEditable||e.tagName==="INPUT"||e.tagName==="SELECT"||e.tagName==="TEXTAREA",b=(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 G=e=>e&&!F(e.target),v=new WeakMap;function T(e,o=G){let t={},p=new Set,u="all",h=0,O=(r,n,d=()=>{},{skipOther:c}={skipOther:!1})=>{let i=typeof n=="function"?"all":n,a=typeof n=="function"?n:d;K(r).forEach(({key:l,shortcut:f})=>{t[l]||(t[l]=[]),t[l].push({scope:i,method:a,shortcut:f,skipOther:c})})},L=(r,n,d="all")=>{K(r).forEach(({key:c,shortcut:i})=>{let a=t[c];if(Array.isArray(a)){let l=t[c].filter(({scope:f,method:y,shortcut:S})=>!(f===d&&S.mods===i.mods&&b(S.special,i.special)&&(n===null||y===n)));l.length?t[c]=l:delete t[c]}})},R=(r,n,d=()=>{})=>{L(r,typeof n=="function"?n:d,typeof n=="function"?"all":n)},N=(r,n)=>L(r,null,n),k=r=>{let n=E(r.key);if(!o(r)||I&&n===P||(h=D(r),!(n==="SHIFT"||n==="ALT"||n==="CTRL"||n==="META")&&!p.has(n)&&p.add(n),!(n in t)))return;let c=t[n].filter(({scope:a,shortcut:{special:l,mods:f}})=>a!==u?!1:b(l,Array.from(p))&&f===h),i=c.find(a=>a.skipOther);i?i.method(r):c.forEach(({method:a})=>{a(r)})},M=r=>{let n=E(r.key);r.key&&r.key.toLowerCase()==="meta"?p.clear():p.delete(n)},B=r=>{Object.keys(t).forEach(n=>{let d=n,c=t[d].filter(({scope:i})=>i!==r);c.length?t[d]=c:delete t[d]})},_=r=>{u=r},j=()=>{t={},p.clear()},H=()=>{p.clear()},C=()=>{if(p.clear(),t={},e){let r=v.get(e);r&&(e.removeEventListener("keydown",r.dispatch),e.removeEventListener("keyup",r.cleanUp),r.window.removeEventListener("focus",r.reset),v.delete(e))}};C();let x=e.defaultView||window;return v.set(e,{dispatch:k,cleanUp:M,reset:H,window:x}),e.addEventListener("keydown",k),e.addEventListener("keyup",M),x.addEventListener("focus",H),{bind:O,unbind:R,unsafeUnbind:N,unbindScope:B,setScope:_,unbindAll:j,getScope:()=>u,destroy:C,isBound:(r,n)=>{let d=n?.scope||u;return K(r).some(({key:i,shortcut:a})=>{let l=t[i];return l?l.some(f=>f.scope===d&&f.shortcut.mods===a.mods&&b(f.shortcut.special,a.special)):!1})},getBoundKeys:r=>{let n=r?.scope||u,d=new Set;return Object.values(t).forEach(c=>{c.forEach(i=>{if(i.scope===n){let a=[];i.shortcut.mods&s.CTRL&&a.push("ctrl"),i.shortcut.mods&s.ALT&&a.push("alt"),i.shortcut.mods&s.SHIFT&&a.push("shift"),i.shortcut.mods&s.META&&a.push("cmd");let l=i.shortcut.special.join("+")||"",f=[...a,l].filter(Boolean).join("+");f&&d.add(f)}})}),Array.from(d)},getHandlers:(r,n)=>{let d=n?.scope||u,c=K(r),i=[];return c.forEach(({key:a,shortcut:l})=>{let f=t[a];f&&f.forEach(y=>{y.scope===d&&y.shortcut.mods===l.mods&&b(y.shortcut.special,l.special)&&i.push(y.method)})}),i}}}var{bind:U,unbind:Q,unsafeUnbind:Z,getScope:ee,setScope:te,unbindScope:ne,unbindAll:re,destroy:oe,isBound:se,getBoundKeys:ie,getHandlers:ae}=T(document),ce=U,de=Q,le=Z;var pe=U;0&&(module.exports={DEFAULT_SCOPE,MODS,SPECIAL,bindKey,createKeybuddy,destroy,getBoundKeys,getHandlers,getScope,isBound,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 isBound,\n getBoundKeys,\n getHandlers,\n} = createKeybuddy(document);\n\nexport const bindKey = bind;\nexport const unbindKey = unbind;\nexport const unsafeUnbindKey = unsafeUnbind;\n\nexport {\n setScope,\n unbindScope,\n unbindAll,\n getScope,\n destroy,\n isBound,\n getBoundKeys,\n getHandlers,\n DEFAULT_SCOPE,\n};\nexport type { KeyString } from './constants';\nexport { MODS, SPECIAL } from './constants';\n\nexport default bind;\n","export const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\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\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';\nimport { getKeyIdentifier } from './keyboard';\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\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 =\n typeof navigator !== 'undefined' && 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, MODS } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype KeyHandler = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: KeyHandler;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n window: Window & typeof globalThis;\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;\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | KeyHandler,\n methodOrNull: KeyHandler = () => {},\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: KeyHandler =\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 | KeyHandler,\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 | KeyHandler,\n methodOrNull: KeyHandler = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: KeyHandler =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n 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 if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\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\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 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 listeners.window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n destroy();\n\n const win = (doc.defaultView || window) as Window & typeof globalThis;\n\n documentListeners.set(doc, { dispatch, cleanUp, reset, window: win });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n win.addEventListener('focus', reset);\n\n const getScope = () => activeScope;\n\n const isBound = (keysStr: string, options?: { scope?: string }): boolean => {\n const scope = options?.scope || activeScope;\n const keyMaps = getKeyMap(keysStr);\n\n return keyMaps.some(({ key, shortcut }) => {\n const keyHandlers = handlers[key];\n if (!keyHandlers) return false;\n\n return keyHandlers.some(\n (h) =>\n h.scope === scope &&\n h.shortcut.mods === shortcut.mods &&\n isEqArray(h.shortcut.special, shortcut.special),\n );\n });\n };\n\n const getBoundKeys = (options?: { scope?: string }): string[] => {\n const scope = options?.scope || activeScope;\n const keys = new Set<string>();\n\n Object.values(handlers).forEach((handlerList) => {\n handlerList.forEach((handler) => {\n if (handler.scope === scope) {\n const modParts: string[] = [];\n if (handler.shortcut.mods & MODS.CTRL) modParts.push('ctrl');\n if (handler.shortcut.mods & MODS.ALT) modParts.push('alt');\n if (handler.shortcut.mods & MODS.SHIFT) modParts.push('shift');\n if (handler.shortcut.mods & MODS.META) modParts.push('cmd');\n\n const keyPart = handler.shortcut.special.join('+') || '';\n const fullKey = [...modParts, keyPart].filter(Boolean).join('+');\n if (fullKey) keys.add(fullKey);\n }\n });\n });\n\n return Array.from(keys);\n };\n\n const getHandlers = (\n keysStr: string,\n options?: { scope?: string },\n ): KeyHandler[] => {\n const scope = options?.scope || activeScope;\n const keyMaps = getKeyMap(keysStr);\n const result: KeyHandler[] = [];\n\n keyMaps.forEach(({ key, shortcut }) => {\n const keyHandlers = handlers[key];\n if (!keyHandlers) return;\n\n keyHandlers.forEach((h) => {\n if (\n h.scope === scope &&\n h.shortcut.mods === shortcut.mods &&\n isEqArray(h.shortcut.special, shortcut.special)\n ) {\n result.push(h.method);\n }\n });\n });\n\n return result;\n };\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope,\n destroy,\n isBound,\n getBoundKeys,\n getHandlers,\n };\n}\n"],"mappings":"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","names":["src_exports","__export","DEFAULT_SCOPE","MODS","SPECIAL","bindKey","createKeybuddy","src_default","destroy","getBoundKeys","getHandlers","getScope","isBound","setScope","unbindAll","unbindKey","unbindScope","unsafeUnbindKey","__toCommonJS","DEFAULT_SCOPE","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getMods","keys","acc","key","MODIFIERS","SPECIAL","getCombinations","keysStr","getKeyMap","keyCmd","getKeyIdentifier","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","win","options","keyHandlers","h","keys","handlerList","modParts","MODS","keyPart","fullKey","keyMaps","result","bind","unbind","unsafeUnbind","getScope","setScope","unbindScope","unbindAll","destroy","isBound","getBoundKeys","getHandlers","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",i={SHIFT:1,ALT:2,CTRL:4,META:8},E={"\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},y={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"},k="CapsLock";var K=e=>y[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 B=e=>y[e]||e.toUpperCase(),q=e=>e.reduce((o,t)=>(t in E?o.mods|=E[t]:o.special.push(y[t]||t.toUpperCase()),o),{mods:0,special:[]}),$=e=>{let t=e.replace(/\s/g,"").split(",");return t[t.length-1]===""&&(t[t.length-2]+=","),t},S=e=>$(e).map(t=>{let a=t.split("+"),u=a[a.length-1];return{key:B(u),shortcut:q(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 V=e=>e&&!D(e.target),A=new WeakMap;function U(e,o=V){let t={},a=new Set,u="all",m=0,F=(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:C,shortcut:b})=>!(g===c&&b.mods===d.mods&&h(b.special,d.special)&&(n===null||C===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=K(r.key);if(!o(r)||P&&n===k||(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!==u?!1:h(l,Array.from(a))&&g===m),d=s.find(p=>p.skipOther);d?d.method(r):s.forEach(({method:p})=>{p(r)})},T=r=>{let n=K(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=>{u=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:()=>u,destroy:M}}var{bind:I,unbind:Y,unsafeUnbind:j,getScope:ue,setScope:ge,unbindScope:ye,unbindAll:me,destroy:be}=U(document),Ee=I,Ke=Y,Se=j;var he=I;export{f as DEFAULT_SCOPE,Ee as bindKey,he as default,be as destroy,ue as getScope,ge as setScope,me as unbindAll,Ke as unbindKey,ye as unbindScope,Se as unsafeUnbindKey};
2
+ var g="all",s={SHIFT:1,ALT:2,CTRL:4,META:8},A={"\u21E7":s.SHIFT,shift:s.SHIFT,"\u2325":s.ALT,alt:s.ALT,option:s.ALT,"\u2303":s.CTRL,ctrl:s.CTRL,control:s.CTRL,"\u2318":s.META,cmd:s.META,command:s.META},m={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 E=t=>m[t]||t.toUpperCase(),x=t=>{let d=0;return t.shiftKey&&(d|=s.SHIFT),t.altKey&&(d|=s.ALT),t.ctrlKey&&(d|=s.CTRL),t.metaKey&&(d|=s.META),d};var j=t=>t.reduce((d,e)=>(e in A?d.mods|=A[e]:d.special.push(m[e]||e.toUpperCase()),d),{mods:0,special:[]}),q=t=>{let e=t.replace(/\s/g,"").split(",");return e[e.length-1]===""&&(e[e.length-2]+=","),e},K=t=>q(t).map(e=>{let f=e.split("+"),u=f[f.length-1];return{key:E(u),shortcut:j(f)}});var P=typeof navigator<"u"&&navigator.userAgent.includes("Firefox"),D=t=>t.isContentEditable||t.tagName==="INPUT"||t.tagName==="SELECT"||t.tagName==="TEXTAREA",b=(t,d)=>{if(t.length!==d.length)return!1;for(let e=0;e<t.length;e++)if(t[e]!==d[e])return!1;return!0};var V=t=>t&&!D(t.target),w=new WeakMap;function I(t,d=V){let e={},f=new Set,u="all",h=0,U=(r,n,c=()=>{},{skipOther:a}={skipOther:!1})=>{let o=typeof n=="function"?"all":n,i=typeof n=="function"?n:c;K(r).forEach(({key:l,shortcut:p})=>{e[l]||(e[l]=[]),e[l].push({scope:o,method:i,shortcut:p,skipOther:a})})},v=(r,n,c="all")=>{K(r).forEach(({key:a,shortcut:o})=>{let i=e[a];if(Array.isArray(i)){let l=e[a].filter(({scope:p,method:y,shortcut:S})=>!(p===c&&S.mods===o.mods&&b(S.special,o.special)&&(n===null||y===n)));l.length?e[a]=l:delete e[a]}})},O=(r,n,c=()=>{})=>{v(r,typeof n=="function"?n:c,typeof n=="function"?"all":n)},R=(r,n)=>v(r,null,n),T=r=>{let n=E(r.key);if(!d(r)||P&&n===C||(h=x(r),!(n==="SHIFT"||n==="ALT"||n==="CTRL"||n==="META")&&!f.has(n)&&f.add(n),!(n in e)))return;let a=e[n].filter(({scope:i,shortcut:{special:l,mods:p}})=>i!==u?!1:b(l,Array.from(f))&&p===h),o=a.find(i=>i.skipOther);o?o.method(r):a.forEach(({method:i})=>{i(r)})},L=r=>{let n=E(r.key);r.key&&r.key.toLowerCase()==="meta"?f.clear():f.delete(n)},N=r=>{Object.keys(e).forEach(n=>{let c=n,a=e[c].filter(({scope:o})=>o!==r);a.length?e[c]=a:delete e[c]})},B=r=>{u=r},_=()=>{e={},f.clear()},k=()=>{f.clear()},M=()=>{if(f.clear(),e={},t){let r=w.get(t);r&&(t.removeEventListener("keydown",r.dispatch),t.removeEventListener("keyup",r.cleanUp),r.window.removeEventListener("focus",r.reset),w.delete(t))}};M();let H=t.defaultView||window;return w.set(t,{dispatch:T,cleanUp:L,reset:k,window:H}),t.addEventListener("keydown",T),t.addEventListener("keyup",L),H.addEventListener("focus",k),{bind:U,unbind:O,unsafeUnbind:R,unbindScope:N,setScope:B,unbindAll:_,getScope:()=>u,destroy:M,isBound:(r,n)=>{let c=n?.scope||u;return K(r).some(({key:o,shortcut:i})=>{let l=e[o];return l?l.some(p=>p.scope===c&&p.shortcut.mods===i.mods&&b(p.shortcut.special,i.special)):!1})},getBoundKeys:r=>{let n=r?.scope||u,c=new Set;return Object.values(e).forEach(a=>{a.forEach(o=>{if(o.scope===n){let i=[];o.shortcut.mods&s.CTRL&&i.push("ctrl"),o.shortcut.mods&s.ALT&&i.push("alt"),o.shortcut.mods&s.SHIFT&&i.push("shift"),o.shortcut.mods&s.META&&i.push("cmd");let l=o.shortcut.special.join("+")||"",p=[...i,l].filter(Boolean).join("+");p&&c.add(p)}})}),Array.from(c)},getHandlers:(r,n)=>{let c=n?.scope||u,a=K(r),o=[];return a.forEach(({key:i,shortcut:l})=>{let p=e[i];p&&p.forEach(y=>{y.scope===c&&y.shortcut.mods===l.mods&&b(y.shortcut.special,l.special)&&o.push(y.method)})}),o}}}var{bind:F,unbind:W,unsafeUnbind:$,getScope:Ke,setScope:be,unbindScope:he,unbindAll:Se,destroy:Ae,isBound:we,getBoundKeys:ve,getHandlers:Te}=I(document),Le=F,ke=W,Me=$;var He=F;export{g as DEFAULT_SCOPE,s as MODS,m as SPECIAL,Le as bindKey,I as createKeybuddy,He as default,Ae as destroy,ve as getBoundKeys,Te as getHandlers,Ke as getScope,we as isBound,be as setScope,Se as unbindAll,ke as unbindKey,he as unbindScope,Me 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":["export const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\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\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';\nimport { getKeyIdentifier } from './keyboard';\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\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 =\n typeof navigator !== 'undefined' && 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, MODS } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype KeyHandler = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: KeyHandler;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n window: Window & typeof globalThis;\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;\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | KeyHandler,\n methodOrNull: KeyHandler = () => {},\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: KeyHandler =\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 | KeyHandler,\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 | KeyHandler,\n methodOrNull: KeyHandler = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: KeyHandler =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n 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 if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\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\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 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 listeners.window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n destroy();\n\n const win = (doc.defaultView || window) as Window & typeof globalThis;\n\n documentListeners.set(doc, { dispatch, cleanUp, reset, window: win });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n win.addEventListener('focus', reset);\n\n const getScope = () => activeScope;\n\n const isBound = (keysStr: string, options?: { scope?: string }): boolean => {\n const scope = options?.scope || activeScope;\n const keyMaps = getKeyMap(keysStr);\n\n return keyMaps.some(({ key, shortcut }) => {\n const keyHandlers = handlers[key];\n if (!keyHandlers) return false;\n\n return keyHandlers.some(\n (h) =>\n h.scope === scope &&\n h.shortcut.mods === shortcut.mods &&\n isEqArray(h.shortcut.special, shortcut.special),\n );\n });\n };\n\n const getBoundKeys = (options?: { scope?: string }): string[] => {\n const scope = options?.scope || activeScope;\n const keys = new Set<string>();\n\n Object.values(handlers).forEach((handlerList) => {\n handlerList.forEach((handler) => {\n if (handler.scope === scope) {\n const modParts: string[] = [];\n if (handler.shortcut.mods & MODS.CTRL) modParts.push('ctrl');\n if (handler.shortcut.mods & MODS.ALT) modParts.push('alt');\n if (handler.shortcut.mods & MODS.SHIFT) modParts.push('shift');\n if (handler.shortcut.mods & MODS.META) modParts.push('cmd');\n\n const keyPart = handler.shortcut.special.join('+') || '';\n const fullKey = [...modParts, keyPart].filter(Boolean).join('+');\n if (fullKey) keys.add(fullKey);\n }\n });\n });\n\n return Array.from(keys);\n };\n\n const getHandlers = (\n keysStr: string,\n options?: { scope?: string },\n ): KeyHandler[] => {\n const scope = options?.scope || activeScope;\n const keyMaps = getKeyMap(keysStr);\n const result: KeyHandler[] = [];\n\n keyMaps.forEach(({ key, shortcut }) => {\n const keyHandlers = handlers[key];\n if (!keyHandlers) return;\n\n keyHandlers.forEach((h) => {\n if (\n h.scope === scope &&\n h.shortcut.mods === shortcut.mods &&\n isEqArray(h.shortcut.special, shortcut.special)\n ) {\n result.push(h.method);\n }\n });\n });\n\n return result;\n };\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope,\n destroy,\n isBound,\n getBoundKeys,\n getHandlers,\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 isBound,\n getBoundKeys,\n getHandlers,\n} = createKeybuddy(document);\n\nexport const bindKey = bind;\nexport const unbindKey = unbind;\nexport const unsafeUnbindKey = unsafeUnbind;\n\nexport {\n setScope,\n unbindScope,\n unbindAll,\n getScope,\n destroy,\n isBound,\n getBoundKeys,\n getHandlers,\n DEFAULT_SCOPE,\n};\nexport type { KeyString } from './constants';\nexport { MODS, SPECIAL } from './constants';\n\nexport default bind;\n"],"mappings":"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","names":["DEFAULT_SCOPE","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","e","modifiers","MODS","getMods","keys","acc","key","MODIFIERS","SPECIAL","getCombinations","keysStr","getKeyMap","keyCmd","getKeyIdentifier","isFirefox","isEditable","el","isEqArray","arr1","arr2","i","defaultFilter","e","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","getKeyIdentifier","isFirefox","CAPS_LOCK_KEY","updateModifiers","currentHandlers","special","mods","primaryAction","action","cleanUp","unbindScope","keyString","setScope","unbindAll","reset","destroy","listeners","win","options","keyHandlers","h","keys","handlerList","modParts","MODS","keyPart","fullKey","keyMaps","result","bind","unbind","unsafeUnbind","getScope","setScope","unbindScope","unbindAll","destroy","isBound","getBoundKeys","getHandlers","createKeybuddy","bindKey","unbindKey","unsafeUnbindKey","src_default","bind"]}
package/package.json CHANGED
@@ -1,13 +1,47 @@
1
1
  {
2
2
  "name": "keybuddy",
3
- "version": "0.5.0",
4
- "main": "./index.js",
3
+ "version": "0.7.0",
4
+ "main": "./index.cjs",
5
+ "module": "./index.js",
6
+ "types": "./index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": {
10
+ "types": "./index.d.ts",
11
+ "default": "./index.js"
12
+ },
13
+ "require": {
14
+ "types": "./index.d.ts",
15
+ "default": "./index.cjs"
16
+ }
17
+ }
18
+ },
19
+ "sideEffects": false,
20
+ "keywords": [
21
+ "keyboard",
22
+ "shortcuts",
23
+ "hotkeys",
24
+ "keybindings",
25
+ "keymaster",
26
+ "key",
27
+ "bindings",
28
+ "typescript"
29
+ ],
5
30
  "license": "MIT",
6
31
  "author": {
7
32
  "email": "elvin.d@outlook.com",
8
33
  "name": "Elvin Dzhavadov"
9
34
  },
10
- "repository": "https://github.com/elv1n/keybuddy",
11
- "private": false,
12
- "typings": "./index.d.ts"
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/elv1n/keybuddy"
38
+ },
39
+ "bugs": {
40
+ "url": "https://github.com/elv1n/keybuddy/issues"
41
+ },
42
+ "homepage": "https://github.com/elv1n/keybuddy#readme",
43
+ "engines": {
44
+ "node": ">=18"
45
+ },
46
+ "private": false
13
47
  }
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 };
@@ -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
@@ -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"]}