Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/ui/menuitemrenderer.js
4122 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Renderer for {@link goog.ui.MenuItem}s.
9
*/
10
11
goog.provide('goog.ui.MenuItemRenderer');
12
13
goog.require('goog.a11y.aria.Role');
14
goog.require('goog.asserts');
15
goog.require('goog.dom');
16
goog.require('goog.dom.TagName');
17
goog.require('goog.dom.classlist');
18
goog.require('goog.ui.Component');
19
goog.require('goog.ui.ControlRenderer');
20
goog.requireType('goog.ui.Control');
21
goog.requireType('goog.ui.ControlContent');
22
goog.requireType('goog.ui.MenuItem');
23
24
25
26
/**
27
* Default renderer for {@link goog.ui.MenuItem}s. Each item has the following
28
* structure:
29
*
30
* <div class="goog-menuitem">
31
* <div class="goog-menuitem-content">
32
* ...(menu item contents)...
33
* </div>
34
* </div>
35
*
36
* @constructor
37
* @extends {goog.ui.ControlRenderer}
38
*/
39
goog.ui.MenuItemRenderer = function() {
40
'use strict';
41
goog.ui.ControlRenderer.call(this);
42
43
/**
44
* Commonly used CSS class names, cached here for convenience (and to avoid
45
* unnecessary string concatenation).
46
* @type {!Array<string>}
47
* @private
48
*/
49
this.classNameCache_ = [];
50
};
51
goog.inherits(goog.ui.MenuItemRenderer, goog.ui.ControlRenderer);
52
goog.addSingletonGetter(goog.ui.MenuItemRenderer);
53
54
55
/**
56
* CSS class name the renderer applies to menu item elements.
57
* @type {string}
58
*/
59
goog.ui.MenuItemRenderer.CSS_CLASS = goog.getCssName('goog-menuitem');
60
61
62
/**
63
* Constants for referencing composite CSS classes.
64
* @enum {number}
65
* @private
66
*/
67
goog.ui.MenuItemRenderer.CompositeCssClassIndex_ = {
68
HOVER: 0,
69
CHECKBOX: 1,
70
CONTENT: 2
71
};
72
73
74
/**
75
* Returns the composite CSS class by using the cached value or by constructing
76
* the value from the base CSS class and the passed index.
77
* @param {goog.ui.MenuItemRenderer.CompositeCssClassIndex_} index Index for the
78
* CSS class - could be highlight, checkbox or content in usual cases.
79
* @return {string} The composite CSS class.
80
* @private
81
*/
82
goog.ui.MenuItemRenderer.prototype.getCompositeCssClass_ = function(index) {
83
'use strict';
84
var result = this.classNameCache_[index];
85
if (!result) {
86
switch (index) {
87
case goog.ui.MenuItemRenderer.CompositeCssClassIndex_.HOVER:
88
result = goog.getCssName(this.getStructuralCssClass(), 'highlight');
89
break;
90
case goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CHECKBOX:
91
result = goog.getCssName(this.getStructuralCssClass(), 'checkbox');
92
break;
93
case goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CONTENT:
94
result = goog.getCssName(this.getStructuralCssClass(), 'content');
95
break;
96
}
97
this.classNameCache_[index] = result;
98
}
99
100
return result;
101
};
102
103
104
/** @override */
105
goog.ui.MenuItemRenderer.prototype.getAriaRole = function() {
106
'use strict';
107
return goog.a11y.aria.Role.MENU_ITEM;
108
};
109
110
111
/**
112
* Overrides {@link goog.ui.ControlRenderer#createDom} by adding extra markup
113
* and stying to the menu item's element if it is selectable or checkable.
114
* @param {goog.ui.Control} item Menu item to render.
115
* @return {!Element} Root element for the item.
116
* @override
117
*/
118
goog.ui.MenuItemRenderer.prototype.createDom = function(item) {
119
'use strict';
120
var element = item.getDomHelper().createDom(
121
goog.dom.TagName.DIV, this.getClassNames(item).join(' '),
122
this.createContent(item.getContent(), item.getDomHelper()));
123
this.setEnableCheckBoxStructure(
124
item, element, item.isSupportedState(goog.ui.Component.State.SELECTED) ||
125
item.isSupportedState(goog.ui.Component.State.CHECKED));
126
return element;
127
};
128
129
130
/** @override */
131
goog.ui.MenuItemRenderer.prototype.getContentElement = function(element) {
132
'use strict';
133
return /** @type {Element} */ (element && element.firstChild);
134
};
135
136
137
/**
138
* Overrides {@link goog.ui.ControlRenderer#decorate} by initializing the
139
* menu item to checkable based on whether the element to be decorated has
140
* extra stying indicating that it should be.
141
* @param {goog.ui.Control} item Menu item instance to decorate the element.
142
* @param {Element} element Element to decorate.
143
* @return {Element} Decorated element.
144
* @override
145
*/
146
goog.ui.MenuItemRenderer.prototype.decorate = function(item, element) {
147
'use strict';
148
goog.asserts.assert(element);
149
if (!this.hasContentStructure(element)) {
150
element.appendChild(
151
/** @type {!Node} */ (
152
this.createContent(element.childNodes, item.getDomHelper())));
153
}
154
if (goog.dom.classlist.contains(element, goog.getCssName('goog-option'))) {
155
(/** @type {goog.ui.MenuItem} */ (item)).setCheckable(true);
156
this.setCheckable(item, element, true);
157
}
158
return goog.ui.MenuItemRenderer.superClass_.decorate.call(
159
this, item, element);
160
};
161
162
163
/**
164
* Takes a menu item's root element, and sets its content to the given text
165
* caption or DOM structure. Overrides the superclass immplementation by
166
* making sure that the checkbox structure (for selectable/checkable menu
167
* items) is preserved.
168
* @param {Element} element The item's root element.
169
* @param {goog.ui.ControlContent} content Text caption or DOM structure to be
170
* set as the item's content.
171
* @override
172
*/
173
goog.ui.MenuItemRenderer.prototype.setContent = function(element, content) {
174
'use strict';
175
// Save the checkbox element, if present.
176
var contentElement = this.getContentElement(element);
177
var checkBoxElement =
178
this.hasCheckBoxStructure(element) ? contentElement.firstChild : null;
179
goog.ui.MenuItemRenderer.superClass_.setContent.call(this, element, content);
180
if (checkBoxElement && !this.hasCheckBoxStructure(element)) {
181
// The call to setContent() blew away the checkbox element; reattach it.
182
contentElement.insertBefore(
183
checkBoxElement, contentElement.firstChild || null);
184
}
185
};
186
187
188
/**
189
* Returns true if the element appears to have a proper menu item structure by
190
* checking whether its first child has the appropriate structural class name.
191
* @param {Element} element Element to check.
192
* @return {boolean} Whether the element appears to have a proper menu item DOM.
193
* @protected
194
*/
195
goog.ui.MenuItemRenderer.prototype.hasContentStructure = function(element) {
196
'use strict';
197
var child = goog.dom.getFirstElementChild(element);
198
var contentClassName = this.getCompositeCssClass_(
199
goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CONTENT);
200
return !!child && goog.dom.classlist.contains(child, contentClassName);
201
};
202
203
204
/**
205
* Wraps the given text caption or existing DOM node(s) in a structural element
206
* containing the menu item's contents.
207
* @param {goog.ui.ControlContent} content Menu item contents.
208
* @param {goog.dom.DomHelper} dom DOM helper for document interaction.
209
* @return {!Element} Menu item content element.
210
* @protected
211
*/
212
goog.ui.MenuItemRenderer.prototype.createContent = function(content, dom) {
213
'use strict';
214
var contentClassName = this.getCompositeCssClass_(
215
goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CONTENT);
216
return dom.createDom(goog.dom.TagName.DIV, contentClassName, content);
217
};
218
219
220
/**
221
* Enables/disables radio button semantics on the menu item.
222
* @param {goog.ui.Control} item Menu item to update.
223
* @param {Element} element Menu item element to update (may be null if the
224
* item hasn't been rendered yet).
225
* @param {boolean} selectable Whether the item should be selectable.
226
*/
227
goog.ui.MenuItemRenderer.prototype.setSelectable = function(
228
item, element, selectable) {
229
'use strict';
230
if (item && element) {
231
this.setEnableCheckBoxStructure(item, element, selectable);
232
}
233
};
234
235
236
/**
237
* Enables/disables checkbox semantics on the menu item.
238
* @param {goog.ui.Control} item Menu item to update.
239
* @param {Element} element Menu item element to update (may be null if the
240
* item hasn't been rendered yet).
241
* @param {boolean} checkable Whether the item should be checkable.
242
*/
243
goog.ui.MenuItemRenderer.prototype.setCheckable = function(
244
item, element, checkable) {
245
'use strict';
246
if (item && element) {
247
this.setEnableCheckBoxStructure(item, element, checkable);
248
}
249
};
250
251
252
/**
253
* Determines whether the item contains a checkbox element.
254
* @param {Element} element Menu item root element.
255
* @return {boolean} Whether the element contains a checkbox element.
256
* @protected
257
*/
258
goog.ui.MenuItemRenderer.prototype.hasCheckBoxStructure = function(element) {
259
'use strict';
260
var contentElement = this.getContentElement(element);
261
if (contentElement) {
262
var child = contentElement.firstChild;
263
var checkboxClassName = this.getCompositeCssClass_(
264
goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CHECKBOX);
265
return !!child && goog.dom.isElement(child) &&
266
goog.dom.classlist.contains(
267
/** @type {!Element} */ (child), checkboxClassName);
268
}
269
return false;
270
};
271
272
273
/**
274
* Adds or removes extra markup and CSS styling to the menu item to make it
275
* selectable or non-selectable, depending on the value of the
276
* `selectable` argument.
277
* @param {!goog.ui.Control} item Menu item to update.
278
* @param {!Element} element Menu item element to update.
279
* @param {boolean} enable Whether to add or remove the checkbox structure.
280
* @protected
281
*/
282
goog.ui.MenuItemRenderer.prototype.setEnableCheckBoxStructure = function(
283
item, element, enable) {
284
'use strict';
285
this.setAriaRole(element, item.getPreferredAriaRole());
286
this.setAriaStates(item, element);
287
if (enable != this.hasCheckBoxStructure(element)) {
288
goog.dom.classlist.enable(element, goog.getCssName('goog-option'), enable);
289
var contentElement = this.getContentElement(element);
290
if (enable) {
291
// Insert checkbox structure.
292
var checkboxClassName = this.getCompositeCssClass_(
293
goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CHECKBOX);
294
contentElement.insertBefore(
295
item.getDomHelper().createDom(
296
goog.dom.TagName.DIV, checkboxClassName),
297
contentElement.firstChild || null);
298
} else {
299
// Remove checkbox structure.
300
contentElement.removeChild(
301
/** @type {!Node} */ (contentElement.firstChild));
302
}
303
}
304
};
305
306
307
/**
308
* Takes a single {@link goog.ui.Component.State}, and returns the
309
* corresponding CSS class name (null if none). Overrides the superclass
310
* implementation by using 'highlight' as opposed to 'hover' as the CSS
311
* class name suffix for the HOVER state, for backwards compatibility.
312
* @param {goog.ui.Component.State} state Component state.
313
* @return {string|undefined} CSS class representing the given state
314
* (undefined if none).
315
* @override
316
*/
317
goog.ui.MenuItemRenderer.prototype.getClassForState = function(state) {
318
'use strict';
319
switch (state) {
320
case goog.ui.Component.State.HOVER:
321
// We use 'highlight' as the suffix, for backwards compatibility.
322
return this.getCompositeCssClass_(
323
goog.ui.MenuItemRenderer.CompositeCssClassIndex_.HOVER);
324
case goog.ui.Component.State.CHECKED:
325
case goog.ui.Component.State.SELECTED:
326
// We use 'goog-option-selected' as the class, for backwards
327
// compatibility.
328
return goog.getCssName('goog-option-selected');
329
default:
330
return goog.ui.MenuItemRenderer.superClass_.getClassForState.call(
331
this, state);
332
}
333
};
334
335
336
/**
337
* Takes a single CSS class name which may represent a component state, and
338
* returns the corresponding component state (0x00 if none). Overrides the
339
* superclass implementation by treating 'goog-option-selected' as special,
340
* for backwards compatibility.
341
* @param {string} className CSS class name, possibly representing a component
342
* state.
343
* @return {goog.ui.Component.State} state Component state corresponding
344
* to the given CSS class (0x00 if none).
345
* @override
346
*/
347
goog.ui.MenuItemRenderer.prototype.getStateFromClass = function(className) {
348
'use strict';
349
var hoverClassName = this.getCompositeCssClass_(
350
goog.ui.MenuItemRenderer.CompositeCssClassIndex_.HOVER);
351
switch (className) {
352
case goog.getCssName('goog-option-selected'):
353
return goog.ui.Component.State.CHECKED;
354
case hoverClassName:
355
return goog.ui.Component.State.HOVER;
356
default:
357
return goog.ui.MenuItemRenderer.superClass_.getStateFromClass.call(
358
this, className);
359
}
360
};
361
362
363
/** @override */
364
goog.ui.MenuItemRenderer.prototype.getCssClass = function() {
365
'use strict';
366
return goog.ui.MenuItemRenderer.CSS_CLASS;
367
};
368
369