keybuddy 0.6.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 +21 -0
- package/README.md +12 -3
- package/index.d.mts +22 -4
- package/index.d.ts +22 -4
- package/index.global.js +1 -1
- package/index.global.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/index.mjs +1 -1
- package/index.mjs.map +1 -1
- package/package.json +39 -5
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
|
@@ -18,7 +18,7 @@ import key from 'keybuddy';
|
|
|
18
18
|
|
|
19
19
|
key('a', e => handleKeyPress('a'))
|
|
20
20
|
key('shift+r', e => handleKeyPress('shift+r'))
|
|
21
|
-
key('⌘+shift+r, ctrl+
|
|
21
|
+
key('⌘+shift+r, ctrl+shift+r', e => handleKeyPress('ctrl+shift+r'))
|
|
22
22
|
|
|
23
23
|
```
|
|
24
24
|
|
|
@@ -30,10 +30,17 @@ Differences:
|
|
|
30
30
|
1. Support multiple keystrokes
|
|
31
31
|
1. Custom scope not conflicting with default one
|
|
32
32
|
1. Unbind requires an action (unsafeUnbindKey for backward compatibility)
|
|
33
|
-
1. Creator instance to replace document with any other DOM element
|
|
33
|
+
1. Creator instance to replace document with any other DOM element
|
|
34
34
|
1. More explicit API
|
|
35
35
|
1. Provides new fixes and maintaining
|
|
36
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)
|
|
37
44
|
|
|
38
45
|
## Supported keys
|
|
39
46
|
|
|
@@ -122,11 +129,13 @@ const myKeybuddy = createKeybuddy(iframe, filterFn?)
|
|
|
122
129
|
myKeybuddy.bind('alt+b', action);
|
|
123
130
|
```
|
|
124
131
|
|
|
132
|
+
For iframe usage examples, see [cypress/component/iframe-bindings.spec.ts](cypress/component/iframe-bindings.spec.ts).
|
|
133
|
+
|
|
125
134
|
### Deno
|
|
126
135
|
```typescript
|
|
127
136
|
import key from "jsr:@keybuddy/core";
|
|
128
137
|
|
|
129
138
|
key('a', e => handleKeyPress('a'))
|
|
130
139
|
key('shift+r', e => handleKeyPress('shift+r'))
|
|
131
|
-
key('⌘+shift+r, ctrl+
|
|
140
|
+
key('⌘+shift+r, ctrl+shift+r', e => handleKeyPress('ctrl+shift+r'))
|
|
132
141
|
```
|
package/index.d.mts
CHANGED
|
@@ -13,19 +13,28 @@ declare const SPECIAL: {
|
|
|
13
13
|
[key: string]: string;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
type
|
|
16
|
+
type KeyHandler = (e: KeyboardEvent) => void;
|
|
17
17
|
type FilterFn = (el: KeyboardEvent) => boolean;
|
|
18
18
|
declare function createKeybuddy(doc: Document, filterFn?: FilterFn): {
|
|
19
|
-
bind: (keysStr: string, scopeOrMethod: string |
|
|
19
|
+
bind: (keysStr: string, scopeOrMethod: string | KeyHandler, methodOrNull?: KeyHandler, { skipOther, }?: {
|
|
20
20
|
skipOther: boolean;
|
|
21
21
|
}) => void;
|
|
22
|
-
unbind: (keysStr: string, scopeOrMethod: string |
|
|
22
|
+
unbind: (keysStr: string, scopeOrMethod: string | KeyHandler, methodOrNull?: KeyHandler) => void;
|
|
23
23
|
unsafeUnbind: (keysStr: string, scope?: string) => void;
|
|
24
24
|
unbindScope: (deleteScope: string) => void;
|
|
25
25
|
setScope: (scope: string) => void;
|
|
26
26
|
unbindAll: () => void;
|
|
27
27
|
getScope: () => string;
|
|
28
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[];
|
|
29
38
|
};
|
|
30
39
|
|
|
31
40
|
declare const bind: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void, { skipOther, }?: {
|
|
@@ -36,10 +45,19 @@ declare const setScope: (scope: string) => void;
|
|
|
36
45
|
declare const unbindScope: (deleteScope: string) => void;
|
|
37
46
|
declare const unbindAll: () => void;
|
|
38
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)[];
|
|
39
57
|
declare const bindKey: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void, { skipOther, }?: {
|
|
40
58
|
skipOther: boolean;
|
|
41
59
|
}) => void;
|
|
42
60
|
declare const unbindKey: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void) => void;
|
|
43
61
|
declare const unsafeUnbindKey: (keysStr: string, scope?: string) => void;
|
|
44
62
|
|
|
45
|
-
export { DEFAULT_SCOPE, type KeyString, MODS, SPECIAL, bindKey, createKeybuddy, 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
|
@@ -13,19 +13,28 @@ declare const SPECIAL: {
|
|
|
13
13
|
[key: string]: string;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
type
|
|
16
|
+
type KeyHandler = (e: KeyboardEvent) => void;
|
|
17
17
|
type FilterFn = (el: KeyboardEvent) => boolean;
|
|
18
18
|
declare function createKeybuddy(doc: Document, filterFn?: FilterFn): {
|
|
19
|
-
bind: (keysStr: string, scopeOrMethod: string |
|
|
19
|
+
bind: (keysStr: string, scopeOrMethod: string | KeyHandler, methodOrNull?: KeyHandler, { skipOther, }?: {
|
|
20
20
|
skipOther: boolean;
|
|
21
21
|
}) => void;
|
|
22
|
-
unbind: (keysStr: string, scopeOrMethod: string |
|
|
22
|
+
unbind: (keysStr: string, scopeOrMethod: string | KeyHandler, methodOrNull?: KeyHandler) => void;
|
|
23
23
|
unsafeUnbind: (keysStr: string, scope?: string) => void;
|
|
24
24
|
unbindScope: (deleteScope: string) => void;
|
|
25
25
|
setScope: (scope: string) => void;
|
|
26
26
|
unbindAll: () => void;
|
|
27
27
|
getScope: () => string;
|
|
28
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[];
|
|
29
38
|
};
|
|
30
39
|
|
|
31
40
|
declare const bind: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void, { skipOther, }?: {
|
|
@@ -36,10 +45,19 @@ declare const setScope: (scope: string) => void;
|
|
|
36
45
|
declare const unbindScope: (deleteScope: string) => void;
|
|
37
46
|
declare const unbindAll: () => void;
|
|
38
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)[];
|
|
39
57
|
declare const bindKey: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void, { skipOther, }?: {
|
|
40
58
|
skipOther: boolean;
|
|
41
59
|
}) => void;
|
|
42
60
|
declare const unbindKey: (keysStr: string, scopeOrMethod: string | ((e: KeyboardEvent) => void), methodOrNull?: (e: KeyboardEvent) => void) => void;
|
|
43
61
|
declare const unsafeUnbindKey: (keysStr: string, scope?: string) => void;
|
|
44
62
|
|
|
45
|
-
export { DEFAULT_SCOPE, type KeyString, MODS, SPECIAL, bindKey, createKeybuddy, 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
|
|
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
|
package/index.global.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/helpers/keyboard.ts","../src/helpers/keymap.ts","../src/helpers/utils.ts","../src/keybuddy.ts"],"sourcesContent":["import { DEFAULT_SCOPE } from './constants';\nimport { createKeybuddy } from './keybuddy';\n\nexport { createKeybuddy };\n\nconst {\n bind,\n unbind,\n unsafeUnbind,\n getScope,\n setScope,\n unbindScope,\n unbindAll,\n destroy,\n} = createKeybuddy(document);\n\nexport const bindKey = bind;\nexport const unbindKey = unbind;\nexport const unsafeUnbindKey = unsafeUnbind;\nexport { setScope, unbindScope, unbindAll, getScope, destroy, DEFAULT_SCOPE };\nexport type { KeyString } from './constants';\nexport { MODS, SPECIAL } from './constants';\nexport default bind;\n","\nexport const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\n// Bitwise flags for modifiers - much faster than object-based tracking\nexport const MODS = {\n SHIFT: 0b0001, // 1\n ALT: 0b0010, // 2\n CTRL: 0b0100, // 4\n META: 0b1000, // 8\n} as const;\n\n// Map string modifier names to bitwise flags for parsing\nexport type ModifierNames = {\n '⇧': number;\n shift: number;\n '⌥': number;\n alt: number;\n option: number;\n '⌃': number;\n ctrl: number;\n control: number;\n '⌘': number;\n cmd: number;\n command: number;\n};\n\nexport const MODIFIERS: ModifierNames = {\n '⇧': MODS.SHIFT,\n shift: MODS.SHIFT,\n '⌥': MODS.ALT,\n alt: MODS.ALT,\n option: MODS.ALT,\n '⌃': MODS.CTRL,\n ctrl: MODS.CTRL,\n control: MODS.CTRL,\n '⌘': MODS.META,\n cmd: MODS.META,\n command: MODS.META,\n};\n\n// Modern key mapping using KeyboardEvent.key values\nexport const SPECIAL: { [key: string]: string } = {\n backspace: 'Backspace',\n tab: 'Tab',\n clear: 'Clear',\n enter: 'Enter',\n return: 'Enter',\n esc: 'Escape',\n escape: 'Escape',\n space: ' ',\n left: 'ArrowLeft',\n up: 'ArrowUp',\n right: 'ArrowRight',\n down: 'ArrowDown',\n del: 'Delete',\n delete: 'Delete',\n home: 'Home',\n end: 'End',\n pageup: 'PageUp',\n pagedown: 'PageDown',\n comma: ',',\n '.': '.',\n '/': '/',\n '`': '`',\n '-': '-',\n '=': '=',\n ';': ';',\n \"'\": \"'\",\n '[': '[',\n ']': ']',\n '\\\\': '\\\\',\n // Normalize Meta key variants\n 'Meta': 'Meta',\n 'MetaLeft': 'Meta',\n 'MetaRight': 'Meta', \n 'OS': 'Meta', // Some browsers use OS instead of Meta\n 'ContextMenu': 'Meta', // Right-click context menu key sometimes acts as Meta\n // Add identity mappings for already-normalized keys\n 'ArrowLeft': 'ArrowLeft',\n 'ArrowUp': 'ArrowUp',\n 'ArrowRight': 'ArrowRight',\n 'ArrowDown': 'ArrowDown',\n 'Backspace': 'Backspace',\n 'Tab': 'Tab',\n 'Clear': 'Clear',\n 'Enter': 'Enter',\n 'Escape': 'Escape',\n 'Delete': 'Delete',\n 'Home': 'Home',\n 'End': 'End',\n 'PageUp': 'PageUp',\n 'PageDown': 'PageDown',\n};\n\nexport const CAPS_LOCK_KEY = 'CapsLock';\n","import { KeyString, MODS, SPECIAL } from '../constants';\n\nexport const getKeyIdentifier = (key: string): KeyString => {\n return (SPECIAL[key] || key.toUpperCase()) as KeyString;\n};\n\nexport const updateModifiers = (e: KeyboardEvent): number => {\n let modifiers = 0;\n if (e.shiftKey) modifiers |= MODS.SHIFT;\n if (e.altKey) modifiers |= MODS.ALT;\n if (e.ctrlKey) modifiers |= MODS.CTRL;\n if (e.metaKey) modifiers |= MODS.META;\n return modifiers;\n};\n","import { KeyString, MODIFIERS, ModifierNames, SPECIAL } from '../constants';\n\nexport interface ParsedShortcut {\n mods: number; // Bitwise flag for modifiers\n special: string[];\n}\nexport interface KeyMap {\n key: KeyString;\n shortcut: ParsedShortcut;\n}\n\nexport const getKeyIdentifier = (key: string): KeyString =>\n (SPECIAL[key] || key.toUpperCase()) as KeyString;\n\nconst getMods = (keys: string[]): ParsedShortcut =>\n keys.reduce(\n (acc, key) => {\n if (key in MODIFIERS) {\n acc.mods |= MODIFIERS[key as keyof ModifierNames];\n } else {\n acc.special.push(SPECIAL[key] || key.toUpperCase());\n }\n return acc;\n },\n {\n mods: 0, // Start with no modifiers\n special: [],\n } as ParsedShortcut,\n );\n\nconst getCombinations = (keysStr: string): string[] => {\n const cleanKeys = keysStr.replace(/\\s/g, '');\n const keys = cleanKeys.split(',');\n if (keys[keys.length - 1] === '') {\n keys[keys.length - 2] += ',';\n }\n\n return keys;\n};\n\nexport const getKeyMap = (keysStr: string): KeyMap[] => {\n const keymap = getCombinations(keysStr);\n return keymap.map((keyCmd) => {\n const keys = keyCmd.split('+');\n const key = keys[keys.length - 1];\n const keyIdentifier = getKeyIdentifier(key);\n\n return {\n key: keyIdentifier,\n shortcut: getMods(keys),\n };\n });\n};\n","export const invariant = (\n condition: boolean,\n message: string,\n ...args: unknown[]\n) => {\n if (\n typeof process !== 'undefined' &&\n process.env &&\n process.env.NODE_ENV === 'development' &&\n !condition\n ) {\n throw new Error(\n `Invariant failed: ${message}${args.length ? ` ${JSON.stringify(args)}` : ''}`,\n );\n }\n};\n\nexport const isFirefox = navigator.userAgent.includes('Firefox');\n\nexport const isEditable = (el: HTMLElement): boolean =>\n el.isContentEditable ||\n el.tagName === 'INPUT' ||\n el.tagName === 'SELECT' ||\n el.tagName === 'TEXTAREA';\n\nexport const isEqArray = (\n arr1: (string | number)[],\n arr2: (string | number)[],\n): boolean => {\n if (arr1.length !== arr2.length) return false;\n\n for (let i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) return false;\n }\n\n return true;\n};\n","import { CAPS_LOCK_KEY, DEFAULT_SCOPE, KeyString } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype noop = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: noop;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\n// WeakMap to track event listener references per document to prevent memory leaks\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n }\n>();\n\nexport function createKeybuddy(\n doc: Document,\n filterFn: FilterFn = defaultFilter,\n) {\n let handlers: { [key: KeyString]: Handler[] } = {};\n const downKeys: Set<KeyString> = new Set();\n let activeScope = DEFAULT_SCOPE;\n\n let modifiers = 0; // Bitwise flag for active modifiers\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n {\n skipOther,\n }: {\n skipOther: boolean;\n } = {\n skipOther: false,\n },\n ): void => {\n const scope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const method: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n if (!handlers[key]) {\n handlers[key] = [];\n }\n const handler = handlers[key];\n if (process.env.NODE_ENV === 'development') {\n if (skipOther) {\n const action = handler.find((i) => i.skipOther);\n invariant(\n !action,\n \"Conflicting 'skipOther' property with action\",\n action,\n );\n }\n }\n\n handler.push({\n scope,\n method,\n shortcut,\n skipOther,\n });\n });\n };\n\n const unbindKeyProcess = (\n keysStr: string,\n deleteMethod: null | noop,\n deleteScope: string = DEFAULT_SCOPE,\n ): void => {\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n const handler = handlers[key];\n if (Array.isArray(handler)) {\n const handler = handlers[key].filter(\n ({ scope, method, shortcut: methodShortcut }: Handler) =>\n !(\n scope === deleteScope &&\n methodShortcut.mods === shortcut.mods &&\n isEqArray(methodShortcut.special, shortcut.special) &&\n (deleteMethod === null ? true : method === deleteMethod)\n ),\n );\n if (handler.length) {\n handlers[key] = handler;\n } else {\n delete handlers[key];\n }\n }\n });\n };\n\n const unbindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n return unbindKeyProcess(keysStr, deleteMethod, deleteScope);\n };\n\n const unsafeUnbindKey = (keysStr: string, scope?: string) =>\n unbindKeyProcess(keysStr, null, scope);\n\n const dispatch = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n if (!filterFn(e)) {\n return;\n }\n\n // fix firefox behavior when caps lock fires three times onkeydown\n // and don't fire at all onkeyup (Firefox 72)\n if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\n // Check if key is not a modifier\n const isModifierKey =\n key === ('SHIFT' as KeyString) ||\n key === ('ALT' as KeyString) ||\n key === ('CTRL' as KeyString) ||\n key === ('META' as KeyString);\n if (!isModifierKey && !downKeys.has(key)) {\n downKeys.add(key);\n }\n // See if we need to ignore the keypress (filter() can can be overridden)\n // by default ignore key presses if a select, textarea, or input is focused\n // if (!assignKey.filter.call(this, event)) return;\n\n // abort if no potentially matching shortcuts found\n if (!(key in handlers)) {\n return;\n }\n\n const currentHandlers = handlers[key].filter(\n ({ scope, shortcut: { special, mods } }) => {\n if (scope !== activeScope) {\n return false;\n }\n\n return isEqArray(special, Array.from(downKeys)) && mods === modifiers;\n },\n );\n\n const primaryAction: Handler | undefined = currentHandlers.find(\n (action) => action.skipOther,\n );\n if (primaryAction) {\n primaryAction.method(e);\n } else {\n currentHandlers.forEach(({ method }) => {\n method(e);\n });\n }\n };\n\n const cleanUp = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n // clean all for meta.\n // Main reason is ctrl+z (or any other native command not fires letter keyup on editable inputs)\n if (e.key && e.key.toLowerCase() === 'meta') {\n downKeys.clear();\n } else {\n downKeys.delete(key);\n }\n };\n\n const unbindScope = (deleteScope: string): void => {\n Object.keys(handlers).forEach((key) => {\n const keyString = key as KeyString;\n const handler = handlers[keyString].filter(\n ({ scope }: Handler) => scope !== deleteScope,\n );\n if (handler.length) {\n handlers[keyString] = handler;\n } else {\n delete handlers[keyString];\n }\n });\n };\n\n const setScope = (scope: string): void => {\n activeScope = scope;\n };\n\n const unbindAll = (): void => {\n handlers = {};\n downKeys.clear();\n };\n\n const reset = (): void => {\n downKeys.clear();\n };\n\n const destroy = (): void => {\n downKeys.clear();\n handlers = {};\n if (doc) {\n const listeners = documentListeners.get(doc);\n if (listeners) {\n doc.removeEventListener('keydown', listeners.dispatch);\n doc.removeEventListener('keyup', listeners.cleanUp);\n window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n // Remove old listeners if they exist\n destroy();\n\n // Store and add new listeners\n documentListeners.set(doc, { dispatch, cleanUp, reset });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n window.addEventListener('focus', reset);\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope: () => activeScope,\n destroy,\n };\n}\n"],"mappings":"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","names":["src_exports","__export","DEFAULT_SCOPE","MODS","SPECIAL","bindKey","createKeybuddy","src_default","destroy","getScope","setScope","unbindAll","unbindKey","unbindScope","unsafeUnbindKey","DEFAULT_SCOPE","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getKeyIdentifier","key","SPECIAL","getMods","keys","acc","MODIFIERS","getCombinations","keysStr","getKeyMap","keyCmd","isFirefox","isEditable","el","isEqArray","arr1","arr2","i","defaultFilter","isEditable","documentListeners","createKeybuddy","doc","filterFn","handlers","downKeys","activeScope","modifiers","bindKey","keysStr","scopeOrMethod","methodOrNull","skipOther","scope","method","getKeyMap","key","shortcut","unbindKeyProcess","deleteMethod","deleteScope","handler","methodShortcut","isEqArray","unbindKey","unsafeUnbindKey","dispatch","e","getKeyIdentifier","isFirefox","CAPS_LOCK_KEY","updateModifiers","currentHandlers","special","mods","primaryAction","action","cleanUp","unbindScope","keyString","setScope","unbindAll","reset","destroy","listeners","bind","unbind","unsafeUnbind","getScope","setScope","unbindScope","unbindAll","destroy","createKeybuddy","bindKey","unbindKey","unsafeUnbindKey","src_default","bind"]}
|
|
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
|
|
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\nexport { createKeybuddy };\n\nconst {\n bind,\n unbind,\n unsafeUnbind,\n getScope,\n setScope,\n unbindScope,\n unbindAll,\n destroy,\n} = createKeybuddy(document);\n\nexport const bindKey = bind;\nexport const unbindKey = unbind;\nexport const unsafeUnbindKey = unsafeUnbind;\nexport { setScope, unbindScope, unbindAll, getScope, destroy, DEFAULT_SCOPE };\nexport type { KeyString } from './constants';\nexport { MODS, SPECIAL } from './constants';\nexport default bind;\n","\nexport const DEFAULT_SCOPE = 'all';\n\ndeclare const KeyStringBrand: unique symbol;\n\nexport type KeyString = string & { readonly [KeyStringBrand]: true };\n\n// Bitwise flags for modifiers - much faster than object-based tracking\nexport const MODS = {\n SHIFT: 0b0001, // 1\n ALT: 0b0010, // 2\n CTRL: 0b0100, // 4\n META: 0b1000, // 8\n} as const;\n\n// Map string modifier names to bitwise flags for parsing\nexport type ModifierNames = {\n '⇧': number;\n shift: number;\n '⌥': number;\n alt: number;\n option: number;\n '⌃': number;\n ctrl: number;\n control: number;\n '⌘': number;\n cmd: number;\n command: number;\n};\n\nexport const MODIFIERS: ModifierNames = {\n '⇧': MODS.SHIFT,\n shift: MODS.SHIFT,\n '⌥': MODS.ALT,\n alt: MODS.ALT,\n option: MODS.ALT,\n '⌃': MODS.CTRL,\n ctrl: MODS.CTRL,\n control: MODS.CTRL,\n '⌘': MODS.META,\n cmd: MODS.META,\n command: MODS.META,\n};\n\n// Modern key mapping using KeyboardEvent.key values\nexport const SPECIAL: { [key: string]: string } = {\n backspace: 'Backspace',\n tab: 'Tab',\n clear: 'Clear',\n enter: 'Enter',\n return: 'Enter',\n esc: 'Escape',\n escape: 'Escape',\n space: ' ',\n left: 'ArrowLeft',\n up: 'ArrowUp',\n right: 'ArrowRight',\n down: 'ArrowDown',\n del: 'Delete',\n delete: 'Delete',\n home: 'Home',\n end: 'End',\n pageup: 'PageUp',\n pagedown: 'PageDown',\n comma: ',',\n '.': '.',\n '/': '/',\n '`': '`',\n '-': '-',\n '=': '=',\n ';': ';',\n \"'\": \"'\",\n '[': '[',\n ']': ']',\n '\\\\': '\\\\',\n // Normalize Meta key variants\n 'Meta': 'Meta',\n 'MetaLeft': 'Meta',\n 'MetaRight': 'Meta', \n 'OS': 'Meta', // Some browsers use OS instead of Meta\n 'ContextMenu': 'Meta', // Right-click context menu key sometimes acts as Meta\n // Add identity mappings for already-normalized keys\n 'ArrowLeft': 'ArrowLeft',\n 'ArrowUp': 'ArrowUp',\n 'ArrowRight': 'ArrowRight',\n 'ArrowDown': 'ArrowDown',\n 'Backspace': 'Backspace',\n 'Tab': 'Tab',\n 'Clear': 'Clear',\n 'Enter': 'Enter',\n 'Escape': 'Escape',\n 'Delete': 'Delete',\n 'Home': 'Home',\n 'End': 'End',\n 'PageUp': 'PageUp',\n 'PageDown': 'PageDown',\n};\n\nexport const CAPS_LOCK_KEY = 'CapsLock';\n","import { KeyString, MODS, SPECIAL } from '../constants';\n\nexport const getKeyIdentifier = (key: string): KeyString => {\n return (SPECIAL[key] || key.toUpperCase()) as KeyString;\n};\n\nexport const updateModifiers = (e: KeyboardEvent): number => {\n let modifiers = 0;\n if (e.shiftKey) modifiers |= MODS.SHIFT;\n if (e.altKey) modifiers |= MODS.ALT;\n if (e.ctrlKey) modifiers |= MODS.CTRL;\n if (e.metaKey) modifiers |= MODS.META;\n return modifiers;\n};\n","import { KeyString, MODIFIERS, ModifierNames, SPECIAL } from '../constants';\n\nexport interface ParsedShortcut {\n mods: number; // Bitwise flag for modifiers\n special: string[];\n}\nexport interface KeyMap {\n key: KeyString;\n shortcut: ParsedShortcut;\n}\n\nexport const getKeyIdentifier = (key: string): KeyString =>\n (SPECIAL[key] || key.toUpperCase()) as KeyString;\n\nconst getMods = (keys: string[]): ParsedShortcut =>\n keys.reduce(\n (acc, key) => {\n if (key in MODIFIERS) {\n acc.mods |= MODIFIERS[key as keyof ModifierNames];\n } else {\n acc.special.push(SPECIAL[key] || key.toUpperCase());\n }\n return acc;\n },\n {\n mods: 0, // Start with no modifiers\n special: [],\n } as ParsedShortcut,\n );\n\nconst getCombinations = (keysStr: string): string[] => {\n const cleanKeys = keysStr.replace(/\\s/g, '');\n const keys = cleanKeys.split(',');\n if (keys[keys.length - 1] === '') {\n keys[keys.length - 2] += ',';\n }\n\n return keys;\n};\n\nexport const getKeyMap = (keysStr: string): KeyMap[] => {\n const keymap = getCombinations(keysStr);\n return keymap.map((keyCmd) => {\n const keys = keyCmd.split('+');\n const key = keys[keys.length - 1];\n const keyIdentifier = getKeyIdentifier(key);\n\n return {\n key: keyIdentifier,\n shortcut: getMods(keys),\n };\n });\n};\n","export const invariant = (\n condition: boolean,\n message: string,\n ...args: unknown[]\n) => {\n if (\n typeof process !== 'undefined' &&\n process.env &&\n process.env.NODE_ENV === 'development' &&\n !condition\n ) {\n throw new Error(\n `Invariant failed: ${message}${args.length ? ` ${JSON.stringify(args)}` : ''}`,\n );\n }\n};\n\nexport const isFirefox = navigator.userAgent.includes('Firefox');\n\nexport const isEditable = (el: HTMLElement): boolean =>\n el.isContentEditable ||\n el.tagName === 'INPUT' ||\n el.tagName === 'SELECT' ||\n el.tagName === 'TEXTAREA';\n\nexport const isEqArray = (\n arr1: (string | number)[],\n arr2: (string | number)[],\n): boolean => {\n if (arr1.length !== arr2.length) return false;\n\n for (let i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) return false;\n }\n\n return true;\n};\n","import { CAPS_LOCK_KEY, DEFAULT_SCOPE, KeyString } from './constants';\nimport { getKeyIdentifier, updateModifiers } from './helpers/keyboard';\nimport { getKeyMap, ParsedShortcut } from './helpers/keymap';\nimport { invariant, isEditable, isEqArray, isFirefox } from './helpers/utils';\n\ntype noop = (e: KeyboardEvent) => void;\ntype FilterFn = (el: KeyboardEvent) => boolean;\n\ninterface Handler {\n scope: string;\n method: noop;\n shortcut: ParsedShortcut;\n skipOther: boolean;\n}\n\nconst defaultFilter = (e: KeyboardEvent): boolean =>\n e && !isEditable(e.target as HTMLElement);\n\n// WeakMap to track event listener references per document to prevent memory leaks\nconst documentListeners = new WeakMap<\n Document,\n {\n dispatch: (e: KeyboardEvent) => void;\n cleanUp: (e: KeyboardEvent) => void;\n reset: () => void;\n }\n>();\n\nexport function createKeybuddy(\n doc: Document,\n filterFn: FilterFn = defaultFilter,\n) {\n let handlers: { [key: KeyString]: Handler[] } = {};\n const downKeys: Set<KeyString> = new Set();\n let activeScope = DEFAULT_SCOPE;\n\n let modifiers = 0; // Bitwise flag for active modifiers\n\n const bindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n {\n skipOther,\n }: {\n skipOther: boolean;\n } = {\n skipOther: false,\n },\n ): void => {\n const scope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const method: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n if (!handlers[key]) {\n handlers[key] = [];\n }\n const handler = handlers[key];\n if (process.env.NODE_ENV === 'development') {\n if (skipOther) {\n const action = handler.find((i) => i.skipOther);\n invariant(\n !action,\n \"Conflicting 'skipOther' property with action\",\n action,\n );\n }\n }\n\n handler.push({\n scope,\n method,\n shortcut,\n skipOther,\n });\n });\n };\n\n const unbindKeyProcess = (\n keysStr: string,\n deleteMethod: null | noop,\n deleteScope: string = DEFAULT_SCOPE,\n ): void => {\n getKeyMap(keysStr).forEach(({ key, shortcut }) => {\n const handler = handlers[key];\n if (Array.isArray(handler)) {\n const handler = handlers[key].filter(\n ({ scope, method, shortcut: methodShortcut }: Handler) =>\n !(\n scope === deleteScope &&\n methodShortcut.mods === shortcut.mods &&\n isEqArray(methodShortcut.special, shortcut.special) &&\n (deleteMethod === null ? true : method === deleteMethod)\n ),\n );\n if (handler.length) {\n handlers[key] = handler;\n } else {\n delete handlers[key];\n }\n }\n });\n };\n\n const unbindKey = (\n keysStr: string,\n scopeOrMethod: string | noop,\n methodOrNull: noop = () => {},\n ) => {\n const deleteScope: string =\n typeof scopeOrMethod === 'function' ? DEFAULT_SCOPE : scopeOrMethod;\n const deleteMethod: noop =\n typeof scopeOrMethod === 'function' ? scopeOrMethod : methodOrNull;\n return unbindKeyProcess(keysStr, deleteMethod, deleteScope);\n };\n\n const unsafeUnbindKey = (keysStr: string, scope?: string) =>\n unbindKeyProcess(keysStr, null, scope);\n\n const dispatch = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n if (!filterFn(e)) {\n return;\n }\n\n // fix firefox behavior when caps lock fires three times onkeydown\n // and don't fire at all onkeyup (Firefox 72)\n if (isFirefox && key === CAPS_LOCK_KEY) {\n return;\n }\n\n modifiers = updateModifiers(e);\n\n // Check if key is not a modifier\n const isModifierKey =\n key === ('SHIFT' as KeyString) ||\n key === ('ALT' as KeyString) ||\n key === ('CTRL' as KeyString) ||\n key === ('META' as KeyString);\n if (!isModifierKey && !downKeys.has(key)) {\n downKeys.add(key);\n }\n // See if we need to ignore the keypress (filter() can can be overridden)\n // by default ignore key presses if a select, textarea, or input is focused\n // if (!assignKey.filter.call(this, event)) return;\n\n // abort if no potentially matching shortcuts found\n if (!(key in handlers)) {\n return;\n }\n\n const currentHandlers = handlers[key].filter(\n ({ scope, shortcut: { special, mods } }) => {\n if (scope !== activeScope) {\n return false;\n }\n\n return isEqArray(special, Array.from(downKeys)) && mods === modifiers;\n },\n );\n\n const primaryAction: Handler | undefined = currentHandlers.find(\n (action) => action.skipOther,\n );\n if (primaryAction) {\n primaryAction.method(e);\n } else {\n currentHandlers.forEach(({ method }) => {\n method(e);\n });\n }\n };\n\n const cleanUp = (e: KeyboardEvent) => {\n const key = getKeyIdentifier(e.key);\n\n // clean all for meta.\n // Main reason is ctrl+z (or any other native command not fires letter keyup on editable inputs)\n if (e.key && e.key.toLowerCase() === 'meta') {\n downKeys.clear();\n } else {\n downKeys.delete(key);\n }\n };\n\n const unbindScope = (deleteScope: string): void => {\n Object.keys(handlers).forEach((key) => {\n const keyString = key as KeyString;\n const handler = handlers[keyString].filter(\n ({ scope }: Handler) => scope !== deleteScope,\n );\n if (handler.length) {\n handlers[keyString] = handler;\n } else {\n delete handlers[keyString];\n }\n });\n };\n\n const setScope = (scope: string): void => {\n activeScope = scope;\n };\n\n const unbindAll = (): void => {\n handlers = {};\n downKeys.clear();\n };\n\n const reset = (): void => {\n downKeys.clear();\n };\n\n const destroy = (): void => {\n downKeys.clear();\n handlers = {};\n if (doc) {\n const listeners = documentListeners.get(doc);\n if (listeners) {\n doc.removeEventListener('keydown', listeners.dispatch);\n doc.removeEventListener('keyup', listeners.cleanUp);\n window.removeEventListener('focus', listeners.reset);\n documentListeners.delete(doc);\n }\n }\n };\n\n // Remove old listeners if they exist\n destroy();\n\n // Store and add new listeners\n documentListeners.set(doc, { dispatch, cleanUp, reset });\n doc.addEventListener('keydown', dispatch);\n doc.addEventListener('keyup', cleanUp);\n window.addEventListener('focus', reset);\n\n return {\n bind: bindKey,\n unbind: unbindKey,\n unsafeUnbind: unsafeUnbindKey,\n unbindScope,\n setScope,\n unbindAll,\n getScope: () => activeScope,\n destroy,\n };\n}\n"],"mappings":"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","names":["src_exports","__export","DEFAULT_SCOPE","MODS","SPECIAL","bindKey","createKeybuddy","src_default","destroy","getScope","setScope","unbindAll","unbindKey","unbindScope","unsafeUnbindKey","__toCommonJS","DEFAULT_SCOPE","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getKeyIdentifier","key","SPECIAL","getMods","keys","acc","MODIFIERS","getCombinations","keysStr","getKeyMap","keyCmd","isFirefox","isEditable","el","isEqArray","arr1","arr2","i","defaultFilter","isEditable","documentListeners","createKeybuddy","doc","filterFn","handlers","downKeys","activeScope","modifiers","bindKey","keysStr","scopeOrMethod","methodOrNull","skipOther","scope","method","getKeyMap","key","shortcut","unbindKeyProcess","deleteMethod","deleteScope","handler","methodShortcut","isEqArray","unbindKey","unsafeUnbindKey","dispatch","e","getKeyIdentifier","isFirefox","CAPS_LOCK_KEY","updateModifiers","currentHandlers","special","mods","primaryAction","action","cleanUp","unbindScope","keyString","setScope","unbindAll","reset","destroy","listeners","bind","unbind","unsafeUnbind","getScope","setScope","unbindScope","unbindAll","destroy","createKeybuddy","bindKey","unbindKey","unsafeUnbindKey","src_default","bind"]}
|
|
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
|
|
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\nexport { createKeybuddy };\n\nconst {\n bind,\n unbind,\n unsafeUnbind,\n getScope,\n setScope,\n unbindScope,\n unbindAll,\n destroy,\n} = createKeybuddy(document);\n\nexport const bindKey = bind;\nexport const unbindKey = unbind;\nexport const unsafeUnbindKey = unsafeUnbind;\nexport { setScope, unbindScope, unbindAll, getScope, destroy, DEFAULT_SCOPE };\nexport type { KeyString } from './constants';\nexport { MODS, SPECIAL } from './constants';\nexport default bind;\n"],"mappings":"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","names":["DEFAULT_SCOPE","MODS","MODIFIERS","SPECIAL","CAPS_LOCK_KEY","getKeyIdentifier","key","SPECIAL","updateModifiers","modifiers","MODS","getKeyIdentifier","key","SPECIAL","getMods","keys","acc","MODIFIERS","getCombinations","keysStr","getKeyMap","keyCmd","isFirefox","isEditable","el","isEqArray","arr1","arr2","i","defaultFilter","isEditable","documentListeners","createKeybuddy","doc","filterFn","handlers","downKeys","activeScope","modifiers","bindKey","keysStr","scopeOrMethod","methodOrNull","skipOther","scope","method","getKeyMap","key","shortcut","unbindKeyProcess","deleteMethod","deleteScope","handler","methodShortcut","isEqArray","unbindKey","unsafeUnbindKey","dispatch","e","getKeyIdentifier","isFirefox","CAPS_LOCK_KEY","updateModifiers","currentHandlers","special","mods","primaryAction","action","cleanUp","unbindScope","keyString","setScope","unbindAll","reset","destroy","listeners","bind","unbind","unsafeUnbind","getScope","setScope","unbindScope","unbindAll","destroy","createKeybuddy","bindKey","unbindKey","unsafeUnbindKey","src_default","bind"]}
|
|
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.
|
|
4
|
-
"main": "./index.
|
|
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":
|
|
11
|
-
|
|
12
|
-
|
|
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
|
}
|