4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-grid-feature-Grouping'>/**
19 </span> * @class Ext.grid.feature.Grouping
20 * @extends Ext.grid.feature.Feature
22 * This feature allows to display the grid rows aggregated into groups as specified by the {@link Ext.data.Store#groupers}
23 * specified on the Store. The group will show the title for the group name and then the appropriate records for the group
24 * underneath. The groups can also be expanded and collapsed.
27 * This feature adds several extra events that will be fired on the grid to interact with the groups:
29 * - {@link #groupclick}
30 * - {@link #groupdblclick}
31 * - {@link #groupcontextmenu}
32 * - {@link #groupexpand}
33 * - {@link #groupcollapse}
35 * ## Menu Augmentation
36 * This feature adds extra options to the grid column menu to provide the user with functionality to modify the grouping.
37 * This can be disabled by setting the {@link #enableGroupingMenu} option. The option to disallow grouping from being turned off
38 * by thew user is {@link #enableNoGroups}.
40 * ## Controlling Group Text
41 * The {@link #groupHeaderTpl} is used to control the rendered title for each group. It can modified to customized
42 * the default display.
46 * var groupingFeature = Ext.create('Ext.grid.feature.Grouping', {
47 * groupHeaderTpl: 'Group: {name} ({rows.length})', //print the number of items in the group
48 * startCollapsed: true // start all groups collapsed
52 * @author Nicolas Ferrero
54 Ext.define('Ext.grid.feature.Grouping', {
55 extend: 'Ext.grid.feature.Feature',
56 alias: 'feature.grouping',
59 eventSelector: '.' + Ext.baseCSSPrefix + 'grid-group-hd',
61 constructor: function() {
62 this.collapsedState = {};
63 this.callParent(arguments);
66 <span id='Ext-grid-feature-Grouping-event-groupclick'> /**
67 </span> * @event groupclick
68 * @param {Ext.view.Table} view
69 * @param {HTMLElement} node
70 * @param {String} group The name of the group
71 * @param {Ext.EventObject} e
74 <span id='Ext-grid-feature-Grouping-event-groupdblclick'> /**
75 </span> * @event groupdblclick
76 * @param {Ext.view.Table} view
77 * @param {HTMLElement} node
78 * @param {String} group The name of the group
79 * @param {Ext.EventObject} e
82 <span id='Ext-grid-feature-Grouping-event-groupcontextmenu'> /**
83 </span> * @event groupcontextmenu
84 * @param {Ext.view.Table} view
85 * @param {HTMLElement} node
86 * @param {String} group The name of the group
87 * @param {Ext.EventObject} e
90 <span id='Ext-grid-feature-Grouping-event-groupcollapse'> /**
91 </span> * @event groupcollapse
92 * @param {Ext.view.Table} view
93 * @param {HTMLElement} node
94 * @param {String} group The name of the group
95 * @param {Ext.EventObject} e
98 <span id='Ext-grid-feature-Grouping-event-groupexpand'> /**
99 </span> * @event groupexpand
100 * @param {Ext.view.Table} view
101 * @param {HTMLElement} node
102 * @param {String} group The name of the group
103 * @param {Ext.EventObject} e
106 <span id='Ext-grid-feature-Grouping-cfg-groupHeaderTpl'> /**
107 </span> * @cfg {String} groupHeaderTpl
108 * Template snippet, this cannot be an actual template. {name} will be replaced with the current group.
109 * Defaults to 'Group: {name}'
111 groupHeaderTpl: 'Group: {name}',
113 <span id='Ext-grid-feature-Grouping-cfg-depthToIndent'> /**
114 </span> * @cfg {Number} depthToIndent
115 * Number of pixels to indent per grouping level
119 collapsedCls: Ext.baseCSSPrefix + 'grid-group-collapsed',
120 hdCollapsedCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsed',
122 <span id='Ext-grid-feature-Grouping-cfg-groupByText'> /**
123 </span> * @cfg {String} groupByText Text displayed in the grid header menu for grouping by header
124 * (defaults to 'Group By This Field').
126 groupByText : 'Group By This Field',
127 <span id='Ext-grid-feature-Grouping-cfg-showGroupsText'> /**
128 </span> * @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping
129 * (defaults to 'Show in Groups').
131 showGroupsText : 'Show in Groups',
133 <span id='Ext-grid-feature-Grouping-cfg-hideGroupedHeader'> /**
134 </span> * @cfg {Boolean} hideGroupedHeader<tt>true</tt> to hide the header that is currently grouped (defaults to <tt>false</tt>)
136 hideGroupedHeader : false,
138 <span id='Ext-grid-feature-Grouping-cfg-startCollapsed'> /**
139 </span> * @cfg {Boolean} startCollapsed <tt>true</tt> to start all groups collapsed (defaults to <tt>false</tt>)
141 startCollapsed : false,
143 <span id='Ext-grid-feature-Grouping-cfg-enableGroupingMenu'> /**
144 </span> * @cfg {Boolean} enableGroupingMenu <tt>true</tt> to enable the grouping control in the header menu (defaults to <tt>true</tt>)
146 enableGroupingMenu : true,
148 <span id='Ext-grid-feature-Grouping-cfg-enableNoGroups'> /**
149 </span> * @cfg {Boolean} enableNoGroups <tt>true</tt> to allow the user to turn off grouping (defaults to <tt>true</tt>)
151 enableNoGroups : true,
159 if (me.lastGroupIndex) {
160 store.group(me.lastGroupIndex);
163 groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
164 groupToggleMenuItem.setChecked(true, true);
168 disable: function() {
175 lastGroup = store.groupers.first();
177 me.lastGroupIndex = lastGroup.property;
178 store.groupers.clear();
182 groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
183 groupToggleMenuItem.setChecked(true, true);
184 groupToggleMenuItem.setChecked(false, true);
188 getFeatureTpl: function(values, parent, x, xcount) {
192 '<tpl if="typeof rows !== \'undefined\'">',
194 '<tr class="' + Ext.baseCSSPrefix + 'grid-group-hd ' + (me.startCollapsed ? me.hdCollapsedCls : '') + ' {hdCollapsedCls}"><td class="' + Ext.baseCSSPrefix + 'grid-cell" colspan="' + parent.columns.length + '" {[this.indentByDepth(values)]}><div class="' + Ext.baseCSSPrefix + 'grid-cell-inner"><div class="' + Ext.baseCSSPrefix + 'grid-group-title">{collapsed}' + me.groupHeaderTpl + '</div></div></td></tr>',
195 // this is the rowbody
196 '<tr id="{viewId}-gp-{name}" class="' + Ext.baseCSSPrefix + 'grid-group-body ' + (me.startCollapsed ? me.collapsedCls : '') + ' {collapsedCls}"><td colspan="' + parent.columns.length + '">{[this.recurse(values)]}</td></tr>',
201 getFragmentTpl: function() {
203 indentByDepth: this.indentByDepth,
204 depthToIndent: this.depthToIndent
208 indentByDepth: function(values) {
209 var depth = values.depth || 0;
210 return 'style="padding-left:'+ depth * this.depthToIndent + 'px;"';
213 // Containers holding these components are responsible for
214 // destroying them, we are just deleting references.
215 destroy: function() {
219 delete me.prunedHeader;
222 // perhaps rename to afterViewRender
223 attachEvents: function() {
226 header, headerId, menu, menuItem;
230 groupclick: me.onGroupClick,
231 rowfocus: me.onRowFocus
233 view.store.on('groupchange', me.onGroupChange, me);
235 me.pruneGroupedHeader();
237 if (me.enableGroupingMenu) {
238 me.injectGroupingMenu();
241 if (me.hideGroupedHeader) {
242 header = view.headerCt.down('gridcolumn[dataIndex=' + me.getGroupField() + ']');
243 headerId = header.id;
244 menu = view.headerCt.getMenu();
245 menuItem = menu.down('menuitem[headerId='+ headerId +']');
247 menuItem.setChecked(false);
252 injectGroupingMenu: function() {
255 headerCt = view.headerCt;
256 headerCt.showMenuBy = me.showMenuBy;
257 headerCt.getMenuItems = me.getMenuItems();
260 showMenuBy: function(t, header) {
261 var menu = this.getMenu(),
262 groupMenuItem = menu.down('#groupMenuItem'),
263 groupableMth = header.groupable === false ? 'disable' : 'enable';
265 groupMenuItem[groupableMth]();
266 Ext.grid.header.Container.prototype.showMenuBy.apply(this, arguments);
269 getMenuItems: function() {
271 groupByText = me.groupByText,
272 disabled = me.disabled,
273 showGroupsText = me.showGroupsText,
274 enableNoGroups = me.enableNoGroups,
275 groupMenuItemClick = Ext.Function.bind(me.onGroupMenuItemClick, me),
276 groupToggleMenuItemClick = Ext.Function.bind(me.onGroupToggleMenuItemClick, me);
278 // runs in the scope of headerCt
280 var o = Ext.grid.header.Container.prototype.getMenuItems.call(this);
282 itemId: 'groupMenuItem',
284 handler: groupMenuItemClick
286 if (enableNoGroups) {
288 itemId: 'groupToggleMenuItem',
289 text: showGroupsText,
291 checkHandler: groupToggleMenuItemClick
299 <span id='Ext-grid-feature-Grouping-method-onGroupMenuItemClick'> /**
300 </span> * Group by the header the user has clicked on.
303 onGroupMenuItemClick: function(menuItem, e) {
304 var menu = menuItem.parentMenu,
305 hdr = menu.activeHeader,
308 delete this.lastGroupIndex;
310 view.store.group(hdr.dataIndex);
311 this.pruneGroupedHeader();
315 <span id='Ext-grid-feature-Grouping-method-onGroupToggleMenuItemClick'> /**
316 </span> * Turn on and off grouping via the menu
319 onGroupToggleMenuItemClick: function(menuItem, checked) {
320 this[checked ? 'enable' : 'disable']();
323 <span id='Ext-grid-feature-Grouping-method-pruneGroupedHeader'> /**
324 </span> * Prunes the grouped header from the header container
327 pruneGroupedHeader: function() {
331 groupField = me.getGroupField(),
332 headerCt = view.headerCt,
333 header = headerCt.down('header[dataIndex=' + groupField + ']');
336 if (me.prunedHeader) {
337 me.prunedHeader.show();
339 me.prunedHeader = header;
344 getGroupField: function(){
345 var group = this.view.store.groupers.first();
347 return group.property;
352 <span id='Ext-grid-feature-Grouping-method-onRowFocus'> /**
353 </span> * When a row gains focus, expand the groups above it
356 onRowFocus: function(rowIdx) {
357 var node = this.view.getNode(rowIdx),
358 groupBd = Ext.fly(node).up('.' + this.collapsedCls);
361 // for multiple level groups, should expand every groupBd
363 this.expand(groupBd);
367 <span id='Ext-grid-feature-Grouping-method-expand'> /**
368 </span> * Expand a group by the groupBody
369 * @param {Ext.core.Element} groupBd
372 expand: function(groupBd) {
375 grid = view.up('gridpanel'),
376 groupBdDom = Ext.getDom(groupBd);
378 me.collapsedState[groupBdDom.id] = false;
380 groupBd.removeCls(me.collapsedCls);
381 groupBd.prev().removeCls(me.hdCollapsedCls);
383 grid.determineScrollbars();
384 grid.invalidateScroller();
385 view.fireEvent('groupexpand');
388 <span id='Ext-grid-feature-Grouping-method-collapse'> /**
389 </span> * Collapse a group by the groupBody
390 * @param {Ext.core.Element} groupBd
393 collapse: function(groupBd) {
396 grid = view.up('gridpanel'),
397 groupBdDom = Ext.getDom(groupBd);
399 me.collapsedState[groupBdDom.id] = true;
401 groupBd.addCls(me.collapsedCls);
402 groupBd.prev().addCls(me.hdCollapsedCls);
404 grid.determineScrollbars();
405 grid.invalidateScroller();
406 view.fireEvent('groupcollapse');
409 onGroupChange: function(){
413 <span id='Ext-grid-feature-Grouping-method-onGroupClick'> /**
414 </span> * Toggle between expanded/collapsed state when clicking on
418 onGroupClick: function(view, group, idx, foo, e) {
420 toggleCls = me.toggleCls,
421 groupBd = Ext.fly(group.nextSibling, '_grouping');
423 if (groupBd.hasCls(me.collapsedCls)) {
426 me.collapse(groupBd);
430 // Injects isRow and closeRow into the metaRowTpl.
431 getMetaRowTplFragments: function() {
434 closeRow: this.closeRow
438 // injected into rowtpl and wrapped around metaRowTpl
439 // becomes part of the standard tpl
441 return '<tpl if="typeof rows === \'undefined\'">';
444 // injected into rowtpl and wrapped around metaRowTpl
445 // becomes part of the standard tpl
446 closeRow: function() {
447 return '</tpl>';
450 // isRow and closeRow are injected via getMetaRowTplFragments
451 mutateMetaRowTpl: function(metaRowTpl) {
452 metaRowTpl.unshift('{[this.isRow()]}');
453 metaRowTpl.push('{[this.closeRow()]}');
456 // injects an additional style attribute via tdAttrKey with the proper
458 getAdditionalData: function(data, idx, record, orig) {
459 var view = this.view,
461 col = hCt.items.getAt(0),
463 tdAttrKey = col.id + '-tdAttr';
465 // maintain the current tdAttr that a user may ahve set.
466 o[tdAttrKey] = this.indentByDepth(data) + " " + (orig[tdAttrKey] ? orig[tdAttrKey] : '');
467 o.collapsed = 'true';
471 // return matching preppedRecords
472 getGroupRows: function(group, records, preppedRecords, fullWidth) {
474 children = group.children,
475 rows = group.rows = [],
477 group.viewId = view.id;
479 Ext.Array.each(records, function(record, idx) {
480 if (Ext.Array.indexOf(children, record) != -1) {
481 rows.push(Ext.apply(preppedRecords[idx], {
486 delete group.children;
487 group.fullWidth = fullWidth;
488 if (me.collapsedState[view.id + '-gp-' + group.name]) {
489 group.collapsedCls = me.collapsedCls;
490 group.hdCollapsedCls = me.hdCollapsedCls;
496 // return the data in a grouped format.
497 collectData: function(records, preppedRecords, startIndex, fullWidth, o) {
499 store = me.view.store,
502 if (!me.disabled && store.isGrouped()) {
503 groups = store.getGroups();
504 Ext.Array.each(groups, function(group, idx){
505 me.getGroupRows(group, records, preppedRecords, fullWidth);
515 // adds the groupName to the groupclick, groupdblclick, groupcontextmenu
516 // events that are fired on the view. Chose not to return the actual
517 // group itself because of its expense and because developers can simply
518 // grab the group via store.getGroups(groupName)
519 getFireEventArgs: function(type, view, featureTarget, e) {
520 var returnArray = [type, view, featureTarget],
521 groupBd = Ext.fly(featureTarget.nextSibling, '_grouping'),
522 groupBdId = Ext.getDom(groupBd).id,
523 prefix = view.id + '-gp-',
524 groupName = groupBdId.substr(prefix.length);
526 returnArray.push(groupName, e);