Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(68)

Side by Side Diff: tracing/tracing/ui/base/table.html

Issue 2162963002: [polymer] Merge of master into polymer10-migration (Closed) Base URL: git@github.com:catapult-project/catapult.git@polymer10-migration
Patch Set: Merge polymer10-migration int polymer10-merge Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tracing/tracing/ui/base/tab_view.html ('k') | tracing/tracing/ui/base/table_test.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 <!DOCTYPE html> 1 <!DOCTYPE html>
2 <!-- 2 <!--
3 Copyright (c) 2014 The Chromium Authors. All rights reserved. 3 Copyright (c) 2014 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be 4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file. 5 found in the LICENSE file.
6 --> 6 -->
7 7
8 <link rel="import" href="/tracing/ui/base/dom_helpers.html"> 8 <link rel="import" href="/tracing/ui/base/dom_helpers.html">
9 <link rel="import" href="/tracing/ui/base/utils.html"> 9 <link rel="import" href="/tracing/ui/base/utils.html">
10 10
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
50 LEFT: 0 /* default */, 50 LEFT: 0 /* default */,
51 RIGHT: 1 51 RIGHT: 1
52 }; 52 };
53 53
54 return { 54 return {
55 TableFormat: TableFormat 55 TableFormat: TableFormat
56 }; 56 };
57 }); 57 });
58 </script> 58 </script>
59 59
60 <dom-module id='tr-ui-b-table'> 60 <dom-module id="tr-ui-b-table">
61 <template> 61 <template>
62 <style> 62 <style>
63 :host { 63 :host {
64 display: flex; 64 display: flex;
65 flex-direction: column; 65 flex-direction: column;
66 } 66 }
67 67
68 table { 68 table {
69 font-size: 12px; 69 font-size: 12px;
70 70
71 flex: 1 1 auto; 71 flex: 1 1 auto;
72 align-self: stretch; 72 align-self: stretch;
73 border-collapse: separate; 73 border-collapse: separate;
74 border-spacing: 0; 74 border-spacing: 0;
75 border-width: 0; 75 border-width: 0;
76 -webkit-user-select: initial; 76 -webkit-user-select: initial;
77 } 77 }
78 78
79 tr > td { 79 tr > td {
80 padding: 2px 4px 2px 4px; 80 padding: 2px 4px 2px 4px;
81 vertical-align: text-top; 81 vertical-align: top;
82 } 82 }
83 83
84 tr:focus, 84 tr:focus,
85 td:focus { 85 td:focus {
86 outline: 1px dotted rgba(0,0,0,0.1); 86 outline: 1px dotted rgba(0,0,0,0.1);
87 outline-offset: 0; 87 outline-offset: 0;
88 } 88 }
89 89
90 button.toggle-button { 90 button.toggle-button {
91 height: 15px; 91 height: 15px;
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
146 tr:not(.empty-row):not([selected]):hover, 146 tr:not(.empty-row):not([selected]):hover,
147 table > tbody[cell-highlight-style="dark"] > 147 table > tbody[cell-highlight-style="dark"] >
148 tr:not(.empty-row):not([selected]) > td:hover { 148 tr:not(.empty-row):not([selected]) > td:hover {
149 background-color: #e6e6e6; /* grey */ 149 background-color: #e6e6e6; /* grey */
150 } 150 }
151 table > tbody[row-highlight-style="dark"] > tr:hover[selected], 151 table > tbody[row-highlight-style="dark"] > tr:hover[selected],
152 table > tbody[cell-highlight-style="dark"] > tr[selected] > td:hover { 152 table > tbody[cell-highlight-style="dark"] > tr[selected] > td:hover {
153 background-color: rgb(171, 217, 202); /* semi-light turquoise */ 153 background-color: rgb(171, 217, 202); /* semi-light turquoise */
154 } 154 }
155 155
156 table > colgroup > col[selected] {
157 background-color: #e6e6e6; /* grey */
158 }
159
156 table > tbody > tr.empty-row > td { 160 table > tbody > tr.empty-row > td {
157 color: #666; 161 color: #666;
158 font-style: italic; 162 font-style: italic;
159 text-align: center; 163 text-align: center;
160 } 164 }
161 165
162 table > tbody.has-footer > tr:last-child > td { 166 table > tbody.has-footer > tr:last-child > td {
163 border-bottom: 1px solid #aaa; 167 border-bottom: 1px solid #aaa;
164 } 168 }
165 169
166 table > tfoot > tr:first-child > td { 170 table > tfoot > tr:first-child > td {
167 border-top: 1px solid #ffffff; 171 border-top: 1px solid #ffffff;
168 } 172 }
169 173
170 expand-button { 174 expand-button {
171 -webkit-user-select: none; 175 -webkit-user-select: none;
172 display: inline-block; 176 display: inline-block;
173 cursor: pointer; 177 cursor: pointer;
174 font-size: 9px; 178 font-size: 9px;
175 min-width: 8px; 179 min-width: 8px;
176 max-width: 8px; 180 max-width: 8px;
177 } 181 }
178 182
179 .button-expanded { 183 .button-expanded {
180 transform: rotate(90deg); 184 transform: rotate(90deg);
181 } 185 }
182 </style> 186 </style>
183 <table> 187 <table>
188 <colgroup id="cols">
189 </colgroup>
184 <thead id="head"> 190 <thead id="head">
185 </thead> 191 </thead>
186 <tbody id="body"> 192 <tbody id="body">
187 </tbody> 193 </tbody>
188 <tfoot id="foot"> 194 <tfoot id="foot">
189 </tfoot> 195 </tfoot>
190 </table> 196 </table>
191 </template> 197 </template>
192 </dom-module> 198 </dom-module>
193 <script> 199 <script>
194 'use strict'; 200 'use strict';
195 (function() { 201 (function() {
196 var RIGHT_ARROW = String.fromCharCode(0x25b6); 202 var RIGHT_ARROW = String.fromCharCode(0x25b6);
197 var UNSORTED_ARROW = String.fromCharCode(0x25BF); 203 var UNSORTED_ARROW = String.fromCharCode(0x25BF);
198 var ASCENDING_ARROW = String.fromCharCode(0x25B4); 204 var ASCENDING_ARROW = String.fromCharCode(0x25B4);
199 var DESCENDING_ARROW = String.fromCharCode(0x25BE); 205 var DESCENDING_ARROW = String.fromCharCode(0x25BE);
200 var BASIC_INDENTATION = 8; 206 var BASIC_INDENTATION = 8;
201 207
202 var SelectionMode = tr.ui.b.TableFormat.SelectionMode; 208 var SelectionMode = tr.ui.b.TableFormat.SelectionMode;
203 var HighlightStyle = tr.ui.b.TableFormat.HighlightStyle; 209 var HighlightStyle = tr.ui.b.TableFormat.HighlightStyle;
204 var ColumnAlignment = tr.ui.b.TableFormat.ColumnAlignment; 210 var ColumnAlignment = tr.ui.b.TableFormat.ColumnAlignment;
205 211
206 Polymer({ 212 Polymer({
207 is: 'tr-ui-b-table', 213 is: 'tr-ui-b-table',
208 214
209 created: function() { 215 created: function() {
210 this.selectionMode_ = SelectionMode.NONE; 216 this.selectionMode_ = SelectionMode.NONE;
211 this.rowHighlightStyle_ = HighlightStyle.DEFAULT; 217 this.rowHighlightStyle_ = HighlightStyle.DEFAULT;
212 this.cellHighlightStyle_ = HighlightStyle.DEFAULT; 218 this.cellHighlightStyle_ = HighlightStyle.DEFAULT;
213 this.selectedTableRowInfo_ = undefined; 219 this.selectedTableRowInfo_ = undefined;
214 this.selectedColumnIndex_ = undefined; 220 this.selectedColumnIndex_ = undefined;
215 221
216 this.tableColumns_ = []; 222 this.tableColumns_ = [];
217 this.tableRows_ = []; 223 this.tableRows_ = [];
218 this.tableRowsInfo_ = new WeakMap(); 224 this.tableRowsInfo_ = new WeakMap();
219 this.tableFooterRows_ = []; 225 this.tableFooterRows_ = [];
220 this.tableFooterRowsInfo_ = new WeakMap(); 226 this.tableFooterRowsInfo_ = new WeakMap();
227 this.sortColumnIndex_ = undefined;
228 this.sortDescending_ = false;
229 this.columnsWithExpandButtons_ = [];
230 this.headerCells_ = [];
231 this.showHeader_ = true;
232 this.emptyValue_ = undefined;
233 this.subRowsPropertyName_ = 'subRows';
234 this.customizeTableRowCallback_ = undefined;
235 },
236
237 ready: function() {
238 this.$.body.addEventListener(
239 'keydown', this.onKeyDown_.bind(this), true);
240 },
241
242 clear: function() {
243 this.selectionMode_ = SelectionMode.NONE;
244 this.rowHighlightStyle_ = HighlightStyle.DEFAULT;
245 this.cellHighlightStyle_ = HighlightStyle.DEFAULT;
246 this.selectedTableRowInfo_ = undefined;
247 this.selectedColumnIndex_ = undefined;
248
249 Polymer.dom(this).textContent = '';
250 this.tableColumns_ = [];
251 this.tableRows_ = [];
252 this.tableRowsInfo_ = new WeakMap();
253 this.tableFooterRows_ = [];
254 this.tableFooterRowsInfo_ = new WeakMap();
255 this.sortColumnIndex_ = undefined;
256 this.sortDescending_ = false;
257 this.columnsWithExpandButtons_ = [];
258 this.headerCells_ = [];
259 this.subRowsPropertyName_ = 'subRows';
260 this.defaultExpansionStateCallback_ = undefined;
261 },
262
263 get showHeader() {
264 return this.showHeader_;
265 },
266
267 set showHeader(showHeader) {
268 this.showHeader_ = showHeader;
269 this.scheduleRebuildHeaders_();
270 },
271
272 set subRowsPropertyName(name) {
273 this.subRowsPropertyName_ = name;
274 },
275
276 /**
277 * This callback will be called whenever a body row is built
278 * for a userRow that has subRows and does not have an explicit
279 * isExpanded field.
280 * The callback should return true if the row should be expanded,
281 * or false if the row should be collapsed.
282 * @param {function(userRow, parentUserRow): boolean} cb The callback.
283 */
284 set defaultExpansionStateCallback(cb) {
285 this.defaultExpansionStateCallback_ = cb;
286 this.scheduleRebuildBody_();
287 },
288
289 /**
290 * This callback will be called whenever a body row is built.
291 * The callback's return value is ignored.
292 * @param {function(userRow, trElement)} cb The callback.
293 */
294 set customizeTableRowCallback(cb) {
295 this.customizeTableRowCallback_ = cb;
296 this.scheduleRebuildBody_();
297 },
298
299 get emptyValue() {
300 return this.emptyValue_;
301 },
302
303 set emptyValue(emptyValue) {
304 var previousEmptyValue = this.emptyValue_;
305 this.emptyValue_ = emptyValue;
306 if (this.tableRows_.length === 0 && emptyValue !== previousEmptyValue)
307 this.scheduleRebuildBody_();
308 },
309
310 /**
311 * Data objects should have the following fields:
312 * mandatory: title, value
313 * optional: width {string}, cmp {function}, colSpan {number},
314 * showExpandButtons {boolean},
315 * align {tr.ui.b.TableFormat.ColumnAlignment}
316 *
317 * @param {Array} columns An array of data objects.
318 */
319 set tableColumns(columns) {
320 // Figure out the columns with expand buttons...
321 var columnsWithExpandButtons = [];
322 for (var i = 0; i < columns.length; i++) {
323 if (columns[i].showExpandButtons)
324 columnsWithExpandButtons.push(i);
325 }
326 if (columnsWithExpandButtons.length === 0) {
327 // First column if none have specified.
328 columnsWithExpandButtons = [0];
329 }
330
331 // Sanity check columns.
332 for (var i = 0; i < columns.length; i++) {
333 var colInfo = columns[i];
334 if (colInfo.width === undefined)
335 continue;
336
337 var hasExpandButton = columnsWithExpandButtons.indexOf(i) !== -1;
338
339 var w = colInfo.width;
340 if (w) {
341 if (/\d+px/.test(w)) {
342 continue;
343 } else if (/\d+%/.test(w)) {
344 if (hasExpandButton) {
345 throw new Error('Columns cannot be %-sized and host ' +
346 ' an expand button');
347 }
348 } else {
349 throw new Error('Unrecognized width string');
350 }
351 }
352 }
353
354 // Commit the change.
355 this.tableColumns_ = columns;
356 this.headerCells_ = [];
357 this.columnsWithExpandButtons_ = columnsWithExpandButtons;
358 this.sortColumnIndex = undefined;
359 this.scheduleRebuildHeaders_();
360
361 // Blow away the table rows, too.
362 this.tableRows = this.tableRows_;
363 },
364
365 get tableColumns() {
366 return this.tableColumns_;
367 },
368
369 /**
370 * @param {Array} rows An array of 'row' objects with the following
371 * fields:
372 * optional: subRows An array of objects that have the same 'row'
373 * structure. Set subRowsPropertyName to use an
374 * alternative field name.
375 */
376 set tableRows(rows) {
377 this.selectedTableRowInfo_ = undefined;
378 this.selectedColumnIndex_ = undefined;
379 this.maybeUpdateSelectedRow_();
380 this.tableRows_ = rows;
381 this.tableRowsInfo_ = new WeakMap();
382 this.scheduleRebuildBody_();
383 },
384
385 get tableRows() {
386 return this.tableRows_;
387 },
388
389 set footerRows(rows) {
390 this.tableFooterRows_ = rows;
391 this.tableFooterRowsInfo_ = new WeakMap();
392 this.scheduleRebuildFooter_();
393 },
394
395 get footerRows() {
396 return this.tableFooterRows_;
397 },
398
399 set sortColumnIndex(number) {
400 if (number === this.sortColumnIndex_)
401 return;
402
403 if (number === undefined) {
221 this.sortColumnIndex_ = undefined; 404 this.sortColumnIndex_ = undefined;
222 this.sortDescending_ = false; 405 this.updateHeaderArrows_();
223 this.columnsWithExpandButtons_ = []; 406 this.dispatchSortingChangedEvent_();
224 this.headerCells_ = []; 407 return;
225 this.showHeader_ = true; 408 }
226 this.emptyValue_ = undefined; 409
227 this.subRowsPropertyName_ = 'subRows'; 410 if (this.tableColumns_.length <= number)
228 this.customizeTableRowCallback_ = undefined; 411 throw new Error('Column number ' + number + ' is out of bounds.');
229 }, 412 if (!this.tableColumns_[number].cmp)
230 413 throw new Error('Column ' + number + ' does not have a comparator.');
231 ready: function() { 414
232 this.$.body.addEventListener( 415 this.sortColumnIndex_ = number;
233 'keydown', this.onKeyDown_.bind(this), true); 416 this.updateHeaderArrows_();
234 }, 417 this.scheduleRebuildBody_();
235 418 this.dispatchSortingChangedEvent_();
236 clear: function() { 419 },
237 this.selectionMode_ = SelectionMode.NONE; 420
238 this.rowHighlightStyle_ = HighlightStyle.DEFAULT; 421 get sortColumnIndex() {
239 this.cellHighlightStyle_ = HighlightStyle.DEFAULT; 422 return this.sortColumnIndex_;
240 this.selectedTableRowInfo_ = undefined; 423 },
241 this.selectedColumnIndex_ = undefined; 424
242 425 set sortDescending(value) {
243 Polymer.dom(this).textContent = ''; 426 var newValue = !!value;
244 this.tableColumns_ = []; 427
245 this.tableRows_ = []; 428 if (newValue !== this.sortDescending_) {
246 this.tableRowsInfo_ = new WeakMap(); 429 this.sortDescending_ = newValue;
247 this.tableFooterRows_ = [];
248 this.tableFooterRowsInfo_ = new WeakMap();
249 this.sortColumnIndex_ = undefined;
250 this.sortDescending_ = false;
251 this.columnsWithExpandButtons_ = [];
252 this.headerCells_ = [];
253 this.subRowsPropertyName_ = 'subRows';
254 this.defaultExpansionStateCallback_ = undefined;
255 },
256
257 get showHeader() {
258 return this.showHeader_;
259 },
260
261 set showHeader(showHeader) {
262 this.showHeader_ = showHeader;
263 this.scheduleRebuildHeaders_();
264 },
265
266 set subRowsPropertyName(name) {
267 this.subRowsPropertyName_ = name;
268 },
269
270 /**
271 * This callback will be called whenever a body row is built
272 * for a userRow that has subRows and does not have an explicit
273 * isExpanded field.
274 * The callback should return true if the row should be expanded,
275 * or false if the row should be collapsed.
276 * @param {function(userRow, parentUserRow): boolean} cb The callback.
277 */
278 set defaultExpansionStateCallback(cb) {
279 this.defaultExpansionStateCallback_ = cb;
280 this.scheduleRebuildBody_();
281 },
282
283 /**
284 * This callback will be called whenever a body row is built.
285 * The callback's return value is ignored.
286 * @param {function(userRow, trElement)} cb The callback.
287 */
288 set customizeTableRowCallback(cb) {
289 this.customizeTableRowCallback_ = cb;
290 this.scheduleRebuildBody_();
291 },
292
293 get emptyValue() {
294 return this.emptyValue_;
295 },
296
297 set emptyValue(emptyValue) {
298 var previousEmptyValue = this.emptyValue_;
299 this.emptyValue_ = emptyValue;
300 if (this.tableRows_.length === 0 && emptyValue !== previousEmptyValue)
301 this.scheduleRebuildBody_();
302 },
303
304 /**
305 * Data objects should have the following fields:
306 * mandatory: title, value
307 * optional: width {string}, cmp {function}, colSpan {number},
308 * showExpandButtons {boolean},
309 * align {tr.ui.b.TableFormat.ColumnAlignment}
310 *
311 * @param {Array} columns An array of data objects.
312 */
313 set tableColumns(columns) {
314 // Figure out the columns with expand buttons...
315 var columnsWithExpandButtons = [];
316 for (var i = 0; i < columns.length; i++) {
317 if (columns[i].showExpandButtons)
318 columnsWithExpandButtons.push(i);
319 }
320 if (columnsWithExpandButtons.length === 0) {
321 // First column if none have specified.
322 columnsWithExpandButtons = [0];
323 }
324
325 // Sanity check columns.
326 for (var i = 0; i < columns.length; i++) {
327 var colInfo = columns[i];
328 if (colInfo.width === undefined)
329 continue;
330
331 var hasExpandButton = columnsWithExpandButtons.indexOf(i) !== -1;
332
333 var w = colInfo.width;
334 if (w) {
335 if (/\d+px/.test(w)) {
336 continue;
337 } else if (/\d+%/.test(w)) {
338 if (hasExpandButton) {
339 throw new Error('Columns cannot be %-sized and host ' +
340 ' an expand button');
341 }
342 } else {
343 throw new Error('Unrecognized width string');
344 }
345 }
346 }
347
348 // Commit the change.
349 this.tableColumns_ = columns;
350 this.headerCells_ = [];
351 this.columnsWithExpandButtons_ = columnsWithExpandButtons;
352 this.sortColumnIndex = undefined;
353 this.scheduleRebuildHeaders_();
354
355 // Blow away the table rows, too.
356 this.tableRows = this.tableRows_;
357 },
358
359 get tableColumns() {
360 return this.tableColumns_;
361 },
362
363 /**
364 * @param {Array} rows An array of 'row' objects with the following
365 * fields:
366 * optional: subRows An array of objects that have the same 'row'
367 * structure. Set subRowsPropertyName to use an
368 * alternative field name.
369 */
370 set tableRows(rows) {
371 this.selectedTableRowInfo_ = undefined;
372 this.selectedColumnIndex_ = undefined;
373 this.maybeUpdateSelectedRow_();
374 this.tableRows_ = rows;
375 this.tableRowsInfo_ = new WeakMap();
376 this.scheduleRebuildBody_();
377 },
378
379 get tableRows() {
380 return this.tableRows_;
381 },
382
383 set footerRows(rows) {
384 this.tableFooterRows_ = rows;
385 this.tableFooterRowsInfo_ = new WeakMap();
386 this.scheduleRebuildFooter_();
387 },
388
389 get footerRows() {
390 return this.tableFooterRows_;
391 },
392
393 set sortColumnIndex(number) {
394 if (number === this.sortColumnIndex_)
395 return;
396
397 if (number === undefined) {
398 this.sortColumnIndex_ = undefined;
399 this.updateHeaderArrows_();
400 this.dispatchSortingChangedEvent_();
401 return;
402 }
403
404 if (this.tableColumns_.length <= number)
405 throw new Error('Column number ' + number + ' is out of bounds.');
406 if (!this.tableColumns_[number].cmp)
407 throw new Error('Column ' + number + ' does not have a comparator.');
408
409 this.sortColumnIndex_ = number;
410 this.updateHeaderArrows_(); 430 this.updateHeaderArrows_();
411 this.scheduleRebuildBody_(); 431 this.scheduleRebuildBody_();
412 this.dispatchSortingChangedEvent_(); 432 this.dispatchSortingChangedEvent_();
413 }, 433 }
414 434 },
415 get sortColumnIndex() { 435
416 return this.sortColumnIndex_; 436 get sortDescending() {
417 }, 437 return this.sortDescending_;
418 438 },
419 set sortDescending(value) { 439
420 var newValue = !!value; 440 updateHeaderArrows_: function() {
421 441 for (var i = 0; i < this.headerCells_.length; i++) {
422 if (newValue !== this.sortDescending_) { 442 if (!this.tableColumns_[i].cmp) {
423 this.sortDescending_ = newValue; 443 this.headerCells_[i].sideContent = '';
424 this.updateHeaderArrows_(); 444 continue;
425 this.scheduleRebuildBody_(); 445 }
426 this.dispatchSortingChangedEvent_(); 446 if (i !== this.sortColumnIndex_) {
427 } 447 this.headerCells_[i].sideContent = UNSORTED_ARROW;
428 }, 448 continue;
429 449 }
430 get sortDescending() { 450 this.headerCells_[i].sideContent = this.sortDescending_ ?
431 return this.sortDescending_; 451 DESCENDING_ARROW : ASCENDING_ARROW;
432 }, 452 }
433 453 },
434 updateHeaderArrows_: function() { 454
435 for (var i = 0; i < this.headerCells_.length; i++) { 455 sortRows_: function(rows) {
436 if (!this.tableColumns_[i].cmp) { 456 rows.sort(function(rowA, rowB) {
437 this.headerCells_[i].sideContent = ''; 457 if (this.sortDescending_)
438 continue; 458 return this.tableColumns_[this.sortColumnIndex_].cmp(
459 rowB.userRow, rowA.userRow);
460 return this.tableColumns_[this.sortColumnIndex_].cmp(
461 rowA.userRow, rowB.userRow);
462 }.bind(this));
463 // Sort expanded sub rows recursively.
464 for (var i = 0; i < rows.length; i++) {
465 if (this.getExpandedForUserRow_(rows[i]))
466 this.sortRows_(rows[i][this.subRowsPropertyName_]);
467 }
468 },
469
470 generateHeaderColumns_: function() {
471 this.headerCells_ = [];
472 Polymer.dom(this.$.head).textContent = '';
473 if (!this.showHeader_)
474 return;
475
476 var tr = this.appendNewElement_(this.$.head, 'tr');
477 for (var i = 0; i < this.tableColumns_.length; i++) {
478 var td = this.appendNewElement_(tr, 'td');
479
480 var headerCell = document.createElement('tr-ui-b-table-header-cell');
481 headerCell.column = this.tableColumns_[i];
482
483 headerCell.addEventListener('selected-column-changed',
484 this.onSelectedColumnChanged_.bind(this));
485
486 // If the table can be sorted by this column, attach a tap callback
487 // to the column.
488 if (this.tableColumns_[i].cmp) {
489 Polymer.dom(td).classList.add('sensitive');
490 headerCell.tapCallback = this.createSortCallback_(i);
491 // Set arrow position, depending on the sortColumnIndex.
492 if (this.sortColumnIndex_ === i)
493 headerCell.sideContent = this.sortDescending_ ?
494 DESCENDING_ARROW : ASCENDING_ARROW;
495 else
496 headerCell.sideContent = UNSORTED_ARROW;
497 }
498
499 Polymer.dom(td).appendChild(headerCell);
500 this.headerCells_.push(headerCell);
501 }
502 },
503
504 onSelectedColumnChanged_: function(event) {
505 // Unselect all other columns.
506 for (var i = 0; i < this.headerCells_.length; ++i) {
507 var colElement = Polymer.dom(this.$.cols).children[i];
508 var headerCell = this.headerCells_[i];
509 if ((event.column === headerCell.column) && event.selected) {
510 Polymer.dom(colElement).setAttribute('selected', true);
511 } else {
512 headerCell.selected = false;
513 Polymer.dom(colElement).removeAttribute('selected');
514 }
515 }
516 },
517
518 applySizes_: function() {
519 if (this.tableRows_.length === 0 && !this.showHeader)
520 return;
521 var rowToRemoveSizing;
522 var rowToSize;
523 if (this.showHeader) {
524 rowToSize = Polymer.dom(this.$.head).children[0];
525 rowToRemoveSizing = Polymer.dom(this.$.body).children[0];
526 } else {
527 rowToSize = Polymer.dom(this.$.body).children[0];
528 rowToRemoveSizing = Polymer.dom(this.$.head).children[0];
529 }
530 for (var i = 0; i < this.tableColumns_.length; i++) {
531 if (rowToRemoveSizing && Polymer.dom(rowToRemoveSizing).children[i]) {
532 var tdToRemoveSizing = Polymer.dom(rowToRemoveSizing).children[i];
533 tdToRemoveSizing.style.minWidth = '';
534 tdToRemoveSizing.style.width = '';
535 }
536
537 // Apply sizing.
538 var td = Polymer.dom(rowToSize).children[i];
539
540 var delta;
541 if (this.columnsWithExpandButtons_.indexOf(i) !== -1) {
542 td.style.paddingLeft = BASIC_INDENTATION + 'px';
543 delta = BASIC_INDENTATION + 'px';
544 } else {
545 delta = undefined;
546 }
547
548 function calc(base, delta) {
549 if (delta)
550 return 'calc(' + base + ' - ' + delta + ')';
551 else
552 return base;
553 }
554
555 var w = this.tableColumns_[i].width;
556 if (w) {
557 if (/\d+px/.test(w)) {
558 td.style.minWidth = calc(w, delta);
559 } else if (/\d+%/.test(w)) {
560 td.style.width = w;
561 } else {
562 throw new Error('Unrecognized width string: ' + w);
439 } 563 }
440 if (i !== this.sortColumnIndex_) { 564 }
441 this.headerCells_[i].sideContent = UNSORTED_ARROW; 565 }
442 continue; 566 },
567
568 createSortCallback_: function(columnNumber) {
569 return function() {
570 var previousIndex = this.sortColumnIndex;
571 this.sortColumnIndex = columnNumber;
572 if (previousIndex !== columnNumber)
573 this.sortDescending = false;
574 else
575 this.sortDescending = !this.sortDescending;
576 }.bind(this);
577 },
578
579 generateTableColNodes_: function() {
580 for (var i = 0; i < this.headerCells_.length; ++i) {
581 var colElement = document.createElement('col');
582 if (this.headerCells_[i].selected)
583 Polymer.dom(colElement).setAttribute('selected', true);
584 Polymer.dom(this.$.cols).appendChild(colElement);
585 }
586 },
587
588 generateTableRowNodes_: function(tableSection, userRows, rowInfoMap,
589 indentation, lastAddedRow,
590 parentRowInfo) {
591 if (this.sortColumnIndex_ !== undefined &&
592 tableSection === this.$.body) {
593 userRows = userRows.slice(); // Don't mess with the input data.
594 userRows.sort(function(rowA, rowB) {
595 var c = this.tableColumns_[this.sortColumnIndex_].cmp(
596 rowA, rowB);
597 if (this.sortDescending_)
598 c = -c;
599 return c;
600 }.bind(this));
601 }
602
603 for (var i = 0; i < userRows.length; i++) {
604 var userRow = userRows[i];
605 var rowInfo = this.getOrCreateRowInfoFor_(rowInfoMap, userRow,
606 parentRowInfo);
607 var htmlNode = this.getHTMLNodeForRowInfo_(
608 tableSection, rowInfo, rowInfoMap, indentation);
609
610 if (lastAddedRow === undefined) {
611 // Put first into the table.
612 Polymer.dom(tableSection).insertBefore(
613 htmlNode, Polymer.dom(tableSection).firstChild);
614 } else {
615 // This is shorthand for insertAfter(htmlNode, lastAdded).
616 var nextSiblingOfLastAdded = Polymer.dom(lastAddedRow).nextSibling;
617 Polymer.dom(tableSection).insertBefore(
618 htmlNode, nextSiblingOfLastAdded);
619 }
620 this.updateTabIndexForTableRowNode_(htmlNode);
621
622 lastAddedRow = htmlNode;
623 if (!rowInfo.isExpanded)
624 continue;
625
626 // Append subrows now.
627 lastAddedRow = this.generateTableRowNodes_(
628 tableSection, userRow[this.subRowsPropertyName_], rowInfoMap,
629 indentation + 1, lastAddedRow, rowInfo);
630 }
631 return lastAddedRow;
632 },
633
634 getOrCreateRowInfoFor_: function(rowInfoMap, userRow, parentRowInfo) {
635 var rowInfo = undefined;
636
637 if (rowInfoMap.has(userRow)) {
638 rowInfo = rowInfoMap.get(userRow);
639 } else {
640 rowInfo = {
641 userRow: userRow,
642 htmlNode: undefined,
643 parentRowInfo: parentRowInfo
644 };
645 rowInfoMap.set(userRow, rowInfo);
646 }
647
648 // Recompute isExpanded in case defaultExpansionStateCallback_ has
649 // changed.
650 rowInfo.isExpanded = this.getExpandedForUserRow_(userRow);
651
652 return rowInfo;
653 },
654
655 customizeTableRow_: function(userRow, trElement) {
656 if (!this.customizeTableRowCallback_)
657 return;
658 this.customizeTableRowCallback_(userRow, trElement);
659 },
660
661 getHTMLNodeForRowInfo_: function(tableSection, rowInfo,
662 rowInfoMap, indentation) {
663 if (rowInfo.htmlNode) {
664 this.customizeTableRow_(rowInfo.userRow, rowInfo.htmlNode);
665 return rowInfo.htmlNode;
666 }
667
668 var INDENT_SPACE = indentation * 16;
669 var INDENT_SPACE_NO_BUTTON = indentation * 16 + BASIC_INDENTATION;
670 var trElement = this.ownerDocument.createElement('tr');
671 rowInfo.htmlNode = trElement;
672 rowInfo.indentation = indentation;
673 trElement.rowInfo = rowInfo;
674 this.customizeTableRow_(rowInfo.userRow, trElement);
675
676 for (var i = 0; i < this.tableColumns_.length;) {
677 var td = this.appendNewElement_(trElement, 'td');
678 td.columnIndex = i;
679
680 var column = this.tableColumns_[i];
681 var value = column.value(rowInfo.userRow);
682 var colSpan = column.colSpan ? column.colSpan : 1;
683 td.style.colSpan = colSpan;
684
685 switch (column.align) {
686 case undefined:
687 case ColumnAlignment.LEFT:
688 break;
689
690 case ColumnAlignment.RIGHT:
691 td.style.textAlign = 'right';
692 break;
693
694 default:
695 throw new Error('Invalid alignment of column at index=' + i +
696 ': ' + column.align);
697 }
698
699 if (this.doesColumnIndexSupportSelection(i))
700 Polymer.dom(td).classList.add('supports-selection');
701
702 if (this.columnsWithExpandButtons_.indexOf(i) != -1) {
703 if (rowInfo.userRow[this.subRowsPropertyName_] &&
704 rowInfo.userRow[this.subRowsPropertyName_].length > 0) {
705 td.style.paddingLeft = INDENT_SPACE + 'px';
706 var expandButton = this.appendNewElement_(td,
707 'expand-button');
708 Polymer.dom(expandButton).textContent = RIGHT_ARROW;
709 if (rowInfo.isExpanded)
710 Polymer.dom(expandButton).classList.add('button-expanded');
711 } else {
712 td.style.paddingLeft = INDENT_SPACE_NO_BUTTON + 'px';
443 } 713 }
444 this.headerCells_[i].sideContent = this.sortDescending_ ? 714 }
445 DESCENDING_ARROW : ASCENDING_ARROW; 715
446 } 716 if (value !== undefined) {
447 }, 717 Polymer.dom(td).appendChild(
448 718 tr.ui.b.asHTMLOrTextNode(value, this.ownerDocument));
449 sortRows_: function(rows) { 719 }
450 rows.sort(function(rowA, rowB) { 720
451 if (this.sortDescending_) 721 i += colSpan;
452 return this.tableColumns_[this.sortColumnIndex_].cmp( 722 }
453 rowB.userRow, rowA.userRow); 723
454 return this.tableColumns_[this.sortColumnIndex_].cmp( 724 var isSelectable = tableSection === this.$.body;
455 rowA.userRow, rowB.userRow); 725 var isExpandable = rowInfo.userRow[this.subRowsPropertyName_] &&
456 }.bind(this)); 726 rowInfo.userRow[this.subRowsPropertyName_].length;
457 // Sort expanded sub rows recursively. 727
458 for (var i = 0; i < rows.length; i++) { 728 if (isSelectable || isExpandable) {
459 if (this.getExpandedForUserRow_(rows[i])) 729 trElement.addEventListener('click', function(e) {
460 this.sortRows_(rows[i][this.subRowsPropertyName_]); 730 e.stopPropagation();
461 } 731 if (e.target.tagName == 'EXPAND-BUTTON') {
462 }, 732 this.setExpandedForUserRow_(
463 733 tableSection, rowInfoMap,
464 generateHeaderColumns_: function() { 734 rowInfo.userRow, !rowInfo.isExpanded);
465 this.headerCells_ = []; 735 return;
466 Polymer.dom(this.$.head).textContent = '';
467 if (!this.showHeader_)
468 return;
469
470 var tr = this.appendNewElement_(this.$.head, 'tr');
471 for (var i = 0; i < this.tableColumns_.length; i++) {
472 var td = this.appendNewElement_(tr, 'td');
473
474 var headerCell = document.createElement('tr-ui-b-table-header-cell');
475 headerCell.cellTitle = this.tableColumns_[i].title;
476 headerCell.align = this.tableColumns_[i].align;
477
478 // If the table can be sorted by this column, attach a tap callback
479 // to the column.
480 if (this.tableColumns_[i].cmp) {
481 Polymer.dom(td).classList.add('sensitive');
482 headerCell.tapCallback = this.createSortCallback_(i);
483 // Set arrow position, depending on the sortColumnIndex.
484 if (this.sortColumnIndex_ === i)
485 headerCell.sideContent = this.sortDescending_ ?
486 DESCENDING_ARROW : ASCENDING_ARROW;
487 else
488 headerCell.sideContent = UNSORTED_ARROW;
489 } 736 }
490 737
491 Polymer.dom(td).appendChild(headerCell); 738 function getTD(cur) {
492 this.headerCells_.push(headerCell); 739 if (cur === trElement)
493 } 740 throw new Error('woah');
494 }, 741 if (cur.parentElement === trElement)
495 742 return cur;
496 applySizes_: function() { 743 return getTD(cur.parentElement);
497 if (this.tableRows_.length === 0 && !this.showHeader)
498 return;
499 var rowToRemoveSizing;
500 var rowToSize;
501 if (this.showHeader) {
502 rowToSize = this.$.head.children[0];
503 rowToRemoveSizing = this.$.body.children[0];
504 } else {
505 rowToSize = this.$.body.children[0];
506 rowToRemoveSizing = this.$.head.children[0];
507 }
508 for (var i = 0; i < this.tableColumns_.length; i++) {
509 if (rowToRemoveSizing && rowToRemoveSizing.children[i]) {
510 var tdToRemoveSizing = rowToRemoveSizing.children[i];
511 tdToRemoveSizing.style.minWidth = '';
512 tdToRemoveSizing.style.width = '';
513 } 744 }
514 745
515 // Apply sizing. 746 // If the row/cell can be selected and it's not selected yet,
516 var td = rowToSize.children[i]; 747 // select it.
517 748 if (isSelectable && this.selectionMode_ !== SelectionMode.NONE) {
518 var delta; 749 var shouldSelect = false;
519 if (this.columnsWithExpandButtons_.indexOf(i) !== -1) { 750 var columnIndex = getTD(e.target).columnIndex;
520 td.style.paddingLeft = BASIC_INDENTATION + 'px'; 751 switch (this.selectionMode_) {
521 delta = BASIC_INDENTATION + 'px'; 752 case SelectionMode.ROW:
522 } else { 753 shouldSelect = this.selectedTableRowInfo_ !== rowInfo;
523 delta = undefined; 754 break;
524 } 755
525 756 case SelectionMode.CELL:
526 function calc(base, delta) { 757 if (this.doesColumnIndexSupportSelection(columnIndex)) {
527 if (delta) 758 shouldSelect = this.selectedTableRowInfo_ !== rowInfo ||
528 return 'calc(' + base + ' - ' + delta + ')'; 759 this.selectedColumnIndex_ !== columnIndex;
529 else 760 }
530 return base; 761 break;
531 } 762
532 763 default:
533 var w = this.tableColumns_[i].width; 764 throw new Error('Invalid selection mode ' +
534 if (w) { 765 this.selectionMode_);
535 if (/\d+px/.test(w)) { 766 }
536 td.style.minWidth = calc(w, delta); 767 if (shouldSelect) {
537 } else if (/\d+%/.test(w)) { 768 this.didTableRowInfoGetClicked_(rowInfo, columnIndex);
538 td.style.width = w; 769 return;
539 } else {
540 throw new Error('Unrecognized width string: ' + w);
541 } 770 }
542 } 771 }
543 } 772
544 }, 773 // Otherwise, if the row is expandable, expand/collapse it.
545 774 if (isExpandable) {
546 createSortCallback_: function(columnNumber) { 775 this.setExpandedForUserRow_(tableSection, rowInfoMap,
547 return function() { 776 rowInfo.userRow, !rowInfo.isExpanded);
548 var previousIndex = this.sortColumnIndex;
549 this.sortColumnIndex = columnNumber;
550 if (previousIndex !== columnNumber)
551 this.sortDescending = false;
552 else
553 this.sortDescending = !this.sortDescending;
554 }.bind(this);
555 },
556
557 generateTableRowNodes_: function(tableSection, userRows, rowInfoMap,
558 indentation, lastAddedRow,
559 parentRowInfo) {
560 if (this.sortColumnIndex_ !== undefined &&
561 tableSection === this.$.body) {
562 userRows = userRows.slice(); // Don't mess with the input data.
563 userRows.sort(function(rowA, rowB) {
564 var c = this.tableColumns_[this.sortColumnIndex_].cmp(
565 rowA, rowB);
566 if (this.sortDescending_)
567 c = -c;
568 return c;
569 }.bind(this));
570 }
571
572 for (var i = 0; i < userRows.length; i++) {
573 var userRow = userRows[i];
574 var rowInfo = this.getOrCreateRowInfoFor_(rowInfoMap, userRow,
575 parentRowInfo);
576 var htmlNode = this.getHTMLNodeForRowInfo_(
577 tableSection, rowInfo, rowInfoMap, indentation);
578
579 if (lastAddedRow === undefined) {
580 // Put first into the table.
581 Polymer.dom(tableSection).insertBefore(
582 htmlNode, Polymer.dom(tableSection).firstChild);
583 } else {
584 // This is shorthand for insertAfter(htmlNode, lastAdded).
585 var nextSiblingOfLastAdded = Polymer.dom(lastAddedRow).nextSibling;
586 Polymer.dom(tableSection).insertBefore(
587 htmlNode, nextSiblingOfLastAdded);
588 } 777 }
589 this.updateTabIndexForTableRowNode_(htmlNode); 778 }.bind(this));
590 779 }
591 lastAddedRow = htmlNode; 780
592 if (!rowInfo.isExpanded) 781 return rowInfo.htmlNode;
593 continue; 782 },
594 783
595 // Append subrows now. 784 removeSubNodes_: function(tableSection, rowInfo, rowInfoMap) {
596 lastAddedRow = this.generateTableRowNodes_( 785 if (rowInfo.userRow[this.subRowsPropertyName_] === undefined)
597 tableSection, userRow[this.subRowsPropertyName_], rowInfoMap, 786 return;
598 indentation + 1, lastAddedRow, rowInfo); 787 for (var i = 0;
599 } 788 i < rowInfo.userRow[this.subRowsPropertyName_].length; i++) {
600 return lastAddedRow; 789 var subRow = rowInfo.userRow[this.subRowsPropertyName_][i];
601 }, 790 var subRowInfo = rowInfoMap.get(subRow);
602 791 if (!subRowInfo)
603 getOrCreateRowInfoFor_: function(rowInfoMap, userRow, parentRowInfo) { 792 continue;
604 var rowInfo = undefined; 793
605 794 var subNode = subRowInfo.htmlNode;
606 if (rowInfoMap.has(userRow)) { 795 if (subNode && Polymer.dom(subNode).parentNode === tableSection) {
607 rowInfo = rowInfoMap.get(userRow); 796 Polymer.dom(tableSection).removeChild(subNode);
797 this.removeSubNodes_(tableSection, subRowInfo, rowInfoMap);
798 }
799 }
800 },
801
802 scheduleRebuildHeaders_: function() {
803 this.headerDirty_ = true;
804 this.scheduleRebuild_();
805 },
806
807 scheduleRebuildBody_: function() {
808 this.bodyDirty_ = true;
809 this.scheduleRebuild_();
810 },
811
812 scheduleRebuildFooter_: function() {
813 this.footerDirty_ = true;
814 this.scheduleRebuild_();
815 },
816
817 scheduleRebuild_: function() {
818 if (this.rebuildPending_)
819 return;
820 this.rebuildPending_ = true;
821 setTimeout(function() {
822 this.rebuildPending_ = false;
823 this.rebuild();
824 }.bind(this), 0);
825 },
826
827 rebuildIfNeeded_: function() {
828 this.rebuild();
829 },
830
831 rebuild: function() {
832 var wasBodyOrHeaderDirty = this.headerDirty_ || this.bodyDirty_;
833
834 if (this.headerDirty_) {
835 this.generateHeaderColumns_();
836 this.headerDirty_ = false;
837 }
838 if (this.bodyDirty_) {
839 Polymer.dom(this.$.cols).textContent = '';
840 this.generateTableColNodes_();
841 Polymer.dom(this.$.body).textContent = '';
842 this.generateTableRowNodes_(
843 this.$.body,
844 this.tableRows_, this.tableRowsInfo_, 0,
845 undefined, undefined);
846 if (this.tableRows_.length === 0 && this.emptyValue_ !== undefined) {
847 var trElement = this.ownerDocument.createElement('tr');
848 Polymer.dom(this.$.body).appendChild(trElement);
849 Polymer.dom(trElement).classList.add('empty-row');
850 var td = this.ownerDocument.createElement('td');
851 Polymer.dom(trElement).appendChild(td);
852 td.colSpan = this.tableColumns_.length;
853 var emptyValue = this.emptyValue_;
854 Polymer.dom(td).appendChild(
855 tr.ui.b.asHTMLOrTextNode(emptyValue, this.ownerDocument));
856 }
857 this.bodyDirty_ = false;
858 }
859
860 if (wasBodyOrHeaderDirty)
861 this.applySizes_();
862
863 if (this.footerDirty_) {
864 Polymer.dom(this.$.foot).textContent = '';
865 this.generateTableRowNodes_(
866 this.$.foot,
867 this.tableFooterRows_, this.tableFooterRowsInfo_, 0,
868 undefined, undefined);
869 if (this.tableFooterRowsInfo_.length) {
870 Polymer.dom(this.$.body).classList.add('has-footer');
608 } else { 871 } else {
609 rowInfo = { 872 Polymer.dom(this.$.body).classList.remove('has-footer');
610 userRow: userRow, 873 }
611 htmlNode: undefined, 874 this.footerDirty_ = false;
612 parentRowInfo: parentRowInfo 875 }
613 }; 876 },
614 rowInfoMap.set(userRow, rowInfo); 877
615 } 878 appendNewElement_: function(parent, tagName) {
616 879 var element = parent.ownerDocument.createElement(tagName);
617 // Recompute isExpanded in case defaultExpansionStateCallback_ has 880 Polymer.dom(parent).appendChild(element);
618 // changed. 881 return element;
619 rowInfo.isExpanded = this.getExpandedForUserRow_(userRow); 882 },
620 883
621 return rowInfo; 884 getExpandedForTableRow: function(userRow) {
622 }, 885 this.rebuildIfNeeded_();
623 886 var rowInfo = this.tableRowsInfo_.get(userRow);
624 customizeTableRow_: function(userRow, trElement) { 887 if (rowInfo === undefined)
625 if (!this.customizeTableRowCallback_) 888 throw new Error('Row has not been seen, must expand its parents');
889 return rowInfo.isExpanded;
890 },
891
892 getExpandedForUserRow_: function(userRow) {
893 if (userRow[this.subRowsPropertyName_] === undefined)
894 return false;
895 if (userRow[this.subRowsPropertyName_].length === 0)
896 return false;
897 if (userRow.isExpanded)
898 return true;
899 if (userRow.isExpanded === false)
900 return false;
901
902 var rowInfo = this.tableRowsInfo_.get(userRow);
903 if (rowInfo && rowInfo.isExpanded)
904 return true;
905
906 if (this.defaultExpansionStateCallback_ === undefined)
907 return false;
908
909 var parentUserRow = undefined;
910 if (rowInfo && rowInfo.parentRowInfo)
911 parentUserRow = rowInfo.parentRowInfo.userRow;
912
913 return this.defaultExpansionStateCallback_(
914 userRow, parentUserRow);
915 },
916
917 setExpandedForTableRow: function(userRow, expanded) {
918 this.rebuildIfNeeded_();
919 var rowInfo = this.tableRowsInfo_.get(userRow);
920 if (rowInfo === undefined)
921 throw new Error('Row has not been seen, must expand its parents');
922 return this.setExpandedForUserRow_(this.$.body, this.tableRowsInfo_,
923 userRow, expanded);
924 },
925
926 setExpandedForUserRow_: function(tableSection, rowInfoMap,
927 userRow, expanded) {
928 this.rebuildIfNeeded_();
929
930 var rowInfo = rowInfoMap.get(userRow);
931 if (rowInfo === undefined)
932 throw new Error('Row has not been seen, must expand its parents');
933
934 rowInfo.isExpanded = !!expanded;
935 // If no node, then nothing further needs doing.
936 if (rowInfo.htmlNode === undefined)
937 return;
938
939 // If its detached, then nothing needs doing.
940 if (rowInfo.htmlNode.parentElement !== tableSection)
941 return;
942
943 // Otherwise, rebuild.
944 var expandButton =
945 Polymer.dom(rowInfo.htmlNode).querySelector('expand-button');
946 if (rowInfo.isExpanded) {
947 Polymer.dom(expandButton).classList.add('button-expanded');
948 var lastAddedRow = rowInfo.htmlNode;
949 if (rowInfo.userRow[this.subRowsPropertyName_]) {
950 this.generateTableRowNodes_(
951 tableSection,
952 rowInfo.userRow[this.subRowsPropertyName_], rowInfoMap,
953 rowInfo.indentation + 1,
954 lastAddedRow, rowInfo);
955 }
956 } else {
957 Polymer.dom(expandButton).classList.remove('button-expanded');
958 this.removeSubNodes_(tableSection, rowInfo, rowInfoMap);
959 }
960
961 this.maybeUpdateSelectedRow_();
962 },
963
964 get selectionMode() {
965 return this.selectionMode_;
966 },
967
968 set selectionMode(selectionMode) {
969 if (!tr.b.dictionaryContainsValue(SelectionMode, selectionMode))
970 throw new Error('Invalid selection mode ' + selectionMode);
971 this.rebuildIfNeeded_();
972 this.selectionMode_ = selectionMode;
973 this.didSelectionStateChange_();
974 },
975
976 get rowHighlightStyle() {
977 return this.rowHighlightStyle_;
978 },
979
980 set rowHighlightStyle(rowHighlightStyle) {
981 if (!tr.b.dictionaryContainsValue(HighlightStyle, rowHighlightStyle))
982 throw new Error('Invalid row highlight style ' + rowHighlightStyle);
983 this.rebuildIfNeeded_();
984 this.rowHighlightStyle_ = rowHighlightStyle;
985 this.didSelectionStateChange_();
986 },
987
988 get resolvedRowHighlightStyle() {
989 if (this.rowHighlightStyle_ !== HighlightStyle.DEFAULT)
990 return this.rowHighlightStyle_;
991 switch (this.selectionMode_) {
992 case SelectionMode.NONE:
993 return HighlightStyle.NONE;
994 case SelectionMode.ROW:
995 return HighlightStyle.DARK;
996 case SelectionMode.CELL:
997 return HighlightStyle.LIGHT;
998 default:
999 throw new Error('Invalid selection mode ' + selectionMode);
1000 }
1001 },
1002
1003 get cellHighlightStyle() {
1004 return this.cellHighlightStyle_;
1005 },
1006
1007 set cellHighlightStyle(cellHighlightStyle) {
1008 if (!tr.b.dictionaryContainsValue(HighlightStyle, cellHighlightStyle))
1009 throw new Error('Invalid cell highlight style ' + cellHighlightStyle);
1010 this.rebuildIfNeeded_();
1011 this.cellHighlightStyle_ = cellHighlightStyle;
1012 this.didSelectionStateChange_();
1013 },
1014
1015 get resolvedCellHighlightStyle() {
1016 if (this.cellHighlightStyle_ !== HighlightStyle.DEFAULT)
1017 return this.cellHighlightStyle_;
1018 switch (this.selectionMode_) {
1019 case SelectionMode.NONE:
1020 case SelectionMode.ROW:
1021 return HighlightStyle.NONE;
1022 case SelectionMode.CELL:
1023 return HighlightStyle.DARK;
1024 default:
1025 throw new Error('Invalid selection mode ' + selectionMode);
1026 }
1027 },
1028
1029 setHighlightStyle_: function(highlightAttribute, resolvedHighlightStyle) {
1030 switch (resolvedHighlightStyle) {
1031 case HighlightStyle.NONE:
1032 Polymer.dom(this.$.body).removeAttribute(highlightAttribute);
1033 break;
1034 case HighlightStyle.LIGHT:
1035 Polymer.dom(this.$.body).setAttribute(highlightAttribute, 'light');
1036 break;
1037 case HighlightStyle.DARK:
1038 Polymer.dom(this.$.body).setAttribute(highlightAttribute, 'dark');
1039 break;
1040 default:
1041 throw new Error('Invalid resolved highlight style ' +
1042 resolvedHighlightStyle);
1043 }
1044 },
1045
1046 didSelectionStateChange_: function() {
1047 this.setHighlightStyle_('row-highlight-style',
1048 this.resolvedRowHighlightStyle);
1049 this.setHighlightStyle_('cell-highlight-style',
1050 this.resolvedCellHighlightStyle);
1051
1052 for (var i = 0; i < Polymer.dom(this.$.body).children.length; i++) {
1053 this.updateTabIndexForTableRowNode_(
1054 Polymer.dom(this.$.body).children[i]);
1055 }
1056 this.maybeUpdateSelectedRow_();
1057 },
1058
1059 maybeUpdateSelectedRow_: function() {
1060 if (this.selectedTableRowInfo_ === undefined)
1061 return;
1062
1063 // Selection may be off.
1064 if (this.selectionMode_ === SelectionMode.NONE) {
1065 this.removeSelectedState_();
1066 this.selectedTableRowInfo_ = undefined;
1067 return;
1068 }
1069
1070 // selectedUserRow may not be visible
1071 function isVisible(rowInfo) {
1072 if (!rowInfo.htmlNode)
1073 return false;
1074 return !!rowInfo.htmlNode.parentElement;
1075 }
1076 if (isVisible(this.selectedTableRowInfo_)) {
1077 this.updateSelectedState_();
1078 return;
1079 }
1080
1081 this.removeSelectedState_();
1082 var curRowInfo = this.selectedTableRowInfo_;
1083 while (curRowInfo && !isVisible(curRowInfo))
1084 curRowInfo = curRowInfo.parentRowInfo;
1085
1086 this.selectedTableRowInfo_ = curRowInfo;
1087 if (this.selectedTableRowInfo_)
1088 this.updateSelectedState_();
1089 },
1090
1091 didTableRowInfoGetClicked_: function(rowInfo, columnIndex) {
1092 switch (this.selectionMode_) {
1093 case SelectionMode.NONE:
626 return; 1094 return;
627 this.customizeTableRowCallback_(userRow, trElement); 1095
628 }, 1096 case SelectionMode.CELL:
629 1097 if (!this.doesColumnIndexSupportSelection(columnIndex))
630 getHTMLNodeForRowInfo_: function(tableSection, rowInfo, 1098 return;
631 rowInfoMap, indentation) { 1099 if (this.selectedColumnIndex !== columnIndex)
632 if (rowInfo.htmlNode) { 1100 this.selectedColumnIndex = columnIndex;
633 this.customizeTableRow_(rowInfo.userRow, rowInfo.htmlNode); 1101 // Fall through.
634 return rowInfo.htmlNode; 1102
635 } 1103 case SelectionMode.ROW:
636 1104 if (this.selectedTableRowInfo_ !== rowInfo)
637 var INDENT_SPACE = indentation * 16; 1105 this.selectedTableRow = rowInfo.userRow;
638 var INDENT_SPACE_NO_BUTTON = indentation * 16 + BASIC_INDENTATION; 1106 }
639 var trElement = this.ownerDocument.createElement('tr'); 1107 },
640 rowInfo.htmlNode = trElement; 1108
641 rowInfo.indentation = indentation; 1109 /**
642 trElement.rowInfo = rowInfo; 1110 * If the selectionMode is CELL and a cell is selected,
643 this.customizeTableRow_(rowInfo.userRow, trElement); 1111 * return an object containing the row, column, and value of the selected
644 1112 * cell.
645 for (var i = 0; i < this.tableColumns_.length;) { 1113 *
646 var td = this.appendNewElement_(trElement, 'td'); 1114 * @return {undefined|!Object}
647 td.columnIndex = i; 1115 */
648 1116 get selectedCell() {
649 var column = this.tableColumns_[i]; 1117 var row = this.selectedTableRow;
650 var value = column.value(rowInfo.userRow); 1118 var columnIndex = this.selectedColumnIndex;
651 var colSpan = column.colSpan ? column.colSpan : 1; 1119 if (row === undefined || columnIndex === undefined ||
652 td.style.colSpan = colSpan; 1120 this.tableColumns_.length <= columnIndex)
653 1121 return undefined;
654 switch (column.align) { 1122 var column = this.tableColumns_[columnIndex];
655 case undefined: 1123 return {
656 case ColumnAlignment.LEFT: 1124 row: row,
657 break; 1125 column: column,
658 1126 value: column.value(row)
659 case ColumnAlignment.RIGHT: 1127 };
660 td.style.textAlign = 'right'; 1128 },
661 break; 1129
662 1130 /**
663 default: 1131 * If a selectable column is selected, return the object describing the
664 throw new Error('Invalid alignment of column at index=' + i + 1132 * selected column.
665 ': ' + column.align); 1133 *
666 } 1134 * Columns with |selectable:true| can be selected independently of rows
667 1135 * and cells. So it is possible to select column 0 and cell [0,0], or
668 if (this.doesColumnIndexSupportSelection(i)) 1136 * column 1 and cell [0,0], for example. See |selectedCell| for how to
669 Polymer.dom(td).classList.add('supports-selection'); 1137 * access the selected cell when the selectionMode is CELL.
670 1138 *
671 if (this.columnsWithExpandButtons_.indexOf(i) != -1) { 1139 * |selectedTableColumn| is entirely independent of |selectedColumnIndex|.
672 if (rowInfo.userRow[this.subRowsPropertyName_] && 1140 * When the table selectionMode is CELL, use |selectedTableRow| and
673 rowInfo.userRow[this.subRowsPropertyName_].length > 0) { 1141 * |selectedColumnIndex| to find the selected cell.
674 td.style.paddingLeft = INDENT_SPACE + 'px'; 1142 * When one or more columns have |selectable:true|, then use
675 var expandButton = this.appendNewElement_(td, 1143 * |selectedTableColumn| to find the selected column, which may be either
676 'expand-button'); 1144 * the same as or different from |selectedColumnIndex|, if a cell is also
677 Polymer.dom(expandButton).textContent = RIGHT_ARROW; 1145 * selected.
678 if (rowInfo.isExpanded) 1146 *
679 Polymer.dom(expandButton).classList.add('button-expanded'); 1147 * @return {undefined|!Object} column
680 } else { 1148 */
681 td.style.paddingLeft = INDENT_SPACE_NO_BUTTON + 'px'; 1149 get selectedTableColumn() {
1150 for (var i = 0; i < this.headerCells_.length; i++) {
1151 if (this.headerCells_[i].selected)
1152 return this.tableColumns_[i];
1153 }
1154 return undefined;
1155 },
1156
1157 /**
1158 * See |get selectedTableColumn()|. |column| must be one of the elements
1159 * of this.tableColumns.
1160 *
1161 * @param {!Object} column
1162 */
1163 set selectedTableColumn(column) {
1164 if (column !== undefined) {
1165 var index = this.tableColumns.indexOf(column);
1166 if (index < 0)
1167 throw new Error('Cannot select unknown column', column);
1168
1169 if (!column.selectable)
1170 throw new Error('Cannot select un-selectable column', column);
1171
1172 var cell = this.headerCells_[index];
1173 cell.selected = true;
1174 }
1175 var e = new tr.b.Event('selected-column-changed');
1176 e.column = column;
1177 e.selected = column !== undefined;
1178 cell.dispatchEvent(e);
1179 },
1180
1181 get selectedTableRow() {
1182 if (!this.selectedTableRowInfo_)
1183 return undefined;
1184 return this.selectedTableRowInfo_.userRow;
1185 },
1186
1187 set selectedTableRow(userRow) {
1188 this.rebuildIfNeeded_();
1189 if (this.selectionMode_ === SelectionMode.NONE)
1190 throw new Error('Selection is off.');
1191
1192 var rowInfo;
1193 if (userRow === undefined) {
1194 rowInfo = undefined;
1195 } else {
1196 rowInfo = this.tableRowsInfo_.get(userRow);
1197 if (!rowInfo)
1198 throw new Error('Row has not been seen, must expand its parents.');
1199 }
1200
1201 var e = this.prepareToChangeSelection_();
1202 this.selectedTableRowInfo_ = rowInfo;
1203
1204 if (this.selectedTableRowInfo_ === undefined) {
1205 this.selectedColumnIndex_ = undefined;
1206 this.removeSelectedState_();
1207 } else {
1208 switch (this.selectionMode_) {
1209 case SelectionMode.ROW:
1210 this.selectedColumnIndex_ = undefined;
1211 break;
1212
1213 case SelectionMode.CELL:
1214 if (this.selectedColumnIndex_ === undefined) {
1215 var i = this.getFirstSelectableColumnIndex_();
1216 if (i == -1)
1217 throw new Error('Cannot find a selectable column.');
1218 this.selectedColumnIndex_ = i;
682 } 1219 }
683 } 1220 break;
684 1221
685 if (value !== undefined) 1222 default:
686 Polymer.dom(td).appendChild( 1223 throw new Error('Invalid selection mode ' + this.selectionMode_);
687 tr.ui.b.asHTMLOrTextNode(value, this.ownerDocument)); 1224 }
688 1225 this.updateSelectedState_();
689 i += colSpan; 1226 }
690 } 1227
691 1228 this.dispatchEvent(e);
692 var isSelectable = tableSection === this.$.body; 1229 },
693 var isExpandable = rowInfo.userRow[this.subRowsPropertyName_] && 1230
694 rowInfo.userRow[this.subRowsPropertyName_].length; 1231 updateTabIndexForTableRowNode_: function(row) {
695 1232 if (this.selectionMode_ === SelectionMode.ROW)
696 if (isSelectable || isExpandable) { 1233 row.tabIndex = 0;
697 trElement.addEventListener('click', function(e) { 1234 else
698 e.stopPropagation(); 1235 Polymer.dom(row).removeAttribute('tabIndex');
699 if (e.target.tagName == 'EXPAND-BUTTON') { 1236
700 this.setExpandedForUserRow_( 1237 var enableCellTab = this.selectionMode_ === SelectionMode.CELL;
701 tableSection, rowInfoMap, 1238 for (var i = 0; i < this.tableColumns_.length; i++) {
702 rowInfo.userRow, !rowInfo.isExpanded); 1239 var cell = Polymer.dom(row).children[i];
1240 if (enableCellTab && this.doesColumnIndexSupportSelection(i))
1241 cell.tabIndex = 0;
1242 else
1243 Polymer.dom(cell).removeAttribute('tabIndex');
1244 }
1245 },
1246
1247 prepareToChangeSelection_: function() {
1248 var e = new tr.b.Event('selection-changed');
1249 var previousSelectedRowInfo = this.selectedTableRowInfo_;
1250 if (previousSelectedRowInfo)
1251 e.previousSelectedTableRow = previousSelectedRowInfo.userRow;
1252 else
1253 e.previousSelectedTableRow = undefined;
1254
1255 this.removeSelectedState_();
1256
1257 return e;
1258 },
1259
1260 removeSelectedState_: function() {
1261 this.setSelectedState_(false);
1262 },
1263
1264 updateSelectedState_: function() {
1265 this.setSelectedState_(true);
1266 },
1267
1268 setSelectedState_: function(select) {
1269 if (this.selectedTableRowInfo_ === undefined)
1270 return;
1271
1272 // Row selection.
1273 var rowNode = this.selectedTableRowInfo_.htmlNode;
1274 if (select)
1275 Polymer.dom(rowNode).setAttribute('selected', true);
1276 else
1277 Polymer.dom(rowNode).removeAttribute('selected');
1278
1279 // Cell selection (if applicable).
1280 var cellNode = Polymer.dom(rowNode).children[this.selectedColumnIndex_];
1281 if (!cellNode)
1282 return;
1283 if (select)
1284 Polymer.dom(cellNode).setAttribute('selected', true);
1285 else
1286 Polymer.dom(cellNode).removeAttribute('selected');
1287 },
1288
1289 doesColumnIndexSupportSelection: function(columnIndex) {
1290 var columnInfo = this.tableColumns_[columnIndex];
1291 var scs = columnInfo.supportsCellSelection;
1292 if (scs === false)
1293 return false;
1294 return true;
1295 },
1296
1297 getFirstSelectableColumnIndex_: function() {
1298 for (var i = 0; i < this.tableColumns_.length; i++) {
1299 if (this.doesColumnIndexSupportSelection(i))
1300 return i;
1301 }
1302 return -1;
1303 },
1304
1305 getSelectableNodeGivenTableRowNode_: function(htmlNode) {
1306 switch (this.selectionMode_) {
1307 case SelectionMode.ROW:
1308 return htmlNode;
1309
1310 case SelectionMode.CELL:
1311 return Polymer.dom(htmlNode).children[this.selectedColumnIndex_];
1312
1313 default:
1314 throw new Error('Invalid selection mode ' + this.selectionMode_);
1315 }
1316 },
1317
1318 get selectedColumnIndex() {
1319 if (this.selectionMode_ !== SelectionMode.CELL)
1320 return undefined;
1321 return this.selectedColumnIndex_;
1322 },
1323
1324 set selectedColumnIndex(selectedColumnIndex) {
1325 this.rebuildIfNeeded_();
1326 if (this.selectionMode_ === SelectionMode.NONE)
1327 throw new Error('Selection is off.');
1328 if (selectedColumnIndex < 0 ||
1329 selectedColumnIndex >= this.tableColumns_.length)
1330 throw new Error('Invalid index');
1331 if (!this.doesColumnIndexSupportSelection(selectedColumnIndex))
1332 throw new Error('Selection is not supported on this column');
1333
1334 var e = this.prepareToChangeSelection_();
1335 this.selectedColumnIndex_ = selectedColumnIndex;
1336 if (this.selectedColumnIndex_ === undefined)
1337 this.selectedTableRowInfo_ = undefined;
1338 this.updateSelectedState_();
1339
1340 this.dispatchEvent(e);
1341 },
1342
1343 onKeyDown_: function(e) {
1344 if (this.selectionMode_ === SelectionMode.NONE)
1345 return;
1346 if (this.selectedTableRowInfo_ === undefined)
1347 return;
1348
1349 var code_to_command_names = {
1350 13: 'ENTER',
1351 37: 'ARROW_LEFT',
1352 38: 'ARROW_UP',
1353 39: 'ARROW_RIGHT',
1354 40: 'ARROW_DOWN'
1355 };
1356 var cmdName = code_to_command_names[e.keyCode];
1357 if (cmdName === undefined)
1358 return;
1359
1360 e.stopPropagation();
1361 e.preventDefault();
1362 this.performKeyCommand_(cmdName);
1363 },
1364
1365 performKeyCommand_: function(cmdName) {
1366 this.rebuildIfNeeded_();
1367
1368 var rowInfo = this.selectedTableRowInfo_;
1369 var htmlNode = rowInfo.htmlNode;
1370 if (cmdName === 'ARROW_UP') {
1371 var prev = htmlNode.previousElementSibling;
1372 if (prev) {
1373 tr.ui.b.scrollIntoViewIfNeeded(prev);
1374 this.selectedTableRow = prev.rowInfo.userRow;
1375 this.focusSelected_();
1376 return;
1377 }
1378 return;
1379 }
1380
1381 if (cmdName === 'ARROW_DOWN') {
1382 var next = htmlNode.nextElementSibling;
1383 if (next) {
1384 tr.ui.b.scrollIntoViewIfNeeded(next);
1385 this.selectedTableRow = next.rowInfo.userRow;
1386 this.focusSelected_();
1387 return;
1388 }
1389 return;
1390 }
1391
1392 if (cmdName === 'ARROW_RIGHT') {
1393 switch (this.selectionMode_) {
1394 case SelectionMode.ROW:
1395 if (rowInfo.userRow[this.subRowsPropertyName_] === undefined)
1396 return;
1397 if (rowInfo.userRow[this.subRowsPropertyName_].length === 0)
1398 return;
1399
1400 if (!rowInfo.isExpanded)
1401 this.setExpandedForTableRow(rowInfo.userRow, true);
1402 this.selectedTableRow =
1403 htmlNode.nextElementSibling.rowInfo.userRow;
1404 this.focusSelected_();
1405 return;
1406
1407 case SelectionMode.CELL:
1408 var newIndex = this.selectedColumnIndex_ + 1;
1409 if (newIndex >= this.tableColumns_.length)
1410 return;
1411 if (!this.doesColumnIndexSupportSelection(newIndex))
1412 return;
1413 this.selectedColumnIndex = newIndex;
1414 this.focusSelected_();
1415 return;
1416
1417 default:
1418 throw new Error('Invalid selection mode ' + this.selectionMode_);
1419 }
1420 }
1421
1422 if (cmdName === 'ARROW_LEFT') {
1423 switch (this.selectionMode_) {
1424 case SelectionMode.ROW:
1425 if (rowInfo.isExpanded) {
1426 this.setExpandedForTableRow(rowInfo.userRow, false);
1427 this.focusSelected_();
703 return; 1428 return;
704 } 1429 }
705 1430
706 function getTD(cur) { 1431 // Not expanded. Select parent...
707 if (cur === trElement) 1432 var parentRowInfo = rowInfo.parentRowInfo;
708 throw new Error('woah'); 1433 if (parentRowInfo) {
709 if (cur.parentElement === trElement) 1434 this.selectedTableRow = parentRowInfo.userRow;
710 return cur; 1435 this.focusSelected_();
711 return getTD(cur.parentElement); 1436 return;
712 } 1437 }
713 1438 return;
714 // If the row/cell can be selected and it's not selected yet, 1439
715 // select it. 1440 case SelectionMode.CELL:
716 if (isSelectable && this.selectionMode_ !== SelectionMode.NONE) { 1441 var newIndex = this.selectedColumnIndex_ - 1;
717 var shouldSelect = false; 1442 if (newIndex < 0)
718 var columnIndex = getTD(e.target).columnIndex; 1443 return;
719 switch (this.selectionMode_) { 1444 if (!this.doesColumnIndexSupportSelection(newIndex))
720 case SelectionMode.ROW: 1445 return;
721 shouldSelect = this.selectedTableRowInfo_ !== rowInfo; 1446 this.selectedColumnIndex = newIndex;
722 break; 1447 this.focusSelected_();
723 1448 return;
724 case SelectionMode.CELL: 1449
725 if (this.doesColumnIndexSupportSelection(columnIndex)) { 1450 default:
726 shouldSelect = this.selectedTableRowInfo_ !== rowInfo || 1451 throw new Error('Invalid selection mode ' + this.selectionMode_);
727 this.selectedColumnIndex_ !== columnIndex; 1452 }
728 } 1453 }
729 break; 1454
730 1455 if (cmdName === 'ENTER') {
731 default:
732 throw new Error('Invalid selection mode ' +
733 this.selectionMode_);
734 }
735 if (shouldSelect) {
736 this.didTableRowInfoGetClicked_(rowInfo, columnIndex);
737 return;
738 }
739 }
740
741 // Otherwise, if the row is expandable, expand/collapse it.
742 if (isExpandable) {
743 this.setExpandedForUserRow_(tableSection, rowInfoMap,
744 rowInfo.userRow, !rowInfo.isExpanded);
745 }
746 }.bind(this));
747 }
748
749 return rowInfo.htmlNode;
750 },
751
752 removeSubNodes_: function(tableSection, rowInfo, rowInfoMap) {
753 if (rowInfo.userRow[this.subRowsPropertyName_] === undefined) 1456 if (rowInfo.userRow[this.subRowsPropertyName_] === undefined)
754 return; 1457 return;
755 for (var i = 0; 1458 if (rowInfo.userRow[this.subRowsPropertyName_].length === 0)
756 i < rowInfo.userRow[this.subRowsPropertyName_].length; i++) {
757 var subRow = rowInfo.userRow[this.subRowsPropertyName_][i];
758 var subRowInfo = rowInfoMap.get(subRow);
759 if (!subRowInfo)
760 continue;
761
762 var subNode = subRowInfo.htmlNode;
763 if (subNode && Polymer.dom(subNode).parentNode === tableSection) {
764 Polymer.dom(tableSection).removeChild(subNode);
765 this.removeSubNodes_(tableSection, subRowInfo, rowInfoMap);
766 }
767 }
768 },
769
770 scheduleRebuildHeaders_: function() {
771 this.headerDirty_ = true;
772 this.scheduleRebuild_();
773 },
774
775 scheduleRebuildBody_: function() {
776 this.bodyDirty_ = true;
777 this.scheduleRebuild_();
778 },
779
780 scheduleRebuildFooter_: function() {
781 this.footerDirty_ = true;
782 this.scheduleRebuild_();
783 },
784
785 scheduleRebuild_: function() {
786 if (this.rebuildPending_)
787 return; 1459 return;
788 this.rebuildPending_ = true; 1460 this.setExpandedForTableRow(rowInfo.userRow, !rowInfo.isExpanded);
789 setTimeout(function() { 1461 this.focusSelected_();
790 this.rebuildPending_ = false; 1462 return;
791 this.rebuild(); 1463 }
792 }.bind(this), 0); 1464
793 }, 1465 throw new Error('Unrecognized command ' + cmdName);
794 1466 },
795 rebuildIfNeeded_: function() { 1467
796 this.rebuild(); 1468 focusSelected_: function() {
797 }, 1469 if (!this.selectedTableRowInfo_)
798 1470 return;
799 rebuild: function() { 1471 var node = this.getSelectableNodeGivenTableRowNode_(
800 var wasBodyOrHeaderDirty = this.headerDirty_ || this.bodyDirty_; 1472 this.selectedTableRowInfo_.htmlNode);
801 1473 node.focus();
802 if (this.headerDirty_) { 1474 },
803 this.generateHeaderColumns_(); 1475
804 this.headerDirty_ = false; 1476 dispatchSortingChangedEvent_: function() {
805 } 1477 var e = new tr.b.Event('sort-column-changed');
806 if (this.bodyDirty_) { 1478 e.sortColumnIndex = this.sortColumnIndex_;
807 Polymer.dom(this.$.body).textContent = ''; 1479 e.sortDescending = this.sortDescending_;
808 this.generateTableRowNodes_( 1480 this.dispatchEvent(e);
809 this.$.body, 1481 }
810 this.tableRows_, this.tableRowsInfo_, 0, 1482 });
811 undefined, undefined); 1483 })();
812 if (this.tableRows_.length === 0 && this.emptyValue_ !== undefined) { 1484 </script>
813 var trElement = this.ownerDocument.createElement('tr'); 1485
814 Polymer.dom(this.$.body).appendChild(trElement); 1486 <dom-module id="tr-ui-b-table-header-cell">
815 Polymer.dom(trElement).classList.add('empty-row');
816 var td = this.ownerDocument.createElement('td');
817 Polymer.dom(trElement).appendChild(td);
818 td.colSpan = this.tableColumns_.length;
819 var emptyValue = this.emptyValue_;
820 Polymer.dom(td).appendChild(
821 tr.ui.b.asHTMLOrTextNode(emptyValue, this.ownerDocument));
822 }
823 this.bodyDirty_ = false;
824 }
825
826 if (wasBodyOrHeaderDirty)
827 this.applySizes_();
828
829 if (this.footerDirty_) {
830 Polymer.dom(this.$.foot).textContent = '';
831 this.generateTableRowNodes_(
832 this.$.foot,
833 this.tableFooterRows_, this.tableFooterRowsInfo_, 0,
834 undefined, undefined);
835 if (this.tableFooterRowsInfo_.length) {
836 Polymer.dom(this.$.body).classList.add('has-footer');
837 } else {
838 Polymer.dom(this.$.body).classList.remove('has-footer');
839 }
840 this.footerDirty_ = false;
841 }
842 },
843
844 appendNewElement_: function(parent, tagName) {
845 var element = parent.ownerDocument.createElement(tagName);
846 Polymer.dom(parent).appendChild(element);
847 return element;
848 },
849
850 getExpandedForTableRow: function(userRow) {
851 this.rebuildIfNeeded_();
852 var rowInfo = this.tableRowsInfo_.get(userRow);
853 if (rowInfo === undefined)
854 throw new Error('Row has not been seen, must expand its parents');
855 return rowInfo.isExpanded;
856 },
857
858 getExpandedForUserRow_: function(userRow) {
859 if (userRow[this.subRowsPropertyName_] === undefined)
860 return false;
861 if (userRow[this.subRowsPropertyName_].length === 0)
862 return false;
863 if (userRow.isExpanded)
864 return true;
865 if (userRow.isExpanded === false)
866 return false;
867
868 var rowInfo = this.tableRowsInfo_.get(userRow);
869 if (rowInfo && rowInfo.isExpanded)
870 return true;
871
872 if (this.defaultExpansionStateCallback_ === undefined)
873 return false;
874
875 var parentUserRow = undefined;
876 if (rowInfo && rowInfo.parentRowInfo)
877 parentUserRow = rowInfo.parentRowInfo.userRow;
878
879 return this.defaultExpansionStateCallback_(
880 userRow, parentUserRow);
881 },
882
883 setExpandedForTableRow: function(userRow, expanded) {
884 this.rebuildIfNeeded_();
885 var rowInfo = this.tableRowsInfo_.get(userRow);
886 if (rowInfo === undefined)
887 throw new Error('Row has not been seen, must expand its parents');
888 return this.setExpandedForUserRow_(this.$.body, this.tableRowsInfo_,
889 userRow, expanded);
890 },
891
892 setExpandedForUserRow_: function(tableSection, rowInfoMap,
893 userRow, expanded) {
894 this.rebuildIfNeeded_();
895
896 var rowInfo = rowInfoMap.get(userRow);
897 if (rowInfo === undefined)
898 throw new Error('Row has not been seen, must expand its parents');
899
900 rowInfo.isExpanded = !!expanded;
901 // If no node, then nothing further needs doing.
902 if (rowInfo.htmlNode === undefined)
903 return;
904
905 // If its detached, then nothing needs doing.
906 if (rowInfo.htmlNode.parentElement !== tableSection)
907 return;
908
909 // Otherwise, rebuild.
910 var expandButton = Polymer.dom(rowInfo.htmlNode)
911 .querySelector('expand-button');
912 if (rowInfo.isExpanded) {
913 Polymer.dom(expandButton).classList.add('button-expanded');
914 var lastAddedRow = rowInfo.htmlNode;
915 if (rowInfo.userRow[this.subRowsPropertyName_]) {
916 this.generateTableRowNodes_(
917 tableSection,
918 rowInfo.userRow[this.subRowsPropertyName_], rowInfoMap,
919 rowInfo.indentation + 1,
920 lastAddedRow, rowInfo);
921 }
922 } else {
923 Polymer.dom(expandButton).classList.remove('button-expanded');
924 this.removeSubNodes_(tableSection, rowInfo, rowInfoMap);
925 }
926
927 this.maybeUpdateSelectedRow_();
928 },
929
930 get selectionMode() {
931 return this.selectionMode_;
932 },
933
934 set selectionMode(selectionMode) {
935 if (!tr.b.dictionaryContainsValue(SelectionMode, selectionMode))
936 throw new Error('Invalid selection mode ' + selectionMode);
937 this.rebuildIfNeeded_();
938 this.selectionMode_ = selectionMode;
939 this.didSelectionStateChange_();
940 },
941
942 get rowHighlightStyle() {
943 return this.rowHighlightStyle_;
944 },
945
946 set rowHighlightStyle(rowHighlightStyle) {
947 if (!tr.b.dictionaryContainsValue(HighlightStyle, rowHighlightStyle))
948 throw new Error('Invalid row highlight style ' + rowHighlightStyle);
949 this.rebuildIfNeeded_();
950 this.rowHighlightStyle_ = rowHighlightStyle;
951 this.didSelectionStateChange_();
952 },
953
954 get resolvedRowHighlightStyle() {
955 if (this.rowHighlightStyle_ !== HighlightStyle.DEFAULT)
956 return this.rowHighlightStyle_;
957 switch (this.selectionMode_) {
958 case SelectionMode.NONE:
959 return HighlightStyle.NONE;
960 case SelectionMode.ROW:
961 return HighlightStyle.DARK;
962 case SelectionMode.CELL:
963 return HighlightStyle.LIGHT;
964 default:
965 throw new Error('Invalid selection mode ' + selectionMode);
966 }
967 },
968
969 get cellHighlightStyle() {
970 return this.cellHighlightStyle_;
971 },
972
973 set cellHighlightStyle(cellHighlightStyle) {
974 if (!tr.b.dictionaryContainsValue(HighlightStyle, cellHighlightStyle))
975 throw new Error('Invalid cell highlight style ' + cellHighlightStyle);
976 this.rebuildIfNeeded_();
977 this.cellHighlightStyle_ = cellHighlightStyle;
978 this.didSelectionStateChange_();
979 },
980
981 get resolvedCellHighlightStyle() {
982 if (this.cellHighlightStyle_ !== HighlightStyle.DEFAULT)
983 return this.cellHighlightStyle_;
984 switch (this.selectionMode_) {
985 case SelectionMode.NONE:
986 case SelectionMode.ROW:
987 return HighlightStyle.NONE;
988 case SelectionMode.CELL:
989 return HighlightStyle.DARK;
990 default:
991 throw new Error('Invalid selection mode ' + selectionMode);
992 }
993 },
994
995 setHighlightStyle_: function(highlightAttribute, resolvedHighlightStyle) {
996 switch (resolvedHighlightStyle) {
997 case HighlightStyle.NONE:
998 Polymer.dom(this.$.body).removeAttribute(highlightAttribute);
999 break;
1000 case HighlightStyle.LIGHT:
1001 Polymer.dom(this.$.body).setAttribute(highlightAttribute, 'light');
1002 break;
1003 case HighlightStyle.DARK:
1004 Polymer.dom(this.$.body).setAttribute(highlightAttribute, 'dark');
1005 break;
1006 default:
1007 throw new Error('Invalid resolved highlight style ' +
1008 resolvedHighlightStyle);
1009 }
1010 },
1011
1012 didSelectionStateChange_: function() {
1013 this.setHighlightStyle_('row-highlight-style',
1014 this.resolvedRowHighlightStyle);
1015 this.setHighlightStyle_('cell-highlight-style',
1016 this.resolvedCellHighlightStyle);
1017
1018 for (var i = 0; i < this.$.body.children.length; i++)
1019 this.updateTabIndexForTableRowNode_(this.$.body.children[i]);
1020 this.maybeUpdateSelectedRow_();
1021 },
1022
1023 maybeUpdateSelectedRow_: function() {
1024 if (this.selectedTableRowInfo_ === undefined)
1025 return;
1026
1027 // Selection may be off.
1028 if (this.selectionMode_ === SelectionMode.NONE) {
1029 this.removeSelectedState_();
1030 this.selectedTableRowInfo_ = undefined;
1031 return;
1032 }
1033
1034 // selectedUserRow may not be visible
1035 function isVisible(rowInfo) {
1036 if (!rowInfo.htmlNode)
1037 return false;
1038 return !!rowInfo.htmlNode.parentElement;
1039 }
1040 if (isVisible(this.selectedTableRowInfo_)) {
1041 this.updateSelectedState_();
1042 return;
1043 }
1044
1045 this.removeSelectedState_();
1046 var curRowInfo = this.selectedTableRowInfo_;
1047 while (curRowInfo && !isVisible(curRowInfo))
1048 curRowInfo = curRowInfo.parentRowInfo;
1049
1050 this.selectedTableRowInfo_ = curRowInfo;
1051 if (this.selectedTableRowInfo_)
1052 this.updateSelectedState_();
1053 },
1054
1055 didTableRowInfoGetClicked_: function(rowInfo, columnIndex) {
1056 switch (this.selectionMode_) {
1057 case SelectionMode.NONE:
1058 return;
1059
1060 case SelectionMode.CELL:
1061 if (!this.doesColumnIndexSupportSelection(columnIndex))
1062 return;
1063 if (this.selectedColumnIndex !== columnIndex)
1064 this.selectedColumnIndex = columnIndex;
1065 // Fall through.
1066
1067 case SelectionMode.ROW:
1068 if (this.selectedTableRowInfo_ !== rowInfo)
1069 this.selectedTableRow = rowInfo.userRow;
1070 }
1071 },
1072
1073 get selectedTableRow() {
1074 if (!this.selectedTableRowInfo_)
1075 return undefined;
1076 return this.selectedTableRowInfo_.userRow;
1077 },
1078
1079 set selectedTableRow(userRow) {
1080 this.rebuildIfNeeded_();
1081 if (this.selectionMode_ === SelectionMode.NONE)
1082 throw new Error('Selection is off.');
1083
1084 var rowInfo;
1085 if (userRow === undefined) {
1086 rowInfo = undefined;
1087 } else {
1088 rowInfo = this.tableRowsInfo_.get(userRow);
1089 if (!rowInfo)
1090 throw new Error('Row has not been seen, must expand its parents.');
1091 }
1092
1093 var e = this.prepareToChangeSelection_();
1094 this.selectedTableRowInfo_ = rowInfo;
1095
1096 if (this.selectedTableRowInfo_ === undefined) {
1097 this.selectedColumnIndex_ = undefined;
1098 this.removeSelectedState_();
1099 } else {
1100 switch (this.selectionMode_) {
1101 case SelectionMode.ROW:
1102 this.selectedColumnIndex_ = undefined;
1103 break;
1104
1105 case SelectionMode.CELL:
1106 if (this.selectedColumnIndex_ === undefined) {
1107 var i = this.getFirstSelectableColumnIndex_();
1108 if (i == -1)
1109 throw new Error('Cannot find a selectable column.');
1110 this.selectedColumnIndex_ = i;
1111 }
1112 break;
1113
1114 default:
1115 throw new Error('Invalid selection mode ' + this.selectionMode_);
1116 }
1117 this.updateSelectedState_();
1118 }
1119
1120 this.dispatchEvent(e);
1121 },
1122
1123 updateTabIndexForTableRowNode_: function(row) {
1124 if (this.selectionMode_ === SelectionMode.ROW)
1125 row.tabIndex = 0;
1126 else
1127 Polymer.dom(row).removeAttribute('tabIndex');
1128
1129 var enableCellTab = this.selectionMode_ === SelectionMode.CELL;
1130 for (var i = 0; i < this.tableColumns_.length; i++) {
1131 var cell = row.children[i];
1132 if (enableCellTab && this.doesColumnIndexSupportSelection(i))
1133 cell.tabIndex = 0;
1134 else
1135 Polymer.dom(cell).removeAttribute('tabIndex');
1136 }
1137 },
1138
1139 prepareToChangeSelection_: function() {
1140 var e = new tr.b.Event('selection-changed');
1141 var previousSelectedRowInfo = this.selectedTableRowInfo_;
1142 if (previousSelectedRowInfo)
1143 e.previousSelectedTableRow = previousSelectedRowInfo.userRow;
1144 else
1145 e.previousSelectedTableRow = undefined;
1146
1147 this.removeSelectedState_();
1148
1149 return e;
1150 },
1151
1152 removeSelectedState_: function() {
1153 this.setSelectedState_(false);
1154 },
1155
1156 updateSelectedState_: function() {
1157 this.setSelectedState_(true);
1158 },
1159
1160 setSelectedState_: function(select) {
1161 if (this.selectedTableRowInfo_ === undefined)
1162 return;
1163
1164 // Row selection.
1165 var rowNode = this.selectedTableRowInfo_.htmlNode;
1166 if (select)
1167 Polymer.dom(rowNode).setAttribute('selected', true);
1168 else
1169 Polymer.dom(rowNode).removeAttribute('selected');
1170
1171 // Cell selection (if applicable).
1172 var cellNode = rowNode.children[this.selectedColumnIndex_];
1173 if (!cellNode)
1174 return;
1175 if (select)
1176 Polymer.dom(cellNode).setAttribute('selected', true);
1177 else
1178 Polymer.dom(cellNode).removeAttribute('selected');
1179 },
1180
1181 doesColumnIndexSupportSelection: function(columnIndex) {
1182 var columnInfo = this.tableColumns_[columnIndex];
1183 var scs = columnInfo.supportsCellSelection;
1184 if (scs === false)
1185 return false;
1186 return true;
1187 },
1188
1189 getFirstSelectableColumnIndex_: function() {
1190 for (var i = 0; i < this.tableColumns_.length; i++) {
1191 if (this.doesColumnIndexSupportSelection(i))
1192 return i;
1193 }
1194 return -1;
1195 },
1196
1197 getSelectableNodeGivenTableRowNode_: function(htmlNode) {
1198 switch (this.selectionMode_) {
1199 case SelectionMode.ROW:
1200 return htmlNode;
1201
1202 case SelectionMode.CELL:
1203 return htmlNode.children[this.selectedColumnIndex_];
1204
1205 default:
1206 throw new Error('Invalid selection mode ' + this.selectionMode_);
1207 }
1208 },
1209
1210 get selectedColumnIndex() {
1211 if (this.selectionMode_ !== SelectionMode.CELL)
1212 return undefined;
1213 return this.selectedColumnIndex_;
1214 },
1215
1216 set selectedColumnIndex(selectedColumnIndex) {
1217 this.rebuildIfNeeded_();
1218 if (this.selectionMode_ === SelectionMode.NONE)
1219 throw new Error('Selection is off.');
1220 if (selectedColumnIndex < 0 ||
1221 selectedColumnIndex >= this.tableColumns_.length)
1222 throw new Error('Invalid index');
1223 if (!this.doesColumnIndexSupportSelection(selectedColumnIndex))
1224 throw new Error('Selection is not supported on this column');
1225
1226 var e = this.prepareToChangeSelection_();
1227 this.selectedColumnIndex_ = selectedColumnIndex;
1228 if (this.selectedColumnIndex_ === undefined)
1229 this.selectedTableRowInfo_ = undefined;
1230 this.updateSelectedState_();
1231
1232 this.dispatchEvent(e);
1233 },
1234
1235 onKeyDown_: function(e) {
1236 if (this.selectionMode_ === SelectionMode.NONE)
1237 return;
1238 if (this.selectedTableRowInfo_ === undefined)
1239 return;
1240
1241 var code_to_command_names = {
1242 13: 'ENTER',
1243 37: 'ARROW_LEFT',
1244 38: 'ARROW_UP',
1245 39: 'ARROW_RIGHT',
1246 40: 'ARROW_DOWN'
1247 };
1248 var cmdName = code_to_command_names[e.keyCode];
1249 if (cmdName === undefined)
1250 return;
1251
1252 e.stopPropagation();
1253 e.preventDefault();
1254 this.performKeyCommand_(cmdName);
1255 },
1256
1257 performKeyCommand_: function(cmdName) {
1258 this.rebuildIfNeeded_();
1259
1260 var rowInfo = this.selectedTableRowInfo_;
1261 var htmlNode = rowInfo.htmlNode;
1262 if (cmdName === 'ARROW_UP') {
1263 var prev = htmlNode.previousElementSibling;
1264 if (prev) {
1265 tr.ui.b.scrollIntoViewIfNeeded(prev);
1266 this.selectedTableRow = prev.rowInfo.userRow;
1267 this.focusSelected_();
1268 return;
1269 }
1270 return;
1271 }
1272
1273 if (cmdName === 'ARROW_DOWN') {
1274 var next = htmlNode.nextElementSibling;
1275 if (next) {
1276 tr.ui.b.scrollIntoViewIfNeeded(next);
1277 this.selectedTableRow = next.rowInfo.userRow;
1278 this.focusSelected_();
1279 return;
1280 }
1281 return;
1282 }
1283
1284 if (cmdName === 'ARROW_RIGHT') {
1285 switch (this.selectionMode_) {
1286 case SelectionMode.ROW:
1287 if (rowInfo.userRow[this.subRowsPropertyName_] === undefined)
1288 return;
1289 if (rowInfo.userRow[this.subRowsPropertyName_].length === 0)
1290 return;
1291
1292 if (!rowInfo.isExpanded)
1293 this.setExpandedForTableRow(rowInfo.userRow, true);
1294 this.selectedTableRow =
1295 Polymer.dom(htmlNode).nextElementSibling.rowInfo.userRow;
1296 this.focusSelected_();
1297 return;
1298
1299 case SelectionMode.CELL:
1300 var newIndex = this.selectedColumnIndex_ + 1;
1301 if (newIndex >= this.tableColumns_.length)
1302 return;
1303 if (!this.doesColumnIndexSupportSelection(newIndex))
1304 return;
1305 this.selectedColumnIndex = newIndex;
1306 this.focusSelected_();
1307 return;
1308
1309 default:
1310 throw new Error('Invalid selection mode ' + this.selectionMode_);
1311 }
1312 }
1313
1314 if (cmdName === 'ARROW_LEFT') {
1315 switch (this.selectionMode_) {
1316 case SelectionMode.ROW:
1317 if (rowInfo.isExpanded) {
1318 this.setExpandedForTableRow(rowInfo.userRow, false);
1319 this.focusSelected_();
1320 return;
1321 }
1322
1323 // Not expanded. Select parent...
1324 var parentRowInfo = rowInfo.parentRowInfo;
1325 if (parentRowInfo) {
1326 this.selectedTableRow = parentRowInfo.userRow;
1327 this.focusSelected_();
1328 return;
1329 }
1330 return;
1331
1332 case SelectionMode.CELL:
1333 var newIndex = this.selectedColumnIndex_ - 1;
1334 if (newIndex < 0)
1335 return;
1336 if (!this.doesColumnIndexSupportSelection(newIndex))
1337 return;
1338 this.selectedColumnIndex = newIndex;
1339 this.focusSelected_();
1340 return;
1341
1342 default:
1343 throw new Error('Invalid selection mode ' + this.selectionMode_);
1344 }
1345 }
1346
1347 if (cmdName === 'ENTER') {
1348 if (rowInfo.userRow[this.subRowsPropertyName_] === undefined)
1349 return;
1350 if (rowInfo.userRow[this.subRowsPropertyName_].length === 0)
1351 return;
1352 this.setExpandedForTableRow(rowInfo.userRow, !rowInfo.isExpanded);
1353 this.focusSelected_();
1354 return;
1355 }
1356
1357 throw new Error('Unrecognized command ' + cmdName);
1358 },
1359
1360 focusSelected_: function() {
1361 if (!this.selectedTableRowInfo_)
1362 return;
1363 var node = this.getSelectableNodeGivenTableRowNode_(
1364 this.selectedTableRowInfo_.htmlNode);
1365 node.focus();
1366 },
1367
1368 dispatchSortingChangedEvent_: function() {
1369 var e = new tr.b.Event('sort-column-changed');
1370 e.sortColumnIndex = this.sortColumnIndex_;
1371 e.sortDescending = this.sortDescending_;
1372 this.dispatchEvent(e);
1373 }
1374 });
1375 })();
1376 </script>
1377
1378 <dom-module id='tr-ui-b-table-header-cell'>
1379 <template> 1487 <template>
1380 <style> 1488 <style>
1381 :host { 1489 :host {
1382 -webkit-user-select: none; 1490 -webkit-user-select: none;
1383 display: flex; 1491 display: flex;
1384 } 1492 }
1385 1493
1386 span { 1494 span {
1387 flex: 0 1 auto; 1495 flex: 0 1 auto;
1388 } 1496 }
1389 1497
1390 side-element { 1498 #side {
1391 -webkit-user-select: none; 1499 -webkit-user-select: none;
1392 flex: 0 0 auto; 1500 flex: 0 0 auto;
1393 padding-left: 4px; 1501 padding-left: 2px;
1502 padding-right: 2px;
1394 vertical-align: top; 1503 vertical-align: top;
1395 font-size: 15px; 1504 font-size: 15px;
1396 font-family: sans-serif; 1505 font-family: sans-serif;
1397 display: inline; 1506 display: inline;
1398 line-height: 85%; 1507 line-height: 85%;
1508 margin-left: 5px;
1509 }
1510
1511 #button {
1512 font-weight: bold;
1513 font-size: 12px;
1514 }
1515
1516 #title:empty, #button:empty, #side:empty {
1517 display: none;
1518 }
1519
1520 #button[selected] {
1521 background: darkgrey;
1399 } 1522 }
1400 </style> 1523 </style>
1401 1524
1402 <span id="title"></span><side-element id="side"></side-element> 1525 <span id="title"></span>
1526 <button id="button"></button>
1527 <button id="side"></button>
1403 </template> 1528 </template>
1404 </dom-module> 1529 </dom-module>
1405
1406 <script> 1530 <script>
1407 'use strict'; 1531 'use strict';
1408 1532
1409 var ColumnAlignment = tr.ui.b.TableFormat.ColumnAlignment; 1533 var ColumnAlignment = tr.ui.b.TableFormat.ColumnAlignment;
1410 1534
1411 Polymer({ 1535 Polymer({
1412 is: 'tr-ui-b-table-header-cell', 1536 is: 'tr-ui-b-table-header-cell',
1413 1537
1414 listeners: {
1415 'tap': 'onTap_'
1416 },
1417
1418 created: function() { 1538 created: function() {
1419 this.tapCallback_ = undefined; 1539 this.tapCallback_ = undefined;
1420 this.cellTitle_ = ''; 1540 this.cellTitle_ = '';
1421 this.align_ = undefined; 1541 this.align_ = undefined;
1542 this.selectable_ = false;
1543 this.column_ = undefined;
1544 },
1545
1546 ready: function() {
1547 this.$.button.addEventListener('click', this.onSelect_.bind(this));
1548 this.$.side.addEventListener('click', this.onTap_.bind(this));
1549 },
1550
1551 onSelect_: function() {
1552 this.selected = this.$.button.getAttribute('selected') !== 'true';
1553 var e = new tr.b.Event('selected-column-changed');
1554 e.column = this.column;
1555 e.selected = this.selected;
1556 this.dispatchEvent(e);
1557 },
1558
1559 set column(column) {
1560 this.column_ = column;
1561 this.selectable = column.selectable;
1562 this.align = column.align;
1563 this.cellTitle = column.title;
1564 },
1565
1566 get column() {
1567 return this.column_;
1568 },
1569
1570 set selectable(selectable) {
1571 this.selectable_ = selectable;
1572 this.cellTitle = this.$.title.childNodes[0] ||
1573 this.$.button.childNodes[0];
1574 },
1575
1576 get selectable() {
1577 return this.selectable_;
1578 },
1579
1580 set selected(selected) {
1581 if (selected)
1582 this.$.button.setAttribute('selected', true);
1583 else
1584 this.$.button.removeAttribute('selected');
1585 },
1586
1587 get selected() {
1588 if (!this.selectable_)
1589 return false;
1590 return this.$.button.getAttribute('selected') === 'true';
1422 }, 1591 },
1423 1592
1424 set cellTitle(value) { 1593 set cellTitle(value) {
1425 this.cellTitle_ = value; 1594 this.cellTitle_ = value;
1426 1595
1427 var titleNode = tr.ui.b.asHTMLOrTextNode( 1596 var titleNode = tr.ui.b.asHTMLOrTextNode(
1428 this.cellTitle_, this.ownerDocument); 1597 this.cellTitle_, this.ownerDocument);
1429 1598
1430 this.$.title.innerText = ''; 1599 this.$.title.innerText = '';
1431 Polymer.dom(this.$.title).appendChild(titleNode); 1600 this.$.button.innerText = '';
1601
1602 if (this.selectable_)
1603 this.$.button.appendChild(titleNode);
1604 else
1605 this.$.title.appendChild(titleNode);
1432 }, 1606 },
1433 1607
1434 get cellTitle() { 1608 get cellTitle() {
1435 return this.cellTitle_; 1609 return this.cellTitle_;
1436 }, 1610 },
1437 1611
1438 set align(align) { 1612 set align(align) {
1439 switch (align) { 1613 switch (align) {
1440 case undefined: 1614 case undefined:
1441 case ColumnAlignment.LEFT: 1615 case ColumnAlignment.LEFT:
1442 this.style.justifyContent = ''; 1616 this.style.justifyContent = '';
1443 break; 1617 break;
1444 1618
1445 case ColumnAlignment.RIGHT: 1619 case ColumnAlignment.RIGHT:
1446 this.style.justifyContent = 'flex-end'; 1620 this.style.justifyContent = 'flex-end';
1447 break; 1621 break;
1448 1622
1449 default: 1623 default:
1450 throw new Error('Invalid alignment of column (title=\'' + 1624 throw new Error('Invalid alignment of column (title=\'' +
1451 this.cellTitle_ + '\'): ' + align); 1625 this.cellTitle_ + '\'): ' + align);
1452 } 1626 }
1453 this.align_ = align; 1627 this.align_ = align;
1454 }, 1628 },
1455 1629
1456 get align() { 1630 get align() {
1457 return this.align_; 1631 return this.align_;
1458 }, 1632 },
1459 1633
1460 clearSideContent: function() { 1634 clearSideContent: function() {
1461 Polymer.dom(this.$.side).textContent = ''; 1635 this.$.side.textContent = '';
1462 }, 1636 },
1463 1637
1464 set sideContent(content) { 1638 set sideContent(content) {
1465 Polymer.dom(this.$.side).textContent = content; 1639 this.$.side.textContent = content;
1640 this.$.side.style.display = content ? 'inline' : 'none';
1466 }, 1641 },
1467 1642
1468 get sideContent() { 1643 get sideContent() {
1469 return Polymer.dom(this.$.side).textContent; 1644 return this.$.side.textContent;
1470 }, 1645 },
1471 1646
1472 set tapCallback(callback) { 1647 set tapCallback(callback) {
1473 this.style.cursor = 'pointer'; 1648 this.style.cursor = 'pointer';
1474 this.tapCallback_ = callback; 1649 this.tapCallback_ = callback;
1475 }, 1650 },
1476 1651
1477 get tapCallback() { 1652 get tapCallback() {
1478 return this.tapCallback_; 1653 return this.tapCallback_;
1479 }, 1654 },
1480 1655
1481 onTap_: function() { 1656 onTap_: function() {
1482 if (this.tapCallback_) 1657 if (this.tapCallback_)
1483 this.tapCallback_(); 1658 this.tapCallback_();
1484 } 1659 }
1485 }); 1660 });
1486 </script> 1661 </script>
OLDNEW
« no previous file with comments | « tracing/tracing/ui/base/tab_view.html ('k') | tracing/tracing/ui/base/table_test.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698