GridSummary Plugin and Horizontal Scrolling Issues
I am running ExtJS 3.3.1 and am attempting to use the GridSummary plugin found here:
http://www.sencha.com/forum/showthre...l=1#post539064
However it seems to be causing an issue when the grid needs to scroll horizontally. Without the plugin my grids scroll horizontally when the column widths are too wide for the grid's width, but with the plugin enabled, the grid's columns that overflow are just hidden. There is a horizontal scrollbar visible, however when used, it only moves the gridsummary row at the bottom, which is way too wide, it is not the same width as the the total width of the grid columns.
Here is the GridSummary plugin code I'm using:
PHP Code:
Ext.ns('Ext.ux.grid');
/**
* @class Ext.ux.grid.GridSummary
* @extends Ext.util.Observable
* A GridPanel plugin that enables dynamic column calculations and a dynamically
* updated total summary row.
*/
Ext.ux.grid.GridSummary = Ext.extend(Ext.util.Observable, {
/**
* @cfg {String} position
* The position where the summary row should be rendered (defaults to 'top').
* The only other supported value is 'bottom'.
*/
position : 'bottom',
/**
* @cfg {Number} scrollBarWidth
* Configurable scrollbar width (used only in the event the Ext.getScrollBarWidth() method is not available)
*/
scrollBarWidth : 17,
constructor : function(config){
Ext.apply(this, config);
Ext.ux.grid.GridSummary.superclass.constructor.call(this);
},
init : function(grid) {
this.grid = grid;
var v = this.view = grid.getView();
// override GridView's onLayout() method
v.onLayout = this.onLayout;
// IE6/7 disappearing vertical scrollbar workaround
if (Ext.isIE6 || Ext.isIE7) {
if (!grid.events['viewready']) {
// check for "viewready" event on GridPanel -- this event is only available in Ext 3.x,
// so the plugin hotwires it in if it doesn't exist
v.afterMethod('afterRender', function() {
this.grid.fireEvent('viewready', this.grid);
}, this);
}
// a small (hacky) delay of ~10ms is required to prevent
// the vertical scrollbar from disappearing in IE6/7
grid.on('viewready', function() {
this.toggleGridHScroll(false);
}, this, { delay: 10 });
} else {
v.afterMethod('render', this.toggleGridHScroll, this);
}
v.afterMethod('render', this.refreshSummary, this);
v.afterMethod('refresh', this.refreshSummary, this);
v.afterMethod('onColumnWidthUpdated', this.doWidth, this);
v.afterMethod('onAllColumnWidthsUpdated', this.doAllWidths, this);
v.afterMethod('onColumnHiddenUpdated', this.doHidden, this);
if (Ext.isGecko || Ext.isOpera) {
// restore gridview's horizontal scroll position when store data is changed
//
// TODO -- when sorting a column in Opera, the summary row's horizontal scroll position is
// synced with the gridview, but is displaced 1 vertical scrollbar width to the right
v.afterMethod('onDataChange', this.restoreGridHScroll, this);
}
grid.on({
bodyscroll : this.syncSummaryScroll,
beforedestroy : this.beforeDestroy,
scope : this
});
// update summary row on store's add/remove/clear/update events
grid.store.on({
add : this.refreshSummary,
remove : this.refreshSummary,
clear : this.refreshSummary,
update : this.refreshSummary,
scope : this
});
if (!this.rowTpl) {
this.rowTpl = new Ext.Template(
'<div class="x-grid3-summary-row x-grid3-gridsummary-row-offset">',
'<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
'<tbody><tr>{cells}</tr></tbody>',
'</table>',
'</div>'
);
this.rowTpl.disableFormats = true;
}
this.rowTpl.compile();
if (!this.cellTpl) {
this.cellTpl = new Ext.Template(
'<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',
'<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
"</td>"
);
this.cellTpl.disableFormats = true;
}
this.cellTpl.compile();
},
/**
* @private
* @param {Object} rs
* @param {Object} cm
*/
calculate : function(rs, cm) {
var data = {}, r, cfg = cm.config, cf,
i, len, cname, j, jlen;
for (i = 0, len = cfg.length; i < len; i++) {
cf = cfg[i]; // get column's configuration
cname = cf.dataIndex; // get column dataIndex
// initialise grid summary row data for
// the current column being worked on
data[cname] = 0;
if (cf.summaryType) {
for (j = 0, jlen = rs.length; j < jlen; j++) {
r = rs[j]; // get a single Record
data[cname] = Ext.ux.grid.GridSummary.Calculations[cf.summaryType](r.get(cname), r, cname, data, j);
}
}
}
return data;
},
// private
onLayout : function(vw, vh) { // note: this method is scoped to the GridView
if (typeof(vh) != 'number') { // handles grid's height:'auto' config
return;
}
if (!this.grid.getGridEl().hasClass('x-grid-hide-gridsummary')) {
// readjust gridview's height only if grid summary row is visible
this.scroller.setHeight(vh - this.summaryWrap.getHeight());
}
},
// private
syncScroll : function(refEl, scrollEl, currX, currY) {
currX = currX || refEl.scrollLeft;
currY = currY || refEl.scrollTop;
if (this.oldX != currX) { // only adjust horizontal scroll when horizontal scroll is detected
scrollEl.scrollLeft = currX;
scrollEl.scrollLeft = currX; // second time for IE (1/2 the time first call fails. other browsers simply ignore repeated calls)
}
// remember current scroll position
this.oldX = currX;
this.oldY = currY;
},
// private
syncSummaryScroll : function(currX, currY) {
var v = this.view,
y = this.oldY;
if (
// workaround for Gecko's horizontal-scroll reset bug
// (see unresolved mozilla bug: https://bugzilla.mozilla.org/show_bug.cgi?id=386444
// "using vertical scrollbar changes horizontal scroll position with overflow-x:hidden and overflow-y:scroll")
Ext.isGecko && // 1) <div>s with overflow-x:hidden have their DOM.scrollLeft property set to 0 when scrolling vertically
currX === 0 && // 2) current x-ordinate is now zero
this.oldX > 0 && // 3) gridview is not at x=0 ordinate
(y !== currY || y === 0) // 4) vertical scroll detected / vertical scrollbar is moved rapidly all the way to the top
) {
this.restoreGridHScroll();
} else {
this.syncScroll(v.scroller.dom, v.summaryWrap.dom, currX, currY);
}
},
// private
restoreGridHScroll : function() {
// restore gridview's original x-ordinate
// (note: this causes an unvoidable flicker in the gridview)
this.view.scroller.dom.scrollLeft = this.oldX || 0;
},
// private
syncGridHScroll : function() {
var v = this.view;
this.syncScroll(v.summaryWrap.dom, v.scroller.dom);
},
// private
doWidth : function(col, w, tw) {
var s = this.getSummaryNode(),
fc = s.dom.firstChild;
fc.style.width = tw;
fc.rows[0].childNodes[col].style.width = w;
this.updateSummaryWidth();
},
// private
doAllWidths : function(ws, tw) {
var s = this.getSummaryNode(),
fc = s.dom.firstChild,
cells = fc.rows[0].childNodes,
wlen = ws.length,
j;
fc.style.width = tw;
for (j = 0; j < wlen; j++) {
cells[j].style.width = ws[j];
}
this.updateSummaryWidth();
},
// private
doHidden : function(col, hidden, tw) {
var s = this.getSummaryNode(),
fc = s.dom.firstChild,
display = hidden ? 'none' : '';
fc.style.width = tw;
fc.rows[0].childNodes[col].style.display = display;
this.updateSummaryWidth();
},
// private
getGridHeader : function() {
if (!this.gridHeader) {
this.gridHeader = this.view.mainHd.child('.x-grid3-header-offset');
}
return this.gridHeader;
},
// private
updateSummaryWidth : function() {
// all browsers add a 1 pixel space between the edges of the vert. and hori. scrollbars,
// so subtract one from the grid header width before setting the summary row's width
// TODO: FIX. This gives the wrong width when using GroupingView and GroupSummary, don't know why
// and haven't investigated it cause it works properly when this is commented out and
// haven't found any undesirable side effect.
// this.getSummaryNode().setWidth(this.getGridHeader().getWidth() - 1);
},
renderSummary : function(o, cs, cm) {
cs = cs || this.view.getColumnData();
var cfg = cm.config,
buf = [], c, p = {}, cf, last = cs.length-1;
for(var i = 0, len = cs.length; i < len; i++){
c = cs[i];
cf = cfg[i];
p.id = c.id;
p.style = c.style;
p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
if (cf.totalLabel) {
p.value = cf.totalLabel;
} else
if (cf.summaryType || cf.summaryRenderer) {
p.value = (cf.summaryRenderer || c.renderer).call(c.scope,o.data[c.name], p, o);
} else {
p.value = '';
}
if (p.value == undefined || p.value === "") {
p.value = " ";
}
buf[buf.length] = this.cellTpl.apply(p);
}
return this.rowTpl.apply({
tstyle: 'width:' + this.view.getTotalWidth() + ';',
cells: buf.join('')
});
},
// private
refreshSummary : function() {
var g = this.grid,
ds = g.store,
cs = this.view.getColumnData(),
cm = g.getColumnModel(),
rs = ds.getRange(),
data = this.calculate(rs, cm),
buf = this.renderSummary({data: data}, cs, cm);
if (!this.view.summaryWrap) {
this.view.summaryWrap = Ext.DomHelper[this.position=='bottom' ? 'insertAfter' : 'insertBefore'](this.view.scroller, {
// IE6/7/8 style hacks:
// - width:100% required for horizontal scroll to appear (all the time for IE6/7, only in GroupingView for IE8)
// - explicit height required for summary row to appear (only for IE6/7, no effect in IE8)
// - overflow-y:hidden required to hide vertical scrollbar in summary row (only for IE6/7, no effect in IE8)
style : 'overflow:auto;' + (Ext.isIE ? 'width:100%;overflow-y:hidden;height:' + ((Ext.getScrollBarWidth ? Ext.getScrollBarWidth() : this.scrollBarWidth) + 18 /* 18 = row-expander height */) + 'px;' : ''),
tag : 'div',
cls : 'x-grid3-gridsummary-row-inner'
}, true);
// synchronise GridView's and GridSummary's horizontal scroll
this.view.summaryWrap.on('scroll', this.syncGridHScroll, this);
}
// update summary row data
this.setSummaryNode(this.view.summaryWrap.update(buf).first());
this.updateSummaryWidth();
},
// private
toggleGridHScroll : function(allowHScroll) {
// toggle GridView's horizontal scrollbar
this.view.scroller[allowHScroll === undefined ? 'toggleClass' : allowHScroll ? 'removeClass' : 'addClass']('x-grid3-gridsummary-hide-hscroll');
},
/**
* Toggle the display of the summary row on/off
* @param {Boolean} visible <tt>true</tt> to show the summary, <tt>false</tt> to hide the summary.
*/
toggleSummary : function(visible) {
var el = this.grid.getGridEl(),
v = this.view;
if (el) {
el[visible === undefined ? 'toggleClass' : visible ? 'removeClass' : 'addClass']('x-grid-hide-gridsummary');
// toggle gridview's horizontal scrollbar
this.toggleGridHScroll();
// readjust gridview height
v.layout();
// sync summary row scroll position
v.summaryWrap.dom.scrollLeft = v.scroller.dom.scrollLeft;
}
},
// get summary row Element
getSummaryNode : function() {
return this.view.summary;
},
// private
setSummaryNode : function(sn) {
this.view.summary = sn;
},
// private
beforeDestroy : function() {
Ext.destroy(
this.view.summary,
this.view.summaryWrap
);
delete this.grid;
delete this.view;
delete this.gridHeader;
delete this.oldX;
delete this.oldY;
}
});
Ext.reg('gridsummary', Ext.ux.grid.GridSummary);
/*
* all Calculation methods are called on each Record in the Store
* with the following 5 parameters:
*
* v - cell value
* record - reference to the current Record
* colName - column name (i.e. the ColumnModel's dataIndex)
* data - the cumulative data for the current column + summaryType up to the current Record
* rowIdx - current row index
*/
Ext.ux.grid.GridSummary.Calculations = {
sum : function(v, record, colName, data, rowIdx) {
return data[colName] + Ext.num(v, 0);
},
count : function(v, record, colName, data, rowIdx) {
return rowIdx + 1;
},
max : function(v, record, colName, data, rowIdx) {
return Math.max(Ext.num(v, 0), data[colName]);
},
min : function(v, record, colName, data, rowIdx) {
return Math.min(Ext.num(v, 0), data[colName]);
},
avg : function(v, record, colName, data, rowIdx) {
var t = data[colName] + Ext.num(v, 0),
count = record.store.getCount();
return rowIdx == count - 1 ? (t / count) : t;
}
};
The CSS:
PHP Code:
.x-grid3-gridsummary-row-inner {overflow:hidden; width:100%;}
.x-grid3-gridsummary-row-offset {width:10000px;}
.x-grid-hide-gridsummary .x-grid3-gridsummary-row-inner {display:none;}