4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/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() {
64 me.collapsedState = {};
65 me.callParent(arguments);
68 <span id='Ext-grid-feature-Grouping-event-groupclick'> /**
69 </span> * @event groupclick
70 * @param {Ext.view.Table} view
71 * @param {HTMLElement} node
72 * @param {String} group The name of the group
73 * @param {Ext.EventObject} e
76 <span id='Ext-grid-feature-Grouping-event-groupdblclick'> /**
77 </span> * @event groupdblclick
78 * @param {Ext.view.Table} view
79 * @param {HTMLElement} node
80 * @param {String} group The name of the group
81 * @param {Ext.EventObject} e
84 <span id='Ext-grid-feature-Grouping-event-groupcontextmenu'> /**
85 </span> * @event groupcontextmenu
86 * @param {Ext.view.Table} view
87 * @param {HTMLElement} node
88 * @param {String} group The name of the group
89 * @param {Ext.EventObject} e
92 <span id='Ext-grid-feature-Grouping-event-groupcollapse'> /**
93 </span> * @event groupcollapse
94 * @param {Ext.view.Table} view
95 * @param {HTMLElement} node
96 * @param {String} group The name of the group
97 * @param {Ext.EventObject} e
100 <span id='Ext-grid-feature-Grouping-event-groupexpand'> /**
101 </span> * @event groupexpand
102 * @param {Ext.view.Table} view
103 * @param {HTMLElement} node
104 * @param {String} group The name of the group
105 * @param {Ext.EventObject} e
108 <span id='Ext-grid-feature-Grouping-cfg-groupHeaderTpl'> /**
109 </span> * @cfg {String} groupHeaderTpl
110 * Template snippet, this cannot be an actual template. {name} will be replaced with the current group.
111 * Defaults to 'Group: {name}'
113 groupHeaderTpl: 'Group: {name}',
115 <span id='Ext-grid-feature-Grouping-cfg-depthToIndent'> /**
116 </span> * @cfg {Number} depthToIndent
117 * Number of pixels to indent per grouping level
121 collapsedCls: Ext.baseCSSPrefix + 'grid-group-collapsed',
122 hdCollapsedCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsed',
124 <span id='Ext-grid-feature-Grouping-cfg-groupByText'> /**
125 </span> * @cfg {String} groupByText Text displayed in the grid header menu for grouping by header.
127 groupByText : 'Group By This Field',
128 <span id='Ext-grid-feature-Grouping-cfg-showGroupsText'> /**
129 </span> * @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping.
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.
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
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
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
151 enableNoGroups : true,
159 me.lastGroupField = me.getGroupField();
161 if (me.lastGroupIndex) {
162 store.group(me.lastGroupIndex);
165 groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
166 groupToggleMenuItem.setChecked(true, true);
170 disable: function() {
174 remote = store.remoteGroup,
178 lastGroup = store.groupers.first();
180 me.lastGroupIndex = lastGroup.property;
182 store.clearGrouping();
187 groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
188 groupToggleMenuItem.setChecked(true, true);
189 groupToggleMenuItem.setChecked(false, true);
195 refreshIf: function() {
196 if (this.blockRefresh !== true) {
201 getFeatureTpl: function(values, parent, x, xcount) {
205 '<tpl if="typeof rows !== \'undefined\'">',
207 '<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>',
208 // this is the rowbody
209 '<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>',
214 getFragmentTpl: function() {
216 indentByDepth: this.indentByDepth,
217 depthToIndent: this.depthToIndent
221 indentByDepth: function(values) {
222 var depth = values.depth || 0;
223 return 'style="padding-left:'+ depth * this.depthToIndent + 'px;"';
226 // Containers holding these components are responsible for
227 // destroying them, we are just deleting references.
228 destroy: function() {
232 delete me.prunedHeader;
235 // perhaps rename to afterViewRender
236 attachEvents: function() {
242 groupclick: me.onGroupClick,
243 rowfocus: me.onRowFocus
245 view.store.on('groupchange', me.onGroupChange, me);
247 me.pruneGroupedHeader();
249 if (me.enableGroupingMenu) {
250 me.injectGroupingMenu();
252 me.lastGroupField = me.getGroupField();
258 injectGroupingMenu: function() {
261 headerCt = view.headerCt;
262 headerCt.showMenuBy = me.showMenuBy;
263 headerCt.getMenuItems = me.getMenuItems();
266 showMenuBy: function(t, header) {
267 var menu = this.getMenu(),
268 groupMenuItem = menu.down('#groupMenuItem'),
269 groupableMth = header.groupable === false ? 'disable' : 'enable';
271 groupMenuItem[groupableMth]();
272 Ext.grid.header.Container.prototype.showMenuBy.apply(this, arguments);
275 getMenuItems: function() {
277 groupByText = me.groupByText,
278 disabled = me.disabled,
279 showGroupsText = me.showGroupsText,
280 enableNoGroups = me.enableNoGroups,
281 groupMenuItemClick = Ext.Function.bind(me.onGroupMenuItemClick, me),
282 groupToggleMenuItemClick = Ext.Function.bind(me.onGroupToggleMenuItemClick, me);
284 // runs in the scope of headerCt
286 var o = Ext.grid.header.Container.prototype.getMenuItems.call(this);
288 iconCls: Ext.baseCSSPrefix + 'group-by-icon',
289 itemId: 'groupMenuItem',
291 handler: groupMenuItemClick
293 if (enableNoGroups) {
295 itemId: 'groupToggleMenuItem',
296 text: showGroupsText,
298 checkHandler: groupToggleMenuItemClick
306 <span id='Ext-grid-feature-Grouping-method-onGroupMenuItemClick'> /**
307 </span> * Group by the header the user has clicked on.
310 onGroupMenuItemClick: function(menuItem, e) {
312 menu = menuItem.parentMenu,
313 hdr = menu.activeHeader,
316 remote = store.remoteGroup;
318 delete me.lastGroupIndex;
321 store.group(hdr.dataIndex);
322 me.pruneGroupedHeader();
330 this.blockRefresh = this.view.blockRefresh = true;
334 this.blockRefresh = this.view.blockRefresh = false;
337 <span id='Ext-grid-feature-Grouping-method-onGroupToggleMenuItemClick'> /**
338 </span> * Turn on and off grouping via the menu
341 onGroupToggleMenuItemClick: function(menuItem, checked) {
342 this[checked ? 'enable' : 'disable']();
345 <span id='Ext-grid-feature-Grouping-method-pruneGroupedHeader'> /**
346 </span> * Prunes the grouped header from the header container
349 pruneGroupedHeader: function() {
353 groupField = me.getGroupField(),
354 headerCt = view.headerCt,
355 header = headerCt.down('header[dataIndex=' + groupField + ']');
358 if (me.prunedHeader) {
359 me.prunedHeader.show();
361 me.prunedHeader = header;
366 getGroupField: function(){
367 var group = this.view.store.groupers.first();
369 return group.property;
374 <span id='Ext-grid-feature-Grouping-method-onRowFocus'> /**
375 </span> * When a row gains focus, expand the groups above it
378 onRowFocus: function(rowIdx) {
379 var node = this.view.getNode(rowIdx),
380 groupBd = Ext.fly(node).up('.' + this.collapsedCls);
383 // for multiple level groups, should expand every groupBd
385 this.expand(groupBd);
389 <span id='Ext-grid-feature-Grouping-method-expand'> /**
390 </span> * Expand a group by the groupBody
391 * @param {Ext.Element} groupBd
394 expand: function(groupBd) {
397 grid = view.up('gridpanel'),
398 groupBdDom = Ext.getDom(groupBd);
400 me.collapsedState[groupBdDom.id] = false;
402 groupBd.removeCls(me.collapsedCls);
403 groupBd.prev().removeCls(me.hdCollapsedCls);
405 grid.determineScrollbars();
406 grid.invalidateScroller();
407 view.fireEvent('groupexpand');
410 <span id='Ext-grid-feature-Grouping-method-collapse'> /**
411 </span> * Collapse a group by the groupBody
412 * @param {Ext.Element} groupBd
415 collapse: function(groupBd) {
418 grid = view.up('gridpanel'),
419 groupBdDom = Ext.getDom(groupBd);
421 me.collapsedState[groupBdDom.id] = true;
423 groupBd.addCls(me.collapsedCls);
424 groupBd.prev().addCls(me.hdCollapsedCls);
426 grid.determineScrollbars();
427 grid.invalidateScroller();
428 view.fireEvent('groupcollapse');
431 onGroupChange: function(){
433 field = me.getGroupField(),
436 if (me.hideGroupedHeader) {
437 if (me.lastGroupField) {
438 menuItem = me.getMenuItem(me.lastGroupField);
440 menuItem.setChecked(true);
444 menuItem = me.getMenuItem(field);
446 menuItem.setChecked(false);
450 if (me.blockRefresh !== true) {
453 me.lastGroupField = field;
456 <span id='Ext-grid-feature-Grouping-method-getMenuItem'> /**
457 </span> * Gets the related menu item for a dataIndex
459 * @return {Ext.grid.header.Container} The header
461 getMenuItem: function(dataIndex){
462 var view = this.view,
463 header = view.headerCt.down('gridcolumn[dataIndex=' + dataIndex + ']'),
464 menu = view.headerCt.getMenu();
466 return menu.down('menuitem[headerId='+ header.id +']');
469 <span id='Ext-grid-feature-Grouping-method-onGroupClick'> /**
470 </span> * Toggle between expanded/collapsed state when clicking on
474 onGroupClick: function(view, group, idx, foo, e) {
476 toggleCls = me.toggleCls,
477 groupBd = Ext.fly(group.nextSibling, '_grouping');
479 if (groupBd.hasCls(me.collapsedCls)) {
482 me.collapse(groupBd);
486 // Injects isRow and closeRow into the metaRowTpl.
487 getMetaRowTplFragments: function() {
490 closeRow: this.closeRow
494 // injected into rowtpl and wrapped around metaRowTpl
495 // becomes part of the standard tpl
497 return '<tpl if="typeof rows === \'undefined\'">';
500 // injected into rowtpl and wrapped around metaRowTpl
501 // becomes part of the standard tpl
502 closeRow: function() {
503 return '</tpl>';
506 // isRow and closeRow are injected via getMetaRowTplFragments
507 mutateMetaRowTpl: function(metaRowTpl) {
508 metaRowTpl.unshift('{[this.isRow()]}');
509 metaRowTpl.push('{[this.closeRow()]}');
512 // injects an additional style attribute via tdAttrKey with the proper
514 getAdditionalData: function(data, idx, record, orig) {
515 var view = this.view,
517 col = hCt.items.getAt(0),
519 tdAttrKey = col.id + '-tdAttr';
521 // maintain the current tdAttr that a user may ahve set.
522 o[tdAttrKey] = this.indentByDepth(data) + " " + (orig[tdAttrKey] ? orig[tdAttrKey] : '');
523 o.collapsed = 'true';
527 // return matching preppedRecords
528 getGroupRows: function(group, records, preppedRecords, fullWidth) {
530 children = group.children,
531 rows = group.rows = [],
533 group.viewId = view.id;
535 Ext.Array.each(records, function(record, idx) {
536 if (Ext.Array.indexOf(children, record) != -1) {
537 rows.push(Ext.apply(preppedRecords[idx], {
542 delete group.children;
543 group.fullWidth = fullWidth;
544 if (me.collapsedState[view.id + '-gp-' + group.name]) {
545 group.collapsedCls = me.collapsedCls;
546 group.hdCollapsedCls = me.hdCollapsedCls;
552 // return the data in a grouped format.
553 collectData: function(records, preppedRecords, startIndex, fullWidth, o) {
555 store = me.view.store,
558 if (!me.disabled && store.isGrouped()) {
559 groups = store.getGroups();
560 Ext.Array.each(groups, function(group, idx){
561 me.getGroupRows(group, records, preppedRecords, fullWidth);
571 // adds the groupName to the groupclick, groupdblclick, groupcontextmenu
572 // events that are fired on the view. Chose not to return the actual
573 // group itself because of its expense and because developers can simply
574 // grab the group via store.getGroups(groupName)
575 getFireEventArgs: function(type, view, featureTarget, e) {
576 var returnArray = [type, view, featureTarget],
577 groupBd = Ext.fly(featureTarget.nextSibling, '_grouping'),
578 groupBdId = Ext.getDom(groupBd).id,
579 prefix = view.id + '-gp-',
580 groupName = groupBdId.substr(prefix.length);
582 returnArray.push(groupName, e);