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.
6 * @fileoverview This implements a table header.
9 cr.define('cr.ui.table', function() {
10 /** @const */ var TableSplitter = cr.ui.TableSplitter;
13 * Creates a new table header.
14 * @param {Object=} opt_propertyBag Optional properties.
16 * @extends {HTMLDivElement}
18 var TableHeader = cr.ui.define('div');
20 TableHeader.prototype = {
21 __proto__: HTMLDivElement.prototype,
26 * Initializes the element.
28 decorate: function() {
29 this.className = 'table-header';
31 this.headerInner_ = this.ownerDocument.createElement('div');
32 this.headerInner_.className = 'table-header-inner';
33 this.appendChild(this.headerInner_);
34 this.addEventListener('touchstart',
35 this.handleTouchStart_.bind(this), false);
39 * Updates table header width. Header width depends on list having a
42 updateWidth: function() {
43 // Header should not span over the vertical scrollbar of the list.
44 var list = this.table_.querySelector('list');
45 this.headerInner_.style.width = list.clientWidth + 'px';
52 var cm = this.table_.columnModel;
54 var headerCells = this.querySelectorAll('.table-header-cell');
55 if (headerCells.length != cm.size) {
60 for (var i = 0; i < cm.size; i++) {
61 headerCells[i].style.width = cm.getWidth(i) + 'px';
63 this.placeSplitters_(this.querySelectorAll('.table-header-splitter'));
68 startBatchUpdates: function() {
72 endBatchUpdates: function() {
74 if (this.batchCount_ == 0)
79 * Redraws table header.
82 if (this.batchCount_ != 0)
85 var cm = this.table_.columnModel;
86 var dm = this.table_.dataModel;
89 this.headerInner_.textContent = '';
95 for (var i = 0; i < cm.size; i++) {
96 var cell = this.ownerDocument.createElement('div');
97 cell.style.width = cm.getWidth(i) + 'px';
98 cell.className = 'table-header-cell';
99 if (dm.isSortable(cm.getId(i)))
100 cell.addEventListener('click',
101 this.createSortFunction_(i).bind(this));
103 cell.appendChild(this.createHeaderLabel_(i));
104 this.headerInner_.appendChild(cell);
106 this.appendSplitters_();
110 * Appends column splitters to the table header.
112 appendSplitters_: function() {
113 var cm = this.table_.columnModel;
115 for (var i = 0; i < cm.size; i++) {
116 // splitter should use CSS for background image.
117 var splitter = new TableSplitter({table: this.table_});
118 splitter.columnIndex = i;
119 splitter.addEventListener('dblclick',
120 this.handleDblClick_.bind(this, i));
122 this.headerInner_.appendChild(splitter);
123 splitters.push(splitter);
125 this.placeSplitters_(splitters);
129 * Place splitters to right positions.
130 * @param {Array.<HTMLElement>|NodeList} splitters Array of splitters.
132 placeSplitters_: function(splitters) {
133 var cm = this.table_.columnModel;
135 for (var i = 0; i < cm.size; i++) {
136 place += cm.getWidth(i);
137 splitters[i].style.webkitMarginStart = place + 'px';
142 * Renders column header. Appends text label and sort arrow if needed.
143 * @param {number} index Column index.
145 createHeaderLabel_: function(index) {
146 var cm = this.table_.columnModel;
147 var dm = this.table_.dataModel;
149 var labelDiv = this.ownerDocument.createElement('div');
150 labelDiv.className = 'table-header-label';
152 if (cm.isEndAlign(index))
153 labelDiv.style.textAlign = 'end';
154 var span = this.ownerDocument.createElement('span');
155 span.appendChild(cm.renderHeader(index, this.table_));
156 span.style.padding = '0';
159 if (dm.sortStatus.field == cm.getId(index)) {
160 if (dm.sortStatus.direction == 'desc')
161 span.className = 'table-header-sort-image-desc';
163 span.className = 'table-header-sort-image-asc';
166 labelDiv.appendChild(span);
171 * Creates sort function for given column.
172 * @param {number} index The index of the column to sort by.
174 createSortFunction_: function(index) {
176 this.table_.sort(index);
181 * Handles the touchstart event. If the touch happened close enough
182 * to a splitter starts dragging.
183 * @param {Event} e The touch event.
185 handleTouchStart_: function(e) {
186 e = /** @type {TouchEvent} */ (e);
187 if (e.touches.length != 1)
189 var clientX = e.touches[0].clientX;
191 var minDistance = TableHeader.TOUCH_DRAG_AREA_WIDTH;
194 var splitters = this.querySelectorAll('.table-header-splitter');
195 for (var i = 0; i < splitters.length; i++) {
196 var r = splitters[i].getBoundingClientRect();
197 if (clientX <= r.left && r.left - clientX <= minDistance) {
198 minDistance = r.left - clientX;
199 candidate = splitters[i];
201 if (clientX >= r.right && clientX - r.right <= minDistance) {
202 minDistance = clientX - r.right;
203 candidate = splitters[i];
207 candidate.startDrag(clientX, true);
208 // Splitter itself shouldn't handle this event.
213 * Handles the double click on a column separator event.
214 * Ajusts column width.
215 * @param {number} index Column index.
216 * @param {Event} e The double click event.
218 handleDblClick_: function(index, e) {
219 this.table_.fitColumn(index);
224 * The table associated with the header.
225 * @type {cr.ui.Table}
227 cr.defineProperty(TableHeader, 'table');
230 * Rectangular area around the splitters sensitive to touch events
233 TableHeader.TOUCH_DRAG_AREA_WIDTH = 30;
236 TableHeader: TableHeader