@cxl/keyboard 0.1.0 → 1.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.
package/README.md CHANGED
@@ -6,7 +6,7 @@ Keyboard Handling Library
6
6
 
7
7
  ## Project Details
8
8
 
9
- - Branch Version: [0.1.0](https://npmjs.com/package/@cxl/keyboard/v/0.1.0)
9
+ - Branch Version: [1.0.0](https://npmjs.com/package/@cxl/keyboard/v/1.0.0)
10
10
  - License: UNLICENSED
11
11
  - Documentation: [Link](https://cxlio.github.io/cxl/keyboard)
12
12
  - Report Issues: [Github](https://github.com/cxlio/cxl/issues)
package/amd/index.d.ts CHANGED
@@ -5,6 +5,7 @@ interface Key {
5
5
  shiftKey: boolean;
6
6
  metaKey: boolean;
7
7
  key: string;
8
+ code: string;
8
9
  }
9
10
  /**
10
11
  * The `KeyboardOptions` interface defines the configuration needed to handle keyboard interactions with
@@ -38,10 +39,24 @@ export interface KeyboardOptions {
38
39
  */
39
40
  capture?: boolean;
40
41
  }
42
+ /**
43
+ * The `KeyboardLayout` interface describes how a keyboard layout should be structured internally.
44
+ */
41
45
  export interface KeyboardLayout {
42
- shiftedKeys: string[];
43
- shiftMap: Record<string, string>;
44
- modKey: 'metaKey' | 'ctrlKey';
46
+ /**
47
+ * A mapping of shifted keys to their non-shifted counterparts, used to accurately
48
+ * interpret which character a shifted key press represents.
49
+ */
50
+ shiftMap: Record<string, string | undefined>;
51
+ /**
52
+ * Maps the representation of keys when combined with the alt key.
53
+ */
54
+ translate(ev: Key): string;
55
+ /**
56
+ * Indicates whether the 'meta' or 'ctrl' key is the primary modifier used for commands,
57
+ * differing based on the operating system (e.g., 'metaKey' for macOS).
58
+ */
59
+ modKey?: 'metaKey' | 'ctrlKey';
45
60
  }
46
61
  export declare const KeyboardLayoutData: Record<string, KeyboardLayout>;
47
62
  /**
@@ -63,16 +78,7 @@ export declare const KeyboardLayoutData: Record<string, KeyboardLayout>;
63
78
  * @return A cleanup function that removes the event listener.
64
79
  */
65
80
  export declare function handleKeyboard({ element, onKey, delay, layout, capture, }: KeyboardOptions): () => void;
66
- /**
67
- * This function, `parseKey`, takes a key combination string and splits it into a sequence of `Key` objects.
68
- * It uses regular expressions to identify each part of the key combination, accounting for modifier keys
69
- * like 'shift', 'ctrl', 'alt', 'meta', and 'mod', the latter being dependent on the `modKey` from the
70
- * `KeyboardLayout` (either 'ctrlKey' or 'metaKey' depending on the platform). For non-modifier keys, it
71
- * checks if the `shiftKey` is active and if the key should be shifted using the `shiftMap`. This logic
72
- * ensures the function captures accurate key state details in preparation for further handling like matching
73
- * key sequences in the `handleKeyboard` function.
74
- */
75
- export declare function parseKey(key: string, { modKey, shiftedKeys, shiftMap }?: KeyboardLayout): Key[];
81
+ export declare function parseKey(key: string, layout?: KeyboardLayout): Key[];
76
82
  /**
77
83
  * Converts a string representing a key combination into a normalized string format.
78
84
  * This function takes a `key` string and an optional `layout` object (defaults to the user's layout).
package/amd/index.js CHANGED
@@ -1 +1 @@
1
- define("@cxl/keyboard",["require","exports"],(function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.KeyboardLayoutData=void 0,t.handleKeyboard=function({element:e,onKey:a,delay:n,layout:r,capture:s}){const y=void 0===n?250:n,l=o.language;let c=[],f=0;const d=r||t.KeyboardLayoutData[l]||t.KeyboardLayoutData["en-US"];function u(e){const t=i(e,d);let o=Date.now();if(!t)return;o-f<y?c.push(t):c=[t];const n=c.slice(0);do{if(!1!==a(n.join(" "),c)){e.stopPropagation(),e.preventDefault(),o=0;break}n.shift()}while(n.length);f=o}return e.addEventListener("keydown",u,{capture:s}),()=>e.removeEventListener("keydown",u,{capture:s})},t.parseKey=y,t.normalize=function(e,t=s()){return y(e,t).map((e=>i(e,t))).join(" ")};const o="undefined"!=typeof window?window.navigator:{language:"en-US",platform:"nodejs"},a=/(shift|ctrl|control|alt|option|meta|command|cmd|mod|[^\s+]+)(\s*\+|\s)?/g,n={"/":"?",".":">",",":"<","'":'"',";":":","[":"{","]":"}","\\":"|","`":"~","=":"+","-":"_",1:"!",2:"@",3:"#",4:"$",5:"%",6:"^",7:"&",8:"*",9:"(",0:")"};t.KeyboardLayoutData={"en-US":{shiftedKeys:Object.values(n),shiftMap:n,modKey:/Mac|iPod|iPhone|iPad/.test(o.platform)?"metaKey":"ctrlKey"}};const r={ArrowUp:"up",ArrowDown:"down",ArrowLeft:"left",ArrowRight:"right",Escape:"esc"," ":"space"};function i(e,{shiftedKeys:t}){const o=e.key;if(!o||"Shift"===o||"Alt"===o||"Meta"===o||"Control"===o)return"";const a=r[o]||o.toLowerCase();let n;return e.ctrlKey&&(n="ctrl"),e.altKey&&(n=n?n+"+alt":"alt"),e.shiftKey&&!t.includes(a)&&(n=n?n+"+shift":"shift"),e.metaKey&&(n=n?n+"+meta":"meta"),n?n+"+"+a:a}function s(){return t.KeyboardLayoutData[o?.language]||t.KeyboardLayoutData["en-US"]}function y(e,{modKey:t,shiftedKeys:o,shiftMap:n}=s()){const r=[];let i,y={ctrlKey:!1,shiftKey:!1,metaKey:!1,altKey:!1,key:""};for(;i=a.exec(e.toLowerCase());){const e=i[1];if("shift"===e)y.shiftKey=!0;else if("ctrl"===e||"control"===e)y.ctrlKey=!0;else if("alt"===e||"option"===e)y.altKey=!0;else if("meta"===e||"cmd"===e||"command"===e)y.metaKey=!0;else if("mod"===e)y[t]=!0;else if(y.key=e,y.shiftKey)if(o.includes(e))y.shiftKey=!1;else{const t=n[e];t&&(y.key=t,y.shiftKey=!1)}"+"!==i[2]&&(r.push(y),y={ctrlKey:!1,shiftKey:!1,metaKey:!1,altKey:!1,key:""})}return r}}));
1
+ define("@cxl/keyboard",["require","exports"],(function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.KeyboardLayoutData=void 0,t.handleKeyboard=function({element:e,onKey:a,delay:n,layout:r,capture:s}){const c=void 0===n?250:n,y=o.language;let u=[],d=0;r||(r=t.KeyboardLayoutData[y]||t.KeyboardLayoutData["en-US"]);const f=l(r);function K(e){const t=i(e,f);let o=Date.now();if(!t)return;o-d<c?u.push(t):u=[t];const n=u.slice(0);do{if(!1!==a(n.join(" "),u)){e.stopPropagation(),e.preventDefault(),o=0;break}n.shift()}while(n.length);d=o}return e.addEventListener("keydown",K,{capture:s}),()=>e.removeEventListener("keydown",K,{capture:s})},t.parseKey=function(e,t=s()){return c(e,l(t))},t.normalize=function(e,t=s()){const o=l(t);return c(e,o).map((e=>i(e,o))).join(" ")};const o="undefined"!=typeof window?window.navigator:{language:"en-US",platform:"nodejs"},a=/Mac|iPod|iPhone|iPad/,n=/(shift|ctrl|control|alt|option|meta|command|cmd|mod|[^\s+]+)(\s*\+|\s)?/g,r={ArrowUp:"up",ArrowDown:"down",ArrowLeft:"left",ArrowRight:"right",Escape:"esc",Space:"space",Backquote:"`",Minus:"-",Equal:"=",BracketLeft:"[",BracketRight:"]",Quote:"'",Backslash:"\\",Apostrophe:"'",Semicolon:";",Comma:",",Period:".",Slash:"/"};function i(e,{translate:t}){const o=e.key;if(!o||"Shift"===o||"Alt"===o||"Meta"===o||"Control"===o||"Dead"===o&&!e.altKey)return"";const a=t(e);let n;return e.ctrlKey&&(n="ctrl"),e.altKey&&(n=n?n+"+alt":"alt"),e.shiftKey&&(n=n?n+"+shift":"shift"),e.metaKey&&(n=n?n+"+meta":"meta"),n?n+"+"+a:a}function s(){return t.KeyboardLayoutData[o?.language]||t.KeyboardLayoutData["en-US"]}function c(e,{modKey:t,shiftMap:o}){const a=[];let r,i={ctrlKey:!1,shiftKey:!1,metaKey:!1,altKey:!1,key:"",code:""};for(;r=n.exec(e.toLowerCase());){const e=r[1];if("shift"===e)i.shiftKey=!0;else if("ctrl"===e||"control"===e)i.ctrlKey=!0;else if("alt"===e||"option"===e)i.altKey=!0;else if("meta"===e||"cmd"===e||"command"===e)i.metaKey=!0;else if("mod"===e)i[t]=!0;else{const t=o[e];t?(i.key=t,i.shiftKey=!0):i.key=e}"+"!==r[2]&&(a.push(i),i={ctrlKey:!1,shiftKey:!1,metaKey:!1,altKey:!1,key:"",code:""})}return a}function l(e){return{modKey:a.test(o.platform)?"metaKey":"ctrlKey",...e}}t.KeyboardLayoutData={"en-US":{shiftMap:{"?":"/",">":".","<":",",'"':"'",":":";","{":"[","}":"]","|":"\\","~":"`","+":"=",_:"-","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0"},translate:({code:e,key:t})=>r[e]||(e.startsWith("Key")?e.slice(3).toLowerCase():e.startsWith("Digit")?e.slice(5):t.toLowerCase())}}}));
package/index.d.ts CHANGED
@@ -5,6 +5,7 @@ interface Key {
5
5
  shiftKey: boolean;
6
6
  metaKey: boolean;
7
7
  key: string;
8
+ code: string;
8
9
  }
9
10
  /**
10
11
  * The `KeyboardOptions` interface defines the configuration needed to handle keyboard interactions with
@@ -38,10 +39,24 @@ export interface KeyboardOptions {
38
39
  */
39
40
  capture?: boolean;
40
41
  }
42
+ /**
43
+ * The `KeyboardLayout` interface describes how a keyboard layout should be structured internally.
44
+ */
41
45
  export interface KeyboardLayout {
42
- shiftedKeys: string[];
43
- shiftMap: Record<string, string>;
44
- modKey: 'metaKey' | 'ctrlKey';
46
+ /**
47
+ * A mapping of shifted keys to their non-shifted counterparts, used to accurately
48
+ * interpret which character a shifted key press represents.
49
+ */
50
+ shiftMap: Record<string, string | undefined>;
51
+ /**
52
+ * Maps the representation of keys when combined with the alt key.
53
+ */
54
+ translate(ev: Key): string;
55
+ /**
56
+ * Indicates whether the 'meta' or 'ctrl' key is the primary modifier used for commands,
57
+ * differing based on the operating system (e.g., 'metaKey' for macOS).
58
+ */
59
+ modKey?: 'metaKey' | 'ctrlKey';
45
60
  }
46
61
  export declare const KeyboardLayoutData: Record<string, KeyboardLayout>;
47
62
  /**
@@ -63,16 +78,7 @@ export declare const KeyboardLayoutData: Record<string, KeyboardLayout>;
63
78
  * @return A cleanup function that removes the event listener.
64
79
  */
65
80
  export declare function handleKeyboard({ element, onKey, delay, layout, capture, }: KeyboardOptions): () => void;
66
- /**
67
- * This function, `parseKey`, takes a key combination string and splits it into a sequence of `Key` objects.
68
- * It uses regular expressions to identify each part of the key combination, accounting for modifier keys
69
- * like 'shift', 'ctrl', 'alt', 'meta', and 'mod', the latter being dependent on the `modKey` from the
70
- * `KeyboardLayout` (either 'ctrlKey' or 'metaKey' depending on the platform). For non-modifier keys, it
71
- * checks if the `shiftKey` is active and if the key should be shifted using the `shiftMap`. This logic
72
- * ensures the function captures accurate key state details in preparation for further handling like matching
73
- * key sequences in the `handleKeyboard` function.
74
- */
75
- export declare function parseKey(key: string, { modKey, shiftedKeys, shiftMap }?: KeyboardLayout): Key[];
81
+ export declare function parseKey(key: string, layout?: KeyboardLayout): Key[];
76
82
  /**
77
83
  * Converts a string representing a key combination into a normalized string format.
78
84
  * This function takes a `key` string and an optional `layout` object (defaults to the user's layout).
package/index.js CHANGED
@@ -11,34 +11,27 @@ const navigator = typeof window !== 'undefined'
11
11
  const IS_MAC = /Mac|iPod|iPhone|iPad/;
12
12
  const PARSE_KEY = /(shift|ctrl|control|alt|option|meta|command|cmd|mod|[^\s+]+)(\s*\+|\s)?/g;
13
13
  const SHIFT_MAP = {
14
- '/': '?',
15
- '.': '>',
16
- ',': '<',
17
- "'": '"',
18
- ';': ':',
19
- '[': '{',
20
- ']': '}',
21
- '\\': '|',
22
- '`': '~',
23
- '=': '+',
24
- '-': '_',
25
- '1': '!',
26
- '2': '@',
27
- '3': '#',
28
- '4': '$',
29
- '5': '%',
30
- '6': '^',
31
- '7': '&',
32
- '8': '*',
33
- '9': '(',
34
- '0': ')',
35
- };
36
- exports.KeyboardLayoutData = {
37
- 'en-US': {
38
- shiftedKeys: Object.values(SHIFT_MAP),
39
- shiftMap: SHIFT_MAP,
40
- modKey: IS_MAC.test(navigator.platform) ? 'metaKey' : 'ctrlKey',
41
- },
14
+ '?': '/',
15
+ '>': '.',
16
+ '<': ',',
17
+ '"': "'",
18
+ ':': ';',
19
+ '{': '[',
20
+ '}': ']',
21
+ '|': '\\',
22
+ '~': '`',
23
+ '+': '=',
24
+ _: '-',
25
+ '!': '1',
26
+ '@': '2',
27
+ '#': '3',
28
+ $: '4',
29
+ '%': '5',
30
+ '^': '6',
31
+ '&': '7',
32
+ '*': '8',
33
+ '(': '9',
34
+ ')': '0',
42
35
  };
43
36
  const TranslateKey = {
44
37
  ArrowUp: 'up',
@@ -46,31 +39,63 @@ const TranslateKey = {
46
39
  ArrowLeft: 'left',
47
40
  ArrowRight: 'right',
48
41
  Escape: 'esc',
49
- ' ': 'space',
42
+ Space: 'space',
43
+ Backquote: '`',
44
+ Minus: '-',
45
+ Equal: '=',
46
+ BracketLeft: '[',
47
+ BracketRight: ']',
48
+ Quote: "'",
49
+ Backslash: '\\',
50
+ Apostrophe: "'",
51
+ Semicolon: ';',
52
+ Comma: ',',
53
+ Period: '.',
54
+ Slash: '/',
55
+ };
56
+ exports.KeyboardLayoutData = {
57
+ 'en-US': {
58
+ shiftMap: SHIFT_MAP,
59
+ translate({ code, key }) {
60
+ const translated = TranslateKey[code];
61
+ if (translated)
62
+ return translated;
63
+ if (code.startsWith('Key'))
64
+ return code.slice(3).toLowerCase();
65
+ if (code.startsWith('Digit'))
66
+ return code.slice(5);
67
+ return key.toLowerCase();
68
+ },
69
+ },
50
70
  };
51
71
  /**
52
- * This function converts a `Key` event into a string representation, encoding modifier keys
53
- * (ctrl, alt, shift, meta) and the key itself. It checks each modifier key flag in the `Key`
54
- * object, constructing a result string with '+' separators. Non-modifier keys are lowercased
55
- * and looked up in `TranslateKey` for a more readable name. If the key itself is a shifted
56
- * character (not included in `shiftedKeys`), 'shift' is added to the modifiers. This string
57
- * format is useful for handling complex keyboard shortcuts consistently across different layouts.
72
+ * The `keyboardEventToString` function converts a `Key` object to a string that represents the key combination pressed.
73
+ * It checks individual modifier keys (`ctrlKey`, `altKey`, `shiftKey`, and `metaKey`) and constructs a concatenated
74
+ * string reflecting these modifiers followed by the translated character key.
75
+ *
76
+ * Exceptions include when the `key` is 'Shift', 'Alt', 'Meta', 'Control', or 'Dead' without an `altKey`,
77
+ * as these are discarded (represented by an empty string). If no modifiers are pressed, it directly returns
78
+ * the translated character.
79
+ *
80
+ * This conversion provides a consistent way to describe key combinations as strings, essential for handling
81
+ * and processing keyboard shortcuts or sequences in the application.
58
82
  */
59
- function keyboardEventToString(ev, { shiftedKeys }) {
83
+ function keyboardEventToString(ev, { translate }) {
60
84
  const key = ev.key;
61
85
  if (!key ||
62
86
  key === 'Shift' ||
63
87
  key === 'Alt' ||
64
88
  key === 'Meta' ||
65
- key === 'Control')
89
+ key === 'Control' ||
90
+ (key === 'Dead' && !ev.altKey))
66
91
  return '';
67
- const ch = TranslateKey[key] || key.toLowerCase();
92
+ const ch = translate(ev);
68
93
  let result;
69
94
  if (ev.ctrlKey)
70
95
  result = 'ctrl';
71
96
  if (ev.altKey)
72
97
  result = result ? result + '+alt' : 'alt';
73
- if (ev.shiftKey && !shiftedKeys.includes(ch))
98
+ if (ev.shiftKey)
74
99
  result = result ? result + '+shift' : 'shift';
75
100
  if (ev.metaKey)
76
101
  result = result ? result + '+meta' : 'meta';
@@ -99,7 +124,8 @@ function handleKeyboard({ element, onKey, delay, layout, capture, }) {
99
124
  const locale = navigator.language;
100
125
  let sequence = [];
101
126
  let lastT = 0;
102
- const newLayout = layout || exports.KeyboardLayoutData[locale] || exports.KeyboardLayoutData['en-US'];
127
+ layout || (layout = exports.KeyboardLayoutData[locale] || exports.KeyboardLayoutData['en-US']);
128
+ const newLayout = augmentLayout(layout);
103
129
  function handler(ev) {
104
130
  const k = keyboardEventToString(ev, newLayout);
105
131
  let t = Date.now();
@@ -124,6 +150,10 @@ function handleKeyboard({ element, onKey, delay, layout, capture, }) {
124
150
  element.addEventListener('keydown', handler, { capture });
125
151
  return () => element.removeEventListener('keydown', handler, { capture });
126
152
  }
153
+ // Retrieves the default `KeyboardLayout` based on the current user's language settings from `navigator.language`.
154
+ // Falls back to the 'en-US' layout if the user's specific language is not available in `KeyboardLayoutData`.
155
+ // This function helps ensure that the keyboard handling can adapt to the most appropriate layout for the user,
156
+ // facilitating the accurate interpretation of key sequences and modifiers according to locale preferences.
127
157
  function getDefaultLayout() {
128
158
  return (exports.KeyboardLayoutData[navigator?.language] || exports.KeyboardLayoutData['en-US']);
129
159
  }
@@ -134,6 +164,7 @@ function newKey() {
134
164
  metaKey: false,
135
165
  altKey: false,
136
166
  key: '',
167
+ code: '',
137
168
  };
138
169
  }
139
170
  /**
@@ -145,7 +176,7 @@ function newKey() {
145
176
  * ensures the function captures accurate key state details in preparation for further handling like matching
146
177
  * key sequences in the `handleKeyboard` function.
147
178
  */
148
- function parseKey(key, { modKey, shiftedKeys, shiftMap } = getDefaultLayout()) {
179
+ function _parseKey(key, { modKey, shiftMap }) {
149
180
  const sequence = [];
150
181
  let match;
151
182
  let event = newKey();
@@ -162,19 +193,13 @@ function parseKey(key, { modKey, shiftedKeys, shiftMap } = getDefaultLayout()) {
162
193
  else if (ch === 'mod')
163
194
  event[modKey] = true;
164
195
  else {
165
- event.key = ch;
166
- if (event.shiftKey) {
167
- if (shiftedKeys.includes(ch)) {
168
- event.shiftKey = false;
169
- }
170
- else {
171
- const shifted = shiftMap[ch];
172
- if (shifted) {
173
- event.key = shifted;
174
- event.shiftKey = false;
175
- }
176
- }
196
+ const shifted = shiftMap[ch];
197
+ if (shifted) {
198
+ event.key = shifted;
199
+ event.shiftKey = true;
177
200
  }
201
+ else
202
+ event.key = ch;
178
203
  }
179
204
  if (match[2] !== '+') {
180
205
  sequence.push(event);
@@ -183,6 +208,15 @@ function parseKey(key, { modKey, shiftedKeys, shiftMap } = getDefaultLayout()) {
183
208
  }
184
209
  return sequence;
185
210
  }
211
+ function augmentLayout(layout) {
212
+ return {
213
+ modKey: IS_MAC.test(navigator.platform) ? 'metaKey' : 'ctrlKey',
214
+ ...layout,
215
+ };
216
+ }
217
+ function parseKey(key, layout = getDefaultLayout()) {
218
+ return _parseKey(key, augmentLayout(layout));
219
+ }
186
220
  /**
187
221
  * Converts a string representing a key combination into a normalized string format.
188
222
  * This function takes a `key` string and an optional `layout` object (defaults to the user's layout).
@@ -193,6 +227,7 @@ function parseKey(key, { modKey, shiftedKeys, shiftMap } = getDefaultLayout()) {
193
227
  * for ensuring consistent representation of key sequences across different keyboard layouts.
194
228
  */
195
229
  function normalize(key, layout = getDefaultLayout()) {
196
- const sequence = parseKey(key, layout);
197
- return sequence.map(key => keyboardEventToString(key, layout)).join(' ');
230
+ const newLayout = augmentLayout(layout);
231
+ const sequence = _parseKey(key, newLayout);
232
+ return sequence.map(key => keyboardEventToString(key, newLayout)).join(' ');
198
233
  }
package/index.min.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.KeyboardLayoutData=void 0,exports.handleKeyboard=handleKeyboard,exports.parseKey=parseKey,exports.normalize=normalize;const navigator="undefined"!=typeof window?window.navigator:{language:"en-US",platform:"nodejs"},IS_MAC=/Mac|iPod|iPhone|iPad/,PARSE_KEY=/(shift|ctrl|control|alt|option|meta|command|cmd|mod|[^\s+]+)(\s*\+|\s)?/g,SHIFT_MAP={"/":"?",".":">",",":"<","'":'"',";":":","[":"{","]":"}","\\":"|","`":"~","=":"+","-":"_",1:"!",2:"@",3:"#",4:"$",5:"%",6:"^",7:"&",8:"*",9:"(",0:")"};exports.KeyboardLayoutData={"en-US":{shiftedKeys:Object.values(SHIFT_MAP),shiftMap:SHIFT_MAP,modKey:IS_MAC.test(navigator.platform)?"metaKey":"ctrlKey"}};const TranslateKey={ArrowUp:"up",ArrowDown:"down",ArrowLeft:"left",ArrowRight:"right",Escape:"esc"," ":"space"};function keyboardEventToString(e,{shiftedKeys:t}){const o=e.key;if(!o||"Shift"===o||"Alt"===o||"Meta"===o||"Control"===o)return"";const a=TranslateKey[o]||o.toLowerCase();let n;return e.ctrlKey&&(n="ctrl"),e.altKey&&(n=n?n+"+alt":"alt"),e.shiftKey&&!t.includes(a)&&(n=n?n+"+shift":"shift"),e.metaKey&&(n=n?n+"+meta":"meta"),n?n+"+"+a:a}function handleKeyboard({element:e,onKey:t,delay:o,layout:a,capture:n}){const r=void 0===o?250:o,s=navigator.language;let i=[],y=0;const l=a||exports.KeyboardLayoutData[s]||exports.KeyboardLayoutData["en-US"];function d(e){const o=keyboardEventToString(e,l);let a=Date.now();if(!o)return;a-y<r?i.push(o):i=[o];const n=i.slice(0);do{if(!1!==t(n.join(" "),i)){e.stopPropagation(),e.preventDefault(),a=0;break}n.shift()}while(n.length);y=a}return e.addEventListener("keydown",d,{capture:n}),()=>e.removeEventListener("keydown",d,{capture:n})}function getDefaultLayout(){return exports.KeyboardLayoutData[navigator?.language]||exports.KeyboardLayoutData["en-US"]}function newKey(){return{ctrlKey:!1,shiftKey:!1,metaKey:!1,altKey:!1,key:""}}function parseKey(e,{modKey:t,shiftedKeys:o,shiftMap:a}=getDefaultLayout()){const n=[];let r,s=newKey();for(;r=PARSE_KEY.exec(e.toLowerCase());){const e=r[1];if("shift"===e)s.shiftKey=!0;else if("ctrl"===e||"control"===e)s.ctrlKey=!0;else if("alt"===e||"option"===e)s.altKey=!0;else if("meta"===e||"cmd"===e||"command"===e)s.metaKey=!0;else if("mod"===e)s[t]=!0;else if(s.key=e,s.shiftKey)if(o.includes(e))s.shiftKey=!1;else{const t=a[e];t&&(s.key=t,s.shiftKey=!1)}"+"!==r[2]&&(n.push(s),s=newKey())}return n}function normalize(e,t=getDefaultLayout()){return parseKey(e,t).map((e=>keyboardEventToString(e,t))).join(" ")}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.KeyboardLayoutData=void 0,exports.handleKeyboard=handleKeyboard,exports.parseKey=parseKey,exports.normalize=normalize;const navigator="undefined"!=typeof window?window.navigator:{language:"en-US",platform:"nodejs"},IS_MAC=/Mac|iPod|iPhone|iPad/,PARSE_KEY=/(shift|ctrl|control|alt|option|meta|command|cmd|mod|[^\s+]+)(\s*\+|\s)?/g,SHIFT_MAP={"?":"/",">":".","<":",",'"':"'",":":";","{":"[","}":"]","|":"\\","~":"`","+":"=",_:"-","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0"},TranslateKey={ArrowUp:"up",ArrowDown:"down",ArrowLeft:"left",ArrowRight:"right",Escape:"esc",Space:"space",Backquote:"`",Minus:"-",Equal:"=",BracketLeft:"[",BracketRight:"]",Quote:"'",Backslash:"\\",Apostrophe:"'",Semicolon:";",Comma:",",Period:".",Slash:"/"};function keyboardEventToString(e,{translate:t}){const a=e.key;if(!a||"Shift"===a||"Alt"===a||"Meta"===a||"Control"===a||"Dead"===a&&!e.altKey)return"";const o=t(e);let n;return e.ctrlKey&&(n="ctrl"),e.altKey&&(n=n?n+"+alt":"alt"),e.shiftKey&&(n=n?n+"+shift":"shift"),e.metaKey&&(n=n?n+"+meta":"meta"),n?n+"+"+o:o}function handleKeyboard({element:e,onKey:t,delay:a,layout:o,capture:n}){const r=void 0===a?250:a,s=navigator.language;let i=[],y=0;o||(o=exports.KeyboardLayoutData[s]||exports.KeyboardLayoutData["en-US"]);const l=augmentLayout(o);function u(e){const a=keyboardEventToString(e,l);let o=Date.now();if(!a)return;o-y<r?i.push(a):i=[a];const n=i.slice(0);do{if(!1!==t(n.join(" "),i)){e.stopPropagation(),e.preventDefault(),o=0;break}n.shift()}while(n.length);y=o}return e.addEventListener("keydown",u,{capture:n}),()=>e.removeEventListener("keydown",u,{capture:n})}function getDefaultLayout(){return exports.KeyboardLayoutData[navigator?.language]||exports.KeyboardLayoutData["en-US"]}function newKey(){return{ctrlKey:!1,shiftKey:!1,metaKey:!1,altKey:!1,key:"",code:""}}function _parseKey(e,{modKey:t,shiftMap:a}){const o=[];let n,r=newKey();for(;n=PARSE_KEY.exec(e.toLowerCase());){const e=n[1];if("shift"===e)r.shiftKey=!0;else if("ctrl"===e||"control"===e)r.ctrlKey=!0;else if("alt"===e||"option"===e)r.altKey=!0;else if("meta"===e||"cmd"===e||"command"===e)r.metaKey=!0;else if("mod"===e)r[t]=!0;else{const t=a[e];t?(r.key=t,r.shiftKey=!0):r.key=e}"+"!==n[2]&&(o.push(r),r=newKey())}return o}function augmentLayout(e){return{modKey:IS_MAC.test(navigator.platform)?"metaKey":"ctrlKey",...e}}function parseKey(e,t=getDefaultLayout()){return _parseKey(e,augmentLayout(t))}function normalize(e,t=getDefaultLayout()){const a=augmentLayout(t);return _parseKey(e,a).map((e=>keyboardEventToString(e,a))).join(" ")}exports.KeyboardLayoutData={"en-US":{shiftMap:SHIFT_MAP,translate({code:e,key:t}){const a=TranslateKey[e];return a||(e.startsWith("Key")?e.slice(3).toLowerCase():e.startsWith("Digit")?e.slice(5):t.toLowerCase())}}};
package/mjs/index.d.ts CHANGED
@@ -4,6 +4,7 @@ interface Key {
4
4
  shiftKey: boolean;
5
5
  metaKey: boolean;
6
6
  key: string;
7
+ code: string;
7
8
  }
8
9
  /**
9
10
  * The `KeyboardOptions` interface defines the configuration needed to handle keyboard interactions with
@@ -37,10 +38,24 @@ export interface KeyboardOptions {
37
38
  */
38
39
  capture?: boolean;
39
40
  }
41
+ /**
42
+ * The `KeyboardLayout` interface describes how a keyboard layout should be structured internally.
43
+ */
40
44
  export interface KeyboardLayout {
41
- shiftedKeys: string[];
42
- shiftMap: Record<string, string>;
43
- modKey: 'metaKey' | 'ctrlKey';
45
+ /**
46
+ * A mapping of shifted keys to their non-shifted counterparts, used to accurately
47
+ * interpret which character a shifted key press represents.
48
+ */
49
+ shiftMap: Record<string, string | undefined>;
50
+ /**
51
+ * Maps the representation of keys when combined with the alt key.
52
+ */
53
+ translate(ev: Key): string;
54
+ /**
55
+ * Indicates whether the 'meta' or 'ctrl' key is the primary modifier used for commands,
56
+ * differing based on the operating system (e.g., 'metaKey' for macOS).
57
+ */
58
+ modKey?: 'metaKey' | 'ctrlKey';
44
59
  }
45
60
  export declare const KeyboardLayoutData: Record<string, KeyboardLayout>;
46
61
  /**
@@ -62,16 +77,7 @@ export declare const KeyboardLayoutData: Record<string, KeyboardLayout>;
62
77
  * @return A cleanup function that removes the event listener.
63
78
  */
64
79
  export declare function handleKeyboard({ element, onKey, delay, layout, capture, }: KeyboardOptions): () => void;
65
- /**
66
- * This function, `parseKey`, takes a key combination string and splits it into a sequence of `Key` objects.
67
- * It uses regular expressions to identify each part of the key combination, accounting for modifier keys
68
- * like 'shift', 'ctrl', 'alt', 'meta', and 'mod', the latter being dependent on the `modKey` from the
69
- * `KeyboardLayout` (either 'ctrlKey' or 'metaKey' depending on the platform). For non-modifier keys, it
70
- * checks if the `shiftKey` is active and if the key should be shifted using the `shiftMap`. This logic
71
- * ensures the function captures accurate key state details in preparation for further handling like matching
72
- * key sequences in the `handleKeyboard` function.
73
- */
74
- export declare function parseKey(key: string, { modKey, shiftedKeys, shiftMap }?: KeyboardLayout): Key[];
80
+ export declare function parseKey(key: string, layout?: KeyboardLayout): Key[];
75
81
  /**
76
82
  * Converts a string representing a key combination into a normalized string format.
77
83
  * This function takes a `key` string and an optional `layout` object (defaults to the user's layout).
package/mjs/index.js CHANGED
@@ -1 +1 @@
1
- const navigator="undefined"!=typeof window?window.navigator:{language:"en-US",platform:"nodejs"},IS_MAC=/Mac|iPod|iPhone|iPad/,PARSE_KEY=/(shift|ctrl|control|alt|option|meta|command|cmd|mod|[^\s+]+)(\s*\+|\s)?/g,SHIFT_MAP={"/":"?",".":">",",":"<","'":'"',";":":","[":"{","]":"}","\\":"|","`":"~","=":"+","-":"_",1:"!",2:"@",3:"#",4:"$",5:"%",6:"^",7:"&",8:"*",9:"(",0:")"};export const KeyboardLayoutData={"en-US":{shiftedKeys:Object.values(SHIFT_MAP),shiftMap:SHIFT_MAP,modKey:IS_MAC.test(navigator.platform)?"metaKey":"ctrlKey"}};const TranslateKey={ArrowUp:"up",ArrowDown:"down",ArrowLeft:"left",ArrowRight:"right",Escape:"esc"," ":"space"};function keyboardEventToString(e,{shiftedKeys:t}){const o=e.key;if(!o||"Shift"===o||"Alt"===o||"Meta"===o||"Control"===o)return"";const a=TranslateKey[o]||o.toLowerCase();let n;return e.ctrlKey&&(n="ctrl"),e.altKey&&(n=n?n+"+alt":"alt"),e.shiftKey&&!t.includes(a)&&(n=n?n+"+shift":"shift"),e.metaKey&&(n=n?n+"+meta":"meta"),n?n+"+"+a:a}export function handleKeyboard({element:e,onKey:t,delay:o,layout:a,capture:n}){const r=void 0===o?250:o,i=navigator.language;let s=[],y=0;const l=a||KeyboardLayoutData[i]||KeyboardLayoutData["en-US"];function f(e){const o=keyboardEventToString(e,l);let a=Date.now();if(!o)return;a-y<r?s.push(o):s=[o];const n=s.slice(0);do{if(!1!==t(n.join(" "),s)){e.stopPropagation(),e.preventDefault(),a=0;break}n.shift()}while(n.length);y=a}return e.addEventListener("keydown",f,{capture:n}),()=>e.removeEventListener("keydown",f,{capture:n})}function getDefaultLayout(){return KeyboardLayoutData[navigator?.language]||KeyboardLayoutData["en-US"]}function newKey(){return{ctrlKey:!1,shiftKey:!1,metaKey:!1,altKey:!1,key:""}}export function parseKey(e,{modKey:t,shiftedKeys:o,shiftMap:a}=getDefaultLayout()){const n=[];let r,i=newKey();for(;r=PARSE_KEY.exec(e.toLowerCase());){const e=r[1];if("shift"===e)i.shiftKey=!0;else if("ctrl"===e||"control"===e)i.ctrlKey=!0;else if("alt"===e||"option"===e)i.altKey=!0;else if("meta"===e||"cmd"===e||"command"===e)i.metaKey=!0;else if("mod"===e)i[t]=!0;else if(i.key=e,i.shiftKey)if(o.includes(e))i.shiftKey=!1;else{const t=a[e];t&&(i.key=t,i.shiftKey=!1)}"+"!==r[2]&&(n.push(i),i=newKey())}return n}export function normalize(e,t=getDefaultLayout()){return parseKey(e,t).map((e=>keyboardEventToString(e,t))).join(" ")}
1
+ const navigator="undefined"!=typeof window?window.navigator:{language:"en-US",platform:"nodejs"},IS_MAC=/Mac|iPod|iPhone|iPad/,PARSE_KEY=/(shift|ctrl|control|alt|option|meta|command|cmd|mod|[^\s+]+)(\s*\+|\s)?/g,SHIFT_MAP={"?":"/",">":".","<":",",'"':"'",":":";","{":"[","}":"]","|":"\\","~":"`","+":"=",_:"-","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0"},TranslateKey={ArrowUp:"up",ArrowDown:"down",ArrowLeft:"left",ArrowRight:"right",Escape:"esc",Space:"space",Backquote:"`",Minus:"-",Equal:"=",BracketLeft:"[",BracketRight:"]",Quote:"'",Backslash:"\\",Apostrophe:"'",Semicolon:";",Comma:",",Period:".",Slash:"/"};export const KeyboardLayoutData={"en-US":{shiftMap:SHIFT_MAP,translate:({code:e,key:t})=>TranslateKey[e]||(e.startsWith("Key")?e.slice(3).toLowerCase():e.startsWith("Digit")?e.slice(5):t.toLowerCase())}};function keyboardEventToString(e,{translate:t}){const a=e.key;if(!a||"Shift"===a||"Alt"===a||"Meta"===a||"Control"===a||"Dead"===a&&!e.altKey)return"";const o=t(e);let n;return e.ctrlKey&&(n="ctrl"),e.altKey&&(n=n?n+"+alt":"alt"),e.shiftKey&&(n=n?n+"+shift":"shift"),e.metaKey&&(n=n?n+"+meta":"meta"),n?n+"+"+o:o}export function handleKeyboard({element:e,onKey:t,delay:a,layout:o,capture:n}){const r=void 0===a?250:a,i=navigator.language;let s=[],y=0;o||(o=KeyboardLayoutData[i]||KeyboardLayoutData["en-US"]);const l=augmentLayout(o);function u(e){const a=keyboardEventToString(e,l);let o=Date.now();if(!a)return;o-y<r?s.push(a):s=[a];const n=s.slice(0);do{if(!1!==t(n.join(" "),s)){e.stopPropagation(),e.preventDefault(),o=0;break}n.shift()}while(n.length);y=o}return e.addEventListener("keydown",u,{capture:n}),()=>e.removeEventListener("keydown",u,{capture:n})}function getDefaultLayout(){return KeyboardLayoutData[navigator?.language]||KeyboardLayoutData["en-US"]}function newKey(){return{ctrlKey:!1,shiftKey:!1,metaKey:!1,altKey:!1,key:"",code:""}}function _parseKey(e,{modKey:t,shiftMap:a}){const o=[];let n,r=newKey();for(;n=PARSE_KEY.exec(e.toLowerCase());){const e=n[1];if("shift"===e)r.shiftKey=!0;else if("ctrl"===e||"control"===e)r.ctrlKey=!0;else if("alt"===e||"option"===e)r.altKey=!0;else if("meta"===e||"cmd"===e||"command"===e)r.metaKey=!0;else if("mod"===e)r[t]=!0;else{const t=a[e];t?(r.key=t,r.shiftKey=!0):r.key=e}"+"!==n[2]&&(o.push(r),r=newKey())}return o}function augmentLayout(e){return{modKey:IS_MAC.test(navigator.platform)?"metaKey":"ctrlKey",...e}}export function parseKey(e,t=getDefaultLayout()){return _parseKey(e,augmentLayout(t))}export function normalize(e,t=getDefaultLayout()){const a=augmentLayout(t);return _parseKey(e,a).map((e=>keyboardEventToString(e,a))).join(" ")}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cxl/keyboard",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Keyboard Handling Library",
5
5
  "license": "UNLICENSED",
6
6
  "files": [
package/test.js CHANGED
@@ -17,29 +17,51 @@ exports.default = (0, spec_1.spec)('keyboard', s => {
17
17
  a.equal((0, index_js_1.normalize)('ctrl+shift+x'), 'ctrl+shift+x');
18
18
  });
19
19
  it.should('parse mod key', a => {
20
- a.equal((0, index_js_1.normalize)('mod+x'), 'ctrl+x');
21
- a.equal((0, index_js_1.normalize)('x+mod'), 'ctrl+x');
22
- a.equal((0, index_js_1.normalize)('MOD+X'), 'ctrl+x');
20
+ const layout = {
21
+ ...index_js_1.KeyboardLayoutData['en-US'],
22
+ modKey: 'ctrlKey',
23
+ };
24
+ a.equal((0, index_js_1.normalize)('mod+x', layout), 'ctrl+x');
25
+ a.equal((0, index_js_1.normalize)('x+mod', layout), 'ctrl+x');
26
+ a.equal((0, index_js_1.normalize)('MOD+X', layout), 'ctrl+x');
27
+ layout.modKey = 'metaKey';
28
+ a.equal((0, index_js_1.normalize)('mod+x', layout), 'meta+x');
29
+ a.equal((0, index_js_1.normalize)('x+mod', layout), 'meta+x');
30
+ a.equal((0, index_js_1.normalize)('MOD+X', layout), 'meta+x');
23
31
  });
24
32
  it.should('normalize shifted keys', a => {
25
- a.equal((0, index_js_1.normalize)('shift+5'), '%');
33
+ a.equal((0, index_js_1.normalize)('%'), 'shift+5');
26
34
  a.equal((0, index_js_1.normalize)('shift+A'), 'shift+a');
27
35
  a.equal((0, index_js_1.normalize)('shift+y'), 'shift+y');
28
- a.equal((0, index_js_1.normalize)('shift+ctrl+5'), 'ctrl+%');
36
+ a.equal((0, index_js_1.normalize)('shift+ctrl+5'), 'ctrl+shift+5');
37
+ a.equal((0, index_js_1.normalize)('ctrl+%'), 'ctrl+shift+5');
29
38
  });
30
39
  it.should('handle non character keys', a => {
31
- a.equal((0, index_js_1.normalize)(':'), ':');
40
+ a.equal((0, index_js_1.normalize)(':'), 'shift+;');
32
41
  a.equal((0, index_js_1.normalize)('/'), '/');
33
42
  a.equal((0, index_js_1.normalize)('.'), '.');
34
- a.equal((0, index_js_1.normalize)('?'), '?');
35
- a.equal((0, index_js_1.normalize)('shift+/'), '?');
36
- a.equal((0, index_js_1.normalize)('shift+?'), '?');
37
- a.equal((0, index_js_1.normalize)('ctrl+:'), 'ctrl+:');
43
+ a.equal((0, index_js_1.normalize)('?'), 'shift+/');
44
+ a.equal((0, index_js_1.normalize)('shift+/'), 'shift+/');
45
+ //a.equal(normalize('shift++'), 'shift+=');
46
+ a.equal((0, index_js_1.normalize)('shift+?'), 'shift+/');
47
+ a.equal((0, index_js_1.normalize)('ctrl+:'), 'ctrl+shift+;');
48
+ a.equal((0, index_js_1.normalize)('ctrl+shift+?'), 'ctrl+shift+/');
49
+ a.equal((0, index_js_1.normalize)(';'), ';');
38
50
  });
39
51
  it.should('normalize special keys with modifiers', a => {
40
52
  a.equal((0, index_js_1.normalize)('ctrl+alt+del'), 'ctrl+alt+del');
41
53
  a.equal((0, index_js_1.normalize)('ctrl+shift+esc'), 'ctrl+shift+esc');
42
54
  });
55
+ it.should('handle case sensitivity', a => {
56
+ a.equal((0, index_js_1.normalize)('CTRL+A'), 'ctrl+a');
57
+ a.equal((0, index_js_1.normalize)('Shift+Y'), 'shift+y');
58
+ });
59
+ /*it.should('handle complex sequences', a => {
60
+ a.equal(
61
+ normalize(' ctrl + shift + x ctrl+y'),
62
+ 'ctrl+shift+x ctrl+y',
63
+ );
64
+ });*/
43
65
  });
44
66
  s.test('parseKey', it => {
45
67
  it.should('correctly parse simple keys without modifiers', a => {
@@ -78,17 +100,51 @@ exports.default = (0, spec_1.spec)('keyboard', s => {
78
100
  a.equal(result[1].ctrlKey, true);
79
101
  });
80
102
  it.should('map mod key to the correct modifier', a => {
81
- const defaultLayout = index_js_1.KeyboardLayoutData['en-US'];
82
- const result = (0, index_js_1.parseKey)('mod+c');
103
+ const layout = index_js_1.KeyboardLayoutData['en-US'];
104
+ const result = (0, index_js_1.parseKey)('mod+c', { ...layout, modKey: 'ctrlKey' });
83
105
  a.equal(result.length, 1);
84
106
  a.equal(result[0].key, 'c');
85
- a.equal(result[0][defaultLayout.modKey], true);
107
+ a.equal(result[0].ctrlKey, true);
108
+ const result2 = (0, index_js_1.parseKey)('mod+c', { ...layout, modKey: 'metaKey' });
109
+ a.equal(result2.length, 1);
110
+ a.equal(result2[0].key, 'c');
111
+ a.equal(result2[0].metaKey, true);
86
112
  });
87
113
  it.should('handle shifted characters correctly', a => {
88
114
  const result = (0, index_js_1.parseKey)('shift+1');
89
115
  a.equal(result.length, 1);
90
- a.equal(result[0].key, '!'); // assuming shift+1 maps to '@' in shiftMap
91
- a.equal(result[0].shiftKey, false);
116
+ a.equal(result[0].key, '1'); // assuming shift+1 maps to '@' in shiftMap
117
+ a.equal(result[0].shiftKey, true);
118
+ });
119
+ it.should('handle keys with special characters', a => {
120
+ const result = (0, index_js_1.parseKey)('ctrl+shift+1');
121
+ a.equal(result.length, 1);
122
+ a.equal(result[0].key, '1');
123
+ a.equal(result[0].ctrlKey, true);
124
+ a.equal(result[0].shiftKey, true);
125
+ });
126
+ it.should('handle unsupported modifier combinations', a => {
127
+ const result = (0, index_js_1.parseKey)('ctrl+alt+meta+z');
128
+ a.equal(result.length, 1);
129
+ a.equal(result[0].key, 'z');
130
+ a.equal(result[0].ctrlKey, true);
131
+ a.equal(result[0].altKey, true);
132
+ a.equal(result[0].metaKey, true);
133
+ });
134
+ it.should('correctly parse special character keys', a => {
135
+ const result = (0, index_js_1.parseKey)('ctrl+alt+!');
136
+ a.equal(result.length, 1);
137
+ a.equal(result[0].key, '1');
138
+ a.equal(result[0].ctrlKey, true);
139
+ a.equal(result[0].altKey, true);
140
+ a.equal(result[0].shiftKey, true);
141
+ });
142
+ it.should('handle unsupported character sequences', a => {
143
+ const result = (0, index_js_1.parseKey)('ctrl+alt+ctrl+z');
144
+ a.equal(result.length, 1);
145
+ a.equal(result[0].key, 'z');
146
+ a.equal(result[0].ctrlKey, true);
147
+ a.equal(result[0].altKey, true);
92
148
  });
93
149
  });
94
150
  s.test('handleKeyboard', it => {
@@ -147,5 +203,37 @@ exports.default = (0, spec_1.spec)('keyboard', s => {
147
203
  dispose();
148
204
  a.equal(off.lastEvent?.called, 1);
149
205
  });
206
+ it.should('handle rapid key presses correctly', (a) => {
207
+ const elementMock = a.element('div');
208
+ const on = a.spyFn(elementMock, 'addEventListener');
209
+ const onKeyMock = (0, spec_1.mockFn)(key => key === 'x y');
210
+ const dispose = (0, index_js_1.handleKeyboard)({
211
+ element: elementMock,
212
+ onKey: onKeyMock,
213
+ delay: 100,
214
+ layout: undefined,
215
+ capture: false,
216
+ });
217
+ const eventX = new KeyboardEvent('keydown', { key: 'x' });
218
+ const eventY = new KeyboardEvent('keydown', { key: 'y' });
219
+ a.assert(typeof on.lastEvent?.arguments[1] === 'function');
220
+ on.lastEvent?.arguments[1](eventX);
221
+ on.lastEvent?.arguments[1](eventY);
222
+ a.equal(onKeyMock.lastArguments?.[0], 'x y');
223
+ dispose();
224
+ });
225
+ it.should('verify event listener is removed on dispose', (a) => {
226
+ const elementMock = a.element('div');
227
+ const off = a.spyFn(elementMock, 'removeEventListener');
228
+ const dispose = (0, index_js_1.handleKeyboard)({
229
+ element: elementMock,
230
+ onKey: (0, spec_1.mockFn)(() => true),
231
+ delay: 100,
232
+ layout: undefined,
233
+ capture: false,
234
+ });
235
+ dispose();
236
+ a.equal(off.lastEvent?.called, 1);
237
+ });
150
238
  });
151
239
  });