| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview This implements a table control. | 6 * @fileoverview This implements a table control. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 cr.define('cr.ui', function() { | 9 cr.define('cr.ui', function() { |
| 10 /** @const */ var ListSelectionModel = cr.ui.ListSelectionModel; | 10 /** @const */ var ListSelectionModel = cr.ui.ListSelectionModel; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 Table.prototype = { | 25 Table.prototype = { |
| 26 __proto__: HTMLDivElement.prototype, | 26 __proto__: HTMLDivElement.prototype, |
| 27 | 27 |
| 28 columnModel_: new TableColumnModel([]), | 28 columnModel_: new TableColumnModel([]), |
| 29 | 29 |
| 30 /** | 30 /** |
| 31 * The table data model. | 31 * The table data model. |
| 32 * | 32 * |
| 33 * @type {cr.ui.ArrayDataModel} | 33 * @type {cr.ui.ArrayDataModel} |
| 34 */ | 34 */ |
| 35 get dataModel() { | 35 get dataModel() { return this.list_.dataModel; }, |
| 36 return this.list_.dataModel; | |
| 37 }, | |
| 38 set dataModel(dataModel) { | 36 set dataModel(dataModel) { |
| 39 if (this.list_.dataModel != dataModel) { | 37 if (this.list_.dataModel != dataModel) { |
| 40 if (this.list_.dataModel) { | 38 if (this.list_.dataModel) { |
| 41 this.list_.dataModel.removeEventListener('sorted', | 39 this.list_.dataModel.removeEventListener( |
| 42 this.boundHandleSorted_); | 40 'sorted', this.boundHandleSorted_); |
| 43 this.list_.dataModel.removeEventListener('change', | 41 this.list_.dataModel.removeEventListener( |
| 44 this.boundHandleChangeList_); | 42 'change', this.boundHandleChangeList_); |
| 45 this.list_.dataModel.removeEventListener('splice', | 43 this.list_.dataModel.removeEventListener( |
| 46 this.boundHandleChangeList_); | 44 'splice', this.boundHandleChangeList_); |
| 47 } | 45 } |
| 48 this.list_.dataModel = dataModel; | 46 this.list_.dataModel = dataModel; |
| 49 if (this.list_.dataModel) { | 47 if (this.list_.dataModel) { |
| 50 this.list_.dataModel.addEventListener('sorted', | 48 this.list_.dataModel.addEventListener( |
| 51 this.boundHandleSorted_); | 49 'sorted', this.boundHandleSorted_); |
| 52 this.list_.dataModel.addEventListener('change', | 50 this.list_.dataModel.addEventListener( |
| 53 this.boundHandleChangeList_); | 51 'change', this.boundHandleChangeList_); |
| 54 this.list_.dataModel.addEventListener('splice', | 52 this.list_.dataModel.addEventListener( |
| 55 this.boundHandleChangeList_); | 53 'splice', this.boundHandleChangeList_); |
| 56 } | 54 } |
| 57 this.header_.redraw(); | 55 this.header_.redraw(); |
| 58 } | 56 } |
| 59 }, | 57 }, |
| 60 | 58 |
| 61 /** | 59 /** |
| 62 * The list of table. | 60 * The list of table. |
| 63 * | 61 * |
| 64 * @type {cr.ui.List} | 62 * @type {cr.ui.List} |
| 65 */ | 63 */ |
| 66 get list() { | 64 get list() { return this.list_; }, |
| 67 return this.list_; | |
| 68 }, | |
| 69 | 65 |
| 70 /** | 66 /** |
| 71 * The table column model. | 67 * The table column model. |
| 72 * | 68 * |
| 73 * @type {cr.ui.table.TableColumnModel} | 69 * @type {cr.ui.table.TableColumnModel} |
| 74 */ | 70 */ |
| 75 get columnModel() { | 71 get columnModel() { return this.columnModel_; }, |
| 76 return this.columnModel_; | |
| 77 }, | |
| 78 set columnModel(columnModel) { | 72 set columnModel(columnModel) { |
| 79 if (this.columnModel_ != columnModel) { | 73 if (this.columnModel_ != columnModel) { |
| 80 if (this.columnModel_) | 74 if (this.columnModel_) |
| 81 this.columnModel_.removeEventListener('resize', this.boundResize_); | 75 this.columnModel_.removeEventListener('resize', this.boundResize_); |
| 82 this.columnModel_ = columnModel; | 76 this.columnModel_ = columnModel; |
| 83 | 77 |
| 84 if (this.columnModel_) | 78 if (this.columnModel_) |
| 85 this.columnModel_.addEventListener('resize', this.boundResize_); | 79 this.columnModel_.addEventListener('resize', this.boundResize_); |
| 86 this.list_.invalidate(); | 80 this.list_.invalidate(); |
| 87 this.redraw(); | 81 this.redraw(); |
| 88 } | 82 } |
| 89 }, | 83 }, |
| 90 | 84 |
| 91 /** | 85 /** |
| 92 * The table selection model. | 86 * The table selection model. |
| 93 * | 87 * |
| 94 * @type | 88 * @type |
| 95 * {cr.ui.ListSelectionModel|cr.ui.ListSingleSelectionModel} | 89 * {cr.ui.ListSelectionModel|cr.ui.ListSingleSelectionModel} |
| 96 */ | 90 */ |
| 97 get selectionModel() { | 91 get selectionModel() { return this.list_.selectionModel; }, |
| 98 return this.list_.selectionModel; | |
| 99 }, | |
| 100 set selectionModel(selectionModel) { | 92 set selectionModel(selectionModel) { |
| 101 if (this.list_.selectionModel != selectionModel) { | 93 if (this.list_.selectionModel != selectionModel) { |
| 102 if (this.dataModel) | 94 if (this.dataModel) |
| 103 selectionModel.adjustLength(this.dataModel.length); | 95 selectionModel.adjustLength(this.dataModel.length); |
| 104 this.list_.selectionModel = selectionModel; | 96 this.list_.selectionModel = selectionModel; |
| 105 } | 97 } |
| 106 }, | 98 }, |
| 107 | 99 |
| 108 /** | 100 /** |
| 109 * The accessor to "autoExpands" property of the list. | 101 * The accessor to "autoExpands" property of the list. |
| 110 * | 102 * |
| 111 * @type {boolean} | 103 * @type {boolean} |
| 112 */ | 104 */ |
| 113 get autoExpands() { | 105 get autoExpands() { return this.list_.autoExpands; }, |
| 114 return this.list_.autoExpands; | 106 set autoExpands(autoExpands) { this.list_.autoExpands = autoExpands; }, |
| 115 }, | |
| 116 set autoExpands(autoExpands) { | |
| 117 this.list_.autoExpands = autoExpands; | |
| 118 }, | |
| 119 | 107 |
| 120 get fixedHeight() { | 108 get fixedHeight() { return this.list_.fixedHeight; }, |
| 121 return this.list_.fixedHeight; | 109 set fixedHeight(fixedHeight) { this.list_.fixedHeight = fixedHeight; }, |
| 122 }, | |
| 123 set fixedHeight(fixedHeight) { | |
| 124 this.list_.fixedHeight = fixedHeight; | |
| 125 }, | |
| 126 | 110 |
| 127 /** | 111 /** |
| 128 * Returns render function for row. | 112 * Returns render function for row. |
| 129 * @return {function(*, cr.ui.Table): HTMLElement} Render function. | 113 * @return {function(*, cr.ui.Table): HTMLElement} Render function. |
| 130 */ | 114 */ |
| 131 getRenderFunction: function() { | 115 getRenderFunction: function() { return this.list_.renderFunction_; }, |
| 132 return this.list_.renderFunction_; | |
| 133 }, | |
| 134 | 116 |
| 135 /** | 117 /** |
| 136 * Sets render function for row. | 118 * Sets render function for row. |
| 137 * @param {function(*, cr.ui.Table): HTMLElement} renderFunction Render | 119 * @param {function(*, cr.ui.Table): HTMLElement} renderFunction Render |
| 138 * function. | 120 * function. |
| 139 */ | 121 */ |
| 140 setRenderFunction: function(renderFunction) { | 122 setRenderFunction: function(renderFunction) { |
| 141 if (renderFunction === this.list_.renderFunction_) | 123 if (renderFunction === this.list_.renderFunction_) |
| 142 return; | 124 return; |
| 143 | 125 |
| 144 this.list_.renderFunction_ = renderFunction; | 126 this.list_.renderFunction_ = renderFunction; |
| 145 cr.dispatchSimpleEvent(this, 'change'); | 127 cr.dispatchSimpleEvent(this, 'change'); |
| 146 }, | 128 }, |
| 147 | 129 |
| 148 /** | 130 /** |
| 149 * The header of the table. | 131 * The header of the table. |
| 150 * | 132 * |
| 151 * @type {cr.ui.table.TableColumnModel} | 133 * @type {cr.ui.table.TableColumnModel} |
| 152 */ | 134 */ |
| 153 get header() { | 135 get header() { return this.header_; }, |
| 154 return this.header_; | |
| 155 }, | |
| 156 | 136 |
| 157 /** | 137 /** |
| 158 * Initializes the element. | 138 * Initializes the element. |
| 159 */ | 139 */ |
| 160 decorate: function() { | 140 decorate: function() { |
| 161 this.header_ = this.ownerDocument.createElement('div'); | 141 this.header_ = this.ownerDocument.createElement('div'); |
| 162 this.list_ = this.ownerDocument.createElement('list'); | 142 this.list_ = this.ownerDocument.createElement('list'); |
| 163 | 143 |
| 164 this.appendChild(this.header_); | 144 this.appendChild(this.header_); |
| 165 this.appendChild(this.list_); | 145 this.appendChild(this.list_); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 212 resize: function() { | 192 resize: function() { |
| 213 // We resize columns only instead of full redraw. | 193 // We resize columns only instead of full redraw. |
| 214 this.list_.resize(); | 194 this.list_.resize(); |
| 215 this.header_.resize(); | 195 this.header_.resize(); |
| 216 }, | 196 }, |
| 217 | 197 |
| 218 /** | 198 /** |
| 219 * Ensures that a given index is inside the viewport. | 199 * Ensures that a given index is inside the viewport. |
| 220 * @param {number} i The index of the item to scroll into view. | 200 * @param {number} i The index of the item to scroll into view. |
| 221 */ | 201 */ |
| 222 scrollIndexIntoView: function(i) { | 202 scrollIndexIntoView: function(i) { this.list_.scrollIndexIntoView(i); }, |
| 223 this.list_.scrollIndexIntoView(i); | |
| 224 }, | |
| 225 | 203 |
| 226 /** | 204 /** |
| 227 * Find the list item element at the given index. | 205 * Find the list item element at the given index. |
| 228 * @param {number} index The index of the list item to get. | 206 * @param {number} index The index of the list item to get. |
| 229 * @return {cr.ui.ListItem} The found list item or null if not found. | 207 * @return {cr.ui.ListItem} The found list item or null if not found. |
| 230 */ | 208 */ |
| 231 getListItemByIndex: function(index) { | 209 getListItemByIndex: function(index) { |
| 232 return this.list_.getListItemByIndex(index); | 210 return this.list_.getListItemByIndex(index); |
| 233 }, | 211 }, |
| 234 | 212 |
| 235 /** | 213 /** |
| 236 * This handles data model 'sorted' event. | 214 * This handles data model 'sorted' event. |
| 237 * After sorting we need to redraw header | 215 * After sorting we need to redraw header |
| 238 * @param {Event} e The 'sorted' event. | 216 * @param {Event} e The 'sorted' event. |
| 239 */ | 217 */ |
| 240 handleSorted_: function(e) { | 218 handleSorted_: function(e) { this.header_.redraw(); }, |
| 241 this.header_.redraw(); | |
| 242 }, | |
| 243 | 219 |
| 244 /** | 220 /** |
| 245 * This handles data model 'change' and 'splice' events. | 221 * This handles data model 'change' and 'splice' events. |
| 246 * Since they may change the visibility of scrollbar, table may need to | 222 * Since they may change the visibility of scrollbar, table may need to |
| 247 * re-calculation the width of column headers. | 223 * re-calculation the width of column headers. |
| 248 * @param {Event} e The 'change' or 'splice' event. | 224 * @param {Event} e The 'change' or 'splice' event. |
| 249 */ | 225 */ |
| 250 handleChangeList_: function(e) { | 226 handleChangeList_: function(e) { |
| 251 requestAnimationFrame(this.header_.updateWidth.bind(this.header_)); | 227 requestAnimationFrame(this.header_.updateWidth.bind(this.header_)); |
| 252 }, | 228 }, |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 335 var container = doc.createElement('li'); | 311 var container = doc.createElement('li'); |
| 336 container.style.display = 'inline-block'; | 312 container.style.display = 'inline-block'; |
| 337 container.style.textAlign = 'start'; | 313 container.style.textAlign = 'start'; |
| 338 // The container will have width of the longest cell. | 314 // The container will have width of the longest cell. |
| 339 container.style.webkitBoxOrient = 'vertical'; | 315 container.style.webkitBoxOrient = 'vertical'; |
| 340 | 316 |
| 341 // Ensure all needed data available. | 317 // Ensure all needed data available. |
| 342 dm.prepareSort(columnId, function() { | 318 dm.prepareSort(columnId, function() { |
| 343 // Select at most MAXIMUM_ROWS_TO_MEASURE items around visible area. | 319 // Select at most MAXIMUM_ROWS_TO_MEASURE items around visible area. |
| 344 var items = list.getItemsInViewPort(list.scrollTop, listHeight); | 320 var items = list.getItemsInViewPort(list.scrollTop, listHeight); |
| 345 var firstIndex = Math.floor(Math.max(0, | 321 var firstIndex = Math.floor(Math.max( |
| 346 (items.last + items.first - MAXIMUM_ROWS_TO_MEASURE) / 2)); | 322 0, (items.last + items.first - MAXIMUM_ROWS_TO_MEASURE) / 2)); |
| 347 var lastIndex = Math.min(dm.length, | 323 var lastIndex = |
| 348 firstIndex + MAXIMUM_ROWS_TO_MEASURE); | 324 Math.min(dm.length, firstIndex + MAXIMUM_ROWS_TO_MEASURE); |
| 349 for (var i = firstIndex; i < lastIndex; i++) { | 325 for (var i = firstIndex; i < lastIndex; i++) { |
| 350 var item = dm.item(i); | 326 var item = dm.item(i); |
| 351 var div = doc.createElement('div'); | 327 var div = doc.createElement('div'); |
| 352 div.className = 'table-row-cell'; | 328 div.className = 'table-row-cell'; |
| 353 div.appendChild(render(item, columnId, table)); | 329 div.appendChild(render(item, columnId, table)); |
| 354 container.appendChild(div); | 330 container.appendChild(div); |
| 355 } | 331 } |
| 356 list.appendChild(container); | 332 list.appendChild(container); |
| 357 var width = parseFloat(window.getComputedStyle(container).width); | 333 var width = parseFloat(window.getComputedStyle(container).width); |
| 358 list.removeChild(container); | 334 list.removeChild(container); |
| 359 cm.setWidth(index, width); | 335 cm.setWidth(index, width); |
| 360 }); | 336 }); |
| 361 }, | 337 }, |
| 362 | 338 |
| 363 normalizeColumns: function() { | 339 normalizeColumns: function() { |
| 364 this.columnModel.normalizeWidths(this.clientWidth); | 340 this.columnModel.normalizeWidths(this.clientWidth); |
| 365 } | 341 } |
| 366 }; | 342 }; |
| 367 | 343 |
| 368 /** | 344 /** |
| 369 * Whether the table or one of its descendents has focus. This is necessary | 345 * Whether the table or one of its descendents has focus. This is necessary |
| 370 * because table contents can contain controls that can be focused, and for | 346 * because table contents can contain controls that can be focused, and for |
| 371 * some purposes (e.g., styling), the table can still be conceptually focused | 347 * some purposes (e.g., styling), the table can still be conceptually focused |
| 372 * at that point even though it doesn't actually have the page focus. | 348 * at that point even though it doesn't actually have the page focus. |
| 373 */ | 349 */ |
| 374 cr.defineProperty(Table, 'hasElementFocus', cr.PropertyKind.BOOL_ATTR); | 350 cr.defineProperty(Table, 'hasElementFocus', cr.PropertyKind.BOOL_ATTR); |
| 375 | 351 |
| 376 return { | 352 return {Table: Table}; |
| 377 Table: Table | |
| 378 }; | |
| 379 }); | 353 }); |
| OLD | NEW |