Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/ui/paletterenderer.js
4576 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.Palette}s.
9
*/
10
11
goog.provide('goog.ui.PaletteRenderer');
12
13
goog.require('goog.a11y.aria');
14
goog.require('goog.a11y.aria.Role');
15
goog.require('goog.a11y.aria.State');
16
goog.require('goog.array');
17
goog.require('goog.asserts');
18
goog.require('goog.dom');
19
goog.require('goog.dom.NodeIterator');
20
goog.require('goog.dom.NodeType');
21
goog.require('goog.dom.TagName');
22
goog.require('goog.dom.classlist');
23
goog.require('goog.dom.dataset');
24
goog.require('goog.iter');
25
goog.require('goog.style');
26
goog.require('goog.ui.ControlRenderer');
27
goog.require('goog.userAgent');
28
goog.requireType('goog.math.Size');
29
goog.requireType('goog.ui.Control');
30
goog.requireType('goog.ui.ControlContent');
31
goog.requireType('goog.ui.Palette');
32
33
34
35
/**
36
* Default renderer for {@link goog.ui.Palette}s. Renders the palette as an
37
* HTML table wrapped in a DIV, with one palette item per cell:
38
*
39
* <div class="goog-palette">
40
* <table class="goog-palette-table">
41
* <tbody class="goog-palette-body">
42
* <tr class="goog-palette-row">
43
* <td class="goog-palette-cell">...Item 0...</td>
44
* <td class="goog-palette-cell">...Item 1...</td>
45
* ...
46
* </tr>
47
* <tr class="goog-palette-row">
48
* ...
49
* </tr>
50
* </tbody>
51
* </table>
52
* </div>
53
*
54
* @constructor
55
* @extends {goog.ui.ControlRenderer}
56
*/
57
goog.ui.PaletteRenderer = function() {
58
'use strict';
59
goog.ui.ControlRenderer.call(this);
60
};
61
goog.inherits(goog.ui.PaletteRenderer, goog.ui.ControlRenderer);
62
goog.addSingletonGetter(goog.ui.PaletteRenderer);
63
64
65
/**
66
* Globally unique ID sequence for cells rendered by this renderer class.
67
* @type {number}
68
* @private
69
*/
70
goog.ui.PaletteRenderer.cellId_ = 0;
71
72
73
/**
74
* Default CSS class to be applied to the root element of components rendered
75
* by this renderer.
76
* @type {string}
77
*/
78
goog.ui.PaletteRenderer.CSS_CLASS = goog.getCssName('goog-palette');
79
80
81
/**
82
* Data attribute to store grid width from palette control.
83
* @const {string}
84
*/
85
goog.ui.PaletteRenderer.GRID_WIDTH_ATTRIBUTE = 'gridWidth';
86
87
88
/**
89
* Returns the palette items arranged in a table wrapped in a DIV, with the
90
* renderer's own CSS class and additional state-specific classes applied to
91
* it.
92
* @param {goog.ui.Control} palette goog.ui.Palette to render.
93
* @return {!Element} Root element for the palette.
94
* @override
95
* @suppress {strictMissingProperties} Added to tighten compiler checks
96
*/
97
goog.ui.PaletteRenderer.prototype.createDom = function(palette) {
98
'use strict';
99
var classNames = this.getClassNames(palette);
100
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
101
var element = palette.getDomHelper().createDom(
102
goog.dom.TagName.DIV, classNames,
103
this.createGrid(
104
/** @type {Array<Node>} */ (palette.getContent()), palette.getSize(),
105
palette.getDomHelper()));
106
// It's safe to store grid width here since `goog.ui.Palette#setSize` cannot
107
// be called after createDom.
108
goog.dom.dataset.set(
109
element, goog.ui.PaletteRenderer.GRID_WIDTH_ATTRIBUTE,
110
palette.getSize().width);
111
return element;
112
};
113
114
115
/**
116
* Returns the given items in a table with `size.width` columns and
117
* `size.height` rows. If the table is too big, empty cells will be
118
* created as needed. If the table is too small, the items that don't fit
119
* will not be rendered.
120
* @param {Array<Node>} items Palette items.
121
* @param {goog.math.Size} size Palette size (columns x rows); both dimensions
122
* must be specified as numbers.
123
* @param {goog.dom.DomHelper} dom DOM helper for document interaction.
124
* @return {!Element} Palette table element.
125
*/
126
goog.ui.PaletteRenderer.prototype.createGrid = function(items, size, dom) {
127
'use strict';
128
var rows = [];
129
for (var row = 0, index = 0; row < size.height; row++) {
130
var cells = [];
131
for (var column = 0; column < size.width; column++) {
132
var item = items && items[index++];
133
cells.push(this.createCell(item, dom));
134
}
135
rows.push(this.createRow(cells, dom));
136
}
137
138
return this.createTable(rows, dom);
139
};
140
141
142
/**
143
* Returns a table element (or equivalent) that wraps the given rows.
144
* @param {Array<Element>} rows Array of row elements.
145
* @param {goog.dom.DomHelper} dom DOM helper for document interaction.
146
* @return {!Element} Palette table element.
147
*/
148
goog.ui.PaletteRenderer.prototype.createTable = function(rows, dom) {
149
'use strict';
150
var table = dom.createDom(
151
goog.dom.TagName.TABLE, goog.getCssName(this.getCssClass(), 'table'),
152
dom.createDom(
153
goog.dom.TagName.TBODY, goog.getCssName(this.getCssClass(), 'body'),
154
rows));
155
goog.a11y.aria.setRole(table, goog.a11y.aria.Role.GRID);
156
table.cellSpacing = '0';
157
table.cellPadding = '0';
158
return table;
159
};
160
161
162
/**
163
* Returns a table row element (or equivalent) that wraps the given cells.
164
* @param {Array<Element>} cells Array of cell elements.
165
* @param {goog.dom.DomHelper} dom DOM helper for document interaction.
166
* @return {!Element} Row element.
167
*/
168
goog.ui.PaletteRenderer.prototype.createRow = function(cells, dom) {
169
'use strict';
170
var row = dom.createDom(
171
goog.dom.TagName.TR, goog.getCssName(this.getCssClass(), 'row'), cells);
172
goog.a11y.aria.setRole(row, goog.a11y.aria.Role.ROW);
173
return row;
174
};
175
176
177
/**
178
* Returns a table cell element (or equivalent) that wraps the given palette
179
* item (which must be a DOM node).
180
* @param {Node|string} node Palette item.
181
* @param {goog.dom.DomHelper} dom DOM helper for document interaction.
182
* @return {!Element} Cell element.
183
*/
184
goog.ui.PaletteRenderer.prototype.createCell = function(node, dom) {
185
'use strict';
186
var cell = dom.createDom(
187
goog.dom.TagName.TD, {
188
'class': goog.getCssName(this.getCssClass(), 'cell'),
189
// Cells must have an ID, for accessibility, so we generate one here.
190
'id': goog.getCssName(this.getCssClass(), 'cell-') +
191
goog.ui.PaletteRenderer.cellId_++
192
},
193
node);
194
goog.a11y.aria.setRole(cell, goog.a11y.aria.Role.GRIDCELL);
195
// Initialize to an unselected state.
196
goog.a11y.aria.setState(cell, goog.a11y.aria.State.SELECTED, false);
197
this.maybeUpdateAriaLabel_(cell);
198
199
return cell;
200
};
201
202
203
/**
204
* Updates the aria label of the cell if it doesn't have one. Descends the DOM
205
* and tries to find an aria label for a grid cell from the first child with a
206
* label or title.
207
* @param {!Element} cell The cell.
208
* @private
209
*/
210
goog.ui.PaletteRenderer.prototype.maybeUpdateAriaLabel_ = function(cell) {
211
'use strict';
212
if (goog.dom.getTextContent(cell) || goog.a11y.aria.getLabel(cell)) {
213
return;
214
}
215
var iter = new goog.dom.NodeIterator(cell);
216
var label = '';
217
var node;
218
while (!label && (node = goog.iter.nextOrValue(iter, null))) {
219
if (node.nodeType == goog.dom.NodeType.ELEMENT) {
220
/**
221
* @suppress {strictMissingProperties} Added to tighten compiler checks
222
*/
223
label =
224
goog.a11y.aria.getLabel(/** @type {!Element} */ (node)) || node.title;
225
}
226
}
227
if (label) {
228
goog.a11y.aria.setLabel(cell, label);
229
}
230
231
return;
232
};
233
234
235
/**
236
* Overrides {@link goog.ui.ControlRenderer#canDecorate} to always return false.
237
* @param {Element} element Ignored.
238
* @return {boolean} False, since palettes don't support the decorate flow (for
239
* now).
240
* @override
241
*/
242
goog.ui.PaletteRenderer.prototype.canDecorate = function(element) {
243
'use strict';
244
return false;
245
};
246
247
248
/**
249
* Overrides {@link goog.ui.ControlRenderer#decorate} to be a no-op, since
250
* palettes don't support the decorate flow (for now).
251
* @param {goog.ui.Control} palette Ignored.
252
* @param {Element} element Ignored.
253
* @return {null} Always null.
254
* @override
255
*/
256
goog.ui.PaletteRenderer.prototype.decorate = function(palette, element) {
257
'use strict';
258
return null;
259
};
260
261
262
/**
263
* Overrides {@link goog.ui.ControlRenderer#setContent} for palettes. Locates
264
* the HTML table representing the palette grid, and replaces the contents of
265
* each cell with a new element from the array of nodes passed as the second
266
* argument. If the new content has too many items the table will have more
267
* rows added to fit, if there are less items than the table has cells, then the
268
* left over cells will be empty.
269
* @param {Element} element Root element of the palette control.
270
* @param {goog.ui.ControlContent} content Array of items to replace existing
271
* palette items.
272
* @override
273
* @suppress {strictPrimitiveOperators}
274
*/
275
goog.ui.PaletteRenderer.prototype.setContent = function(element, content) {
276
'use strict';
277
var items = /** @type {Array<Node>} */ (content);
278
if (element) {
279
var tbody = goog.dom.getElementsByTagNameAndClass(
280
goog.dom.TagName.TBODY, goog.getCssName(this.getCssClass(), 'body'),
281
element)[0];
282
if (tbody) {
283
var index = 0;
284
Array.prototype.forEach.call(tbody.rows, function(row) {
285
'use strict';
286
goog.array.forEach(row.cells, function(cell) {
287
'use strict';
288
goog.dom.removeChildren(cell);
289
goog.a11y.aria.removeState(cell, goog.a11y.aria.State.LABEL);
290
if (items) {
291
var item = items[index++];
292
if (item) {
293
goog.dom.appendChild(cell, item);
294
this.maybeUpdateAriaLabel_(cell);
295
}
296
}
297
}, this);
298
}, this);
299
300
// Make space for any additional items.
301
if (index < items.length) {
302
var cells = [];
303
var dom = goog.dom.getDomHelper(element);
304
var width = goog.dom.dataset.get(
305
element, goog.ui.PaletteRenderer.GRID_WIDTH_ATTRIBUTE);
306
while (index < items.length) {
307
var item = items[index++];
308
cells.push(this.createCell(item, dom));
309
if (cells.length == width) {
310
var row = this.createRow(cells, dom);
311
goog.dom.appendChild(tbody, row);
312
cells.length = 0;
313
}
314
}
315
if (cells.length > 0) {
316
while (cells.length < width) {
317
cells.push(this.createCell('', dom));
318
}
319
var row = this.createRow(cells, dom);
320
goog.dom.appendChild(tbody, row);
321
}
322
}
323
}
324
// Make sure the new contents are still unselectable.
325
goog.style.setUnselectable(element, true, goog.userAgent.GECKO);
326
}
327
};
328
329
330
/**
331
* Returns the item corresponding to the given node, or null if the node is
332
* neither a palette cell nor part of a palette item.
333
* @param {goog.ui.Palette} palette Palette in which to look for the item.
334
* @param {Node} node Node to look for.
335
* @return {Node} The corresponding palette item (null if not found).
336
* @suppress {strictMissingProperties} Added to tighten compiler checks
337
*/
338
goog.ui.PaletteRenderer.prototype.getContainingItem = function(palette, node) {
339
'use strict';
340
var root = palette.getElement();
341
while (node && node.nodeType == goog.dom.NodeType.ELEMENT && node != root) {
342
if (node.tagName == goog.dom.TagName.TD &&
343
goog.dom.classlist.contains(
344
/** @type {!Element} */ (node),
345
goog.getCssName(this.getCssClass(), 'cell'))) {
346
return node.firstChild;
347
}
348
node = node.parentNode;
349
}
350
351
return null;
352
};
353
354
355
/**
356
* Updates the highlight styling of the palette cell containing the given node
357
* based on the value of the Boolean argument.
358
* @param {goog.ui.Palette} palette Palette containing the item.
359
* @param {Node} node Item whose cell is to be highlighted or un-highlighted.
360
* @param {boolean} highlight If true, the cell is highlighted; otherwise it is
361
* un-highlighted.
362
*/
363
goog.ui.PaletteRenderer.prototype.highlightCell = function(
364
palette, node, highlight) {
365
'use strict';
366
if (node) {
367
var cell = this.getCellForItem(node);
368
goog.asserts.assert(cell);
369
goog.dom.classlist.enable(
370
cell, goog.getCssName(this.getCssClass(), 'cell-hover'), highlight);
371
// See https://www.w3.org/TR/wai-aria/#aria-activedescendant
372
// for an explanation of the activedescendant.
373
if (highlight) {
374
goog.a11y.aria.setState(
375
palette.getElementStrict(), goog.a11y.aria.State.ACTIVEDESCENDANT,
376
cell.id);
377
} else if (
378
cell.id ==
379
goog.a11y.aria.getState(
380
palette.getElementStrict(),
381
goog.a11y.aria.State.ACTIVEDESCENDANT)) {
382
goog.a11y.aria.removeState(
383
palette.getElementStrict(), goog.a11y.aria.State.ACTIVEDESCENDANT);
384
}
385
}
386
};
387
388
389
/**
390
* @param {Node} node Item whose cell is to be returned.
391
* @return {Element} The grid cell for the palette item.
392
*/
393
goog.ui.PaletteRenderer.prototype.getCellForItem = function(node) {
394
'use strict';
395
return /** @type {Element} */ (node ? node.parentNode : null);
396
};
397
398
399
/**
400
* Updates the selection styling of the palette cell containing the given node
401
* based on the value of the Boolean argument.
402
* @param {goog.ui.Palette} palette Palette containing the item.
403
* @param {Node} node Item whose cell is to be selected or deselected.
404
* @param {boolean} select If true, the cell is selected; otherwise it is
405
* deselected.
406
*/
407
goog.ui.PaletteRenderer.prototype.selectCell = function(palette, node, select) {
408
'use strict';
409
if (node) {
410
var cell = /** @type {!Element} */ (node.parentNode);
411
goog.dom.classlist.enable(
412
cell, goog.getCssName(this.getCssClass(), 'cell-selected'), select);
413
goog.a11y.aria.setState(cell, goog.a11y.aria.State.SELECTED, select);
414
}
415
};
416
417
418
/**
419
* Returns the CSS class to be applied to the root element of components
420
* rendered using this renderer.
421
* @return {string} Renderer-specific CSS class.
422
* @override
423
*/
424
goog.ui.PaletteRenderer.prototype.getCssClass = function() {
425
'use strict';
426
return goog.ui.PaletteRenderer.CSS_CLASS;
427
};
428
429