RowExpander.html 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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">// feature idea to enable Ajax loading and then the content
  19. // cache would actually make sense. Should we dictate that they use
  20. // data or support raw html as well?
  21. <span id='Ext-ux-RowExpander'>/**
  22. </span> * @class Ext.ux.RowExpander
  23. * @extends Ext.AbstractPlugin
  24. * Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables
  25. * a second row body which expands/contracts. The expand/contract behavior is configurable to react
  26. * on clicking of the column, double click of the row, and/or hitting enter while a row is selected.
  27. *
  28. * @ptype rowexpander
  29. */
  30. Ext.define('Ext.ux.RowExpander', {
  31. extend: 'Ext.AbstractPlugin',
  32. requires: [
  33. 'Ext.grid.feature.RowBody',
  34. 'Ext.grid.feature.RowWrap'
  35. ],
  36. alias: 'plugin.rowexpander',
  37. rowBodyTpl: null,
  38. <span id='Ext-ux-RowExpander-cfg-expandOnEnter'> /**
  39. </span> * @cfg {Boolean} expandOnEnter
  40. * &lt;tt&gt;true&lt;/tt&gt; to toggle selected row(s) between expanded/collapsed when the enter
  41. * key is pressed (defaults to &lt;tt&gt;true&lt;/tt&gt;).
  42. */
  43. expandOnEnter: true,
  44. <span id='Ext-ux-RowExpander-cfg-expandOnDblClick'> /**
  45. </span> * @cfg {Boolean} expandOnDblClick
  46. * &lt;tt&gt;true&lt;/tt&gt; to toggle a row between expanded/collapsed when double clicked
  47. * (defaults to &lt;tt&gt;true&lt;/tt&gt;).
  48. */
  49. expandOnDblClick: true,
  50. <span id='Ext-ux-RowExpander-cfg-selectRowOnExpand'> /**
  51. </span> * @cfg {Boolean} selectRowOnExpand
  52. * &lt;tt&gt;true&lt;/tt&gt; to select a row when clicking on the expander icon
  53. * (defaults to &lt;tt&gt;false&lt;/tt&gt;).
  54. */
  55. selectRowOnExpand: false,
  56. rowBodyTrSelector: '.x-grid-rowbody-tr',
  57. rowBodyHiddenCls: 'x-grid-row-body-hidden',
  58. rowCollapsedCls: 'x-grid-row-collapsed',
  59. renderer: function(value, metadata, record, rowIdx, colIdx) {
  60. if (colIdx === 0) {
  61. metadata.tdCls = 'x-grid-td-expander';
  62. }
  63. return '&lt;div class=&quot;x-grid-row-expander&quot;&gt;&amp;#160;&lt;/div&gt;';
  64. },
  65. <span id='Ext-ux-RowExpander-event-expandbody'> /**
  66. </span> * @event expandbody
  67. * &lt;b&lt;Fired through the grid's View&lt;/b&gt;
  68. * @param {HTMLElement} rowNode The &amp;lt;tr&gt; element which owns the expanded row.
  69. * @param {Ext.data.Model} record The record providing the data.
  70. * @param {HTMLElement} expandRow The &amp;lt;tr&gt; element containing the expanded data.
  71. */
  72. <span id='Ext-ux-RowExpander-event-collapsebody'> /**
  73. </span> * @event collapsebody
  74. * &lt;b&lt;Fired through the grid's View.&lt;/b&gt;
  75. * @param {HTMLElement} rowNode The &amp;lt;tr&gt; element which owns the expanded row.
  76. * @param {Ext.data.Model} record The record providing the data.
  77. * @param {HTMLElement} expandRow The &amp;lt;tr&gt; element containing the expanded data.
  78. */
  79. constructor: function() {
  80. this.callParent(arguments);
  81. var grid = this.getCmp();
  82. this.recordsExpanded = {};
  83. // &lt;debug&gt;
  84. if (!this.rowBodyTpl) {
  85. Ext.Error.raise(&quot;The 'rowBodyTpl' config is required and is not defined.&quot;);
  86. }
  87. // &lt;/debug&gt;
  88. // TODO: if XTemplate/Template receives a template as an arg, should
  89. // just return it back!
  90. var rowBodyTpl = Ext.create('Ext.XTemplate', this.rowBodyTpl),
  91. features = [{
  92. ftype: 'rowbody',
  93. columnId: this.getHeaderId(),
  94. recordsExpanded: this.recordsExpanded,
  95. rowBodyHiddenCls: this.rowBodyHiddenCls,
  96. rowCollapsedCls: this.rowCollapsedCls,
  97. getAdditionalData: this.getRowBodyFeatureData,
  98. getRowBodyContents: function(data) {
  99. return rowBodyTpl.applyTemplate(data);
  100. }
  101. },{
  102. ftype: 'rowwrap'
  103. }];
  104. if (grid.features) {
  105. grid.features = features.concat(grid.features);
  106. } else {
  107. grid.features = features;
  108. }
  109. // NOTE: features have to be added before init (before Table.initComponent)
  110. },
  111. init: function(grid) {
  112. this.callParent(arguments);
  113. this.grid = grid;
  114. // Columns have to be added in init (after columns has been used to create the
  115. // headerCt). Otherwise, shared column configs get corrupted, e.g., if put in the
  116. // prototype.
  117. this.addExpander();
  118. grid.on('render', this.bindView, this, {single: true});
  119. grid.on('reconfigure', this.onReconfigure, this);
  120. },
  121. onReconfigure: function(){
  122. this.addExpander();
  123. },
  124. addExpander: function(){
  125. this.grid.headerCt.insert(0, this.getHeaderConfig());
  126. },
  127. getHeaderId: function() {
  128. if (!this.headerId) {
  129. this.headerId = Ext.id();
  130. }
  131. return this.headerId;
  132. },
  133. getRowBodyFeatureData: function(data, idx, record, orig) {
  134. var o = Ext.grid.feature.RowBody.prototype.getAdditionalData.apply(this, arguments),
  135. id = this.columnId;
  136. o.rowBodyColspan = o.rowBodyColspan - 1;
  137. o.rowBody = this.getRowBodyContents(data);
  138. o.rowCls = this.recordsExpanded[record.internalId] ? '' : this.rowCollapsedCls;
  139. o.rowBodyCls = this.recordsExpanded[record.internalId] ? '' : this.rowBodyHiddenCls;
  140. o[id + '-tdAttr'] = ' valign=&quot;top&quot; rowspan=&quot;2&quot; ';
  141. if (orig[id+'-tdAttr']) {
  142. o[id+'-tdAttr'] += orig[id+'-tdAttr'];
  143. }
  144. return o;
  145. },
  146. bindView: function() {
  147. var view = this.getCmp().getView(),
  148. viewEl;
  149. if (!view.rendered) {
  150. view.on('render', this.bindView, this, {single: true});
  151. } else {
  152. viewEl = view.getEl();
  153. if (this.expandOnEnter) {
  154. this.keyNav = Ext.create('Ext.KeyNav', viewEl, {
  155. 'enter' : this.onEnter,
  156. scope: this
  157. });
  158. }
  159. if (this.expandOnDblClick) {
  160. view.on('itemdblclick', this.onDblClick, this);
  161. }
  162. this.view = view;
  163. }
  164. },
  165. onEnter: function(e) {
  166. var view = this.view,
  167. ds = view.store,
  168. sm = view.getSelectionModel(),
  169. sels = sm.getSelection(),
  170. ln = sels.length,
  171. i = 0,
  172. rowIdx;
  173. for (; i &lt; ln; i++) {
  174. rowIdx = ds.indexOf(sels[i]);
  175. this.toggleRow(rowIdx);
  176. }
  177. },
  178. toggleRow: function(rowIdx) {
  179. var view = this.view,
  180. rowNode = view.getNode(rowIdx),
  181. row = Ext.get(rowNode),
  182. nextBd = Ext.get(row).down(this.rowBodyTrSelector),
  183. record = view.getRecord(rowNode),
  184. grid = this.getCmp();
  185. if (row.hasCls(this.rowCollapsedCls)) {
  186. row.removeCls(this.rowCollapsedCls);
  187. nextBd.removeCls(this.rowBodyHiddenCls);
  188. this.recordsExpanded[record.internalId] = true;
  189. view.refreshSize();
  190. view.fireEvent('expandbody', rowNode, record, nextBd.dom);
  191. } else {
  192. row.addCls(this.rowCollapsedCls);
  193. nextBd.addCls(this.rowBodyHiddenCls);
  194. this.recordsExpanded[record.internalId] = false;
  195. view.refreshSize();
  196. view.fireEvent('collapsebody', rowNode, record, nextBd.dom);
  197. }
  198. },
  199. onDblClick: function(view, cell, rowIdx, cellIndex, e) {
  200. this.toggleRow(rowIdx);
  201. },
  202. getHeaderConfig: function() {
  203. var me = this,
  204. toggleRow = Ext.Function.bind(me.toggleRow, me),
  205. selectRowOnExpand = me.selectRowOnExpand;
  206. return {
  207. id: this.getHeaderId(),
  208. width: 24,
  209. sortable: false,
  210. resizable: false,
  211. draggable: false,
  212. hideable: false,
  213. menuDisabled: true,
  214. cls: Ext.baseCSSPrefix + 'grid-header-special',
  215. renderer: function(value, metadata) {
  216. metadata.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
  217. return '&lt;div class=&quot;' + Ext.baseCSSPrefix + 'grid-row-expander&quot;&gt;&amp;#160;&lt;/div&gt;';
  218. },
  219. processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
  220. if (type == &quot;mousedown&quot; &amp;&amp; e.getTarget('.x-grid-row-expander')) {
  221. var row = e.getTarget('.x-grid-row');
  222. toggleRow(row);
  223. return selectRowOnExpand;
  224. }
  225. }
  226. };
  227. }
  228. });
  229. </pre>
  230. </body>
  231. </html>