/*
* 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.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;