@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 +19 -1
- package/README.md +14 -10
- package/cypress/fixtures/example.json +5 -0
- package/cypress/support/commands.js +25 -0
- package/cypress/support/component-index.html +14 -0
- package/cypress/support/component.js +27 -0
- package/cypress/support/e2e.js +20 -0
- package/dist/shortcuts.cjs +1 -0
- package/dist/shortcuts.esm.mjs +1 -0
- package/dist/shortcuts.umd.js +1 -0
- package/package.json +33 -20
- package/rollup.config.js +40 -0
- package/src/main.js +8 -1
package/Changelog.md
CHANGED
|
@@ -2,7 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
###
|
|
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
|

|
|
4
4
|

|
|
5
|
+

|
|
6
|
+

|
|
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
|
|
285
|
-
, unload
|
|
286
|
-
, changeContext
|
|
287
|
-
, emit
|
|
288
|
-
, pause
|
|
289
|
-
, resume
|
|
290
|
-
, listContexts
|
|
291
|
-
, getContext
|
|
292
|
-
, getNote
|
|
293
|
-
, setNote
|
|
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,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
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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": "
|
|
17
|
-
"test": "cypress
|
|
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.
|
|
29
|
+
"@peter.naydenov/notice": "^2.2.3"
|
|
25
30
|
},
|
|
26
31
|
"devDependencies": {
|
|
27
|
-
"@
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
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": "^
|
|
42
|
+
"vite": "^5.1.1"
|
|
35
43
|
},
|
|
36
|
-
"
|
|
37
|
-
|
|
44
|
+
"keywords": [
|
|
45
|
+
"shortcut",
|
|
46
|
+
"key",
|
|
47
|
+
"keyboard",
|
|
48
|
+
"mouse",
|
|
49
|
+
"click"
|
|
50
|
+
]
|
|
38
51
|
}
|
package/rollup.config.js
ADDED
|
@@ -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
|
|
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 ]) => {
|