3 * Copyright(c) 2006-2010 Sencha Inc.
5 * http://www.sencha.com/license
8 * @class Ext.grid.PivotGridView
9 * @extends Ext.grid.GridView
10 * Specialised GridView for rendering Pivot Grid components. Config can be passed to the PivotGridView via the PivotGrid constructor's
13 new Ext.grid.PivotGrid({
15 title: 'My Pivot Grid',
16 getCellCls: function(value) {
17 return value > 10 'red' : 'green';
22 * <p>Currently {@link #title} and {@link #getCellCls} are the only configuration options accepted by PivotGridView. All other
23 * interaction is performed via the {@link Ext.grid.PivotGrid PivotGrid} class.</p>
25 Ext.grid.PivotGridView = Ext.extend(Ext.grid.GridView, {
28 * The CSS class added to all group header cells. Defaults to 'grid-hd-group-cell'
29 * @property colHeaderCellCls
32 colHeaderCellCls: 'grid-hd-group-cell',
35 * @cfg {String} title Optional title to be placed in the top left corner of the PivotGrid. Defaults to an empty string.
40 * @cfg {Function} getCellCls Optional function which should return a CSS class name for each cell value. This is useful when
41 * color coding cells based on their value. Defaults to undefined.
45 * Returns the headers to be rendered at the top of the grid. Should be a 2-dimensional array, where each item specifies the number
46 * of columns it groups (column in this case refers to normal grid columns). In the example below we have 5 city groups, which are
47 * each part of a continent supergroup. The colspan for each city group refers to the number of normal grid columns that group spans,
48 * so in this case the grid would be expected to have a total of 12 columns:
53 {header: 'England', colspan: 5},
54 {header: 'USA', colspan: 3}
59 {header: 'London', colspan: 2},
60 {header: 'Cambridge', colspan: 3},
61 {header: 'Palo Alto', colspan: 3}
66 * In the example above we have cities nested under countries. The nesting could be deeper if desired - e.g. Continent -> Country ->
67 * State -> City, or any other structure. The only constaint is that the same depth must be used throughout the structure.
68 * @return {Array} A tree structure containing the headers to be rendered. Must include the colspan property at each level, which should
69 * be the sum of all child nodes beneath this node.
71 getColumnHeaders: function() {
72 return this.grid.topAxis.buildHeaders();;
76 * Returns the headers to be rendered on the left of the grid. Should be a 2-dimensional array, where each item specifies the number
77 * of rows it groups. In the example below we have 5 city groups, which are each part of a continent supergroup. The rowspan for each
78 * city group refers to the number of normal grid columns that group spans, so in this case the grid would be expected to have a
85 {header: 'England', rowspan: 5},
86 {header: 'USA', rowspan: 3}
92 {header: 'London', rowspan: 2},
93 {header: 'Cambridge', rowspan: 3},
94 {header: 'Palo Alto', rowspan: 3}
99 * In the example above we have cities nested under countries. The nesting could be deeper if desired - e.g. Continent -> Country ->
100 * State -> City, or any other structure. The only constaint is that the same depth must be used throughout the structure.
101 * @return {Array} A tree structure containing the headers to be rendered. Must include the colspan property at each level, which should
102 * be the sum of all child nodes beneath this node.
103 * Each group may specify the width it should be rendered with.
104 * @return {Array} The row groups
106 getRowHeaders: function() {
107 return this.grid.leftAxis.buildHeaders();
112 * Renders rows between start and end indexes
113 * @param {Number} startRow Index of the first row to render
114 * @param {Number} endRow Index of the last row to render
116 renderRows : function(startRow, endRow) {
117 var grid = this.grid,
118 rows = grid.extractData(),
119 rowCount = rows.length,
120 templates = this.templates,
121 renderer = grid.renderer,
122 hasRenderer = typeof renderer == 'function',
123 getCellCls = this.getCellCls,
124 hasGetCellCls = typeof getCellCls == 'function',
125 cellTemplate = templates.cell,
126 rowTemplate = templates.row,
129 tstyle = 'width:' + this.getGridInnerWidth() + 'px;',
130 colBuffer, column, i;
132 startRow = startRow || 0;
133 endRow = Ext.isDefined(endRow) ? endRow : rowCount - 1;
135 for (i = 0; i < rowCount; i++) {
137 colCount = row.length;
140 rowIndex = startRow + i;
142 //build up each column's HTML
143 for (j = 0; j < colCount; j++) {
146 meta.css = j === 0 ? 'x-grid3-cell-first ' : (j == (colCount - 1) ? 'x-grid3-cell-last ' : '');
147 meta.attr = meta.cellAttr = '';
150 if (Ext.isEmpty(meta.value)) {
151 meta.value = ' ';
155 meta.value = renderer(meta.value);
159 meta.css += getCellCls(meta.value) + ' ';
162 colBuffer[colBuffer.length] = cellTemplate.apply(meta);
165 rowBuffer[rowBuffer.length] = rowTemplate.apply({
168 cells : colBuffer.join(""),
173 return rowBuffer.join("");
177 * The master template to use when rendering the GridView. Has a default template
178 * @property Ext.Template
181 masterTpl: new Ext.Template(
182 '<div class="x-grid3 x-pivotgrid" hidefocus="true">',
183 '<div class="x-grid3-viewport">',
184 '<div class="x-grid3-header">',
185 '<div class="x-grid3-header-title"><span>{title}</span></div>',
186 '<div class="x-grid3-header-inner">',
187 '<div class="x-grid3-header-offset" style="{ostyle}"></div>',
189 '<div class="x-clear"></div>',
191 '<div class="x-grid3-scroller">',
192 '<div class="x-grid3-row-headers"></div>',
193 '<div class="x-grid3-body" style="{bstyle}">{body}</div>',
194 '<a href="#" class="x-grid3-focus" tabIndex="-1"></a>',
197 '<div class="x-grid3-resize-marker"> </div>',
198 '<div class="x-grid3-resize-proxy"> </div>',
204 * Adds a gcell template to the internal templates object. This is used to render the headers in a multi-level column header.
206 initTemplates: function() {
207 Ext.grid.PivotGridView.superclass.initTemplates.apply(this, arguments);
209 var templates = this.templates || {};
210 if (!templates.gcell) {
211 templates.gcell = new Ext.XTemplate(
212 '<td class="x-grid3-hd x-grid3-gcell x-grid3-td-{id} ux-grid-hd-group-row-{row} ' + this.colHeaderCellCls + '" style="{style}">',
213 '<div {tooltip} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">',
214 this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '', '{value}',
220 this.templates = templates;
221 this.hrowRe = new RegExp("ux-grid-hd-group-row-(\\d+)", "");
226 * Sets up the reference to the row headers element
228 initElements: function() {
229 Ext.grid.PivotGridView.superclass.initElements.apply(this, arguments);
232 * @property rowHeadersEl
234 * The element containing all row headers
236 this.rowHeadersEl = new Ext.Element(this.scroller.child('div.x-grid3-row-headers'));
239 * @property headerTitleEl
241 * The element that contains the optional title (top left section of the pivot grid)
243 this.headerTitleEl = new Ext.Element(this.mainHd.child('div.x-grid3-header-title'));
248 * Takes row headers into account when calculating total available width
250 getGridInnerWidth: function() {
251 var previousWidth = Ext.grid.PivotGridView.superclass.getGridInnerWidth.apply(this, arguments);
253 return previousWidth - this.getTotalRowHeaderWidth();
257 * Returns the total width of all row headers as specified by {@link #getRowHeaders}
258 * @return {Number} The total width
260 getTotalRowHeaderWidth: function() {
261 var headers = this.getRowHeaders(),
262 length = headers.length,
266 for (i = 0; i< length; i++) {
267 total += headers[i].width;
275 * Returns the total height of all column headers
276 * @return {Number} The total height
278 getTotalColumnHeaderHeight: function() {
279 return this.getColumnHeaders().length * 21;
284 * Slight specialisation of the GridView renderUI - just adds the row headers
286 renderUI : function() {
287 var templates = this.templates,
288 innerWidth = this.getGridInnerWidth();
290 return templates.master.apply({
291 body : templates.body.apply({rows:' '}),
292 ostyle: 'width:' + innerWidth + 'px',
293 bstyle: 'width:' + innerWidth + 'px'
299 * Make sure that the headers and rows are all sized correctly during layout
301 onLayout: function(width, height) {
302 Ext.grid.PivotGridView.superclass.onLayout.apply(this, arguments);
304 var width = this.getGridInnerWidth();
306 this.resizeColumnHeaders(width);
307 this.resizeAllRows(width);
311 * Refreshs the grid UI
312 * @param {Boolean} headersToo (optional) True to also refresh the headers
314 refresh : function(headersToo) {
315 this.fireEvent('beforerefresh', this);
316 this.grid.stopEditing(true);
318 var result = this.renderBody();
319 this.mainBody.update(result).setWidth(this.getGridInnerWidth());
320 if (headersToo === true) {
321 this.updateHeaders();
322 this.updateHeaderSortState();
324 this.processRows(0, true);
326 this.applyEmptyText();
327 this.fireEvent('refresh', this);
332 * Bypasses GridView's renderHeaders as they are taken care of separately by the PivotAxis instances
334 renderHeaders: Ext.emptyFn,
338 * Taken care of by PivotAxis
340 fitColumns: Ext.emptyFn,
344 * Called on layout, ensures that the width of each column header is correct. Omitting this can lead to faulty
345 * layouts when nested in a container.
346 * @param {Number} width The new width
348 resizeColumnHeaders: function(width) {
349 var topAxis = this.grid.topAxis;
351 if (topAxis.rendered) {
352 topAxis.el.setWidth(width);
358 * Sets the row header div to the correct width. Should be called after rendering and reconfiguration of headers
360 resizeRowHeaders: function() {
361 var rowHeaderWidth = this.getTotalRowHeaderWidth(),
362 marginStyle = String.format("margin-left: {0}px;", rowHeaderWidth);
364 this.rowHeadersEl.setWidth(rowHeaderWidth);
365 this.mainBody.applyStyles(marginStyle);
366 Ext.fly(this.innerHd).applyStyles(marginStyle);
368 this.headerTitleEl.setWidth(rowHeaderWidth);
369 this.headerTitleEl.setHeight(this.getTotalColumnHeaderHeight());
374 * Resizes all rendered rows to the given width. Usually called by onLayout
375 * @param {Number} width The new width
377 resizeAllRows: function(width) {
378 var rows = this.getRows(),
379 length = rows.length,
382 for (i = 0; i < length; i++) {
383 Ext.fly(rows[i]).setWidth(width);
384 Ext.fly(rows[i]).child('table').setWidth(width);
390 * Updates the Row Headers, deferring the updating of Column Headers to GridView
392 updateHeaders: function() {
393 this.renderGroupRowHeaders();
394 this.renderGroupColumnHeaders();
399 * Renders all row header groups at all levels based on the structure fetched from {@link #getGroupRowHeaders}
401 renderGroupRowHeaders: function() {
402 var leftAxis = this.grid.leftAxis;
404 this.resizeRowHeaders();
405 leftAxis.rendered = false;
406 leftAxis.render(this.rowHeadersEl);
408 this.setTitle(this.title);
412 * Sets the title text in the top left segment of the PivotGridView
413 * @param {String} title The title
415 setTitle: function(title) {
416 this.headerTitleEl.child('span').dom.innerHTML = title;
421 * Renders all column header groups at all levels based on the structure fetched from {@link #getColumnHeaders}
423 renderGroupColumnHeaders: function() {
424 var topAxis = this.grid.topAxis;
426 topAxis.rendered = false;
427 topAxis.render(this.innerHd.firstChild);
432 * Overridden to test whether the user is hovering over a group cell, in which case we don't show the menu
434 isMenuDisabled: function(cellIndex, el) {