/* * Ext JS Library 2.0 Copyright(c) 2006-2007, Ext JS, LLC. licensing@extjs.com * * http://extjs.com/license */ /** * @class Ext.grid.GroupingView * @extends Ext.grid.GridView Adds the ability for single level grouping to the * grid. * *

 * var grid = new Ext.grid.GridPanel({
 * 	// A groupingStore is required for a GroupingView
 * 	store : new Ext.data.GroupingStore({
 * 				reader : reader,
 * 				data : xg.dummyData,
 * 				sortInfo : {
 * 					field : 'company',
 * 					direction : "ASC"
 * 				},
 * 				groupField : 'industry'
 * 			}),
 * 
 * 	columns : [{
 * 				id : 'company',
 * 				header : "Company",
 * 				width : 60,
 * 				sortable : true,
 * 				dataIndex : 'company'
 * 			}, {
 * 				header : "Price",
 * 				width : 20,
 * 				sortable : true,
 * 				renderer : Ext.util.Format.usMoney,
 * 				dataIndex : 'price'
 * 			}, {
 * 				header : "Change",
 * 				width : 20,
 * 				sortable : true,
 * 				dataIndex : 'change',
 * 				renderer : Ext.util.Format.usMoney
 * 			}, {
 * 				header : "Industry",
 * 				width : 20,
 * 				sortable : true,
 * 				dataIndex : 'industry'
 * 			}, {
 * 				header : "Last Updated",
 * 				width : 20,
 * 				sortable : true,
 * 				renderer : Ext.util.Format.dateRenderer('m/d/Y'),
 * 				dataIndex : 'lastChange'
 * 			}],
 * 
 * 	view : new Ext.grid.GroupingView({
 * 		forceFit : true,
 * 		// custom grouping text template to display the number of items per group
 * 		groupTextTpl : '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
 * 	}),
 * 
 * 	frame : true,
 * 	width : 700,
 * 	height : 450,
 * 	collapsible : true,
 * 	animCollapse : false,
 * 	title : 'Grouping Example',
 * 	iconCls : 'icon-grid',
 * 	renderTo : document.body
 * });
 * 
* * @constructor * @param {Object} * config */ Ext.grid.GroupingView = Ext.extend(Ext.grid.GridView, { /** * @cfg {Boolean} hideGroupedColumn True to hide the column that is * currently grouped */ hideGroupedColumn : false, /** * @cfg {Boolean} showGroupName True to display the name for each set of * grouped rows (defaults to false) */ showGroupName : true, /** * @cfg {Boolean} startCollapsed True to start all groups collapsed */ startCollapsed : false, /** * @cfg {Boolean} enableGrouping False to disable grouping functionality * (defaults to true) */ enableGrouping : true, /** * @cfg {Boolean} enableGroupingMenu True to enable the grouping control in * the column menu */ enableGroupingMenu : true, /** * @cfg {Boolean} enableNoGroups True to allow the user to turn off grouping */ enableNoGroups : true, /** * @cfg {String} emptyGroupText The text to display when there is an empty * group value */ emptyGroupText : '(None)', /** * @cfg {Boolean} ignoreAdd True to skip refreshing the view when new rows * are added (defaults to false) */ ignoreAdd : false, /** * @cfg {String} groupTextTpl The template used to render the group text */ groupTextTpl : '{text}', // private gidSeed : 1000, // private initTemplates : function() { Ext.grid.GroupingView.superclass.initTemplates.call(this); this.state = {}; var sm = this.grid.getSelectionModel(); sm.on(sm.selectRow ? 'beforerowselect' : 'beforecellselect', this.onBeforeRowSelect, this); if (!this.startGroup) { this.startGroup = new Ext.XTemplate( '
', '
', this.groupTextTpl, '
', '
'); } this.startGroup.compile(); this.endGroup = '
'; }, // private findGroup : function(el) { return Ext.fly(el).up('.x-grid-group', this.mainBody.dom); }, // private getGroups : function() { return this.hasRows() ? this.mainBody.dom.childNodes : []; }, // private onAdd : function() { if (this.enableGrouping && !this.ignoreAdd) { var ss = this.getScrollState(); this.refresh(); this.restoreScroll(ss); } else if (!this.enableGrouping) { Ext.grid.GroupingView.superclass.onAdd.apply(this, arguments); } }, // private onRemove : function(ds, record, index, isUpdate) { Ext.grid.GroupingView.superclass.onRemove.apply(this, arguments); var g = document.getElementById(record._groupId); if (g && g.childNodes[1].childNodes.length < 1) { Ext.removeNode(g); } this.applyEmptyText(); }, // private refreshRow : function(record) { if (this.ds.getCount() == 1) { this.refresh(); } else { this.isUpdating = true; Ext.grid.GroupingView.superclass.refreshRow.apply(this, arguments); this.isUpdating = false; } }, // private beforeMenuShow : function() { var field = this.getGroupField(); var g = this.hmenu.items.get('groupBy'); if (g) { g.setDisabled(this.cm.config[this.hdCtxIndex].groupable === false); } var s = this.hmenu.items.get('showGroups'); if (s) { s.setChecked(!!field); } }, // private renderUI : function() { Ext.grid.GroupingView.superclass.renderUI.call(this); this.mainBody.on('mousedown', this.interceptMouse, this); if (this.enableGroupingMenu && this.hmenu) { this.hmenu.add('-', { id : 'groupBy', text : this.groupByText, handler : this.onGroupByClick, scope : this, iconCls : 'x-group-by-icon' }); if (this.enableNoGroups) { this.hmenu.add({ id : 'showGroups', text : this.showGroupsText, checked : true, checkHandler : this.onShowGroupsClick, scope : this }); } this.hmenu.on('beforeshow', this.beforeMenuShow, this); } }, // private onGroupByClick : function() { this.grid.store.groupBy(this.cm.getDataIndex(this.hdCtxIndex)); }, // private onShowGroupsClick : function(mi, checked) { if (checked) { this.onGroupByClick(); } else { this.grid.store.clearGrouping(); } }, /** * Toggles the specified group if no value is passed, otherwise sets the * expanded state of the group to the value passed. * * @param {String} * groupId The groupId assigned to the group (see getGroupId) * @param {Boolean} * expanded (optional) */ toggleGroup : function(group, expanded) { this.grid.stopEditing(); group = Ext.getDom(group); var gel = Ext.fly(group); expanded = expanded !== undefined ? expanded : gel .hasClass('x-grid-group-collapsed'); this.state[gel.dom.id] = expanded; gel[expanded ? 'removeClass' : 'addClass']('x-grid-group-collapsed'); }, /** * Toggles all groups if no value is passed, otherwise sets the expanded * state of all groups to the value passed. * * @param {Boolean} * expanded (optional) */ toggleAllGroups : function(expanded) { var groups = this.getGroups(); for (var i = 0, len = groups.length; i < len; i++) { this.toggleGroup(groups[i], expanded); } }, /** * Expands all grouped rows. */ expandAllGroups : function() { this.toggleAllGroups(true); }, /** * Collapses all grouped rows. */ collapseAllGroups : function() { this.toggleAllGroups(false); }, // private interceptMouse : function(e) { var hd = e.getTarget('.x-grid-group-hd', this.mainBody); if (hd) { e.stopEvent(); this.toggleGroup(hd.parentNode); } }, // private getGroup : function(v, r, groupRenderer, rowIndex, colIndex, ds) { var g = groupRenderer ? groupRenderer(v, {}, r, rowIndex, colIndex, ds) : String(v); if (g === '') { g = this.cm.config[colIndex].emptyGroupText || this.emptyGroupText; } return g; }, // private getGroupField : function() { return this.grid.store.getGroupState(); }, // private renderRows : function() { var groupField = this.getGroupField(); var eg = !!groupField; // if they turned off grouping and the last grouped field is hidden if (this.hideGroupedColumn) { var colIndex = this.cm.findColumnIndex(groupField); if (!eg && this.lastGroupField !== undefined) { this.mainBody.update(''); this.cm.setHidden(this.cm.findColumnIndex(this.lastGroupField), false); delete this.lastGroupField; } else if (eg && this.lastGroupField === undefined) { this.lastGroupField = groupField; this.cm.setHidden(colIndex, true); } else if (eg && this.lastGroupField !== undefined && groupField !== this.lastGroupField) { this.mainBody.update(''); var oldIndex = this.cm.findColumnIndex(this.lastGroupField); this.cm.setHidden(oldIndex, false); this.lastGroupField = groupField; this.cm.setHidden(colIndex, true); } } return Ext.grid.GroupingView.superclass.renderRows.apply(this, arguments); }, // private doRender : function(cs, rs, ds, startRow, colCount, stripe) { if (rs.length < 1) { return ''; } var groupField = this.getGroupField(); var colIndex = this.cm.findColumnIndex(groupField); this.enableGrouping = !!groupField; if (!this.enableGrouping || this.isUpdating) { return Ext.grid.GroupingView.superclass.doRender.apply(this, arguments); } var gstyle = 'width:' + this.getTotalWidth() + ';'; var gidPrefix = this.grid.getGridEl().id; var cfg = this.cm.config[colIndex]; var groupRenderer = cfg.groupRenderer || cfg.renderer; var cls = this.startCollapsed ? 'x-grid-group-collapsed' : ''; var prefix = this.showGroupName ? (cfg.groupName || cfg.header) + ': ' : ''; var groups = [], curGroup, i, len, gid; for (i = 0, len = rs.length; i < len; i++) { var rowIndex = startRow + i; var r = rs[i], gvalue = r.data[groupField], g = this.getGroup( gvalue, r, groupRenderer, rowIndex, colIndex, ds); if (!curGroup || curGroup.group != g) { gid = gidPrefix + '-gp-' + groupField + '-' + g; var gcls = cls ? cls : (this.state[gid] === false ? 'x-grid-group-collapsed' : ''); curGroup = { group : g, gvalue : gvalue, text : prefix + g, groupId : gid, startRow : rowIndex, rs : [r], cls : gcls, style : gstyle }; groups.push(curGroup); } else { curGroup.rs.push(r); } r._groupId = gid; } var buf = []; for (i = 0, len = groups.length; i < len; i++) { var g = groups[i]; this.doGroupStart(buf, g, cs, ds, colCount); buf[buf.length] = Ext.grid.GroupingView.superclass.doRender.call( this, cs, g.rs, ds, g.startRow, colCount, stripe); this.doGroupEnd(buf, g, cs, ds, colCount); } return buf.join(''); }, /** * Dynamically tries to determine the groupId of a specific value * * @param {String} * value * @return {String} The group id */ getGroupId : function(value) { var gidPrefix = this.grid.getGridEl().id; var groupField = this.getGroupField(); var colIndex = this.cm.findColumnIndex(groupField); var cfg = this.cm.config[colIndex]; var groupRenderer = cfg.groupRenderer || cfg.renderer; var gtext = this.getGroup(value, { data : {} }, groupRenderer, 0, colIndex, this.ds); return gidPrefix + '-gp-' + groupField + '-' + value; }, // private doGroupStart : function(buf, g, cs, ds, colCount) { buf[buf.length] = this.startGroup.apply(g); }, // private doGroupEnd : function(buf, g, cs, ds, colCount) { buf[buf.length] = this.endGroup; }, // private getRows : function() { if (!this.enableGrouping) { return Ext.grid.GroupingView.superclass.getRows.call(this); } var r = []; var g, gs = this.getGroups(); for (var i = 0, len = gs.length; i < len; i++) { g = gs[i].childNodes[1].childNodes; for (var j = 0, jlen = g.length; j < jlen; j++) { r[r.length] = g[j]; } } return r; }, // private updateGroupWidths : function() { if (!this.enableGrouping || !this.hasRows()) { return; } var tw = Math.max(this.cm.getTotalWidth(), this.el.dom.offsetWidth - this.scrollOffset) + 'px'; var gs = this.getGroups(); for (var i = 0, len = gs.length; i < len; i++) { gs[i].firstChild.style.width = tw; } }, // private onColumnWidthUpdated : function(col, w, tw) { this.updateGroupWidths(); }, // private onAllColumnWidthsUpdated : function(ws, tw) { this.updateGroupWidths(); }, // private onColumnHiddenUpdated : function(col, hidden, tw) { this.updateGroupWidths(); }, // private onLayout : function() { this.updateGroupWidths(); }, // private onBeforeRowSelect : function(sm, rowIndex) { if (!this.enableGrouping) { return; } var row = this.getRow(rowIndex); if (row && !row.offsetParent) { var g = this.findGroup(row); this.toggleGroup(g, true); } }, /** * @cfg {String} groupByText Text displayed in the grid header menu for * grouping by a column (defaults to 'Group By This Field'). */ groupByText : 'Group By This Field', /** * @cfg {String} showGroupsText Text displayed in the grid header for * enabling/disabling grouping (defaults to 'Show in Groups'). */ showGroupsText : 'Show in Groups' }); // private Ext.grid.GroupingView.GROUP_ID = 1000;