@peter.naydenov/shortcuts 2.1.0 → 3.0.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.
Files changed (39) hide show
  1. package/Changelog.md +24 -0
  2. package/How..to.make.plugins.md +41 -0
  3. package/Migration.guide.md +77 -0
  4. package/README-v.2.x.x.md +375 -0
  5. package/README.md +108 -58
  6. package/cypress/fixtures/example.json +5 -0
  7. package/cypress/support/commands.js +25 -0
  8. package/cypress/support/component-index.html +14 -0
  9. package/cypress/support/component.js +27 -0
  10. package/cypress/support/e2e.js +20 -0
  11. package/dist/shortcuts.cjs +1 -0
  12. package/dist/shortcuts.esm.mjs +1 -0
  13. package/dist/shortcuts.umd.js +1 -0
  14. package/package.json +32 -19
  15. package/rollup.config.js +40 -0
  16. package/src/main.js +81 -30
  17. package/src/methods/_normalizeWithPlugins.js +25 -0
  18. package/src/methods/_readShortcutWithPlugins.js +24 -0
  19. package/src/methods/_systemAction.js +25 -0
  20. package/src/methods/changeContext.js +20 -26
  21. package/src/methods/index.js +7 -13
  22. package/src/methods/load.js +21 -14
  23. package/src/plugins/click/_findTarget.js +20 -0
  24. package/src/plugins/click/_listenDOM.js +117 -0
  25. package/src/plugins/click/_normalizeShortcutName.js +44 -0
  26. package/src/plugins/click/_readClickEvent.js +24 -0
  27. package/src/plugins/click/_registerShortcutEvents.js +30 -0
  28. package/src/plugins/click/index.js +74 -0
  29. package/src/plugins/key/_listenDOM.js +138 -0
  30. package/src/plugins/key/_normalizeShortcutName.js +31 -0
  31. package/src/{methods → plugins/key}/_readKeyEvent.js +2 -3
  32. package/src/plugins/key/_registerShortcutEvents.js +28 -0
  33. package/src/plugins/key/index.js +76 -0
  34. package/test/01-general.cy.js +189 -154
  35. package/src/methods/_findTarget.js +0 -19
  36. package/src/methods/_listen.js +0 -210
  37. package/src/methods/_readMouseEvent.js +0 -24
  38. package/src/methods/_readShortcut.js +0 -17
  39. /package/src/{methods → plugins/key}/_specialChars.js +0 -0
package/README.md CHANGED
@@ -2,22 +2,20 @@
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
 
9
+
10
+ Describe all page activities as list of shortcuts wrapped in contexts. Switch among available contexts.
11
+ Library has a plugin system to extend the possible `shortcut names`/`event coverage`. Plugins role is to convert DOM events to shortcut strings, then the core part will trigger the action functions related to the shortcut. At the moment we have 2 plugins:
12
+ - `key` - converts keyboard events to shortcut names;
13
+ - `click` - converts mouse events to shortcut names;
7
14
 
8
- Define a context based keyboard-shortcuts and describe a mouse clicks. Switch among contexts.
15
+ Planned work on the plugins for `scroll`, `drag-drop`, `input-change`, etc...
9
16
 
10
17
 
11
18
 
12
-
13
- ## What's new?
14
-
15
- - Version 1.1.x and above are coming with method `emit` that make possible to trigger shortcuts programmatically. In `shortcuts` you can mix keyboard, mouse and programmatical events that is prity everything that can happen in a web page;
16
- - Method `setDependencies` comes after version 2.0.0. You can insert external libraries and modules that you will need to access from action functions. They will come as **dependencies** object. In function `emit` will come as first argument. Other arguments are attached behind the dependencies object;
17
- - Another html data attribute is added - `data-quick-click`. Example: `<button data-click="id" data-quick-click>Click me</button>`. This attribute is saying to `shortcuts` that target element don't need to wait for more then 1 click and shortcut can be executed immediately after the click. Attribute is available in version 2.0.0 and above;
18
- - Method `listShortcuts` is added in version 2.0.0. It returns a list of shortcuts for requested context. If context is not set, will return a list of all shortcuts;
19
-
20
-
21
19
  ## Shortcut Description Rules
22
20
  The shortcuts definition includes a context name and a set of rules(object). The rules are a set of key-value pairs. The key is a shortcut name and the value is a function or array of functions, to be executed when the shortcut is triggered (action function).
23
21
 
@@ -42,14 +40,28 @@ The shortcuts definition includes a context name and a set of rules(object). The
42
40
  ]
43
41
  }
44
42
  }
43
+ // shortcutName after v.3.0.0 have a plugin prefix. - 'pluginPrefix:shortcutName'.
44
+ // For example: 'key:s+alt' - for 's+alt' shortcut that is handled by 'key' plugin.
45
45
  ```
46
46
 
47
47
  Load a shortcut definition by calling `load` method.
48
48
 
49
49
  ```js
50
- include shortcuts from '@peter.naydenov/shortcuts'
50
+ // for es6 module projects:
51
+ include { shortcuts, pluginKey, pluginClick } from '@peter.naydenov/shortcuts'
52
+ // for commonjs projects:
53
+ const { shortcuts, pluginKey, pluginClick } = require('@peter.naydenov/shortcuts')
54
+
55
+
56
+
51
57
  const short = shortcuts ();
58
+ // Load a needed plugins
59
+ // short.enablePlugin ( pluginCode, ?pluginOptions )
60
+ short.enablePlugin ( pluginKey )
61
+ short.enablePlugin ( pluginClick )
62
+ // Load a shortcut definition
52
63
  short.load ( shortcutDefinition )
64
+
53
65
  ```
54
66
 
55
67
  Shortcuts are working only if contex is active. To activate a context call `changeContext` method.
@@ -102,39 +114,45 @@ short.getNote ()
102
114
 
103
115
 
104
116
 
105
- ## Mouse Event Descriptions
117
+ ## Plugin 'click' Shortcut Descriptions
106
118
  Mouse event name is build from the following parts:
107
119
  ```js
108
- // mouse-click-<mouse button>-<number of clicks>
120
+ // click:<mouse button>-<number of clicks>-<modifier key>-<modifier key>-<modifier key>
109
121
  // example:
110
- // mouse-click-left-2 -> for double click with left mouse button
111
- // mouse-click-right-3 -> for triple click with right mouse button
122
+ // click: left-2 -> for double click with left mouse button
123
+ // click: right-3 -> for triple click with right mouse button
112
124
 
113
125
  // mouse button options: left, right, middle
114
126
  ```
115
127
 
116
- The modifier keys `ctrl`, `alt`, and `shift` are supported. They are added to the mouse event by sign `+`:
128
+ The modifier keys `ctrl`, `alt`, and `shift` are supported. They are added to the mouse event by sign `-`:
117
129
 
118
130
  ```js
119
131
  // example:
120
- // ctrl+mouse-click-left-1 -> for single click with left mouse button and ctrl key pressed
132
+ // click: left-1-ctrl -> for single click with left mouse button and ctrl key pressed
121
133
  ```
122
- Order of describing mouse event and modifier keys is not important.
134
+
135
+ Order of describing click event and modifier keys is not important.
123
136
 
124
137
  ```js
125
138
  // example:
126
- // mouse-click-left-1+ctrl -> same as above
139
+ // click: ctrl-left-1 -> same as above
127
140
 
128
141
  // These 3 descriptions are equal:
129
- // mouse-click-left-1+ctrl+alt+shift
130
- // alt+shift+mouse-click-left-1+ctrl
131
- // mouse-click-left-1+shift+ctrl+alt
142
+ // click: left-1-ctrl-alt-shift
143
+ // click: alt-shift-left-1-ctrl
144
+ // click: left-1-shift-ctrl-alt
132
145
  ```
133
146
 
134
- Multiple clicks are detected automatically by time interval between clicks. The default interval is 320ms but you can change it by setting `mouseWait` option. Read more in section `Options`.
147
+ Multiple clicks are detected automatically by time interval between clicks. The default interval is 320ms but you can change it by setting `mouseWait` click plugin option.
148
+ ```js
149
+ short.enablePlugin ( pluginClick, { mouseWait: 500 }) // set the interval to 500ms
150
+ ```
151
+
152
+ Read more in section `Options`.
135
153
 
136
154
 
137
- ## Define a mouse targets
155
+ ## Define a Click Targets
138
156
  Target HTML elements for `shortcuts` are defined by `data-click` attribute. The value of the attribute is the name of the target. Example:
139
157
 
140
158
  ```html
@@ -142,7 +160,7 @@ Target HTML elements for `shortcuts` are defined by `data-click` attribute. The
142
160
  <!-- target name is 'id' -->
143
161
  ```
144
162
 
145
- Attribute is customizable by setting `clickTarget` option. Read more in section `Options`.
163
+ Attribute is customizable by setting `clickTarget` click plugin option. Read more in section `Options`.
146
164
 
147
165
  If current shortcuts context contain definition for 2 or more clicks, this may slow down the execution of single shortcuts because `shortcuts` will wait for the time interval to detect multiple clicks. To avoid this for specific targets, you can set `data-quick-click` attribute to the target element. Example:
148
166
 
@@ -155,17 +173,18 @@ Using a <a> tag is a special case. It's always recognized as a target, and alway
155
173
  ```html
156
174
  <a href="#">Click me</a>
157
175
  <!-- Recognized as a target and will not wait for more then 1 click -->
158
- <!-- Take care for the action from shortcut `mouse-click-left-1`. -->
176
+ <!-- Take care for the action from shortcut `click: left-1`. -->
159
177
  ```
160
178
 
161
- Clicking on <a> tag will not execute anything. All events are blocked by default. In your `mouse-click-left-1` action function you can write a code to execute the default action. Example:
179
+ Clicking on <a> tag will execute default browser behaviour. In your `click:left-1` action function you can take the control. Example:
162
180
 
163
181
  ```js
164
182
  {
165
183
  contextName : {
166
- 'mouse-click-left-1' : function ( {target, event} ) {
167
- if ( target.tagName === 'A' ) { // All targets that are <a> tags will execute the default action
168
- window.location.href = target.href // Go to the link
184
+ 'click:left-1' : function ( {target, event} ) {
185
+ if ( target.tagName === 'A' ) { // To prevent default action on <a> tag
186
+ event.preventDefault ()
187
+ // do something...
169
188
  }
170
189
  }
171
190
  }
@@ -174,35 +193,35 @@ Clicking on <a> tag will not execute anything. All events are blocked by default
174
193
 
175
194
 
176
195
 
177
- ## Keyboard Event Descriptions
196
+ ## Plugin 'key' Event Descriptions
178
197
  Keyboard event description contains a key name and a modifier keys if they are used. The modifier keys `ctrl`, `alt`, and `shift` are supported. They are added to the keyboard event by sign `+`:
179
198
 
180
199
  ```js
181
200
  // example:
182
- // ctrl+alt+shift+a -> for key 'a' with ctrl, alt and shift keys pressed
201
+ // key: ctrl+alt+shift+a -> for key 'a' with ctrl, alt and shift keys pressed
183
202
  ```
184
203
 
185
204
  Keyboard event description support a shortcut sequenses. These means that you can press a sequence of keys to trigger a shortcut. The sequence elements are separated by sign "," ( coma ):
186
205
 
187
206
  ```js
188
207
  // example:
189
- // a,b,c -> for key 'a' then key 'b' then key 'c'
208
+ // key: a,b,c -> for key 'a' then key 'b' then key 'c'
190
209
 
191
- // g+shift,o,t,o -> for key 'g' with shift, then key 'o', then key 't' then key 'o'
210
+ // key: g+shift,o,t,o -> for key 'g' with shift, then key 'o', then key 't' then key 'o'
192
211
  ```
193
212
 
194
213
  Order of describing keyboard event and modifier keys is not important, but sequence elements are:
195
214
 
196
215
  ```js
197
216
  // example:
198
- // a+ctrl,l,o,t -> a with ctrl, then l, then o, then t
217
+ // key: a+ctrl,l,o,t -> a with ctrl, then l, then o, then t
199
218
  // this is equal to:
200
- // ctrl+a,l,o,t
219
+ // key: ctrl+a,l,o,t
201
220
  // but not equal to:
202
- // ctrl+a,o,t,l
221
+ // key: ctrl+a,o,t,l
203
222
  ```
204
223
 
205
- Keyboard sequence is detected automatically by time interval between key presses. The default interval is 480ms but you can change it by setting `keyWait` option. Read more in section `Options`.
224
+ Keyboard sequence is detected automatically by time interval between key presses. The default interval is 480ms but you can change it by setting `keyWait` key plugin option. Read more in section `Options`.
206
225
 
207
226
  There is a way to disable automatic sequence detection and mark the begining and the end of the sequense by using a keyboard action functions. Read more in section `Keyboard Action Functions`.
208
227
 
@@ -224,19 +243,19 @@ Special characters that are available for your shortcut descriptions:
224
243
  - ']' - close square bracket key
225
244
  - '`' - backtick key
226
245
 
227
- **Warning**: For keys with two symbols, in shortcut description use the lower one. Examples: Use '=' instead of '+', use '/' instead of '?', etc. Modifier keys are available for special characters too.
246
+ **Warning**: For keys with two symbols(look at the keyboard), in shortcut description use the lower one. Examples: Use '=' instead of '+', use '/' instead of '?', etc. Modifier keys are available for special characters too.
228
247
 
229
248
  **Warining**: Some of the shortcuts are used by OS and the browswer, so they are not available.
230
249
 
231
250
 
232
251
 
233
252
  ## Action Functions
234
- Action functions are called when a shortcut is triggered. They is a difference between keyboard and mouse action functions. Arguments are slightly different.
253
+ Action functions are called when a shortcut is triggered. There is a difference among plugin action functions. Arguments are slightly different.
235
254
 
236
255
 
237
256
 
238
257
  ### Keyboard Action Functions
239
- Description of keyboard action functions is:
258
+ Description of `key` plugin action functions is:
240
259
  ```js
241
260
  function myKeyHandler ({
242
261
  context // (string) Name of the current context;
@@ -255,7 +274,7 @@ function myKeyHandler ({
255
274
 
256
275
 
257
276
  ### Mouse Action Functions
258
- Mouse action functions can be described like:
277
+ Click plugin action functions can be described like:
259
278
 
260
279
  ```js
261
280
  function myMouseHandler ({
@@ -283,16 +302,23 @@ Description of the methods of shortcut instance:
283
302
  ```js
284
303
  load : 'Load and extend a shortcut definition.'
285
304
  , unload : 'Remove a shortcut context with all its shortcuts.'
286
- , changeContext : 'Switch to existing shortcut context.'
305
+
306
+ , enablePlugin : 'Enable a plugin.'
307
+ , disablePlugin : 'Disable a plugin.'
308
+ , mutePlugin : 'Mute a plugin. All events for the plugin will be ignored.'
309
+ , unmutePlugin : 'Unmute a plugin. All events for the plugin will be listened again.'
310
+
311
+ , changeContext : 'Switch to existing shortcut context or shitch off the context(if no argument).'
312
+ , getContext : 'Return a name of current context or null if there is no context selected'
287
313
  , emit : 'Trigger a shortcut or custom event programmatically.'
288
314
  , pause : 'Stop listening for shortcuts.'
289
315
  , resume : 'Resume listening for shortcuts.'
290
316
  , listContexts : 'Return list of available contexts.'
291
- , getContext : 'Return a name of current context or null if there is no context selected'
317
+ , listShortcuts : 'Return list of shortcuts per context.'
292
318
  , getNote : `Return a name of current note or null if note isn't set`
293
319
  , setNote : 'Set a note to current context.'
294
320
  , setDependencies : 'Set dependencies that will be available in action functions.'
295
- , getDependencies : 'Return dependencies object.'
321
+ , getDependencies : 'Return a dependencies object.'
296
322
  ```
297
323
 
298
324
  ### How to 'pause' and 'resume'?
@@ -310,27 +336,44 @@ short.resume ('*') // will resume all shortcuts
310
336
 
311
337
  ## Options
312
338
 
313
- By `options` you can customize the behavior of the shortcuts. Here is the list of available options:
339
+ Shortcut receives `options` during the start. Here is the list of available options:
314
340
 
315
341
  ```js
316
- mouseWait : 'Timeout for entering multiple mouse events. Default value - 320.'
317
- , keyWait : 'Timeout for entering shortcut sequence in ms. Default value - 480'
318
- , clickTarget : 'Data attribute name to recognize click items in HTML. Default value - click' // data attribute 'click' means attribute ( data-click='someName' )
319
- , listenFor : `List input signal sources. Default value - [ 'mouse', 'keyboard' ]`
320
- , onShortcut : 'False or a callback function that is called when a shortcut is triggered. Default value - false'
342
+ onShortcut : 'Callback function that is called when a shortcut is triggered. Default value - false'
343
+ ```
344
+ ```js
345
+ const short = shortcut ({onShortcut: (shortcut) => console.log(shortcut) }) // Log in console each triggered shortcut
346
+ ```
347
+
348
+
349
+
350
+ ### Plugin 'key' options
351
+ ```js
352
+ keyWait : 'Timeout for entering shortcut sequence in ms. Default value - 480'
321
353
  , streamKeys : 'False or a callback function that is called when a key is pressed. Default value - false'
322
354
  ```
323
355
 
324
- You can request default list of options with their default values:
325
356
 
357
+
358
+ ### Plugin 'click' options
326
359
  ```js
327
- shortcuts.getDefaults ()
328
- // Note: This method is availalble on the original shortcuts object, not on the shortcuts instance.
360
+ mouseWait : 'Timeout for entering multiple mouse events. Default value - 320.'
361
+ , clickTarget : 'Data attribute name to recognize click items in HTML. Default value - click' // data attribute 'click' means attribute ( data-click='someName' )
362
+ ```
363
+
364
+ Plugin options are provided as a second argument during the plugin enabling. It's look like this:
365
+
366
+ ```js
367
+ short.enablePlugin ( pluginKey, {
368
+ keyWait: 500 // set the interval to 500ms
369
+ , streamKeys: (key) => console.log(key) // Log in console each pressed key
370
+ })
371
+
372
+ short.enablePlugin ( pluginClick, {
373
+ mouseWait: 200 // set the interval between multiple clicks to 200ms
374
+ , clickTarget: 'puk' // data attribute 'puk' means attribute ( data-puk='someName' )
375
+ })
329
376
 
330
- // start a shortcuts with default options
331
- const short = shortcuts ()
332
- const short = shortcuts ( shortcuts.getDefaults () ) // same as above
333
- // The idea behind getDefaults is to see what options are available and what are their default values.
334
377
  ```
335
378
 
336
379
 
@@ -359,6 +402,13 @@ const short = shortcuts ( shortcuts.getDefaults () ) // same as above
359
402
 
360
403
 
361
404
 
405
+ ## Links
406
+
407
+ - [History of changes](https://github.com/PeterNaydenov/shortcuts/blob/main/Changelog.md)
408
+ - [Migration guide](https://github.com/PeterNaydenov/shortcuts/blob/main/Migration.guide.md)
409
+ - [How to make a plugin](https://github.com/PeterNaydenov/shortcuts/blob/main/How.to.make.plugins.md)
410
+
411
+
362
412
 
363
413
  ## Credits
364
414
  '@peter.naydenov/shortcuts' was created and supported by Peter Naydenov.
@@ -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 t=require("@peter.naydenov/notice");var e={_normalizeWithPlugins:function(t,e){return function(t){const n=e.shortcuts;Object.keys(n).forEach((e=>{Object.entries(n[e]).forEach((([o,r])=>{const i=t(o);i!==o&&(delete n[e][o],n[e][i]=r)}))}))}},_readShortcutWithPlugins:function(t,e){return function(n){const{inAPI:o}=t,r=n.split(":")[0],i=o._systemAction(r,"none");let s=n;return-1!==i&&(s=e.plugins[i].shortcutName(n)),s}},_systemAction:function(t,e){return function(t,n,o=null){return e.plugins.findIndex((e=>e.getPrefix()===t&&(e[n]&&e[n](o),!0)))}},changeContext:function(t,e){const{shortcuts:n,currentContext:o}=e,{ev:r}=t;return function(t=!1){const i=o.name;if(!t)return r.reset(),void(o.name=null);i!==t&&(n[t]?(n[i]&&r.reset(),o.name=t,e.plugins.forEach((e=>e.contextChange(t))),Object.entries(n[t]).forEach((([t,e])=>{e.forEach((e=>r.on(t,e)))})),r.on("*",((...t)=>{e.exposeShortcut&&e.exposeShortcut(...t)}))):r.emit("@shortcuts-error",`Context '${t}' does not exist`))}},listShortcuts:function(t,e){const n=e.shortcuts;return function(t=null){if(null!=t){let e=n[t];return null==e?null:Object.entries(e).map((([t,e])=>t))}return Object.keys(n).map((t=>{let e={};return e.context=t,e.shortcuts=Object.entries(n[t]).map((([t,e])=>t)),e}))}},load:function(t,e){const{shortcuts:n,plugins:o}=e,{API:{changeContext:r,getContext:i}}=t;return function(t){const e=i(),s=o.map((t=>t.getPrefix().toUpperCase()));let c=!1;Object.entries(t).forEach((([t,r])=>{t===e&&(c=!0),n[t]={},Object.entries(r).forEach((([e,r])=>{let i=e,c=e.toUpperCase().trim(),u=s.map(((t,e)=>c.startsWith(t)?e:null)).filter((t=>null!==t));if(u.length){let t=u[0];i=o[t].shortcutName(e)}r instanceof Function&&(r=[r]),n[t][i]=r}))})),c&&(r(),r(e))}},unload:function(t,e){const{currentContext:n,shortcuts:o}=e,{ev:r}=t;return function(t){n.name!==t?o[t]?delete o[t]:r.emit("shortcuts-error",`Context '${t}' does not exist`):r.emit("shortcuts-error",`Context '${t}' can't be removed during is current active context. Change the context first`)}}};function n(t){const e=t.toUpperCase(),n=/KEY\s*\:/i.test(e),o=e.indexOf(":");if(!n)return t;return`KEY:${e.slice(o+1).split(",").map((t=>t.trim())).map((t=>t.split("+").map((t=>t.trim())).sort().join("+"))).join(",")}`}function o(t,e){let{shiftKey:n,altKey:o,ctrlKey:r}=t,i=t.code.replace("Key","").replace("Digit",""),s=[];return r&&s.push("CTRL"),n&&s.push("SHIFT"),o&&s.push("ALT"),e.hasOwnProperty(i)?s.push(e[i].toUpperCase()):["ControlLeft","ControlRight","ShiftLeft","ShiftRight","AltLeft","AltRight","Meta"].includes(i)||s.push(i.toUpperCase()),s.sort()}function r(t,e){let n=0;const{regex:o}=t,{listenOptions:r,currentContext:{name:i},shortcuts:s}=e;return null==i?0:(Object.entries(s[i]).forEach((([t,e])=>{if(!o.test(t))return;n++;let i=t.slice(4).split(",").length;r.maxSequence<i&&(r.maxSequence=i)})),n)}function i(){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"}}function s(t,e,n){const{listenOptions:{clickTarget:o}}=e;let r=n;return r===document||r===document.body?null:r.dataset[o]||"A"===r.nodeName?r:s(t,e,r.parentNode)}function c(t){const e=t.toUpperCase(),n=/CLICK\s*\:/i.test(e),o=["LEFT","MIDDLE","RIGHT"],r=["ALT","SHIFT","CTRL"];let i=null,s=[],c=0,u=e.indexOf(":");return n?(e.slice(u+1).trim().split("-").map((t=>t.trim())).forEach((t=>{o.includes(t)?i=t:r.includes(t)?s.push(t):isNaN(t)||(c=t)})),`CLICK:${i}-${c}${s.length>0?"-":""}${s.sort().join("-")}`):t}function u(t,e){let{shiftKey:n,altKey:o,ctrlKey:r,key:i,button:s}=t,c=`CLICK:${["LEFT","MIDDLE","RIGHT"][s]}-${e}`,u=[];return r&&u.push("CTRL"),n&&u.push("SHIFT"),o&&u.push("ALT"),u.length>0?`${c}${u.length>0?"-":""}${u.sort().join("-")}`:`${c}`}function l(t,e){let n=0;const{regex:o}=t,{listenOptions:r,currentContext:{name:i},shortcuts:s}=e;return null==i?0:(Object.entries(s[i]).forEach((([t,e])=>{if(!o.test(t))return;n++;let[,i]=t.slice(6).split("-");r.maxClicks<i&&(r.maxClicks=i)})),n)}exports.pluginClick=function(t,e,n){let{currentContext:o,shortcuts:r}=e,{inAPI:i}=t,a={ev:t.ev,_findTarget:s,_readClickEvent:u,mainDependencies:t,regex:/CLICK\s*\:/i},m={currentContext:o,shortcuts:r,listenOptions:{mouseWait:n.mouseWait?n.mouseWait:320,maxClicks:1,clickTarget:n.clickTarget?n.clickTarget:"click"}};i._normalizeWithPlugins(c);let p=function(t,e){const{ev:n,_findTarget:o,_readClickEvent:r,mainDependencies:i}=t,{listenOptions:s,currentContext:c}=e,{mouseWait:u}=s;let l=null,a=null,m=null,p=null,h=0;function f(){const t=r(a,h),e={target:l,targetProps:l?l.getBoundingClientRect():null,x:a.clientX,y:a.clientY,context:c.name,note:c.note,event:a,dependencies:i.extra,type:"click"};n.emit(t,e),m=null,p=null,l=null,a=null,h=0}function d(n){let r=s.maxClicks;return clearTimeout(m),p?(clearTimeout(p),void(p=setTimeout((()=>p=null),u))):(l=o(t,e,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,h++,h>=r?(f(),void(r>1&&(p=setTimeout((()=>p=null),u)))):void(m=setTimeout(f,u)))}function g(n){let r=s.maxClicks;return clearTimeout(m),p?(clearTimeout(p),void(p=setTimeout((()=>p=null),u))):(l=o(t,e,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,h++,h>=r?(f(),void(r>1&&(p=setTimeout((()=>p=null),u)))):void(m=setTimeout(f,u)))}return{start:function(){e.active||(window.addEventListener("contextmenu",g),document.addEventListener("click",d),e.active=!0)},stop:function(){e.active&&(window.removeEventListener("contextmenu",g),document.removeEventListener("click",d),e.active=!1)}}}(a,m),h=l(a,m);h>0&&p.start();const f={getPrefix:()=>"click",shortcutName:t=>c(t),contextChange:()=>{h=l(a,m),h<1&&p.stop(),h>0&&p.start()},mute:()=>p.stop(),unmute:()=>p.start(),destroy:()=>{p.stop(),m=null,f=null}};return Object.freeze(f),f},exports.pluginKey=function(t,e,s={}){let{currentContext:c,shortcuts:u,exposeShortcut:l}=e,{inAPI:a}=t,m={ev:t.ev,_specialChars:i,_readKeyEvent:o,mainDependencies:t,regex:/KEY\s*\:/i},p={currentContext:c,shortcuts:u,active:!1,listenOptions:{keyWait:s.keyWait?s.keyWait:480,maxSequence:1,keyIgnore:null},streamKeys:!(!s.streamKeys||"function"!=typeof s.streamKeys)&&s.streamKeys,exposeShortcut:l};a._normalizeWithPlugins(n);let h=function(t,e){const{ev:n,_specialChars:o,_readKeyEvent:r,mainDependencies:i}=t,{currentContext:s,streamKeys:c,listenOptions:u}=e,{keyWait:l}=u;let a=[],m=null,p=!0,h=!1;const f=()=>p=!1,d=()=>p=!0,g=()=>h=!0,x=()=>!1===p;function y(){let t=a.map((t=>[t.join("+")]));const e={wait:f,end:d,ignore:g,isWaiting:x,note:s.note,context:s.name,dependencies:i.extra,type:"key"};if(!p){let o=t.at(-1);n.emit(o,e),h&&(t=t.slice(0,-1),h=!1)}if(p){const o=`KEY:${t.join(",")}`;n.emit(o,e),a=[],m=null}}function C(e){if(clearTimeout(m),o.hasOwnProperty(e.code))return a.push(r(e,o)),c&&c({key:e.key,context:s.name,note:s.note,dependencies:t.extra}),u.keyIgnore?(clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):p&&a.length===u.maxSequence?(y(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):void(p?m=setTimeout(y,l):y())}function k(e){if(!o.hasOwnProperty(e.code)){if(clearTimeout(m),c&&c({key:e.key,context:s.name,note:s.note,dependencies:t.extra}),u.keyIgnore)return clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));if(a.push(r(e,o)),p&&a.length===u.maxSequence)return y(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));p?m=setTimeout(y,l):y()}}return{start:function(){e.active||(document.addEventListener("keydown",C),document.addEventListener("keypress",k),e.active=!0)},stop:function(){e.active&&(document.removeEventListener("keydown",C),document.removeEventListener("keypress",k),e.active=!1)}}}(m,p),f=r(m,p);f>0&&h.start();const d={getPrefix:()=>"key",shortcutName:t=>n(t),contextChange:t=>{f=r(m,p),f<1&&h.stop(),f>0&&h.start()},mute:()=>h.stop(),unmute:()=>h.start(),destroy:()=>{h.stop(),p=null,d=null}};return Object.freeze(d),d},exports.shortcuts=function(n={}){const o=t(),r={},i={},s={currentContext:{name:null,note:null},shortcuts:{},plugins:[],exposeShortcut:!(!n.onShortcut||"function"!=typeof n.onShortcut)&&n.onShortcut},c={ev:o,inAPI:r,API:i,extra:{}};return i.enablePlugin=(t,e={})=>{const n=t.name;if(-1===r._systemAction(n,"none")){let n;n=t(c,s,e),s.plugins.push(n)}},i.disablePlugin=t=>{const e=r._systemAction(t,"destroy");-1!==e&&(s.plugins=s.plugins.filter(((t,n)=>n!==e)))},i.mutePlugin=t=>r._systemAction(t,"mute"),i.unmutePlugin=t=>r._systemAction(t,"unmute"),i.getContext=()=>s.currentContext.name,i.getNote=()=>s.currentContext.note,i.setNote=(t=null)=>{"string"!=typeof t&&null!=t||(s.currentContext.note=t)},i.pause=(t="*")=>{let e=r._readShortcutWithPlugins(t);o.stop(e)},i.resume=(t="*")=>{const e=r._readShortcutWithPlugins(t);o.start(e)},i.emit=(t,...e)=>o.emit(r._readShortcutWithPlugins(t),...e),i.listContexts=()=>Object.keys(s.shortcuts),i.setDependencies=t=>c.extra={...c.extra,...t},i.getDependencies=()=>c.extra,Object.entries(e).forEach((([t,e])=>{t.startsWith("_")?r[t]=e(c,s):i[t]=e(c,s)})),i};
@@ -0,0 +1 @@
1
+ import t from"@peter.naydenov/notice";var e={_normalizeWithPlugins:function(t,e){return function(t){const n=e.shortcuts;Object.keys(n).forEach((e=>{Object.entries(n[e]).forEach((([o,r])=>{const i=t(o);i!==o&&(delete n[e][o],n[e][i]=r)}))}))}},_readShortcutWithPlugins:function(t,e){return function(n){const{inAPI:o}=t,r=n.split(":")[0],i=o._systemAction(r,"none");let s=n;return-1!==i&&(s=e.plugins[i].shortcutName(n)),s}},_systemAction:function(t,e){return function(t,n,o=null){return e.plugins.findIndex((e=>e.getPrefix()===t&&(e[n]&&e[n](o),!0)))}},changeContext:function(t,e){const{shortcuts:n,currentContext:o}=e,{ev:r}=t;return function(t=!1){const i=o.name;if(!t)return r.reset(),void(o.name=null);i!==t&&(n[t]?(n[i]&&r.reset(),o.name=t,e.plugins.forEach((e=>e.contextChange(t))),Object.entries(n[t]).forEach((([t,e])=>{e.forEach((e=>r.on(t,e)))})),r.on("*",((...t)=>{e.exposeShortcut&&e.exposeShortcut(...t)}))):r.emit("@shortcuts-error",`Context '${t}' does not exist`))}},listShortcuts:function(t,e){const n=e.shortcuts;return function(t=null){if(null!=t){let e=n[t];return null==e?null:Object.entries(e).map((([t,e])=>t))}return Object.keys(n).map((t=>{let e={};return e.context=t,e.shortcuts=Object.entries(n[t]).map((([t,e])=>t)),e}))}},load:function(t,e){const{shortcuts:n,plugins:o}=e,{API:{changeContext:r,getContext:i}}=t;return function(t){const e=i(),s=o.map((t=>t.getPrefix().toUpperCase()));let c=!1;Object.entries(t).forEach((([t,r])=>{t===e&&(c=!0),n[t]={},Object.entries(r).forEach((([e,r])=>{let i=e,c=e.toUpperCase().trim(),u=s.map(((t,e)=>c.startsWith(t)?e:null)).filter((t=>null!==t));if(u.length){let t=u[0];i=o[t].shortcutName(e)}r instanceof Function&&(r=[r]),n[t][i]=r}))})),c&&(r(),r(e))}},unload:function(t,e){const{currentContext:n,shortcuts:o}=e,{ev:r}=t;return function(t){n.name!==t?o[t]?delete o[t]:r.emit("shortcuts-error",`Context '${t}' does not exist`):r.emit("shortcuts-error",`Context '${t}' can't be removed during is current active context. Change the context first`)}}};function n(t){const e=t.toUpperCase(),n=/KEY\s*\:/i.test(e),o=e.indexOf(":");if(!n)return t;return`KEY:${e.slice(o+1).split(",").map((t=>t.trim())).map((t=>t.split("+").map((t=>t.trim())).sort().join("+"))).join(",")}`}function o(t,e){let{shiftKey:n,altKey:o,ctrlKey:r}=t,i=t.code.replace("Key","").replace("Digit",""),s=[];return r&&s.push("CTRL"),n&&s.push("SHIFT"),o&&s.push("ALT"),e.hasOwnProperty(i)?s.push(e[i].toUpperCase()):["ControlLeft","ControlRight","ShiftLeft","ShiftRight","AltLeft","AltRight","Meta"].includes(i)||s.push(i.toUpperCase()),s.sort()}function r(t,e){let n=0;const{regex:o}=t,{listenOptions:r,currentContext:{name:i},shortcuts:s}=e;return null==i?0:(Object.entries(s[i]).forEach((([t,e])=>{if(!o.test(t))return;n++;let i=t.slice(4).split(",").length;r.maxSequence<i&&(r.maxSequence=i)})),n)}function i(){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"}}function s(t,e,s={}){let{currentContext:c,shortcuts:u,exposeShortcut:l}=e,{inAPI:a}=t,m={ev:t.ev,_specialChars:i,_readKeyEvent:o,mainDependencies:t,regex:/KEY\s*\:/i},p={currentContext:c,shortcuts:u,active:!1,listenOptions:{keyWait:s.keyWait?s.keyWait:480,maxSequence:1,keyIgnore:null},streamKeys:!(!s.streamKeys||"function"!=typeof s.streamKeys)&&s.streamKeys,exposeShortcut:l};a._normalizeWithPlugins(n);let h=function(t,e){const{ev:n,_specialChars:o,_readKeyEvent:r,mainDependencies:i}=t,{currentContext:s,streamKeys:c,listenOptions:u}=e,{keyWait:l}=u;let a=[],m=null,p=!0,h=!1;const f=()=>p=!1,d=()=>p=!0,g=()=>h=!0,x=()=>!1===p;function y(){let t=a.map((t=>[t.join("+")]));const e={wait:f,end:d,ignore:g,isWaiting:x,note:s.note,context:s.name,dependencies:i.extra,type:"key"};if(!p){let o=t.at(-1);n.emit(o,e),h&&(t=t.slice(0,-1),h=!1)}if(p){const o=`KEY:${t.join(",")}`;n.emit(o,e),a=[],m=null}}function C(e){if(clearTimeout(m),o.hasOwnProperty(e.code))return a.push(r(e,o)),c&&c({key:e.key,context:s.name,note:s.note,dependencies:t.extra}),u.keyIgnore?(clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):p&&a.length===u.maxSequence?(y(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):void(p?m=setTimeout(y,l):y())}function k(e){if(!o.hasOwnProperty(e.code)){if(clearTimeout(m),c&&c({key:e.key,context:s.name,note:s.note,dependencies:t.extra}),u.keyIgnore)return clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));if(a.push(r(e,o)),p&&a.length===u.maxSequence)return y(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));p?m=setTimeout(y,l):y()}}return{start:function(){e.active||(document.addEventListener("keydown",C),document.addEventListener("keypress",k),e.active=!0)},stop:function(){e.active&&(document.removeEventListener("keydown",C),document.removeEventListener("keypress",k),e.active=!1)}}}(m,p),f=r(m,p);f>0&&h.start();const d={getPrefix:()=>"key",shortcutName:t=>n(t),contextChange:t=>{f=r(m,p),f<1&&h.stop(),f>0&&h.start()},mute:()=>h.stop(),unmute:()=>h.start(),destroy:()=>{h.stop(),p=null,d=null}};return Object.freeze(d),d}function c(t,e,n){const{listenOptions:{clickTarget:o}}=e;let r=n;return r===document||r===document.body?null:r.dataset[o]||"A"===r.nodeName?r:c(t,e,r.parentNode)}function u(t){const e=t.toUpperCase(),n=/CLICK\s*\:/i.test(e),o=["LEFT","MIDDLE","RIGHT"],r=["ALT","SHIFT","CTRL"];let i=null,s=[],c=0,u=e.indexOf(":");return n?(e.slice(u+1).trim().split("-").map((t=>t.trim())).forEach((t=>{o.includes(t)?i=t:r.includes(t)?s.push(t):isNaN(t)||(c=t)})),`CLICK:${i}-${c}${s.length>0?"-":""}${s.sort().join("-")}`):t}function l(t,e){let{shiftKey:n,altKey:o,ctrlKey:r,key:i,button:s}=t,c=`CLICK:${["LEFT","MIDDLE","RIGHT"][s]}-${e}`,u=[];return r&&u.push("CTRL"),n&&u.push("SHIFT"),o&&u.push("ALT"),u.length>0?`${c}${u.length>0?"-":""}${u.sort().join("-")}`:`${c}`}function a(t,e){let n=0;const{regex:o}=t,{listenOptions:r,currentContext:{name:i},shortcuts:s}=e;return null==i?0:(Object.entries(s[i]).forEach((([t,e])=>{if(!o.test(t))return;n++;let[,i]=t.slice(6).split("-");r.maxClicks<i&&(r.maxClicks=i)})),n)}function m(t,e,n){let{currentContext:o,shortcuts:r}=e,{inAPI:i}=t,s={ev:t.ev,_findTarget:c,_readClickEvent:l,mainDependencies:t,regex:/CLICK\s*\:/i},m={currentContext:o,shortcuts:r,listenOptions:{mouseWait:n.mouseWait?n.mouseWait:320,maxClicks:1,clickTarget:n.clickTarget?n.clickTarget:"click"}};i._normalizeWithPlugins(u);let p=function(t,e){const{ev:n,_findTarget:o,_readClickEvent:r,mainDependencies:i}=t,{listenOptions:s,currentContext:c}=e,{mouseWait:u}=s;let l=null,a=null,m=null,p=null,h=0;function f(){const t=r(a,h),e={target:l,targetProps:l?l.getBoundingClientRect():null,x:a.clientX,y:a.clientY,context:c.name,note:c.note,event:a,dependencies:i.extra,type:"click"};n.emit(t,e),m=null,p=null,l=null,a=null,h=0}function d(n){let r=s.maxClicks;return clearTimeout(m),p?(clearTimeout(p),void(p=setTimeout((()=>p=null),u))):(l=o(t,e,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,h++,h>=r?(f(),void(r>1&&(p=setTimeout((()=>p=null),u)))):void(m=setTimeout(f,u)))}function g(n){let r=s.maxClicks;return clearTimeout(m),p?(clearTimeout(p),void(p=setTimeout((()=>p=null),u))):(l=o(t,e,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,h++,h>=r?(f(),void(r>1&&(p=setTimeout((()=>p=null),u)))):void(m=setTimeout(f,u)))}return{start:function(){e.active||(window.addEventListener("contextmenu",g),document.addEventListener("click",d),e.active=!0)},stop:function(){e.active&&(window.removeEventListener("contextmenu",g),document.removeEventListener("click",d),e.active=!1)}}}(s,m),h=a(s,m);h>0&&p.start();const f={getPrefix:()=>"click",shortcutName:t=>u(t),contextChange:()=>{h=a(s,m),h<1&&p.stop(),h>0&&p.start()},mute:()=>p.stop(),unmute:()=>p.start(),destroy:()=>{p.stop(),m=null,f=null}};return Object.freeze(f),f}function p(n={}){const o=t(),r={},i={},s={currentContext:{name:null,note:null},shortcuts:{},plugins:[],exposeShortcut:!(!n.onShortcut||"function"!=typeof n.onShortcut)&&n.onShortcut},c={ev:o,inAPI:r,API:i,extra:{}};return i.enablePlugin=(t,e={})=>{const n=t.name;if(-1===r._systemAction(n,"none")){let n;n=t(c,s,e),s.plugins.push(n)}},i.disablePlugin=t=>{const e=r._systemAction(t,"destroy");-1!==e&&(s.plugins=s.plugins.filter(((t,n)=>n!==e)))},i.mutePlugin=t=>r._systemAction(t,"mute"),i.unmutePlugin=t=>r._systemAction(t,"unmute"),i.getContext=()=>s.currentContext.name,i.getNote=()=>s.currentContext.note,i.setNote=(t=null)=>{"string"!=typeof t&&null!=t||(s.currentContext.note=t)},i.pause=(t="*")=>{let e=r._readShortcutWithPlugins(t);o.stop(e)},i.resume=(t="*")=>{const e=r._readShortcutWithPlugins(t);o.start(e)},i.emit=(t,...e)=>o.emit(r._readShortcutWithPlugins(t),...e),i.listContexts=()=>Object.keys(s.shortcuts),i.setDependencies=t=>c.extra={...c.extra,...t},i.getDependencies=()=>c.extra,Object.entries(e).forEach((([t,e])=>{t.startsWith("_")?r[t]=e(c,s):i[t]=e(c,s)})),i}export{m as pluginClick,s as pluginKey,p as shortcuts};
@@ -0,0 +1 @@
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).shortcuts={})}(this,(function(e){"use strict";var t={_normalizeWithPlugins:function(e,t){return function(e){const n=t.shortcuts;Object.keys(n).forEach((t=>{Object.entries(n[t]).forEach((([o,r])=>{const i=e(o);i!==o&&(delete n[t][o],n[t][i]=r)}))}))}},_readShortcutWithPlugins:function(e,t){return function(n){const{inAPI:o}=e,r=n.split(":")[0],i=o._systemAction(r,"none");let s=n;return-1!==i&&(s=t.plugins[i].shortcutName(n)),s}},_systemAction:function(e,t){return function(e,n,o=null){return t.plugins.findIndex((t=>t.getPrefix()===e&&(t[n]&&t[n](o),!0)))}},changeContext:function(e,t){const{shortcuts:n,currentContext:o}=t,{ev:r}=e;return function(e=!1){const i=o.name;if(!e)return r.reset(),void(o.name=null);i!==e&&(n[e]?(n[i]&&r.reset(),o.name=e,t.plugins.forEach((t=>t.contextChange(e))),Object.entries(n[e]).forEach((([e,t])=>{t.forEach((t=>r.on(e,t)))})),r.on("*",((...e)=>{t.exposeShortcut&&t.exposeShortcut(...e)}))):r.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,plugins:o}=t,{API:{changeContext:r,getContext:i}}=e;return function(e){const t=i(),s=o.map((e=>e.getPrefix().toUpperCase()));let c=!1;Object.entries(e).forEach((([e,r])=>{e===t&&(c=!0),n[e]={},Object.entries(r).forEach((([t,r])=>{let i=t,c=t.toUpperCase().trim(),u=s.map(((e,t)=>c.startsWith(e)?t:null)).filter((e=>null!==e));if(u.length){let e=u[0];i=o[e].shortcutName(t)}r instanceof Function&&(r=[r]),n[e][i]=r}))})),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 n(e){const t=e.toUpperCase(),n=/KEY\s*\:/i.test(t),o=t.indexOf(":");if(!n)return e;return`KEY:${t.slice(o+1).split(",").map((e=>e.trim())).map((e=>e.split("+").map((e=>e.trim())).sort().join("+"))).join(",")}`}function o(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()}function r(e,t){let n=0;const{regex:o}=e,{listenOptions:r,currentContext:{name:i},shortcuts:s}=t;return null==i?0:(Object.entries(s[i]).forEach((([e,t])=>{if(!o.test(e))return;n++;let i=e.slice(4).split(",").length;r.maxSequence<i&&(r.maxSequence=i)})),n)}function i(){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"}}function s(e,t,n){const{listenOptions:{clickTarget:o}}=t;let r=n;return r===document||r===document.body?null:r.dataset[o]||"A"===r.nodeName?r:s(e,t,r.parentNode)}function c(e){const t=e.toUpperCase(),n=/CLICK\s*\:/i.test(t),o=["LEFT","MIDDLE","RIGHT"],r=["ALT","SHIFT","CTRL"];let i=null,s=[],c=0,u=t.indexOf(":");return n?(t.slice(u+1).trim().split("-").map((e=>e.trim())).forEach((e=>{o.includes(e)?i=e:r.includes(e)?s.push(e):isNaN(e)||(c=e)})),`CLICK:${i}-${c}${s.length>0?"-":""}${s.sort().join("-")}`):e}function u(e,t){let{shiftKey:n,altKey:o,ctrlKey:r,key:i,button:s}=e,c=`CLICK:${["LEFT","MIDDLE","RIGHT"][s]}-${t}`,u=[];return r&&u.push("CTRL"),n&&u.push("SHIFT"),o&&u.push("ALT"),u.length>0?`${c}${u.length>0?"-":""}${u.sort().join("-")}`:`${c}`}function l(e,t){let n=0;const{regex:o}=e,{listenOptions:r,currentContext:{name:i},shortcuts:s}=t;return null==i?0:(Object.entries(s[i]).forEach((([e,t])=>{if(!o.test(e))return;n++;let[,i]=e.slice(6).split("-");r.maxClicks<i&&(r.maxClicks=i)})),n)}e.pluginClick=function(e,t,n){let{currentContext:o,shortcuts:r}=t,{inAPI:i}=e,a={ev:e.ev,_findTarget:s,_readClickEvent:u,mainDependencies:e,regex:/CLICK\s*\:/i},f={currentContext:o,shortcuts:r,listenOptions:{mouseWait:n.mouseWait?n.mouseWait:320,maxClicks:1,clickTarget:n.clickTarget?n.clickTarget:"click"}};i._normalizeWithPlugins(c);let m=function(e,t){const{ev:n,_findTarget:o,_readClickEvent:r,mainDependencies:i}=e,{listenOptions:s,currentContext:c}=t,{mouseWait:u}=s;let l=null,a=null,f=null,m=null,p=0;function h(){const e=r(a,p),t={target:l,targetProps:l?l.getBoundingClientRect():null,x:a.clientX,y:a.clientY,context:c.name,note:c.note,event:a,dependencies:i.extra,type:"click"};n.emit(e,t),f=null,m=null,l=null,a=null,p=0}function d(n){let r=s.maxClicks;return clearTimeout(f),m?(clearTimeout(m),void(m=setTimeout((()=>m=null),u))):(l=o(e,t,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,p++,p>=r?(h(),void(r>1&&(m=setTimeout((()=>m=null),u)))):void(f=setTimeout(h,u)))}function g(n){let r=s.maxClicks;return clearTimeout(f),m?(clearTimeout(m),void(m=setTimeout((()=>m=null),u))):(l=o(e,t,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,p++,p>=r?(h(),void(r>1&&(m=setTimeout((()=>m=null),u)))):void(f=setTimeout(h,u)))}return{start:function(){t.active||(window.addEventListener("contextmenu",g),document.addEventListener("click",d),t.active=!0)},stop:function(){t.active&&(window.removeEventListener("contextmenu",g),document.removeEventListener("click",d),t.active=!1)}}}(a,f),p=l(a,f);p>0&&m.start();const h={getPrefix:()=>"click",shortcutName:e=>c(e),contextChange:()=>{p=l(a,f),p<1&&m.stop(),p>0&&m.start()},mute:()=>m.stop(),unmute:()=>m.start(),destroy:()=>{m.stop(),f=null,h=null}};return Object.freeze(h),h},e.pluginKey=function(e,t,s={}){let{currentContext:c,shortcuts:u,exposeShortcut:l}=t,{inAPI:a}=e,f={ev:e.ev,_specialChars:i,_readKeyEvent:o,mainDependencies:e,regex:/KEY\s*\:/i},m={currentContext:c,shortcuts:u,active:!1,listenOptions:{keyWait:s.keyWait?s.keyWait:480,maxSequence:1,keyIgnore:null},streamKeys:!(!s.streamKeys||"function"!=typeof s.streamKeys)&&s.streamKeys,exposeShortcut:l};a._normalizeWithPlugins(n);let p=function(e,t){const{ev:n,_specialChars:o,_readKeyEvent:r,mainDependencies:i}=e,{currentContext:s,streamKeys:c,listenOptions:u}=t,{keyWait:l}=u;let a=[],f=null,m=!0,p=!1;const h=()=>m=!1,d=()=>m=!0,g=()=>p=!0,y=()=>!1===m;function x(){let e=a.map((e=>[e.join("+")]));const t={wait:h,end:d,ignore:g,isWaiting:y,note:s.note,context:s.name,dependencies:i.extra,type:"key"};if(!m){let o=e.at(-1);n.emit(o,t),p&&(e=e.slice(0,-1),p=!1)}if(m){const o=`KEY:${e.join(",")}`;n.emit(o,t),a=[],f=null}}function C(t){if(clearTimeout(f),o.hasOwnProperty(t.code))return a.push(r(t,o)),c&&c({key:t.key,context:s.name,note:s.note,dependencies:e.extra}),u.keyIgnore?(clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):m&&a.length===u.maxSequence?(x(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):void(m?f=setTimeout(x,l):x())}function k(t){if(!o.hasOwnProperty(t.code)){if(clearTimeout(f),c&&c({key:t.key,context:s.name,note:s.note,dependencies:e.extra}),u.keyIgnore)return clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));if(a.push(r(t,o)),m&&a.length===u.maxSequence)return x(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));m?f=setTimeout(x,l):x()}}return{start:function(){t.active||(document.addEventListener("keydown",C),document.addEventListener("keypress",k),t.active=!0)},stop:function(){t.active&&(document.removeEventListener("keydown",C),document.removeEventListener("keypress",k),t.active=!1)}}}(f,m),h=r(f,m);h>0&&p.start();const d={getPrefix:()=>"key",shortcutName:e=>n(e),contextChange:e=>{h=r(f,m),h<1&&p.stop(),h>0&&p.start()},mute:()=>p.stop(),unmute:()=>p.start(),destroy:()=>{p.stop(),m=null,d=null}};return Object.freeze(d),d},e.shortcuts=function(e={}){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:{},plugins:[],exposeShortcut:!(!e.onShortcut||"function"!=typeof e.onShortcut)&&e.onShortcut},s={ev:n,inAPI:o,API:r,extra:{}};return r.enablePlugin=(e,t={})=>{const n=e.name;if(-1===o._systemAction(n,"none")){let n;n=e(s,i,t),i.plugins.push(n)}},r.disablePlugin=e=>{const t=o._systemAction(e,"destroy");-1!==t&&(i.plugins=i.plugins.filter(((e,n)=>n!==t)))},r.mutePlugin=e=>o._systemAction(e,"mute"),r.unmutePlugin=e=>o._systemAction(e,"unmute"),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="*")=>{let t=o._readShortcutWithPlugins(e);n.stop(t)},r.resume=(e="*")=>{const t=o._readShortcutWithPlugins(e);n.start(t)},r.emit=(e,...t)=>n.emit(o._readShortcutWithPlugins(e),...t),r.listContexts=()=>Object.keys(i.shortcuts),r.setDependencies=e=>s.extra={...s.extra,...e},r.getDependencies=()=>s.extra,Object.entries(t).forEach((([e,t])=>{e.startsWith("_")?o[e]=t(s,i):r[e]=t(s,i)})),r}}));
package/package.json CHANGED
@@ -1,19 +1,24 @@
1
1
  {
2
2
  "name": "@peter.naydenov/shortcuts",
3
- "version": "2.1.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": "3.0.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",
21
+ "build": "rollup -c",
17
22
  "test": "cypress open --component --browser chrome test"
18
23
  },
19
24
  "repository": {
@@ -21,18 +26,26 @@
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.1.0",
38
+ "cypress": "^13.6.6",
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.5"
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
+ ];