Last active
August 29, 2015 14:01
-
-
Save aghuddleston/25b19dfe10f7eb40e54c to your computer and use it in GitHub Desktop.
Ext JS 4.1.1 RowExpander grid plugin updated to play nicer with checkboxes and clearing all expanded
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @class Ext.ux.RowExpander | |
* @extends Ext.AbstractPlugin | |
* Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables | |
* a second row body which expands/contracts. The expand/contract behavior is configurable to react | |
* on clicking of the column, double click of the row, and/or hitting enter while a row is selected. | |
* | |
* @ptype rowexpander | |
*/ | |
Ext.define('Ext.ux.RowExpander', { | |
extend: 'Ext.AbstractPlugin', | |
requires: [ | |
'Ext.grid.feature.RowBody', | |
'Ext.grid.feature.RowWrap' | |
], | |
alias: 'plugin.rowexpander', | |
rowBodyTpl: null, | |
/** | |
* @cfg {Boolean} expandOnEnter | |
* <tt>true</tt> to toggle selected row(s) between expanded/collapsed when the enter | |
* key is pressed (defaults to <tt>true</tt>). | |
*/ | |
expandOnEnter: true, | |
/** | |
* @cfg {Boolean} expandOnDblClick | |
* <tt>true</tt> to toggle a row between expanded/collapsed when double clicked | |
* (defaults to <tt>true</tt>). | |
*/ | |
expandOnDblClick: true, | |
/** | |
* @cfg {Boolean} selectRowOnExpand | |
* <tt>true</tt> to select a row when clicking on the expander icon | |
* (defaults to <tt>false</tt>). | |
*/ | |
selectRowOnExpand: false, | |
rowBodyTrSelector: '.x-grid-rowbody-tr', | |
rowBodyHiddenCls: 'x-grid-row-body-hidden', | |
rowCollapsedCls: 'x-grid-row-collapsed', | |
renderer: function(value, metadata, record, rowIdx, colIdx) { | |
if (colIdx === 0) { | |
metadata.tdCls = 'x-grid-td-expander'; | |
} | |
return '<div class="x-grid-row-expander"> </div>'; | |
}, | |
/** | |
* @event expandbody | |
* <b<Fired through the grid's View</b> | |
* @param {HTMLElement} rowNode The <tr> element which owns the expanded row. | |
* @param {Ext.data.Model} record The record providing the data. | |
* @param {HTMLElement} expandRow The <tr> element containing the expanded data. | |
*/ | |
/** | |
* @event collapsebody | |
* <b<Fired through the grid's View.</b> | |
* @param {HTMLElement} rowNode The <tr> element which owns the expanded row. | |
* @param {Ext.data.Model} record The record providing the data. | |
* @param {HTMLElement} expandRow The <tr> element containing the expanded data. | |
*/ | |
constructor: function() { | |
this.callParent(arguments); | |
var grid = this.getCmp(); | |
this.recordsExpanded = {}; | |
// <debug> | |
if (!this.rowBodyTpl) { | |
Ext.Error.raise("The 'rowBodyTpl' config is required and is not defined."); | |
} | |
// </debug> | |
// TODO: if XTemplate/Template receives a template as an arg, should | |
// just return it back! | |
var rowBodyTpl = Ext.create('Ext.XTemplate', this.rowBodyTpl), | |
features = [{ | |
ftype: 'rowbody', | |
columnId: this.getHeaderId(), | |
recordsExpanded: this.recordsExpanded, | |
rowBodyHiddenCls: this.rowBodyHiddenCls, | |
rowCollapsedCls: this.rowCollapsedCls, | |
getAdditionalData: this.getRowBodyFeatureData, | |
getRowBodyContents: function(data) { | |
return rowBodyTpl.applyTemplate(data); | |
} | |
},{ | |
ftype: 'rowwrap' | |
}]; | |
if (grid.features) { | |
grid.features = features.concat(grid.features); | |
} else { | |
grid.features = features; | |
} | |
// NOTE: features have to be added before init (before Table.initComponent) | |
}, | |
init: function(grid) { | |
this.callParent(arguments); | |
this.grid = grid; | |
// Columns have to be added in init (after columns has been used to create the | |
// headerCt). Otherwise, shared column configs get corrupted, e.g., if put in the | |
// prototype. | |
this.addExpander(); | |
grid.on('render', this.bindView, this, {single: true}); | |
grid.on('reconfigure', this.onReconfigure, this); | |
}, | |
onReconfigure: function(){ | |
this.addExpander(); | |
}, | |
addExpander: function(){ | |
this.grid.headerCt.insert(0, this.getHeaderConfig()); | |
}, | |
getHeaderId: function() { | |
if (!this.headerId) { | |
this.headerId = Ext.id(); | |
} | |
return this.headerId; | |
}, | |
getRowBodyFeatureData: function(data, idx, record, orig) { | |
var o = Ext.grid.feature.RowBody.prototype.getAdditionalData.apply(this, arguments), | |
id = this.columnId, | |
reduceColSpan = 1; | |
// Row expander adds row body to the right of the expander column, but | |
// if checkbox model is also set up on the grid, need to be to the right | |
// of 2 columns (checkbox and expander) and not just 1 column (expander) | |
if (this.grid && this.grid.selModel && this.grid.selModel.selType) { | |
reduceColSpan = this.grid.selModel.selType ==='checkboxmodel' ? 2 : 1; | |
} | |
o.rowBodyColspan = o.rowBodyColspan - reduceColSpan; | |
o.rowBody = this.getRowBodyContents(data); | |
o.rowCls = this.recordsExpanded[record.internalId] ? '' : this.rowCollapsedCls; | |
o.rowBodyCls = this.recordsExpanded[record.internalId] ? '' : this.rowBodyHiddenCls; | |
o[id + '-tdAttr'] = ' valign="top" rowspan="2" '; | |
if (orig[id+'-tdAttr']) { | |
o[id+'-tdAttr'] += orig[id+'-tdAttr']; | |
} | |
return o; | |
}, | |
bindView: function() { | |
var view = this.getCmp().getView(), | |
viewEl; | |
if (!view.rendered) { | |
view.on('render', this.bindView, this, {single: true}); | |
} else { | |
viewEl = view.getEl(); | |
if (this.expandOnEnter) { | |
this.keyNav = Ext.create('Ext.KeyNav', viewEl, { | |
'enter' : this.onEnter, | |
scope: this | |
}); | |
} | |
if (this.expandOnDblClick) { | |
view.on('itemdblclick', this.onDblClick, this); | |
} | |
this.view = view; | |
} | |
}, | |
onEnter: function(e) { | |
var view = this.view, | |
ds = view.store, | |
sm = view.getSelectionModel(), | |
sels = sm.getSelection(), | |
ln = sels.length, | |
i = 0, | |
rowIdx; | |
for (; i < ln; i++) { | |
rowIdx = ds.indexOf(sels[i]); | |
this.toggleRow(rowIdx); | |
} | |
}, | |
toggleRow: function(rowIdx) { | |
var view = this.view, | |
rowNode = view.getNode(rowIdx), | |
row = Ext.get(rowNode), | |
nextBd = Ext.get(row).down(this.rowBodyTrSelector), | |
record = view.getRecord(rowNode), | |
grid = this.getCmp(); | |
if (row.hasCls(this.rowCollapsedCls)) { | |
row.removeCls(this.rowCollapsedCls); | |
nextBd.removeCls(this.rowBodyHiddenCls); | |
this.recordsExpanded[record.internalId] = true; | |
view.refreshSize(); | |
view.fireEvent('expandbody', rowNode, record, nextBd.dom); | |
} else { | |
row.addCls(this.rowCollapsedCls); | |
nextBd.addCls(this.rowBodyHiddenCls); | |
this.recordsExpanded[record.internalId] = false; | |
view.refreshSize(); | |
view.fireEvent('collapsebody', rowNode, record, nextBd.dom); | |
} | |
}, | |
onDblClick: function(view, cell, rowIdx, cellIndex, e) { | |
this.toggleRow(rowIdx); | |
}, | |
getHeaderConfig: function() { | |
var me = this, | |
toggleRow = Ext.Function.bind(me.toggleRow, me), | |
selectRowOnExpand = me.selectRowOnExpand; | |
return { | |
id: this.getHeaderId(), | |
width: 24, | |
sortable: false, | |
resizable: false, | |
draggable: false, | |
hideable: false, | |
menuDisabled: true, | |
cls: Ext.baseCSSPrefix + 'grid-header-special', | |
renderer: function(value, metadata) { | |
metadata.tdCls = Ext.baseCSSPrefix + 'grid-cell-special'; | |
return '<div class="' + Ext.baseCSSPrefix + 'grid-row-expander"> </div>'; | |
}, | |
processEvent: function(type, view, cell, recordIndex, cellIndex, e) { | |
if (type == "mousedown" && e.getTarget('.x-grid-row-expander')) { | |
var row = e.getTarget('.x-grid-row'); | |
toggleRow(row); | |
return selectRowOnExpand; | |
} | |
} | |
}; | |
}, | |
// Clear out the list of expanded records. This isn't "collapse all" | |
// functionality, but more of a "reset". | |
clearExpanded: function() { | |
this.recordsExpanded = {}; | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment