@peter.naydenov/shortcuts 3.5.1 → 4.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 (98) hide show
  1. package/API.md +939 -0
  2. package/CODE_OF_CONDUCT.md +84 -0
  3. package/CONTRIBUTING.md +476 -0
  4. package/Changelog.md +30 -1
  5. package/How.to.create.plugins.md +929 -0
  6. package/Migration.guide.md +48 -0
  7. package/README.md +396 -24
  8. package/dist/main.d.ts +54 -2
  9. package/dist/methods/_normalizeWithPlugins.d.ts +63 -1
  10. package/dist/methods/_readShortcutWithPlugins.d.ts +8 -1
  11. package/dist/methods/_setupPlugin.d.ts +9 -0
  12. package/dist/methods/_systemAction.d.ts +8 -1
  13. package/dist/methods/changeContext.d.ts +8 -1
  14. package/dist/methods/index.d.ts +2 -0
  15. package/dist/methods/listShortcuts.d.ts +1 -16
  16. package/dist/methods/load.d.ts +8 -1
  17. package/dist/methods/unload.d.ts +8 -1
  18. package/dist/plugins/click/_findTarget.d.ts +9 -1
  19. package/dist/plugins/click/_listenDOM.d.ts +76 -3
  20. package/dist/plugins/click/_normalizeShortcutName.d.ts +7 -1
  21. package/dist/plugins/click/_registerShortcutEvents.d.ts +26 -0
  22. package/dist/plugins/click/index.d.ts +6 -5
  23. package/dist/plugins/form/_defaults.d.ts +13 -1
  24. package/dist/plugins/form/_listenDOM.d.ts +66 -3
  25. package/dist/plugins/form/_registerShortcutEvents.d.ts +95 -1
  26. package/dist/plugins/form/index.d.ts +2 -3
  27. package/dist/plugins/hover/_findTarget.d.ts +10 -0
  28. package/dist/plugins/hover/_listenDOM.d.ts +68 -0
  29. package/dist/plugins/hover/_normalizeShortcutName.d.ts +2 -0
  30. package/dist/plugins/hover/_registerShortcutEvents.d.ts +28 -0
  31. package/dist/plugins/hover/index.d.ts +14 -0
  32. package/dist/plugins/key/_listenDOM.d.ts +61 -3
  33. package/dist/plugins/key/_registerShortcutEvents.d.ts +26 -0
  34. package/dist/plugins/key/_specialChars.d.ts +6 -31
  35. package/dist/plugins/key/index.d.ts +2 -3
  36. package/dist/plugins/scroll/_listenDOM.d.ts +58 -0
  37. package/dist/plugins/scroll/_normalizeShortcutName.d.ts +2 -0
  38. package/dist/plugins/scroll/_registerShortcutEvents.d.ts +28 -0
  39. package/dist/plugins/scroll/index.d.ts +16 -0
  40. package/dist/shortcuts.cjs +1 -1
  41. package/dist/shortcuts.esm.mjs +1 -1
  42. package/dist/shortcuts.umd.js +1 -1
  43. package/eslint.config.js +80 -0
  44. package/html/assets/index-COTh6lXR.css +1 -0
  45. package/html/assets/index-DOkKC3NI.js +53 -0
  46. package/html/bg.png +0 -0
  47. package/html/favicon.ico +0 -0
  48. package/html/favicon.svg +5 -0
  49. package/html/html.meta.json.gz +0 -0
  50. package/html/index.html +32 -0
  51. package/package.json +16 -12
  52. package/shortcuts.png +0 -0
  53. package/src/main.js +52 -22
  54. package/src/methods/_normalizeWithPlugins.js +26 -2
  55. package/src/methods/_readShortcutWithPlugins.js +9 -2
  56. package/src/methods/_setupPlugin.js +93 -0
  57. package/src/methods/_systemAction.js +12 -4
  58. package/src/methods/changeContext.js +11 -3
  59. package/src/methods/index.js +2 -0
  60. package/src/methods/listShortcuts.js +5 -12
  61. package/src/methods/load.js +11 -4
  62. package/src/methods/unload.js +8 -1
  63. package/src/plugins/click/_findTarget.js +11 -5
  64. package/src/plugins/click/_listenDOM.js +58 -20
  65. package/src/plugins/click/_normalizeShortcutName.js +11 -4
  66. package/src/plugins/click/_readClickEvent.js +1 -1
  67. package/src/plugins/click/_registerShortcutEvents.js +33 -5
  68. package/src/plugins/click/index.js +34 -51
  69. package/src/plugins/form/_defaults.js +13 -3
  70. package/src/plugins/form/_listenDOM.js +46 -9
  71. package/src/plugins/form/_normalizeShortcutName.js +2 -2
  72. package/src/plugins/form/_registerShortcutEvents.js +93 -17
  73. package/src/plugins/form/index.js +26 -47
  74. package/src/plugins/hover/_findTarget.js +26 -0
  75. package/src/plugins/hover/_listenDOM.js +154 -0
  76. package/src/plugins/hover/_normalizeShortcutName.js +21 -0
  77. package/src/plugins/hover/_registerShortcutEvents.js +51 -0
  78. package/src/plugins/hover/index.js +71 -0
  79. package/src/plugins/key/_listenDOM.js +67 -33
  80. package/src/plugins/key/_normalizeShortcutName.js +4 -3
  81. package/src/plugins/key/_readKeyEvent.js +1 -1
  82. package/src/plugins/key/_registerShortcutEvents.js +34 -5
  83. package/src/plugins/key/_specialChars.js +5 -0
  84. package/src/plugins/key/index.js +35 -50
  85. package/src/plugins/scroll/_listenDOM.js +141 -0
  86. package/src/plugins/scroll/_normalizeShortcutName.js +21 -0
  87. package/src/plugins/scroll/_registerShortcutEvents.js +50 -0
  88. package/src/plugins/scroll/index.js +61 -0
  89. package/test/01-general.test.js +92 -23
  90. package/test/02-key.test.js +241 -40
  91. package/test/03-click.test.js +291 -47
  92. package/test/04-form.test.js +241 -47
  93. package/test/05-hover.test.js +463 -0
  94. package/test/06-scroll.test.js +374 -0
  95. package/test-helpers/Block.jsx +3 -2
  96. package/test-helpers/style.css +6 -1
  97. package/vitest.config.js +13 -11
  98. package/How..to.make.plugins.md +0 -41
package/html/bg.png ADDED
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ <svg width="165" height="165" viewBox="0 0 165 165" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M120.831 57.2543L84.693 109.505C84.3099 110.059 83.7558 110.474 83.1148 110.687C82.4738 110.9 81.7809 110.898 81.1412 110.684C80.5015 110.469 79.95 110.052 79.5702 109.497C79.1905 108.941 79.0032 108.277 79.037 107.606L80.4833 78.7582L57.1343 73.8064C56.6353 73.7007 56.1704 73.474 55.7807 73.1465C55.391 72.8191 55.0885 72.4009 54.9001 71.929C54.7117 71.4571 54.6432 70.9461 54.7006 70.4412C54.758 69.9364 54.9395 69.4532 55.2291 69.0345L91.3675 16.7837C91.7507 16.2294 92.3048 15.8145 92.9458 15.6018C93.5869 15.3891 94.2798 15.3902 94.9196 15.6051C95.5593 15.8199 96.1109 16.2367 96.4906 16.7923C96.8703 17.3478 97.0575 18.0117 97.0236 18.6833L95.5773 47.5314L118.926 52.4828C119.425 52.5885 119.89 52.8152 120.28 53.1426C120.67 53.4701 120.972 53.8883 121.16 54.3602C121.349 54.8321 121.417 55.3431 121.36 55.8479C121.303 56.3528 121.121 56.836 120.831 57.2547L120.831 57.2543Z" fill="#FCC72B"/>
3
+ <path d="M82.9866 153.343C82.0254 153.344 81.0735 153.156 80.1855 152.788C79.2975 152.42 78.4909 151.88 77.8122 151.2L43.6658 117.056C42.2998 115.683 41.5341 113.824 41.5366 111.887C41.5392 109.95 42.3098 108.092 43.6796 106.723C45.0493 105.353 46.9064 104.582 48.8435 104.579C50.7807 104.577 52.6399 105.342 54.0134 106.708L82.9866 135.678L146.105 72.5626C147.481 71.2088 149.336 70.4536 151.266 70.4615C153.197 70.4693 155.046 71.2396 156.41 72.6045C157.775 73.9695 158.546 75.8184 158.554 77.7487C158.561 79.679 157.806 81.5342 156.452 82.9101L88.1597 151.2C87.4811 151.881 86.6747 152.42 85.7869 152.788C84.8992 153.156 83.9475 153.344 82.9866 153.343Z" fill="#729B1B"/>
4
+ <path d="M82.9572 153.343C83.9184 153.344 84.8703 153.156 85.7583 152.788C86.6463 152.42 87.4528 151.88 88.1316 151.2L122.278 117.056C123.644 115.683 124.41 113.824 124.407 111.887C124.405 109.95 123.634 108.092 122.264 106.723C120.894 105.353 119.037 104.582 117.1 104.579C115.163 104.577 113.304 105.342 111.93 106.708L82.9572 135.678L19.8389 72.5626C18.4629 71.2088 16.6077 70.4536 14.6775 70.4615C12.7472 70.4693 10.8982 71.2396 9.53331 72.6045C8.16839 73.9695 7.39811 75.8184 7.39025 77.7487C7.38239 79.679 8.13759 81.5342 9.49135 82.9101L77.784 151.2C78.4627 151.881 79.2691 152.42 80.1568 152.788C81.0446 153.156 81.9963 153.344 82.9572 153.343Z" fill="#729B1B" fill-opacity="0.5"/>
5
+ </svg>
Binary file
@@ -0,0 +1,32 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" href="./favicon.ico" sizes="48x48">
6
+ <link rel="icon" href="./favicon.svg" sizes="any" type="image/svg+xml">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+ <title>Vitest</title>
9
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
11
+ <link
12
+ href="https://fonts.googleapis.com/css2?family=Readex+Pro:wght@300;400&display=swap"
13
+ rel="stylesheet"
14
+ />
15
+ <script>
16
+ (function () {
17
+ const prefersDark =
18
+ window.matchMedia &&
19
+ window.matchMedia("(prefers-color-scheme: dark)").matches;
20
+ const setting = localStorage.getItem("vueuse-color-scheme") || "auto";
21
+ if (setting === "dark" || (prefersDark && setting !== "light"))
22
+ document.documentElement.classList.toggle("dark", true);
23
+ })();
24
+ </script>
25
+ <script>window.METADATA_PATH="html.meta.json.gz"</script>
26
+ <script type="module" src="./assets/index-DOkKC3NI.js"></script>
27
+ <link rel="stylesheet" href="./assets/index-COTh6lXR.css">
28
+ </head>
29
+ <body>
30
+ <div id="app"></div>
31
+ </body>
32
+ </html>
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@peter.naydenov/shortcuts",
3
3
  "description": "Context control of shortcuts based on keyboard and mouse events",
4
- "version": "3.5.1",
4
+ "version": "4.0.0",
5
5
  "license": "MIT",
6
6
  "author": "Peter Naydenov",
7
- "main": "./dist/shortcuts.umd.js",
7
+ "main": "./src/main.js",
8
8
  "types": "./dist/main.d.ts",
9
9
  "type": "module",
10
10
  "exports": {
@@ -24,7 +24,8 @@
24
24
  "test": "vitest",
25
25
  "test:ui": "vitest --ui --open",
26
26
  "cover": "vitest run --coverage",
27
- "types": "tsc"
27
+ "types": "tsc",
28
+ "lint": "eslint src/ test/*.js"
28
29
  },
29
30
  "repository": {
30
31
  "type": "git",
@@ -34,24 +35,27 @@
34
35
  "@peter.naydenov/notice": "^2.4.1"
35
36
  },
36
37
  "devDependencies": {
37
- "@peter.naydenov/visual-controller-for-react": "^3.0.1",
38
- "@rollup/plugin-commonjs": "^28.0.8",
38
+ "@peter.naydenov/visual-controller-for-react": "^3.0.2",
39
+ "@rollup/plugin-commonjs": "^29.0.0",
39
40
  "@rollup/plugin-node-resolve": "^16.0.3",
40
41
  "@rollup/plugin-terser": "^0.4.4",
41
42
  "@testing-library/dom": "^10.4.1",
42
- "@vitejs/plugin-react": "^5.0.4",
43
- "@vitest/browser": "^3.2.4",
44
- "@vitest/coverage-v8": "^3.2.4",
45
- "@vitest/ui": "^3.2.4",
46
- "ask-for-promise": "^3.0.2",
43
+ "@testing-library/user-event": "^14.6.1",
44
+ "@vitejs/plugin-react": "^5.1.0",
45
+ "@vitest/browser": "^4.0.8",
46
+ "@vitest/browser-playwright": "^4.0.8",
47
+ "@vitest/coverage-v8": "^4.0.8",
48
+ "@vitest/ui": "^4.0.8",
49
+ "ask-for-promise": "^3.1.0",
47
50
  "chai": "^6.2.0",
51
+ "eslint": "^9.39.1",
48
52
  "mocha": "^11.7.4",
49
53
  "playwright": "^1.56.1",
50
54
  "react": "^19.2.0",
51
55
  "react-dom": "^19.2.0",
52
56
  "typescript": "^5.9.3",
53
- "vite": "^7.1.11",
54
- "vitest": "^3.2.4"
57
+ "vite": "^7.2.2",
58
+ "vitest": "^4.0.8"
55
59
  },
56
60
  "keywords": [
57
61
  "shortcut",
package/shortcuts.png ADDED
Binary file
package/src/main.js CHANGED
@@ -1,5 +1,22 @@
1
1
  'use strict'
2
2
 
3
+ /**
4
+ * @typedef {Object} dependencies
5
+ * @property {Object} ev - Event emitter instance
6
+ * @property {Object} inAPI - Internal API object
7
+ * @property {Object} API - Public API object
8
+ * @property {Object} extra - Extra dependencies object
9
+ */
10
+
11
+ /**
12
+ * @typedef {Object} state
13
+ * @property {Object} currentContext - Current context data container with name and note properties
14
+ * @property {Object} shortcuts - Shortcuts object: { contextName : { shortcut : callback[] } }
15
+ * @property {Array} plugins - Array of active plugins
16
+ * @property {Function|null} exposeShortcut - Keyboard shortcut log function
17
+ * @property {string} ERROR_EVENT_NAME - Name for error events
18
+ */
19
+
3
20
  /**
4
21
  * @typedef {Object} PluginAPI
5
22
  * @property {function(): string} getPrefix - Get plugin prefix
@@ -10,9 +27,15 @@
10
27
  * @property {function(): void} destroy - Destroy the plugin
11
28
  */
12
29
 
30
+ /**
31
+ * @typedef {Object} contextShortcuts
32
+ * @property {string} context - Context name
33
+ * @property {string[]} shortcuts - List of shortcuts in a context
34
+ */
35
+
13
36
  /**
14
37
  * @typedef {Object} ShortcutsAPI
15
- * @property {function(function, Object): void} enablePlugin - Enable a plugin
38
+ * @property {function(Function, Object): void} enablePlugin - Enable a plugin
16
39
  * @property {function(string): void} disablePlugin - Disable a plugin
17
40
  * @property {function(string): number} mutePlugin - Mute a plugin
18
41
  * @property {function(string): number} unmutePlugin - Unmute a plugin
@@ -28,7 +51,7 @@
28
51
  * @property {function(): Object} getDependencies - Get external dependencies
29
52
  * @property {function(): void} reset - Reset the library instance
30
53
  * @property {function(string|boolean): void} changeContext - Change current context
31
- * @property {function(string|null): string[]|Object[]} listShortcuts - List shortcuts
54
+ * @property {function(string|null): string[]|contextShortcuts[]|null} listShortcuts - List shortcuts
32
55
  * @property {function(Object): void} load - Load shortcuts into contexts
33
56
  * @property {function(string): void} unload - Unload a context
34
57
  */
@@ -55,9 +78,11 @@ import notice from '@peter.naydenov/notice' // Docs: https://github.com/Peter
55
78
  import methods from './methods/index.js'
56
79
 
57
80
  // Plugins
58
- import pluginKey from './plugins/key/index.js'
59
- import pluginClick from './plugins/click/index.js'
60
- import pluginForm from './plugins/form/index.js'
81
+ import pluginKey from './plugins/key/index.js'
82
+ import pluginClick from './plugins/click/index.js'
83
+ import pluginForm from './plugins/form/index.js'
84
+ import pluginHover from './plugins/hover/index.js'
85
+ import pluginScroll from './plugins/scroll/index.js'
61
86
 
62
87
 
63
88
 
@@ -72,12 +97,10 @@ import pluginForm from './plugins/form/index.js'
72
97
  * @returns {ShortcutsAPI} The shortcuts API
73
98
  */
74
99
  function main ( options = {} ) {
75
- let
100
+ const
76
101
  inAPI = {} // API for internal methods
77
102
  , API = {} // API for public methods
78
- ;
79
- const
80
- ev = notice () // Event emitter instance
103
+ , ev = notice () // Event emitter instance
81
104
  , state = {
82
105
  currentContext : { name: null, note: null } // Context data container
83
106
  , shortcuts : {} // shortcuts = { contextName : { shortcut : callback[] } }
@@ -86,35 +109,37 @@ function main ( options = {} ) {
86
109
  , ERROR_EVENT_NAME : ( options.errorEventName ) ? options.errorEventName : '@shortcuts-error'
87
110
  } // state
88
111
  ;
89
- let dependencies = {
112
+ const dependencies = {
90
113
  ev
91
114
  , inAPI
92
115
  , API
93
116
  , extra : {}
94
117
  };
95
118
 
96
-
97
-
98
119
  // ---------------------- > PLUGIN METHODS < ---------------------- //
99
120
  /**
100
121
  * @function enablePlugin
101
122
  * @description Enable a plugin
123
+ * @param {Function} plugin - Plugin function to enable
124
+ * @param {Object} [options={}] - Plugin configuration options
102
125
  * @returns {void}
103
126
  */
104
127
  API.enablePlugin = ( plugin, options={}) => {
105
128
  if ( typeof plugin !== 'function' ) return
106
- let plugApp = plugin ( dependencies, state, options )
129
+ const setupPlugin = inAPI._setupPlugin
130
+ const plugApp = plugin ( setupPlugin, options )
107
131
  const
108
132
  name = plugApp.getPrefix ()
109
133
  , ix = inAPI._systemAction ( name, 'none' )
110
134
  ;
111
-
112
135
  if ( ix === -1 ) { // If plugin is not registered
113
136
  // Started instance of the plugin
114
137
  state.plugins.push ( plugApp )
138
+ plugApp.unmute ()
115
139
  }
116
140
  else {
117
141
  plugApp.destroy ()
142
+ state.plugins[ix].unmute ()
118
143
  }
119
144
  } // enable func.
120
145
 
@@ -123,25 +148,28 @@ function main ( options = {} ) {
123
148
  /**
124
149
  * @function disablePlugin
125
150
  * @description Disable a plugin
151
+ * @param {string} pluginName - Name of the plugin to disable
126
152
  * @returns {void}
127
153
  */
128
154
  API.disablePlugin = pluginName => {
129
155
  const ix = inAPI._systemAction ( pluginName, 'destroy' );
130
- if ( ix !== -1 ) state.plugins = state.plugins.filter ( (plugin, i) => i !== ix )
156
+ if ( ix !== -1 ) state.plugins.splice ( ix, 1 )
131
157
  } // disable func.
132
158
 
133
159
 
134
160
  /**
135
161
  * @function mutePlugin
136
162
  * @description Mute a plugin
137
- * @returns number - Index of the plugin in the plugins array ( -1 if not found ).
163
+ * @param {string} pluginName - Name of the plugin to mute
164
+ * @returns {number} - Index of the plugin in the plugins array ( -1 if not found )
138
165
  */
139
166
  API.mutePlugin = pluginName => inAPI._systemAction ( pluginName, 'mute' )
140
167
 
141
168
  /**
142
- * @function unmute
169
+ * @function unmutePlugin
143
170
  * @description Unmute a plugin
144
- * @returns number - Index of the plugin in the plugins array ( -1 if not found ).
171
+ * @param {string} pluginName - Name of the plugin to unmute
172
+ * @returns {number} - Index of the plugin in the plugins array ( -1 if not found )
145
173
  */
146
174
  API.unmutePlugin = pluginName => inAPI._systemAction ( pluginName, 'unmute' )
147
175
 
@@ -185,7 +213,7 @@ function main ( options = {} ) {
185
213
  * @returns {void}
186
214
  */
187
215
  API.pause = (name='*') => {
188
- let pausedEvent = inAPI._readShortcutWithPlugins ( name );
216
+ const pausedEvent = inAPI._readShortcutWithPlugins ( name );
189
217
  ev.stop ( pausedEvent )
190
218
  }
191
219
 
@@ -222,15 +250,15 @@ function main ( options = {} ) {
222
250
  /**
223
251
  * @function setDependencies
224
252
  * @description Set a dependency package that will be provided to each action function
225
- * @param {object} deps - Enumerate external dependencies
253
+ * @param {Object} deps - Enumerate external dependencies
226
254
  * @returns {void}
227
255
  */
228
- API.setDependencies = deps => dependencies.extra = { ...dependencies.extra, ...deps }
256
+ API.setDependencies = deps => Object.assign ( dependencies.extra, deps )
229
257
 
230
258
  /**
231
259
  * @function getDependencies
232
260
  * @description Get a dependency package that will be provided to each action function
233
- * @returns {object} - Enumerate external dependencies
261
+ * @returns {Object} - Enumerate external dependencies
234
262
  **/
235
263
  API.getDependencies = () => dependencies.extra
236
264
 
@@ -267,6 +295,8 @@ export {
267
295
  pluginKey
268
296
  , pluginClick
269
297
  , pluginForm
298
+ , pluginHover
299
+ , pluginScroll
270
300
  }
271
301
 
272
302
 
@@ -1,8 +1,32 @@
1
- function _normalizeWithPlugins ( dependencies, state ) {
1
+ /**
2
+ * @typedef {Object} dependencies
3
+ * @property {Object} ev - Event emitter instance
4
+ * @property {Object} inAPI - Internal API object
5
+ * @property {Object} API - Public API object
6
+ * @property {Object} extra - Extra dependencies object
7
+ */
8
+
9
+ /**
10
+ * @typedef {Object} state
11
+ * @property {Object} currentContext - Current context data container
12
+ * @property {Object} shortcuts - Shortcuts object: { contextName : { shortcut : callback[] } }
13
+ * @property {Array} plugins - Array of active plugins
14
+ * @property {Function|null} exposeShortcut - Keyboard shortcut log function
15
+ * @property {string} ERROR_EVENT_NAME - Name for error events
16
+ */
17
+
2
18
  /**
3
19
  * @function _normalizeWithPlugins
4
20
  * @description Function used by plugins during the enable process to normalize the existing and related to the plugin shortcut names.
5
- * @param {function} _normalizeShortcutName - Plugin internal 'normalize' function.
21
+ * @param {dependencies} dependencies - Dependencies object containing inAPI
22
+ * @param {state} state - State object containing shortcuts
23
+ * @returns {function} - Returns a function that takes a normalize function
24
+ */
25
+ function _normalizeWithPlugins ( dependencies, state ) {
26
+ /**
27
+ * @function _normalizeWithPlugins
28
+ * @description Normalize shortcut names across all contexts using plugin's normalize function
29
+ * @param {function} _normalizeShortcutName - Plugin internal 'normalize' function
6
30
  * @returns {void}
7
31
  */
8
32
  return function _normalizeWithPlugins ( _normalizeShortcutName ) {
@@ -1,10 +1,17 @@
1
1
  'use strict'
2
+ /**
3
+ * @function _readShortcutWithPlugins
4
+ * @description Searches for belonging plugin and call the plugin method to normalize the shortcut name.
5
+ * @param {dependencies} dependencies - Dependencies object containing inAPI
6
+ * @param {state} state - State object containing plugins
7
+ * @returns {function} - Returns a function that processes shortcut names
8
+ */
2
9
  function _readShortcutWithPlugins ( dependencies, state ) {
3
10
  /**
4
11
  * @function _readShortcutWithPlugins
5
12
  * @description Searches for belonging plugin and call the plugin method to normalize the shortcut name.
6
- * @param {string} shortcut - The shortcut to read.
7
- * @returns {string} - The normalized shortcut name.
13
+ * @param {string} shortcut - The shortcut to read
14
+ * @returns {string} - The normalized shortcut name
8
15
  */
9
16
  return function _readShortcutWithPlugins ( shortcut ) {
10
17
  const
@@ -0,0 +1,93 @@
1
+ /**
2
+ * @function _setupPlugin
3
+ * @description Setup a plugin with provided settings and dependencies
4
+ * @param {dependencies} dependencies - Dependencies object containing ev, extra, inAPI, API
5
+ * @param {state} state - State object containing currentContext, shortcuts, exposeShortcut, ERROR_EVENT_NAME
6
+ * @returns {function} - Returns a function that takes plugin settings and returns plugin API
7
+ */
8
+ function _setupPlugin ( dependencies, state ) {
9
+
10
+ const { inAPI } = dependencies;
11
+
12
+ const {
13
+ currentContext
14
+ , shortcuts
15
+ , exposeShortcut
16
+ , ERROR_EVENT_NAME
17
+ } = state
18
+
19
+ /**
20
+ * @function _setupPlugin
21
+ * @description Setup a plugin with provided settings
22
+ * @param {Object} settings - Plugin configuration object
23
+ * @param {string} settings.prefix - Plugin prefix
24
+ * @param {function} settings._normalizeShortcutName - Normalize shortcut plugin method
25
+ * @param {function} settings._registerShortcutEvents - Register shortcut plugin method
26
+ * @param {function} settings._listenDOM - DOM listener plugin method
27
+ * @param {Object} settings.pluginState - Plugin state object
28
+ * @param {Object} settings.deps - Plugin dependencies object
29
+ * @returns {PluginAPI} - Plugin API object with methods
30
+ */
31
+ return function _setupPlugin ( settings ) {
32
+ const {
33
+ prefix // Plugin prefix (string)
34
+ , _normalizeShortcutName // Normalize shortcut plugin method (function)
35
+ , _registerShortcutEvents // Register shortcut plugin method (function)
36
+ , _listenDOM // DOM listener plugin method (function)
37
+
38
+ , pluginState // Plugin state (object)
39
+ , deps // Plugin dependencies (object)
40
+ } = settings
41
+ , { resetState } = deps // Reset plugin state (function)
42
+ ;
43
+
44
+ pluginState.currentContext = currentContext
45
+ pluginState.shortcuts = shortcuts
46
+ pluginState.exposeShortcut = exposeShortcut
47
+ pluginState.ERROR_EVENT_NAME = ERROR_EVENT_NAME
48
+
49
+
50
+
51
+ const plugDeps = {
52
+ ev: dependencies.ev
53
+ , extra: dependencies.extra
54
+ , ...deps
55
+ }
56
+
57
+ // Read shortcuts names from all context entities and normalize entries related to the plugin
58
+ inAPI._normalizeWithPlugins ( _normalizeShortcutName )
59
+
60
+ let countShortcuts = _registerShortcutEvents ( plugDeps, pluginState );
61
+ const listener = _listenDOM ( plugDeps, pluginState ) // DOM listener object with 'start' and 'stop' methods
62
+ if ( countShortcuts > 0 ) listener.start ()
63
+
64
+ const pluginAPI = {
65
+ getPrefix : () => prefix
66
+ , shortcutName : key => { // Format a key string according plugin needs
67
+ return _normalizeShortcutName ( key )
68
+ }
69
+ , contextChange : () => {
70
+ resetState ()
71
+ countShortcuts = _registerShortcutEvents ( plugDeps, pluginState )
72
+ if ( countShortcuts < 1 ) { // Remove DOM listener if there are no shortcuts in the current context
73
+ listener.stop ()
74
+ }
75
+ if ( countShortcuts > 0 ) { // Add DOM listener if there are shortcuts in the current context
76
+ listener.start ()
77
+ }
78
+ }
79
+ , mute : () => listener.stop ()
80
+ , unmute : () => listener.start ()
81
+ , destroy : () => {
82
+ listener.stop ()
83
+ resetState ()
84
+ }
85
+ }; // pluginAPI
86
+ Object.freeze ( pluginAPI )
87
+ return pluginAPI
88
+ }} // _setupPlugin
89
+
90
+
91
+ export default _setupPlugin
92
+
93
+
@@ -1,12 +1,20 @@
1
1
  'use strict'
2
2
 
3
+ /**
4
+ * @function _systemAction
5
+ * @description Call a specific plugin method.
6
+ * @param {dependencies} dependencies - Dependencies object containing inAPI
7
+ * @param {state} state - State object containing plugins array
8
+ * @returns {function} - Returns a function that executes plugin actions
9
+ */
3
10
  function _systemAction ( dependencies, state ) {
4
11
  /**
5
12
  * @function _systemAction
6
- * @description Call a specifc plugin method.
7
- * @param {string} pluginName - The name of the plugin.
8
- * @param {string} fn - The name of the method to call.
9
- * @param {any} params - The parameters to pass to the method.
13
+ * @description Call a specific plugin method.
14
+ * @param {string} pluginName - The name of the plugin
15
+ * @param {string} fn - The name of the method to call
16
+ * @param {any} [params=null] - The parameters to pass to the method
17
+ * @returns {number} - Index of the plugin in the plugins array (-1 if not found)
10
18
  */
11
19
  return function _systemAction ( pluginName, fn, params=null ) { // Specific plugin command: Mute, unmute, pause, resume, destroy
12
20
  return state.plugins.findIndex ( plugin => {
@@ -1,5 +1,12 @@
1
1
  'use strict'
2
2
 
3
+ /**
4
+ * @function changeContext
5
+ * @description Change current context with shortcuts belonging to it
6
+ * @param {dependencies} dependencies - Dependencies object containing ev
7
+ * @param {state} state - State object containing shortcuts, currentContext, ERROR_EVENT_NAME
8
+ * @returns {function} - Returns a function that changes context
9
+ */
3
10
  function changeContext ( dependencies, state ) {
4
11
  const
5
12
  {
@@ -22,8 +29,8 @@ function expose () {
22
29
 
23
30
  /**
24
31
  * @function changeContext
25
- * @description Change current context with shortcuts belonging to it.
26
- * @param {string} [contextName=false] - Name of context to change to. Default 'false' will switch off all shortcuts.
32
+ * @description Change current context with shortcuts belonging to it
33
+ * @param {string|boolean} [contextName=false] - Name of context to change to. Default 'false' will switch off all shortcuts
27
34
  * @returns {void}
28
35
  */
29
36
  return function changeContext ( contextName = false ) {
@@ -45,9 +52,10 @@ return function changeContext ( contextName = false ) {
45
52
  ev.reset () // Disable all shortcuts from current context
46
53
  }
47
54
 
48
- currentContext.name = contextName
55
+ currentContext.name = contextName
49
56
  state.plugins.forEach ( plugin => plugin.contextChange ( contextName ) ) // Inform plugins for context change
50
57
  Object.entries ( shortcuts[contextName] ).forEach ( ([shortcutName, list ]) => { // Enable new context shortcuts
58
+ if ( shortcutName.includes (':SETUP') ) return
51
59
  list.forEach ( fn => ev.on ( shortcutName, fn ) )
52
60
  })
53
61
  expose ()
@@ -6,6 +6,7 @@ import load from './load.js'
6
6
  import unload from './unload.js'
7
7
  import changeContext from './changeContext.js'
8
8
  import listShortcuts from './listShortcuts.js'
9
+ import _setupPlugin from './_setupPlugin.js'
9
10
 
10
11
 
11
12
 
@@ -13,6 +14,7 @@ export default {
13
14
  // Internal methods
14
15
  _normalizeWithPlugins
15
16
  , _readShortcutWithPlugins
17
+ , _setupPlugin
16
18
  , _systemAction
17
19
 
18
20
  // Public methods
@@ -1,32 +1,25 @@
1
1
  'use strict'
2
- /**
3
- * @typedef {object} contextShortcuts
4
- * @property {string} context - Context name
5
- * @property {string[]} shortcuts - List of shortcuts in a context
6
- */
7
-
8
-
9
2
 
10
3
  function listShortcuts ( dependencies, state ) {
11
4
  const shortcuts = state.shortcuts;
12
5
  /**
13
6
  * @function listShortcuts
14
- * @description List all shortcuts in all contexts or in a specific context.
15
- * @param {string}[ contextName=null] - List of shortcuts for provided context name. (optional)
16
- * @returns {string[]|contextShortcuts[]} - List of shortcuts for a specified context or list of contextShortcuts for all contexts.
7
+ * @description List all shortcuts in all contexts or in a specific context
8
+ * @param {string|null} [contextName=null] - List of shortcuts for provided context name (optional)
9
+ * @returns {string[]|contextShortcuts[]|null} - List of shortcuts for a specified context, list of contextShortcuts for all contexts, or null if context doesn't exist
17
10
  */
18
11
  return function listShortcuts ( contextName=null ) {
19
12
 
20
13
  // Create a list of shortcuts for a specific context:
21
14
  if ( contextName != null ) {
22
- let context = shortcuts[contextName];
15
+ const context = shortcuts[contextName];
23
16
  if ( context == null ) return null
24
17
  return Object.entries ( context ).map ( ([shortcut, callbacks]) => shortcut )
25
18
  }
26
19
 
27
20
  // Create a list of allShortcuts for each context:
28
21
  return Object.keys ( shortcuts ).map ( contextName => {
29
- let result = {};
22
+ const result = {};
30
23
  result['context'] = contextName
31
24
  result['shortcuts'] = Object.entries ( shortcuts[contextName] ).map ( ([shortcut, callbacks]) => shortcut )
32
25
  return result
@@ -1,5 +1,12 @@
1
1
  'use strict'
2
2
 
3
+ /**
4
+ * @function load
5
+ * @description Load a context with shortcuts object
6
+ * @param {dependencies} dependencies - Dependencies object containing API with changeContext and getContext
7
+ * @param {state} state - State object containing shortcuts and plugins
8
+ * @returns {function} - Returns a function that loads shortcuts
9
+ */
3
10
  function load ( dependencies, state ) {
4
11
  const
5
12
  { shortcuts, plugins } = state
@@ -27,11 +34,11 @@ return function load ( shortcutsUpdate ) {
27
34
  Object.entries ( contextShortcuts ).forEach ( ([ title, payload ]) => {
28
35
  let
29
36
  name = title
30
- , test = title.toUpperCase().trim()
31
- ;
32
- let pluginIndexList = pluginPrefixList.map ( (prefix,i) => test.startsWith ( prefix ) ? i : null ).filter ( i => i !== null );
37
+ ; const test = title.toUpperCase().trim()
38
+
39
+ const pluginIndexList = pluginPrefixList.map ( (prefix,i) => test.startsWith ( prefix ) ? i : null ).filter ( i => i !== null );
33
40
  if ( pluginIndexList.length ) {
34
- let id = pluginIndexList[0];
41
+ const id = pluginIndexList[0];
35
42
  name = plugins[id].shortcutName ( title )
36
43
  }
37
44
  if ( payload instanceof Function ) payload = [ payload ]
@@ -1,5 +1,12 @@
1
1
  'use strict'
2
2
 
3
+ /**
4
+ * @function unload
5
+ * @description Unload a non-active context with shortcuts
6
+ * @param {dependencies} dependencies - Dependencies object containing ev
7
+ * @param {state} state - State object containing currentContext, shortcuts, ERROR_EVENT_NAME
8
+ * @returns {function} - Returns a function that unloads contexts
9
+ */
3
10
  function unload ( dependencies, state ) {
4
11
  const
5
12
  { currentContext, shortcuts, ERROR_EVENT_NAME } = state
@@ -7,7 +14,7 @@ const
7
14
  ;
8
15
  /**
9
16
  * @function unload
10
- * @description Unload a non-active context with shortcuts.
17
+ * @description Unload a non-active context with shortcuts
11
18
  * @param {string} contextName - Context name to unload
12
19
  * @returns {void}
13
20
  */
@@ -1,15 +1,21 @@
1
1
  'use strict'
2
2
 
3
+ /**
4
+ * @function _findTarget
5
+ * @description Find the appropriate click target element by checking if element has any of the target attributes
6
+ * @param {Object} dependencies - Dependencies object
7
+ * @param {Object} state - Plugin state containing listenOptions with clickTarget array
8
+ * @param {Element} target - DOM element to start searching from
9
+ * @returns {Element|null} - Target element or null if not found
10
+ */
3
11
  function _findTarget ( dependencies, state, target ) {
4
-
5
12
  const { listenOptions : {clickTarget}} = state;
6
13
 
7
- let t = target;
8
- if ( t === document ) return null
14
+ const t = target;
9
15
  if ( t === document.body ) return null
10
16
 
11
- if ( t.dataset[clickTarget] ) return t
12
- if ( t.nodeName === 'A' ) return t
17
+ const found = clickTarget.some ( attr => ( t.hasAttribute ( attr ) ) )
18
+ if ( found ) return t
13
19
  return _findTarget ( dependencies, state, t.parentNode )
14
20
  } // _findTarget func.
15
21