@peter.naydenov/shortcuts 2.0.0 → 2.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/Changelog.md CHANGED
@@ -2,7 +2,25 @@
2
2
 
3
3
 
4
4
 
5
- ### 2.0.0 ( 2023-10-14 )
5
+ ### x.x.x ( 2024-02-?? )( in progress )
6
+ - [ ] New: Load different context during promise execution. Return back when promise resolved. Use it for creation of loading screens and disable shortcuts during loading, then return back to normal; (still in progress);
7
+
8
+
9
+
10
+ ### 2.2.0 ( 2024-02-10 )
11
+ - [x] Folder 'dist' was added to the project. Includes commonjs, umd and esm versions of the library;
12
+ - [x] Package.json: "exports" section was added. Allows you to use package as commonjs or es6 module without additional configuration;
13
+ - [x] Rollup was added to the project. Used to build the library versions;
14
+
15
+
16
+
17
+ ### 2.1.0 ( 2023-10-17 )
18
+ - [x] Method `setDependencies` to add more external objects available in all action functions;
19
+ - [x] Method `getDependencies` to look at existing `dependencies` list;
20
+
21
+
22
+
23
+ ### 2.0.0 ( 2023-10-16 )
6
24
  - [x] HTML attribute `data-quick-click` is available to speed up single click response;
7
25
  - [x] Documentation on methods `pause` and `resume`;
8
26
  - [x] Method `listShortcuts` to get names of all shortcuts defined;
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  ![version](https://img.shields.io/github/package-json/v/peterNaydenov/shortcuts)
4
4
  ![license](https://img.shields.io/github/license/peterNaydenov/shortcuts)
5
+ ![GitHub issues](https://img.shields.io/github/issues/peterNaydenov/shortcuts)
6
+ ![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40peter.naydenov%2Fshortcuts)
5
7
 
6
8
 
7
9
 
@@ -281,16 +283,18 @@ function myMouseHandler ({
281
283
  Description of the methods of shortcut instance:
282
284
 
283
285
  ```js
284
- load : 'Load and extend a shortcut definition.'
285
- , unload : 'Remove a shortcut context with all its shortcuts.'
286
- , changeContext : 'Switch to existing shortcut context.'
287
- , emit : 'Trigger a shortcut or custom event programmatically.'
288
- , pause : 'Stop listening for shortcuts.'
289
- , resume : 'Resume listening for shortcuts.'
290
- , listContexts : 'Return list of available contexts.'
291
- , getContext : 'Return a name of current context or null if there is no context selected'
292
- , getNote : `Return a name of current note or null if note isn't set`
293
- , setNote : 'Set a note to current context.'
286
+ load : 'Load and extend a shortcut definition.'
287
+ , unload : 'Remove a shortcut context with all its shortcuts.'
288
+ , changeContext : 'Switch to existing shortcut context.'
289
+ , emit : 'Trigger a shortcut or custom event programmatically.'
290
+ , pause : 'Stop listening for shortcuts.'
291
+ , resume : 'Resume listening for shortcuts.'
292
+ , listContexts : 'Return list of available contexts.'
293
+ , getContext : 'Return a name of current context or null if there is no context selected'
294
+ , getNote : `Return a name of current note or null if note isn't set`
295
+ , setNote : 'Set a note to current context.'
296
+ , setDependencies : 'Set dependencies that will be available in action functions.'
297
+ , getDependencies : 'Return dependencies object.'
294
298
  ```
295
299
 
296
300
  ### How to 'pause' and 'resume'?
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "Using fixtures to represent data",
3
+ "email": "hello@cypress.io",
4
+ "body": "Fixtures are a great way to mock data for responses to routes"
5
+ }
@@ -0,0 +1,25 @@
1
+ // ***********************************************
2
+ // This example commands.js shows you how to
3
+ // create various custom commands and overwrite
4
+ // existing commands.
5
+ //
6
+ // For more comprehensive examples of custom
7
+ // commands please read more here:
8
+ // https://on.cypress.io/custom-commands
9
+ // ***********************************************
10
+ //
11
+ //
12
+ // -- This is a parent command --
13
+ // Cypress.Commands.add('login', (email, password) => { ... })
14
+ //
15
+ //
16
+ // -- This is a child command --
17
+ // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
18
+ //
19
+ //
20
+ // -- This is a dual command --
21
+ // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
22
+ //
23
+ //
24
+ // -- This will overwrite an existing command --
25
+ // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
7
+ <title>Components App</title>
8
+ <!-- Used by Next.js to inject CSS. -->
9
+ <div id="__next_css__DO_NOT_USE__"></div>
10
+ </head>
11
+ <body>
12
+ <div data-cy-root></div>
13
+ </body>
14
+ </html>
@@ -0,0 +1,27 @@
1
+ // ***********************************************************
2
+ // This example support/component.js is processed and
3
+ // loaded automatically before your test files.
4
+ //
5
+ // This is a great place to put global configuration and
6
+ // behavior that modifies Cypress.
7
+ //
8
+ // You can change the location of this file or turn off
9
+ // automatically serving support files with the
10
+ // 'supportFile' configuration option.
11
+ //
12
+ // You can read more here:
13
+ // https://on.cypress.io/configuration
14
+ // ***********************************************************
15
+
16
+ // Import commands.js using ES2015 syntax:
17
+ import './commands'
18
+
19
+ // Alternatively you can use CommonJS syntax:
20
+ // require('./commands')
21
+
22
+ import { mount } from 'cypress/react18'
23
+
24
+ Cypress.Commands.add('mount', mount)
25
+
26
+ // Example use:
27
+ // cy.mount(<MyComponent />)
@@ -0,0 +1,20 @@
1
+ // ***********************************************************
2
+ // This example support/e2e.js is processed and
3
+ // loaded automatically before your test files.
4
+ //
5
+ // This is a great place to put global configuration and
6
+ // behavior that modifies Cypress.
7
+ //
8
+ // You can change the location of this file or turn off
9
+ // automatically serving support files with the
10
+ // 'supportFile' configuration option.
11
+ //
12
+ // You can read more here:
13
+ // https://on.cypress.io/configuration
14
+ // ***********************************************************
15
+
16
+ // Import commands.js using ES2015 syntax:
17
+ import './commands'
18
+
19
+ // Alternatively you can use CommonJS syntax:
20
+ // require('./commands')
@@ -0,0 +1 @@
1
+ "use strict";var e=require("@peter.naydenov/notice");var t={_findTarget:function(e,t){const{listenOptions:{clickTarget:n}}=t;return function e(t){const o=t;return o===document||o===document.body?null:o.dataset[n]||"A"===o.nodeName?o:e(o.parentNode)}},_listen:function(e,t){return function(){const{ev:n,inAPI:{_findTarget:o,_specialChars:r,_readKeyEvent:i,_readMouseEvent:s}}=e,{exposeShortcut:u,currentContext:c,streamKeys:a,listenOptions:l}=t,{mouseWait:m,keyWait:d,clickTarget:p,listenFor:h}=l;let y=[],k=null,f=null,g=null,x=null,T=null,C=!0,F=!1,v=0;const I=()=>C=!1,E=()=>C=!0,S=()=>F=!0,A=()=>!1===C;function O(){let t=y.map((e=>[e.join("+")]));if(!C){let e=t.at(-1);n.emit(e,{wait:I,end:E,ignore:S,isWaiting:A,note:c.note,context:c.name}),F&&(t=t.slice(0,-1),F=!1)}const o={wait:I,end:E,ignore:S,isWaiting:A,note:c.note,context:c.name,dependencies:e.extra};C&&(n.emit(t.join(","),o),u&&u({shortcut:t.join(","),context:c.name,note:c.note,dependencies:e.extra}),y=[],g=null)}function K(){const t=s(f,v),o={target:k,targetProps:k?k.getBoundingClientRect():null,x:f.clientX,y:f.clientY,context:c.name,note:c.note,event:f,dependencies:e.extra};n.emit(t.join("+"),o),u&&u({shortcut:t.join("+"),context:c.name,note:c.note,dependencies:e.extra}),x=null,T=null,k=null,f=null,v=0}h.includes("mouse")&&(window.addEventListener("contextmenu",(e=>{let t=l.maxClicks;return e.preventDefault(),clearTimeout(x),T?(clearTimeout(T),void(T=setTimeout((()=>T=null),m))):(k=o(e.target),k&&k.dataset.hasOwnProperty("quickClick")&&(t=1),k&&"A"===k.tagName&&(t=1),f=e,v++,v>=t?(K(),void(t>1&&(T=setTimeout((()=>T=null),m)))):void(x=setTimeout(K,m)))})),document.addEventListener("click",(e=>{let t=l.maxClicks;return e.preventDefault(),clearTimeout(x),T?(clearTimeout(T),void(T=setTimeout((()=>T=null),m))):(k=o(e.target),k&&k.dataset.hasOwnProperty("quickClick")&&(t=1),k&&"A"===k.tagName&&(t=1),f=e,v++,v>=t?(K(),void(t>1&&(T=setTimeout((()=>T=null),m)))):void(x=setTimeout(K,m)))}))),h.includes("keyboard")&&(document.addEventListener("keydown",(t=>{if(clearTimeout(g),r.hasOwnProperty(t.code))return y.push(i(t,r)),a&&a({key:t.key,context:c.name,note:c.note,dependencies:e.extra}),l.keyIgnore?(clearTimeout(l.keyIgnore),void(l.keyIgnore=setTimeout((()=>l.keyIgnore=null),d))):C&&y.length===l.maxSequence?(O(),void(l.keyIgnore=setTimeout((()=>l.keyIgnore=null),d))):void(C?g=setTimeout(O,d):O())})),document.addEventListener("keypress",(t=>{if(!r.hasOwnProperty(t.code)){if(clearTimeout(g),a&&a({key:t.key,context:c.name,note:c.note,dependencies:e.extra}),l.keyIgnore)return clearTimeout(l.keyIgnore),void(l.keyIgnore=setTimeout((()=>l.keyIgnore=null),d));if(y.push(i(t,r)),C&&y.length===l.maxSequence)return O(),void(l.keyIgnore=setTimeout((()=>l.keyIgnore=null),d));C?g=setTimeout(O,d):O()}})))}},_readKeyEvent:function(){return function(e,t){let{shiftKey:n,altKey:o,ctrlKey:r}=e,i=e.code.replace("Key","").replace("Digit",""),s=[];return r&&s.push("CTRL"),n&&s.push("SHIFT"),o&&s.push("ALT"),t.hasOwnProperty(i)?s.push(t[i].toUpperCase()):["ControlLeft","ControlRight","ShiftLeft","ShiftRight","AltLeft","AltRight","Meta"].includes(i)||s.push(i.toUpperCase()),s.sort()}},_readMouseEvent:function(){return function(e,t){let{shiftKey:n,altKey:o,ctrlKey:r,key:i,button:s}=e,u=`MOUSE-CLICK-${["LEFT","MIDDLE","RIGHT"][s]}-${t}`,c=[];return c.push(u),r&&c.push("CTRL"),n&&c.push("SHIFT"),o&&c.push("ALT"),c.sort()}},_readShortcut:function(){return function(e){return e.split(",").map((e=>e.trim())).map((e=>e.split("+").map((e=>e.toUpperCase())).sort().join("+"))).join(",")}},_specialChars:function(){return{ArrowLeft:"LEFT",ArrowUp:"UP",ArrowRight:"RIGHT",ArrowDown:"DOWN",Enter:"ENTER",NumpadEnter:"ENTER",Escape:"ESC",Backspace:"BACKSPACE",Space:"SPACE",Tab:"TAB",Backquote:"`",BracketLeft:"[",BracketRight:"]",Equal:"=",Slash:"/",Backslash:"\\",IntlBackslash:"`",F1:"F1",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",F10:"F10",F11:"F11",F12:"F12"}},changeContext:function(e,t){const{shortcuts:n,listenOptions:o,currentContext:r}=t,{ev:i}=e;return function(e=!1){const t=r.name;if(o.maxSequence=1,o.maxClicks=1,o.keyIgnore>=0&&(clearTimeout(o.keyIgnore),o.keyIgnore=null),!e)return i.reset(),void(r.name=null);t!==e&&(n[e]?(n[t]&&i.reset(),Object.entries(n[e]).forEach((([e,t])=>{if(e.includes("MOUSE-CLICK-")){let[,,,t]=e.split("-"),n=parseInt(t);o.maxClicks<n&&(o.maxClicks=n)}else{let t=e.split(",").length;o.maxSequence<t&&(o.maxSequence=t)}t.forEach((t=>i.on(e,t)))})),r.name=e):i.emit("shortcuts-error",`Context '${e}' does not exist`))}},listShortcuts:function(e,t){const n=t.shortcuts;return function(e=null){if(null!=e){let t=n[e];return null==t?null:Object.entries(t).map((([e,t])=>e))}return Object.keys(n).map((e=>{let t={};return t.context=e,t.shortcuts=Object.entries(n[e]).map((([e,t])=>e)),t}))}},load:function(e,t){const{shortcuts:n}=t,{inAPI:{_readShortcut:o},API:{changeContext:r,getContext:i}}=e;return function(e){const t=i(),s=Object.keys(e);let u=!1;s.forEach((r=>{const i=Object.entries(e[r]);r===t&&(u=!0),n[r]={},i.forEach((([e,t])=>{const i=o(e);t instanceof Function&&(t=[t]),n[r][i]=t}))})),u&&(r(),r(t))}},unload:function(e,t){const{currentContext:n,shortcuts:o}=t,{ev:r}=e;return function(e){n.name!==e?o[e]?delete o[e]:r.emit("shortcuts-error",`Context '${e}' does not exist`):r.emit("shortcuts-error",`Context '${e}' can't be removed during is current active context. Change the context first`)}}};function n(n={}){const o=e(),r={},i={},s={currentContext:{name:null,note:null},shortcuts:{},listenOptions:{mouseWait:n.mouseWait?n.mouseWait:320,maxClicks:1,keyWait:n.keyWait?n.keyWait:480,maxSequence:1,clickTarget:n.clickTarget?n.clickTarget:"click",listenFor:n.listenFor&&Array.isArray(n.listenFor)?n.listenFor:["mouse","keyboard"],keyIgnore:null},exposeShortcut:!(!n.onShortcut||"function"!=typeof n.onShortcut)&&n.onShortcut,streamKeys:!(!n.streamKeys||"function"!=typeof n.streamKeys)&&n.streamKeys},u={ev:o,inAPI:r,API:i,extra:{}};return i.getContext=()=>s.currentContext.name,i.getNote=()=>s.currentContext.note,i.setNote=(e=null)=>{"string"!=typeof e&&null!=e||(s.currentContext.note=e)},i.pause=(e="*")=>o.stop(r._readShortcut(e)),i.resume=(e="*")=>o.start(r._readShortcut(e)),i.emit=(e,...t)=>o.emit(r._readShortcut(e),u.extra,...t),i.listContexts=()=>Object.keys(shortcuts),i.setDependencies=e=>u.extra={...u.extra,...e},i.getDependencies=()=>u.extra,Object.entries(t).forEach((([e,t])=>{e.startsWith("_")?r[e]=t(u,s):i[e]=t(u,s)})),r._listen(),i}n.getDefaults=()=>({mouseWait:320,keyWait:480,clickTarget:"click",listenFor:["mouse","keyboard"],onShortcut:!1,streamKeys:!1}),module.exports=n;
@@ -0,0 +1 @@
1
+ import e from"@peter.naydenov/notice";var t={_findTarget:function(e,t){const{listenOptions:{clickTarget:n}}=t;return function e(t){const o=t;return o===document||o===document.body?null:o.dataset[n]||"A"===o.nodeName?o:e(o.parentNode)}},_listen:function(e,t){return function(){const{ev:n,inAPI:{_findTarget:o,_specialChars:r,_readKeyEvent:i,_readMouseEvent:s}}=e,{exposeShortcut:u,currentContext:c,streamKeys:a,listenOptions:l}=t,{mouseWait:m,keyWait:d,clickTarget:p,listenFor:h}=l;let y=[],k=null,f=null,g=null,x=null,T=null,C=!0,F=!1,v=0;const I=()=>C=!1,E=()=>C=!0,S=()=>F=!0,A=()=>!1===C;function O(){let t=y.map((e=>[e.join("+")]));if(!C){let e=t.at(-1);n.emit(e,{wait:I,end:E,ignore:S,isWaiting:A,note:c.note,context:c.name}),F&&(t=t.slice(0,-1),F=!1)}const o={wait:I,end:E,ignore:S,isWaiting:A,note:c.note,context:c.name,dependencies:e.extra};C&&(n.emit(t.join(","),o),u&&u({shortcut:t.join(","),context:c.name,note:c.note,dependencies:e.extra}),y=[],g=null)}function K(){const t=s(f,v),o={target:k,targetProps:k?k.getBoundingClientRect():null,x:f.clientX,y:f.clientY,context:c.name,note:c.note,event:f,dependencies:e.extra};n.emit(t.join("+"),o),u&&u({shortcut:t.join("+"),context:c.name,note:c.note,dependencies:e.extra}),x=null,T=null,k=null,f=null,v=0}h.includes("mouse")&&(window.addEventListener("contextmenu",(e=>{let t=l.maxClicks;return e.preventDefault(),clearTimeout(x),T?(clearTimeout(T),void(T=setTimeout((()=>T=null),m))):(k=o(e.target),k&&k.dataset.hasOwnProperty("quickClick")&&(t=1),k&&"A"===k.tagName&&(t=1),f=e,v++,v>=t?(K(),void(t>1&&(T=setTimeout((()=>T=null),m)))):void(x=setTimeout(K,m)))})),document.addEventListener("click",(e=>{let t=l.maxClicks;return e.preventDefault(),clearTimeout(x),T?(clearTimeout(T),void(T=setTimeout((()=>T=null),m))):(k=o(e.target),k&&k.dataset.hasOwnProperty("quickClick")&&(t=1),k&&"A"===k.tagName&&(t=1),f=e,v++,v>=t?(K(),void(t>1&&(T=setTimeout((()=>T=null),m)))):void(x=setTimeout(K,m)))}))),h.includes("keyboard")&&(document.addEventListener("keydown",(t=>{if(clearTimeout(g),r.hasOwnProperty(t.code))return y.push(i(t,r)),a&&a({key:t.key,context:c.name,note:c.note,dependencies:e.extra}),l.keyIgnore?(clearTimeout(l.keyIgnore),void(l.keyIgnore=setTimeout((()=>l.keyIgnore=null),d))):C&&y.length===l.maxSequence?(O(),void(l.keyIgnore=setTimeout((()=>l.keyIgnore=null),d))):void(C?g=setTimeout(O,d):O())})),document.addEventListener("keypress",(t=>{if(!r.hasOwnProperty(t.code)){if(clearTimeout(g),a&&a({key:t.key,context:c.name,note:c.note,dependencies:e.extra}),l.keyIgnore)return clearTimeout(l.keyIgnore),void(l.keyIgnore=setTimeout((()=>l.keyIgnore=null),d));if(y.push(i(t,r)),C&&y.length===l.maxSequence)return O(),void(l.keyIgnore=setTimeout((()=>l.keyIgnore=null),d));C?g=setTimeout(O,d):O()}})))}},_readKeyEvent:function(){return function(e,t){let{shiftKey:n,altKey:o,ctrlKey:r}=e,i=e.code.replace("Key","").replace("Digit",""),s=[];return r&&s.push("CTRL"),n&&s.push("SHIFT"),o&&s.push("ALT"),t.hasOwnProperty(i)?s.push(t[i].toUpperCase()):["ControlLeft","ControlRight","ShiftLeft","ShiftRight","AltLeft","AltRight","Meta"].includes(i)||s.push(i.toUpperCase()),s.sort()}},_readMouseEvent:function(){return function(e,t){let{shiftKey:n,altKey:o,ctrlKey:r,key:i,button:s}=e,u=`MOUSE-CLICK-${["LEFT","MIDDLE","RIGHT"][s]}-${t}`,c=[];return c.push(u),r&&c.push("CTRL"),n&&c.push("SHIFT"),o&&c.push("ALT"),c.sort()}},_readShortcut:function(){return function(e){return e.split(",").map((e=>e.trim())).map((e=>e.split("+").map((e=>e.toUpperCase())).sort().join("+"))).join(",")}},_specialChars:function(){return{ArrowLeft:"LEFT",ArrowUp:"UP",ArrowRight:"RIGHT",ArrowDown:"DOWN",Enter:"ENTER",NumpadEnter:"ENTER",Escape:"ESC",Backspace:"BACKSPACE",Space:"SPACE",Tab:"TAB",Backquote:"`",BracketLeft:"[",BracketRight:"]",Equal:"=",Slash:"/",Backslash:"\\",IntlBackslash:"`",F1:"F1",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",F10:"F10",F11:"F11",F12:"F12"}},changeContext:function(e,t){const{shortcuts:n,listenOptions:o,currentContext:r}=t,{ev:i}=e;return function(e=!1){const t=r.name;if(o.maxSequence=1,o.maxClicks=1,o.keyIgnore>=0&&(clearTimeout(o.keyIgnore),o.keyIgnore=null),!e)return i.reset(),void(r.name=null);t!==e&&(n[e]?(n[t]&&i.reset(),Object.entries(n[e]).forEach((([e,t])=>{if(e.includes("MOUSE-CLICK-")){let[,,,t]=e.split("-"),n=parseInt(t);o.maxClicks<n&&(o.maxClicks=n)}else{let t=e.split(",").length;o.maxSequence<t&&(o.maxSequence=t)}t.forEach((t=>i.on(e,t)))})),r.name=e):i.emit("shortcuts-error",`Context '${e}' does not exist`))}},listShortcuts:function(e,t){const n=t.shortcuts;return function(e=null){if(null!=e){let t=n[e];return null==t?null:Object.entries(t).map((([e,t])=>e))}return Object.keys(n).map((e=>{let t={};return t.context=e,t.shortcuts=Object.entries(n[e]).map((([e,t])=>e)),t}))}},load:function(e,t){const{shortcuts:n}=t,{inAPI:{_readShortcut:o},API:{changeContext:r,getContext:i}}=e;return function(e){const t=i(),s=Object.keys(e);let u=!1;s.forEach((r=>{const i=Object.entries(e[r]);r===t&&(u=!0),n[r]={},i.forEach((([e,t])=>{const i=o(e);t instanceof Function&&(t=[t]),n[r][i]=t}))})),u&&(r(),r(t))}},unload:function(e,t){const{currentContext:n,shortcuts:o}=t,{ev:r}=e;return function(e){n.name!==e?o[e]?delete o[e]:r.emit("shortcuts-error",`Context '${e}' does not exist`):r.emit("shortcuts-error",`Context '${e}' can't be removed during is current active context. Change the context first`)}}};function n(n={}){const o=e(),r={},i={},s={currentContext:{name:null,note:null},shortcuts:{},listenOptions:{mouseWait:n.mouseWait?n.mouseWait:320,maxClicks:1,keyWait:n.keyWait?n.keyWait:480,maxSequence:1,clickTarget:n.clickTarget?n.clickTarget:"click",listenFor:n.listenFor&&Array.isArray(n.listenFor)?n.listenFor:["mouse","keyboard"],keyIgnore:null},exposeShortcut:!(!n.onShortcut||"function"!=typeof n.onShortcut)&&n.onShortcut,streamKeys:!(!n.streamKeys||"function"!=typeof n.streamKeys)&&n.streamKeys},u={ev:o,inAPI:r,API:i,extra:{}};return i.getContext=()=>s.currentContext.name,i.getNote=()=>s.currentContext.note,i.setNote=(e=null)=>{"string"!=typeof e&&null!=e||(s.currentContext.note=e)},i.pause=(e="*")=>o.stop(r._readShortcut(e)),i.resume=(e="*")=>o.start(r._readShortcut(e)),i.emit=(e,...t)=>o.emit(r._readShortcut(e),u.extra,...t),i.listContexts=()=>Object.keys(shortcuts),i.setDependencies=e=>u.extra={...u.extra,...e},i.getDependencies=()=>u.extra,Object.entries(t).forEach((([e,t])=>{e.startsWith("_")?r[e]=t(u,s):i[e]=t(u,s)})),r._listen(),i}n.getDefaults=()=>({mouseWait:320,keyWait:480,clickTarget:"click",listenFor:["mouse","keyboard"],onShortcut:!1,streamKeys:!1});export{n as default};
@@ -0,0 +1 @@
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).shortcuts=t()}(this,(function(){"use strict";var e={_findTarget:function(e,t){const{listenOptions:{clickTarget:n}}=t;return function e(t){const o=t;return o===document||o===document.body?null:o.dataset[n]||"A"===o.nodeName?o:e(o.parentNode)}},_listen:function(e,t){return function(){const{ev:n,inAPI:{_findTarget:o,_specialChars:r,_readKeyEvent:i,_readMouseEvent:s}}=e,{exposeShortcut:c,currentContext:u,streamKeys:l,listenOptions:a}=t,{mouseWait:d,keyWait:f,clickTarget:m,listenFor:h}=a;let p=[],g=null,y=null,k=null,x=null,T=null,C=!0,F=!1,E=0;const v=()=>C=!1,I=()=>C=!0,S=()=>F=!0,A=()=>!1===C;function O(){let t=p.map((e=>[e.join("+")]));if(!C){let e=t.at(-1);n.emit(e,{wait:v,end:I,ignore:S,isWaiting:A,note:u.note,context:u.name}),F&&(t=t.slice(0,-1),F=!1)}const o={wait:v,end:I,ignore:S,isWaiting:A,note:u.note,context:u.name,dependencies:e.extra};C&&(n.emit(t.join(","),o),c&&c({shortcut:t.join(","),context:u.name,note:u.note,dependencies:e.extra}),p=[],k=null)}function b(){const t=s(y,E),o={target:g,targetProps:g?g.getBoundingClientRect():null,x:y.clientX,y:y.clientY,context:u.name,note:u.note,event:y,dependencies:e.extra};n.emit(t.join("+"),o),c&&c({shortcut:t.join("+"),context:u.name,note:u.note,dependencies:e.extra}),x=null,T=null,g=null,y=null,E=0}h.includes("mouse")&&(window.addEventListener("contextmenu",(e=>{let t=a.maxClicks;return e.preventDefault(),clearTimeout(x),T?(clearTimeout(T),void(T=setTimeout((()=>T=null),d))):(g=o(e.target),g&&g.dataset.hasOwnProperty("quickClick")&&(t=1),g&&"A"===g.tagName&&(t=1),y=e,E++,E>=t?(b(),void(t>1&&(T=setTimeout((()=>T=null),d)))):void(x=setTimeout(b,d)))})),document.addEventListener("click",(e=>{let t=a.maxClicks;return e.preventDefault(),clearTimeout(x),T?(clearTimeout(T),void(T=setTimeout((()=>T=null),d))):(g=o(e.target),g&&g.dataset.hasOwnProperty("quickClick")&&(t=1),g&&"A"===g.tagName&&(t=1),y=e,E++,E>=t?(b(),void(t>1&&(T=setTimeout((()=>T=null),d)))):void(x=setTimeout(b,d)))}))),h.includes("keyboard")&&(document.addEventListener("keydown",(t=>{if(clearTimeout(k),r.hasOwnProperty(t.code))return p.push(i(t,r)),l&&l({key:t.key,context:u.name,note:u.note,dependencies:e.extra}),a.keyIgnore?(clearTimeout(a.keyIgnore),void(a.keyIgnore=setTimeout((()=>a.keyIgnore=null),f))):C&&p.length===a.maxSequence?(O(),void(a.keyIgnore=setTimeout((()=>a.keyIgnore=null),f))):void(C?k=setTimeout(O,f):O())})),document.addEventListener("keypress",(t=>{if(!r.hasOwnProperty(t.code)){if(clearTimeout(k),l&&l({key:t.key,context:u.name,note:u.note,dependencies:e.extra}),a.keyIgnore)return clearTimeout(a.keyIgnore),void(a.keyIgnore=setTimeout((()=>a.keyIgnore=null),f));if(p.push(i(t,r)),C&&p.length===a.maxSequence)return O(),void(a.keyIgnore=setTimeout((()=>a.keyIgnore=null),f));C?k=setTimeout(O,f):O()}})))}},_readKeyEvent:function(){return function(e,t){let{shiftKey:n,altKey:o,ctrlKey:r}=e,i=e.code.replace("Key","").replace("Digit",""),s=[];return r&&s.push("CTRL"),n&&s.push("SHIFT"),o&&s.push("ALT"),t.hasOwnProperty(i)?s.push(t[i].toUpperCase()):["ControlLeft","ControlRight","ShiftLeft","ShiftRight","AltLeft","AltRight","Meta"].includes(i)||s.push(i.toUpperCase()),s.sort()}},_readMouseEvent:function(){return function(e,t){let{shiftKey:n,altKey:o,ctrlKey:r,key:i,button:s}=e,c=`MOUSE-CLICK-${["LEFT","MIDDLE","RIGHT"][s]}-${t}`,u=[];return u.push(c),r&&u.push("CTRL"),n&&u.push("SHIFT"),o&&u.push("ALT"),u.sort()}},_readShortcut:function(){return function(e){return e.split(",").map((e=>e.trim())).map((e=>e.split("+").map((e=>e.toUpperCase())).sort().join("+"))).join(",")}},_specialChars:function(){return{ArrowLeft:"LEFT",ArrowUp:"UP",ArrowRight:"RIGHT",ArrowDown:"DOWN",Enter:"ENTER",NumpadEnter:"ENTER",Escape:"ESC",Backspace:"BACKSPACE",Space:"SPACE",Tab:"TAB",Backquote:"`",BracketLeft:"[",BracketRight:"]",Equal:"=",Slash:"/",Backslash:"\\",IntlBackslash:"`",F1:"F1",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",F10:"F10",F11:"F11",F12:"F12"}},changeContext:function(e,t){const{shortcuts:n,listenOptions:o,currentContext:r}=t,{ev:i}=e;return function(e=!1){const t=r.name;if(o.maxSequence=1,o.maxClicks=1,o.keyIgnore>=0&&(clearTimeout(o.keyIgnore),o.keyIgnore=null),!e)return i.reset(),void(r.name=null);t!==e&&(n[e]?(n[t]&&i.reset(),Object.entries(n[e]).forEach((([e,t])=>{if(e.includes("MOUSE-CLICK-")){let[,,,t]=e.split("-"),n=parseInt(t);o.maxClicks<n&&(o.maxClicks=n)}else{let t=e.split(",").length;o.maxSequence<t&&(o.maxSequence=t)}t.forEach((t=>i.on(e,t)))})),r.name=e):i.emit("shortcuts-error",`Context '${e}' does not exist`))}},listShortcuts:function(e,t){const n=t.shortcuts;return function(e=null){if(null!=e){let t=n[e];return null==t?null:Object.entries(t).map((([e,t])=>e))}return Object.keys(n).map((e=>{let t={};return t.context=e,t.shortcuts=Object.entries(n[e]).map((([e,t])=>e)),t}))}},load:function(e,t){const{shortcuts:n}=t,{inAPI:{_readShortcut:o},API:{changeContext:r,getContext:i}}=e;return function(e){const t=i(),s=Object.keys(e);let c=!1;s.forEach((r=>{const i=Object.entries(e[r]);r===t&&(c=!0),n[r]={},i.forEach((([e,t])=>{const i=o(e);t instanceof Function&&(t=[t]),n[r][i]=t}))})),c&&(r(),r(t))}},unload:function(e,t){const{currentContext:n,shortcuts:o}=t,{ev:r}=e;return function(e){n.name!==e?o[e]?delete o[e]:r.emit("shortcuts-error",`Context '${e}' does not exist`):r.emit("shortcuts-error",`Context '${e}' can't be removed during is current active context. Change the context first`)}}};function t(t={}){const n=new function(){let e={"*":[]},t={},n=[],o=!1,r="";return{on:function(t,n){e[t]||(e[t]=[]),e[t].push(n)},once:function(e,n){"*"!==e&&(t[e]||(t[e]=[]),t[e].push(n))},off:function(n,o){if(o)return e[n]&&(e[n]=e[n].filter((e=>e!==o))),t[n]&&(t[n]=t[n].filter((e=>e!==o))),e[n]&&0===e[n].length&&delete e[n],void(t[n]&&0===t[n].length&&delete e[n]);t[n]&&delete t[n],e[n]&&delete e[n]},reset:function(){e={"*":[]},t={},n=[]},emit:function(){const[i,...s]=arguments;function c(t){"*"!==t&&(n.includes(t)||(e[t].forEach((e=>e(...s))),e["*"].forEach((e=>e(i,...s)))))}if(o&&(console.log(`${r} Event "${i}" was triggered.`),s.length>0&&(console.log("Arguments:"),console.log(...s),console.log("^----"))),"*"!==i){if(t[i]){if(n.includes(i))return;t[i].forEach((e=>e(...s))),delete t[i]}e[i]&&c(i)}else Object.keys(e).forEach((e=>c(e)))},stop:function(o){if("*"!==o)n.push(o);else{const o=Object.keys(e),r=Object.keys(t);n=[...r,...o]}},start:function(e){n="*"!==e?n.filter((t=>e!=t)):[]},debug:function(e,t){o=!!e,t&&"string"==typeof t&&(r=t)}}},o={},r={},i={currentContext:{name:null,note:null},shortcuts:{},listenOptions:{mouseWait:t.mouseWait?t.mouseWait:320,maxClicks:1,keyWait:t.keyWait?t.keyWait:480,maxSequence:1,clickTarget:t.clickTarget?t.clickTarget:"click",listenFor:t.listenFor&&Array.isArray(t.listenFor)?t.listenFor:["mouse","keyboard"],keyIgnore:null},exposeShortcut:!(!t.onShortcut||"function"!=typeof t.onShortcut)&&t.onShortcut,streamKeys:!(!t.streamKeys||"function"!=typeof t.streamKeys)&&t.streamKeys},s={ev:n,inAPI:o,API:r,extra:{}};return r.getContext=()=>i.currentContext.name,r.getNote=()=>i.currentContext.note,r.setNote=(e=null)=>{"string"!=typeof e&&null!=e||(i.currentContext.note=e)},r.pause=(e="*")=>n.stop(o._readShortcut(e)),r.resume=(e="*")=>n.start(o._readShortcut(e)),r.emit=(e,...t)=>n.emit(o._readShortcut(e),s.extra,...t),r.listContexts=()=>Object.keys(shortcuts),r.setDependencies=e=>s.extra={...s.extra,...e},r.getDependencies=()=>s.extra,Object.entries(e).forEach((([e,t])=>{e.startsWith("_")?o[e]=t(s,i):r[e]=t(s,i)})),o._listen(),r}return t.getDefaults=()=>({mouseWait:320,keyWait:480,clickTarget:"click",listenFor:["mouse","keyboard"],onShortcut:!1,streamKeys:!1}),t}));
package/package.json CHANGED
@@ -1,38 +1,51 @@
1
1
  {
2
2
  "name": "@peter.naydenov/shortcuts",
3
- "version": "2.0.0",
4
3
  "description": "Context control of shortcuts based on keyboard and mouse events",
5
- "keywords": [
6
- "shortcut",
7
- "key",
8
- "keyboard",
9
- "mouse",
10
- "click"
11
- ],
12
- "main": "src/main.js",
4
+ "version": "2.2.0",
5
+ "license": "MIT",
6
+ "author": "Peter Naydenov",
7
+ "main": "./dist/shortcuts.umd.js",
13
8
  "type": "module",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/shortcuts.esm.mjs",
12
+ "require": "./dist/shortcuts.cjs",
13
+ "default": "./dist/shortcuts.umd.js"
14
+ },
15
+ "./package.json": "./package.json",
16
+ "./dist/*": "./dist/*",
17
+ "./src/*": "./src/*"
18
+ },
14
19
  "scripts": {
15
20
  "dev": "vite",
16
- "build": "vite build",
17
- "test": "cypress open --component --browser chrome test"
21
+ "build": "rollup -c",
22
+ "test": "cypress run --component --browser chrome test"
18
23
  },
19
24
  "repository": {
20
25
  "type": "git",
21
26
  "url": "git+https://github.com/PeterNaydenov/shortcuts"
22
27
  },
23
28
  "dependencies": {
24
- "@peter.naydenov/notice": "^2.2.1"
29
+ "@peter.naydenov/notice": "^2.2.3"
25
30
  },
26
31
  "devDependencies": {
27
- "@vitejs/plugin-react": "^4.1.0",
28
- "ask-for-promise": "^1.4.0",
29
- "chai": "^4.3.10",
30
- "cypress": "^13.3.1",
31
- "mocha": "^10.2.0",
32
+ "@rollup/plugin-commonjs": "^25.0.7",
33
+ "@rollup/plugin-node-resolve": "^15.2.3",
34
+ "@rollup/plugin-terser": "^0.4.4",
35
+ "@vitejs/plugin-react": "^4.2.1",
36
+ "ask-for-promise": "^2.0.3",
37
+ "chai": "^5.0.3",
38
+ "cypress": "^13.6.4",
39
+ "mocha": "^10.3.0",
32
40
  "react": "^18.2.0",
33
41
  "react-dom": "^18.2.0",
34
- "vite": "^4.4.11"
42
+ "vite": "^5.1.1"
35
43
  },
36
- "author": "Peter Naydenov",
37
- "license": "MIT"
44
+ "keywords": [
45
+ "shortcut",
46
+ "key",
47
+ "keyboard",
48
+ "mouse",
49
+ "click"
50
+ ]
38
51
  }
@@ -0,0 +1,40 @@
1
+ import resolve from '@rollup/plugin-node-resolve'
2
+ import commonjs from '@rollup/plugin-commonjs'
3
+ import terser from '@rollup/plugin-terser';
4
+
5
+
6
+ export default [
7
+ // browser-friendly UMD build
8
+ {
9
+ input: 'src/main.js',
10
+ output: {
11
+ name: 'shortcuts',
12
+ file: 'dist/shortcuts.umd.js',
13
+ format: 'umd',
14
+ globals : {
15
+ '@peter.naydenov/notice': 'notice'
16
+ }
17
+ },
18
+ plugins: [
19
+ resolve(), // so Rollup can find `ms`
20
+ commonjs() // so Rollup can convert `ms` to an ES module
21
+ , terser()
22
+ ]
23
+ },
24
+
25
+ // CommonJS (for Node) and ES module (for bundlers) build.
26
+ // (We could have three entries in the configuration array
27
+ // instead of two, but it's quicker to generate multiple
28
+ // builds from a single configuration where possible, using
29
+ // an array for the `output` option, where we can specify
30
+ // `file` and `format` for each target)
31
+ {
32
+ input: 'src/main.js',
33
+ external: ['@peter.naydenov/notice'],
34
+ output: [
35
+ { file: 'dist/shortcuts.cjs' , format: 'cjs' },
36
+ { file: 'dist/shortcuts.esm.mjs', format: 'es' }
37
+ ],
38
+ plugins: [ terser() ]
39
+ }
40
+ ];
package/src/main.js CHANGED
@@ -10,7 +10,7 @@
10
10
  * - Development was started on June 21st, 2023
11
11
  * - First version was published on August 14th, 2023
12
12
  * - Method 'emit' was added on September 30st, 2023
13
- * - Version 2.0.0 was published on October 14th, 2023
13
+ * - Version 2.0.0 was published on October 16th, 2023
14
14
  */
15
15
 
16
16
 
@@ -112,6 +112,13 @@ function main ( options = {} ) {
112
112
  */
113
113
  API.setDependencies = (deps) => dependencies.extra = { ...dependencies.extra, ...deps }
114
114
 
115
+ /**
116
+ * @function getDependencies
117
+ * @description Get a dependency package that will be provided to each action function
118
+ * @returns {object} - Enumerate external dependencies
119
+ **/
120
+ API.getDependencies = () => dependencies.extra
121
+
115
122
 
116
123
 
117
124
  Object.entries ( methods ).forEach ( ([ name, method ]) => {