X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/b37ceabb82336ee82757cd32efe353cfab8ec267..f5240829880f87e0cf581c6a296e436fdef0ef80:/docs/source/PivotAxis.html diff --git a/docs/source/PivotAxis.html b/docs/source/PivotAxis.html new file mode 100644 index 00000000..6c916aeb --- /dev/null +++ b/docs/source/PivotAxis.html @@ -0,0 +1,330 @@ + + + + The source code + + + + +
/*!
+ * Ext JS Library 3.3.0
+ * Copyright(c) 2006-2010 Ext JS, Inc.
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+
/** + * @class Ext.grid.PivotAxis + * @extends Ext.Component + *

PivotAxis is a class that supports a {@link Ext.grid.PivotGrid}. Each PivotGrid contains two PivotAxis instances - the left + * axis and the top axis. Each PivotAxis defines an ordered set of dimensions, each of which should correspond to a field in a + * Store's Record (see {@link Ext.grid.PivotGrid} documentation for further explanation).

+ *

Developers should have little interaction with the PivotAxis instances directly as most of their management is performed by + * the PivotGrid. An exception is the dynamic reconfiguration of axes at run time - to achieve this we use PivotAxis's + * {@link #setDimensions} function and refresh the grid:

+

+var pivotGrid = new Ext.grid.PivotGrid({
+    //some PivotGrid config here
+});
+
+//change the left axis dimensions
+pivotGrid.leftAxis.setDimensions([
+    {
+        dataIndex: 'person',
+        direction: 'DESC',
+        width    : 100
+    },
+    {
+        dataIndex: 'product',
+        direction: 'ASC',
+        width    : 80
+    }
+]);
+
+pivotGrid.view.refresh(true);
+
+ * This clears the previous dimensions on the axis and redraws the grid with the new dimensions. + */ +Ext.grid.PivotAxis = Ext.extend(Ext.Component, { +
/** + * @cfg {String} orientation One of 'vertical' or 'horizontal'. Defaults to horizontal + */ + orientation: 'horizontal', + +
/** + * @cfg {Number} defaultHeaderWidth The width to render each row header that does not have a width specified via + {@link #getRowGroupHeaders}. Defaults to 80. + */ + defaultHeaderWidth: 80, + + /** + * @private + * @cfg {Number} paddingWidth The amount of padding used by each cell. + * TODO: From 4.x onwards this can be removed as it won't be needed. For now it is used to account for the differences between + * the content box and border box measurement models + */ + paddingWidth: 7, + +
/** + * Updates the dimensions used by this axis + * @param {Array} dimensions The new dimensions + */ + setDimensions: function(dimensions) { + this.dimensions = dimensions; + }, + + /** + * @private + * Builds the html table that contains the dimensions for this axis. This branches internally between vertical + * and horizontal orientations because the table structure is slightly different in each case + */ + onRender: function(ct, position) { + var rows = this.orientation == 'horizontal' + ? this.renderHorizontalRows() + : this.renderVerticalRows(); + + this.el = Ext.DomHelper.overwrite(ct.dom, {tag: 'table', cn: rows}, true); + }, + + /** + * @private + * Specialised renderer for horizontal oriented axes + * @return {Object} The HTML Domspec for a horizontal oriented axis + */ + renderHorizontalRows: function() { + var headers = this.buildHeaders(), + rowCount = headers.length, + rows = [], + cells, cols, colCount, i, j; + + for (i = 0; i < rowCount; i++) { + cells = []; + cols = headers[i].items; + colCount = cols.length; + + for (j = 0; j < colCount; j++) { + cells.push({ + tag: 'td', + html: cols[j].header, + colspan: cols[j].span + }); + } + + rows[i] = { + tag: 'tr', + cn: cells + }; + } + + return rows; + }, + + /** + * @private + * Specialised renderer for vertical oriented axes + * @return {Object} The HTML Domspec for a vertical oriented axis + */ + renderVerticalRows: function() { + var headers = this.buildHeaders(), + colCount = headers.length, + rowCells = [], + rows = [], + rowCount, col, row, colWidth, i, j; + + for (i = 0; i < colCount; i++) { + col = headers[i]; + colWidth = col.width || 80; + rowCount = col.items.length; + + for (j = 0; j < rowCount; j++) { + row = col.items[j]; + + rowCells[row.start] = rowCells[row.start] || []; + rowCells[row.start].push({ + tag : 'td', + html : row.header, + rowspan: row.span, + width : Ext.isBorderBox ? colWidth : colWidth - this.paddingWidth + }); + } + } + + rowCount = rowCells.length; + for (i = 0; i < rowCount; i++) { + rows[i] = { + tag: 'tr', + cn : rowCells[i] + }; + } + + return rows; + }, + + /** + * @private + * Returns the set of all unique tuples based on the bound store and dimension definitions. + * Internally we construct a new, temporary store to make use of the multi-sort capabilities of Store. In + * 4.x this functionality should have been moved to MixedCollection so this step should not be needed. + * @return {Array} All unique tuples + */ + getTuples: function() { + var newStore = new Ext.data.Store({}); + + newStore.data = this.store.data.clone(); + newStore.fields = this.store.fields; + + var sorters = [], + dimensions = this.dimensions, + length = dimensions.length, + i; + + for (i = 0; i < length; i++) { + sorters.push({ + field : dimensions[i].dataIndex, + direction: dimensions[i].direction || 'ASC' + }); + } + + newStore.sort(sorters); + + var records = newStore.data.items, + hashes = [], + tuples = [], + recData, hash, info, data, key; + + length = records.length; + + for (i = 0; i < length; i++) { + info = this.getRecordInfo(records[i]); + data = info.data; + hash = ""; + + for (key in data) { + hash += data[key] + '---'; + } + + if (hashes.indexOf(hash) == -1) { + hashes.push(hash); + tuples.push(info); + } + } + + newStore.destroy(); + + return tuples; + }, + + /** + * @private + */ + getRecordInfo: function(record) { + var dimensions = this.dimensions, + length = dimensions.length, + data = {}, + dimension, dataIndex, i; + + //get an object containing just the data we are interested in based on the configured dimensions + for (i = 0; i < length; i++) { + dimension = dimensions[i]; + dataIndex = dimension.dataIndex; + + data[dataIndex] = record.get(dataIndex); + } + + //creates a specialised matcher function for a given tuple. The returned function will return + //true if the record passed to it matches the dataIndex values of each dimension in this axis + var createMatcherFunction = function(data) { + return function(record) { + for (var dataIndex in data) { + if (record.get(dataIndex) != data[dataIndex]) { + return false; + } + } + + return true; + }; + }; + + return { + data: data, + matcher: createMatcherFunction(data) + }; + }, + + /** + * @private + * Uses the calculated set of tuples to build an array of headers that can be rendered into a table using rowspan or + * colspan. Basically this takes the set of tuples and spans any cells that run into one another, so if we had dimensions + * of Person and Product and several tuples containing different Products for the same Person, those Products would be + * spanned. + * @return {Array} The headers + */ + buildHeaders: function() { + var tuples = this.getTuples(), + rowCount = tuples.length, + dimensions = this.dimensions, + colCount = dimensions.length, + headers = [], + tuple, rows, currentHeader, previousHeader, span, start, isLast, changed, i, j; + + for (i = 0; i < colCount; i++) { + dimension = dimensions[i]; + rows = []; + span = 0; + start = 0; + + for (j = 0; j < rowCount; j++) { + tuple = tuples[j]; + isLast = j == (rowCount - 1); + currentHeader = tuple.data[dimension.dataIndex]; + + /* + * 'changed' indicates that we need to create a new cell. This should be true whenever the cell + * above (previousHeader) is different from this cell, or when the cell on the previous dimension + * changed (e.g. if the current dimension is Product and the previous was Person, we need to start + * a new cell if Product is the same but Person changed, so we check the previous dimension and tuple) + */ + changed = previousHeader != undefined && previousHeader != currentHeader; + if (i > 0 && j > 0) { + changed = changed || tuple.data[dimensions[i-1].dataIndex] != tuples[j-1].data[dimensions[i-1].dataIndex]; + } + + if (changed) { + rows.push({ + header: previousHeader, + span : span, + start : start + }); + + start += span; + span = 0; + } + + if (isLast) { + rows.push({ + header: currentHeader, + span : span + 1, + start : start + }); + + start += span; + span = 0; + } + + previousHeader = currentHeader; + span++; + } + + headers.push({ + items: rows, + width: dimension.width || this.defaultHeaderWidth + }); + + previousHeader = undefined; + } + + return headers; + } +}); +
+ + \ No newline at end of file