tinykeys 1.1.3 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/dist/tinykeys.d.ts +18 -1
- package/dist/tinykeys.js +1 -1
- package/dist/tinykeys.js.map +1 -1
- package/dist/tinykeys.modern.js +1 -1
- package/dist/tinykeys.modern.js.map +1 -1
- package/dist/tinykeys.module.js +1 -1
- package/dist/tinykeys.module.js.map +1 -1
- package/dist/tinykeys.umd.js +1 -1
- package/dist/tinykeys.umd.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -139,3 +139,32 @@ Each press can optionally be prefixed with modifier keys:
|
|
|
139
139
|
```
|
|
140
140
|
|
|
141
141
|
Each press in the sequence must be pressed within 1000ms of the last.
|
|
142
|
+
|
|
143
|
+
## Additional Configuration Options
|
|
144
|
+
|
|
145
|
+
You can configure the behavior of tinykeys in a couple ways using a third
|
|
146
|
+
`options` parameter.
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
tinykey(window, {
|
|
150
|
+
"M": toggleMute,
|
|
151
|
+
}, {
|
|
152
|
+
event: "keyup",
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### `options.event`
|
|
157
|
+
|
|
158
|
+
Valid values: `"keydown"`, `"keyup"`
|
|
159
|
+
|
|
160
|
+
Key presses will listen to this event (default: `"keydown"`).
|
|
161
|
+
|
|
162
|
+
> **Note:** Do not pass `"keypress"`, it is deprecated in browsers.
|
|
163
|
+
|
|
164
|
+
### `options.timeout`
|
|
165
|
+
|
|
166
|
+
Keybinding sequences will wait this long between key presses before cancelling
|
|
167
|
+
(default: `1000`).
|
|
168
|
+
|
|
169
|
+
> **Note:** Setting this value too low (i.e. `300`) will be too fast for many
|
|
170
|
+
> of your users.
|
package/dist/tinykeys.d.ts
CHANGED
|
@@ -4,6 +4,23 @@
|
|
|
4
4
|
export interface KeyBindingMap {
|
|
5
5
|
[keybinding: string]: (event: KeyboardEvent) => void;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Options to configure the behavior of keybindings.
|
|
9
|
+
*/
|
|
10
|
+
export interface KeyBindingOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Key presses will listen to this event (default: "keydown").
|
|
13
|
+
*/
|
|
14
|
+
event?: "keydown" | "keyup";
|
|
15
|
+
/**
|
|
16
|
+
* Keybinding sequences will wait this long between key presses before
|
|
17
|
+
* cancelling (default: 1000).
|
|
18
|
+
*
|
|
19
|
+
* **Note:** Setting this value too low (i.e. `300`) will be too fast for many
|
|
20
|
+
* of your users.
|
|
21
|
+
*/
|
|
22
|
+
timeout?: number;
|
|
23
|
+
}
|
|
7
24
|
/**
|
|
8
25
|
* Subscribes to keybindings.
|
|
9
26
|
*
|
|
@@ -26,4 +43,4 @@ export interface KeyBindingMap {
|
|
|
26
43
|
* })
|
|
27
44
|
* ```
|
|
28
45
|
*/
|
|
29
|
-
export default function keybindings(target: Window | HTMLElement, keyBindingMap: KeyBindingMap): () => void;
|
|
46
|
+
export default function keybindings(target: Window | HTMLElement, keyBindingMap: KeyBindingMap, options?: KeyBindingOptions): () => void;
|
package/dist/tinykeys.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=["Shift","Meta","Alt","Control"],t="object"==typeof navigator&&/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"Meta":"Control";module.exports=function(n,o){var r=Object.keys(o).map(function(e){return[(n=e,n.trim().split(" ").map(function(e){var n=e.split(/\b\+/),o=n.pop();return[n=n.map(function(e){return"$mod"===e?t:e}),o]})),o[e]];var n}),
|
|
1
|
+
var e=["Shift","Meta","Alt","Control"],t="object"==typeof navigator&&/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"Meta":"Control";module.exports=function(n,o,r){var i,a;void 0===r&&(r={});var u=null!=(i=r.timeout)?i:1e3,c=null!=(a=r.event)?a:"keydown",f=Object.keys(o).map(function(e){return[(n=e,n.trim().split(" ").map(function(e){var n=e.split(/\b\+/),o=n.pop();return[n=n.map(function(e){return"$mod"===e?t:e}),o]})),o[e]];var n}),l=new Map,d=null,p=function(t){t instanceof KeyboardEvent&&(f.forEach(function(n){var o=n[0],r=n[1],i=l.get(o)||o;!function(t,n){return!(n[1].toUpperCase()!==t.key.toUpperCase()&&n[1]!==t.code||n[0].find(function(e){return!t.getModifierState(e)})||e.find(function(e){return!n[0].includes(e)&&n[1]!==e&&t.getModifierState(e)}))}(t,i[0])?t.getModifierState(t.key)||l.delete(o):i.length>1?l.set(o,i.slice(1)):(l.delete(o),r(t))}),d&&clearTimeout(d),d=setTimeout(l.clear.bind(l),u))};return n.addEventListener(c,p),function(){n.removeEventListener(c,p)}};
|
|
2
2
|
//# sourceMappingURL=tinykeys.js.map
|
package/dist/tinykeys.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tinykeys.js","sources":["../src/tinykeys.ts"],"sourcesContent":["type KeyBindingPress = [string[], string]\n\n/**\n * A map of keybinding strings to event handlers.\n */\nexport interface KeyBindingMap {\n\t[keybinding: string]: (event: KeyboardEvent) => void\n}\n\n/**\n * These are the modifier keys that change the meaning of keybindings.\n *\n * Note: Ignoring \"AltGraph\" because it is covered by the others.\n */\nlet KEYBINDING_MODIFIER_KEYS = [\"Shift\", \"Meta\", \"Alt\", \"Control\"]\n\n/**\n * Keybinding sequences should timeout if individual key presses are more than\n * 1s apart.\n */\nlet
|
|
1
|
+
{"version":3,"file":"tinykeys.js","sources":["../src/tinykeys.ts"],"sourcesContent":["type KeyBindingPress = [string[], string]\n\n/**\n * A map of keybinding strings to event handlers.\n */\nexport interface KeyBindingMap {\n\t[keybinding: string]: (event: KeyboardEvent) => void\n}\n\n/**\n * Options to configure the behavior of keybindings.\n */\n export interface KeyBindingOptions {\n /**\n * Key presses will listen to this event (default: \"keydown\").\n */\n event?: \"keydown\" | \"keyup\"\n\n /**\n * Keybinding sequences will wait this long between key presses before\n\t * cancelling (default: 1000).\n\t *\n\t * **Note:** Setting this value too low (i.e. `300`) will be too fast for many\n\t * of your users.\n */\n timeout?: number\n}\n\n/**\n * These are the modifier keys that change the meaning of keybindings.\n *\n * Note: Ignoring \"AltGraph\" because it is covered by the others.\n */\nlet KEYBINDING_MODIFIER_KEYS = [\"Shift\", \"Meta\", \"Alt\", \"Control\"]\n\n/**\n * Keybinding sequences should timeout if individual key presses are more than\n * 1s apart by default.\n */\nlet DEFAULT_TIMEOUT = 1000\n\n/**\n * Keybinding sequences should bind to this event by default.\n */\n let DEFAULT_EVENT = \"keydown\"\n\n/**\n * An alias for creating platform-specific keybinding aliases.\n */\nlet MOD =\n\ttypeof navigator === \"object\" &&\n\t/Mac|iPod|iPhone|iPad/.test(navigator.platform)\n\t\t? \"Meta\"\n\t\t: \"Control\"\n\n/**\n * Parses a \"Key Binding String\" into its parts\n *\n * grammar = `<sequence>`\n * <sequence> = `<press> <press> <press> ...`\n * <press> = `<key>` or `<mods>+<key>`\n * <mods> = `<mod>+<mod>+...`\n */\nfunction parse(str: string): KeyBindingPress[] {\n\treturn str\n\t\t.trim()\n\t\t.split(\" \")\n\t\t.map(press => {\n\t\t\tlet mods = press.split(/\\b\\+/)\n\t\t\tlet key = mods.pop() as string\n\t\t\tmods = mods.map(mod => (mod === \"$mod\" ? MOD : mod))\n\t\t\treturn [mods, key]\n\t\t})\n}\n\n/**\n * This tells us if a series of events matches a key binding sequence either\n * partially or exactly.\n */\nfunction match(event: KeyboardEvent, press: KeyBindingPress): boolean {\n\t// prettier-ignore\n\treturn !(\n\t\t// Allow either the `event.key` or the `event.code`\n\t\t// MDN event.key: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key\n\t\t// MDN event.code: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code\n\t\t(\n\t\t\tpress[1].toUpperCase() !== event.key.toUpperCase() &&\n\t\t\tpress[1] !== event.code\n\t\t) ||\n\n\t\t// Ensure all the modifiers in the keybinding are pressed.\n\t\tpress[0].find(mod => {\n\t\t\treturn !event.getModifierState(mod)\n\t\t}) ||\n\n\t\t// KEYBINDING_MODIFIER_KEYS (Shift/Control/etc) change the meaning of a\n\t\t// keybinding. So if they are pressed but aren't part of the current\n\t\t// keybinding press, then we don't have a match.\n\t\tKEYBINDING_MODIFIER_KEYS.find(mod => {\n\t\t\treturn !press[0].includes(mod) && press[1] !== mod && event.getModifierState(mod)\n\t\t})\n\t)\n}\n\n/**\n * Subscribes to keybindings.\n *\n * Returns an unsubscribe method.\n *\n * @example\n * ```js\n * import keybindings from \"../src/keybindings\"\n *\n * keybindings(window, {\n * \t\"Shift+d\": () => {\n * \t\talert(\"The 'Shift' and 'd' keys were pressed at the same time\")\n * \t},\n * \t\"y e e t\": () => {\n * \t\talert(\"The keys 'y', 'e', 'e', and 't' were pressed in order\")\n * \t},\n * \t\"$mod+d\": () => {\n * \t\talert(\"Either 'Control+d' or 'Meta+d' were pressed\")\n * \t},\n * })\n * ```\n */\nexport default function keybindings(\n\ttarget: Window | HTMLElement,\n\tkeyBindingMap: KeyBindingMap,\n\toptions: KeyBindingOptions = {},\n): () => void {\n\tlet timeout = options.timeout ?? DEFAULT_TIMEOUT\n\tlet event = options.event ?? DEFAULT_EVENT\n\n\tlet keyBindings = Object.keys(keyBindingMap).map(key => {\n\t\treturn [parse(key), keyBindingMap[key]] as const\n\t})\n\n\tlet possibleMatches = new Map<KeyBindingPress[], KeyBindingPress[]>()\n\tlet timer: number | null = null\n\n\tlet onKeyEvent: EventListener = event => {\n\t\t// Ensure and stop any event that isn't a full keyboard event.\n\t\t// Autocomplete option navigation and selection would fire a instanceof Event,\n\t\t// instead of the expected KeyboardEvent\n\t\tif (!(event instanceof KeyboardEvent)) {\n\t\t\treturn\n\t\t}\n\n\t\tkeyBindings.forEach(keyBinding => {\n\t\t\tlet sequence = keyBinding[0]\n\t\t\tlet callback = keyBinding[1]\n\n\t\t\tlet prev = possibleMatches.get(sequence)\n\t\t\tlet remainingExpectedPresses = prev ? prev : sequence\n\t\t\tlet currentExpectedPress = remainingExpectedPresses[0]\n\n\t\t\tlet matches = match(event, currentExpectedPress)\n\n\t\t\tif (!matches) {\n\t\t\t\t// Modifier keydown events shouldn't break sequences\n\t\t\t\t// Note: This works because:\n\t\t\t\t// - non-modifiers will always return false\n\t\t\t\t// - if the current keypress is a modifier then it will return true when we check its state\n\t\t\t\t// MDN: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState\n\t\t\t\tif (!event.getModifierState(event.key)) {\n\t\t\t\t\tpossibleMatches.delete(sequence)\n\t\t\t\t}\n\t\t\t} else if (remainingExpectedPresses.length > 1) {\n\t\t\t\tpossibleMatches.set(sequence, remainingExpectedPresses.slice(1))\n\t\t\t} else {\n\t\t\t\tpossibleMatches.delete(sequence)\n\t\t\t\tcallback(event)\n\t\t\t}\n\t\t})\n\n\t\tif (timer) {\n\t\t\tclearTimeout(timer)\n\t\t}\n\n\t\ttimer = setTimeout(possibleMatches.clear.bind(possibleMatches), timeout)\n\t}\n\n\ttarget.addEventListener(event, onKeyEvent)\n\n\treturn () => {\n\t\ttarget.removeEventListener(event, onKeyEvent)\n\t}\n}\n"],"names":["KEYBINDING_MODIFIER_KEYS","MOD","navigator","test","platform","target","keyBindingMap","options","timeout","event","keyBindings","Object","keys","map","key","str","trim","split","press","mods","pop","mod","possibleMatches","Map","timer","onKeyEvent","KeyboardEvent","forEach","keyBinding","sequence","callback","remainingExpectedPresses","get","toUpperCase","code","find","getModifierState","includes","match","length","set","slice","clearTimeout","setTimeout","clear","bind","addEventListener","removeEventListener"],"mappings":"AAiCA,IAAIA,EAA2B,CAAC,QAAS,OAAQ,MAAO,WAgBpDC,EACkB,iBAAdC,WACP,uBAAuBC,KAAKD,UAAUE,UACnC,OACA,kCA0EHC,EACAC,EACAC,oBAAAA,IAAAA,EAA6B,IAE7B,IAAIC,WAAUD,EAAQC,WA5FD,IA6FjBC,WAAQF,EAAQE,SAxFA,UA0FhBC,EAAcC,OAAOC,KAAKN,GAAeO,IAAI,SAAAC,GAChD,MAAO,EAxEMC,EAwECD,EAvERC,EACLC,OACAC,MAAM,KACNJ,IAAI,SAAAK,GACJ,IAAIC,EAAOD,EAAMD,MAAM,QACnBH,EAAMK,EAAKC,MAEf,MAAO,CADPD,EAAOA,EAAKN,IAAI,SAAAQ,SAAgB,SAARA,EAAiBpB,EAAMoB,IACjCP,MAgEKR,EAAcQ,IAxEpC,IAAeC,IA2EVO,EAAkB,IAAIC,IACtBC,EAAuB,KAEvBC,EAA4B,SAAAhB,GAIzBA,aAAiBiB,gBAIvBhB,EAAYiB,QAAQ,SAAAC,GACnB,IAAIC,EAAWD,EAAW,GACtBE,EAAWF,EAAW,GAGtBG,EADOT,EAAgBU,IAAIH,IACcA,GA3EhD,SAAepB,EAAsBS,GAEpC,QAKEA,EAAM,GAAGe,gBAAkBxB,EAAMK,IAAImB,eACrCf,EAAM,KAAOT,EAAMyB,MAIpBhB,EAAM,GAAGiB,KAAK,SAAAd,GACb,OAAQZ,EAAM2B,iBAAiBf,MAMhCrB,EAAyBmC,KAAK,SAAAd,GAC7B,OAAQH,EAAM,GAAGmB,SAAShB,IAAQH,EAAM,KAAOG,GAAOZ,EAAM2B,iBAAiBf,MA0D/DiB,CAAM7B,EAFOsB,EAAyB,IAU9CtB,EAAM2B,iBAAiB3B,EAAMK,MACjCQ,SAAuBO,GAEdE,EAAyBQ,OAAS,EAC5CjB,EAAgBkB,IAAIX,EAAUE,EAAyBU,MAAM,KAE7DnB,SAAuBO,GACvBC,EAASrB,MAIPe,GACHkB,aAAalB,GAGdA,EAAQmB,WAAWrB,EAAgBsB,MAAMC,KAAKvB,GAAkBd,KAKjE,OAFAH,EAAOyC,iBAAiBrC,EAAOgB,cAG9BpB,EAAO0C,oBAAoBtC,EAAOgB"}
|
package/dist/tinykeys.modern.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
let e=["Shift","Meta","Alt","Control"],t="object"==typeof navigator&&/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"Meta":"Control";
|
|
1
|
+
let e=["Shift","Meta","Alt","Control"],t="object"==typeof navigator&&/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"Meta":"Control";function n(n,o,r={}){var i,a;let l=null!=(i=r.timeout)?i:1e3,d=null!=(a=r.event)?a:"keydown",p=Object.keys(o).map(e=>{return[(n=e,n.trim().split(" ").map(e=>{let n=e.split(/\b\+/),o=n.pop();return n=n.map(e=>"$mod"===e?t:e),[n,o]})),o[e]];var n}),u=new Map,f=null,s=t=>{t instanceof KeyboardEvent&&(p.forEach(n=>{let o=n[0],r=n[1],i=u.get(o)||o;!function(t,n){return!(n[1].toUpperCase()!==t.key.toUpperCase()&&n[1]!==t.code||n[0].find(e=>!t.getModifierState(e))||e.find(e=>!n[0].includes(e)&&n[1]!==e&&t.getModifierState(e)))}(t,i[0])?t.getModifierState(t.key)||u.delete(o):i.length>1?u.set(o,i.slice(1)):(u.delete(o),r(t))}),f&&clearTimeout(f),f=setTimeout(u.clear.bind(u),l))};return n.addEventListener(d,s),()=>{n.removeEventListener(d,s)}}export default n;
|
|
2
2
|
//# sourceMappingURL=tinykeys.modern.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tinykeys.modern.js","sources":["../src/tinykeys.ts"],"sourcesContent":["type KeyBindingPress = [string[], string]\n\n/**\n * A map of keybinding strings to event handlers.\n */\nexport interface KeyBindingMap {\n\t[keybinding: string]: (event: KeyboardEvent) => void\n}\n\n/**\n * These are the modifier keys that change the meaning of keybindings.\n *\n * Note: Ignoring \"AltGraph\" because it is covered by the others.\n */\nlet KEYBINDING_MODIFIER_KEYS = [\"Shift\", \"Meta\", \"Alt\", \"Control\"]\n\n/**\n * Keybinding sequences should timeout if individual key presses are more than\n * 1s apart.\n */\nlet
|
|
1
|
+
{"version":3,"file":"tinykeys.modern.js","sources":["../src/tinykeys.ts"],"sourcesContent":["type KeyBindingPress = [string[], string]\n\n/**\n * A map of keybinding strings to event handlers.\n */\nexport interface KeyBindingMap {\n\t[keybinding: string]: (event: KeyboardEvent) => void\n}\n\n/**\n * Options to configure the behavior of keybindings.\n */\n export interface KeyBindingOptions {\n /**\n * Key presses will listen to this event (default: \"keydown\").\n */\n event?: \"keydown\" | \"keyup\"\n\n /**\n * Keybinding sequences will wait this long between key presses before\n\t * cancelling (default: 1000).\n\t *\n\t * **Note:** Setting this value too low (i.e. `300`) will be too fast for many\n\t * of your users.\n */\n timeout?: number\n}\n\n/**\n * These are the modifier keys that change the meaning of keybindings.\n *\n * Note: Ignoring \"AltGraph\" because it is covered by the others.\n */\nlet KEYBINDING_MODIFIER_KEYS = [\"Shift\", \"Meta\", \"Alt\", \"Control\"]\n\n/**\n * Keybinding sequences should timeout if individual key presses are more than\n * 1s apart by default.\n */\nlet DEFAULT_TIMEOUT = 1000\n\n/**\n * Keybinding sequences should bind to this event by default.\n */\n let DEFAULT_EVENT = \"keydown\"\n\n/**\n * An alias for creating platform-specific keybinding aliases.\n */\nlet MOD =\n\ttypeof navigator === \"object\" &&\n\t/Mac|iPod|iPhone|iPad/.test(navigator.platform)\n\t\t? \"Meta\"\n\t\t: \"Control\"\n\n/**\n * Parses a \"Key Binding String\" into its parts\n *\n * grammar = `<sequence>`\n * <sequence> = `<press> <press> <press> ...`\n * <press> = `<key>` or `<mods>+<key>`\n * <mods> = `<mod>+<mod>+...`\n */\nfunction parse(str: string): KeyBindingPress[] {\n\treturn str\n\t\t.trim()\n\t\t.split(\" \")\n\t\t.map(press => {\n\t\t\tlet mods = press.split(/\\b\\+/)\n\t\t\tlet key = mods.pop() as string\n\t\t\tmods = mods.map(mod => (mod === \"$mod\" ? MOD : mod))\n\t\t\treturn [mods, key]\n\t\t})\n}\n\n/**\n * This tells us if a series of events matches a key binding sequence either\n * partially or exactly.\n */\nfunction match(event: KeyboardEvent, press: KeyBindingPress): boolean {\n\t// prettier-ignore\n\treturn !(\n\t\t// Allow either the `event.key` or the `event.code`\n\t\t// MDN event.key: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key\n\t\t// MDN event.code: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code\n\t\t(\n\t\t\tpress[1].toUpperCase() !== event.key.toUpperCase() &&\n\t\t\tpress[1] !== event.code\n\t\t) ||\n\n\t\t// Ensure all the modifiers in the keybinding are pressed.\n\t\tpress[0].find(mod => {\n\t\t\treturn !event.getModifierState(mod)\n\t\t}) ||\n\n\t\t// KEYBINDING_MODIFIER_KEYS (Shift/Control/etc) change the meaning of a\n\t\t// keybinding. So if they are pressed but aren't part of the current\n\t\t// keybinding press, then we don't have a match.\n\t\tKEYBINDING_MODIFIER_KEYS.find(mod => {\n\t\t\treturn !press[0].includes(mod) && press[1] !== mod && event.getModifierState(mod)\n\t\t})\n\t)\n}\n\n/**\n * Subscribes to keybindings.\n *\n * Returns an unsubscribe method.\n *\n * @example\n * ```js\n * import keybindings from \"../src/keybindings\"\n *\n * keybindings(window, {\n * \t\"Shift+d\": () => {\n * \t\talert(\"The 'Shift' and 'd' keys were pressed at the same time\")\n * \t},\n * \t\"y e e t\": () => {\n * \t\talert(\"The keys 'y', 'e', 'e', and 't' were pressed in order\")\n * \t},\n * \t\"$mod+d\": () => {\n * \t\talert(\"Either 'Control+d' or 'Meta+d' were pressed\")\n * \t},\n * })\n * ```\n */\nexport default function keybindings(\n\ttarget: Window | HTMLElement,\n\tkeyBindingMap: KeyBindingMap,\n\toptions: KeyBindingOptions = {},\n): () => void {\n\tlet timeout = options.timeout ?? DEFAULT_TIMEOUT\n\tlet event = options.event ?? DEFAULT_EVENT\n\n\tlet keyBindings = Object.keys(keyBindingMap).map(key => {\n\t\treturn [parse(key), keyBindingMap[key]] as const\n\t})\n\n\tlet possibleMatches = new Map<KeyBindingPress[], KeyBindingPress[]>()\n\tlet timer: number | null = null\n\n\tlet onKeyEvent: EventListener = event => {\n\t\t// Ensure and stop any event that isn't a full keyboard event.\n\t\t// Autocomplete option navigation and selection would fire a instanceof Event,\n\t\t// instead of the expected KeyboardEvent\n\t\tif (!(event instanceof KeyboardEvent)) {\n\t\t\treturn\n\t\t}\n\n\t\tkeyBindings.forEach(keyBinding => {\n\t\t\tlet sequence = keyBinding[0]\n\t\t\tlet callback = keyBinding[1]\n\n\t\t\tlet prev = possibleMatches.get(sequence)\n\t\t\tlet remainingExpectedPresses = prev ? prev : sequence\n\t\t\tlet currentExpectedPress = remainingExpectedPresses[0]\n\n\t\t\tlet matches = match(event, currentExpectedPress)\n\n\t\t\tif (!matches) {\n\t\t\t\t// Modifier keydown events shouldn't break sequences\n\t\t\t\t// Note: This works because:\n\t\t\t\t// - non-modifiers will always return false\n\t\t\t\t// - if the current keypress is a modifier then it will return true when we check its state\n\t\t\t\t// MDN: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState\n\t\t\t\tif (!event.getModifierState(event.key)) {\n\t\t\t\t\tpossibleMatches.delete(sequence)\n\t\t\t\t}\n\t\t\t} else if (remainingExpectedPresses.length > 1) {\n\t\t\t\tpossibleMatches.set(sequence, remainingExpectedPresses.slice(1))\n\t\t\t} else {\n\t\t\t\tpossibleMatches.delete(sequence)\n\t\t\t\tcallback(event)\n\t\t\t}\n\t\t})\n\n\t\tif (timer) {\n\t\t\tclearTimeout(timer)\n\t\t}\n\n\t\ttimer = setTimeout(possibleMatches.clear.bind(possibleMatches), timeout)\n\t}\n\n\ttarget.addEventListener(event, onKeyEvent)\n\n\treturn () => {\n\t\ttarget.removeEventListener(event, onKeyEvent)\n\t}\n}\n"],"names":["KEYBINDING_MODIFIER_KEYS","MOD","navigator","test","platform","keybindings","target","keyBindingMap","options","timeout","event","keyBindings","Object","keys","map","key","str","trim","split","press","mods","pop","mod","possibleMatches","Map","timer","onKeyEvent","KeyboardEvent","forEach","keyBinding","sequence","callback","remainingExpectedPresses","get","toUpperCase","code","find","getModifierState","includes","match","delete","length","set","slice","clearTimeout","setTimeout","clear","bind","addEventListener","removeEventListener"],"mappings":"AAiCA,IAAIA,EAA2B,CAAC,QAAS,OAAQ,MAAO,WAgBpDC,EACkB,iBAAdC,WACP,uBAAuBC,KAAKD,UAAUE,UACnC,OACA,mBAyEoBC,EACvBC,EACAC,EACAC,EAA6B,YAE7B,IAAIC,WAAUD,EAAQC,WA5FD,IA6FjBC,WAAQF,EAAQE,SAxFA,UA0FhBC,EAAcC,OAAOC,KAAKN,GAAeO,IAAIC,IAChD,MAAO,EAxEMC,EAwECD,EAvERC,EACLC,OACAC,MAAM,KACNJ,IAAIK,IACJ,IAAIC,EAAOD,EAAMD,MAAM,QACnBH,EAAMK,EAAKC,MAEf,OADAD,EAAOA,EAAKN,IAAIQ,GAAgB,SAARA,EAAiBrB,EAAMqB,GACxC,CAACF,EAAML,MAgEKR,EAAcQ,IAxEpC,IAAeC,IA2EVO,EAAkB,IAAIC,IACtBC,EAAuB,KAEvBC,EAA4BhB,IAIzBA,aAAiBiB,gBAIvBhB,EAAYiB,QAAQC,IACnB,IAAIC,EAAWD,EAAW,GACtBE,EAAWF,EAAW,GAGtBG,EADOT,EAAgBU,IAAIH,IACcA,GA3EhD,SAAepB,EAAsBS,GAEpC,QAKEA,EAAM,GAAGe,gBAAkBxB,EAAMK,IAAImB,eACrCf,EAAM,KAAOT,EAAMyB,MAIpBhB,EAAM,GAAGiB,KAAKd,IACLZ,EAAM2B,iBAAiBf,KAMhCtB,EAAyBoC,KAAKd,IACrBH,EAAM,GAAGmB,SAAShB,IAAQH,EAAM,KAAOG,GAAOZ,EAAM2B,iBAAiBf,KA0D/DiB,CAAM7B,EAFOsB,EAAyB,IAU9CtB,EAAM2B,iBAAiB3B,EAAMK,MACjCQ,EAAgBiB,OAAOV,GAEdE,EAAyBS,OAAS,EAC5ClB,EAAgBmB,IAAIZ,EAAUE,EAAyBW,MAAM,KAE7DpB,EAAgBiB,OAAOV,GACvBC,EAASrB,MAIPe,GACHmB,aAAanB,GAGdA,EAAQoB,WAAWtB,EAAgBuB,MAAMC,KAAKxB,GAAkBd,KAKjE,OAFAH,EAAO0C,iBAAiBtC,EAAOgB,GAExB,KACNpB,EAAO2C,oBAAoBvC,EAAOgB"}
|
package/dist/tinykeys.module.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=["Shift","Meta","Alt","Control"],t="object"==typeof navigator&&/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"Meta":"Control";
|
|
1
|
+
var e=["Shift","Meta","Alt","Control"],t="object"==typeof navigator&&/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"Meta":"Control";function n(n,o,r){var i,a;void 0===r&&(r={});var u=null!=(i=r.timeout)?i:1e3,f=null!=(a=r.event)?a:"keydown",c=Object.keys(o).map(function(e){return[(n=e,n.trim().split(" ").map(function(e){var n=e.split(/\b\+/),o=n.pop();return[n=n.map(function(e){return"$mod"===e?t:e}),o]})),o[e]];var n}),l=new Map,d=null,p=function(t){t instanceof KeyboardEvent&&(c.forEach(function(n){var o=n[0],r=n[1],i=l.get(o)||o;!function(t,n){return!(n[1].toUpperCase()!==t.key.toUpperCase()&&n[1]!==t.code||n[0].find(function(e){return!t.getModifierState(e)})||e.find(function(e){return!n[0].includes(e)&&n[1]!==e&&t.getModifierState(e)}))}(t,i[0])?t.getModifierState(t.key)||l.delete(o):i.length>1?l.set(o,i.slice(1)):(l.delete(o),r(t))}),d&&clearTimeout(d),d=setTimeout(l.clear.bind(l),u))};return n.addEventListener(f,p),function(){n.removeEventListener(f,p)}}export default n;
|
|
2
2
|
//# sourceMappingURL=tinykeys.module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tinykeys.module.js","sources":["../src/tinykeys.ts"],"sourcesContent":["type KeyBindingPress = [string[], string]\n\n/**\n * A map of keybinding strings to event handlers.\n */\nexport interface KeyBindingMap {\n\t[keybinding: string]: (event: KeyboardEvent) => void\n}\n\n/**\n * These are the modifier keys that change the meaning of keybindings.\n *\n * Note: Ignoring \"AltGraph\" because it is covered by the others.\n */\nlet KEYBINDING_MODIFIER_KEYS = [\"Shift\", \"Meta\", \"Alt\", \"Control\"]\n\n/**\n * Keybinding sequences should timeout if individual key presses are more than\n * 1s apart.\n */\nlet
|
|
1
|
+
{"version":3,"file":"tinykeys.module.js","sources":["../src/tinykeys.ts"],"sourcesContent":["type KeyBindingPress = [string[], string]\n\n/**\n * A map of keybinding strings to event handlers.\n */\nexport interface KeyBindingMap {\n\t[keybinding: string]: (event: KeyboardEvent) => void\n}\n\n/**\n * Options to configure the behavior of keybindings.\n */\n export interface KeyBindingOptions {\n /**\n * Key presses will listen to this event (default: \"keydown\").\n */\n event?: \"keydown\" | \"keyup\"\n\n /**\n * Keybinding sequences will wait this long between key presses before\n\t * cancelling (default: 1000).\n\t *\n\t * **Note:** Setting this value too low (i.e. `300`) will be too fast for many\n\t * of your users.\n */\n timeout?: number\n}\n\n/**\n * These are the modifier keys that change the meaning of keybindings.\n *\n * Note: Ignoring \"AltGraph\" because it is covered by the others.\n */\nlet KEYBINDING_MODIFIER_KEYS = [\"Shift\", \"Meta\", \"Alt\", \"Control\"]\n\n/**\n * Keybinding sequences should timeout if individual key presses are more than\n * 1s apart by default.\n */\nlet DEFAULT_TIMEOUT = 1000\n\n/**\n * Keybinding sequences should bind to this event by default.\n */\n let DEFAULT_EVENT = \"keydown\"\n\n/**\n * An alias for creating platform-specific keybinding aliases.\n */\nlet MOD =\n\ttypeof navigator === \"object\" &&\n\t/Mac|iPod|iPhone|iPad/.test(navigator.platform)\n\t\t? \"Meta\"\n\t\t: \"Control\"\n\n/**\n * Parses a \"Key Binding String\" into its parts\n *\n * grammar = `<sequence>`\n * <sequence> = `<press> <press> <press> ...`\n * <press> = `<key>` or `<mods>+<key>`\n * <mods> = `<mod>+<mod>+...`\n */\nfunction parse(str: string): KeyBindingPress[] {\n\treturn str\n\t\t.trim()\n\t\t.split(\" \")\n\t\t.map(press => {\n\t\t\tlet mods = press.split(/\\b\\+/)\n\t\t\tlet key = mods.pop() as string\n\t\t\tmods = mods.map(mod => (mod === \"$mod\" ? MOD : mod))\n\t\t\treturn [mods, key]\n\t\t})\n}\n\n/**\n * This tells us if a series of events matches a key binding sequence either\n * partially or exactly.\n */\nfunction match(event: KeyboardEvent, press: KeyBindingPress): boolean {\n\t// prettier-ignore\n\treturn !(\n\t\t// Allow either the `event.key` or the `event.code`\n\t\t// MDN event.key: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key\n\t\t// MDN event.code: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code\n\t\t(\n\t\t\tpress[1].toUpperCase() !== event.key.toUpperCase() &&\n\t\t\tpress[1] !== event.code\n\t\t) ||\n\n\t\t// Ensure all the modifiers in the keybinding are pressed.\n\t\tpress[0].find(mod => {\n\t\t\treturn !event.getModifierState(mod)\n\t\t}) ||\n\n\t\t// KEYBINDING_MODIFIER_KEYS (Shift/Control/etc) change the meaning of a\n\t\t// keybinding. So if they are pressed but aren't part of the current\n\t\t// keybinding press, then we don't have a match.\n\t\tKEYBINDING_MODIFIER_KEYS.find(mod => {\n\t\t\treturn !press[0].includes(mod) && press[1] !== mod && event.getModifierState(mod)\n\t\t})\n\t)\n}\n\n/**\n * Subscribes to keybindings.\n *\n * Returns an unsubscribe method.\n *\n * @example\n * ```js\n * import keybindings from \"../src/keybindings\"\n *\n * keybindings(window, {\n * \t\"Shift+d\": () => {\n * \t\talert(\"The 'Shift' and 'd' keys were pressed at the same time\")\n * \t},\n * \t\"y e e t\": () => {\n * \t\talert(\"The keys 'y', 'e', 'e', and 't' were pressed in order\")\n * \t},\n * \t\"$mod+d\": () => {\n * \t\talert(\"Either 'Control+d' or 'Meta+d' were pressed\")\n * \t},\n * })\n * ```\n */\nexport default function keybindings(\n\ttarget: Window | HTMLElement,\n\tkeyBindingMap: KeyBindingMap,\n\toptions: KeyBindingOptions = {},\n): () => void {\n\tlet timeout = options.timeout ?? DEFAULT_TIMEOUT\n\tlet event = options.event ?? DEFAULT_EVENT\n\n\tlet keyBindings = Object.keys(keyBindingMap).map(key => {\n\t\treturn [parse(key), keyBindingMap[key]] as const\n\t})\n\n\tlet possibleMatches = new Map<KeyBindingPress[], KeyBindingPress[]>()\n\tlet timer: number | null = null\n\n\tlet onKeyEvent: EventListener = event => {\n\t\t// Ensure and stop any event that isn't a full keyboard event.\n\t\t// Autocomplete option navigation and selection would fire a instanceof Event,\n\t\t// instead of the expected KeyboardEvent\n\t\tif (!(event instanceof KeyboardEvent)) {\n\t\t\treturn\n\t\t}\n\n\t\tkeyBindings.forEach(keyBinding => {\n\t\t\tlet sequence = keyBinding[0]\n\t\t\tlet callback = keyBinding[1]\n\n\t\t\tlet prev = possibleMatches.get(sequence)\n\t\t\tlet remainingExpectedPresses = prev ? prev : sequence\n\t\t\tlet currentExpectedPress = remainingExpectedPresses[0]\n\n\t\t\tlet matches = match(event, currentExpectedPress)\n\n\t\t\tif (!matches) {\n\t\t\t\t// Modifier keydown events shouldn't break sequences\n\t\t\t\t// Note: This works because:\n\t\t\t\t// - non-modifiers will always return false\n\t\t\t\t// - if the current keypress is a modifier then it will return true when we check its state\n\t\t\t\t// MDN: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState\n\t\t\t\tif (!event.getModifierState(event.key)) {\n\t\t\t\t\tpossibleMatches.delete(sequence)\n\t\t\t\t}\n\t\t\t} else if (remainingExpectedPresses.length > 1) {\n\t\t\t\tpossibleMatches.set(sequence, remainingExpectedPresses.slice(1))\n\t\t\t} else {\n\t\t\t\tpossibleMatches.delete(sequence)\n\t\t\t\tcallback(event)\n\t\t\t}\n\t\t})\n\n\t\tif (timer) {\n\t\t\tclearTimeout(timer)\n\t\t}\n\n\t\ttimer = setTimeout(possibleMatches.clear.bind(possibleMatches), timeout)\n\t}\n\n\ttarget.addEventListener(event, onKeyEvent)\n\n\treturn () => {\n\t\ttarget.removeEventListener(event, onKeyEvent)\n\t}\n}\n"],"names":["KEYBINDING_MODIFIER_KEYS","MOD","navigator","test","platform","keybindings","target","keyBindingMap","options","timeout","event","keyBindings","Object","keys","map","key","str","trim","split","press","mods","pop","mod","possibleMatches","Map","timer","onKeyEvent","KeyboardEvent","forEach","keyBinding","sequence","callback","remainingExpectedPresses","get","toUpperCase","code","find","getModifierState","includes","match","length","set","slice","clearTimeout","setTimeout","clear","bind","addEventListener","removeEventListener"],"mappings":"AAiCA,IAAIA,EAA2B,CAAC,QAAS,OAAQ,MAAO,WAgBpDC,EACkB,iBAAdC,WACP,uBAAuBC,KAAKD,UAAUE,UACnC,OACA,mBAyEoBC,EACvBC,EACAC,EACAC,oBAAAA,IAAAA,EAA6B,IAE7B,IAAIC,WAAUD,EAAQC,WA5FD,IA6FjBC,WAAQF,EAAQE,SAxFA,UA0FhBC,EAAcC,OAAOC,KAAKN,GAAeO,IAAI,SAAAC,GAChD,MAAO,EAxEMC,EAwECD,EAvERC,EACLC,OACAC,MAAM,KACNJ,IAAI,SAAAK,GACJ,IAAIC,EAAOD,EAAMD,MAAM,QACnBH,EAAMK,EAAKC,MAEf,MAAO,CADPD,EAAOA,EAAKN,IAAI,SAAAQ,SAAgB,SAARA,EAAiBrB,EAAMqB,IACjCP,MAgEKR,EAAcQ,IAxEpC,IAAeC,IA2EVO,EAAkB,IAAIC,IACtBC,EAAuB,KAEvBC,EAA4B,SAAAhB,GAIzBA,aAAiBiB,gBAIvBhB,EAAYiB,QAAQ,SAAAC,GACnB,IAAIC,EAAWD,EAAW,GACtBE,EAAWF,EAAW,GAGtBG,EADOT,EAAgBU,IAAIH,IACcA,GA3EhD,SAAepB,EAAsBS,GAEpC,QAKEA,EAAM,GAAGe,gBAAkBxB,EAAMK,IAAImB,eACrCf,EAAM,KAAOT,EAAMyB,MAIpBhB,EAAM,GAAGiB,KAAK,SAAAd,GACb,OAAQZ,EAAM2B,iBAAiBf,MAMhCtB,EAAyBoC,KAAK,SAAAd,GAC7B,OAAQH,EAAM,GAAGmB,SAAShB,IAAQH,EAAM,KAAOG,GAAOZ,EAAM2B,iBAAiBf,MA0D/DiB,CAAM7B,EAFOsB,EAAyB,IAU9CtB,EAAM2B,iBAAiB3B,EAAMK,MACjCQ,SAAuBO,GAEdE,EAAyBQ,OAAS,EAC5CjB,EAAgBkB,IAAIX,EAAUE,EAAyBU,MAAM,KAE7DnB,SAAuBO,GACvBC,EAASrB,MAIPe,GACHkB,aAAalB,GAGdA,EAAQmB,WAAWrB,EAAgBsB,MAAMC,KAAKvB,GAAkBd,KAKjE,OAFAH,EAAOyC,iBAAiBrC,EAAOgB,cAG9BpB,EAAO0C,oBAAoBtC,EAAOgB"}
|
package/dist/tinykeys.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e||self).tinykeys=t()}(this,function(){var e=["Shift","Meta","Alt","Control"],t="object"==typeof navigator&&/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"Meta":"Control";return function(n,o,i){var r,a;void 0===i&&(i={});var f=null!=(r=i.timeout)?r:1e3,u=null!=(a=i.event)?a:"keydown",d=Object.keys(o).map(function(e){return[(n=e,n.trim().split(" ").map(function(e){var n=e.split(/\b\+/),o=n.pop();return[n=n.map(function(e){return"$mod"===e?t:e}),o]})),o[e]];var n}),l=new Map,c=null,p=function(t){t instanceof KeyboardEvent&&(d.forEach(function(n){var o=n[0],i=n[1],r=l.get(o)||o;!function(t,n){return!(n[1].toUpperCase()!==t.key.toUpperCase()&&n[1]!==t.code||n[0].find(function(e){return!t.getModifierState(e)})||e.find(function(e){return!n[0].includes(e)&&n[1]!==e&&t.getModifierState(e)}))}(t,r[0])?t.getModifierState(t.key)||l.delete(o):r.length>1?l.set(o,r.slice(1)):(l.delete(o),i(t))}),c&&clearTimeout(c),c=setTimeout(l.clear.bind(l),f))};return n.addEventListener(u,p),function(){n.removeEventListener(u,p)}}});
|
|
2
2
|
//# sourceMappingURL=tinykeys.umd.js.map
|
package/dist/tinykeys.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tinykeys.umd.js","sources":["../src/tinykeys.ts"],"sourcesContent":["type KeyBindingPress = [string[], string]\n\n/**\n * A map of keybinding strings to event handlers.\n */\nexport interface KeyBindingMap {\n\t[keybinding: string]: (event: KeyboardEvent) => void\n}\n\n/**\n * These are the modifier keys that change the meaning of keybindings.\n *\n * Note: Ignoring \"AltGraph\" because it is covered by the others.\n */\nlet KEYBINDING_MODIFIER_KEYS = [\"Shift\", \"Meta\", \"Alt\", \"Control\"]\n\n/**\n * Keybinding sequences should timeout if individual key presses are more than\n * 1s apart.\n */\nlet
|
|
1
|
+
{"version":3,"file":"tinykeys.umd.js","sources":["../src/tinykeys.ts"],"sourcesContent":["type KeyBindingPress = [string[], string]\n\n/**\n * A map of keybinding strings to event handlers.\n */\nexport interface KeyBindingMap {\n\t[keybinding: string]: (event: KeyboardEvent) => void\n}\n\n/**\n * Options to configure the behavior of keybindings.\n */\n export interface KeyBindingOptions {\n /**\n * Key presses will listen to this event (default: \"keydown\").\n */\n event?: \"keydown\" | \"keyup\"\n\n /**\n * Keybinding sequences will wait this long between key presses before\n\t * cancelling (default: 1000).\n\t *\n\t * **Note:** Setting this value too low (i.e. `300`) will be too fast for many\n\t * of your users.\n */\n timeout?: number\n}\n\n/**\n * These are the modifier keys that change the meaning of keybindings.\n *\n * Note: Ignoring \"AltGraph\" because it is covered by the others.\n */\nlet KEYBINDING_MODIFIER_KEYS = [\"Shift\", \"Meta\", \"Alt\", \"Control\"]\n\n/**\n * Keybinding sequences should timeout if individual key presses are more than\n * 1s apart by default.\n */\nlet DEFAULT_TIMEOUT = 1000\n\n/**\n * Keybinding sequences should bind to this event by default.\n */\n let DEFAULT_EVENT = \"keydown\"\n\n/**\n * An alias for creating platform-specific keybinding aliases.\n */\nlet MOD =\n\ttypeof navigator === \"object\" &&\n\t/Mac|iPod|iPhone|iPad/.test(navigator.platform)\n\t\t? \"Meta\"\n\t\t: \"Control\"\n\n/**\n * Parses a \"Key Binding String\" into its parts\n *\n * grammar = `<sequence>`\n * <sequence> = `<press> <press> <press> ...`\n * <press> = `<key>` or `<mods>+<key>`\n * <mods> = `<mod>+<mod>+...`\n */\nfunction parse(str: string): KeyBindingPress[] {\n\treturn str\n\t\t.trim()\n\t\t.split(\" \")\n\t\t.map(press => {\n\t\t\tlet mods = press.split(/\\b\\+/)\n\t\t\tlet key = mods.pop() as string\n\t\t\tmods = mods.map(mod => (mod === \"$mod\" ? MOD : mod))\n\t\t\treturn [mods, key]\n\t\t})\n}\n\n/**\n * This tells us if a series of events matches a key binding sequence either\n * partially or exactly.\n */\nfunction match(event: KeyboardEvent, press: KeyBindingPress): boolean {\n\t// prettier-ignore\n\treturn !(\n\t\t// Allow either the `event.key` or the `event.code`\n\t\t// MDN event.key: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key\n\t\t// MDN event.code: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code\n\t\t(\n\t\t\tpress[1].toUpperCase() !== event.key.toUpperCase() &&\n\t\t\tpress[1] !== event.code\n\t\t) ||\n\n\t\t// Ensure all the modifiers in the keybinding are pressed.\n\t\tpress[0].find(mod => {\n\t\t\treturn !event.getModifierState(mod)\n\t\t}) ||\n\n\t\t// KEYBINDING_MODIFIER_KEYS (Shift/Control/etc) change the meaning of a\n\t\t// keybinding. So if they are pressed but aren't part of the current\n\t\t// keybinding press, then we don't have a match.\n\t\tKEYBINDING_MODIFIER_KEYS.find(mod => {\n\t\t\treturn !press[0].includes(mod) && press[1] !== mod && event.getModifierState(mod)\n\t\t})\n\t)\n}\n\n/**\n * Subscribes to keybindings.\n *\n * Returns an unsubscribe method.\n *\n * @example\n * ```js\n * import keybindings from \"../src/keybindings\"\n *\n * keybindings(window, {\n * \t\"Shift+d\": () => {\n * \t\talert(\"The 'Shift' and 'd' keys were pressed at the same time\")\n * \t},\n * \t\"y e e t\": () => {\n * \t\talert(\"The keys 'y', 'e', 'e', and 't' were pressed in order\")\n * \t},\n * \t\"$mod+d\": () => {\n * \t\talert(\"Either 'Control+d' or 'Meta+d' were pressed\")\n * \t},\n * })\n * ```\n */\nexport default function keybindings(\n\ttarget: Window | HTMLElement,\n\tkeyBindingMap: KeyBindingMap,\n\toptions: KeyBindingOptions = {},\n): () => void {\n\tlet timeout = options.timeout ?? DEFAULT_TIMEOUT\n\tlet event = options.event ?? DEFAULT_EVENT\n\n\tlet keyBindings = Object.keys(keyBindingMap).map(key => {\n\t\treturn [parse(key), keyBindingMap[key]] as const\n\t})\n\n\tlet possibleMatches = new Map<KeyBindingPress[], KeyBindingPress[]>()\n\tlet timer: number | null = null\n\n\tlet onKeyEvent: EventListener = event => {\n\t\t// Ensure and stop any event that isn't a full keyboard event.\n\t\t// Autocomplete option navigation and selection would fire a instanceof Event,\n\t\t// instead of the expected KeyboardEvent\n\t\tif (!(event instanceof KeyboardEvent)) {\n\t\t\treturn\n\t\t}\n\n\t\tkeyBindings.forEach(keyBinding => {\n\t\t\tlet sequence = keyBinding[0]\n\t\t\tlet callback = keyBinding[1]\n\n\t\t\tlet prev = possibleMatches.get(sequence)\n\t\t\tlet remainingExpectedPresses = prev ? prev : sequence\n\t\t\tlet currentExpectedPress = remainingExpectedPresses[0]\n\n\t\t\tlet matches = match(event, currentExpectedPress)\n\n\t\t\tif (!matches) {\n\t\t\t\t// Modifier keydown events shouldn't break sequences\n\t\t\t\t// Note: This works because:\n\t\t\t\t// - non-modifiers will always return false\n\t\t\t\t// - if the current keypress is a modifier then it will return true when we check its state\n\t\t\t\t// MDN: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState\n\t\t\t\tif (!event.getModifierState(event.key)) {\n\t\t\t\t\tpossibleMatches.delete(sequence)\n\t\t\t\t}\n\t\t\t} else if (remainingExpectedPresses.length > 1) {\n\t\t\t\tpossibleMatches.set(sequence, remainingExpectedPresses.slice(1))\n\t\t\t} else {\n\t\t\t\tpossibleMatches.delete(sequence)\n\t\t\t\tcallback(event)\n\t\t\t}\n\t\t})\n\n\t\tif (timer) {\n\t\t\tclearTimeout(timer)\n\t\t}\n\n\t\ttimer = setTimeout(possibleMatches.clear.bind(possibleMatches), timeout)\n\t}\n\n\ttarget.addEventListener(event, onKeyEvent)\n\n\treturn () => {\n\t\ttarget.removeEventListener(event, onKeyEvent)\n\t}\n}\n"],"names":["KEYBINDING_MODIFIER_KEYS","MOD","navigator","test","platform","target","keyBindingMap","options","timeout","event","keyBindings","Object","keys","map","key","str","trim","split","press","mods","pop","mod","possibleMatches","Map","timer","onKeyEvent","KeyboardEvent","forEach","keyBinding","sequence","callback","remainingExpectedPresses","get","toUpperCase","code","find","getModifierState","includes","match","length","set","slice","clearTimeout","setTimeout","clear","bind","addEventListener","removeEventListener"],"mappings":"2NAiCA,IAAIA,EAA2B,CAAC,QAAS,OAAQ,MAAO,WAgBpDC,EACkB,iBAAdC,WACP,uBAAuBC,KAAKD,UAAUE,UACnC,OACA,0BA0EHC,EACAC,EACAC,oBAAAA,IAAAA,EAA6B,IAE7B,IAAIC,WAAUD,EAAQC,WA5FD,IA6FjBC,WAAQF,EAAQE,SAxFA,UA0FhBC,EAAcC,OAAOC,KAAKN,GAAeO,IAAI,SAAAC,GAChD,MAAO,EAxEMC,EAwECD,EAvERC,EACLC,OACAC,MAAM,KACNJ,IAAI,SAAAK,GACJ,IAAIC,EAAOD,EAAMD,MAAM,QACnBH,EAAMK,EAAKC,MAEf,MAAO,CADPD,EAAOA,EAAKN,IAAI,SAAAQ,SAAgB,SAARA,EAAiBpB,EAAMoB,IACjCP,MAgEKR,EAAcQ,IAxEpC,IAAeC,IA2EVO,EAAkB,IAAIC,IACtBC,EAAuB,KAEvBC,EAA4B,SAAAhB,GAIzBA,aAAiBiB,gBAIvBhB,EAAYiB,QAAQ,SAAAC,GACnB,IAAIC,EAAWD,EAAW,GACtBE,EAAWF,EAAW,GAGtBG,EADOT,EAAgBU,IAAIH,IACcA,GA3EhD,SAAepB,EAAsBS,GAEpC,QAKEA,EAAM,GAAGe,gBAAkBxB,EAAMK,IAAImB,eACrCf,EAAM,KAAOT,EAAMyB,MAIpBhB,EAAM,GAAGiB,KAAK,SAAAd,GACb,OAAQZ,EAAM2B,iBAAiBf,MAMhCrB,EAAyBmC,KAAK,SAAAd,GAC7B,OAAQH,EAAM,GAAGmB,SAAShB,IAAQH,EAAM,KAAOG,GAAOZ,EAAM2B,iBAAiBf,MA0D/DiB,CAAM7B,EAFOsB,EAAyB,IAU9CtB,EAAM2B,iBAAiB3B,EAAMK,MACjCQ,SAAuBO,GAEdE,EAAyBQ,OAAS,EAC5CjB,EAAgBkB,IAAIX,EAAUE,EAAyBU,MAAM,KAE7DnB,SAAuBO,GACvBC,EAASrB,MAIPe,GACHkB,aAAalB,GAGdA,EAAQmB,WAAWrB,EAAgBsB,MAAMC,KAAKvB,GAAkBd,KAKjE,OAFAH,EAAOyC,iBAAiBrC,EAAOgB,cAG9BpB,EAAO0C,oBAAoBtC,EAAOgB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tinykeys",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A tiny (~400 B) & modern library for keybindings.",
|
|
5
5
|
"author": "Jamie Kyle <me@thejameskyle.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,10 +56,10 @@
|
|
|
56
56
|
"@typescript-eslint/parser": "^3.7.1",
|
|
57
57
|
"ava": "^3.11.0",
|
|
58
58
|
"eslint": "^7.5.0",
|
|
59
|
-
"eslint-plugin-ava": "^
|
|
60
|
-
"husky": "^
|
|
61
|
-
"lint-staged": "^
|
|
62
|
-
"microbundle": "^0.
|
|
59
|
+
"eslint-plugin-ava": "^12.0.0",
|
|
60
|
+
"husky": "^6.0.0",
|
|
61
|
+
"lint-staged": "^11.0.0",
|
|
62
|
+
"microbundle": "^0.13.0",
|
|
63
63
|
"nyc": "^15.1.0",
|
|
64
64
|
"parcel": "^1.12.4",
|
|
65
65
|
"prettier": "^2.0.5",
|