@rwh/keystrokes
Advanced tools
Comparing version 1.0.0-beta.4 to 1.0.0-beta.5
@@ -5,3 +5,4 @@ function $parcel$export(e, n, v, s) { | ||
$parcel$export(module.exports, "getGlobalKeystrokesInstance", () => $882b6d93070905b3$export$95543b191b4ac069); | ||
$parcel$export(module.exports, "setGlobalKeystrokes", () => $882b6d93070905b3$export$a75db6f902c6d390); | ||
$parcel$export(module.exports, "getGlobalKeystrokes", () => $882b6d93070905b3$export$7b1f1cb04f669410); | ||
$parcel$export(module.exports, "setGlobalKeystrokesOptions", () => $882b6d93070905b3$export$74e69be5ba6b6e12); | ||
@@ -17,2 +18,3 @@ $parcel$export(module.exports, "bindKey", () => $882b6d93070905b3$export$83d305b8c3d49e47); | ||
$parcel$export(module.exports, "parseKeyCombo", () => $882b6d93070905b3$export$c9a05b4f8b6ef437); | ||
$parcel$export(module.exports, "createTestKeystrokes", () => $882b6d93070905b3$export$367ccd255ed3dfb4); | ||
$parcel$export(module.exports, "Keystrokes", () => $93868bd8661a9a21$export$2f47270a5e20576c); | ||
@@ -397,4 +399,7 @@ class $4fe11402f7ee6a28$export$7d68c18adf0621c5 { | ||
let $882b6d93070905b3$var$globalKeystrokes; | ||
const $882b6d93070905b3$export$95543b191b4ac069 = ()=>{ | ||
if (!$882b6d93070905b3$var$globalKeystrokes) $882b6d93070905b3$var$globalKeystrokes = new (0, $93868bd8661a9a21$export$2f47270a5e20576c)($882b6d93070905b3$var$globalKeystrokesOptions); | ||
const $882b6d93070905b3$export$a75db6f902c6d390 = (keystrokes)=>{ | ||
$882b6d93070905b3$var$globalKeystrokes = keystrokes ?? new (0, $93868bd8661a9a21$export$2f47270a5e20576c)($882b6d93070905b3$var$globalKeystrokesOptions); | ||
}; | ||
const $882b6d93070905b3$export$7b1f1cb04f669410 = ()=>{ | ||
if (!$882b6d93070905b3$var$globalKeystrokes) $882b6d93070905b3$export$a75db6f902c6d390(); | ||
return $882b6d93070905b3$var$globalKeystrokes; | ||
@@ -405,13 +410,39 @@ }; | ||
}; | ||
const $882b6d93070905b3$export$83d305b8c3d49e47 = (...args)=>$882b6d93070905b3$export$95543b191b4ac069().bindKey(...args); | ||
const $882b6d93070905b3$export$9c401994fb1382f3 = (...args)=>$882b6d93070905b3$export$95543b191b4ac069().unbindKey(...args); | ||
const $882b6d93070905b3$export$707a784695817436 = (...args)=>$882b6d93070905b3$export$95543b191b4ac069().bindKeyCombo(...args); | ||
const $882b6d93070905b3$export$fadfd81eb29ffdf4 = (...args)=>$882b6d93070905b3$export$95543b191b4ac069().unbindKeyCombo(...args); | ||
const $882b6d93070905b3$export$d8aec7935f889416 = (...args)=>$882b6d93070905b3$export$95543b191b4ac069().checkKey(...args); | ||
const $882b6d93070905b3$export$fca68bd042c03a38 = (...args)=>$882b6d93070905b3$export$95543b191b4ac069().checkKeyCombo(...args); | ||
const $882b6d93070905b3$export$83d305b8c3d49e47 = (...args)=>$882b6d93070905b3$export$7b1f1cb04f669410().bindKey(...args); | ||
const $882b6d93070905b3$export$9c401994fb1382f3 = (...args)=>$882b6d93070905b3$export$7b1f1cb04f669410().unbindKey(...args); | ||
const $882b6d93070905b3$export$707a784695817436 = (...args)=>$882b6d93070905b3$export$7b1f1cb04f669410().bindKeyCombo(...args); | ||
const $882b6d93070905b3$export$fadfd81eb29ffdf4 = (...args)=>$882b6d93070905b3$export$7b1f1cb04f669410().unbindKeyCombo(...args); | ||
const $882b6d93070905b3$export$d8aec7935f889416 = (...args)=>$882b6d93070905b3$export$7b1f1cb04f669410().checkKey(...args); | ||
const $882b6d93070905b3$export$fca68bd042c03a38 = (...args)=>$882b6d93070905b3$export$7b1f1cb04f669410().checkKeyCombo(...args); | ||
const $882b6d93070905b3$export$3fba9181102f2487 = (0, $742625ffac22206d$export$ed36a648cd79390d).normalizeKeyCombo; | ||
const $882b6d93070905b3$export$531394fdfe259c9e = (0, $742625ffac22206d$export$ed36a648cd79390d).stringifyKeyCombo; | ||
const $882b6d93070905b3$export$c9a05b4f8b6ef437 = (0, $742625ffac22206d$export$ed36a648cd79390d).parseKeyCombo; | ||
const $882b6d93070905b3$export$367ccd255ed3dfb4 = ()=>{ | ||
let activate; | ||
let deactivate; | ||
let press; | ||
let release; | ||
const testKeystrokes = Object.assign(new (0, $93868bd8661a9a21$export$2f47270a5e20576c)({ | ||
onActive (f) { | ||
activate = f; | ||
}, | ||
onInactive (f) { | ||
deactivate = f; | ||
}, | ||
onKeyPressed (f) { | ||
press = f; | ||
}, | ||
onKeyReleased (f) { | ||
release = f; | ||
} | ||
}), { | ||
activate: activate, | ||
deactivate: deactivate, | ||
press: press, | ||
release: release | ||
}); | ||
return testKeystrokes; | ||
}; | ||
//# sourceMappingURL=index.js.map |
@@ -379,4 +379,7 @@ class $f323746ddedca5d6$export$7d68c18adf0621c5 { | ||
let $149c1bd638913645$var$globalKeystrokes; | ||
const $149c1bd638913645$export$95543b191b4ac069 = ()=>{ | ||
if (!$149c1bd638913645$var$globalKeystrokes) $149c1bd638913645$var$globalKeystrokes = new (0, $b48b34d93dc6249e$export$2f47270a5e20576c)($149c1bd638913645$var$globalKeystrokesOptions); | ||
const $149c1bd638913645$export$a75db6f902c6d390 = (keystrokes)=>{ | ||
$149c1bd638913645$var$globalKeystrokes = keystrokes ?? new (0, $b48b34d93dc6249e$export$2f47270a5e20576c)($149c1bd638913645$var$globalKeystrokesOptions); | ||
}; | ||
const $149c1bd638913645$export$7b1f1cb04f669410 = ()=>{ | ||
if (!$149c1bd638913645$var$globalKeystrokes) $149c1bd638913645$export$a75db6f902c6d390(); | ||
return $149c1bd638913645$var$globalKeystrokes; | ||
@@ -387,14 +390,40 @@ }; | ||
}; | ||
const $149c1bd638913645$export$83d305b8c3d49e47 = (...args)=>$149c1bd638913645$export$95543b191b4ac069().bindKey(...args); | ||
const $149c1bd638913645$export$9c401994fb1382f3 = (...args)=>$149c1bd638913645$export$95543b191b4ac069().unbindKey(...args); | ||
const $149c1bd638913645$export$707a784695817436 = (...args)=>$149c1bd638913645$export$95543b191b4ac069().bindKeyCombo(...args); | ||
const $149c1bd638913645$export$fadfd81eb29ffdf4 = (...args)=>$149c1bd638913645$export$95543b191b4ac069().unbindKeyCombo(...args); | ||
const $149c1bd638913645$export$d8aec7935f889416 = (...args)=>$149c1bd638913645$export$95543b191b4ac069().checkKey(...args); | ||
const $149c1bd638913645$export$fca68bd042c03a38 = (...args)=>$149c1bd638913645$export$95543b191b4ac069().checkKeyCombo(...args); | ||
const $149c1bd638913645$export$83d305b8c3d49e47 = (...args)=>$149c1bd638913645$export$7b1f1cb04f669410().bindKey(...args); | ||
const $149c1bd638913645$export$9c401994fb1382f3 = (...args)=>$149c1bd638913645$export$7b1f1cb04f669410().unbindKey(...args); | ||
const $149c1bd638913645$export$707a784695817436 = (...args)=>$149c1bd638913645$export$7b1f1cb04f669410().bindKeyCombo(...args); | ||
const $149c1bd638913645$export$fadfd81eb29ffdf4 = (...args)=>$149c1bd638913645$export$7b1f1cb04f669410().unbindKeyCombo(...args); | ||
const $149c1bd638913645$export$d8aec7935f889416 = (...args)=>$149c1bd638913645$export$7b1f1cb04f669410().checkKey(...args); | ||
const $149c1bd638913645$export$fca68bd042c03a38 = (...args)=>$149c1bd638913645$export$7b1f1cb04f669410().checkKeyCombo(...args); | ||
const $149c1bd638913645$export$3fba9181102f2487 = (0, $cbc1b2a1218eedb5$export$ed36a648cd79390d).normalizeKeyCombo; | ||
const $149c1bd638913645$export$531394fdfe259c9e = (0, $cbc1b2a1218eedb5$export$ed36a648cd79390d).stringifyKeyCombo; | ||
const $149c1bd638913645$export$c9a05b4f8b6ef437 = (0, $cbc1b2a1218eedb5$export$ed36a648cd79390d).parseKeyCombo; | ||
const $149c1bd638913645$export$367ccd255ed3dfb4 = ()=>{ | ||
let activate; | ||
let deactivate; | ||
let press; | ||
let release; | ||
const testKeystrokes = Object.assign(new (0, $b48b34d93dc6249e$export$2f47270a5e20576c)({ | ||
onActive (f) { | ||
activate = f; | ||
}, | ||
onInactive (f) { | ||
deactivate = f; | ||
}, | ||
onKeyPressed (f) { | ||
press = f; | ||
}, | ||
onKeyReleased (f) { | ||
release = f; | ||
} | ||
}), { | ||
activate: activate, | ||
deactivate: deactivate, | ||
press: press, | ||
release: release | ||
}); | ||
return testKeystrokes; | ||
}; | ||
export {$149c1bd638913645$export$95543b191b4ac069 as getGlobalKeystrokesInstance, $149c1bd638913645$export$74e69be5ba6b6e12 as setGlobalKeystrokesOptions, $149c1bd638913645$export$83d305b8c3d49e47 as bindKey, $149c1bd638913645$export$9c401994fb1382f3 as unbindKey, $149c1bd638913645$export$707a784695817436 as bindKeyCombo, $149c1bd638913645$export$fadfd81eb29ffdf4 as unbindKeyCombo, $149c1bd638913645$export$d8aec7935f889416 as checkKey, $149c1bd638913645$export$fca68bd042c03a38 as checkKeyCombo, $149c1bd638913645$export$3fba9181102f2487 as normalizeKeyCombo, $149c1bd638913645$export$531394fdfe259c9e as stringifyKeyCombo, $149c1bd638913645$export$c9a05b4f8b6ef437 as parseKeyCombo, $b48b34d93dc6249e$export$2f47270a5e20576c as Keystrokes}; | ||
export {$149c1bd638913645$export$a75db6f902c6d390 as setGlobalKeystrokes, $149c1bd638913645$export$7b1f1cb04f669410 as getGlobalKeystrokes, $149c1bd638913645$export$74e69be5ba6b6e12 as setGlobalKeystrokesOptions, $149c1bd638913645$export$83d305b8c3d49e47 as bindKey, $149c1bd638913645$export$9c401994fb1382f3 as unbindKey, $149c1bd638913645$export$707a784695817436 as bindKeyCombo, $149c1bd638913645$export$fadfd81eb29ffdf4 as unbindKeyCombo, $149c1bd638913645$export$d8aec7935f889416 as checkKey, $149c1bd638913645$export$fca68bd042c03a38 as checkKeyCombo, $149c1bd638913645$export$3fba9181102f2487 as normalizeKeyCombo, $149c1bd638913645$export$531394fdfe259c9e as stringifyKeyCombo, $149c1bd638913645$export$c9a05b4f8b6ef437 as parseKeyCombo, $149c1bd638913645$export$367ccd255ed3dfb4 as createTestKeystrokes, $b48b34d93dc6249e$export$2f47270a5e20576c as Keystrokes}; | ||
//# sourceMappingURL=module.js.map |
@@ -51,3 +51,4 @@ export type KeyEvent<E> = { | ||
declare let globalKeystrokes: Keystrokes; | ||
export const getGlobalKeystrokesInstance: () => Keystrokes<KeyboardEvent>; | ||
export const setGlobalKeystrokes: (keystrokes?: Keystrokes) => void; | ||
export const getGlobalKeystrokes: () => Keystrokes<KeyboardEvent>; | ||
export const setGlobalKeystrokesOptions: (options: KeystrokesOptions) => void; | ||
@@ -63,3 +64,10 @@ export const bindKey: typeof globalKeystrokes.bindKey; | ||
export const parseKeyCombo: typeof KeyComboState.parseKeyCombo; | ||
export type TestKeystrokes<E> = Keystrokes<E> & { | ||
activate(): void; | ||
deactivate(): void; | ||
press(key: KeyEvent<E>): void; | ||
release(key: KeyEvent<E>): void; | ||
}; | ||
export const createTestKeystrokes: <E = KeyboardEvent>() => TestKeystrokes<E>; | ||
//# sourceMappingURL=types.d.ts.map |
@@ -1,2 +0,2 @@ | ||
!function(){function e(e,t,s,i){Object.defineProperty(e,t,{get:s,set:i,enumerable:!0,configurable:!0})}var t={};function s(e,t,s){return t in e?Object.defineProperty(e,t,{value:s,enumerable:!0,configurable:!0,writable:!0}):e[t]=s,e}e(t,"getGlobalKeystrokesInstance",(function(){return u})),e(t,"setGlobalKeystrokesOptions",(function(){return m})),e(t,"bindKey",(function(){return K})),e(t,"unbindKey",(function(){return b})),e(t,"bindKeyCombo",(function(){return f})),e(t,"unbindKeyCombo",(function(){return C})),e(t,"checkKey",(function(){return p})),e(t,"checkKeyCombo",(function(){return v})),e(t,"normalizeKeyCombo",(function(){return S})),e(t,"stringifyKeyCombo",(function(){return k})),e(t,"parseKeyCombo",(function(){return P})),e(t,"Keystrokes",(function(){return d}));class i{get isEmpty(){return!this._onPressed&&!this._onPressedWithRepeat&&!this._onReleased}isOwnHandler(e){return this._identity===e}executePressed(e){this._isPressed||this._onPressed?.(e),this._isPressed=!0,this._onPressedWithRepeat?.(e)}executeReleased(e){this._isPressed&&this._onReleased?.(e),this._isPressed=!1}constructor(e){this._isPressed=!1,this._identity=e,"function"==typeof e?this._onPressedWithRepeat=e:(this._onPressed=e.onPressed,this._onPressedWithRepeat=e.onPressedWithRepeat,this._onReleased=e.onReleased)}}class n{static parseKeyCombo(e){if(n._parseCache[e])return n._parseCache[e];const t=e.toLowerCase();let s="",i=[],o=[i],r=[o];const a=[r];let h=!1;for(let n=0;n<e.length;n+=1)"\\"===t[n]?h=!0:"+"!==t[n]&&">"!==t[n]&&","!==t[n]||h?t[n].match(/[^\s]/)&&(s&&(","===s?(i=[],o=[i],r=[o],a.push(r)):">"===s?(i=[],o=[i],r.push(o)):"+"===s&&(i=[],o.push(i)),s=""),h=!1,i.push(t[n])):s=t[n];const c=a.map((e=>e.map((e=>e.map((e=>e.join("")))))));return n._parseCache[e]=c,c}static stringifyKeyCombo(e){return e.map((e=>e.map((e=>e.map((e=>"+"===e?"\\+":">"===e?"\\>":","===e?"\\,":e)).join("+"))).join(">"))).join(",")}static normalizeKeyCombo(e){if(n._normalizationCache[e])return n._normalizationCache[e];const t=this.stringifyKeyCombo(this.parseKeyCombo(e));return n._normalizationCache[e]=t,t}get isPressed(){return!!this._isPressedWithFinalKey}isOwnHandler(e){return this._handlerState.isOwnHandler(e)}executePressed(e){this._isPressedWithFinalKey===e.key&&this._handlerState.executePressed(this._wrapEvent(e))}executeReleased(e){this._isPressedWithFinalKey===e.key&&(this._isPressedWithFinalKey="",this._handlerState.executeReleased(this._wrapEvent(e)))}updateState(e){const t=this._parsedKeyCombo[this._sequenceIndex];if(0===e.length)return;let s=0;for(const i of t){let t=s;for(const n of i){let i=!1;for(let o=s;o<e.length;o+=1){if(n===e[o]){o>t&&(t=o),i=!0;break}}if(!i)return void(this._handlerState.isEmpty&&(this._isPressedWithFinalKey=""))}s=t}for(const s of e){let e=!1;for(const i of t)for(const t of i)if(s===t){e=!0;break}if(!e)return void(this._sequenceIndex=0)}this._sequenceIndex<this._parsedKeyCombo.length-1?this._sequenceIndex+=1:(this._sequenceIndex=0,this._isPressedWithFinalKey=e[e.length-1])}_wrapEvent(e){return{keyCombo:this._normalizedKeyCombo,originalEvent:e.originalEvent}}constructor(e,t={}){this._normalizedKeyCombo=n.normalizeKeyCombo(e),this._parsedKeyCombo=n.parseKeyCombo(e),this._handlerState=new i(t),this._isPressedWithFinalKey="",this._sequenceIndex=0}}s(n,"_parseCache",{}),s(n,"_normalizationCache",{});const o="function"==typeof requestAnimationFrame?e=>requestAnimationFrame(e):e=>setTimeout(e,0),r=()=>new Promise((e=>o(e))),a=e=>{try{const t=()=>e();return addEventListener("focus",t),()=>{removeEventListener("focus",t)}}catch{}},h=e=>{try{const t=()=>e();return addEventListener("blur",t),()=>{removeEventListener("blur",t)}}catch{}},c=e=>{try{const t=t=>e({key:t.key,originalEvent:t});return document.addEventListener("keydown",t),()=>{document.removeEventListener("keydown",t)}}catch{}},y=e=>{try{const t=t=>e({key:t.key,originalEvent:t});return document.addEventListener("keyup",t),()=>{document.removeEventListener("keyup",t)}}catch{}};class d{get pressedKeys(){return this._activeKeys.slice(0)}bindKey(e,t){e=e.toLowerCase();const s=new i(t);this._handlerStates[e]??=[],this._handlerStates[e].push(s)}unbindKey(e,t){e=e.toLowerCase();const s=this._handlerStates[e];if(s)if(t)for(let e=0;e<s.length;e+=1)s[e].isOwnHandler(t)&&(s.splice(e,1),e-=1);else s.length=0}bindKeyCombo(e,t){e=n.normalizeKeyCombo(e);const s=new n(e,t);this._keyComboStates[e]??=[],this._keyComboStates[e].push(s),this._keyComboStatesArray.push(s)}unbindKeyCombo(e,t){e=n.normalizeKeyCombo(e);const s=this._keyComboStates[e];if(s)if(t){for(let e=0;e<s.length;e+=1)if(s[e].isOwnHandler(t)){for(let t=0;t<this._keyComboStatesArray.length;t+=1)this._keyComboStatesArray[t]===s[e]&&(this._keyComboStatesArray.splice(t,1),t-=1);s.splice(e,1),e-=1}}else s.length=0}checkKey(e){return this._activeKeySet.has(e.toLowerCase())}checkKeyCombo(e){e=n.normalizeKeyCombo(e),this._watchedKeyComboStates[e]||(this._watchedKeyComboStates[e]=new n(e));const t=this._watchedKeyComboStates[e];return t.updateState(this._activeKeys),t.isPressed}bindEnvironment(){this.unbindEnvironment();const e=this._onActiveBinder((()=>{this._isActive=!0})),t=this._onInactiveBinder((()=>{this._isActive=!1})),s=this._onKeyPressedBinder((e=>{this._handleKeyPress(e)})),i=this._onKeyReleasedBinder((e=>{this._handleKeyRelease(e)}));this._unbinder=()=>{e?.(),t?.(),s?.(),i?.()}}unbindEnvironment(){this._unbinder?.()}_handleKeyPress(e){(async()=>{if(!this._isActive)return;let t=e.key.toLowerCase();const s=this._keyRemap[t];s&&(t=s);const i=this._handlerStates[t];if(i)for(const t of i)t.executePressed(e);this._activeKeySet.has(t)||(this._activeKeySet.add(t),this._activeKeys.push(t)),await this._updateKeyComboStates();for(const t of this._keyComboStatesArray)t.executePressed(e)})().catch((e=>{console.error(e)}))}_handleKeyRelease(e){(async()=>{const t=e.key.toLowerCase(),s=this._handlerStates[t];if(s)for(const t of s)t.executeReleased(e);if(this._activeKeySet.has(t)){this._activeKeySet.delete(t);for(let e=0;e<this._activeKeys.length;e+=1)if(this._activeKeys[e]===t){this._activeKeys.splice(e,1),e-=1;break}}this._tryReleaseSelfReleasingKeys(),await this._updateKeyComboStates();for(const t of this._keyComboStatesArray)t.executeReleased(e)})().catch((e=>{console.error(e)}))}async _updateKeyComboStates(){if(this._isUpdatingKeyComboState)return await r();this._isUpdatingKeyComboState=!0,await r();for(const e of this._keyComboStatesArray)e.updateState(this._activeKeys);this._isUpdatingKeyComboState=!1}_tryReleaseSelfReleasingKeys(){for(const e of this._activeKeys){let t=!1;for(const s of this._selfReleasingKeys)if(e===s){t=!0;break}if(!t)return}for(const e of this._activeKeys)this._handleKeyRelease({key:e})}constructor(e={}){this._isActive=!0,this._isUpdatingKeyComboState=!1,this._onActiveBinder=e.onActive??a,this._onInactiveBinder=e.onInactive??h,this._onKeyPressedBinder=e.onKeyPressed??c,this._onKeyReleasedBinder=e.onKeyReleased??y,this._selfReleasingKeys=e.selfReleasingKeys??[],this._keyRemap=e.keyRemap??{},this._handlerStates={},this._keyComboStates={},this._keyComboStatesArray=[],this._activeKeys=[],this._activeKeySet=new Set,this._watchedKeyComboStates={},this.bindEnvironment()}}let l,_;const u=()=>(_||(_=new d(l)),_),m=e=>{l=e},K=(...e)=>u().bindKey(...e),b=(...e)=>u().unbindKey(...e),f=(...e)=>u().bindKeyCombo(...e),C=(...e)=>u().unbindKeyCombo(...e),p=(...e)=>u().checkKey(...e),v=(...e)=>u().checkKeyCombo(...e),S=n.normalizeKeyCombo,k=n.stringifyKeyCombo,P=n.parseKeyCombo;window.keystrokes=t}(); | ||
!function(){function e(e,t,s,i){Object.defineProperty(e,t,{get:s,set:i,enumerable:!0,configurable:!0})}var t={};function s(e,t,s){return t in e?Object.defineProperty(e,t,{value:s,enumerable:!0,configurable:!0,writable:!0}):e[t]=s,e}e(t,"setGlobalKeystrokes",(function(){return u})),e(t,"getGlobalKeystrokes",(function(){return m})),e(t,"setGlobalKeystrokesOptions",(function(){return K})),e(t,"bindKey",(function(){return b})),e(t,"unbindKey",(function(){return f})),e(t,"bindKeyCombo",(function(){return C})),e(t,"unbindKeyCombo",(function(){return p})),e(t,"checkKey",(function(){return v})),e(t,"checkKeyCombo",(function(){return k})),e(t,"normalizeKeyCombo",(function(){return S})),e(t,"stringifyKeyCombo",(function(){return P})),e(t,"parseKeyCombo",(function(){return g})),e(t,"createTestKeystrokes",(function(){return w})),e(t,"Keystrokes",(function(){return d}));class i{get isEmpty(){return!this._onPressed&&!this._onPressedWithRepeat&&!this._onReleased}isOwnHandler(e){return this._identity===e}executePressed(e){this._isPressed||this._onPressed?.(e),this._isPressed=!0,this._onPressedWithRepeat?.(e)}executeReleased(e){this._isPressed&&this._onReleased?.(e),this._isPressed=!1}constructor(e){this._isPressed=!1,this._identity=e,"function"==typeof e?this._onPressedWithRepeat=e:(this._onPressed=e.onPressed,this._onPressedWithRepeat=e.onPressedWithRepeat,this._onReleased=e.onReleased)}}class n{static parseKeyCombo(e){if(n._parseCache[e])return n._parseCache[e];const t=e.toLowerCase();let s="",i=[],o=[i],r=[o];const a=[r];let h=!1;for(let n=0;n<e.length;n+=1)"\\"===t[n]?h=!0:"+"!==t[n]&&">"!==t[n]&&","!==t[n]||h?t[n].match(/[^\s]/)&&(s&&(","===s?(i=[],o=[i],r=[o],a.push(r)):">"===s?(i=[],o=[i],r.push(o)):"+"===s&&(i=[],o.push(i)),s=""),h=!1,i.push(t[n])):s=t[n];const c=a.map((e=>e.map((e=>e.map((e=>e.join("")))))));return n._parseCache[e]=c,c}static stringifyKeyCombo(e){return e.map((e=>e.map((e=>e.map((e=>"+"===e?"\\+":">"===e?"\\>":","===e?"\\,":e)).join("+"))).join(">"))).join(",")}static normalizeKeyCombo(e){if(n._normalizationCache[e])return n._normalizationCache[e];const t=this.stringifyKeyCombo(this.parseKeyCombo(e));return n._normalizationCache[e]=t,t}get isPressed(){return!!this._isPressedWithFinalKey}isOwnHandler(e){return this._handlerState.isOwnHandler(e)}executePressed(e){this._isPressedWithFinalKey===e.key&&this._handlerState.executePressed(this._wrapEvent(e))}executeReleased(e){this._isPressedWithFinalKey===e.key&&(this._isPressedWithFinalKey="",this._handlerState.executeReleased(this._wrapEvent(e)))}updateState(e){const t=this._parsedKeyCombo[this._sequenceIndex];if(0===e.length)return;let s=0;for(const i of t){let t=s;for(const n of i){let i=!1;for(let o=s;o<e.length;o+=1){if(n===e[o]){o>t&&(t=o),i=!0;break}}if(!i)return void(this._handlerState.isEmpty&&(this._isPressedWithFinalKey=""))}s=t}for(const s of e){let e=!1;for(const i of t)for(const t of i)if(s===t){e=!0;break}if(!e)return void(this._sequenceIndex=0)}this._sequenceIndex<this._parsedKeyCombo.length-1?this._sequenceIndex+=1:(this._sequenceIndex=0,this._isPressedWithFinalKey=e[e.length-1])}_wrapEvent(e){return{keyCombo:this._normalizedKeyCombo,originalEvent:e.originalEvent}}constructor(e,t={}){this._normalizedKeyCombo=n.normalizeKeyCombo(e),this._parsedKeyCombo=n.parseKeyCombo(e),this._handlerState=new i(t),this._isPressedWithFinalKey="",this._sequenceIndex=0}}s(n,"_parseCache",{}),s(n,"_normalizationCache",{});const o="function"==typeof requestAnimationFrame?e=>requestAnimationFrame(e):e=>setTimeout(e,0),r=()=>new Promise((e=>o(e))),a=e=>{try{const t=()=>e();return addEventListener("focus",t),()=>{removeEventListener("focus",t)}}catch{}},h=e=>{try{const t=()=>e();return addEventListener("blur",t),()=>{removeEventListener("blur",t)}}catch{}},c=e=>{try{const t=t=>e({key:t.key,originalEvent:t});return document.addEventListener("keydown",t),()=>{document.removeEventListener("keydown",t)}}catch{}},y=e=>{try{const t=t=>e({key:t.key,originalEvent:t});return document.addEventListener("keyup",t),()=>{document.removeEventListener("keyup",t)}}catch{}};class d{get pressedKeys(){return this._activeKeys.slice(0)}bindKey(e,t){e=e.toLowerCase();const s=new i(t);this._handlerStates[e]??=[],this._handlerStates[e].push(s)}unbindKey(e,t){e=e.toLowerCase();const s=this._handlerStates[e];if(s)if(t)for(let e=0;e<s.length;e+=1)s[e].isOwnHandler(t)&&(s.splice(e,1),e-=1);else s.length=0}bindKeyCombo(e,t){e=n.normalizeKeyCombo(e);const s=new n(e,t);this._keyComboStates[e]??=[],this._keyComboStates[e].push(s),this._keyComboStatesArray.push(s)}unbindKeyCombo(e,t){e=n.normalizeKeyCombo(e);const s=this._keyComboStates[e];if(s)if(t){for(let e=0;e<s.length;e+=1)if(s[e].isOwnHandler(t)){for(let t=0;t<this._keyComboStatesArray.length;t+=1)this._keyComboStatesArray[t]===s[e]&&(this._keyComboStatesArray.splice(t,1),t-=1);s.splice(e,1),e-=1}}else s.length=0}checkKey(e){return this._activeKeySet.has(e.toLowerCase())}checkKeyCombo(e){e=n.normalizeKeyCombo(e),this._watchedKeyComboStates[e]||(this._watchedKeyComboStates[e]=new n(e));const t=this._watchedKeyComboStates[e];return t.updateState(this._activeKeys),t.isPressed}bindEnvironment(){this.unbindEnvironment();const e=this._onActiveBinder((()=>{this._isActive=!0})),t=this._onInactiveBinder((()=>{this._isActive=!1})),s=this._onKeyPressedBinder((e=>{this._handleKeyPress(e)})),i=this._onKeyReleasedBinder((e=>{this._handleKeyRelease(e)}));this._unbinder=()=>{e?.(),t?.(),s?.(),i?.()}}unbindEnvironment(){this._unbinder?.()}_handleKeyPress(e){(async()=>{if(!this._isActive)return;let t=e.key.toLowerCase();const s=this._keyRemap[t];s&&(t=s);const i=this._handlerStates[t];if(i)for(const t of i)t.executePressed(e);this._activeKeySet.has(t)||(this._activeKeySet.add(t),this._activeKeys.push(t)),await this._updateKeyComboStates();for(const t of this._keyComboStatesArray)t.executePressed(e)})().catch((e=>{console.error(e)}))}_handleKeyRelease(e){(async()=>{const t=e.key.toLowerCase(),s=this._handlerStates[t];if(s)for(const t of s)t.executeReleased(e);if(this._activeKeySet.has(t)){this._activeKeySet.delete(t);for(let e=0;e<this._activeKeys.length;e+=1)if(this._activeKeys[e]===t){this._activeKeys.splice(e,1),e-=1;break}}this._tryReleaseSelfReleasingKeys(),await this._updateKeyComboStates();for(const t of this._keyComboStatesArray)t.executeReleased(e)})().catch((e=>{console.error(e)}))}async _updateKeyComboStates(){if(this._isUpdatingKeyComboState)return await r();this._isUpdatingKeyComboState=!0,await r();for(const e of this._keyComboStatesArray)e.updateState(this._activeKeys);this._isUpdatingKeyComboState=!1}_tryReleaseSelfReleasingKeys(){for(const e of this._activeKeys){let t=!1;for(const s of this._selfReleasingKeys)if(e===s){t=!0;break}if(!t)return}for(const e of this._activeKeys)this._handleKeyRelease({key:e})}constructor(e={}){this._isActive=!0,this._isUpdatingKeyComboState=!1,this._onActiveBinder=e.onActive??a,this._onInactiveBinder=e.onInactive??h,this._onKeyPressedBinder=e.onKeyPressed??c,this._onKeyReleasedBinder=e.onKeyReleased??y,this._selfReleasingKeys=e.selfReleasingKeys??[],this._keyRemap=e.keyRemap??{},this._handlerStates={},this._keyComboStates={},this._keyComboStatesArray=[],this._activeKeys=[],this._activeKeySet=new Set,this._watchedKeyComboStates={},this.bindEnvironment()}}let l,_;const u=e=>{_=e??new d(l)},m=()=>(_||u(),_),K=e=>{l=e},b=(...e)=>m().bindKey(...e),f=(...e)=>m().unbindKey(...e),C=(...e)=>m().bindKeyCombo(...e),p=(...e)=>m().unbindKeyCombo(...e),v=(...e)=>m().checkKey(...e),k=(...e)=>m().checkKeyCombo(...e),S=n.normalizeKeyCombo,P=n.stringifyKeyCombo,g=n.parseKeyCombo,w=()=>{let e,t,s,i;return Object.assign(new d({onActive(t){e=t},onInactive(e){t=e},onKeyPressed(e){s=e},onKeyReleased(e){i=e}}),{activate:e,deactivate:t,press:s,release:i})};window.keystrokes=t}(); | ||
//# sourceMappingURL=keystrokes.js.map |
{ | ||
"name": "@rwh/keystrokes", | ||
"version": "1.0.0-beta.4", | ||
"description": "Keystrokes as an easy to use library for binding functions to keys and key combos. It can be used with any TypeScript or JavaScript project, even in non-browser environments.", | ||
"version": "1.0.0-beta.5", | ||
"description": "Keystrokes is an easy to use library for binding functions to keys and key combos. It can be used with any TypeScript or JavaScript project, even in non-browser environments.", | ||
"source": "./src/index.ts", | ||
@@ -52,3 +52,2 @@ "main": "./dist/index.js", | ||
"@parcel/transformer-typescript-types": "^2.8.3", | ||
"@swc/helpers": "^0.4.14", | ||
"@types/mocha": "^10.0.1", | ||
@@ -72,3 +71,3 @@ "@types/node": "^18.11.11", | ||
"lint": "eslint ./src", | ||
"test": "mocha -r ts-node/register ./src/test/**/*.spec.ts", | ||
"test": "mocha -r ts-node/register ./src/tests/**/*.spec.ts", | ||
"build": "rm -rf dist/ && parcel build", | ||
@@ -75,0 +74,0 @@ "dev": "parcel build --watch" |
341
readme.md
@@ -1,4 +0,1 @@ | ||
<p align="center"> | ||
<img src="https://raw.githubusercontent.com/RobertWHurst/Keystrokes/master/logo.png"> | ||
</p> | ||
@@ -23,335 +20,9 @@ <p align="center"> | ||
__Take note, Keystrokes is in early alpha. If you encounter a bug please [report it][bug-report].__ | ||
# @rwh/keystrokes | ||
Keystrokes as a quick and easy to use library for binding functions to keys | ||
and key combos. It can also be used to check if keys or key combos are pressed | ||
ad-hoc. It supports any TypeScript or JavaScript project, and can be used in | ||
non browser environments too. | ||
This is the main keystrokes package. It contains the heart of keystrokes and | ||
can be used in any JavaScript or TypeScript project For more information about | ||
how to use the library see the [Readme][#readme] at the root of this | ||
repository. | ||
```js | ||
import { bindKey, bindKeyCombo } from '@rwh/keystrokes' | ||
bindKey('a', () => | ||
console.log('You\'re pressing "a"')) | ||
bindKeyCombo('ctrl > y, r', () => | ||
console.log('You pressed "ctrl" then "y", released both, and are pressing "r"')) | ||
``` | ||
# Installation | ||
Keystrokes is available on [npm][npm]. This works great when using a build | ||
system like [Parcel][parcel], [Vite][vite], [Turbopack][turbopack], or | ||
[webpack][webpack]. | ||
```sh | ||
npm install @rwh/keystrokes | ||
``` | ||
```js | ||
import { bindKey } from '@rwh/keystrokes' | ||
bindKey('a', () => console.log('you pressed a')) | ||
``` | ||
If node modules aren't an option for you, you can use an npm CDN such as | ||
[jsDelivr][jsdelivr] or [UNPKG][unpkg]. | ||
```html | ||
<script src="https://unpkg.com/browse/@rwh/keystrokes@latest/keystrokes.js"> | ||
<script> | ||
keystrokes.bindKey('a', () => console.log('you pressed a')) | ||
</script> | ||
``` | ||
# Binding Keys and Key Combos | ||
As in the example at the top of the page, Keystrokes exports a bindKey and | ||
bindKeyCombo function. These function will bind a handler function, or handler | ||
object to a key or key combo. | ||
The key names used in your bindings is determined by the environment you are | ||
using Keystrokes in. They are always case insensitive. The default behavior, | ||
intended for browser environments, is to use the value of the key property from | ||
keyboard events. You get get a list of valid [key names here][key-names]. | ||
```js | ||
import { bindKey, bindKeyCombo } from '@rwh/keystrokes' | ||
bindKey('a', () => | ||
console.log('You\'re pressing "a"')) | ||
bindKeyCombo('ctrl > y, r', () => | ||
console.log('You pressed "ctrl" then "y", released both, and are pressing "r"')) | ||
bindKey('a', { | ||
onPressed: () => console.log('You pressed "a"'), | ||
onPressedWithRepeat: () => console.log('You\'re pressing "a"'), | ||
onReleased: () => console.log('You released "a"'), | ||
}) | ||
bindKeyCombo('ctrl > y, r', { | ||
onPressed: () => console.log('You pressed "ctrl" then "y", released both, then pressed "r"'), | ||
onPressedWithRepeat: () => console.log('You pressed "ctrl" then "y", released both, and are pressing "r"'), | ||
onReleased: () => console.log('You released "r"'), | ||
}) | ||
``` | ||
Note that when you pass a function handler instead of an object handler, it is | ||
short hand for passing an object handler with a `onPressedWithRepeat` method. | ||
```js | ||
const handler = () => console.log('You pressed "ctrl" then "y", released both, and are pressing "r"') | ||
bindKeyCombo('ctrl > y, r', handler) | ||
// ...is shorthand for... | ||
bindKeyCombo('ctrl > y, r', { onPressedWithRepeat: handler }) | ||
``` | ||
# Unbinding Keys and Key Combos | ||
In more complex applications it's likely you'll need to unbind handlers, such | ||
as when you change your view. In order to do so you just need to keep a | ||
reference to the handler so you can unbind it. | ||
```js | ||
import { bindKeyCombo, unbindKeyCombo } from '@rwh/keystrokes' | ||
const handler = () => ... | ||
// bind the combo to the handler | ||
bindKeyCombo('ctrl > y, r', handler) | ||
// ...and some time later... | ||
// unbind the handler | ||
unbindKeyCombo('ctrl > y, r', handler) | ||
``` | ||
You can also wipe out all bound handlers on a combo by excluding a handler | ||
reference. | ||
```js | ||
// unbind all handlers for the combo 'ctrl > y, r' | ||
unbindKeyCombo('ctrl > y, r') | ||
``` | ||
# Checking Keys and Key Combos | ||
If you have a situation where you want to check if a key or key combo is | ||
pressed at anytime you can do so with `checkKey` and/or `checkKeyCombo` | ||
```js | ||
import { checkKey, checkKeyCombo } from '@rwh/keystrokes' | ||
// keyIsPressed will be true if a is pressed, and false otherwise | ||
const keyIsPressed = checkKey('a') | ||
// keyComboIsPressed will be true if ctrl then y was pressed and r is pressed. | ||
// It will be false otherwise. | ||
const keyComboIsPressed = checkKeyCombo('ctrl > y, r') | ||
``` | ||
# Working with Popular frontend frameworks | ||
## React | ||
I recommend creating a react hook you can use to handle the activation | ||
of key combos. Below I've provided a basic hook that should do the trick | ||
for most React users. | ||
```js | ||
import { useEffect, useState } from 'react' | ||
import { bindKeyCombo, unbindKeyCombo } from '@rwh/keystrokes' | ||
export const useKeyCombo = (keyCombo) => { | ||
const [isPressed, setIsPressed] = useState(false) | ||
useEffect(() => { | ||
const handler = { | ||
onPressed: () => setIsPressed(true), | ||
onReleased: () => setIsPressed(false), | ||
} | ||
bindKeyCombo(keyCombo, handler) | ||
return () => { unbindKeyCombo(keyCombo, handler) } | ||
}, [keyCombo]) | ||
return isPressed | ||
} | ||
``` | ||
## Vue | ||
I recommend creating a vue composable you can use to handle the activation | ||
of key combos. Below I've provided a basic composable that should do the trick | ||
for most Vue users. | ||
```js | ||
import { ref, unref, watchEffect } from 'vue' | ||
import { bindKeyCombo, unbindKeyCombo } from '@rwh/keystrokes' | ||
export const useKeyCombo = (keyCombo) => { | ||
const isPressed = ref(false) | ||
watchEffect((cleanUp) => { | ||
const handler = { | ||
onPressed: () => { isPressed.value = true }, | ||
onReleased: () => { isPressed.value = false }, | ||
} | ||
const keyComboStr = unref(keyCombo) | ||
bindKeyCombo(keyComboStr, handler) | ||
cleanUp(() => { unbindKeyCombo(keyComboStr, handler) }) | ||
}) | ||
return isPressed | ||
} | ||
``` | ||
# Creating Instances | ||
If you'd rather create your own instances of Keystrokes, rather than using the | ||
global instance, you can do so by constructing the Keystrokes class. Keystrokes | ||
class instance has all of the functions we've looked at above as methods. | ||
```js | ||
import { Keystrokes } from '@rwh/keystrokes' | ||
const keystrokes = new Keystrokes() | ||
// All of the functions we've reviewed above are methods on the instance | ||
keystrokes.bindKey(...) | ||
keystrokes.bindKeyCombo(...) | ||
keystrokes.unbindKey(...) | ||
keystrokes.unbindKeyCombo(...) | ||
keystrokes.checkKey(...) | ||
keystrokes.checkKeyCombo(...) | ||
``` | ||
If you want to go this route you won't have to work about overhead from the | ||
global instance as it is only created if you use the exported functions | ||
associated with it. | ||
# Configuration Options | ||
Keystrokes has a few configuration options that you can configure by passing | ||
them to the `Keystrokes` constructor, or by calling the | ||
`setGlobalKeystrokesOptions` before using any of the functions exported by the | ||
package associated with the global instance. | ||
## Available Options | ||
selfReleasingKeys?: string[] | ||
keyRemap?: Record<string, string> | ||
Option | Description | ||
------------------|------------------------------------------ | ||
selfReleasingKeys | Some environments may not properly fire release events for all keys. Adding them to this array will ensure they are released automatically when no other keys are pressed. | ||
keyRemap | An object of key value pairs with the key being the key to rename, and the value being the new name. | ||
onActive | A binder to track viewport focus. See [Non Browser Environments](#non-browser-environments) for details. | ||
onInactive | A binder to track viewport blur. See [Non Browser Environments](#non-browser-environments) for details. | ||
onKeyPressed | A binder to track when keys are pressed. See [Non Browser Environments](#non-browser-environments) for details. | ||
onKeyReleased | A binder to track when keys are released. See [Non Browser Environments](#non-browser-environments) for details. | ||
Here is an example where we are configuring the global instance. | ||
```js | ||
import { bindKey, setGlobalKeystrokesOptions } from '@rwh/keystrokes' | ||
// Must be called before binding or checking keys or key combos | ||
setGlobalKeystrokesOptions({ | ||
keyRemap: { ' ': 'spacebar' } | ||
}) | ||
bindKey(...) | ||
``` | ||
And here is an example where we are passing the options to the `Keystrokes` | ||
constructor. These options will only effect the constructed instance. | ||
```js | ||
import { Keystrokes } from '@rwh/keystrokes' | ||
const keystrokes = new Keystrokes({ | ||
keyRemap: { ' ': 'spacebar' } | ||
}) | ||
keystrokes.bindKey(...) | ||
``` | ||
# Non Browser Environments | ||
Should you wish to use Keystrokes in a non browser environment, you can do | ||
so with the use of the `onActive`, `onInactive`, `onKeyPressed`, and | ||
`onKeyReleased` binder options. Binders are functions that are called by | ||
keystrokes when constructed. The binder is passed a handler function. Your | ||
binder is expected to call this handler whenever the event associated with the | ||
binder occurs. Binders may also return a function which will be called when the | ||
library is unbound from the environment. | ||
By default Keystrokes will internally setup binders that work with browser | ||
environments if you do not provide your own. This results in the same behavior | ||
as the following code. | ||
```js | ||
import { Keystrokes } from '@rwh/keystrokes' | ||
const keystrokes = new Keystrokes({ | ||
onActive: handler => { | ||
const listener = () => handler() | ||
window.addEventListener('focus', listener) | ||
return () => { | ||
window.removeEventListener('focus', listener) | ||
} | ||
}, | ||
onInactive: handler => { | ||
const listener = () => handler() | ||
window.addEventListener('blur', listener) | ||
return () => { | ||
window.removeEventListener('blur', listener) | ||
} | ||
}, | ||
onKeyPressed: handler => { | ||
const listener = event => handler({ key: event.key, originalEvent: event }) | ||
window.addEventListener('keydown', listener) | ||
return () => { | ||
window.removeEventListener('keydown', listener) | ||
} | ||
}, | ||
onKeyReleased: handler => { | ||
const listener = event => handler({ key: event.key, originalEvent: event }) | ||
window.addEventListener('keyup', listener) | ||
return () => { | ||
window.removeEventListener('keyup', listener) | ||
} | ||
} | ||
}) | ||
keystrokes.bindKey(...) | ||
``` | ||
# Help Welcome | ||
If you want to support this project by throwing be some coffee money It's | ||
greatly appreciated. | ||
[![sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/RobertWHurst) | ||
If your interested in providing feedback or would like to contribute please feel | ||
free to do so. I recommend first [opening an issue][feature-request] expressing | ||
your feedback or intent to contribute a change, from there we can consider your | ||
feedback or guide your contribution efforts. Any and all help is greatly | ||
appreciated since this is an open source effort after all. | ||
Thank you! | ||
[npm]: https://www.npmjs.com | ||
[parcel]: https://parceljs.org | ||
[vite]: https://vitejs.dev | ||
[turbopack]: https://turbo.build/pack | ||
[webpack]: https://webpack.js.org | ||
[jsdelivr]: https://www.jsdelivr.com/package/npm/@rwh/keystrokes | ||
[unpkg]: https://unpkg.com/browse/@rwh/keystrokes@latest/ | ||
[key-names]: https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values | ||
[bug-report]: https://github.com/RobertWHurst/Keystrokes/issues/new?template=bug_report.md | ||
[feature-request]: https://github.com/RobertWHurst/Keystrokes/issues/new?template=feature_request.md | ||
[#readme]: ../ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
18
952
179868
11
28