Skip to content

Commit 3e76476

Browse files
committed
Refactor keyboard package
In preparation for changes.
1 parent d4a2ccf commit 3e76476

File tree

4 files changed

+334
-315
lines changed

4 files changed

+334
-315
lines changed

packages/keyboard/src/core.ts

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
// Copyright (c) Jupyter Development Team.
2+
// Distributed under the terms of the Modified BSD License.
3+
/*-----------------------------------------------------------------------------
4+
| Copyright (c) 2014-2017, PhosphorJS Contributors
5+
|
6+
| Distributed under the terms of the BSD 3-Clause License.
7+
|
8+
| The full license is in the file LICENSE, distributed with this software.
9+
|----------------------------------------------------------------------------*/
10+
11+
/**
12+
* An object which represents an abstract keyboard layout.
13+
*/
14+
export interface IKeyboardLayout {
15+
/**
16+
* The human readable name of the layout.
17+
*
18+
* This value is used primarily for display and debugging purposes.
19+
*/
20+
readonly name: string;
21+
22+
/**
23+
* Get an array of all key values supported by the layout.
24+
*
25+
* @returns A new array of the supported key values.
26+
*
27+
* #### Notes
28+
* This can be useful for authoring tools and debugging, when it's
29+
* necessary to know which keys are available for shortcut use.
30+
*/
31+
keys(): string[];
32+
33+
/**
34+
* Test whether the given key is a valid value for the layout.
35+
*
36+
* @param key - The user provided key to test for validity.
37+
*
38+
* @returns `true` if the key is valid, `false` otherwise.
39+
*/
40+
isValidKey(key: string): boolean;
41+
42+
/**
43+
* Test whether the given key is a modifier key.
44+
*
45+
* @param key - The user provided key.
46+
*
47+
* @returns `true` if the key is a modifier key, `false` otherwise.
48+
*
49+
* #### Notes
50+
* This is necessary so that we don't process modifier keys pressed
51+
* in the middle of the key sequence.
52+
* E.g. "Shift C Ctrl P" is actually 4 keydown events:
53+
* "Shift", "Shift P", "Ctrl", "Ctrl P",
54+
* and events for "Shift" and "Ctrl" should be ignored.
55+
*/
56+
isModifierKey(key: string): boolean;
57+
58+
/**
59+
* Get the key for a `'keydown'` event.
60+
*
61+
* @param event - The event object for a `'keydown'` event.
62+
*
63+
* @returns The associated key value, or an empty string if the event
64+
* does not represent a valid primary key.
65+
*/
66+
keyForKeydownEvent(event: KeyboardEvent): string;
67+
}
68+
69+
/**
70+
* A concrete implementation of {@link IKeyboardLayout} based on keycodes.
71+
*
72+
* The `keyCode` property of a `'keydown'` event is a browser and OS
73+
* specific representation of the physical key (not character) which
74+
* was pressed on a keyboard. While not the most convenient API, it
75+
* is currently the only one which works reliably on all browsers.
76+
*
77+
* This class accepts a user-defined mapping of keycode to key, which
78+
* allows for reliable shortcuts tailored to the user's system.
79+
*/
80+
export class KeycodeLayout implements IKeyboardLayout {
81+
/**
82+
* Construct a new keycode layout.
83+
*
84+
* @param name - The human readable name for the layout.
85+
*
86+
* @param codes - A mapping of keycode to key value.
87+
*
88+
* @param modifierKeys - Array of modifier key names
89+
*/
90+
constructor(
91+
name: string,
92+
codes: KeycodeLayout.CodeMap,
93+
modifierKeys: string[] = []
94+
) {
95+
this.name = name;
96+
this._codes = codes;
97+
this._keys = KeycodeLayout.extractKeys(codes);
98+
this._modifierKeys = KeycodeLayout.convertToKeySet(modifierKeys);
99+
}
100+
101+
/**
102+
* The human readable name of the layout.
103+
*/
104+
readonly name: string;
105+
106+
/**
107+
* Get an array of the key values supported by the layout.
108+
*
109+
* @returns A new array of the supported key values.
110+
*/
111+
keys(): string[] {
112+
return Object.keys(this._keys);
113+
}
114+
115+
/**
116+
* Test whether the given key is a valid value for the layout.
117+
*
118+
* @param key - The user provided key to test for validity.
119+
*
120+
* @returns `true` if the key is valid, `false` otherwise.
121+
*/
122+
isValidKey(key: string): boolean {
123+
return key in this._keys;
124+
}
125+
126+
/**
127+
* Test whether the given key is a modifier key.
128+
*
129+
* @param key - The user provided key.
130+
*
131+
* @returns `true` if the key is a modifier key, `false` otherwise.
132+
*/
133+
isModifierKey(key: string): boolean {
134+
return key in this._modifierKeys;
135+
}
136+
137+
/**
138+
* Get the key for a `'keydown'` event.
139+
*
140+
* @param event - The event object for a `'keydown'` event.
141+
*
142+
* @returns The associated key value, or an empty string if
143+
* the event does not represent a valid primary key.
144+
*/
145+
keyForKeydownEvent(event: KeyboardEvent): string {
146+
return this._codes[event.keyCode] || '';
147+
}
148+
149+
private _keys: KeycodeLayout.KeySet;
150+
private _codes: KeycodeLayout.CodeMap;
151+
private _modifierKeys: KeycodeLayout.KeySet;
152+
}
153+
154+
/**
155+
* The namespace for the `KeycodeLayout` class statics.
156+
*/
157+
export namespace KeycodeLayout {
158+
/**
159+
* A type alias for a keycode map.
160+
*/
161+
export type CodeMap = { readonly [code: number]: string };
162+
163+
/**
164+
* A type alias for a key set.
165+
*/
166+
export type KeySet = { readonly [key: string]: boolean };
167+
168+
/**
169+
* Extract the set of keys from a code map.
170+
*
171+
* @param codes - The code map of interest.
172+
*
173+
* @returns A set of the keys in the code map.
174+
*/
175+
export function extractKeys(codes: CodeMap): KeySet {
176+
let keys: any = Object.create(null);
177+
for (let c in codes) {
178+
keys[codes[c]] = true;
179+
}
180+
return keys as KeySet;
181+
}
182+
183+
/**
184+
* Convert array of keys to a key set.
185+
*
186+
* @param keys - The array that needs to be converted
187+
*
188+
* @returns A set of the keys in the array.
189+
*/
190+
export function convertToKeySet(keys: string[]): KeySet {
191+
let keySet = Object(null);
192+
for (let i = 0, n = keys.length; i < n; ++i) {
193+
keySet[keys[i]] = true;
194+
}
195+
return keySet;
196+
}
197+
}

0 commit comments

Comments
 (0)