Index: tracing/tracing/ui/base/table.html |
diff --git a/tracing/tracing/ui/base/table.html b/tracing/tracing/ui/base/table.html |
index a610892ab3e91714af47663ba7a00f0b39a65b68..64693c27701db597d6a0b9082eec561e4abe5f4e 100644 |
--- a/tracing/tracing/ui/base/table.html |
+++ b/tracing/tracing/ui/base/table.html |
@@ -57,7 +57,7 @@ tr.exportTo('tr.ui.b', function() { |
}); |
</script> |
-<dom-module id='tr-ui-b-table'> |
+<dom-module id="tr-ui-b-table"> |
<template> |
<style> |
:host { |
@@ -78,7 +78,7 @@ tr.exportTo('tr.ui.b', function() { |
tr > td { |
padding: 2px 4px 2px 4px; |
- vertical-align: text-top; |
+ vertical-align: top; |
} |
tr:focus, |
@@ -153,6 +153,10 @@ tr.exportTo('tr.ui.b', function() { |
background-color: rgb(171, 217, 202); /* semi-light turquoise */ |
} |
+ table > colgroup > col[selected] { |
+ background-color: #e6e6e6; /* grey */ |
+ } |
+ |
table > tbody > tr.empty-row > td { |
color: #666; |
font-style: italic; |
@@ -181,6 +185,8 @@ tr.exportTo('tr.ui.b', function() { |
} |
</style> |
<table> |
+ <colgroup id="cols"> |
+ </colgroup> |
<thead id="head"> |
</thead> |
<tbody id="body"> |
@@ -190,1192 +196,1294 @@ tr.exportTo('tr.ui.b', function() { |
</table> |
</template> |
</dom-module> |
- <script> |
- 'use strict'; |
- (function() { |
- var RIGHT_ARROW = String.fromCharCode(0x25b6); |
- var UNSORTED_ARROW = String.fromCharCode(0x25BF); |
- var ASCENDING_ARROW = String.fromCharCode(0x25B4); |
- var DESCENDING_ARROW = String.fromCharCode(0x25BE); |
- var BASIC_INDENTATION = 8; |
- |
- var SelectionMode = tr.ui.b.TableFormat.SelectionMode; |
- var HighlightStyle = tr.ui.b.TableFormat.HighlightStyle; |
- var ColumnAlignment = tr.ui.b.TableFormat.ColumnAlignment; |
- |
- Polymer({ |
- is: 'tr-ui-b-table', |
- |
- created: function() { |
- this.selectionMode_ = SelectionMode.NONE; |
- this.rowHighlightStyle_ = HighlightStyle.DEFAULT; |
- this.cellHighlightStyle_ = HighlightStyle.DEFAULT; |
- this.selectedTableRowInfo_ = undefined; |
- this.selectedColumnIndex_ = undefined; |
+<script> |
+'use strict'; |
+(function() { |
+ var RIGHT_ARROW = String.fromCharCode(0x25b6); |
+ var UNSORTED_ARROW = String.fromCharCode(0x25BF); |
+ var ASCENDING_ARROW = String.fromCharCode(0x25B4); |
+ var DESCENDING_ARROW = String.fromCharCode(0x25BE); |
+ var BASIC_INDENTATION = 8; |
+ |
+ var SelectionMode = tr.ui.b.TableFormat.SelectionMode; |
+ var HighlightStyle = tr.ui.b.TableFormat.HighlightStyle; |
+ var ColumnAlignment = tr.ui.b.TableFormat.ColumnAlignment; |
+ |
+ Polymer({ |
+ is: 'tr-ui-b-table', |
+ |
+ created: function() { |
+ this.selectionMode_ = SelectionMode.NONE; |
+ this.rowHighlightStyle_ = HighlightStyle.DEFAULT; |
+ this.cellHighlightStyle_ = HighlightStyle.DEFAULT; |
+ this.selectedTableRowInfo_ = undefined; |
+ this.selectedColumnIndex_ = undefined; |
+ |
+ this.tableColumns_ = []; |
+ this.tableRows_ = []; |
+ this.tableRowsInfo_ = new WeakMap(); |
+ this.tableFooterRows_ = []; |
+ this.tableFooterRowsInfo_ = new WeakMap(); |
+ this.sortColumnIndex_ = undefined; |
+ this.sortDescending_ = false; |
+ this.columnsWithExpandButtons_ = []; |
+ this.headerCells_ = []; |
+ this.showHeader_ = true; |
+ this.emptyValue_ = undefined; |
+ this.subRowsPropertyName_ = 'subRows'; |
+ this.customizeTableRowCallback_ = undefined; |
+ }, |
+ |
+ ready: function() { |
+ this.$.body.addEventListener( |
+ 'keydown', this.onKeyDown_.bind(this), true); |
+ }, |
+ |
+ clear: function() { |
+ this.selectionMode_ = SelectionMode.NONE; |
+ this.rowHighlightStyle_ = HighlightStyle.DEFAULT; |
+ this.cellHighlightStyle_ = HighlightStyle.DEFAULT; |
+ this.selectedTableRowInfo_ = undefined; |
+ this.selectedColumnIndex_ = undefined; |
+ |
+ Polymer.dom(this).textContent = ''; |
+ this.tableColumns_ = []; |
+ this.tableRows_ = []; |
+ this.tableRowsInfo_ = new WeakMap(); |
+ this.tableFooterRows_ = []; |
+ this.tableFooterRowsInfo_ = new WeakMap(); |
+ this.sortColumnIndex_ = undefined; |
+ this.sortDescending_ = false; |
+ this.columnsWithExpandButtons_ = []; |
+ this.headerCells_ = []; |
+ this.subRowsPropertyName_ = 'subRows'; |
+ this.defaultExpansionStateCallback_ = undefined; |
+ }, |
+ |
+ get showHeader() { |
+ return this.showHeader_; |
+ }, |
+ |
+ set showHeader(showHeader) { |
+ this.showHeader_ = showHeader; |
+ this.scheduleRebuildHeaders_(); |
+ }, |
+ |
+ set subRowsPropertyName(name) { |
+ this.subRowsPropertyName_ = name; |
+ }, |
+ |
+ /** |
+ * This callback will be called whenever a body row is built |
+ * for a userRow that has subRows and does not have an explicit |
+ * isExpanded field. |
+ * The callback should return true if the row should be expanded, |
+ * or false if the row should be collapsed. |
+ * @param {function(userRow, parentUserRow): boolean} cb The callback. |
+ */ |
+ set defaultExpansionStateCallback(cb) { |
+ this.defaultExpansionStateCallback_ = cb; |
+ this.scheduleRebuildBody_(); |
+ }, |
+ |
+ /** |
+ * This callback will be called whenever a body row is built. |
+ * The callback's return value is ignored. |
+ * @param {function(userRow, trElement)} cb The callback. |
+ */ |
+ set customizeTableRowCallback(cb) { |
+ this.customizeTableRowCallback_ = cb; |
+ this.scheduleRebuildBody_(); |
+ }, |
+ |
+ get emptyValue() { |
+ return this.emptyValue_; |
+ }, |
+ |
+ set emptyValue(emptyValue) { |
+ var previousEmptyValue = this.emptyValue_; |
+ this.emptyValue_ = emptyValue; |
+ if (this.tableRows_.length === 0 && emptyValue !== previousEmptyValue) |
+ this.scheduleRebuildBody_(); |
+ }, |
+ |
+ /** |
+ * Data objects should have the following fields: |
+ * mandatory: title, value |
+ * optional: width {string}, cmp {function}, colSpan {number}, |
+ * showExpandButtons {boolean}, |
+ * align {tr.ui.b.TableFormat.ColumnAlignment} |
+ * |
+ * @param {Array} columns An array of data objects. |
+ */ |
+ set tableColumns(columns) { |
+ // Figure out the columns with expand buttons... |
+ var columnsWithExpandButtons = []; |
+ for (var i = 0; i < columns.length; i++) { |
+ if (columns[i].showExpandButtons) |
+ columnsWithExpandButtons.push(i); |
+ } |
+ if (columnsWithExpandButtons.length === 0) { |
+ // First column if none have specified. |
+ columnsWithExpandButtons = [0]; |
+ } |
- this.tableColumns_ = []; |
- this.tableRows_ = []; |
- this.tableRowsInfo_ = new WeakMap(); |
- this.tableFooterRows_ = []; |
- this.tableFooterRowsInfo_ = new WeakMap(); |
- this.sortColumnIndex_ = undefined; |
- this.sortDescending_ = false; |
- this.columnsWithExpandButtons_ = []; |
- this.headerCells_ = []; |
- this.showHeader_ = true; |
- this.emptyValue_ = undefined; |
- this.subRowsPropertyName_ = 'subRows'; |
- this.customizeTableRowCallback_ = undefined; |
- }, |
- |
- ready: function() { |
- this.$.body.addEventListener( |
- 'keydown', this.onKeyDown_.bind(this), true); |
- }, |
- |
- clear: function() { |
- this.selectionMode_ = SelectionMode.NONE; |
- this.rowHighlightStyle_ = HighlightStyle.DEFAULT; |
- this.cellHighlightStyle_ = HighlightStyle.DEFAULT; |
- this.selectedTableRowInfo_ = undefined; |
- this.selectedColumnIndex_ = undefined; |
+ // Sanity check columns. |
+ for (var i = 0; i < columns.length; i++) { |
+ var colInfo = columns[i]; |
+ if (colInfo.width === undefined) |
+ continue; |
- Polymer.dom(this).textContent = ''; |
- this.tableColumns_ = []; |
- this.tableRows_ = []; |
- this.tableRowsInfo_ = new WeakMap(); |
- this.tableFooterRows_ = []; |
- this.tableFooterRowsInfo_ = new WeakMap(); |
- this.sortColumnIndex_ = undefined; |
- this.sortDescending_ = false; |
- this.columnsWithExpandButtons_ = []; |
- this.headerCells_ = []; |
- this.subRowsPropertyName_ = 'subRows'; |
- this.defaultExpansionStateCallback_ = undefined; |
- }, |
- |
- get showHeader() { |
- return this.showHeader_; |
- }, |
- |
- set showHeader(showHeader) { |
- this.showHeader_ = showHeader; |
- this.scheduleRebuildHeaders_(); |
- }, |
- |
- set subRowsPropertyName(name) { |
- this.subRowsPropertyName_ = name; |
- }, |
- |
- /** |
- * This callback will be called whenever a body row is built |
- * for a userRow that has subRows and does not have an explicit |
- * isExpanded field. |
- * The callback should return true if the row should be expanded, |
- * or false if the row should be collapsed. |
- * @param {function(userRow, parentUserRow): boolean} cb The callback. |
- */ |
- set defaultExpansionStateCallback(cb) { |
- this.defaultExpansionStateCallback_ = cb; |
- this.scheduleRebuildBody_(); |
- }, |
- |
- /** |
- * This callback will be called whenever a body row is built. |
- * The callback's return value is ignored. |
- * @param {function(userRow, trElement)} cb The callback. |
- */ |
- set customizeTableRowCallback(cb) { |
- this.customizeTableRowCallback_ = cb; |
- this.scheduleRebuildBody_(); |
- }, |
- |
- get emptyValue() { |
- return this.emptyValue_; |
- }, |
- |
- set emptyValue(emptyValue) { |
- var previousEmptyValue = this.emptyValue_; |
- this.emptyValue_ = emptyValue; |
- if (this.tableRows_.length === 0 && emptyValue !== previousEmptyValue) |
- this.scheduleRebuildBody_(); |
- }, |
- |
- /** |
- * Data objects should have the following fields: |
- * mandatory: title, value |
- * optional: width {string}, cmp {function}, colSpan {number}, |
- * showExpandButtons {boolean}, |
- * align {tr.ui.b.TableFormat.ColumnAlignment} |
- * |
- * @param {Array} columns An array of data objects. |
- */ |
- set tableColumns(columns) { |
- // Figure out the columns with expand buttons... |
- var columnsWithExpandButtons = []; |
- for (var i = 0; i < columns.length; i++) { |
- if (columns[i].showExpandButtons) |
- columnsWithExpandButtons.push(i); |
- } |
- if (columnsWithExpandButtons.length === 0) { |
- // First column if none have specified. |
- columnsWithExpandButtons = [0]; |
- } |
+ var hasExpandButton = columnsWithExpandButtons.indexOf(i) !== -1; |
- // Sanity check columns. |
- for (var i = 0; i < columns.length; i++) { |
- var colInfo = columns[i]; |
- if (colInfo.width === undefined) |
+ var w = colInfo.width; |
+ if (w) { |
+ if (/\d+px/.test(w)) { |
continue; |
- |
- var hasExpandButton = columnsWithExpandButtons.indexOf(i) !== -1; |
- |
- var w = colInfo.width; |
- if (w) { |
- if (/\d+px/.test(w)) { |
- continue; |
- } else if (/\d+%/.test(w)) { |
- if (hasExpandButton) { |
- throw new Error('Columns cannot be %-sized and host ' + |
- ' an expand button'); |
- } |
- } else { |
- throw new Error('Unrecognized width string'); |
+ } else if (/\d+%/.test(w)) { |
+ if (hasExpandButton) { |
+ throw new Error('Columns cannot be %-sized and host ' + |
+ ' an expand button'); |
} |
+ } else { |
+ throw new Error('Unrecognized width string'); |
} |
} |
+ } |
- // Commit the change. |
- this.tableColumns_ = columns; |
- this.headerCells_ = []; |
- this.columnsWithExpandButtons_ = columnsWithExpandButtons; |
- this.sortColumnIndex = undefined; |
- this.scheduleRebuildHeaders_(); |
- |
- // Blow away the table rows, too. |
- this.tableRows = this.tableRows_; |
- }, |
- |
- get tableColumns() { |
- return this.tableColumns_; |
- }, |
- |
- /** |
- * @param {Array} rows An array of 'row' objects with the following |
- * fields: |
- * optional: subRows An array of objects that have the same 'row' |
- * structure. Set subRowsPropertyName to use an |
- * alternative field name. |
- */ |
- set tableRows(rows) { |
- this.selectedTableRowInfo_ = undefined; |
- this.selectedColumnIndex_ = undefined; |
- this.maybeUpdateSelectedRow_(); |
- this.tableRows_ = rows; |
- this.tableRowsInfo_ = new WeakMap(); |
- this.scheduleRebuildBody_(); |
- }, |
+ // Commit the change. |
+ this.tableColumns_ = columns; |
+ this.headerCells_ = []; |
+ this.columnsWithExpandButtons_ = columnsWithExpandButtons; |
+ this.sortColumnIndex = undefined; |
+ this.scheduleRebuildHeaders_(); |
+ |
+ // Blow away the table rows, too. |
+ this.tableRows = this.tableRows_; |
+ }, |
+ |
+ get tableColumns() { |
+ return this.tableColumns_; |
+ }, |
+ |
+ /** |
+ * @param {Array} rows An array of 'row' objects with the following |
+ * fields: |
+ * optional: subRows An array of objects that have the same 'row' |
+ * structure. Set subRowsPropertyName to use an |
+ * alternative field name. |
+ */ |
+ set tableRows(rows) { |
+ this.selectedTableRowInfo_ = undefined; |
+ this.selectedColumnIndex_ = undefined; |
+ this.maybeUpdateSelectedRow_(); |
+ this.tableRows_ = rows; |
+ this.tableRowsInfo_ = new WeakMap(); |
+ this.scheduleRebuildBody_(); |
+ }, |
+ |
+ get tableRows() { |
+ return this.tableRows_; |
+ }, |
+ |
+ set footerRows(rows) { |
+ this.tableFooterRows_ = rows; |
+ this.tableFooterRowsInfo_ = new WeakMap(); |
+ this.scheduleRebuildFooter_(); |
+ }, |
+ |
+ get footerRows() { |
+ return this.tableFooterRows_; |
+ }, |
+ |
+ set sortColumnIndex(number) { |
+ if (number === this.sortColumnIndex_) |
+ return; |
+ |
+ if (number === undefined) { |
+ this.sortColumnIndex_ = undefined; |
+ this.updateHeaderArrows_(); |
+ this.dispatchSortingChangedEvent_(); |
+ return; |
+ } |
- get tableRows() { |
- return this.tableRows_; |
- }, |
+ if (this.tableColumns_.length <= number) |
+ throw new Error('Column number ' + number + ' is out of bounds.'); |
+ if (!this.tableColumns_[number].cmp) |
+ throw new Error('Column ' + number + ' does not have a comparator.'); |
- set footerRows(rows) { |
- this.tableFooterRows_ = rows; |
- this.tableFooterRowsInfo_ = new WeakMap(); |
- this.scheduleRebuildFooter_(); |
- }, |
+ this.sortColumnIndex_ = number; |
+ this.updateHeaderArrows_(); |
+ this.scheduleRebuildBody_(); |
+ this.dispatchSortingChangedEvent_(); |
+ }, |
- get footerRows() { |
- return this.tableFooterRows_; |
- }, |
+ get sortColumnIndex() { |
+ return this.sortColumnIndex_; |
+ }, |
- set sortColumnIndex(number) { |
- if (number === this.sortColumnIndex_) |
- return; |
+ set sortDescending(value) { |
+ var newValue = !!value; |
- if (number === undefined) { |
- this.sortColumnIndex_ = undefined; |
- this.updateHeaderArrows_(); |
- this.dispatchSortingChangedEvent_(); |
- return; |
- } |
- |
- if (this.tableColumns_.length <= number) |
- throw new Error('Column number ' + number + ' is out of bounds.'); |
- if (!this.tableColumns_[number].cmp) |
- throw new Error('Column ' + number + ' does not have a comparator.'); |
- |
- this.sortColumnIndex_ = number; |
+ if (newValue !== this.sortDescending_) { |
+ this.sortDescending_ = newValue; |
this.updateHeaderArrows_(); |
this.scheduleRebuildBody_(); |
this.dispatchSortingChangedEvent_(); |
- }, |
- |
- get sortColumnIndex() { |
- return this.sortColumnIndex_; |
- }, |
+ } |
+ }, |
- set sortDescending(value) { |
- var newValue = !!value; |
+ get sortDescending() { |
+ return this.sortDescending_; |
+ }, |
- if (newValue !== this.sortDescending_) { |
- this.sortDescending_ = newValue; |
- this.updateHeaderArrows_(); |
- this.scheduleRebuildBody_(); |
- this.dispatchSortingChangedEvent_(); |
+ updateHeaderArrows_: function() { |
+ for (var i = 0; i < this.headerCells_.length; i++) { |
+ if (!this.tableColumns_[i].cmp) { |
+ this.headerCells_[i].sideContent = ''; |
+ continue; |
} |
- }, |
- |
- get sortDescending() { |
- return this.sortDescending_; |
- }, |
- |
- updateHeaderArrows_: function() { |
- for (var i = 0; i < this.headerCells_.length; i++) { |
- if (!this.tableColumns_[i].cmp) { |
- this.headerCells_[i].sideContent = ''; |
- continue; |
- } |
- if (i !== this.sortColumnIndex_) { |
- this.headerCells_[i].sideContent = UNSORTED_ARROW; |
- continue; |
- } |
- this.headerCells_[i].sideContent = this.sortDescending_ ? |
- DESCENDING_ARROW : ASCENDING_ARROW; |
+ if (i !== this.sortColumnIndex_) { |
+ this.headerCells_[i].sideContent = UNSORTED_ARROW; |
+ continue; |
} |
- }, |
+ this.headerCells_[i].sideContent = this.sortDescending_ ? |
+ DESCENDING_ARROW : ASCENDING_ARROW; |
+ } |
+ }, |
- sortRows_: function(rows) { |
- rows.sort(function(rowA, rowB) { |
- if (this.sortDescending_) |
- return this.tableColumns_[this.sortColumnIndex_].cmp( |
- rowB.userRow, rowA.userRow); |
+ sortRows_: function(rows) { |
+ rows.sort(function(rowA, rowB) { |
+ if (this.sortDescending_) |
return this.tableColumns_[this.sortColumnIndex_].cmp( |
- rowA.userRow, rowB.userRow); |
- }.bind(this)); |
- // Sort expanded sub rows recursively. |
- for (var i = 0; i < rows.length; i++) { |
- if (this.getExpandedForUserRow_(rows[i])) |
- this.sortRows_(rows[i][this.subRowsPropertyName_]); |
- } |
- }, |
- |
- generateHeaderColumns_: function() { |
- this.headerCells_ = []; |
- Polymer.dom(this.$.head).textContent = ''; |
- if (!this.showHeader_) |
- return; |
- |
- var tr = this.appendNewElement_(this.$.head, 'tr'); |
- for (var i = 0; i < this.tableColumns_.length; i++) { |
- var td = this.appendNewElement_(tr, 'td'); |
- |
- var headerCell = document.createElement('tr-ui-b-table-header-cell'); |
- headerCell.cellTitle = this.tableColumns_[i].title; |
- headerCell.align = this.tableColumns_[i].align; |
- |
- // If the table can be sorted by this column, attach a tap callback |
- // to the column. |
- if (this.tableColumns_[i].cmp) { |
- Polymer.dom(td).classList.add('sensitive'); |
- headerCell.tapCallback = this.createSortCallback_(i); |
- // Set arrow position, depending on the sortColumnIndex. |
- if (this.sortColumnIndex_ === i) |
- headerCell.sideContent = this.sortDescending_ ? |
- DESCENDING_ARROW : ASCENDING_ARROW; |
- else |
- headerCell.sideContent = UNSORTED_ARROW; |
- } |
- |
- Polymer.dom(td).appendChild(headerCell); |
- this.headerCells_.push(headerCell); |
+ rowB.userRow, rowA.userRow); |
+ return this.tableColumns_[this.sortColumnIndex_].cmp( |
+ rowA.userRow, rowB.userRow); |
+ }.bind(this)); |
+ // Sort expanded sub rows recursively. |
+ for (var i = 0; i < rows.length; i++) { |
+ if (this.getExpandedForUserRow_(rows[i])) |
+ this.sortRows_(rows[i][this.subRowsPropertyName_]); |
+ } |
+ }, |
+ |
+ generateHeaderColumns_: function() { |
+ this.headerCells_ = []; |
+ Polymer.dom(this.$.head).textContent = ''; |
+ if (!this.showHeader_) |
+ return; |
+ |
+ var tr = this.appendNewElement_(this.$.head, 'tr'); |
+ for (var i = 0; i < this.tableColumns_.length; i++) { |
+ var td = this.appendNewElement_(tr, 'td'); |
+ |
+ var headerCell = document.createElement('tr-ui-b-table-header-cell'); |
+ headerCell.column = this.tableColumns_[i]; |
+ |
+ headerCell.addEventListener('selected-column-changed', |
+ this.onSelectedColumnChanged_.bind(this)); |
+ |
+ // If the table can be sorted by this column, attach a tap callback |
+ // to the column. |
+ if (this.tableColumns_[i].cmp) { |
+ Polymer.dom(td).classList.add('sensitive'); |
+ headerCell.tapCallback = this.createSortCallback_(i); |
+ // Set arrow position, depending on the sortColumnIndex. |
+ if (this.sortColumnIndex_ === i) |
+ headerCell.sideContent = this.sortDescending_ ? |
+ DESCENDING_ARROW : ASCENDING_ARROW; |
+ else |
+ headerCell.sideContent = UNSORTED_ARROW; |
} |
- }, |
- applySizes_: function() { |
- if (this.tableRows_.length === 0 && !this.showHeader) |
- return; |
- var rowToRemoveSizing; |
- var rowToSize; |
- if (this.showHeader) { |
- rowToSize = this.$.head.children[0]; |
- rowToRemoveSizing = this.$.body.children[0]; |
+ Polymer.dom(td).appendChild(headerCell); |
+ this.headerCells_.push(headerCell); |
+ } |
+ }, |
+ |
+ onSelectedColumnChanged_: function(event) { |
+ // Unselect all other columns. |
+ for (var i = 0; i < this.headerCells_.length; ++i) { |
+ var colElement = Polymer.dom(this.$.cols).children[i]; |
+ var headerCell = this.headerCells_[i]; |
+ if ((event.column === headerCell.column) && event.selected) { |
+ Polymer.dom(colElement).setAttribute('selected', true); |
} else { |
- rowToSize = this.$.body.children[0]; |
- rowToRemoveSizing = this.$.head.children[0]; |
+ headerCell.selected = false; |
+ Polymer.dom(colElement).removeAttribute('selected'); |
+ } |
+ } |
+ }, |
+ |
+ applySizes_: function() { |
+ if (this.tableRows_.length === 0 && !this.showHeader) |
+ return; |
+ var rowToRemoveSizing; |
+ var rowToSize; |
+ if (this.showHeader) { |
+ rowToSize = Polymer.dom(this.$.head).children[0]; |
+ rowToRemoveSizing = Polymer.dom(this.$.body).children[0]; |
+ } else { |
+ rowToSize = Polymer.dom(this.$.body).children[0]; |
+ rowToRemoveSizing = Polymer.dom(this.$.head).children[0]; |
+ } |
+ for (var i = 0; i < this.tableColumns_.length; i++) { |
+ if (rowToRemoveSizing && Polymer.dom(rowToRemoveSizing).children[i]) { |
+ var tdToRemoveSizing = Polymer.dom(rowToRemoveSizing).children[i]; |
+ tdToRemoveSizing.style.minWidth = ''; |
+ tdToRemoveSizing.style.width = ''; |
} |
- for (var i = 0; i < this.tableColumns_.length; i++) { |
- if (rowToRemoveSizing && rowToRemoveSizing.children[i]) { |
- var tdToRemoveSizing = rowToRemoveSizing.children[i]; |
- tdToRemoveSizing.style.minWidth = ''; |
- tdToRemoveSizing.style.width = ''; |
- } |
- |
- // Apply sizing. |
- var td = rowToSize.children[i]; |
- |
- var delta; |
- if (this.columnsWithExpandButtons_.indexOf(i) !== -1) { |
- td.style.paddingLeft = BASIC_INDENTATION + 'px'; |
- delta = BASIC_INDENTATION + 'px'; |
- } else { |
- delta = undefined; |
- } |
- function calc(base, delta) { |
- if (delta) |
- return 'calc(' + base + ' - ' + delta + ')'; |
- else |
- return base; |
- } |
+ // Apply sizing. |
+ var td = Polymer.dom(rowToSize).children[i]; |
- var w = this.tableColumns_[i].width; |
- if (w) { |
- if (/\d+px/.test(w)) { |
- td.style.minWidth = calc(w, delta); |
- } else if (/\d+%/.test(w)) { |
- td.style.width = w; |
- } else { |
- throw new Error('Unrecognized width string: ' + w); |
- } |
- } |
+ var delta; |
+ if (this.columnsWithExpandButtons_.indexOf(i) !== -1) { |
+ td.style.paddingLeft = BASIC_INDENTATION + 'px'; |
+ delta = BASIC_INDENTATION + 'px'; |
+ } else { |
+ delta = undefined; |
} |
- }, |
- |
- createSortCallback_: function(columnNumber) { |
- return function() { |
- var previousIndex = this.sortColumnIndex; |
- this.sortColumnIndex = columnNumber; |
- if (previousIndex !== columnNumber) |
- this.sortDescending = false; |
+ |
+ function calc(base, delta) { |
+ if (delta) |
+ return 'calc(' + base + ' - ' + delta + ')'; |
else |
- this.sortDescending = !this.sortDescending; |
- }.bind(this); |
- }, |
- |
- generateTableRowNodes_: function(tableSection, userRows, rowInfoMap, |
- indentation, lastAddedRow, |
- parentRowInfo) { |
- if (this.sortColumnIndex_ !== undefined && |
- tableSection === this.$.body) { |
- userRows = userRows.slice(); // Don't mess with the input data. |
- userRows.sort(function(rowA, rowB) { |
- var c = this.tableColumns_[this.sortColumnIndex_].cmp( |
- rowA, rowB); |
- if (this.sortDescending_) |
- c = -c; |
- return c; |
- }.bind(this)); |
+ return base; |
} |
- for (var i = 0; i < userRows.length; i++) { |
- var userRow = userRows[i]; |
- var rowInfo = this.getOrCreateRowInfoFor_(rowInfoMap, userRow, |
- parentRowInfo); |
- var htmlNode = this.getHTMLNodeForRowInfo_( |
- tableSection, rowInfo, rowInfoMap, indentation); |
- |
- if (lastAddedRow === undefined) { |
- // Put first into the table. |
- Polymer.dom(tableSection).insertBefore( |
- htmlNode, Polymer.dom(tableSection).firstChild); |
+ var w = this.tableColumns_[i].width; |
+ if (w) { |
+ if (/\d+px/.test(w)) { |
+ td.style.minWidth = calc(w, delta); |
+ } else if (/\d+%/.test(w)) { |
+ td.style.width = w; |
} else { |
- // This is shorthand for insertAfter(htmlNode, lastAdded). |
- var nextSiblingOfLastAdded = Polymer.dom(lastAddedRow).nextSibling; |
- Polymer.dom(tableSection).insertBefore( |
- htmlNode, nextSiblingOfLastAdded); |
+ throw new Error('Unrecognized width string: ' + w); |
} |
- this.updateTabIndexForTableRowNode_(htmlNode); |
- |
- lastAddedRow = htmlNode; |
- if (!rowInfo.isExpanded) |
- continue; |
- |
- // Append subrows now. |
- lastAddedRow = this.generateTableRowNodes_( |
- tableSection, userRow[this.subRowsPropertyName_], rowInfoMap, |
- indentation + 1, lastAddedRow, rowInfo); |
} |
- return lastAddedRow; |
- }, |
- |
- getOrCreateRowInfoFor_: function(rowInfoMap, userRow, parentRowInfo) { |
- var rowInfo = undefined; |
+ } |
+ }, |
+ |
+ createSortCallback_: function(columnNumber) { |
+ return function() { |
+ var previousIndex = this.sortColumnIndex; |
+ this.sortColumnIndex = columnNumber; |
+ if (previousIndex !== columnNumber) |
+ this.sortDescending = false; |
+ else |
+ this.sortDescending = !this.sortDescending; |
+ }.bind(this); |
+ }, |
+ |
+ generateTableColNodes_: function() { |
+ for (var i = 0; i < this.headerCells_.length; ++i) { |
+ var colElement = document.createElement('col'); |
+ if (this.headerCells_[i].selected) |
+ Polymer.dom(colElement).setAttribute('selected', true); |
+ Polymer.dom(this.$.cols).appendChild(colElement); |
+ } |
+ }, |
+ |
+ generateTableRowNodes_: function(tableSection, userRows, rowInfoMap, |
+ indentation, lastAddedRow, |
+ parentRowInfo) { |
+ if (this.sortColumnIndex_ !== undefined && |
+ tableSection === this.$.body) { |
+ userRows = userRows.slice(); // Don't mess with the input data. |
+ userRows.sort(function(rowA, rowB) { |
+ var c = this.tableColumns_[this.sortColumnIndex_].cmp( |
+ rowA, rowB); |
+ if (this.sortDescending_) |
+ c = -c; |
+ return c; |
+ }.bind(this)); |
+ } |
- if (rowInfoMap.has(userRow)) { |
- rowInfo = rowInfoMap.get(userRow); |
+ for (var i = 0; i < userRows.length; i++) { |
+ var userRow = userRows[i]; |
+ var rowInfo = this.getOrCreateRowInfoFor_(rowInfoMap, userRow, |
+ parentRowInfo); |
+ var htmlNode = this.getHTMLNodeForRowInfo_( |
+ tableSection, rowInfo, rowInfoMap, indentation); |
+ |
+ if (lastAddedRow === undefined) { |
+ // Put first into the table. |
+ Polymer.dom(tableSection).insertBefore( |
+ htmlNode, Polymer.dom(tableSection).firstChild); |
} else { |
- rowInfo = { |
- userRow: userRow, |
- htmlNode: undefined, |
- parentRowInfo: parentRowInfo |
- }; |
- rowInfoMap.set(userRow, rowInfo); |
- } |
- |
- // Recompute isExpanded in case defaultExpansionStateCallback_ has |
- // changed. |
- rowInfo.isExpanded = this.getExpandedForUserRow_(userRow); |
- |
- return rowInfo; |
- }, |
- |
- customizeTableRow_: function(userRow, trElement) { |
- if (!this.customizeTableRowCallback_) |
- return; |
- this.customizeTableRowCallback_(userRow, trElement); |
- }, |
- |
- getHTMLNodeForRowInfo_: function(tableSection, rowInfo, |
- rowInfoMap, indentation) { |
- if (rowInfo.htmlNode) { |
- this.customizeTableRow_(rowInfo.userRow, rowInfo.htmlNode); |
- return rowInfo.htmlNode; |
+ // This is shorthand for insertAfter(htmlNode, lastAdded). |
+ var nextSiblingOfLastAdded = Polymer.dom(lastAddedRow).nextSibling; |
+ Polymer.dom(tableSection).insertBefore( |
+ htmlNode, nextSiblingOfLastAdded); |
} |
+ this.updateTabIndexForTableRowNode_(htmlNode); |
- var INDENT_SPACE = indentation * 16; |
- var INDENT_SPACE_NO_BUTTON = indentation * 16 + BASIC_INDENTATION; |
- var trElement = this.ownerDocument.createElement('tr'); |
- rowInfo.htmlNode = trElement; |
- rowInfo.indentation = indentation; |
- trElement.rowInfo = rowInfo; |
- this.customizeTableRow_(rowInfo.userRow, trElement); |
- |
- for (var i = 0; i < this.tableColumns_.length;) { |
- var td = this.appendNewElement_(trElement, 'td'); |
- td.columnIndex = i; |
- |
- var column = this.tableColumns_[i]; |
- var value = column.value(rowInfo.userRow); |
- var colSpan = column.colSpan ? column.colSpan : 1; |
- td.style.colSpan = colSpan; |
- |
- switch (column.align) { |
- case undefined: |
- case ColumnAlignment.LEFT: |
- break; |
- |
- case ColumnAlignment.RIGHT: |
- td.style.textAlign = 'right'; |
- break; |
- |
- default: |
- throw new Error('Invalid alignment of column at index=' + i + |
- ': ' + column.align); |
- } |
- |
- if (this.doesColumnIndexSupportSelection(i)) |
- Polymer.dom(td).classList.add('supports-selection'); |
- |
- if (this.columnsWithExpandButtons_.indexOf(i) != -1) { |
- if (rowInfo.userRow[this.subRowsPropertyName_] && |
- rowInfo.userRow[this.subRowsPropertyName_].length > 0) { |
- td.style.paddingLeft = INDENT_SPACE + 'px'; |
- var expandButton = this.appendNewElement_(td, |
- 'expand-button'); |
- Polymer.dom(expandButton).textContent = RIGHT_ARROW; |
- if (rowInfo.isExpanded) |
- Polymer.dom(expandButton).classList.add('button-expanded'); |
- } else { |
- td.style.paddingLeft = INDENT_SPACE_NO_BUTTON + 'px'; |
- } |
- } |
- |
- if (value !== undefined) |
- Polymer.dom(td).appendChild( |
- tr.ui.b.asHTMLOrTextNode(value, this.ownerDocument)); |
- |
- i += colSpan; |
- } |
+ lastAddedRow = htmlNode; |
+ if (!rowInfo.isExpanded) |
+ continue; |
- var isSelectable = tableSection === this.$.body; |
- var isExpandable = rowInfo.userRow[this.subRowsPropertyName_] && |
- rowInfo.userRow[this.subRowsPropertyName_].length; |
- |
- if (isSelectable || isExpandable) { |
- trElement.addEventListener('click', function(e) { |
- e.stopPropagation(); |
- if (e.target.tagName == 'EXPAND-BUTTON') { |
- this.setExpandedForUserRow_( |
- tableSection, rowInfoMap, |
- rowInfo.userRow, !rowInfo.isExpanded); |
- return; |
- } |
+ // Append subrows now. |
+ lastAddedRow = this.generateTableRowNodes_( |
+ tableSection, userRow[this.subRowsPropertyName_], rowInfoMap, |
+ indentation + 1, lastAddedRow, rowInfo); |
+ } |
+ return lastAddedRow; |
+ }, |
+ |
+ getOrCreateRowInfoFor_: function(rowInfoMap, userRow, parentRowInfo) { |
+ var rowInfo = undefined; |
+ |
+ if (rowInfoMap.has(userRow)) { |
+ rowInfo = rowInfoMap.get(userRow); |
+ } else { |
+ rowInfo = { |
+ userRow: userRow, |
+ htmlNode: undefined, |
+ parentRowInfo: parentRowInfo |
+ }; |
+ rowInfoMap.set(userRow, rowInfo); |
+ } |
- function getTD(cur) { |
- if (cur === trElement) |
- throw new Error('woah'); |
- if (cur.parentElement === trElement) |
- return cur; |
- return getTD(cur.parentElement); |
- } |
+ // Recompute isExpanded in case defaultExpansionStateCallback_ has |
+ // changed. |
+ rowInfo.isExpanded = this.getExpandedForUserRow_(userRow); |
- // If the row/cell can be selected and it's not selected yet, |
- // select it. |
- if (isSelectable && this.selectionMode_ !== SelectionMode.NONE) { |
- var shouldSelect = false; |
- var columnIndex = getTD(e.target).columnIndex; |
- switch (this.selectionMode_) { |
- case SelectionMode.ROW: |
- shouldSelect = this.selectedTableRowInfo_ !== rowInfo; |
- break; |
- |
- case SelectionMode.CELL: |
- if (this.doesColumnIndexSupportSelection(columnIndex)) { |
- shouldSelect = this.selectedTableRowInfo_ !== rowInfo || |
- this.selectedColumnIndex_ !== columnIndex; |
- } |
- break; |
- |
- default: |
- throw new Error('Invalid selection mode ' + |
- this.selectionMode_); |
- } |
- if (shouldSelect) { |
- this.didTableRowInfoGetClicked_(rowInfo, columnIndex); |
- return; |
- } |
- } |
+ return rowInfo; |
+ }, |
- // Otherwise, if the row is expandable, expand/collapse it. |
- if (isExpandable) { |
- this.setExpandedForUserRow_(tableSection, rowInfoMap, |
- rowInfo.userRow, !rowInfo.isExpanded); |
- } |
- }.bind(this)); |
- } |
+ customizeTableRow_: function(userRow, trElement) { |
+ if (!this.customizeTableRowCallback_) |
+ return; |
+ this.customizeTableRowCallback_(userRow, trElement); |
+ }, |
+ getHTMLNodeForRowInfo_: function(tableSection, rowInfo, |
+ rowInfoMap, indentation) { |
+ if (rowInfo.htmlNode) { |
+ this.customizeTableRow_(rowInfo.userRow, rowInfo.htmlNode); |
return rowInfo.htmlNode; |
- }, |
- |
- removeSubNodes_: function(tableSection, rowInfo, rowInfoMap) { |
- if (rowInfo.userRow[this.subRowsPropertyName_] === undefined) |
- return; |
- for (var i = 0; |
- i < rowInfo.userRow[this.subRowsPropertyName_].length; i++) { |
- var subRow = rowInfo.userRow[this.subRowsPropertyName_][i]; |
- var subRowInfo = rowInfoMap.get(subRow); |
- if (!subRowInfo) |
- continue; |
- |
- var subNode = subRowInfo.htmlNode; |
- if (subNode && Polymer.dom(subNode).parentNode === tableSection) { |
- Polymer.dom(tableSection).removeChild(subNode); |
- this.removeSubNodes_(tableSection, subRowInfo, rowInfoMap); |
- } |
- } |
- }, |
- |
- scheduleRebuildHeaders_: function() { |
- this.headerDirty_ = true; |
- this.scheduleRebuild_(); |
- }, |
- |
- scheduleRebuildBody_: function() { |
- this.bodyDirty_ = true; |
- this.scheduleRebuild_(); |
- }, |
- |
- scheduleRebuildFooter_: function() { |
- this.footerDirty_ = true; |
- this.scheduleRebuild_(); |
- }, |
+ } |
- scheduleRebuild_: function() { |
- if (this.rebuildPending_) |
- return; |
- this.rebuildPending_ = true; |
- setTimeout(function() { |
- this.rebuildPending_ = false; |
- this.rebuild(); |
- }.bind(this), 0); |
- }, |
- |
- rebuildIfNeeded_: function() { |
- this.rebuild(); |
- }, |
+ var INDENT_SPACE = indentation * 16; |
+ var INDENT_SPACE_NO_BUTTON = indentation * 16 + BASIC_INDENTATION; |
+ var trElement = this.ownerDocument.createElement('tr'); |
+ rowInfo.htmlNode = trElement; |
+ rowInfo.indentation = indentation; |
+ trElement.rowInfo = rowInfo; |
+ this.customizeTableRow_(rowInfo.userRow, trElement); |
+ |
+ for (var i = 0; i < this.tableColumns_.length;) { |
+ var td = this.appendNewElement_(trElement, 'td'); |
+ td.columnIndex = i; |
+ |
+ var column = this.tableColumns_[i]; |
+ var value = column.value(rowInfo.userRow); |
+ var colSpan = column.colSpan ? column.colSpan : 1; |
+ td.style.colSpan = colSpan; |
+ |
+ switch (column.align) { |
+ case undefined: |
+ case ColumnAlignment.LEFT: |
+ break; |
- rebuild: function() { |
- var wasBodyOrHeaderDirty = this.headerDirty_ || this.bodyDirty_; |
+ case ColumnAlignment.RIGHT: |
+ td.style.textAlign = 'right'; |
+ break; |
- if (this.headerDirty_) { |
- this.generateHeaderColumns_(); |
- this.headerDirty_ = false; |
- } |
- if (this.bodyDirty_) { |
- Polymer.dom(this.$.body).textContent = ''; |
- this.generateTableRowNodes_( |
- this.$.body, |
- this.tableRows_, this.tableRowsInfo_, 0, |
- undefined, undefined); |
- if (this.tableRows_.length === 0 && this.emptyValue_ !== undefined) { |
- var trElement = this.ownerDocument.createElement('tr'); |
- Polymer.dom(this.$.body).appendChild(trElement); |
- Polymer.dom(trElement).classList.add('empty-row'); |
- var td = this.ownerDocument.createElement('td'); |
- Polymer.dom(trElement).appendChild(td); |
- td.colSpan = this.tableColumns_.length; |
- var emptyValue = this.emptyValue_; |
- Polymer.dom(td).appendChild( |
- tr.ui.b.asHTMLOrTextNode(emptyValue, this.ownerDocument)); |
- } |
- this.bodyDirty_ = false; |
+ default: |
+ throw new Error('Invalid alignment of column at index=' + i + |
+ ': ' + column.align); |
} |
- if (wasBodyOrHeaderDirty) |
- this.applySizes_(); |
- |
- if (this.footerDirty_) { |
- Polymer.dom(this.$.foot).textContent = ''; |
- this.generateTableRowNodes_( |
- this.$.foot, |
- this.tableFooterRows_, this.tableFooterRowsInfo_, 0, |
- undefined, undefined); |
- if (this.tableFooterRowsInfo_.length) { |
- Polymer.dom(this.$.body).classList.add('has-footer'); |
+ if (this.doesColumnIndexSupportSelection(i)) |
+ Polymer.dom(td).classList.add('supports-selection'); |
+ |
+ if (this.columnsWithExpandButtons_.indexOf(i) != -1) { |
+ if (rowInfo.userRow[this.subRowsPropertyName_] && |
+ rowInfo.userRow[this.subRowsPropertyName_].length > 0) { |
+ td.style.paddingLeft = INDENT_SPACE + 'px'; |
+ var expandButton = this.appendNewElement_(td, |
+ 'expand-button'); |
+ Polymer.dom(expandButton).textContent = RIGHT_ARROW; |
+ if (rowInfo.isExpanded) |
+ Polymer.dom(expandButton).classList.add('button-expanded'); |
} else { |
- Polymer.dom(this.$.body).classList.remove('has-footer'); |
+ td.style.paddingLeft = INDENT_SPACE_NO_BUTTON + 'px'; |
} |
- this.footerDirty_ = false; |
} |
- }, |
- |
- appendNewElement_: function(parent, tagName) { |
- var element = parent.ownerDocument.createElement(tagName); |
- Polymer.dom(parent).appendChild(element); |
- return element; |
- }, |
- |
- getExpandedForTableRow: function(userRow) { |
- this.rebuildIfNeeded_(); |
- var rowInfo = this.tableRowsInfo_.get(userRow); |
- if (rowInfo === undefined) |
- throw new Error('Row has not been seen, must expand its parents'); |
- return rowInfo.isExpanded; |
- }, |
- |
- getExpandedForUserRow_: function(userRow) { |
- if (userRow[this.subRowsPropertyName_] === undefined) |
- return false; |
- if (userRow[this.subRowsPropertyName_].length === 0) |
- return false; |
- if (userRow.isExpanded) |
- return true; |
- if (userRow.isExpanded === false) |
- return false; |
- |
- var rowInfo = this.tableRowsInfo_.get(userRow); |
- if (rowInfo && rowInfo.isExpanded) |
- return true; |
- |
- if (this.defaultExpansionStateCallback_ === undefined) |
- return false; |
- |
- var parentUserRow = undefined; |
- if (rowInfo && rowInfo.parentRowInfo) |
- parentUserRow = rowInfo.parentRowInfo.userRow; |
- |
- return this.defaultExpansionStateCallback_( |
- userRow, parentUserRow); |
- }, |
- |
- setExpandedForTableRow: function(userRow, expanded) { |
- this.rebuildIfNeeded_(); |
- var rowInfo = this.tableRowsInfo_.get(userRow); |
- if (rowInfo === undefined) |
- throw new Error('Row has not been seen, must expand its parents'); |
- return this.setExpandedForUserRow_(this.$.body, this.tableRowsInfo_, |
- userRow, expanded); |
- }, |
- |
- setExpandedForUserRow_: function(tableSection, rowInfoMap, |
- userRow, expanded) { |
- this.rebuildIfNeeded_(); |
- |
- var rowInfo = rowInfoMap.get(userRow); |
- if (rowInfo === undefined) |
- throw new Error('Row has not been seen, must expand its parents'); |
- |
- rowInfo.isExpanded = !!expanded; |
- // If no node, then nothing further needs doing. |
- if (rowInfo.htmlNode === undefined) |
- return; |
- // If its detached, then nothing needs doing. |
- if (rowInfo.htmlNode.parentElement !== tableSection) |
- return; |
- |
- // Otherwise, rebuild. |
- var expandButton = Polymer.dom(rowInfo.htmlNode) |
- .querySelector('expand-button'); |
- if (rowInfo.isExpanded) { |
- Polymer.dom(expandButton).classList.add('button-expanded'); |
- var lastAddedRow = rowInfo.htmlNode; |
- if (rowInfo.userRow[this.subRowsPropertyName_]) { |
- this.generateTableRowNodes_( |
- tableSection, |
- rowInfo.userRow[this.subRowsPropertyName_], rowInfoMap, |
- rowInfo.indentation + 1, |
- lastAddedRow, rowInfo); |
- } |
- } else { |
- Polymer.dom(expandButton).classList.remove('button-expanded'); |
- this.removeSubNodes_(tableSection, rowInfo, rowInfoMap); |
+ if (value !== undefined) { |
+ Polymer.dom(td).appendChild( |
+ tr.ui.b.asHTMLOrTextNode(value, this.ownerDocument)); |
} |
- this.maybeUpdateSelectedRow_(); |
- }, |
+ i += colSpan; |
+ } |
- get selectionMode() { |
- return this.selectionMode_; |
- }, |
+ var isSelectable = tableSection === this.$.body; |
+ var isExpandable = rowInfo.userRow[this.subRowsPropertyName_] && |
+ rowInfo.userRow[this.subRowsPropertyName_].length; |
+ |
+ if (isSelectable || isExpandable) { |
+ trElement.addEventListener('click', function(e) { |
+ e.stopPropagation(); |
+ if (e.target.tagName == 'EXPAND-BUTTON') { |
+ this.setExpandedForUserRow_( |
+ tableSection, rowInfoMap, |
+ rowInfo.userRow, !rowInfo.isExpanded); |
+ return; |
+ } |
- set selectionMode(selectionMode) { |
- if (!tr.b.dictionaryContainsValue(SelectionMode, selectionMode)) |
- throw new Error('Invalid selection mode ' + selectionMode); |
- this.rebuildIfNeeded_(); |
- this.selectionMode_ = selectionMode; |
- this.didSelectionStateChange_(); |
- }, |
+ function getTD(cur) { |
+ if (cur === trElement) |
+ throw new Error('woah'); |
+ if (cur.parentElement === trElement) |
+ return cur; |
+ return getTD(cur.parentElement); |
+ } |
- get rowHighlightStyle() { |
- return this.rowHighlightStyle_; |
- }, |
- |
- set rowHighlightStyle(rowHighlightStyle) { |
- if (!tr.b.dictionaryContainsValue(HighlightStyle, rowHighlightStyle)) |
- throw new Error('Invalid row highlight style ' + rowHighlightStyle); |
- this.rebuildIfNeeded_(); |
- this.rowHighlightStyle_ = rowHighlightStyle; |
- this.didSelectionStateChange_(); |
- }, |
- |
- get resolvedRowHighlightStyle() { |
- if (this.rowHighlightStyle_ !== HighlightStyle.DEFAULT) |
- return this.rowHighlightStyle_; |
- switch (this.selectionMode_) { |
- case SelectionMode.NONE: |
- return HighlightStyle.NONE; |
- case SelectionMode.ROW: |
- return HighlightStyle.DARK; |
- case SelectionMode.CELL: |
- return HighlightStyle.LIGHT; |
- default: |
- throw new Error('Invalid selection mode ' + selectionMode); |
- } |
- }, |
+ // If the row/cell can be selected and it's not selected yet, |
+ // select it. |
+ if (isSelectable && this.selectionMode_ !== SelectionMode.NONE) { |
+ var shouldSelect = false; |
+ var columnIndex = getTD(e.target).columnIndex; |
+ switch (this.selectionMode_) { |
+ case SelectionMode.ROW: |
+ shouldSelect = this.selectedTableRowInfo_ !== rowInfo; |
+ break; |
+ |
+ case SelectionMode.CELL: |
+ if (this.doesColumnIndexSupportSelection(columnIndex)) { |
+ shouldSelect = this.selectedTableRowInfo_ !== rowInfo || |
+ this.selectedColumnIndex_ !== columnIndex; |
+ } |
+ break; |
+ |
+ default: |
+ throw new Error('Invalid selection mode ' + |
+ this.selectionMode_); |
+ } |
+ if (shouldSelect) { |
+ this.didTableRowInfoGetClicked_(rowInfo, columnIndex); |
+ return; |
+ } |
+ } |
- get cellHighlightStyle() { |
- return this.cellHighlightStyle_; |
- }, |
- |
- set cellHighlightStyle(cellHighlightStyle) { |
- if (!tr.b.dictionaryContainsValue(HighlightStyle, cellHighlightStyle)) |
- throw new Error('Invalid cell highlight style ' + cellHighlightStyle); |
- this.rebuildIfNeeded_(); |
- this.cellHighlightStyle_ = cellHighlightStyle; |
- this.didSelectionStateChange_(); |
- }, |
- |
- get resolvedCellHighlightStyle() { |
- if (this.cellHighlightStyle_ !== HighlightStyle.DEFAULT) |
- return this.cellHighlightStyle_; |
- switch (this.selectionMode_) { |
- case SelectionMode.NONE: |
- case SelectionMode.ROW: |
- return HighlightStyle.NONE; |
- case SelectionMode.CELL: |
- return HighlightStyle.DARK; |
- default: |
- throw new Error('Invalid selection mode ' + selectionMode); |
- } |
- }, |
+ // Otherwise, if the row is expandable, expand/collapse it. |
+ if (isExpandable) { |
+ this.setExpandedForUserRow_(tableSection, rowInfoMap, |
+ rowInfo.userRow, !rowInfo.isExpanded); |
+ } |
+ }.bind(this)); |
+ } |
- setHighlightStyle_: function(highlightAttribute, resolvedHighlightStyle) { |
- switch (resolvedHighlightStyle) { |
- case HighlightStyle.NONE: |
- Polymer.dom(this.$.body).removeAttribute(highlightAttribute); |
- break; |
- case HighlightStyle.LIGHT: |
- Polymer.dom(this.$.body).setAttribute(highlightAttribute, 'light'); |
- break; |
- case HighlightStyle.DARK: |
- Polymer.dom(this.$.body).setAttribute(highlightAttribute, 'dark'); |
- break; |
- default: |
- throw new Error('Invalid resolved highlight style ' + |
- resolvedHighlightStyle); |
+ return rowInfo.htmlNode; |
+ }, |
+ |
+ removeSubNodes_: function(tableSection, rowInfo, rowInfoMap) { |
+ if (rowInfo.userRow[this.subRowsPropertyName_] === undefined) |
+ return; |
+ for (var i = 0; |
+ i < rowInfo.userRow[this.subRowsPropertyName_].length; i++) { |
+ var subRow = rowInfo.userRow[this.subRowsPropertyName_][i]; |
+ var subRowInfo = rowInfoMap.get(subRow); |
+ if (!subRowInfo) |
+ continue; |
+ |
+ var subNode = subRowInfo.htmlNode; |
+ if (subNode && Polymer.dom(subNode).parentNode === tableSection) { |
+ Polymer.dom(tableSection).removeChild(subNode); |
+ this.removeSubNodes_(tableSection, subRowInfo, rowInfoMap); |
} |
- }, |
- |
- didSelectionStateChange_: function() { |
- this.setHighlightStyle_('row-highlight-style', |
- this.resolvedRowHighlightStyle); |
- this.setHighlightStyle_('cell-highlight-style', |
- this.resolvedCellHighlightStyle); |
- |
- for (var i = 0; i < this.$.body.children.length; i++) |
- this.updateTabIndexForTableRowNode_(this.$.body.children[i]); |
- this.maybeUpdateSelectedRow_(); |
- }, |
+ } |
+ }, |
+ |
+ scheduleRebuildHeaders_: function() { |
+ this.headerDirty_ = true; |
+ this.scheduleRebuild_(); |
+ }, |
+ |
+ scheduleRebuildBody_: function() { |
+ this.bodyDirty_ = true; |
+ this.scheduleRebuild_(); |
+ }, |
+ |
+ scheduleRebuildFooter_: function() { |
+ this.footerDirty_ = true; |
+ this.scheduleRebuild_(); |
+ }, |
+ |
+ scheduleRebuild_: function() { |
+ if (this.rebuildPending_) |
+ return; |
+ this.rebuildPending_ = true; |
+ setTimeout(function() { |
+ this.rebuildPending_ = false; |
+ this.rebuild(); |
+ }.bind(this), 0); |
+ }, |
- maybeUpdateSelectedRow_: function() { |
- if (this.selectedTableRowInfo_ === undefined) |
- return; |
+ rebuildIfNeeded_: function() { |
+ this.rebuild(); |
+ }, |
- // Selection may be off. |
- if (this.selectionMode_ === SelectionMode.NONE) { |
- this.removeSelectedState_(); |
- this.selectedTableRowInfo_ = undefined; |
- return; |
- } |
+ rebuild: function() { |
+ var wasBodyOrHeaderDirty = this.headerDirty_ || this.bodyDirty_; |
- // selectedUserRow may not be visible |
- function isVisible(rowInfo) { |
- if (!rowInfo.htmlNode) |
- return false; |
- return !!rowInfo.htmlNode.parentElement; |
- } |
- if (isVisible(this.selectedTableRowInfo_)) { |
- this.updateSelectedState_(); |
- return; |
+ if (this.headerDirty_) { |
+ this.generateHeaderColumns_(); |
+ this.headerDirty_ = false; |
+ } |
+ if (this.bodyDirty_) { |
+ Polymer.dom(this.$.cols).textContent = ''; |
+ this.generateTableColNodes_(); |
+ Polymer.dom(this.$.body).textContent = ''; |
+ this.generateTableRowNodes_( |
+ this.$.body, |
+ this.tableRows_, this.tableRowsInfo_, 0, |
+ undefined, undefined); |
+ if (this.tableRows_.length === 0 && this.emptyValue_ !== undefined) { |
+ var trElement = this.ownerDocument.createElement('tr'); |
+ Polymer.dom(this.$.body).appendChild(trElement); |
+ Polymer.dom(trElement).classList.add('empty-row'); |
+ var td = this.ownerDocument.createElement('td'); |
+ Polymer.dom(trElement).appendChild(td); |
+ td.colSpan = this.tableColumns_.length; |
+ var emptyValue = this.emptyValue_; |
+ Polymer.dom(td).appendChild( |
+ tr.ui.b.asHTMLOrTextNode(emptyValue, this.ownerDocument)); |
} |
+ this.bodyDirty_ = false; |
+ } |
- this.removeSelectedState_(); |
- var curRowInfo = this.selectedTableRowInfo_; |
- while (curRowInfo && !isVisible(curRowInfo)) |
- curRowInfo = curRowInfo.parentRowInfo; |
- |
- this.selectedTableRowInfo_ = curRowInfo; |
- if (this.selectedTableRowInfo_) |
- this.updateSelectedState_(); |
- }, |
- |
- didTableRowInfoGetClicked_: function(rowInfo, columnIndex) { |
- switch (this.selectionMode_) { |
- case SelectionMode.NONE: |
- return; |
- |
- case SelectionMode.CELL: |
- if (!this.doesColumnIndexSupportSelection(columnIndex)) |
- return; |
- if (this.selectedColumnIndex !== columnIndex) |
- this.selectedColumnIndex = columnIndex; |
- // Fall through. |
- |
- case SelectionMode.ROW: |
- if (this.selectedTableRowInfo_ !== rowInfo) |
- this.selectedTableRow = rowInfo.userRow; |
- } |
- }, |
- |
- get selectedTableRow() { |
- if (!this.selectedTableRowInfo_) |
- return undefined; |
- return this.selectedTableRowInfo_.userRow; |
- }, |
- |
- set selectedTableRow(userRow) { |
- this.rebuildIfNeeded_(); |
- if (this.selectionMode_ === SelectionMode.NONE) |
- throw new Error('Selection is off.'); |
- |
- var rowInfo; |
- if (userRow === undefined) { |
- rowInfo = undefined; |
+ if (wasBodyOrHeaderDirty) |
+ this.applySizes_(); |
+ |
+ if (this.footerDirty_) { |
+ Polymer.dom(this.$.foot).textContent = ''; |
+ this.generateTableRowNodes_( |
+ this.$.foot, |
+ this.tableFooterRows_, this.tableFooterRowsInfo_, 0, |
+ undefined, undefined); |
+ if (this.tableFooterRowsInfo_.length) { |
+ Polymer.dom(this.$.body).classList.add('has-footer'); |
} else { |
- rowInfo = this.tableRowsInfo_.get(userRow); |
- if (!rowInfo) |
- throw new Error('Row has not been seen, must expand its parents.'); |
+ Polymer.dom(this.$.body).classList.remove('has-footer'); |
} |
+ this.footerDirty_ = false; |
+ } |
+ }, |
+ |
+ appendNewElement_: function(parent, tagName) { |
+ var element = parent.ownerDocument.createElement(tagName); |
+ Polymer.dom(parent).appendChild(element); |
+ return element; |
+ }, |
+ |
+ getExpandedForTableRow: function(userRow) { |
+ this.rebuildIfNeeded_(); |
+ var rowInfo = this.tableRowsInfo_.get(userRow); |
+ if (rowInfo === undefined) |
+ throw new Error('Row has not been seen, must expand its parents'); |
+ return rowInfo.isExpanded; |
+ }, |
+ |
+ getExpandedForUserRow_: function(userRow) { |
+ if (userRow[this.subRowsPropertyName_] === undefined) |
+ return false; |
+ if (userRow[this.subRowsPropertyName_].length === 0) |
+ return false; |
+ if (userRow.isExpanded) |
+ return true; |
+ if (userRow.isExpanded === false) |
+ return false; |
- var e = this.prepareToChangeSelection_(); |
- this.selectedTableRowInfo_ = rowInfo; |
+ var rowInfo = this.tableRowsInfo_.get(userRow); |
+ if (rowInfo && rowInfo.isExpanded) |
+ return true; |
- if (this.selectedTableRowInfo_ === undefined) { |
- this.selectedColumnIndex_ = undefined; |
- this.removeSelectedState_(); |
- } else { |
- switch (this.selectionMode_) { |
- case SelectionMode.ROW: |
- this.selectedColumnIndex_ = undefined; |
- break; |
- |
- case SelectionMode.CELL: |
- if (this.selectedColumnIndex_ === undefined) { |
- var i = this.getFirstSelectableColumnIndex_(); |
- if (i == -1) |
- throw new Error('Cannot find a selectable column.'); |
- this.selectedColumnIndex_ = i; |
- } |
- break; |
- |
- default: |
- throw new Error('Invalid selection mode ' + this.selectionMode_); |
- } |
- this.updateSelectedState_(); |
+ if (this.defaultExpansionStateCallback_ === undefined) |
+ return false; |
+ |
+ var parentUserRow = undefined; |
+ if (rowInfo && rowInfo.parentRowInfo) |
+ parentUserRow = rowInfo.parentRowInfo.userRow; |
+ |
+ return this.defaultExpansionStateCallback_( |
+ userRow, parentUserRow); |
+ }, |
+ |
+ setExpandedForTableRow: function(userRow, expanded) { |
+ this.rebuildIfNeeded_(); |
+ var rowInfo = this.tableRowsInfo_.get(userRow); |
+ if (rowInfo === undefined) |
+ throw new Error('Row has not been seen, must expand its parents'); |
+ return this.setExpandedForUserRow_(this.$.body, this.tableRowsInfo_, |
+ userRow, expanded); |
+ }, |
+ |
+ setExpandedForUserRow_: function(tableSection, rowInfoMap, |
+ userRow, expanded) { |
+ this.rebuildIfNeeded_(); |
+ |
+ var rowInfo = rowInfoMap.get(userRow); |
+ if (rowInfo === undefined) |
+ throw new Error('Row has not been seen, must expand its parents'); |
+ |
+ rowInfo.isExpanded = !!expanded; |
+ // If no node, then nothing further needs doing. |
+ if (rowInfo.htmlNode === undefined) |
+ return; |
+ |
+ // If its detached, then nothing needs doing. |
+ if (rowInfo.htmlNode.parentElement !== tableSection) |
+ return; |
+ |
+ // Otherwise, rebuild. |
+ var expandButton = |
+ Polymer.dom(rowInfo.htmlNode).querySelector('expand-button'); |
+ if (rowInfo.isExpanded) { |
+ Polymer.dom(expandButton).classList.add('button-expanded'); |
+ var lastAddedRow = rowInfo.htmlNode; |
+ if (rowInfo.userRow[this.subRowsPropertyName_]) { |
+ this.generateTableRowNodes_( |
+ tableSection, |
+ rowInfo.userRow[this.subRowsPropertyName_], rowInfoMap, |
+ rowInfo.indentation + 1, |
+ lastAddedRow, rowInfo); |
} |
+ } else { |
+ Polymer.dom(expandButton).classList.remove('button-expanded'); |
+ this.removeSubNodes_(tableSection, rowInfo, rowInfoMap); |
+ } |
- this.dispatchEvent(e); |
- }, |
+ this.maybeUpdateSelectedRow_(); |
+ }, |
+ |
+ get selectionMode() { |
+ return this.selectionMode_; |
+ }, |
+ |
+ set selectionMode(selectionMode) { |
+ if (!tr.b.dictionaryContainsValue(SelectionMode, selectionMode)) |
+ throw new Error('Invalid selection mode ' + selectionMode); |
+ this.rebuildIfNeeded_(); |
+ this.selectionMode_ = selectionMode; |
+ this.didSelectionStateChange_(); |
+ }, |
+ |
+ get rowHighlightStyle() { |
+ return this.rowHighlightStyle_; |
+ }, |
+ |
+ set rowHighlightStyle(rowHighlightStyle) { |
+ if (!tr.b.dictionaryContainsValue(HighlightStyle, rowHighlightStyle)) |
+ throw new Error('Invalid row highlight style ' + rowHighlightStyle); |
+ this.rebuildIfNeeded_(); |
+ this.rowHighlightStyle_ = rowHighlightStyle; |
+ this.didSelectionStateChange_(); |
+ }, |
+ |
+ get resolvedRowHighlightStyle() { |
+ if (this.rowHighlightStyle_ !== HighlightStyle.DEFAULT) |
+ return this.rowHighlightStyle_; |
+ switch (this.selectionMode_) { |
+ case SelectionMode.NONE: |
+ return HighlightStyle.NONE; |
+ case SelectionMode.ROW: |
+ return HighlightStyle.DARK; |
+ case SelectionMode.CELL: |
+ return HighlightStyle.LIGHT; |
+ default: |
+ throw new Error('Invalid selection mode ' + selectionMode); |
+ } |
+ }, |
+ |
+ get cellHighlightStyle() { |
+ return this.cellHighlightStyle_; |
+ }, |
+ |
+ set cellHighlightStyle(cellHighlightStyle) { |
+ if (!tr.b.dictionaryContainsValue(HighlightStyle, cellHighlightStyle)) |
+ throw new Error('Invalid cell highlight style ' + cellHighlightStyle); |
+ this.rebuildIfNeeded_(); |
+ this.cellHighlightStyle_ = cellHighlightStyle; |
+ this.didSelectionStateChange_(); |
+ }, |
+ |
+ get resolvedCellHighlightStyle() { |
+ if (this.cellHighlightStyle_ !== HighlightStyle.DEFAULT) |
+ return this.cellHighlightStyle_; |
+ switch (this.selectionMode_) { |
+ case SelectionMode.NONE: |
+ case SelectionMode.ROW: |
+ return HighlightStyle.NONE; |
+ case SelectionMode.CELL: |
+ return HighlightStyle.DARK; |
+ default: |
+ throw new Error('Invalid selection mode ' + selectionMode); |
+ } |
+ }, |
+ |
+ setHighlightStyle_: function(highlightAttribute, resolvedHighlightStyle) { |
+ switch (resolvedHighlightStyle) { |
+ case HighlightStyle.NONE: |
+ Polymer.dom(this.$.body).removeAttribute(highlightAttribute); |
+ break; |
+ case HighlightStyle.LIGHT: |
+ Polymer.dom(this.$.body).setAttribute(highlightAttribute, 'light'); |
+ break; |
+ case HighlightStyle.DARK: |
+ Polymer.dom(this.$.body).setAttribute(highlightAttribute, 'dark'); |
+ break; |
+ default: |
+ throw new Error('Invalid resolved highlight style ' + |
+ resolvedHighlightStyle); |
+ } |
+ }, |
- updateTabIndexForTableRowNode_: function(row) { |
- if (this.selectionMode_ === SelectionMode.ROW) |
- row.tabIndex = 0; |
- else |
- Polymer.dom(row).removeAttribute('tabIndex'); |
+ didSelectionStateChange_: function() { |
+ this.setHighlightStyle_('row-highlight-style', |
+ this.resolvedRowHighlightStyle); |
+ this.setHighlightStyle_('cell-highlight-style', |
+ this.resolvedCellHighlightStyle); |
- var enableCellTab = this.selectionMode_ === SelectionMode.CELL; |
- for (var i = 0; i < this.tableColumns_.length; i++) { |
- var cell = row.children[i]; |
- if (enableCellTab && this.doesColumnIndexSupportSelection(i)) |
- cell.tabIndex = 0; |
- else |
- Polymer.dom(cell).removeAttribute('tabIndex'); |
- } |
- }, |
+ for (var i = 0; i < Polymer.dom(this.$.body).children.length; i++) { |
+ this.updateTabIndexForTableRowNode_( |
+ Polymer.dom(this.$.body).children[i]); |
+ } |
+ this.maybeUpdateSelectedRow_(); |
+ }, |
- prepareToChangeSelection_: function() { |
- var e = new tr.b.Event('selection-changed'); |
- var previousSelectedRowInfo = this.selectedTableRowInfo_; |
- if (previousSelectedRowInfo) |
- e.previousSelectedTableRow = previousSelectedRowInfo.userRow; |
- else |
- e.previousSelectedTableRow = undefined; |
+ maybeUpdateSelectedRow_: function() { |
+ if (this.selectedTableRowInfo_ === undefined) |
+ return; |
+ // Selection may be off. |
+ if (this.selectionMode_ === SelectionMode.NONE) { |
this.removeSelectedState_(); |
+ this.selectedTableRowInfo_ = undefined; |
+ return; |
+ } |
- return e; |
- }, |
+ // selectedUserRow may not be visible |
+ function isVisible(rowInfo) { |
+ if (!rowInfo.htmlNode) |
+ return false; |
+ return !!rowInfo.htmlNode.parentElement; |
+ } |
+ if (isVisible(this.selectedTableRowInfo_)) { |
+ this.updateSelectedState_(); |
+ return; |
+ } |
- removeSelectedState_: function() { |
- this.setSelectedState_(false); |
- }, |
+ this.removeSelectedState_(); |
+ var curRowInfo = this.selectedTableRowInfo_; |
+ while (curRowInfo && !isVisible(curRowInfo)) |
+ curRowInfo = curRowInfo.parentRowInfo; |
- updateSelectedState_: function() { |
- this.setSelectedState_(true); |
- }, |
+ this.selectedTableRowInfo_ = curRowInfo; |
+ if (this.selectedTableRowInfo_) |
+ this.updateSelectedState_(); |
+ }, |
- setSelectedState_: function(select) { |
- if (this.selectedTableRowInfo_ === undefined) |
+ didTableRowInfoGetClicked_: function(rowInfo, columnIndex) { |
+ switch (this.selectionMode_) { |
+ case SelectionMode.NONE: |
return; |
- // Row selection. |
- var rowNode = this.selectedTableRowInfo_.htmlNode; |
- if (select) |
- Polymer.dom(rowNode).setAttribute('selected', true); |
- else |
- Polymer.dom(rowNode).removeAttribute('selected'); |
- |
- // Cell selection (if applicable). |
- var cellNode = rowNode.children[this.selectedColumnIndex_]; |
- if (!cellNode) |
- return; |
- if (select) |
- Polymer.dom(cellNode).setAttribute('selected', true); |
- else |
- Polymer.dom(cellNode).removeAttribute('selected'); |
- }, |
+ case SelectionMode.CELL: |
+ if (!this.doesColumnIndexSupportSelection(columnIndex)) |
+ return; |
+ if (this.selectedColumnIndex !== columnIndex) |
+ this.selectedColumnIndex = columnIndex; |
+ // Fall through. |
- doesColumnIndexSupportSelection: function(columnIndex) { |
- var columnInfo = this.tableColumns_[columnIndex]; |
- var scs = columnInfo.supportsCellSelection; |
- if (scs === false) |
- return false; |
- return true; |
- }, |
+ case SelectionMode.ROW: |
+ if (this.selectedTableRowInfo_ !== rowInfo) |
+ this.selectedTableRow = rowInfo.userRow; |
+ } |
+ }, |
+ |
+ /** |
+ * If the selectionMode is CELL and a cell is selected, |
+ * return an object containing the row, column, and value of the selected |
+ * cell. |
+ * |
+ * @return {undefined|!Object} |
+ */ |
+ get selectedCell() { |
+ var row = this.selectedTableRow; |
+ var columnIndex = this.selectedColumnIndex; |
+ if (row === undefined || columnIndex === undefined || |
+ this.tableColumns_.length <= columnIndex) |
+ return undefined; |
+ var column = this.tableColumns_[columnIndex]; |
+ return { |
+ row: row, |
+ column: column, |
+ value: column.value(row) |
+ }; |
+ }, |
+ |
+ /** |
+ * If a selectable column is selected, return the object describing the |
+ * selected column. |
+ * |
+ * Columns with |selectable:true| can be selected independently of rows |
+ * and cells. So it is possible to select column 0 and cell [0,0], or |
+ * column 1 and cell [0,0], for example. See |selectedCell| for how to |
+ * access the selected cell when the selectionMode is CELL. |
+ * |
+ * |selectedTableColumn| is entirely independent of |selectedColumnIndex|. |
+ * When the table selectionMode is CELL, use |selectedTableRow| and |
+ * |selectedColumnIndex| to find the selected cell. |
+ * When one or more columns have |selectable:true|, then use |
+ * |selectedTableColumn| to find the selected column, which may be either |
+ * the same as or different from |selectedColumnIndex|, if a cell is also |
+ * selected. |
+ * |
+ * @return {undefined|!Object} column |
+ */ |
+ get selectedTableColumn() { |
+ for (var i = 0; i < this.headerCells_.length; i++) { |
+ if (this.headerCells_[i].selected) |
+ return this.tableColumns_[i]; |
+ } |
+ return undefined; |
+ }, |
+ |
+ /** |
+ * See |get selectedTableColumn()|. |column| must be one of the elements |
+ * of this.tableColumns. |
+ * |
+ * @param {!Object} column |
+ */ |
+ set selectedTableColumn(column) { |
+ if (column !== undefined) { |
+ var index = this.tableColumns.indexOf(column); |
+ if (index < 0) |
+ throw new Error('Cannot select unknown column', column); |
+ |
+ if (!column.selectable) |
+ throw new Error('Cannot select un-selectable column', column); |
+ |
+ var cell = this.headerCells_[index]; |
+ cell.selected = true; |
+ } |
+ var e = new tr.b.Event('selected-column-changed'); |
+ e.column = column; |
+ e.selected = column !== undefined; |
+ cell.dispatchEvent(e); |
+ }, |
+ |
+ get selectedTableRow() { |
+ if (!this.selectedTableRowInfo_) |
+ return undefined; |
+ return this.selectedTableRowInfo_.userRow; |
+ }, |
+ |
+ set selectedTableRow(userRow) { |
+ this.rebuildIfNeeded_(); |
+ if (this.selectionMode_ === SelectionMode.NONE) |
+ throw new Error('Selection is off.'); |
+ |
+ var rowInfo; |
+ if (userRow === undefined) { |
+ rowInfo = undefined; |
+ } else { |
+ rowInfo = this.tableRowsInfo_.get(userRow); |
+ if (!rowInfo) |
+ throw new Error('Row has not been seen, must expand its parents.'); |
+ } |
- getFirstSelectableColumnIndex_: function() { |
- for (var i = 0; i < this.tableColumns_.length; i++) { |
- if (this.doesColumnIndexSupportSelection(i)) |
- return i; |
- } |
- return -1; |
- }, |
+ var e = this.prepareToChangeSelection_(); |
+ this.selectedTableRowInfo_ = rowInfo; |
- getSelectableNodeGivenTableRowNode_: function(htmlNode) { |
+ if (this.selectedTableRowInfo_ === undefined) { |
+ this.selectedColumnIndex_ = undefined; |
+ this.removeSelectedState_(); |
+ } else { |
switch (this.selectionMode_) { |
case SelectionMode.ROW: |
- return htmlNode; |
+ this.selectedColumnIndex_ = undefined; |
+ break; |
case SelectionMode.CELL: |
- return htmlNode.children[this.selectedColumnIndex_]; |
+ if (this.selectedColumnIndex_ === undefined) { |
+ var i = this.getFirstSelectableColumnIndex_(); |
+ if (i == -1) |
+ throw new Error('Cannot find a selectable column.'); |
+ this.selectedColumnIndex_ = i; |
+ } |
+ break; |
default: |
throw new Error('Invalid selection mode ' + this.selectionMode_); |
} |
- }, |
- |
- get selectedColumnIndex() { |
- if (this.selectionMode_ !== SelectionMode.CELL) |
- return undefined; |
- return this.selectedColumnIndex_; |
- }, |
- |
- set selectedColumnIndex(selectedColumnIndex) { |
- this.rebuildIfNeeded_(); |
- if (this.selectionMode_ === SelectionMode.NONE) |
- throw new Error('Selection is off.'); |
- if (selectedColumnIndex < 0 || |
- selectedColumnIndex >= this.tableColumns_.length) |
- throw new Error('Invalid index'); |
- if (!this.doesColumnIndexSupportSelection(selectedColumnIndex)) |
- throw new Error('Selection is not supported on this column'); |
- |
- var e = this.prepareToChangeSelection_(); |
- this.selectedColumnIndex_ = selectedColumnIndex; |
- if (this.selectedColumnIndex_ === undefined) |
- this.selectedTableRowInfo_ = undefined; |
this.updateSelectedState_(); |
+ } |
- this.dispatchEvent(e); |
- }, |
+ this.dispatchEvent(e); |
+ }, |
- onKeyDown_: function(e) { |
- if (this.selectionMode_ === SelectionMode.NONE) |
- return; |
- if (this.selectedTableRowInfo_ === undefined) |
+ updateTabIndexForTableRowNode_: function(row) { |
+ if (this.selectionMode_ === SelectionMode.ROW) |
+ row.tabIndex = 0; |
+ else |
+ Polymer.dom(row).removeAttribute('tabIndex'); |
+ |
+ var enableCellTab = this.selectionMode_ === SelectionMode.CELL; |
+ for (var i = 0; i < this.tableColumns_.length; i++) { |
+ var cell = Polymer.dom(row).children[i]; |
+ if (enableCellTab && this.doesColumnIndexSupportSelection(i)) |
+ cell.tabIndex = 0; |
+ else |
+ Polymer.dom(cell).removeAttribute('tabIndex'); |
+ } |
+ }, |
+ |
+ prepareToChangeSelection_: function() { |
+ var e = new tr.b.Event('selection-changed'); |
+ var previousSelectedRowInfo = this.selectedTableRowInfo_; |
+ if (previousSelectedRowInfo) |
+ e.previousSelectedTableRow = previousSelectedRowInfo.userRow; |
+ else |
+ e.previousSelectedTableRow = undefined; |
+ |
+ this.removeSelectedState_(); |
+ |
+ return e; |
+ }, |
+ |
+ removeSelectedState_: function() { |
+ this.setSelectedState_(false); |
+ }, |
+ |
+ updateSelectedState_: function() { |
+ this.setSelectedState_(true); |
+ }, |
+ |
+ setSelectedState_: function(select) { |
+ if (this.selectedTableRowInfo_ === undefined) |
+ return; |
+ |
+ // Row selection. |
+ var rowNode = this.selectedTableRowInfo_.htmlNode; |
+ if (select) |
+ Polymer.dom(rowNode).setAttribute('selected', true); |
+ else |
+ Polymer.dom(rowNode).removeAttribute('selected'); |
+ |
+ // Cell selection (if applicable). |
+ var cellNode = Polymer.dom(rowNode).children[this.selectedColumnIndex_]; |
+ if (!cellNode) |
+ return; |
+ if (select) |
+ Polymer.dom(cellNode).setAttribute('selected', true); |
+ else |
+ Polymer.dom(cellNode).removeAttribute('selected'); |
+ }, |
+ |
+ doesColumnIndexSupportSelection: function(columnIndex) { |
+ var columnInfo = this.tableColumns_[columnIndex]; |
+ var scs = columnInfo.supportsCellSelection; |
+ if (scs === false) |
+ return false; |
+ return true; |
+ }, |
+ |
+ getFirstSelectableColumnIndex_: function() { |
+ for (var i = 0; i < this.tableColumns_.length; i++) { |
+ if (this.doesColumnIndexSupportSelection(i)) |
+ return i; |
+ } |
+ return -1; |
+ }, |
+ |
+ getSelectableNodeGivenTableRowNode_: function(htmlNode) { |
+ switch (this.selectionMode_) { |
+ case SelectionMode.ROW: |
+ return htmlNode; |
+ |
+ case SelectionMode.CELL: |
+ return Polymer.dom(htmlNode).children[this.selectedColumnIndex_]; |
+ |
+ default: |
+ throw new Error('Invalid selection mode ' + this.selectionMode_); |
+ } |
+ }, |
+ |
+ get selectedColumnIndex() { |
+ if (this.selectionMode_ !== SelectionMode.CELL) |
+ return undefined; |
+ return this.selectedColumnIndex_; |
+ }, |
+ |
+ set selectedColumnIndex(selectedColumnIndex) { |
+ this.rebuildIfNeeded_(); |
+ if (this.selectionMode_ === SelectionMode.NONE) |
+ throw new Error('Selection is off.'); |
+ if (selectedColumnIndex < 0 || |
+ selectedColumnIndex >= this.tableColumns_.length) |
+ throw new Error('Invalid index'); |
+ if (!this.doesColumnIndexSupportSelection(selectedColumnIndex)) |
+ throw new Error('Selection is not supported on this column'); |
+ |
+ var e = this.prepareToChangeSelection_(); |
+ this.selectedColumnIndex_ = selectedColumnIndex; |
+ if (this.selectedColumnIndex_ === undefined) |
+ this.selectedTableRowInfo_ = undefined; |
+ this.updateSelectedState_(); |
+ |
+ this.dispatchEvent(e); |
+ }, |
+ |
+ onKeyDown_: function(e) { |
+ if (this.selectionMode_ === SelectionMode.NONE) |
+ return; |
+ if (this.selectedTableRowInfo_ === undefined) |
+ return; |
+ |
+ var code_to_command_names = { |
+ 13: 'ENTER', |
+ 37: 'ARROW_LEFT', |
+ 38: 'ARROW_UP', |
+ 39: 'ARROW_RIGHT', |
+ 40: 'ARROW_DOWN' |
+ }; |
+ var cmdName = code_to_command_names[e.keyCode]; |
+ if (cmdName === undefined) |
+ return; |
+ |
+ e.stopPropagation(); |
+ e.preventDefault(); |
+ this.performKeyCommand_(cmdName); |
+ }, |
+ |
+ performKeyCommand_: function(cmdName) { |
+ this.rebuildIfNeeded_(); |
+ |
+ var rowInfo = this.selectedTableRowInfo_; |
+ var htmlNode = rowInfo.htmlNode; |
+ if (cmdName === 'ARROW_UP') { |
+ var prev = htmlNode.previousElementSibling; |
+ if (prev) { |
+ tr.ui.b.scrollIntoViewIfNeeded(prev); |
+ this.selectedTableRow = prev.rowInfo.userRow; |
+ this.focusSelected_(); |
return; |
+ } |
+ return; |
+ } |
- var code_to_command_names = { |
- 13: 'ENTER', |
- 37: 'ARROW_LEFT', |
- 38: 'ARROW_UP', |
- 39: 'ARROW_RIGHT', |
- 40: 'ARROW_DOWN' |
- }; |
- var cmdName = code_to_command_names[e.keyCode]; |
- if (cmdName === undefined) |
+ if (cmdName === 'ARROW_DOWN') { |
+ var next = htmlNode.nextElementSibling; |
+ if (next) { |
+ tr.ui.b.scrollIntoViewIfNeeded(next); |
+ this.selectedTableRow = next.rowInfo.userRow; |
+ this.focusSelected_(); |
return; |
+ } |
+ return; |
+ } |
+ |
+ if (cmdName === 'ARROW_RIGHT') { |
+ switch (this.selectionMode_) { |
+ case SelectionMode.ROW: |
+ if (rowInfo.userRow[this.subRowsPropertyName_] === undefined) |
+ return; |
+ if (rowInfo.userRow[this.subRowsPropertyName_].length === 0) |
+ return; |
- e.stopPropagation(); |
- e.preventDefault(); |
- this.performKeyCommand_(cmdName); |
- }, |
- |
- performKeyCommand_: function(cmdName) { |
- this.rebuildIfNeeded_(); |
- |
- var rowInfo = this.selectedTableRowInfo_; |
- var htmlNode = rowInfo.htmlNode; |
- if (cmdName === 'ARROW_UP') { |
- var prev = htmlNode.previousElementSibling; |
- if (prev) { |
- tr.ui.b.scrollIntoViewIfNeeded(prev); |
- this.selectedTableRow = prev.rowInfo.userRow; |
+ if (!rowInfo.isExpanded) |
+ this.setExpandedForTableRow(rowInfo.userRow, true); |
+ this.selectedTableRow = |
+ htmlNode.nextElementSibling.rowInfo.userRow; |
this.focusSelected_(); |
return; |
- } |
- return; |
- } |
- if (cmdName === 'ARROW_DOWN') { |
- var next = htmlNode.nextElementSibling; |
- if (next) { |
- tr.ui.b.scrollIntoViewIfNeeded(next); |
- this.selectedTableRow = next.rowInfo.userRow; |
+ case SelectionMode.CELL: |
+ var newIndex = this.selectedColumnIndex_ + 1; |
+ if (newIndex >= this.tableColumns_.length) |
+ return; |
+ if (!this.doesColumnIndexSupportSelection(newIndex)) |
+ return; |
+ this.selectedColumnIndex = newIndex; |
this.focusSelected_(); |
return; |
- } |
- return; |
+ |
+ default: |
+ throw new Error('Invalid selection mode ' + this.selectionMode_); |
} |
+ } |
- if (cmdName === 'ARROW_RIGHT') { |
- switch (this.selectionMode_) { |
- case SelectionMode.ROW: |
- if (rowInfo.userRow[this.subRowsPropertyName_] === undefined) |
- return; |
- if (rowInfo.userRow[this.subRowsPropertyName_].length === 0) |
- return; |
- |
- if (!rowInfo.isExpanded) |
- this.setExpandedForTableRow(rowInfo.userRow, true); |
- this.selectedTableRow = |
- Polymer.dom(htmlNode).nextElementSibling.rowInfo.userRow; |
+ if (cmdName === 'ARROW_LEFT') { |
+ switch (this.selectionMode_) { |
+ case SelectionMode.ROW: |
+ if (rowInfo.isExpanded) { |
+ this.setExpandedForTableRow(rowInfo.userRow, false); |
this.focusSelected_(); |
return; |
+ } |
- case SelectionMode.CELL: |
- var newIndex = this.selectedColumnIndex_ + 1; |
- if (newIndex >= this.tableColumns_.length) |
- return; |
- if (!this.doesColumnIndexSupportSelection(newIndex)) |
- return; |
- this.selectedColumnIndex = newIndex; |
+ // Not expanded. Select parent... |
+ var parentRowInfo = rowInfo.parentRowInfo; |
+ if (parentRowInfo) { |
+ this.selectedTableRow = parentRowInfo.userRow; |
this.focusSelected_(); |
return; |
+ } |
+ return; |
- default: |
- throw new Error('Invalid selection mode ' + this.selectionMode_); |
- } |
- } |
- |
- if (cmdName === 'ARROW_LEFT') { |
- switch (this.selectionMode_) { |
- case SelectionMode.ROW: |
- if (rowInfo.isExpanded) { |
- this.setExpandedForTableRow(rowInfo.userRow, false); |
- this.focusSelected_(); |
- return; |
- } |
- |
- // Not expanded. Select parent... |
- var parentRowInfo = rowInfo.parentRowInfo; |
- if (parentRowInfo) { |
- this.selectedTableRow = parentRowInfo.userRow; |
- this.focusSelected_(); |
- return; |
- } |
+ case SelectionMode.CELL: |
+ var newIndex = this.selectedColumnIndex_ - 1; |
+ if (newIndex < 0) |
return; |
- |
- case SelectionMode.CELL: |
- var newIndex = this.selectedColumnIndex_ - 1; |
- if (newIndex < 0) |
- return; |
- if (!this.doesColumnIndexSupportSelection(newIndex)) |
- return; |
- this.selectedColumnIndex = newIndex; |
- this.focusSelected_(); |
+ if (!this.doesColumnIndexSupportSelection(newIndex)) |
return; |
+ this.selectedColumnIndex = newIndex; |
+ this.focusSelected_(); |
+ return; |
- default: |
- throw new Error('Invalid selection mode ' + this.selectionMode_); |
- } |
+ default: |
+ throw new Error('Invalid selection mode ' + this.selectionMode_); |
} |
+ } |
- if (cmdName === 'ENTER') { |
- if (rowInfo.userRow[this.subRowsPropertyName_] === undefined) |
- return; |
- if (rowInfo.userRow[this.subRowsPropertyName_].length === 0) |
- return; |
- this.setExpandedForTableRow(rowInfo.userRow, !rowInfo.isExpanded); |
- this.focusSelected_(); |
+ if (cmdName === 'ENTER') { |
+ if (rowInfo.userRow[this.subRowsPropertyName_] === undefined) |
return; |
- } |
+ if (rowInfo.userRow[this.subRowsPropertyName_].length === 0) |
+ return; |
+ this.setExpandedForTableRow(rowInfo.userRow, !rowInfo.isExpanded); |
+ this.focusSelected_(); |
+ return; |
+ } |
- throw new Error('Unrecognized command ' + cmdName); |
- }, |
+ throw new Error('Unrecognized command ' + cmdName); |
+ }, |
+ |
+ focusSelected_: function() { |
+ if (!this.selectedTableRowInfo_) |
+ return; |
+ var node = this.getSelectableNodeGivenTableRowNode_( |
+ this.selectedTableRowInfo_.htmlNode); |
+ node.focus(); |
+ }, |
+ |
+ dispatchSortingChangedEvent_: function() { |
+ var e = new tr.b.Event('sort-column-changed'); |
+ e.sortColumnIndex = this.sortColumnIndex_; |
+ e.sortDescending = this.sortDescending_; |
+ this.dispatchEvent(e); |
+ } |
+ }); |
+})(); |
+</script> |
- focusSelected_: function() { |
- if (!this.selectedTableRowInfo_) |
- return; |
- var node = this.getSelectableNodeGivenTableRowNode_( |
- this.selectedTableRowInfo_.htmlNode); |
- node.focus(); |
- }, |
- |
- dispatchSortingChangedEvent_: function() { |
- var e = new tr.b.Event('sort-column-changed'); |
- e.sortColumnIndex = this.sortColumnIndex_; |
- e.sortDescending = this.sortDescending_; |
- this.dispatchEvent(e); |
- } |
- }); |
- })(); |
- </script> |
- |
-<dom-module id='tr-ui-b-table-header-cell'> |
+<dom-module id="tr-ui-b-table-header-cell"> |
<template> |
<style> |
:host { |
@@ -1387,22 +1495,38 @@ tr.exportTo('tr.ui.b', function() { |
flex: 0 1 auto; |
} |
- side-element { |
+ #side { |
-webkit-user-select: none; |
flex: 0 0 auto; |
- padding-left: 4px; |
+ padding-left: 2px; |
+ padding-right: 2px; |
vertical-align: top; |
font-size: 15px; |
font-family: sans-serif; |
display: inline; |
line-height: 85%; |
+ margin-left: 5px; |
+ } |
+ |
+ #button { |
+ font-weight: bold; |
+ font-size: 12px; |
+ } |
+ |
+ #title:empty, #button:empty, #side:empty { |
+ display: none; |
+ } |
+ |
+ #button[selected] { |
+ background: darkgrey; |
} |
</style> |
- <span id="title"></span><side-element id="side"></side-element> |
+ <span id="title"></span> |
+ <button id="button"></button> |
+ <button id="side"></button> |
</template> |
</dom-module> |
- |
<script> |
'use strict'; |
@@ -1411,14 +1535,59 @@ var ColumnAlignment = tr.ui.b.TableFormat.ColumnAlignment; |
Polymer({ |
is: 'tr-ui-b-table-header-cell', |
- listeners: { |
- 'tap': 'onTap_' |
- }, |
- |
created: function() { |
this.tapCallback_ = undefined; |
this.cellTitle_ = ''; |
this.align_ = undefined; |
+ this.selectable_ = false; |
+ this.column_ = undefined; |
+ }, |
+ |
+ ready: function() { |
+ this.$.button.addEventListener('click', this.onSelect_.bind(this)); |
+ this.$.side.addEventListener('click', this.onTap_.bind(this)); |
+ }, |
+ |
+ onSelect_: function() { |
+ this.selected = this.$.button.getAttribute('selected') !== 'true'; |
+ var e = new tr.b.Event('selected-column-changed'); |
+ e.column = this.column; |
+ e.selected = this.selected; |
+ this.dispatchEvent(e); |
+ }, |
+ |
+ set column(column) { |
+ this.column_ = column; |
+ this.selectable = column.selectable; |
+ this.align = column.align; |
+ this.cellTitle = column.title; |
+ }, |
+ |
+ get column() { |
+ return this.column_; |
+ }, |
+ |
+ set selectable(selectable) { |
+ this.selectable_ = selectable; |
+ this.cellTitle = this.$.title.childNodes[0] || |
+ this.$.button.childNodes[0]; |
+ }, |
+ |
+ get selectable() { |
+ return this.selectable_; |
+ }, |
+ |
+ set selected(selected) { |
+ if (selected) |
+ this.$.button.setAttribute('selected', true); |
+ else |
+ this.$.button.removeAttribute('selected'); |
+ }, |
+ |
+ get selected() { |
+ if (!this.selectable_) |
+ return false; |
+ return this.$.button.getAttribute('selected') === 'true'; |
}, |
set cellTitle(value) { |
@@ -1428,7 +1597,12 @@ Polymer({ |
this.cellTitle_, this.ownerDocument); |
this.$.title.innerText = ''; |
- Polymer.dom(this.$.title).appendChild(titleNode); |
+ this.$.button.innerText = ''; |
+ |
+ if (this.selectable_) |
+ this.$.button.appendChild(titleNode); |
+ else |
+ this.$.title.appendChild(titleNode); |
}, |
get cellTitle() { |
@@ -1458,15 +1632,16 @@ Polymer({ |
}, |
clearSideContent: function() { |
- Polymer.dom(this.$.side).textContent = ''; |
+ this.$.side.textContent = ''; |
}, |
set sideContent(content) { |
- Polymer.dom(this.$.side).textContent = content; |
+ this.$.side.textContent = content; |
+ this.$.side.style.display = content ? 'inline' : 'none'; |
}, |
get sideContent() { |
- return Polymer.dom(this.$.side).textContent; |
+ return this.$.side.textContent; |
}, |
set tapCallback(callback) { |