/**
* This feature can display summaries for all nested groups and a grand summary
* for the entire store assigned to the grid panel.
*/
Ext.define('Ext.grid.feature.AdvancedGroupingSummary', {
extend: 'Ext.grid.feature.AdvancedGrouping',
alias: 'feature.advancedgroupingsummary',
groupSummaryPosition: 'bottom',
/**
* @cfg summaryPosition
* @inheritdoc
* @localdoc
* * `'docked'`: Show the summary row docked at the top/bottom
* of the grid. Used together with the {@link dock} config
*/
summaryPosition: 'docked',
/**
* @cfg {String} dock
* Configure `'top'` or `'bottom'` to create a fixed summary row
* either above or below the scrollable table.
*
*/
dock: 'bottom',
dockedSummaryCls: Ext.baseCSSPrefix + 'docked-grid-summary',
summaryCls: Ext.baseCSSPrefix + 'grid-summary',
summarySelector: '.' + Ext.baseCSSPrefix + 'grid-summary',
summaryTableCls: Ext.baseCSSPrefix + 'grid-item',
init: function(grid) {
var me = this;
me.refreshBarTask = new Ext.util.DelayedTask(me.onStoreUpdate, me);
me.callParent([grid]);
grid.headerCt.on({
columnschanged: me.refreshBar, // this includes columns visibility
afterlayout: me.afterHeaderCtLayout,
scope: me
});
grid.on({
beforerender: me.onBeforeGridRendered,
afterrender: me.onAfterGridRendered,
scope: me,
single: true
});
},
destroy: function() {
var me = this;
me.refreshBarTask.cancel();
me.summaryBar = Ext.destroy(me.summaryBar);
me.callParent();
},
/**
* @inheritDoc
*/
setSummaryPosition: function(value) {
var me = this,
lockingPartner = me.lockingPartner,
bar = me.getSummaryBar(),
dock = me.dock;
me.showSummary = (value === 'docked' && (dock === 'top' || dock === 'bottom'));
bar.setHidden(!me.showSummary);
if (lockingPartner) {
lockingPartner.getSummaryBar().setHidden(!me.showSummary);
}
me.callParent([value]);
},
onBeforeGridRendered: function() {
var me = this,
view = me.view,
grid = me.grid,
dock = me.dock,
pos = me.summaryPosition,
tableCls = [me.summaryTableCls],
showSummary;
me.showSummary = showSummary = (pos === 'docked' && (dock === 'top' || dock === 'bottom'));
if (view.columnLines) {
tableCls[tableCls.length] = view.ownerCt.colLinesCls;
}
me.summaryBar = grid.addDocked({
focusable: true,
childEls: ['innerCt', 'item'],
renderTpl: [
'<div id="{id}-innerCt" data-ref="innerCt" role="presentation">',
// eslint-disable-next-line max-len
'<table id="{id}-item" data-ref="item" cellPadding="0" cellSpacing="0" class="' + tableCls.join(' ') + '">',
'<tr class="' + me.summaryCls + '"></tr>',
'</table>',
'</div>'
],
scrollable: {
x: false,
y: false
},
itemId: 'summaryBar',
hidden: !showSummary,
cls: [me.dockedSummaryCls, me.dockedSummaryCls + '-' + dock],
xtype: 'component',
dock: dock,
weight: 10000000
})[0];
},
onAfterGridRendered: function() {
var me = this,
bar = me.summaryBar;
me.view.getScrollable().addPartner(bar.getScrollable(), 'x');
me.onStoreUpdate();
bar.innerCt.on({
click: 'onBarEvent',
dblclick: 'onBarEvent',
contextmenu: 'onBarEvent',
delegate: '.' + Ext.baseCSSPrefix + 'grid-cell',
scope: me
});
},
getSummaryBar: function() {
var me = this;
if (!me.summaryBar) {
me.onBeforeGridRendered();
me.onAfterGridRendered();
}
return me.summaryBar;
},
setupRowValues: function(rowValues, renderData) {
this.callParent([rowValues, renderData]);
if (renderData.isSummary && this.showSummary) {
Ext.Array.remove(rowValues.rowClasses, this.eventCls);
rowValues.rowClasses.push('x-grid-row', this.summaryCls);
}
},
onStoreUpdate: function() {
var me = this,
view = me.view,
selector = me.summarySelector,
record, newRowDom, oldRowDom, p, data;
if (!view || !view.rendered || !me.showSummary) {
return;
}
record = view.getStore().getSummaryRecord();
data = me.dataSource.renderData[record.getId()] = {
isSummary: true
};
me.setRenderers(data);
newRowDom = Ext.fly(view.createRowElement(record, -1)).down(selector, true);
me.resetRenderers();
if (!newRowDom) {
return;
}
// Summary row is inside the docked summaryBar Component
p = me.summaryBar.item.dom.firstChild;
oldRowDom = p.firstChild;
p.insertBefore(newRowDom, oldRowDom);
p.removeChild(oldRowDom);
},
refreshBar: function() {
this.refreshBarTask.delay(10);
},
// Synchronize column widths in the docked summary Component or the inline summary row
// depending on whether we are docked or not.
afterHeaderCtLayout: function(headerCt) {
var me = this,
view = me.view,
columns = view.getVisibleColumnManager().getColumns(),
len = columns.length,
summaryEl, el, width, innerCt, column, i;
if (me.showSummary && view.refreshCounter) {
summaryEl = me.summaryBar.el;
width = headerCt.getTableWidth();
innerCt = me.summaryBar.innerCt;
// Stretch the innerCt of the summary bar upon headerCt layout
me.summaryBar.item.setWidth(width);
// headerCt's tooNarrow flag is set by its layout if the columns overflow.
// Must not measure+set in after layout phase, this is a write phase.
if (headerCt.tooNarrow) {
width += Ext.getScrollbarSize().width;
}
innerCt.setWidth(width);
// If the layout was in response to a clearView, there'll be no summary element
if (summaryEl) {
for (i = 0; i < len; i++) {
column = columns[i];
el = summaryEl.down(view.getCellSelector(column), true);
if (el) {
Ext.fly(el).setWidth(column.width ||
(column.lastBox ? column.lastBox.width : 100));
}
}
}
}
},
onBarEvent: function(e, cell) {
var me = this,
view = me.view,
grid = view.ownerGrid,
record = view.getStore().getSummaryRecord(),
fireArg = Ext.apply({
record: record,
column: view.getHeaderByCell(cell),
cell: cell,
row: me.summaryBar.getEl(),
grid: grid,
feature: me,
e: e
}, me.dataSource.getRenderData(record));
return grid.fireEvent('summary' + e.type, grid, fireArg);
},
privates: {
getOwnerGridListeners: function() {
var listeners = this.callParent();
return Ext.apply(listeners, {
columnmove: this.onStoreUpdate
});
},
getStoreListeners: function() {
var me = this,
listeners = me.callParent();
return Ext.apply(listeners, {
update: me.refreshBar,
datachanged: me.refreshBar,
remotesummarieschanged: me.refreshBar,
summarieschanged: me.refreshBar
});
}
}
});