Grouping.html 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  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; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-grid-feature-Grouping'>/**
  19. </span> * This feature allows to display the grid rows aggregated into groups as specified by the {@link Ext.data.Store#groupers}
  20. * specified on the Store. The group will show the title for the group name and then the appropriate records for the group
  21. * underneath. The groups can also be expanded and collapsed.
  22. *
  23. * ## Extra Events
  24. *
  25. * This feature adds several extra events that will be fired on the grid to interact with the groups:
  26. *
  27. * - {@link #groupclick}
  28. * - {@link #groupdblclick}
  29. * - {@link #groupcontextmenu}
  30. * - {@link #groupexpand}
  31. * - {@link #groupcollapse}
  32. *
  33. * ## Menu Augmentation
  34. *
  35. * This feature adds extra options to the grid column menu to provide the user with functionality to modify the grouping.
  36. * This can be disabled by setting the {@link #enableGroupingMenu} option. The option to disallow grouping from being turned off
  37. * by the user is {@link #enableNoGroups}.
  38. *
  39. * ## Controlling Group Text
  40. *
  41. * The {@link #groupHeaderTpl} is used to control the rendered title for each group. It can modified to customized
  42. * the default display.
  43. *
  44. * ## Example Usage
  45. *
  46. * @example
  47. * var store = Ext.create('Ext.data.Store', {
  48. * storeId:'employeeStore',
  49. * fields:['name', 'seniority', 'department'],
  50. * groupField: 'department',
  51. * data: {'employees':[
  52. * { &quot;name&quot;: &quot;Michael Scott&quot;, &quot;seniority&quot;: 7, &quot;department&quot;: &quot;Management&quot; },
  53. * { &quot;name&quot;: &quot;Dwight Schrute&quot;, &quot;seniority&quot;: 2, &quot;department&quot;: &quot;Sales&quot; },
  54. * { &quot;name&quot;: &quot;Jim Halpert&quot;, &quot;seniority&quot;: 3, &quot;department&quot;: &quot;Sales&quot; },
  55. * { &quot;name&quot;: &quot;Kevin Malone&quot;, &quot;seniority&quot;: 4, &quot;department&quot;: &quot;Accounting&quot; },
  56. * { &quot;name&quot;: &quot;Angela Martin&quot;, &quot;seniority&quot;: 5, &quot;department&quot;: &quot;Accounting&quot; }
  57. * ]},
  58. * proxy: {
  59. * type: 'memory',
  60. * reader: {
  61. * type: 'json',
  62. * root: 'employees'
  63. * }
  64. * }
  65. * });
  66. *
  67. * Ext.create('Ext.grid.Panel', {
  68. * title: 'Employees',
  69. * store: Ext.data.StoreManager.lookup('employeeStore'),
  70. * columns: [
  71. * { text: 'Name', dataIndex: 'name' },
  72. * { text: 'Seniority', dataIndex: 'seniority' }
  73. * ],
  74. * features: [{ftype:'grouping'}],
  75. * width: 200,
  76. * height: 275,
  77. * renderTo: Ext.getBody()
  78. * });
  79. *
  80. * **Note:** To use grouping with a grid that has {@link Ext.grid.column.Column#locked locked columns}, you need to supply
  81. * the grouping feature as a config object - so the grid can create two instances of the grouping feature.
  82. *
  83. * @author Nicolas Ferrero
  84. */
  85. Ext.define('Ext.grid.feature.Grouping', {
  86. extend: 'Ext.grid.feature.Feature',
  87. alias: 'feature.grouping',
  88. eventPrefix: 'group',
  89. eventSelector: '.' + Ext.baseCSSPrefix + 'grid-group-hd',
  90. bodySelector: '.' + Ext.baseCSSPrefix + 'grid-group-body',
  91. constructor: function() {
  92. var me = this;
  93. me.collapsedState = {};
  94. me.callParent(arguments);
  95. },
  96. <span id='Ext-grid-feature-Grouping-event-groupclick'> /**
  97. </span> * @event groupclick
  98. * @param {Ext.view.Table} view
  99. * @param {HTMLElement} node
  100. * @param {String} group The name of the group
  101. * @param {Ext.EventObject} e
  102. */
  103. <span id='Ext-grid-feature-Grouping-event-groupdblclick'> /**
  104. </span> * @event groupdblclick
  105. * @param {Ext.view.Table} view
  106. * @param {HTMLElement} node
  107. * @param {String} group The name of the group
  108. * @param {Ext.EventObject} e
  109. */
  110. <span id='Ext-grid-feature-Grouping-event-groupcontextmenu'> /**
  111. </span> * @event groupcontextmenu
  112. * @param {Ext.view.Table} view
  113. * @param {HTMLElement} node
  114. * @param {String} group The name of the group
  115. * @param {Ext.EventObject} e
  116. */
  117. <span id='Ext-grid-feature-Grouping-event-groupcollapse'> /**
  118. </span> * @event groupcollapse
  119. * @param {Ext.view.Table} view
  120. * @param {HTMLElement} node
  121. * @param {String} group The name of the group
  122. */
  123. <span id='Ext-grid-feature-Grouping-event-groupexpand'> /**
  124. </span> * @event groupexpand
  125. * @param {Ext.view.Table} view
  126. * @param {HTMLElement} node
  127. * @param {String} group The name of the group
  128. */
  129. <span id='Ext-grid-feature-Grouping-cfg-groupHeaderTpl'> /**
  130. </span> * @cfg {String/Array/Ext.Template} groupHeaderTpl
  131. * A string Template snippet, an array of strings (optionally followed by an object containing Template methods) to be used to construct a Template, or a Template instance.
  132. *
  133. * - Example 1 (Template snippet):
  134. *
  135. * groupHeaderTpl: 'Group: {name}'
  136. *
  137. * - Example 2 (Array):
  138. *
  139. * groupHeaderTpl: [
  140. * 'Group: ',
  141. * '&lt;div&gt;{name:this.formatName}&lt;/div&gt;',
  142. * {
  143. * formatName: function(name) {
  144. * return Ext.String.trim(name);
  145. * }
  146. * }
  147. * ]
  148. *
  149. * - Example 3 (Template Instance):
  150. *
  151. * groupHeaderTpl: Ext.create('Ext.XTemplate',
  152. * 'Group: ',
  153. * '&lt;div&gt;{name:this.formatName}&lt;/div&gt;',
  154. * {
  155. * formatName: function(name) {
  156. * return Ext.String.trim(name);
  157. * }
  158. * }
  159. * )
  160. *
  161. * @cfg {String} groupHeaderTpl.groupField The field name being grouped by.
  162. * @cfg {String} groupHeaderTpl.columnName The column header associated with the field being grouped by *if there is a column for the field*, falls back to the groupField name.
  163. * @cfg {Mixed} groupHeaderTpl.groupValue The value of the {@link Ext.data.Store#groupField groupField} for the group header being rendered.
  164. * @cfg {String} groupHeaderTpl.renderedGroupValue The rendered value of the {@link Ext.data.Store#groupField groupField} for the group header being rendered, as produced by the column renderer.
  165. * @cfg {String} groupHeaderTpl.name An alias for renderedGroupValue
  166. * @cfg {Object[]} groupHeaderTpl.rows An array of child row data objects as returned by the View's {@link Ext.view.AbstractView#prepareData prepareData} method.
  167. * @cfg {Ext.data.Model[]} groupHeaderTpl.children An array containing the child records for the group being rendered.
  168. */
  169. groupHeaderTpl: '{columnName}: {name}',
  170. <span id='Ext-grid-feature-Grouping-cfg-depthToIndent'> /**
  171. </span> * @cfg {Number} [depthToIndent=17]
  172. * Number of pixels to indent per grouping level
  173. */
  174. depthToIndent: 17,
  175. collapsedCls: Ext.baseCSSPrefix + 'grid-group-collapsed',
  176. hdCollapsedCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsed',
  177. hdCollapsibleCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsible',
  178. //&lt;locale&gt;
  179. <span id='Ext-grid-feature-Grouping-cfg-groupByText'> /**
  180. </span> * @cfg {String} [groupByText=&quot;Group by this field&quot;]
  181. * Text displayed in the grid header menu for grouping by header.
  182. */
  183. groupByText : 'Group by this field',
  184. //&lt;/locale&gt;
  185. //&lt;locale&gt;
  186. <span id='Ext-grid-feature-Grouping-cfg-showGroupsText'> /**
  187. </span> * @cfg {String} [showGroupsText=&quot;Show in groups&quot;]
  188. * Text displayed in the grid header for enabling/disabling grouping.
  189. */
  190. showGroupsText : 'Show in groups',
  191. //&lt;/locale&gt;
  192. <span id='Ext-grid-feature-Grouping-cfg-hideGroupedHeader'> /**
  193. </span> * @cfg {Boolean} [hideGroupedHeader=false]
  194. * True to hide the header that is currently grouped.
  195. */
  196. hideGroupedHeader : false,
  197. <span id='Ext-grid-feature-Grouping-cfg-startCollapsed'> /**
  198. </span> * @cfg {Boolean} [startCollapsed=false]
  199. * True to start all groups collapsed.
  200. */
  201. startCollapsed : false,
  202. <span id='Ext-grid-feature-Grouping-cfg-enableGroupingMenu'> /**
  203. </span> * @cfg {Boolean} [enableGroupingMenu=true]
  204. * True to enable the grouping control in the header menu.
  205. */
  206. enableGroupingMenu : true,
  207. <span id='Ext-grid-feature-Grouping-cfg-enableNoGroups'> /**
  208. </span> * @cfg {Boolean} [enableNoGroups=true]
  209. * True to allow the user to turn off grouping.
  210. */
  211. enableNoGroups : true,
  212. <span id='Ext-grid-feature-Grouping-cfg-collapsible'> /**
  213. </span> * @cfg {Boolean} [collapsible=true]
  214. * Set to `falsee` to disable collapsing groups from the UI.
  215. *
  216. * This is set to `false` when the associated {@link Ext.data.Store store} is
  217. * {@link Ext.data.Store#buffered buffered}.
  218. */
  219. collapsible: true,
  220. enable: function() {
  221. var me = this,
  222. view = me.view,
  223. store = view.store,
  224. groupToggleMenuItem;
  225. me.lastGroupField = me.getGroupField();
  226. if (me.lastGroupIndex) {
  227. me.block();
  228. store.group(me.lastGroupIndex);
  229. me.unblock();
  230. }
  231. me.callParent();
  232. groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
  233. groupToggleMenuItem.setChecked(true, true);
  234. me.refreshIf();
  235. },
  236. disable: function() {
  237. var me = this,
  238. view = me.view,
  239. store = view.store,
  240. remote = store.remoteGroup,
  241. groupToggleMenuItem,
  242. lastGroup;
  243. lastGroup = store.groupers.first();
  244. if (lastGroup) {
  245. me.lastGroupIndex = lastGroup.property;
  246. me.block();
  247. store.clearGrouping();
  248. me.unblock();
  249. }
  250. me.callParent();
  251. groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
  252. groupToggleMenuItem.setChecked(true, true);
  253. groupToggleMenuItem.setChecked(false, true);
  254. me.refreshIf();
  255. },
  256. refreshIf: function() {
  257. var ownerCt = this.grid.ownerCt,
  258. view = this.view;
  259. if (!view.store.remoteGroup &amp;&amp; !this.blockRefresh) {
  260. // We are one side of a lockable grid, so refresh the locking view
  261. if (ownerCt &amp;&amp; ownerCt.lockable) {
  262. ownerCt.view.refresh();
  263. } else {
  264. view.refresh();
  265. }
  266. }
  267. },
  268. getFeatureTpl: function(values, parent, x, xcount) {
  269. return [
  270. '&lt;tpl if=&quot;typeof rows !== \'undefined\'&quot;&gt;',
  271. // group row tpl
  272. '&lt;tr id=&quot;{groupHeaderId}&quot; class=&quot;' + Ext.baseCSSPrefix + 'grid-group-hd {hdCollapsedCls} {collapsibleClass}&quot;&gt;&lt;td class=&quot;' + Ext.baseCSSPrefix + 'grid-cell&quot; colspan=&quot;' + parent.columns.length + '&quot; {[this.indentByDepth(values)]}&gt;&lt;div class=&quot;' + Ext.baseCSSPrefix + 'grid-cell-inner&quot;&gt;&lt;div class=&quot;' + Ext.baseCSSPrefix + 'grid-group-title&quot;&gt;{collapsed}{[this.renderGroupHeaderTpl(values, parent)]}&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;',
  273. // this is the rowbody
  274. '&lt;tr id=&quot;{groupBodyId}&quot; class=&quot;' + Ext.baseCSSPrefix + 'grid-group-body {collapsedCls}&quot;&gt;&lt;td colspan=&quot;' + parent.columns.length + '&quot;&gt;{[this.recurse(values)]}&lt;/td&gt;&lt;/tr&gt;',
  275. '&lt;/tpl&gt;'
  276. ].join('');
  277. },
  278. getFragmentTpl: function() {
  279. var me = this;
  280. return {
  281. indentByDepth: me.indentByDepth,
  282. depthToIndent: me.depthToIndent,
  283. renderGroupHeaderTpl: function(values, parent) {
  284. return Ext.XTemplate.getTpl(me, 'groupHeaderTpl').apply(values, parent);
  285. }
  286. };
  287. },
  288. indentByDepth: function(values) {
  289. return 'style=&quot;padding-left:'+ ((values.depth || 0) * this.depthToIndent) + 'px;&quot;';
  290. },
  291. // Containers holding these components are responsible for
  292. // destroying them, we are just deleting references.
  293. destroy: function() {
  294. delete this.view;
  295. delete this.prunedHeader;
  296. },
  297. // perhaps rename to afterViewRender
  298. attachEvents: function() {
  299. var me = this,
  300. view = me.view;
  301. view.on({
  302. scope: me,
  303. groupclick: me.onGroupClick,
  304. rowfocus: me.onRowFocus
  305. });
  306. view.mon(view.store, {
  307. scope: me,
  308. groupchange: me.onGroupChange,
  309. remove: me.onRemove,
  310. add: me.onAdd,
  311. update: me.onUpdate
  312. });
  313. if (me.enableGroupingMenu) {
  314. me.injectGroupingMenu();
  315. }
  316. me.pruneGroupedHeader();
  317. me.lastGroupField = me.getGroupField();
  318. me.block();
  319. me.onGroupChange();
  320. me.unblock();
  321. },
  322. // If we add a new item that doesn't belong to a rendered group, refresh the view
  323. onAdd: function(store, records){
  324. var me = this,
  325. view = me.view,
  326. groupField = me.getGroupField(),
  327. i = 0,
  328. len = records.length,
  329. activeGroups,
  330. addedGroups,
  331. groups,
  332. needsRefresh,
  333. group;
  334. if (view.rendered) {
  335. addedGroups = {};
  336. activeGroups = {};
  337. for (; i &lt; len; ++i) {
  338. group = records[i].get(groupField);
  339. if (addedGroups[group] === undefined) {
  340. addedGroups[group] = 0;
  341. }
  342. addedGroups[group] += 1;
  343. }
  344. groups = store.getGroups();
  345. for (i = 0, len = groups.length; i &lt; len; ++i) {
  346. group = groups[i];
  347. activeGroups[group.name] = group.children.length;
  348. }
  349. for (group in addedGroups) {
  350. if (addedGroups[group] === activeGroups[group]) {
  351. needsRefresh = true;
  352. break;
  353. }
  354. }
  355. if (needsRefresh) {
  356. view.refresh();
  357. }
  358. }
  359. },
  360. onUpdate: function(store, record, type, changedFields){
  361. var view = this.view;
  362. if (view.rendered &amp;&amp; !changedFields || Ext.Array.contains(changedFields, this.getGroupField())) {
  363. view.refresh();
  364. }
  365. },
  366. onRemove: function(store, record) {
  367. var me = this,
  368. groupField = me.getGroupField(),
  369. removedGroup = record.get(groupField),
  370. view = me.view;
  371. if (view.rendered) {
  372. // If that was the last one in the group, force a refresh
  373. if (store.findExact(groupField, removedGroup) === -1) {
  374. me.view.refresh();
  375. }
  376. }
  377. },
  378. injectGroupingMenu: function() {
  379. var me = this,
  380. headerCt = me.view.headerCt;
  381. headerCt.showMenuBy = me.showMenuBy;
  382. headerCt.getMenuItems = me.getMenuItems();
  383. },
  384. showMenuBy: function(t, header) {
  385. var menu = this.getMenu(),
  386. groupMenuItem = menu.down('#groupMenuItem'),
  387. groupableMth = header.groupable === false ? 'disable' : 'enable';
  388. groupMenuItem[groupableMth]();
  389. Ext.grid.header.Container.prototype.showMenuBy.apply(this, arguments);
  390. },
  391. getMenuItems: function() {
  392. var me = this,
  393. groupByText = me.groupByText,
  394. disabled = me.disabled || !me.getGroupField(),
  395. showGroupsText = me.showGroupsText,
  396. enableNoGroups = me.enableNoGroups,
  397. getMenuItems = me.view.headerCt.getMenuItems;
  398. // runs in the scope of headerCt
  399. return function() {
  400. // We cannot use the method from HeaderContainer's prototype here
  401. // because other plugins or features may already have injected an implementation
  402. var o = getMenuItems.call(this);
  403. o.push('-', {
  404. iconCls: Ext.baseCSSPrefix + 'group-by-icon',
  405. itemId: 'groupMenuItem',
  406. text: groupByText,
  407. handler: me.onGroupMenuItemClick,
  408. scope: me
  409. });
  410. if (enableNoGroups) {
  411. o.push({
  412. itemId: 'groupToggleMenuItem',
  413. text: showGroupsText,
  414. checked: !disabled,
  415. checkHandler: me.onGroupToggleMenuItemClick,
  416. scope: me
  417. });
  418. }
  419. return o;
  420. };
  421. },
  422. <span id='Ext-grid-feature-Grouping-method-onGroupMenuItemClick'> /**
  423. </span> * Group by the header the user has clicked on.
  424. * @private
  425. */
  426. onGroupMenuItemClick: function(menuItem, e) {
  427. var me = this,
  428. menu = menuItem.parentMenu,
  429. hdr = menu.activeHeader,
  430. view = me.view,
  431. store = view.store;
  432. delete me.lastGroupIndex;
  433. me.block();
  434. me.enable();
  435. store.group(hdr.dataIndex);
  436. me.pruneGroupedHeader();
  437. me.unblock();
  438. me.refreshIf();
  439. },
  440. block: function(){
  441. this.blockRefresh = this.view.blockRefresh = true;
  442. },
  443. unblock: function(){
  444. this.blockRefresh = this.view.blockRefresh = false;
  445. },
  446. <span id='Ext-grid-feature-Grouping-method-onGroupToggleMenuItemClick'> /**
  447. </span> * Turn on and off grouping via the menu
  448. * @private
  449. */
  450. onGroupToggleMenuItemClick: function(menuItem, checked) {
  451. this[checked ? 'enable' : 'disable']();
  452. },
  453. <span id='Ext-grid-feature-Grouping-method-pruneGroupedHeader'> /**
  454. </span> * Prunes the grouped header from the header container
  455. * @private
  456. */
  457. pruneGroupedHeader: function() {
  458. var me = this,
  459. header = me.getGroupedHeader();
  460. if (me.hideGroupedHeader &amp;&amp; header) {
  461. if (me.prunedHeader) {
  462. me.prunedHeader.show();
  463. }
  464. me.prunedHeader = header;
  465. header.hide();
  466. }
  467. },
  468. getGroupedHeader: function(){
  469. var groupField = this.getGroupField(),
  470. headerCt = this.view.headerCt;
  471. return groupField ? headerCt.down('[dataIndex=' + groupField + ']') : null;
  472. },
  473. getGroupField: function(){
  474. var group = this.view.store.groupers.first();
  475. if (group) {
  476. return group.property;
  477. }
  478. return '';
  479. },
  480. <span id='Ext-grid-feature-Grouping-method-onRowFocus'> /**
  481. </span> * When a row gains focus, expand the groups above it
  482. * @private
  483. */
  484. onRowFocus: function(rowIdx) {
  485. var node = this.view.getNode(rowIdx),
  486. groupBd = Ext.fly(node).up('.' + this.collapsedCls);
  487. if (groupBd) {
  488. // for multiple level groups, should expand every groupBd
  489. // above
  490. this.expand(groupBd);
  491. }
  492. },
  493. <span id='Ext-grid-feature-Grouping-method-isExpanded'> /**
  494. </span> * Returns `true` if the named group is expanded.
  495. * @param {String} groupName The group name as returned from {@link Ext.data.Store#getGroupString getGroupString}. This is usually the value of
  496. * the {@link Ext.data.Store#groupField groupField}.
  497. * @return {Boolean} `true` if the group defined by that value is expanded.
  498. */
  499. isExpanded: function(groupName) {
  500. return (this.collapsedState[groupName] === false);
  501. },
  502. <span id='Ext-grid-feature-Grouping-method-expand'> /**
  503. </span> * Expand a group
  504. * @param {String/Ext.Element} groupName The group name, or the element that contains the group body
  505. * @param {Boolean} focus Pass `true` to focus the group after expand.
  506. */
  507. expand: function(groupName, focus, /*private*/ preventSizeCalculation) {
  508. var me = this,
  509. view = me.view,
  510. groupHeader,
  511. groupBody,
  512. lockingPartner = me.lockingPartner;
  513. // We've been passed the group name
  514. if (Ext.isString(groupName)) {
  515. groupBody = Ext.fly(me.getGroupBodyId(groupName), '_grouping');
  516. }
  517. // We've been passed an element
  518. else {
  519. groupBody = Ext.fly(groupName, '_grouping')
  520. groupName = me.getGroupName(groupBody);
  521. }
  522. groupHeader = Ext.get(me.getGroupHeaderId(groupName));
  523. // If we are collapsed...
  524. if (me.collapsedState[groupName]) {
  525. groupBody.removeCls(me.collapsedCls);
  526. groupBody.prev().removeCls(me.hdCollapsedCls);
  527. if (preventSizeCalculation !== true) {
  528. view.refreshSize();
  529. }
  530. view.fireEvent('groupexpand', view, groupHeader, groupName);
  531. me.collapsedState[groupName] = false;
  532. // If we are one side of a locking view, the other side has to stay in sync
  533. if (lockingPartner) {
  534. lockingPartner.expand(groupName, focus, preventSizeCalculation);
  535. }
  536. if (focus) {
  537. groupBody.scrollIntoView(view.el, null, true);
  538. }
  539. }
  540. },
  541. <span id='Ext-grid-feature-Grouping-method-expandAll'> /**
  542. </span> * Expand all groups
  543. */
  544. expandAll: function(){
  545. var me = this,
  546. view = me.view,
  547. els = view.el.select(me.eventSelector).elements,
  548. e,
  549. eLen = els.length;
  550. for (e = 0; e &lt; eLen; e++) {
  551. me.expand(Ext.fly(els[e]).next(), false, true);
  552. }
  553. view.refreshSize();
  554. },
  555. <span id='Ext-grid-feature-Grouping-method-collapse'> /**
  556. </span> * Collapse a group
  557. * @param {String/Ext.Element} groupName The group name, or the element that contains group body
  558. * @param {Boolean} focus Pass `true` to focus the group after expand.
  559. */
  560. collapse: function(groupName, focus, /*private*/ preventSizeCalculation) {
  561. var me = this,
  562. view = me.view,
  563. groupHeader,
  564. groupBody,
  565. lockingPartner = me.lockingPartner;
  566. // We've been passed the group name
  567. if (Ext.isString(groupName)) {
  568. groupBody = Ext.fly(me.getGroupBodyId(groupName), '_grouping');
  569. }
  570. // We've been passed an element
  571. else {
  572. groupBody = Ext.fly(groupName, '_grouping')
  573. groupName = me.getGroupName(groupBody);
  574. }
  575. groupHeader = Ext.get(me.getGroupHeaderId(groupName));
  576. // If we are not collapsed...
  577. if (!me.collapsedState[groupName]) {
  578. groupBody.addCls(me.collapsedCls);
  579. groupBody.prev().addCls(me.hdCollapsedCls);
  580. if (preventSizeCalculation !== true) {
  581. view.refreshSize();
  582. }
  583. view.fireEvent('groupcollapse', view, groupHeader, groupName);
  584. me.collapsedState[groupName] = true;
  585. // If we are one side of a locking view, the other side has to stay in sync
  586. if (lockingPartner) {
  587. lockingPartner.collapse(groupName, focus, preventSizeCalculation);
  588. }
  589. if (focus) {
  590. groupHeader.scrollIntoView(view.el, null, true);
  591. }
  592. }
  593. },
  594. <span id='Ext-grid-feature-Grouping-method-collapseAll'> /**
  595. </span> * Collapse all groups
  596. */
  597. collapseAll: function() {
  598. var me = this,
  599. view = me.view,
  600. els = view.el.select(me.eventSelector).elements,
  601. e,
  602. eLen = els.length;
  603. for (e = 0; e &lt; eLen; e++) {
  604. me.collapse(Ext.fly(els[e]).next(), false, true);
  605. }
  606. view.refreshSize();
  607. },
  608. onGroupChange: function(){
  609. var me = this,
  610. field = me.getGroupField(),
  611. menuItem,
  612. visibleGridColumns,
  613. groupingByLastVisibleColumn;
  614. if (me.hideGroupedHeader) {
  615. if (me.lastGroupField) {
  616. menuItem = me.getMenuItem(me.lastGroupField);
  617. if (menuItem) {
  618. menuItem.setChecked(true);
  619. }
  620. }
  621. if (field) {
  622. visibleGridColumns = me.view.headerCt.getVisibleGridColumns();
  623. // See if we are being asked to group by the sole remaining visible column.
  624. // If so, then do not hide that column.
  625. groupingByLastVisibleColumn = ((visibleGridColumns.length === 1) &amp;&amp; (visibleGridColumns[0].dataIndex == field));
  626. menuItem = me.getMenuItem(field);
  627. if (menuItem &amp;&amp; !groupingByLastVisibleColumn) {
  628. menuItem.setChecked(false);
  629. }
  630. }
  631. }
  632. me.refreshIf();
  633. me.lastGroupField = field;
  634. },
  635. <span id='Ext-grid-feature-Grouping-method-getMenuItem'> /**
  636. </span> * Gets the related menu item for a dataIndex
  637. * @private
  638. * @return {Ext.grid.header.Container} The header
  639. */
  640. getMenuItem: function(dataIndex){
  641. var view = this.view,
  642. header = view.headerCt.down('gridcolumn[dataIndex=' + dataIndex + ']'),
  643. menu = view.headerCt.getMenu();
  644. return header ? menu.down('menuitem[headerId='+ header.id +']') : null;
  645. },
  646. <span id='Ext-grid-feature-Grouping-method-onGroupClick'> /**
  647. </span> * Toggle between expanded/collapsed state when clicking on
  648. * the group.
  649. * @private
  650. */
  651. onGroupClick: function(view, rowElement, groupName, e) {
  652. var me = this;
  653. if (me.collapsible) {
  654. if (me.collapsedState[groupName]) {
  655. me.expand(groupName);
  656. } else {
  657. me.collapse(groupName);
  658. }
  659. }
  660. },
  661. // Injects isRow and closeRow into the metaRowTpl.
  662. getMetaRowTplFragments: function() {
  663. return {
  664. isRow: this.isRow,
  665. closeRow: this.closeRow
  666. };
  667. },
  668. // injected into rowtpl and wrapped around metaRowTpl
  669. // becomes part of the standard tpl
  670. isRow: function() {
  671. return '&lt;tpl if=&quot;typeof rows === \'undefined\'&quot;&gt;';
  672. },
  673. // injected into rowtpl and wrapped around metaRowTpl
  674. // becomes part of the standard tpl
  675. closeRow: function() {
  676. return '&lt;/tpl&gt;';
  677. },
  678. // isRow and closeRow are injected via getMetaRowTplFragments
  679. mutateMetaRowTpl: function(metaRowTpl) {
  680. metaRowTpl.unshift('{[this.isRow()]}');
  681. metaRowTpl.push('{[this.closeRow()]}');
  682. },
  683. // injects an additional style attribute via tdAttrKey with the proper
  684. // amount of padding
  685. getAdditionalData: function(data, idx, record, orig) {
  686. var view = this.view,
  687. hCt = view.headerCt,
  688. col = hCt.items.getAt(0),
  689. o = {},
  690. tdAttrKey;
  691. // If there *are* any columne in this grid (possible empty side of a locking grid)...
  692. // Add the padding-left style to indent the row according to grouping depth.
  693. // Preserve any current tdAttr that a user may have set.
  694. if (col) {
  695. tdAttrKey = col.id + '-tdAttr';
  696. o[tdAttrKey] = this.indentByDepth(data) + &quot; &quot; + (orig[tdAttrKey] ? orig[tdAttrKey] : '');
  697. o.collapsed = 'true';
  698. o.data = record.getData();
  699. }
  700. return o;
  701. },
  702. // return matching preppedRecords
  703. getGroupRows: function(group, records, preppedRecords, fullWidth) {
  704. var me = this,
  705. children = group.children,
  706. rows = group.rows = [],
  707. view = me.view,
  708. header = me.getGroupedHeader(),
  709. groupField = me.getGroupField(),
  710. index = -1,
  711. r,
  712. rLen = records.length,
  713. record;
  714. // Buffered rendering implies that user collapsing is disabled.
  715. if (view.store.buffered) {
  716. me.collapsible = false;
  717. }
  718. group.viewId = view.id;
  719. for (r = 0; r &lt; rLen; r++) {
  720. record = records[r];
  721. if (record.get(groupField) == group.name) {
  722. index = r;
  723. }
  724. if (Ext.Array.indexOf(children, record) != -1) {
  725. rows.push(Ext.apply(preppedRecords[r], {
  726. depth : 1
  727. }));
  728. }
  729. }
  730. group.groupField = groupField,
  731. group.groupHeaderId = me.getGroupHeaderId(group.name);
  732. group.groupBodyId = me.getGroupBodyId(group.name);
  733. group.fullWidth = fullWidth;
  734. group.columnName = header ? header.text : groupField;
  735. group.groupValue = group.name;
  736. // Here we attempt to overwrite the group name value from the Store with
  737. // the get the rendered value of the column from the *prepped* record
  738. if (header &amp;&amp; index &gt; -1) {
  739. group.name = group.renderedValue = preppedRecords[index][header.id];
  740. }
  741. if (me.collapsedState[group.name]) {
  742. group.collapsedCls = me.collapsedCls;
  743. group.hdCollapsedCls = me.hdCollapsedCls;
  744. } else {
  745. group.collapsedCls = group.hdCollapsedCls = '';
  746. }
  747. // Collapsibility of groups may be disabled.
  748. if (me.collapsible) {
  749. group.collapsibleClass = me.hdCollapsibleCls;
  750. } else {
  751. group.collapsibleClass = '';
  752. }
  753. return group;
  754. },
  755. // Create an associated DOM id for the group's header element given the group name
  756. getGroupHeaderId: function(groupName) {
  757. return this.view.id + '-hd-' + groupName;
  758. },
  759. // Create an associated DOM id for the group's body element given the group name
  760. getGroupBodyId: function(groupName) {
  761. return this.view.id + '-bd-' + groupName;
  762. },
  763. // Get the group name from an associated element whether it's within a header or a body
  764. getGroupName: function(element) {
  765. var me = this,
  766. targetEl;
  767. // See if element is, or is within a group header. If so, we can extract its name
  768. targetEl = Ext.fly(element).findParent(me.eventSelector);
  769. if (targetEl) {
  770. return targetEl.id.split(this.view.id + '-hd-')[1];
  771. }
  772. // See if element is, or is within a group body. If so, we can extract its name
  773. targetEl = Ext.fly(element).findParent(me.bodySelector);
  774. if (targetEl) {
  775. return targetEl.id.split(this.view.id + '-bd-')[1];
  776. }
  777. },
  778. // return the data in a grouped format.
  779. collectData: function(records, preppedRecords, startIndex, fullWidth, o) {
  780. var me = this,
  781. store = me.view.store,
  782. collapsedState = me.collapsedState,
  783. collapseGroups,
  784. g,
  785. groups, gLen, group;
  786. if (me.startCollapsed) {
  787. // If we start collapse, we'll set the state of the groups here
  788. // and unset the flag so any subsequent expand/collapse is
  789. // managed by the feature
  790. me.startCollapsed = false;
  791. collapseGroups = true;
  792. }
  793. if (!me.disabled &amp;&amp; store.isGrouped()) {
  794. o.rows = groups = store.getGroups();
  795. gLen = groups.length;
  796. for (g = 0; g &lt; gLen; g++) {
  797. group = groups[g];
  798. if (collapseGroups) {
  799. collapsedState[group.name] = true;
  800. }
  801. me.getGroupRows(group, records, preppedRecords, fullWidth);
  802. }
  803. }
  804. return o;
  805. },
  806. // adds the groupName to the groupclick, groupdblclick, groupcontextmenu
  807. // events that are fired on the view. Chose not to return the actual
  808. // group itself because of its expense and because developers can simply
  809. // grab the group via store.getGroups(groupName)
  810. getFireEventArgs: function(type, view, targetEl, e) {
  811. return [type, view, targetEl, this.getGroupName(targetEl), e];
  812. }
  813. });
  814. </pre>
  815. </body>
  816. </html>