1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
ash / webui / common / resources / keyboard_key.js [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import './keyboard_icons.html.js';
import 'chrome://resources/ash/common/cr_elements/cr_shared_style.css.js';
import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/ash/common/i18n_behavior.js';
import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getTemplate} from './keyboard_key.html.js';
// TODO(michaelcheco): Add unit test coverage for keyboard-key.
/**
* @fileoverview
* 'keyboard-key' provides a visual representation of a single key for the
* 'keyboard-diagram' component. A single 'keyboard-key' can display one "main
* glyph" and up to four "corner glyphs".
*
* The main glyph can be a text string (specified by 'main-glyph') or an icon
* (specified by 'icon'). By default it is centered vertically and horizontally
* on the key, and icons are scaled to fill the key while maintaining their
* aspect ratio. The main glyph supports ellipsing text strings that don't fit
* on the key, making it suitable for long labels such as "backspace" as well as
* letter keys with single characters on them.
*
* Adding the 'left' or 'right' classes to the key will align the main glyph to
* the left or right side respectively, and set icon widths to 24px.
*
* The four corner glyphs ('top-left-glyph', 'bottom-left-glyph', etc.) must be
* text strings, generally single characters. If at least one of the right-side
* glyphs is set, the corner glyphs will be arranged in the four quadrants of
* the key like so:
*
* +---+
* |a c|
* | |
* |b d|
* +---+
*
* If neither right-side glyph is set, the "left" glyphs will be centered
* horizontally, like so:
*
* +---+
* | a |
* | |
* | b |
* +---+
*
* Both a main glyph and one or more corner glyphs may be set, for example for
* the 'e' key on a German keyboard, which has a centered main glyph but also a
* Euro symbol in the bottom-right:
*
* +---+
* | |
* | e |
* | €|
* +---+
*/
/**
* Enum of key states.
* @enum {string}
*/
export const KeyboardKeyState = {
/** The key has not been pressed during this test session. */
NOT_PRESSED: 'not-pressed',
/** The key is currently pressed. */
PRESSED: 'pressed',
/** The key is not currently pressed, but we've seen it pressed previously. */
TESTED: 'tested',
};
/**
* @constructor
* @extends {PolymerElement}
* @implements {I18nBehaviorInterface}
*/
const KeyboardKeyElementBase =
mixinBehaviors([I18nBehavior], PolymerElement);
/** @polymer */
export class KeyboardKeyElement extends KeyboardKeyElementBase {
static get is() {
return 'keyboard-key';
}
static get template() {
return getTemplate();
}
static get properties() {
return {
/**
* The text to show on the key, if any.
* @type {?string}
*/
mainGlyph: String,
/**
* The text to show in the top-left of the key (or top-center if no
* right-side glyphs are set).
* @type {?string}
*/
topLeftGlyph: String,
/**
* The text to show in the top-right of the key.
* @type {?string}
*/
topRightGlyph: String,
/**
* The text to show in the bottom-left of the key (or bottom-center if no
* right-side glyphs are set).
* @type {?string}
*/
bottomLeftGlyph: String,
/**
* The text to show in the bottom-right of the key.
* @type {?string}
*/
bottomRightGlyph: String,
/** @protected {boolean} */
showCornerGlyphs_: {
type: Boolean,
computed: 'computeShowCornerGlyphs_(' +
'topLeftGlyph, topRightGlyph, bottomLeftGlyph, bottomRightGlyph)',
},
/** @protected {boolean} */
showSecondColumn_: {
type: Boolean,
computed: 'computeShowSecondColumn_(topRightGlyph, bottomRightGlyph)',
},
/**
* The name of the icon to use, if any. The name should be of the form:
* `iconset_name:icon_name`.
* @type {?string}
*/
icon: String,
/**
* The state to display the key in.
* @type {!KeyboardKeyState}
*/
state: {
type: String,
value: KeyboardKeyState.NOT_PRESSED,
reflectToAttribute: true,
observer: KeyboardKeyElement.prototype.keyboardKeyStateChanged,
},
/**
* The key name to report to assistive technologies. Defaults to mainGlyph
* if not set.
* @type {?string}
*/
ariaName: String,
/** @protected {string} */
ariaLabel_: {
type: String,
computed: 'computeAriaLabel_(' +
'ariaName, mainGlyph, bottomLeftGlyph, bottomRightGlyph, state)',
},
};
}
computeAriaLabel_(
ariaName, mainGlyph, bottomLeftGlyph, bottomRightGlyph, state) {
const name =
ariaName || mainGlyph || bottomRightGlyph || bottomLeftGlyph || '';
const stateStringIds = {
[KeyboardKeyState.NOT_PRESSED]: 'keyboardDiagramAriaLabelNotPressed',
[KeyboardKeyState.PRESSED]: 'keyboardDiagramAriaLabelPressed',
[KeyboardKeyState.TESTED]: 'keyboardDiagramAriaLabelTested',
};
return this.i18n(stateStringIds[state], name);
}
/**
* @param {?string} topLeftGlyph
* @param {?string} topRightGlyph
* @param {?string} bottomLeftGlyph
* @param {?string} bottomRightGlyph
* @return {boolean}
* @private
*/
computeShowCornerGlyphs_(
topLeftGlyph, topRightGlyph, bottomLeftGlyph, bottomRightGlyph) {
return !!(
topLeftGlyph || topRightGlyph || bottomLeftGlyph || bottomRightGlyph);
}
/**
* @param {?string} topRightGlyph
* @param {?string} bottomRightGlyph
* @return {boolean}
* @private
*/
computeShowSecondColumn_(topRightGlyph, bottomRightGlyph) {
return !!(topRightGlyph || bottomRightGlyph);
}
/**
* Triggers 'announce-text' event to be used in pair with cr-a11y-announcer.
* Event provides A11Y "live" update to screen readers when a key is pressed.
* @protected
*/
keyboardKeyStateChanged() {
if (this.state === KeyboardKeyState.PRESSED) {
this.dispatchEvent(new CustomEvent('announce-text', {
bubbles: true,
composed: true,
detail: {text: this.ariaLabel_},
}));
}
}
}
customElements.define(KeyboardKeyElement.is, KeyboardKeyElement);