1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5/** 6 * @fileoverview This extends cr.ui.List for use in the table. 7 */ 8 9cr.define('cr.ui.table', function() { 10 /** @const */ var List = cr.ui.List; 11 /** @const */ var TableRow = cr.ui.table.TableRow; 12 /** @const */ var ListItem = cr.ui.ListItem; 13 14 /** 15 * Creates a new table list element. 16 * @param {Object=} opt_propertyBag Optional properties. 17 * @constructor 18 * @extends {cr.ui.List} 19 */ 20 var TableList = cr.ui.define('list'); 21 22 TableList.prototype = { 23 __proto__: List.prototype, 24 25 table_: null, 26 27 /** 28 * Initializes the element. 29 */ 30 decorate: function() { 31 List.prototype.decorate.apply(this); 32 this.className = 'list'; 33 }, 34 35 /** 36 * Resizes columns. Called when column width changed. 37 */ 38 resize: function() { 39 if (this.updateScrollbars_()) 40 List.prototype.redraw.call(this); // Redraw items only. 41 this.resizeCells_(); 42 }, 43 44 /** 45 * Updates width of cells. 46 */ 47 resizeCells_: function() { 48 var cm = this.table_.columnModel; 49 for (var row = this.firstElementChild; row; 50 row = row.nextElementSibling) { 51 if (row.tagName != 'LI') 52 continue; 53 54 for (var i = 0; i < cm.size; i++) { 55 row.children[i].style.width = cm.getWidth(i) + 'px'; 56 } 57 row.style.width = cm.totalWidth + 'px'; 58 } 59 this.afterFiller_.style.width = cm.totalWidth + 'px'; 60 }, 61 62 /** 63 * Redraws the viewport. 64 */ 65 redraw: function() { 66 if (this.batchCount_ != 0) 67 return; 68 this.updateScrollbars_(); 69 70 List.prototype.redraw.call(this); 71 this.resizeCells_(); 72 }, 73 74 /** 75 * Returns the height of after filler in the list. 76 * @param {number} lastIndex The index of item past the last in viewport. 77 * @return {number} The height of after filler. 78 * @override 79 */ 80 getAfterFillerHeight: function(lastIndex) { 81 // If the list is empty set height to 1 to show horizontal 82 // scroll bar. 83 return lastIndex == 0 ? 1 : 84 cr.ui.List.prototype.getAfterFillerHeight.call(this, lastIndex); 85 }, 86 87 /** 88 * Shows or hides vertical and horizontal scroll bars in the list. 89 * @return {boolean} True if horizontal scroll bar changed. 90 */ 91 updateScrollbars_: function() { 92 var cm = this.table.columnModel; 93 var style = this.style; 94 if (!cm || cm.size == 0) { 95 if (style.overflow != 'hidden') { 96 style.overflow = 'hidden'; 97 return true; 98 } else { 99 return false; 100 } 101 } 102 103 var height = this.offsetHeight; 104 var changed = false; 105 var offsetWidth = this.offsetWidth; 106 if (cm.totalWidth > offsetWidth) { 107 if (style.overflowX != 'scroll') { 108 style.overflowX = 'scroll'; 109 } 110 // Once we sure there will be horizontal 111 // scrollbar calculate with this height. 112 height = this.clientHeight; 113 } 114 if (this.areAllItemsVisible_(height)) { 115 if (cm.totalWidth <= offsetWidth && style.overflowX != 'hidden') { 116 style.overflowX = 'hidden'; 117 } 118 changed = this.showVerticalScrollBar_(false); 119 } else { 120 changed = this.showVerticalScrollBar_(true); 121 var x = cm.totalWidth <= this.clientWidth ? 'hidden' : 'scroll'; 122 if (style.overflowX != x) { 123 style.overflowX = x; 124 } 125 } 126 return changed; 127 }, 128 129 /** 130 * Shows or hides vertical scroll bar. 131 * @param {boolean} show True to show. 132 * @return {boolean} True if visibility changed. 133 */ 134 showVerticalScrollBar_: function(show) { 135 var style = this.style; 136 if (show && style.overflowY == 'scroll') 137 return false; 138 if (!show && style.overflowY == 'hidden') 139 return false; 140 style.overflowY = show ? 'scroll' : 'hidden'; 141 return true; 142 }, 143 144 /** 145 * @param {number} visibleHeight Height in pixels. 146 * @return {boolean} True if all rows could be accomodiated in 147 * visibleHeight pixels. 148 */ 149 areAllItemsVisible_: function(visibleHeight) { 150 if (!this.dataModel || this.dataModel.length == 0) 151 return true; 152 return this.getItemTop(this.dataModel.length) <= visibleHeight; 153 }, 154 155 /** 156 * Creates a new list item. 157 * @param {*} dataItem The value to use for the item. 158 * @return {!ListItem} The newly created list item. 159 */ 160 createItem: function(dataItem) { 161 return this.table_.getRenderFunction().call(null, dataItem, this.table_); 162 }, 163 164 renderFunction_: function(dataItem, table) { 165 // `This` must not be accessed here, since it may be anything, especially 166 // not a pointer to this object. 167 168 var cm = table.columnModel; 169 var listItem = List.prototype.createItem.call(table.list, ''); 170 listItem.className = 'table-row'; 171 172 for (var i = 0; i < cm.size; i++) { 173 var cell = table.ownerDocument.createElement('div'); 174 cell.style.width = cm.getWidth(i) + 'px'; 175 cell.className = 'table-row-cell'; 176 if (cm.isEndAlign(i)) 177 cell.style.textAlign = 'end'; 178 cell.appendChild( 179 cm.getRenderFunction(i).call(null, dataItem, cm.getId(i), table)); 180 181 listItem.appendChild(cell); 182 } 183 listItem.style.width = cm.totalWidth + 'px'; 184 185 return listItem; 186 }, 187 }; 188 189 /** 190 * The table associated with the list. 191 * @type {cr.ui.Table} 192 */ 193 cr.defineProperty(TableList, 'table'); 194 195 return { 196 TableList: TableList 197 }; 198}); 199