@cxl/keyboard 0.0.1 → 0.1.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 +2 -2
- package/amd/index.d.ts +86 -0
- package/amd/index.js +1 -0
- package/index.d.ts +67 -7
- package/index.js +74 -29
- package/index.min.js +1 -0
- package/mjs/index.d.ts +85 -0
- package/mjs/index.js +1 -0
- package/package.json +7 -8
- package/test.js +127 -1
- package/index.js.map +0 -1
- package/test.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/js/%40cxl%2Fkeyboard)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Keyboard Handling Library
|
|
6
6
|
|
|
7
7
|
## Project Details
|
|
8
8
|
|
|
9
|
-
- Branch Version: [0.0
|
|
9
|
+
- Branch Version: [0.1.0](https://npmjs.com/package/@cxl/keyboard/v/0.1.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
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/// <amd-module name="@cxl/keyboard" />
|
|
2
|
+
interface Key {
|
|
3
|
+
ctrlKey: boolean;
|
|
4
|
+
altKey: boolean;
|
|
5
|
+
shiftKey: boolean;
|
|
6
|
+
metaKey: boolean;
|
|
7
|
+
key: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The `KeyboardOptions` interface defines the configuration needed to handle keyboard interactions with
|
|
11
|
+
* a specific HTML element.
|
|
12
|
+
*/
|
|
13
|
+
export interface KeyboardOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Specifies the HTML element on which keyboard events are captured.
|
|
16
|
+
* This element will receive focus and listen for key events.
|
|
17
|
+
*/
|
|
18
|
+
element: HTMLElement;
|
|
19
|
+
/**
|
|
20
|
+
* A callback function that processes key events. It receives the current key being pressed
|
|
21
|
+
* and the sequence of keys accumulated so far. It returns `true` or `false` to indicate whether
|
|
22
|
+
* the sequence was consumed.
|
|
23
|
+
*/
|
|
24
|
+
onKey: (key: string, sequence: string[]) => boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The time in milliseconds to wait between key presses before resetting the sequence.
|
|
27
|
+
* Defaults to 250ms if not provided, ensuring thoughtful key sequences rather than accidental key presses.
|
|
28
|
+
*/
|
|
29
|
+
delay?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Optional parameter to specify a custom `KeyboardLayout`.
|
|
32
|
+
* Defaults to 'en-US' if not provided. This affects how key combinations and modifier keys are interpreted.
|
|
33
|
+
*/
|
|
34
|
+
layout?: KeyboardLayout;
|
|
35
|
+
/**
|
|
36
|
+
* Boolean indicating whether to capture key events in the capture phase.
|
|
37
|
+
* This determines when the event listener reacts in the event propagation flow.
|
|
38
|
+
*/
|
|
39
|
+
capture?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface KeyboardLayout {
|
|
42
|
+
shiftedKeys: string[];
|
|
43
|
+
shiftMap: Record<string, string>;
|
|
44
|
+
modKey: 'metaKey' | 'ctrlKey';
|
|
45
|
+
}
|
|
46
|
+
export declare const KeyboardLayoutData: Record<string, KeyboardLayout>;
|
|
47
|
+
/**
|
|
48
|
+
* The `handleKeyboard` function sets up event listeners to handle keyboard events on a given HTML element.
|
|
49
|
+
* It accepts a configuration object (`KeyboardOptions`) which includes the target HTML element, a callback
|
|
50
|
+
* `onKey` to handle keyboard sequences, a `delay` for typing sequence recognition, an optional keyboard
|
|
51
|
+
* layout, and an optional capture mode for events.
|
|
52
|
+
*
|
|
53
|
+
* - Defaults the delay to 250ms if not specified, using this delay to determine when to treat subsequent
|
|
54
|
+
* key presses as part of the same sequence or a new one.
|
|
55
|
+
* - Determines the keyboard layout, either from the provided options or defaults, which affects how keys
|
|
56
|
+
* (especially with modifiers) are interpreted.
|
|
57
|
+
* - Adds a `keydown` event listener to the provided element, allowing modification of event behavior such
|
|
58
|
+
* as propagation and default handling based on the `onKey` callback result.
|
|
59
|
+
* - Builds a key sequence based on timing and calls the `onKey` handler with the assembled sequence,
|
|
60
|
+
* stopping event propagation based on handler response.
|
|
61
|
+
* - The handler uses the sequence to manage shortcuts and performs preventative measures against default
|
|
62
|
+
* browser actions.
|
|
63
|
+
* @return A cleanup function that removes the event listener.
|
|
64
|
+
*/
|
|
65
|
+
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[];
|
|
76
|
+
/**
|
|
77
|
+
* Converts a string representing a key combination into a normalized string format.
|
|
78
|
+
* This function takes a `key` string and an optional `layout` object (defaults to the user's layout).
|
|
79
|
+
* It uses `parseKey` to break the key combination into individual `Key` objects, each representing
|
|
80
|
+
* a parsed key press. It then converts each `Key` object back to a string using `keyboardEventToString`,
|
|
81
|
+
* considering the layout's details like `shiftedKeys` and `shiftMap`. The resulting
|
|
82
|
+
* strings are concatenated with spaces and returned as the normalized form. This is useful
|
|
83
|
+
* for ensuring consistent representation of key sequences across different keyboard layouts.
|
|
84
|
+
*/
|
|
85
|
+
export declare function normalize(key: string, layout?: KeyboardLayout): string;
|
|
86
|
+
export {};
|
package/amd/index.js
ADDED
|
@@ -0,0 +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}}));
|
package/index.d.ts
CHANGED
|
@@ -1,26 +1,86 @@
|
|
|
1
1
|
/// <amd-module name="@cxl/keyboard" />
|
|
2
|
-
|
|
3
|
-
export interface Key {
|
|
2
|
+
interface Key {
|
|
4
3
|
ctrlKey: boolean;
|
|
5
4
|
altKey: boolean;
|
|
6
5
|
shiftKey: boolean;
|
|
7
6
|
metaKey: boolean;
|
|
8
7
|
key: string;
|
|
9
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* The `KeyboardOptions` interface defines the configuration needed to handle keyboard interactions with
|
|
11
|
+
* a specific HTML element.
|
|
12
|
+
*/
|
|
10
13
|
export interface KeyboardOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Specifies the HTML element on which keyboard events are captured.
|
|
16
|
+
* This element will receive focus and listen for key events.
|
|
17
|
+
*/
|
|
11
18
|
element: HTMLElement;
|
|
19
|
+
/**
|
|
20
|
+
* A callback function that processes key events. It receives the current key being pressed
|
|
21
|
+
* and the sequence of keys accumulated so far. It returns `true` or `false` to indicate whether
|
|
22
|
+
* the sequence was consumed.
|
|
23
|
+
*/
|
|
12
24
|
onKey: (key: string, sequence: string[]) => boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The time in milliseconds to wait between key presses before resetting the sequence.
|
|
27
|
+
* Defaults to 250ms if not provided, ensuring thoughtful key sequences rather than accidental key presses.
|
|
28
|
+
*/
|
|
13
29
|
delay?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Optional parameter to specify a custom `KeyboardLayout`.
|
|
32
|
+
* Defaults to 'en-US' if not provided. This affects how key combinations and modifier keys are interpreted.
|
|
33
|
+
*/
|
|
14
34
|
layout?: KeyboardLayout;
|
|
35
|
+
/**
|
|
36
|
+
* Boolean indicating whether to capture key events in the capture phase.
|
|
37
|
+
* This determines when the event listener reacts in the event propagation flow.
|
|
38
|
+
*/
|
|
39
|
+
capture?: boolean;
|
|
15
40
|
}
|
|
16
41
|
export interface KeyboardLayout {
|
|
17
42
|
shiftedKeys: string[];
|
|
18
43
|
shiftMap: Record<string, string>;
|
|
19
44
|
modKey: 'metaKey' | 'ctrlKey';
|
|
20
45
|
}
|
|
21
|
-
export declare const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
46
|
+
export declare const KeyboardLayoutData: Record<string, KeyboardLayout>;
|
|
47
|
+
/**
|
|
48
|
+
* The `handleKeyboard` function sets up event listeners to handle keyboard events on a given HTML element.
|
|
49
|
+
* It accepts a configuration object (`KeyboardOptions`) which includes the target HTML element, a callback
|
|
50
|
+
* `onKey` to handle keyboard sequences, a `delay` for typing sequence recognition, an optional keyboard
|
|
51
|
+
* layout, and an optional capture mode for events.
|
|
52
|
+
*
|
|
53
|
+
* - Defaults the delay to 250ms if not specified, using this delay to determine when to treat subsequent
|
|
54
|
+
* key presses as part of the same sequence or a new one.
|
|
55
|
+
* - Determines the keyboard layout, either from the provided options or defaults, which affects how keys
|
|
56
|
+
* (especially with modifiers) are interpreted.
|
|
57
|
+
* - Adds a `keydown` event listener to the provided element, allowing modification of event behavior such
|
|
58
|
+
* as propagation and default handling based on the `onKey` callback result.
|
|
59
|
+
* - Builds a key sequence based on timing and calls the `onKey` handler with the assembled sequence,
|
|
60
|
+
* stopping event propagation based on handler response.
|
|
61
|
+
* - The handler uses the sequence to manage shortcuts and performs preventative measures against default
|
|
62
|
+
* browser actions.
|
|
63
|
+
* @return A cleanup function that removes the event listener.
|
|
64
|
+
*/
|
|
65
|
+
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[];
|
|
76
|
+
/**
|
|
77
|
+
* Converts a string representing a key combination into a normalized string format.
|
|
78
|
+
* This function takes a `key` string and an optional `layout` object (defaults to the user's layout).
|
|
79
|
+
* It uses `parseKey` to break the key combination into individual `Key` objects, each representing
|
|
80
|
+
* a parsed key press. It then converts each `Key` object back to a string using `keyboardEventToString`,
|
|
81
|
+
* considering the layout's details like `shiftedKeys` and `shiftMap`. The resulting
|
|
82
|
+
* strings are concatenated with spaces and returned as the normalized form. This is useful
|
|
83
|
+
* for ensuring consistent representation of key sequences across different keyboard layouts.
|
|
84
|
+
*/
|
|
26
85
|
export declare function normalize(key: string, layout?: KeyboardLayout): string;
|
|
86
|
+
export {};
|
package/index.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.
|
|
3
|
+
exports.KeyboardLayoutData = void 0;
|
|
4
|
+
exports.handleKeyboard = handleKeyboard;
|
|
5
|
+
exports.parseKey = parseKey;
|
|
6
|
+
exports.normalize = normalize;
|
|
7
|
+
///<amd-module name="@cxl/keyboard"/>
|
|
5
8
|
const navigator = typeof window !== 'undefined'
|
|
6
9
|
? window.navigator
|
|
7
10
|
: { language: 'en-US', platform: 'nodejs' };
|
|
8
11
|
const IS_MAC = /Mac|iPod|iPhone|iPad/;
|
|
9
|
-
const PARSE_KEY = /(shift|ctrl|control|alt|option|meta|command|cmd|mod
|
|
12
|
+
const PARSE_KEY = /(shift|ctrl|control|alt|option|meta|command|cmd|mod|[^\s+]+)(\s*\+|\s)?/g;
|
|
10
13
|
const SHIFT_MAP = {
|
|
11
14
|
'/': '?',
|
|
12
15
|
'.': '>',
|
|
@@ -30,24 +33,29 @@ const SHIFT_MAP = {
|
|
|
30
33
|
'9': '(',
|
|
31
34
|
'0': ')',
|
|
32
35
|
};
|
|
33
|
-
|
|
36
|
+
exports.KeyboardLayoutData = {
|
|
34
37
|
'en-US': {
|
|
35
38
|
shiftedKeys: Object.values(SHIFT_MAP),
|
|
36
39
|
shiftMap: SHIFT_MAP,
|
|
37
40
|
modKey: IS_MAC.test(navigator.platform) ? 'metaKey' : 'ctrlKey',
|
|
38
41
|
},
|
|
39
42
|
};
|
|
40
|
-
|
|
41
|
-
Shift: '',
|
|
42
|
-
Alt: '',
|
|
43
|
-
Meta: '',
|
|
44
|
-
Control: '',
|
|
43
|
+
const TranslateKey = {
|
|
45
44
|
ArrowUp: 'up',
|
|
46
45
|
ArrowDown: 'down',
|
|
47
46
|
ArrowLeft: 'left',
|
|
48
47
|
ArrowRight: 'right',
|
|
49
48
|
Escape: 'esc',
|
|
49
|
+
' ': 'space',
|
|
50
50
|
};
|
|
51
|
+
/**
|
|
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.
|
|
58
|
+
*/
|
|
51
59
|
function keyboardEventToString(ev, { shiftedKeys }) {
|
|
52
60
|
const key = ev.key;
|
|
53
61
|
if (!key ||
|
|
@@ -56,7 +64,7 @@ function keyboardEventToString(ev, { shiftedKeys }) {
|
|
|
56
64
|
key === 'Meta' ||
|
|
57
65
|
key === 'Control')
|
|
58
66
|
return '';
|
|
59
|
-
const ch =
|
|
67
|
+
const ch = TranslateKey[key] || key.toLowerCase();
|
|
60
68
|
let result;
|
|
61
69
|
if (ev.ctrlKey)
|
|
62
70
|
result = 'ctrl';
|
|
@@ -68,12 +76,30 @@ function keyboardEventToString(ev, { shiftedKeys }) {
|
|
|
68
76
|
result = result ? result + '+meta' : 'meta';
|
|
69
77
|
return result ? result + '+' + ch : ch;
|
|
70
78
|
}
|
|
71
|
-
|
|
79
|
+
/**
|
|
80
|
+
* The `handleKeyboard` function sets up event listeners to handle keyboard events on a given HTML element.
|
|
81
|
+
* It accepts a configuration object (`KeyboardOptions`) which includes the target HTML element, a callback
|
|
82
|
+
* `onKey` to handle keyboard sequences, a `delay` for typing sequence recognition, an optional keyboard
|
|
83
|
+
* layout, and an optional capture mode for events.
|
|
84
|
+
*
|
|
85
|
+
* - Defaults the delay to 250ms if not specified, using this delay to determine when to treat subsequent
|
|
86
|
+
* key presses as part of the same sequence or a new one.
|
|
87
|
+
* - Determines the keyboard layout, either from the provided options or defaults, which affects how keys
|
|
88
|
+
* (especially with modifiers) are interpreted.
|
|
89
|
+
* - Adds a `keydown` event listener to the provided element, allowing modification of event behavior such
|
|
90
|
+
* as propagation and default handling based on the `onKey` callback result.
|
|
91
|
+
* - Builds a key sequence based on timing and calls the `onKey` handler with the assembled sequence,
|
|
92
|
+
* stopping event propagation based on handler response.
|
|
93
|
+
* - The handler uses the sequence to manage shortcuts and performs preventative measures against default
|
|
94
|
+
* browser actions.
|
|
95
|
+
* @return A cleanup function that removes the event listener.
|
|
96
|
+
*/
|
|
97
|
+
function handleKeyboard({ element, onKey, delay, layout, capture, }) {
|
|
72
98
|
const D = delay === undefined ? 250 : delay;
|
|
73
99
|
const locale = navigator.language;
|
|
74
100
|
let sequence = [];
|
|
75
101
|
let lastT = 0;
|
|
76
|
-
const newLayout = layout || KeyboardLayoutData[locale] || KeyboardLayoutData['en-US'];
|
|
102
|
+
const newLayout = layout || exports.KeyboardLayoutData[locale] || exports.KeyboardLayoutData['en-US'];
|
|
77
103
|
function handler(ev) {
|
|
78
104
|
const k = keyboardEventToString(ev, newLayout);
|
|
79
105
|
let t = Date.now();
|
|
@@ -95,14 +121,12 @@ function handleKeyboard({ element, onKey, delay, layout, }) {
|
|
|
95
121
|
} while (seq.length);
|
|
96
122
|
lastT = t;
|
|
97
123
|
}
|
|
98
|
-
element.addEventListener('keydown', handler);
|
|
99
|
-
return () => element.removeEventListener('keydown', handler);
|
|
124
|
+
element.addEventListener('keydown', handler, { capture });
|
|
125
|
+
return () => element.removeEventListener('keydown', handler, { capture });
|
|
100
126
|
}
|
|
101
|
-
exports.handleKeyboard = handleKeyboard;
|
|
102
127
|
function getDefaultLayout() {
|
|
103
|
-
return (KeyboardLayoutData[navigator
|
|
128
|
+
return (exports.KeyboardLayoutData[navigator?.language] || exports.KeyboardLayoutData['en-US']);
|
|
104
129
|
}
|
|
105
|
-
exports.getDefaultLayout = getDefaultLayout;
|
|
106
130
|
function newKey() {
|
|
107
131
|
return {
|
|
108
132
|
ctrlKey: false,
|
|
@@ -112,7 +136,16 @@ function newKey() {
|
|
|
112
136
|
key: '',
|
|
113
137
|
};
|
|
114
138
|
}
|
|
115
|
-
|
|
139
|
+
/**
|
|
140
|
+
* This function, `parseKey`, takes a key combination string and splits it into a sequence of `Key` objects.
|
|
141
|
+
* It uses regular expressions to identify each part of the key combination, accounting for modifier keys
|
|
142
|
+
* like 'shift', 'ctrl', 'alt', 'meta', and 'mod', the latter being dependent on the `modKey` from the
|
|
143
|
+
* `KeyboardLayout` (either 'ctrlKey' or 'metaKey' depending on the platform). For non-modifier keys, it
|
|
144
|
+
* checks if the `shiftKey` is active and if the key should be shifted using the `shiftMap`. This logic
|
|
145
|
+
* ensures the function captures accurate key state details in preparation for further handling like matching
|
|
146
|
+
* key sequences in the `handleKeyboard` function.
|
|
147
|
+
*/
|
|
148
|
+
function parseKey(key, { modKey, shiftedKeys, shiftMap } = getDefaultLayout()) {
|
|
116
149
|
const sequence = [];
|
|
117
150
|
let match;
|
|
118
151
|
let event = newKey();
|
|
@@ -129,10 +162,19 @@ function parseKey(key, { modKey, shiftMap } = getDefaultLayout()) {
|
|
|
129
162
|
else if (ch === 'mod')
|
|
130
163
|
event[modKey] = true;
|
|
131
164
|
else {
|
|
132
|
-
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
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
|
+
}
|
|
177
|
+
}
|
|
136
178
|
}
|
|
137
179
|
if (match[2] !== '+') {
|
|
138
180
|
sequence.push(event);
|
|
@@ -141,13 +183,16 @@ function parseKey(key, { modKey, shiftMap } = getDefaultLayout()) {
|
|
|
141
183
|
}
|
|
142
184
|
return sequence;
|
|
143
185
|
}
|
|
144
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Converts a string representing a key combination into a normalized string format.
|
|
188
|
+
* This function takes a `key` string and an optional `layout` object (defaults to the user's layout).
|
|
189
|
+
* It uses `parseKey` to break the key combination into individual `Key` objects, each representing
|
|
190
|
+
* a parsed key press. It then converts each `Key` object back to a string using `keyboardEventToString`,
|
|
191
|
+
* considering the layout's details like `shiftedKeys` and `shiftMap`. The resulting
|
|
192
|
+
* strings are concatenated with spaces and returned as the normalized form. This is useful
|
|
193
|
+
* for ensuring consistent representation of key sequences across different keyboard layouts.
|
|
194
|
+
*/
|
|
145
195
|
function normalize(key, layout = getDefaultLayout()) {
|
|
146
196
|
const sequence = parseKey(key, layout);
|
|
147
|
-
|
|
148
|
-
while (i--)
|
|
149
|
-
sequence[i] = keyboardEventToString(sequence[i], layout);
|
|
150
|
-
return sequence.join(' ');
|
|
197
|
+
return sequence.map(key => keyboardEventToString(key, layout)).join(' ');
|
|
151
198
|
}
|
|
152
|
-
exports.normalize = normalize;
|
|
153
|
-
//# sourceMappingURL=index.js.map
|
package/index.min.js
ADDED
|
@@ -0,0 +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(" ")}
|
package/mjs/index.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
interface Key {
|
|
2
|
+
ctrlKey: boolean;
|
|
3
|
+
altKey: boolean;
|
|
4
|
+
shiftKey: boolean;
|
|
5
|
+
metaKey: boolean;
|
|
6
|
+
key: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The `KeyboardOptions` interface defines the configuration needed to handle keyboard interactions with
|
|
10
|
+
* a specific HTML element.
|
|
11
|
+
*/
|
|
12
|
+
export interface KeyboardOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Specifies the HTML element on which keyboard events are captured.
|
|
15
|
+
* This element will receive focus and listen for key events.
|
|
16
|
+
*/
|
|
17
|
+
element: HTMLElement;
|
|
18
|
+
/**
|
|
19
|
+
* A callback function that processes key events. It receives the current key being pressed
|
|
20
|
+
* and the sequence of keys accumulated so far. It returns `true` or `false` to indicate whether
|
|
21
|
+
* the sequence was consumed.
|
|
22
|
+
*/
|
|
23
|
+
onKey: (key: string, sequence: string[]) => boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The time in milliseconds to wait between key presses before resetting the sequence.
|
|
26
|
+
* Defaults to 250ms if not provided, ensuring thoughtful key sequences rather than accidental key presses.
|
|
27
|
+
*/
|
|
28
|
+
delay?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Optional parameter to specify a custom `KeyboardLayout`.
|
|
31
|
+
* Defaults to 'en-US' if not provided. This affects how key combinations and modifier keys are interpreted.
|
|
32
|
+
*/
|
|
33
|
+
layout?: KeyboardLayout;
|
|
34
|
+
/**
|
|
35
|
+
* Boolean indicating whether to capture key events in the capture phase.
|
|
36
|
+
* This determines when the event listener reacts in the event propagation flow.
|
|
37
|
+
*/
|
|
38
|
+
capture?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface KeyboardLayout {
|
|
41
|
+
shiftedKeys: string[];
|
|
42
|
+
shiftMap: Record<string, string>;
|
|
43
|
+
modKey: 'metaKey' | 'ctrlKey';
|
|
44
|
+
}
|
|
45
|
+
export declare const KeyboardLayoutData: Record<string, KeyboardLayout>;
|
|
46
|
+
/**
|
|
47
|
+
* The `handleKeyboard` function sets up event listeners to handle keyboard events on a given HTML element.
|
|
48
|
+
* It accepts a configuration object (`KeyboardOptions`) which includes the target HTML element, a callback
|
|
49
|
+
* `onKey` to handle keyboard sequences, a `delay` for typing sequence recognition, an optional keyboard
|
|
50
|
+
* layout, and an optional capture mode for events.
|
|
51
|
+
*
|
|
52
|
+
* - Defaults the delay to 250ms if not specified, using this delay to determine when to treat subsequent
|
|
53
|
+
* key presses as part of the same sequence or a new one.
|
|
54
|
+
* - Determines the keyboard layout, either from the provided options or defaults, which affects how keys
|
|
55
|
+
* (especially with modifiers) are interpreted.
|
|
56
|
+
* - Adds a `keydown` event listener to the provided element, allowing modification of event behavior such
|
|
57
|
+
* as propagation and default handling based on the `onKey` callback result.
|
|
58
|
+
* - Builds a key sequence based on timing and calls the `onKey` handler with the assembled sequence,
|
|
59
|
+
* stopping event propagation based on handler response.
|
|
60
|
+
* - The handler uses the sequence to manage shortcuts and performs preventative measures against default
|
|
61
|
+
* browser actions.
|
|
62
|
+
* @return A cleanup function that removes the event listener.
|
|
63
|
+
*/
|
|
64
|
+
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[];
|
|
75
|
+
/**
|
|
76
|
+
* Converts a string representing a key combination into a normalized string format.
|
|
77
|
+
* This function takes a `key` string and an optional `layout` object (defaults to the user's layout).
|
|
78
|
+
* It uses `parseKey` to break the key combination into individual `Key` objects, each representing
|
|
79
|
+
* a parsed key press. It then converts each `Key` object back to a string using `keyboardEventToString`,
|
|
80
|
+
* considering the layout's details like `shiftedKeys` and `shiftMap`. The resulting
|
|
81
|
+
* strings are concatenated with spaces and returned as the normalized form. This is useful
|
|
82
|
+
* for ensuring consistent representation of key sequences across different keyboard layouts.
|
|
83
|
+
*/
|
|
84
|
+
export declare function normalize(key: string, layout?: KeyboardLayout): string;
|
|
85
|
+
export {};
|
package/mjs/index.js
ADDED
|
@@ -0,0 +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(" ")}
|
package/package.json
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cxl/keyboard",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Keyboard Handling Library",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"files": [
|
|
7
7
|
"*.js",
|
|
8
8
|
"*.d.ts",
|
|
9
|
-
"*.
|
|
9
|
+
"*.css",
|
|
10
10
|
"amd/*.js",
|
|
11
11
|
"amd/*.d.ts",
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"es6/*.d.ts",
|
|
15
|
-
"es6/*.js.map",
|
|
12
|
+
"mjs/*.js",
|
|
13
|
+
"mjs/*.d.ts",
|
|
16
14
|
"LICENSE",
|
|
17
15
|
"*.md"
|
|
18
16
|
],
|
|
19
17
|
"main": "index.js",
|
|
18
|
+
"browser": "amd/index.js",
|
|
20
19
|
"homepage": "https://cxlio.github.io/cxl/keyboard",
|
|
21
20
|
"bugs": "https://github.com/cxlio/cxl/issues",
|
|
22
21
|
"repository": {
|
|
@@ -24,4 +23,4 @@
|
|
|
24
23
|
"url": "https://github.com/cxlio/cxl.git",
|
|
25
24
|
"directory": "keyboard"
|
|
26
25
|
}
|
|
27
|
-
}
|
|
26
|
+
}
|
package/test.js
CHANGED
|
@@ -4,11 +4,16 @@ const spec_1 = require("@cxl/spec");
|
|
|
4
4
|
const index_js_1 = require("@cxl/keyboard");
|
|
5
5
|
exports.default = (0, spec_1.spec)('keyboard', s => {
|
|
6
6
|
s.test('normalize', it => {
|
|
7
|
+
it.should('handle empty and null inputs', a => {
|
|
8
|
+
a.equal((0, index_js_1.normalize)(''), '');
|
|
9
|
+
});
|
|
7
10
|
it.should('parse single keys', a => {
|
|
8
11
|
a.equal((0, index_js_1.normalize)(' x '), 'x');
|
|
12
|
+
a.equal((0, index_js_1.normalize)('ctrl+x'), 'ctrl+x');
|
|
9
13
|
});
|
|
10
14
|
it.should('parse multiple keys', a => {
|
|
11
15
|
a.equal((0, index_js_1.normalize)(' x y'), 'x y');
|
|
16
|
+
a.equal((0, index_js_1.normalize)('x y'), 'x y');
|
|
12
17
|
a.equal((0, index_js_1.normalize)('ctrl+shift+x'), 'ctrl+shift+x');
|
|
13
18
|
});
|
|
14
19
|
it.should('parse mod key', a => {
|
|
@@ -18,8 +23,129 @@ exports.default = (0, spec_1.spec)('keyboard', s => {
|
|
|
18
23
|
});
|
|
19
24
|
it.should('normalize shifted keys', a => {
|
|
20
25
|
a.equal((0, index_js_1.normalize)('shift+5'), '%');
|
|
26
|
+
a.equal((0, index_js_1.normalize)('shift+A'), 'shift+a');
|
|
27
|
+
a.equal((0, index_js_1.normalize)('shift+y'), 'shift+y');
|
|
21
28
|
a.equal((0, index_js_1.normalize)('shift+ctrl+5'), 'ctrl+%');
|
|
22
29
|
});
|
|
30
|
+
it.should('handle non character keys', a => {
|
|
31
|
+
a.equal((0, index_js_1.normalize)(':'), ':');
|
|
32
|
+
a.equal((0, index_js_1.normalize)('/'), '/');
|
|
33
|
+
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+:');
|
|
38
|
+
});
|
|
39
|
+
it.should('normalize special keys with modifiers', a => {
|
|
40
|
+
a.equal((0, index_js_1.normalize)('ctrl+alt+del'), 'ctrl+alt+del');
|
|
41
|
+
a.equal((0, index_js_1.normalize)('ctrl+shift+esc'), 'ctrl+shift+esc');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
s.test('parseKey', it => {
|
|
45
|
+
it.should('correctly parse simple keys without modifiers', a => {
|
|
46
|
+
const result = (0, index_js_1.parseKey)('a');
|
|
47
|
+
a.equal(result.length, 1);
|
|
48
|
+
a.equal(result[0].key, 'a');
|
|
49
|
+
a.equal(result[0].shiftKey, false);
|
|
50
|
+
a.equal(result[0].ctrlKey, false);
|
|
51
|
+
a.equal(result[0].altKey, false);
|
|
52
|
+
a.equal(result[0].metaKey, false);
|
|
53
|
+
});
|
|
54
|
+
it.should('correctly parse modifier keys', a => {
|
|
55
|
+
const result = (0, index_js_1.parseKey)('shift+b');
|
|
56
|
+
a.equal(result.length, 1);
|
|
57
|
+
a.equal(result[0].key, 'b');
|
|
58
|
+
a.equal(result[0].shiftKey, true);
|
|
59
|
+
a.equal(result[0].ctrlKey, false);
|
|
60
|
+
a.equal(result[0].altKey, false);
|
|
61
|
+
a.equal(result[0].metaKey, false);
|
|
62
|
+
});
|
|
63
|
+
it.should('correctly parse multiple keys with modifiers', a => {
|
|
64
|
+
const result = (0, index_js_1.parseKey)('ctrl+alt+del');
|
|
65
|
+
a.equal(result.length, 1);
|
|
66
|
+
a.equal(result[0].key, 'del');
|
|
67
|
+
a.equal(result[0].shiftKey, false);
|
|
68
|
+
a.equal(result[0].ctrlKey, true);
|
|
69
|
+
a.equal(result[0].altKey, true);
|
|
70
|
+
a.equal(result[0].metaKey, false);
|
|
71
|
+
});
|
|
72
|
+
it.should('handle key sequences separated by spaces', a => {
|
|
73
|
+
const result = (0, index_js_1.parseKey)('ctrl+a ctrl+b');
|
|
74
|
+
a.equal(result.length, 2);
|
|
75
|
+
a.equal(result[0].key, 'a');
|
|
76
|
+
a.equal(result[0].ctrlKey, true);
|
|
77
|
+
a.equal(result[1].key, 'b');
|
|
78
|
+
a.equal(result[1].ctrlKey, true);
|
|
79
|
+
});
|
|
80
|
+
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');
|
|
83
|
+
a.equal(result.length, 1);
|
|
84
|
+
a.equal(result[0].key, 'c');
|
|
85
|
+
a.equal(result[0][defaultLayout.modKey], true);
|
|
86
|
+
});
|
|
87
|
+
it.should('handle shifted characters correctly', a => {
|
|
88
|
+
const result = (0, index_js_1.parseKey)('shift+1');
|
|
89
|
+
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);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
s.test('handleKeyboard', it => {
|
|
95
|
+
it.should('call onKey with the correct sequence', (a) => {
|
|
96
|
+
// Setup a mock element
|
|
97
|
+
const elementMock = a.element('div');
|
|
98
|
+
const on = a.spyFn(elementMock, 'addEventListener');
|
|
99
|
+
const off = a.spyFn(elementMock, 'removeEventListener');
|
|
100
|
+
// Setup a mock callback
|
|
101
|
+
const onKeyMock = (0, spec_1.mockFn)(key => (key === 'a b' ? true : false));
|
|
102
|
+
// Register the handler
|
|
103
|
+
const dispose = (0, index_js_1.handleKeyboard)({
|
|
104
|
+
element: elementMock,
|
|
105
|
+
onKey: onKeyMock,
|
|
106
|
+
delay: 100,
|
|
107
|
+
layout: undefined,
|
|
108
|
+
capture: false,
|
|
109
|
+
});
|
|
110
|
+
// Simulate keydown events
|
|
111
|
+
const eventA = new KeyboardEvent('keydown', { key: 'a' });
|
|
112
|
+
const eventB = new KeyboardEvent('keydown', { key: 'b' });
|
|
113
|
+
// This will call the handler linked to the event
|
|
114
|
+
a.assert(typeof on.lastEvent?.arguments[1] === 'function');
|
|
115
|
+
on.lastEvent?.arguments[1](eventA);
|
|
116
|
+
a.equal(onKeyMock.lastArguments?.[0], 'a');
|
|
117
|
+
on.lastEvent?.arguments[1](eventB);
|
|
118
|
+
a.equal(onKeyMock.lastArguments?.[0], 'a b');
|
|
119
|
+
// Cleanup
|
|
120
|
+
dispose();
|
|
121
|
+
a.equal(off.lastEvent?.called, 1);
|
|
122
|
+
});
|
|
123
|
+
it.should('reset sequence after delay', async (a) => {
|
|
124
|
+
const elementMock = a.element('div');
|
|
125
|
+
const on = a.spyFn(elementMock, 'addEventListener');
|
|
126
|
+
const off = a.spyFn(elementMock, 'removeEventListener');
|
|
127
|
+
const onKeyMock = (0, spec_1.mockFn)(key => (key === 'a b' ? true : false));
|
|
128
|
+
const dispose = (0, index_js_1.handleKeyboard)({
|
|
129
|
+
element: elementMock,
|
|
130
|
+
onKey: onKeyMock,
|
|
131
|
+
delay: 100,
|
|
132
|
+
layout: undefined,
|
|
133
|
+
capture: false,
|
|
134
|
+
});
|
|
135
|
+
// Simulate keydown event
|
|
136
|
+
const eventA = new KeyboardEvent('keydown', { key: 'a' });
|
|
137
|
+
a.assert(typeof on.lastEvent?.arguments[1] === 'function');
|
|
138
|
+
on.lastEvent?.arguments[1](eventA);
|
|
139
|
+
// Advance time beyond delay
|
|
140
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
141
|
+
a.equal(onKeyMock.lastArguments?.[0], 'a');
|
|
142
|
+
// Simulate another keydown event
|
|
143
|
+
const eventC = new KeyboardEvent('keydown', { key: 'c' });
|
|
144
|
+
on.lastEvent?.arguments[1](eventC);
|
|
145
|
+
a.equal(onKeyMock.lastArguments?.[0], 'c');
|
|
146
|
+
// Cleanup
|
|
147
|
+
dispose();
|
|
148
|
+
a.equal(off.lastEvent?.called, 1);
|
|
149
|
+
});
|
|
23
150
|
});
|
|
24
151
|
});
|
|
25
|
-
//# sourceMappingURL=test.js.map
|
package/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../keyboard/index.ts"],"names":[],"mappings":"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"}
|
package/test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test.js","sourceRoot":"","sources":["../../keyboard/test.ts"],"names":[],"mappings":";;AAAA,oCAAiC;AACjC,4CAAuC;AAEvC,kBAAe,IAAA,WAAI,EAAC,UAAU,EAAE,CAAC,CAAC,EAAE;IACnC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE;QACxB,EAAE,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC,CAAC,EAAE;YAClC,CAAC,CAAC,KAAK,CAAC,IAAA,oBAAS,EAAC,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,MAAM,CAAC,qBAAqB,EAAE,CAAC,CAAC,EAAE;YACpC,CAAC,CAAC,KAAK,CAAC,IAAA,oBAAS,EAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC;YACpC,CAAC,CAAC,KAAK,CAAC,IAAA,oBAAS,EAAC,cAAc,CAAC,EAAE,cAAc,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC,EAAE;YAC9B,CAAC,CAAC,KAAK,CAAC,IAAA,oBAAS,EAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;YACtC,CAAC,CAAC,KAAK,CAAC,IAAA,oBAAS,EAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;YACtC,CAAC,CAAC,KAAK,CAAC,IAAA,oBAAS,EAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,MAAM,CAAC,wBAAwB,EAAE,CAAC,CAAC,EAAE;YACvC,CAAC,CAAC,KAAK,CAAC,IAAA,oBAAS,EAAC,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC;YACnC,CAAC,CAAC,KAAK,CAAC,IAAA,oBAAS,EAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
|