3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
15 // feature idea to enable Ajax loading and then the content
16 // cache would actually make sense. Should we dictate that they use
17 // data or support raw html as well?
20 * @class Ext.ux.RowExpander
21 * @extends Ext.AbstractPlugin
22 * Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables
23 * a second row body which expands/contracts. The expand/contract behavior is configurable to react
24 * on clicking of the column, double click of the row, and/or hitting enter while a row is selected.
28 Ext.define('Ext.ux.RowExpander', {
29 extend: 'Ext.AbstractPlugin',
32 'Ext.grid.feature.RowBody',
33 'Ext.grid.feature.RowWrap'
36 alias: 'plugin.rowexpander',
41 * @cfg {Boolean} expandOnEnter
42 * <tt>true</tt> to toggle selected row(s) between expanded/collapsed when the enter
43 * key is pressed (defaults to <tt>true</tt>).
48 * @cfg {Boolean} expandOnDblClick
49 * <tt>true</tt> to toggle a row between expanded/collapsed when double clicked
50 * (defaults to <tt>true</tt>).
52 expandOnDblClick: true,
55 * @cfg {Boolean} selectRowOnExpand
56 * <tt>true</tt> to select a row when clicking on the expander icon
57 * (defaults to <tt>false</tt>).
59 selectRowOnExpand: false,
61 rowBodyTrSelector: '.x-grid-rowbody-tr',
62 rowBodyHiddenCls: 'x-grid-row-body-hidden',
63 rowCollapsedCls: 'x-grid-row-collapsed',
67 renderer: function(value, metadata, record, rowIdx, colIdx) {
69 metadata.tdCls = 'x-grid-td-expander';
71 return '<div class="x-grid-row-expander"> </div>';
76 * <b<Fired through the grid's View</b>
77 * @param {HtmlElement} rowNode The <tr> element which owns the expanded row.
78 * @param {Ext.data.Model} record The record providing the data.
79 * @param {HtmlElement} expandRow The <tr> element containing the expanded data.
83 * <b<Fired through the grid's View.</b>
84 * @param {HtmlElement} rowNode The <tr> element which owns the expanded row.
85 * @param {Ext.data.Model} record The record providing the data.
86 * @param {HtmlElement} expandRow The <tr> element containing the expanded data.
89 constructor: function() {
90 this.callParent(arguments);
91 var grid = this.getCmp();
92 this.recordsExpanded = {};
94 if (!this.rowBodyTpl) {
95 Ext.Error.raise("The 'rowBodyTpl' config is required and is not defined.");
98 // TODO: if XTemplate/Template receives a template as an arg, should
99 // just return it back!
100 var rowBodyTpl = Ext.create('Ext.XTemplate', this.rowBodyTpl),
103 columnId: this.getHeaderId(),
104 recordsExpanded: this.recordsExpanded,
105 rowBodyHiddenCls: this.rowBodyHiddenCls,
106 rowCollapsedCls: this.rowCollapsedCls,
107 getAdditionalData: this.getRowBodyFeatureData,
108 getRowBodyContents: function(data) {
109 return rowBodyTpl.applyTemplate(data);
116 grid.features = features.concat(grid.features);
118 grid.features = features;
121 // NOTE: features have to be added before init (before Table.initComponent)
124 init: function(grid) {
125 this.callParent(arguments);
127 // Columns have to be added in init (after columns has been used to create the
128 // headerCt). Otherwise, shared column configs get corrupted, e.g., if put in the
130 grid.headerCt.insert(0, this.getHeaderConfig());
131 grid.on('render', this.bindView, this, {single: true});
134 getHeaderId: function() {
135 if (!this.headerId) {
136 this.headerId = Ext.id();
138 return this.headerId;
141 getRowBodyFeatureData: function(data, idx, record, orig) {
142 var o = Ext.grid.feature.RowBody.prototype.getAdditionalData.apply(this, arguments),
144 o.rowBodyColspan = o.rowBodyColspan - 1;
145 o.rowBody = this.getRowBodyContents(data);
146 o.rowCls = this.recordsExpanded[record.internalId] ? '' : this.rowCollapsedCls;
147 o.rowBodyCls = this.recordsExpanded[record.internalId] ? '' : this.rowBodyHiddenCls;
148 o[id + '-tdAttr'] = ' valign="top" rowspan="2" ';
149 if (orig[id+'-tdAttr']) {
150 o[id+'-tdAttr'] += orig[id+'-tdAttr'];
155 bindView: function() {
156 var view = this.getCmp().getView(),
159 if (!view.rendered) {
160 view.on('render', this.bindView, this, {single: true});
162 viewEl = view.getEl();
163 if (this.expandOnEnter) {
164 this.keyNav = Ext.create('Ext.KeyNav', viewEl, {
165 'enter' : this.onEnter,
169 if (this.expandOnDblClick) {
170 view.on('itemdblclick', this.onDblClick, this);
176 onEnter: function(e) {
177 var view = this.view,
179 sm = view.getSelectionModel(),
180 sels = sm.getSelection(),
185 for (; i < ln; i++) {
186 rowIdx = ds.indexOf(sels[i]);
187 this.toggleRow(rowIdx);
191 toggleRow: function(rowIdx) {
192 var rowNode = this.view.getNode(rowIdx),
193 row = Ext.get(rowNode),
194 nextBd = Ext.get(row).down(this.rowBodyTrSelector),
195 record = this.view.getRecord(rowNode),
196 grid = this.getCmp();
198 if (row.hasCls(this.rowCollapsedCls)) {
199 row.removeCls(this.rowCollapsedCls);
200 nextBd.removeCls(this.rowBodyHiddenCls);
201 this.recordsExpanded[record.internalId] = true;
202 this.view.fireEvent('expandbody', rowNode, record, nextBd.dom);
204 row.addCls(this.rowCollapsedCls);
205 nextBd.addCls(this.rowBodyHiddenCls);
206 this.recordsExpanded[record.internalId] = false;
207 this.view.fireEvent('collapsebody', rowNode, record, nextBd.dom);
211 // If Grid is auto-heighting itself, then perform a component layhout to accommodate the new height
212 if (!grid.isFixedHeight()) {
213 grid.doComponentLayout();
215 this.view.up('gridpanel').invalidateScroller();
218 onDblClick: function(view, cell, rowIdx, cellIndex, e) {
220 this.toggleRow(rowIdx);
223 getHeaderConfig: function() {
225 toggleRow = Ext.Function.bind(me.toggleRow, me),
226 selectRowOnExpand = me.selectRowOnExpand;
229 id: this.getHeaderId(),
236 cls: Ext.baseCSSPrefix + 'grid-header-special',
237 renderer: function(value, metadata) {
238 metadata.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
240 return '<div class="' + Ext.baseCSSPrefix + 'grid-row-expander"> </div>';
242 processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
243 if (type == "mousedown" && e.getTarget('.x-grid-row-expander')) {
244 var row = e.getTarget('.x-grid-row');
246 return selectRowOnExpand;