46472dd40940692dd4820e5d93aeda46e510c330.svn-base 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. /*
  2. * Ext JS Library 2.0 Copyright(c) 2006-2007, Ext JS, LLC. licensing@extjs.com
  3. *
  4. * http://extjs.com/license
  5. */
  6. /**
  7. * @class Ext.grid.GroupingView
  8. * @extends Ext.grid.GridView Adds the ability for single level grouping to the
  9. * grid.
  10. *
  11. * <pre><code>
  12. * var grid = new Ext.grid.GridPanel({
  13. * // A groupingStore is required for a GroupingView
  14. * store : new Ext.data.GroupingStore({
  15. * reader : reader,
  16. * data : xg.dummyData,
  17. * sortInfo : {
  18. * field : 'company',
  19. * direction : &quot;ASC&quot;
  20. * },
  21. * groupField : 'industry'
  22. * }),
  23. *
  24. * columns : [{
  25. * id : 'company',
  26. * header : &quot;Company&quot;,
  27. * width : 60,
  28. * sortable : true,
  29. * dataIndex : 'company'
  30. * }, {
  31. * header : &quot;Price&quot;,
  32. * width : 20,
  33. * sortable : true,
  34. * renderer : Ext.util.Format.usMoney,
  35. * dataIndex : 'price'
  36. * }, {
  37. * header : &quot;Change&quot;,
  38. * width : 20,
  39. * sortable : true,
  40. * dataIndex : 'change',
  41. * renderer : Ext.util.Format.usMoney
  42. * }, {
  43. * header : &quot;Industry&quot;,
  44. * width : 20,
  45. * sortable : true,
  46. * dataIndex : 'industry'
  47. * }, {
  48. * header : &quot;Last Updated&quot;,
  49. * width : 20,
  50. * sortable : true,
  51. * renderer : Ext.util.Format.dateRenderer('m/d/Y'),
  52. * dataIndex : 'lastChange'
  53. * }],
  54. *
  55. * view : new Ext.grid.GroupingView({
  56. * forceFit : true,
  57. * // custom grouping text template to display the number of items per group
  58. * groupTextTpl : '{text} ({[values.rs.length]} {[values.rs.length &gt; 1 ? &quot;Items&quot; : &quot;Item&quot;]})'
  59. * }),
  60. *
  61. * frame : true,
  62. * width : 700,
  63. * height : 450,
  64. * collapsible : true,
  65. * animCollapse : false,
  66. * title : 'Grouping Example',
  67. * iconCls : 'icon-grid',
  68. * renderTo : document.body
  69. * });
  70. * </code></pre>
  71. *
  72. * @constructor
  73. * @param {Object}
  74. * config
  75. */
  76. Ext.grid.GroupingView = Ext.extend(Ext.grid.GridView, {
  77. /**
  78. * @cfg {Boolean} hideGroupedColumn True to hide the column that is
  79. * currently grouped
  80. */
  81. hideGroupedColumn : false,
  82. /**
  83. * @cfg {Boolean} showGroupName True to display the name for each set of
  84. * grouped rows (defaults to false)
  85. */
  86. showGroupName : true,
  87. /**
  88. * @cfg {Boolean} startCollapsed True to start all groups collapsed
  89. */
  90. startCollapsed : false,
  91. /**
  92. * @cfg {Boolean} enableGrouping False to disable grouping functionality
  93. * (defaults to true)
  94. */
  95. enableGrouping : true,
  96. /**
  97. * @cfg {Boolean} enableGroupingMenu True to enable the grouping control in
  98. * the column menu
  99. */
  100. enableGroupingMenu : true,
  101. /**
  102. * @cfg {Boolean} enableNoGroups True to allow the user to turn off grouping
  103. */
  104. enableNoGroups : true,
  105. /**
  106. * @cfg {String} emptyGroupText The text to display when there is an empty
  107. * group value
  108. */
  109. emptyGroupText : '(None)',
  110. /**
  111. * @cfg {Boolean} ignoreAdd True to skip refreshing the view when new rows
  112. * are added (defaults to false)
  113. */
  114. ignoreAdd : false,
  115. /**
  116. * @cfg {String} groupTextTpl The template used to render the group text
  117. */
  118. groupTextTpl : '{text}',
  119. // private
  120. gidSeed : 1000,
  121. // private
  122. initTemplates : function() {
  123. Ext.grid.GroupingView.superclass.initTemplates.call(this);
  124. this.state = {};
  125. var sm = this.grid.getSelectionModel();
  126. sm.on(sm.selectRow ? 'beforerowselect' : 'beforecellselect',
  127. this.onBeforeRowSelect, this);
  128. if (!this.startGroup) {
  129. this.startGroup = new Ext.XTemplate(
  130. '<div id="{groupId}" class="x-grid-group {cls}">',
  131. '<div id="{groupId}-hd" class="x-grid-group-hd" style="{style}"><div>',
  132. this.groupTextTpl, '</div></div>',
  133. '<div id="{groupId}-bd" class="x-grid-group-body">');
  134. }
  135. this.startGroup.compile();
  136. this.endGroup = '</div></div>';
  137. },
  138. // private
  139. findGroup : function(el) {
  140. return Ext.fly(el).up('.x-grid-group', this.mainBody.dom);
  141. },
  142. // private
  143. getGroups : function() {
  144. return this.hasRows() ? this.mainBody.dom.childNodes : [];
  145. },
  146. // private
  147. onAdd : function() {
  148. if (this.enableGrouping && !this.ignoreAdd) {
  149. var ss = this.getScrollState();
  150. this.refresh();
  151. this.restoreScroll(ss);
  152. } else if (!this.enableGrouping) {
  153. Ext.grid.GroupingView.superclass.onAdd.apply(this, arguments);
  154. }
  155. },
  156. // private
  157. onRemove : function(ds, record, index, isUpdate) {
  158. Ext.grid.GroupingView.superclass.onRemove.apply(this, arguments);
  159. var g = document.getElementById(record._groupId);
  160. if (g && g.childNodes[1].childNodes.length < 1) {
  161. Ext.removeNode(g);
  162. }
  163. this.applyEmptyText();
  164. },
  165. // private
  166. refreshRow : function(record) {
  167. if (this.ds.getCount() == 1) {
  168. this.refresh();
  169. } else {
  170. this.isUpdating = true;
  171. Ext.grid.GroupingView.superclass.refreshRow.apply(this, arguments);
  172. this.isUpdating = false;
  173. }
  174. },
  175. // private
  176. beforeMenuShow : function() {
  177. var field = this.getGroupField();
  178. var g = this.hmenu.items.get('groupBy');
  179. if (g) {
  180. g.setDisabled(this.cm.config[this.hdCtxIndex].groupable === false);
  181. }
  182. var s = this.hmenu.items.get('showGroups');
  183. if (s) {
  184. s.setChecked(!!field);
  185. }
  186. },
  187. // private
  188. renderUI : function() {
  189. Ext.grid.GroupingView.superclass.renderUI.call(this);
  190. this.mainBody.on('mousedown', this.interceptMouse, this);
  191. if (this.enableGroupingMenu && this.hmenu) {
  192. this.hmenu.add('-', {
  193. id : 'groupBy',
  194. text : this.groupByText,
  195. handler : this.onGroupByClick,
  196. scope : this,
  197. iconCls : 'x-group-by-icon'
  198. });
  199. if (this.enableNoGroups) {
  200. this.hmenu.add({
  201. id : 'showGroups',
  202. text : this.showGroupsText,
  203. checked : true,
  204. checkHandler : this.onShowGroupsClick,
  205. scope : this
  206. });
  207. }
  208. this.hmenu.on('beforeshow', this.beforeMenuShow, this);
  209. }
  210. },
  211. // private
  212. onGroupByClick : function() {
  213. this.grid.store.groupBy(this.cm.getDataIndex(this.hdCtxIndex));
  214. },
  215. // private
  216. onShowGroupsClick : function(mi, checked) {
  217. if (checked) {
  218. this.onGroupByClick();
  219. } else {
  220. this.grid.store.clearGrouping();
  221. }
  222. },
  223. /**
  224. * Toggles the specified group if no value is passed, otherwise sets the
  225. * expanded state of the group to the value passed.
  226. *
  227. * @param {String}
  228. * groupId The groupId assigned to the group (see getGroupId)
  229. * @param {Boolean}
  230. * expanded (optional)
  231. */
  232. toggleGroup : function(group, expanded) {
  233. this.grid.stopEditing();
  234. group = Ext.getDom(group);
  235. var gel = Ext.fly(group);
  236. expanded = expanded !== undefined ? expanded : gel
  237. .hasClass('x-grid-group-collapsed');
  238. this.state[gel.dom.id] = expanded;
  239. gel[expanded ? 'removeClass' : 'addClass']('x-grid-group-collapsed');
  240. },
  241. /**
  242. * Toggles all groups if no value is passed, otherwise sets the expanded
  243. * state of all groups to the value passed.
  244. *
  245. * @param {Boolean}
  246. * expanded (optional)
  247. */
  248. toggleAllGroups : function(expanded) {
  249. var groups = this.getGroups();
  250. for (var i = 0, len = groups.length; i < len; i++) {
  251. this.toggleGroup(groups[i], expanded);
  252. }
  253. },
  254. /**
  255. * Expands all grouped rows.
  256. */
  257. expandAllGroups : function() {
  258. this.toggleAllGroups(true);
  259. },
  260. /**
  261. * Collapses all grouped rows.
  262. */
  263. collapseAllGroups : function() {
  264. this.toggleAllGroups(false);
  265. },
  266. // private
  267. interceptMouse : function(e) {
  268. var hd = e.getTarget('.x-grid-group-hd', this.mainBody);
  269. if (hd) {
  270. e.stopEvent();
  271. this.toggleGroup(hd.parentNode);
  272. }
  273. },
  274. // private
  275. getGroup : function(v, r, groupRenderer, rowIndex, colIndex, ds) {
  276. var g = groupRenderer
  277. ? groupRenderer(v, {}, r, rowIndex, colIndex, ds)
  278. : String(v);
  279. if (g === '') {
  280. g = this.cm.config[colIndex].emptyGroupText || this.emptyGroupText;
  281. }
  282. return g;
  283. },
  284. // private
  285. getGroupField : function() {
  286. return this.grid.store.getGroupState();
  287. },
  288. // private
  289. renderRows : function() {
  290. var groupField = this.getGroupField();
  291. var eg = !!groupField;
  292. // if they turned off grouping and the last grouped field is hidden
  293. if (this.hideGroupedColumn) {
  294. var colIndex = this.cm.findColumnIndex(groupField);
  295. if (!eg && this.lastGroupField !== undefined) {
  296. this.mainBody.update('');
  297. this.cm.setHidden(this.cm.findColumnIndex(this.lastGroupField),
  298. false);
  299. delete this.lastGroupField;
  300. } else if (eg && this.lastGroupField === undefined) {
  301. this.lastGroupField = groupField;
  302. this.cm.setHidden(colIndex, true);
  303. } else if (eg && this.lastGroupField !== undefined
  304. && groupField !== this.lastGroupField) {
  305. this.mainBody.update('');
  306. var oldIndex = this.cm.findColumnIndex(this.lastGroupField);
  307. this.cm.setHidden(oldIndex, false);
  308. this.lastGroupField = groupField;
  309. this.cm.setHidden(colIndex, true);
  310. }
  311. }
  312. return Ext.grid.GroupingView.superclass.renderRows.apply(this,
  313. arguments);
  314. },
  315. // private
  316. doRender : function(cs, rs, ds, startRow, colCount, stripe) {
  317. if (rs.length < 1) {
  318. return '';
  319. }
  320. var groupField = this.getGroupField();
  321. var colIndex = this.cm.findColumnIndex(groupField);
  322. this.enableGrouping = !!groupField;
  323. if (!this.enableGrouping || this.isUpdating) {
  324. return Ext.grid.GroupingView.superclass.doRender.apply(this,
  325. arguments);
  326. }
  327. var gstyle = 'width:' + this.getTotalWidth() + ';';
  328. var gidPrefix = this.grid.getGridEl().id;
  329. var cfg = this.cm.config[colIndex];
  330. var groupRenderer = cfg.groupRenderer || cfg.renderer;
  331. var cls = this.startCollapsed ? 'x-grid-group-collapsed' : '';
  332. var prefix = this.showGroupName
  333. ? (cfg.groupName || cfg.header) + ': '
  334. : '';
  335. var groups = [], curGroup, i, len, gid;
  336. for (i = 0, len = rs.length; i < len; i++) {
  337. var rowIndex = startRow + i;
  338. var r = rs[i], gvalue = r.data[groupField], g = this.getGroup(
  339. gvalue, r, groupRenderer, rowIndex, colIndex, ds);
  340. if (!curGroup || curGroup.group != g) {
  341. gid = gidPrefix + '-gp-' + groupField + '-' + g;
  342. var gcls = cls ? cls : (this.state[gid] === false
  343. ? 'x-grid-group-collapsed'
  344. : '');
  345. curGroup = {
  346. group : g,
  347. gvalue : gvalue,
  348. text : prefix + g,
  349. groupId : gid,
  350. startRow : rowIndex,
  351. rs : [r],
  352. cls : gcls,
  353. style : gstyle
  354. };
  355. groups.push(curGroup);
  356. } else {
  357. curGroup.rs.push(r);
  358. }
  359. r._groupId = gid;
  360. }
  361. var buf = [];
  362. for (i = 0, len = groups.length; i < len; i++) {
  363. var g = groups[i];
  364. this.doGroupStart(buf, g, cs, ds, colCount);
  365. buf[buf.length] = Ext.grid.GroupingView.superclass.doRender.call(
  366. this, cs, g.rs, ds, g.startRow, colCount, stripe);
  367. this.doGroupEnd(buf, g, cs, ds, colCount);
  368. }
  369. return buf.join('');
  370. },
  371. /**
  372. * Dynamically tries to determine the groupId of a specific value
  373. *
  374. * @param {String}
  375. * value
  376. * @return {String} The group id
  377. */
  378. getGroupId : function(value) {
  379. var gidPrefix = this.grid.getGridEl().id;
  380. var groupField = this.getGroupField();
  381. var colIndex = this.cm.findColumnIndex(groupField);
  382. var cfg = this.cm.config[colIndex];
  383. var groupRenderer = cfg.groupRenderer || cfg.renderer;
  384. var gtext = this.getGroup(value, {
  385. data : {}
  386. }, groupRenderer, 0, colIndex, this.ds);
  387. return gidPrefix + '-gp-' + groupField + '-' + value;
  388. },
  389. // private
  390. doGroupStart : function(buf, g, cs, ds, colCount) {
  391. buf[buf.length] = this.startGroup.apply(g);
  392. },
  393. // private
  394. doGroupEnd : function(buf, g, cs, ds, colCount) {
  395. buf[buf.length] = this.endGroup;
  396. },
  397. // private
  398. getRows : function() {
  399. if (!this.enableGrouping) {
  400. return Ext.grid.GroupingView.superclass.getRows.call(this);
  401. }
  402. var r = [];
  403. var g, gs = this.getGroups();
  404. for (var i = 0, len = gs.length; i < len; i++) {
  405. g = gs[i].childNodes[1].childNodes;
  406. for (var j = 0, jlen = g.length; j < jlen; j++) {
  407. r[r.length] = g[j];
  408. }
  409. }
  410. return r;
  411. },
  412. // private
  413. updateGroupWidths : function() {
  414. if (!this.enableGrouping || !this.hasRows()) {
  415. return;
  416. }
  417. var tw = Math.max(this.cm.getTotalWidth(), this.el.dom.offsetWidth
  418. - this.scrollOffset)
  419. + 'px';
  420. var gs = this.getGroups();
  421. for (var i = 0, len = gs.length; i < len; i++) {
  422. gs[i].firstChild.style.width = tw;
  423. }
  424. },
  425. // private
  426. onColumnWidthUpdated : function(col, w, tw) {
  427. this.updateGroupWidths();
  428. },
  429. // private
  430. onAllColumnWidthsUpdated : function(ws, tw) {
  431. this.updateGroupWidths();
  432. },
  433. // private
  434. onColumnHiddenUpdated : function(col, hidden, tw) {
  435. this.updateGroupWidths();
  436. },
  437. // private
  438. onLayout : function() {
  439. this.updateGroupWidths();
  440. },
  441. // private
  442. onBeforeRowSelect : function(sm, rowIndex) {
  443. if (!this.enableGrouping) {
  444. return;
  445. }
  446. var row = this.getRow(rowIndex);
  447. if (row && !row.offsetParent) {
  448. var g = this.findGroup(row);
  449. this.toggleGroup(g, true);
  450. }
  451. },
  452. /**
  453. * @cfg {String} groupByText Text displayed in the grid header menu for
  454. * grouping by a column (defaults to 'Group By This Field').
  455. */
  456. groupByText : 'Group By This Field',
  457. /**
  458. * @cfg {String} showGroupsText Text displayed in the grid header for
  459. * enabling/disabling grouping (defaults to 'Show in Groups').
  460. */
  461. showGroupsText : 'Show in Groups'
  462. });
  463. // private
  464. Ext.grid.GroupingView.GROUP_ID = 1000;