Treegrid updates, remove duplicate code in stickyheaders
parent
9cc2a9c60c
commit
eef62d3656
165
stickyheaders.js
165
stickyheaders.js
|
@ -1,11 +1,13 @@
|
||||||
// Simple Sticky Header and Column implementation for HTML tables
|
// Simple Sticky Header and Column implementation for HTML tables
|
||||||
// (c) Vitaliy Filippov 2016
|
// (c) Vitaliy Filippov 2016
|
||||||
// License: MPL 2.0+
|
// License: MPL 2.0+
|
||||||
// Version: 2016-08-18
|
// Version: 2016-12-06
|
||||||
|
|
||||||
// USAGE:
|
// USAGE:
|
||||||
// makeStickyHeaders(table): add sticky header and footer to table
|
// makeStickyHeaders(table): add sticky header and footer to table
|
||||||
// table.parentNode is assumed to be the scroll container
|
// table.parentNode is assumed to be the scroll container
|
||||||
|
// checkStickyHeaders(table): call this after making table visible
|
||||||
|
// (if you've hidden it before)
|
||||||
// initStickyRows(table, trs): initialise table rows (trs = array[TableRow]) if not yet
|
// initStickyRows(table, trs): initialise table rows (trs = array[TableRow]) if not yet
|
||||||
// fixStickyRows(table, trs): fix table row (trs = array[TableRow]) after modifications
|
// fixStickyRows(table, trs): fix table row (trs = array[TableRow]) after modifications
|
||||||
// fixStickyHeader(table): fix table header after modifications
|
// fixStickyHeader(table): fix table header after modifications
|
||||||
|
@ -34,13 +36,12 @@ var _scri = 0;
|
||||||
function getOffsetRect(e)
|
function getOffsetRect(e)
|
||||||
{
|
{
|
||||||
var b = e.getBoundingClientRect();
|
var b = e.getBoundingClientRect();
|
||||||
var r = {
|
return {
|
||||||
left: Math.round(b.left),
|
left: Math.round(b.left*100)/100,
|
||||||
top: Math.round(b.top),
|
top: Math.round(b.top*100)/100,
|
||||||
|
width: Math.round((b.right-b.left)*100)/100,
|
||||||
|
height: Math.round((b.bottom-b.top)*100)/100
|
||||||
};
|
};
|
||||||
r.width = Math.round(b.right)-r.left;
|
|
||||||
r.height = Math.round(b.bottom)-r.top;
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.makeStickyHeaders = function(table)
|
window.makeStickyHeaders = function(table)
|
||||||
|
@ -51,93 +52,51 @@ window.makeStickyHeaders = function(table)
|
||||||
(document.head||document.getElementsByTagName('head')[0]).appendChild(table._scrstyle);
|
(document.head||document.getElementsByTagName('head')[0]).appendChild(table._scrstyle);
|
||||||
table._scri = _scri++;
|
table._scri = _scri++;
|
||||||
}
|
}
|
||||||
var rs = table.rows;
|
if (!table.rows.length || !table.rows[0].children.length)
|
||||||
if (!rs.length || !rs[0].children.length)
|
|
||||||
return;
|
return;
|
||||||
var w = [], l = [], h = rs[0].offsetHeight;
|
var w = [], l = [], h = table.rows[0].offsetHeight;
|
||||||
var sr = rs[0].cloneNode(true);
|
var sr = table.rows[0].cloneNode();
|
||||||
sr.insertBefore(document.createElement('td'), sr.firstChild);
|
sr.appendChild(document.createElement('td'));
|
||||||
sr.style.visibility = 'hidden';
|
|
||||||
sr.children[0].style.width = '0';
|
sr.children[0].style.width = '0';
|
||||||
sr.children[0].style.display = 'block';
|
sr.children[0].style.display = 'block';
|
||||||
sr.children[0].style.position = 'absolute';
|
sr.children[0].style.position = 'absolute';
|
||||||
sr.children[0].style.padding = '0';
|
sr.children[0].style.padding = '0';
|
||||||
var pb = getOffsetRect(rs[0]);
|
sr.style.visibility = 'hidden';
|
||||||
for (var i = 0; i < rs[0].children.length; i++)
|
table._widths = [];
|
||||||
{
|
|
||||||
var e = rs[0].children[i];
|
|
||||||
var b = getOffsetRect(e);
|
|
||||||
w[i] = b.width;
|
|
||||||
l[i] = b.left-pb.left;
|
|
||||||
}
|
|
||||||
table._widths = w;
|
|
||||||
table._sizerow = sr;
|
table._sizerow = sr;
|
||||||
rs[0].style.height = '0';
|
table.rows[0].style.height = '0';
|
||||||
rs[0].style.display = 'block';
|
table.rows[0].style.display = 'block';
|
||||||
rs[0].style.position = 'absolute';
|
table.rows[0].style.position = 'absolute';
|
||||||
rs[0].parentNode.insertBefore(sr, rs[0].nextSibling);
|
table.rows[0].parentNode.insertBefore(sr, table.rows[0].nextSibling);
|
||||||
for (var i = 0; i < rs[0].children.length; i++)
|
fixStickyHeader(table, true);
|
||||||
{
|
table._fixPending = true;
|
||||||
var e = rs[0].children[i];
|
checkStickyHeaders(table);
|
||||||
e._oldWidth = e.style.width;
|
var row0top = getOffsetRect(table.rows[0]).top-getOffsetRect(table.rows[0].offsetParent).top;
|
||||||
e._sticky = true;
|
|
||||||
e.style.position = 'absolute';
|
|
||||||
e.style.width = w[i]+'px';
|
|
||||||
e.style.height = h+'px';
|
|
||||||
e.style.left = l[i]+'px';
|
|
||||||
e.style.display = 'block';
|
|
||||||
e.style.zIndex = '1';
|
|
||||||
}
|
|
||||||
// Try to minimise reflows: (1) make some changes
|
|
||||||
for (var i = rs.length-1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (rs[i] == sr)
|
|
||||||
continue;
|
|
||||||
if (!rs[i].children.length)
|
|
||||||
continue;
|
|
||||||
var e = rs[i].children[0];
|
|
||||||
var d = e.cloneNode(true);
|
|
||||||
d.style.visibility = 'hidden';
|
|
||||||
rs[i].insertBefore(d, e.nextSibling);
|
|
||||||
if (i != 0)
|
|
||||||
{
|
|
||||||
e.style.position = 'absolute';
|
|
||||||
e.style.display = 'block';
|
|
||||||
e.style.zIndex = '1';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
e.style.left = '';
|
|
||||||
e.style.zIndex = '2';
|
|
||||||
}
|
|
||||||
e.className += ' _scri'+table._scri;
|
|
||||||
}
|
|
||||||
// 2) <REFLOW> (offsetHeight causes reflow)
|
|
||||||
// 3) remember all heights
|
|
||||||
var hs = [];
|
|
||||||
for (var i = rs.length-1; i >= 1; i--)
|
|
||||||
hs[i] = getOffsetRect(rs[i]).height;
|
|
||||||
// 4) make other changes
|
|
||||||
for (var i = rs.length-1; i >= 1; i--)
|
|
||||||
{
|
|
||||||
if (rs[i] == sr)
|
|
||||||
continue;
|
|
||||||
if (!rs[i].children.length)
|
|
||||||
continue;
|
|
||||||
var e = rs[i].children[0];
|
|
||||||
e._oldWidth = e.style.width;
|
|
||||||
e.style.height = hs[i]+'px';
|
|
||||||
e.style.width = w[0]+'px';
|
|
||||||
}
|
|
||||||
var row0top = getOffsetRect(rs[0]).top-getOffsetRect(rs[0].offsetParent).top;
|
|
||||||
addListener(table.parentNode, 'scroll', function(e)
|
addListener(table.parentNode, 'scroll', function(e)
|
||||||
{
|
{
|
||||||
var l = this.scrollLeft, t = this.scrollTop;
|
var l = this.scrollLeft, t = this.scrollTop;
|
||||||
table.rows[0].style.top = (t+row0top)+'px';
|
table.rows[0].style.top = (t+row0top)+'px';
|
||||||
table._scrstyle.innerHTML = '._scri'+table._scri+' { left: '+l+'px; }';
|
if (!table._scrlastleft || table._scrlastleft != l)
|
||||||
|
{
|
||||||
|
table._scrstyle.innerHTML = l > 0 ? '._scri'+table._scri+' { left: '+l+'px; }' : '';
|
||||||
|
table._scrlastleft = l;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should be called after showing table to recalculate fixed cell sizes
|
||||||
|
window.checkStickyHeaders = function(table)
|
||||||
|
{
|
||||||
|
if (table._fixPending)
|
||||||
|
{
|
||||||
|
delete table._fixPending;
|
||||||
|
var rows = [];
|
||||||
|
for (var i = 2; i < table.rows.length; i++)
|
||||||
|
rows.push(table.rows[i]);
|
||||||
|
fixStickyRows(table, rows);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check if row(s) are not yet initialised and fix them
|
// check if row(s) are not yet initialised and fix them
|
||||||
window.initStickyRows = function(table, trs, nocols)
|
window.initStickyRows = function(table, trs, nocols)
|
||||||
{
|
{
|
||||||
|
@ -177,28 +136,40 @@ window.fixStickyRows = function(table, trs, nocols)
|
||||||
d = e.cloneNode(true);
|
d = e.cloneNode(true);
|
||||||
d.style.visibility = 'hidden';
|
d.style.visibility = 'hidden';
|
||||||
e.style.position = 'absolute';
|
e.style.position = 'absolute';
|
||||||
|
if (e.rowSpan > 1)
|
||||||
|
e.rowSpan = 1;
|
||||||
|
if (e.style.display == 'none')
|
||||||
|
e.style.visibility = 'hidden';
|
||||||
e.style.display = 'block';
|
e.style.display = 'block';
|
||||||
e.style.zIndex = '1';
|
e.style.zIndex = '1';
|
||||||
trs[i].insertBefore(d, e.nextSibling);
|
trs[i].insertBefore(d, e.nextSibling);
|
||||||
e.className += ' _scri'+table._scri;
|
e.className += ' _scri'+table._scri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// (2) <REFLOW> caused by offsetWidth/offsetHeight
|
if (table.offsetParent)
|
||||||
// (3) remember sizes
|
|
||||||
var w = [], h = [], b;
|
|
||||||
for (var i = 0; i < trs.length; i++)
|
|
||||||
{
|
{
|
||||||
e = trs[i].children[1];
|
// (2) <REFLOW> caused by offsetWidth/offsetHeight
|
||||||
b = getOffsetRect(e);
|
// (3) remember sizes
|
||||||
w[i] = b.width;
|
var w = [], h = [], b;
|
||||||
h[i] = b.height;
|
for (var i = 0; i < trs.length; i++)
|
||||||
|
{
|
||||||
|
e = trs[i].children[1];
|
||||||
|
b = getOffsetRect(e);
|
||||||
|
w[i] = b.width;
|
||||||
|
h[i] = b.height;
|
||||||
|
}
|
||||||
|
// (4) apply sizes
|
||||||
|
for (var i = 0; i < trs.length; i++)
|
||||||
|
{
|
||||||
|
e = trs[i].children[0];
|
||||||
|
e.style.width = w[i]+'px';
|
||||||
|
e.style.height = h[i]+'px';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// (4) apply sizes
|
else
|
||||||
for (var i = 0; i < trs.length; i++)
|
|
||||||
{
|
{
|
||||||
e = trs[i].children[0];
|
// table is invisible
|
||||||
e.style.width = w[i]+'px';
|
table._fixPending = true;
|
||||||
e.style.height = h[i]+'px';
|
|
||||||
}
|
}
|
||||||
if (!nocols)
|
if (!nocols)
|
||||||
fixStickyColumnSizes(table);
|
fixStickyColumnSizes(table);
|
||||||
|
@ -206,6 +177,12 @@ window.fixStickyRows = function(table, trs, nocols)
|
||||||
|
|
||||||
window.fixStickyColumnSizes = function(table, force)
|
window.fixStickyColumnSizes = function(table, force)
|
||||||
{
|
{
|
||||||
|
if (!table.offsetParent)
|
||||||
|
{
|
||||||
|
// table is invisible
|
||||||
|
table._fixPending = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
// check&fix column sizes
|
// check&fix column sizes
|
||||||
var changed = false, resizeFixed = false;
|
var changed = false, resizeFixed = false;
|
||||||
for (var i = 1; i < table._sizerow.children.length; i++)
|
for (var i = 1; i < table._sizerow.children.length; i++)
|
||||||
|
|
253
treegrid.js
253
treegrid.js
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Very simple and fast tree grid/table, compatible with dynamic loading and stickyheaders.js
|
* Very simple and fast tree grid/table, compatible with dynamic loading and stickyheaders.js
|
||||||
* License: MPL 2.0+, (c) Vitaliy Filippov 2016+
|
* License: MPL 2.0+, (c) Vitaliy Filippov 2016+
|
||||||
* Version: 2016-10-24
|
* Version: 2016-12-06
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,8 +48,8 @@
|
||||||
* TG.initCellEditing();
|
* TG.initCellEditing();
|
||||||
*
|
*
|
||||||
* // use simple mouse cell selection
|
* // use simple mouse cell selection
|
||||||
* // HTMLElement restrictToNode: the element to listen for mousedown events
|
* // boolean useMultiple: whether to initialise multi-cell selection (true) or single-cell (false)
|
||||||
* TG.initCellSelection(restrictToNode);
|
* TG.initCellSelection(useMultiple);
|
||||||
*
|
*
|
||||||
* Callbacks:
|
* Callbacks:
|
||||||
*
|
*
|
||||||
|
@ -87,6 +87,67 @@ function TreeGrid(options)
|
||||||
new TreeGridNode({ children: options.items }, this);
|
new TreeGridNode({ children: options.items }, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TreeGridNode(node, grid, level, insertBefore, startHidden)
|
||||||
|
{
|
||||||
|
this.grid = grid;
|
||||||
|
this.level = level;
|
||||||
|
this.key = node.key;
|
||||||
|
this.data = node.data;
|
||||||
|
if (this.level === undefined)
|
||||||
|
this.level = -1;
|
||||||
|
if (!grid.root)
|
||||||
|
grid.root = this;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.leaf = node.leaf;
|
||||||
|
this.collapsed = node.collapsed || !node.leaf && (!node.children || !node.children.length);
|
||||||
|
this.tr = [];
|
||||||
|
for (var i = 0; i < (node.rows || 1); i++)
|
||||||
|
{
|
||||||
|
var tr = document.createElement('tr');
|
||||||
|
if (startHidden)
|
||||||
|
tr.style.display = 'none';
|
||||||
|
tr._node = this;
|
||||||
|
this.tr.push(tr);
|
||||||
|
}
|
||||||
|
this._oldCells = [];
|
||||||
|
this.render(undefined, undefined, false, true);
|
||||||
|
for (var i = 0; i < this.tr.length; i++)
|
||||||
|
{
|
||||||
|
insertBefore ? grid.tbody.insertBefore(this.tr[i], insertBefore) : grid.tbody.appendChild(this.tr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.children = [];
|
||||||
|
this.childrenByKey = {};
|
||||||
|
if (node.children)
|
||||||
|
{
|
||||||
|
var trs = [];
|
||||||
|
for (var i = 0; i < node.children.length; i++)
|
||||||
|
{
|
||||||
|
var child = new TreeGridNode(node.children[i], grid, this.level+1, insertBefore, this.collapsed);
|
||||||
|
child._index = i;
|
||||||
|
this.children.push(child);
|
||||||
|
if (child.key !== undefined)
|
||||||
|
this.childrenByKey[child.key] = child;
|
||||||
|
if (grid.stickyInit && !this.collapsed)
|
||||||
|
trs.push.apply(trs, child.tr);
|
||||||
|
}
|
||||||
|
if (trs.length)
|
||||||
|
fixStickyRows(grid.table, trs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
function htmlspecialchars(text)
|
||||||
|
{
|
||||||
|
return (''+text).replace(/&/g, '&')
|
||||||
|
.replace(/'/g, ''') // '
|
||||||
|
.replace(/"/g, '"') // "
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>');
|
||||||
|
}
|
||||||
|
|
||||||
TreeGrid.prototype.initStickyHeaders = function(options)
|
TreeGrid.prototype.initStickyHeaders = function(options)
|
||||||
{
|
{
|
||||||
this.sticky = true;
|
this.sticky = true;
|
||||||
|
@ -95,12 +156,18 @@ TreeGrid.prototype.initStickyHeaders = function(options)
|
||||||
this._makeStickyHeaders();
|
this._makeStickyHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TreeGrid.prototype.checkStickyHeaders = function()
|
||||||
|
{
|
||||||
|
if (this.stickyInit)
|
||||||
|
checkStickyHeaders(this.table);
|
||||||
|
}
|
||||||
|
|
||||||
TreeGrid.prototype._makeStickyHeaders = function()
|
TreeGrid.prototype._makeStickyHeaders = function()
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
|
this.table.className += ' stickyheaders';
|
||||||
makeStickyHeaders(this.table);
|
makeStickyHeaders(this.table);
|
||||||
this.stickyInit = true;
|
this.stickyInit = true;
|
||||||
this.table.className += ' stickyheaders';
|
|
||||||
if (!this.noStickyResize)
|
if (!this.noStickyResize)
|
||||||
{
|
{
|
||||||
addListener(window, 'resize', function()
|
addListener(window, 'resize', function()
|
||||||
|
@ -144,13 +211,29 @@ TreeGrid.prototype.onExpand = function(node)
|
||||||
TreeGrid.prototype._setProps = function(el, props)
|
TreeGrid.prototype._setProps = function(el, props)
|
||||||
{
|
{
|
||||||
if (props === undefined || props === null)
|
if (props === undefined || props === null)
|
||||||
|
{
|
||||||
for (var j in this.bindProps)
|
for (var j in this.bindProps)
|
||||||
el[j] = '';
|
{
|
||||||
|
if (j == 'innerHTML' || j == 'className')
|
||||||
|
el[j] = '';
|
||||||
|
else
|
||||||
|
el.setAttribute(j, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (typeof props == 'string')
|
else if (typeof props == 'string')
|
||||||
|
{
|
||||||
el.innerHTML = props;
|
el.innerHTML = props;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
for (var j in this.bindProps)
|
for (var j in this.bindProps)
|
||||||
el[j] = props[j] !== undefined ? props[j] : '';
|
{
|
||||||
|
if (j == 'innerHTML' || j == 'className')
|
||||||
|
el[j] = (props[j] !== undefined ? props[j] : '');
|
||||||
|
else
|
||||||
|
el.setAttribute(j, (props[j] !== undefined ? props[j] : ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple cell editing
|
// Simple cell editing
|
||||||
|
@ -186,23 +269,33 @@ TreeGrid.prototype.initCellEditing = function()
|
||||||
var td = evt.target||evt.srcElement;
|
var td = evt.target||evt.srcElement;
|
||||||
while (td.nodeName != 'TABLE' && td.nodeName != 'TD')
|
while (td.nodeName != 'TABLE' && td.nodeName != 'TD')
|
||||||
td = td.parentNode;
|
td = td.parentNode;
|
||||||
if (td.nodeName == 'TD' && td.parentNode._node && td.previousSibling && td.className != 'celleditor')
|
if (td.nodeName == 'TD' && td.parentNode._node && td.previousSibling && td.className.indexOf('celleditor') < 0)
|
||||||
{
|
{
|
||||||
var params = self.onStartCellEdit &&
|
var params = self.onStartCellEdit &&
|
||||||
self.onStartCellEdit(td.parentNode._node, self._getSubrow(td), self._getCellIndex(td, true), td) || {};
|
self.onStartCellEdit(td.parentNode._node, self._getSubrow(td), self._getCellIndex(td, true), td) || {};
|
||||||
if (params.abort)
|
if (params.abort)
|
||||||
return;
|
return;
|
||||||
self.editedCells.push(td);
|
self.editedCells.push(td);
|
||||||
td.className += ' celleditor';
|
|
||||||
if (params.value === undefined)
|
if (params.value === undefined)
|
||||||
params.value = td.innerHTML;
|
params.value = td.innerHTML;
|
||||||
td.innerHTML = '<input type="text" value="'+htmlspecialchars(params.value)+'" />';
|
td._origWidth = td.style.width;
|
||||||
addListener(td.firstChild, 'keydown', function(evt)
|
td.style.width = td.offsetWidth+'px';
|
||||||
|
td.className += ' celleditor';
|
||||||
|
if ('ActiveXObject' in window)
|
||||||
|
{
|
||||||
|
td.innerHTML = '<div><input type="text" value="'+htmlspecialchars(params.value)+'" /></div>';
|
||||||
|
td.style.height = 'inherit';
|
||||||
|
td.parentNode.style.height = '1px';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
td.innerHTML = '<input type="text" value="'+htmlspecialchars(params.value)+'" />';
|
||||||
|
var inp = td.getElementsByTagName('input')[0];
|
||||||
|
addListener(inp, 'keydown', function(evt)
|
||||||
{
|
{
|
||||||
if (evt.keyCode == 13 || evt.keyCode == 10)
|
if (evt.keyCode == 13 || evt.keyCode == 10)
|
||||||
self.stopCellEditing(td);
|
self.stopCellEditing(td);
|
||||||
});
|
});
|
||||||
td.firstChild.focus();
|
inp.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
addListener(document.body, 'click', function(evt)
|
addListener(document.body, 'click', function(evt)
|
||||||
|
@ -226,8 +319,10 @@ TreeGrid.prototype.stopCellEditing = function(td, _int)
|
||||||
var subrow = this._getSubrow(td);
|
var subrow = this._getSubrow(td);
|
||||||
var node = td.parentNode._node;
|
var node = td.parentNode._node;
|
||||||
node._oldCells[i] = undefined;
|
node._oldCells[i] = undefined;
|
||||||
|
td.style.width = td._origWidth;
|
||||||
|
var inp = td.getElementsByTagName('input')[0];
|
||||||
var params = this.onStopCellEdit &&
|
var params = this.onStopCellEdit &&
|
||||||
this.onStopCellEdit(node, subrow, i, td.firstChild ? td.firstChild.value : null, td) || {};
|
this.onStopCellEdit(node, subrow, i, inp ? inp.value : null, td) || {};
|
||||||
node.render(i, subrow, true);
|
node.render(i, subrow, true);
|
||||||
if (!_int)
|
if (!_int)
|
||||||
{
|
{
|
||||||
|
@ -244,12 +339,9 @@ TreeGrid.prototype.stopCellEditing = function(td, _int)
|
||||||
|
|
||||||
// Simple cell selection
|
// Simple cell selection
|
||||||
|
|
||||||
TreeGrid.prototype.initCellSelection = function(restrictToNode)
|
TreeGrid.prototype.initCellSelection = function(useMultiple)
|
||||||
{
|
{
|
||||||
var startDrag, dragDiv;
|
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
self.table.className += ' disable-text-select';
|
|
||||||
|
|
||||||
self.selectCell = function(cell)
|
self.selectCell = function(cell)
|
||||||
{
|
{
|
||||||
|
@ -271,7 +363,34 @@ TreeGrid.prototype.initCellSelection = function(restrictToNode)
|
||||||
els[i].className = els[i].className.replace(' selected', '');
|
els[i].className = els[i].className.replace(' selected', '');
|
||||||
};
|
};
|
||||||
|
|
||||||
addListener(restrictToNode || document.body, 'mousedown', function(evt)
|
if (useMultiple)
|
||||||
|
initMultiCellSelection(self);
|
||||||
|
else
|
||||||
|
initSingleCellSelection(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSingleCellSelection(self)
|
||||||
|
{
|
||||||
|
addListener(self.table, 'mousedown', function(evt)
|
||||||
|
{
|
||||||
|
self.deselectAll();
|
||||||
|
if (evt.which != 1)
|
||||||
|
return;
|
||||||
|
var td = evt.target||evt.srcElement;
|
||||||
|
while (td && (td.nodeName != 'TD' && td.nodeName != 'TH'))
|
||||||
|
td = td.parentNode;
|
||||||
|
if (td)
|
||||||
|
self.selectCell(td);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initMultiCellSelection(self)
|
||||||
|
{
|
||||||
|
var startDrag, dragDiv;
|
||||||
|
|
||||||
|
self.table.className += ' disable-text-select';
|
||||||
|
|
||||||
|
addListener(self.table, 'mousedown', function(evt)
|
||||||
{
|
{
|
||||||
evt = getEventCoord(evt);
|
evt = getEventCoord(evt);
|
||||||
if (!evt.shiftKey)
|
if (!evt.shiftKey)
|
||||||
|
@ -346,7 +465,7 @@ TreeGrid.prototype.initCellSelection = function(restrictToNode)
|
||||||
startDrag = null;
|
startDrag = null;
|
||||||
if (x2 > x1+10 && y2 > y1+10)
|
if (x2 > x1+10 && y2 > y1+10)
|
||||||
{
|
{
|
||||||
var bodyPos = getOffset(self.tbody);
|
var bodyPos = self.tbody.getBoundingClientRect();
|
||||||
if (x1 < bodyPos.left+self.tbody.offsetWidth &&
|
if (x1 < bodyPos.left+self.tbody.offsetWidth &&
|
||||||
y1 < bodyPos.top+self.tbody.offsetHeight &&
|
y1 < bodyPos.top+self.tbody.offsetHeight &&
|
||||||
x2 > bodyPos.left && y2 > bodyPos.top)
|
x2 > bodyPos.left && y2 > bodyPos.top)
|
||||||
|
@ -401,56 +520,6 @@ TreeGrid.prototype.initCellSelection = function(restrictToNode)
|
||||||
|
|
||||||
// Tree grid node class
|
// Tree grid node class
|
||||||
|
|
||||||
function TreeGridNode(node, grid, level, insertBefore, startHidden)
|
|
||||||
{
|
|
||||||
this.grid = grid;
|
|
||||||
this.level = level;
|
|
||||||
this.key = node.key;
|
|
||||||
this.data = node.data;
|
|
||||||
if (this.level === undefined)
|
|
||||||
this.level = -1;
|
|
||||||
if (!grid.root)
|
|
||||||
grid.root = this;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.leaf = node.leaf;
|
|
||||||
this.collapsed = node.collapsed || !node.leaf && (!node.children || !node.children.length);
|
|
||||||
this.tr = [];
|
|
||||||
for (var i = 0; i < (node.rows || 1); i++)
|
|
||||||
{
|
|
||||||
var tr = document.createElement('tr');
|
|
||||||
if (startHidden)
|
|
||||||
tr.style.display = 'none';
|
|
||||||
tr._node = this;
|
|
||||||
this.tr.push(tr);
|
|
||||||
}
|
|
||||||
this._oldCells = [];
|
|
||||||
this.render();
|
|
||||||
for (var i = 0; i < this.tr.length; i++)
|
|
||||||
{
|
|
||||||
insertBefore ? grid.tbody.insertBefore(this.tr[i], insertBefore) : grid.tbody.appendChild(this.tr[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.children = [];
|
|
||||||
this.childrenByKey = {};
|
|
||||||
if (node.children)
|
|
||||||
{
|
|
||||||
var trs = [];
|
|
||||||
for (var i = 0; i < node.children.length; i++)
|
|
||||||
{
|
|
||||||
var child = new TreeGridNode(node.children[i], grid, this.level+1, insertBefore, this.collapsed);
|
|
||||||
child._index = i;
|
|
||||||
this.children.push(child);
|
|
||||||
if (child.key !== undefined)
|
|
||||||
this.childrenByKey[child.key] = child;
|
|
||||||
if (grid.stickyInit && !this.collapsed)
|
|
||||||
trs.push.apply(trs, child.tr);
|
|
||||||
}
|
|
||||||
if (trs.length)
|
|
||||||
fixStickyRows(grid.table, trs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeGridNode.prototype.getKey = function()
|
TreeGridNode.prototype.getKey = function()
|
||||||
{
|
{
|
||||||
return this.key;
|
return this.key;
|
||||||
|
@ -500,25 +569,49 @@ TreeGridNode.prototype._renderCell = function(rowIndex, colIndex, cell, force)
|
||||||
{
|
{
|
||||||
// this may be the first render of a row inside a stickyheaders grid
|
// this may be the first render of a row inside a stickyheaders grid
|
||||||
var tr = this.tr[rowIndex];
|
var tr = this.tr[rowIndex];
|
||||||
var ri = tr.cells.length && tr.cells[0].className.indexOf(' _scri') >= 0 ? (colIndex > 0 ? colIndex+1 : 0) : colIndex;
|
var isFix = tr.cells.length && tr.cells[0].className.indexOf(' _scri') >= 0;
|
||||||
|
var ri = isFix ? (colIndex > 0 ? colIndex+1 : 0) : colIndex;
|
||||||
while (!tr.cells[ri])
|
while (!tr.cells[ri])
|
||||||
tr.appendChild(document.createElement('td'));
|
tr.appendChild(document.createElement('td'));
|
||||||
// virtualDOM-like approach: compare old HTML properties
|
// virtualDOM-like approach: compare old HTML properties
|
||||||
if (force || !this._oldCells[rowIndex] || !this._oldCells[rowIndex][colIndex] ||
|
if (force || !this._oldCells[rowIndex] || !this._oldCells[rowIndex][colIndex] ||
|
||||||
cell && !this._hashEqual(this._oldCells[rowIndex][colIndex], cell))
|
cell && !this._hashEqual(this._oldCells[rowIndex][colIndex], cell))
|
||||||
{
|
{
|
||||||
|
var old;
|
||||||
|
if (isFix && colIndex == 0 && rowIndex == 0)
|
||||||
|
{
|
||||||
|
old = {
|
||||||
|
width: tr.cells[ri].style.width,
|
||||||
|
height: tr.cells[ri].style.height,
|
||||||
|
position: tr.cells[ri].style.position,
|
||||||
|
display: tr.cells[ri].style.display,
|
||||||
|
zIndex: tr.cells[ri].style.zIndex
|
||||||
|
};
|
||||||
|
}
|
||||||
this.grid._setProps(tr.cells[ri], cell);
|
this.grid._setProps(tr.cells[ri], cell);
|
||||||
if (colIndex == 0)
|
if (colIndex == 0)
|
||||||
{
|
{
|
||||||
if (rowIndex == 0)
|
if (rowIndex == 0)
|
||||||
|
{
|
||||||
this._addCollapser();
|
this._addCollapser();
|
||||||
|
if (isFix)
|
||||||
|
{
|
||||||
|
// FIXME: this is rather ugly :-( we need to restore stickyheaders styles
|
||||||
|
tr.cells[ri]._oldWidth = tr.cells[ri].style.width;
|
||||||
|
for (var i in old)
|
||||||
|
tr.cells[ri].style[i] = old[i];
|
||||||
|
tr.cells[ri].className += ' _scri'+this.grid.table._scri;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
tr.cells[ri].style.paddingLeft = (this.grid._initialPadding + this.grid.levelIndent * this.level + 20) + 'px';
|
tr.cells[ri].style.paddingLeft = (this.grid._initialPadding + this.grid.levelIndent * this.level + 20) + 'px';
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeGridNode.prototype.render = function(colidx, rowidx, force)
|
TreeGridNode.prototype.render = function(colidx, rowidx, force, skipStickyFix)
|
||||||
{
|
{
|
||||||
var cells = this.grid.renderer(this, colidx, rowidx);
|
var cells = this.grid.renderer(this, colidx, rowidx);
|
||||||
if (this.tr.length == 1)
|
if (this.tr.length == 1)
|
||||||
|
@ -527,17 +620,19 @@ TreeGridNode.prototype.render = function(colidx, rowidx, force)
|
||||||
colidx = undefined;
|
colidx = undefined;
|
||||||
if (rowidx === null || rowidx < 0 || rowidx >= this.tr.length)
|
if (rowidx === null || rowidx < 0 || rowidx >= this.tr.length)
|
||||||
rowidx = undefined;
|
rowidx = undefined;
|
||||||
|
var modified = false;
|
||||||
if (rowidx !== undefined)
|
if (rowidx !== undefined)
|
||||||
{
|
{
|
||||||
if (colidx !== undefined)
|
if (colidx !== undefined)
|
||||||
{
|
{
|
||||||
this._renderCell(rowidx, colidx, cells[rowidx] && cells[rowidx][colidx], force);
|
modified = this._renderCell(rowidx, colidx, cells[rowidx] && cells[rowidx][colidx], force) || modified;
|
||||||
|
this._oldCells[rowidx] = this._oldCells[rowidx] || [];
|
||||||
this._oldCells[rowidx][colidx] = cells[rowidx] && cells[rowidx][colidx];
|
this._oldCells[rowidx][colidx] = cells[rowidx] && cells[rowidx][colidx];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (var j = 0; j < this.grid.header.length; j++)
|
for (var j = 0; j < this.grid.header.length; j++)
|
||||||
this._renderCell(rowidx, j, cells[rowidx] && cells[rowidx][j], force);
|
modified = this._renderCell(rowidx, j, cells[rowidx] && cells[rowidx][j], force) || modified;
|
||||||
this._oldCells[rowidx] = cells[rowidx];
|
this._oldCells[rowidx] = cells[rowidx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -545,7 +640,8 @@ TreeGridNode.prototype.render = function(colidx, rowidx, force)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < this.tr.length; i++)
|
for (var i = 0; i < this.tr.length; i++)
|
||||||
{
|
{
|
||||||
this._renderCell(i, colidx, cells[i] && cells[i][colidx], force);
|
modified = this._renderCell(i, colidx, cells[i] && cells[i][colidx], force) || modified;
|
||||||
|
this._oldCells[i] = this._oldCells[i] || [];
|
||||||
this._oldCells[i][colidx] = cells[i] && cells[i][colidx];
|
this._oldCells[i][colidx] = cells[i] && cells[i][colidx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -553,9 +649,12 @@ TreeGridNode.prototype.render = function(colidx, rowidx, force)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < this.tr.length; i++)
|
for (var i = 0; i < this.tr.length; i++)
|
||||||
for (var j = 0; j < this.grid.header.length; j++)
|
for (var j = 0; j < this.grid.header.length; j++)
|
||||||
this._renderCell(i, j, cells[i] && cells[i][j], force);
|
modified = this._renderCell(i, j, cells[i] && cells[i][j], force) || modified;
|
||||||
this._oldCells = cells;
|
this._oldCells = cells;
|
||||||
}
|
}
|
||||||
|
if (modified && this.grid.stickyInit && !skipStickyFix)
|
||||||
|
fixStickyColumnSizes(this.grid.table);
|
||||||
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeGridNode.prototype._getToggleHandler = function()
|
TreeGridNode.prototype._getToggleHandler = function()
|
||||||
|
@ -606,8 +705,8 @@ TreeGridNode.prototype.toggle = function()
|
||||||
if (!e.collapsed)
|
if (!e.collapsed)
|
||||||
st = st.concat(e.children);
|
st = st.concat(e.children);
|
||||||
}
|
}
|
||||||
if (trs.length)
|
if (trs.length && this.grid.stickyInit)
|
||||||
initStickyRows(this.grid.table, trs, true);
|
fixStickyRows(this.grid.table, trs);
|
||||||
}
|
}
|
||||||
if (this.grid.stickyInit)
|
if (this.grid.stickyInit)
|
||||||
fixStickyColumnSizes(this.grid.table);
|
fixStickyColumnSizes(this.grid.table);
|
||||||
|
@ -708,4 +807,4 @@ TreeGridNode.prototype.addChildren = function(nodes, insertBefore)
|
||||||
return this.children.slice(insertBefore, nodes.length);
|
return this.children.slice(insertBefore, nodes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
;
|
})();
|
||||||
|
|
|
@ -55,7 +55,7 @@ table.stickyheaders td:nth-child(2) { width: 25%; }
|
||||||
} ]);
|
} ]);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
TG.initStickyHeaders();
|
TG.initStickyHeaders({});
|
||||||
}, 3000);
|
}, 3000);
|
||||||
TG.onExpand = function(node)
|
TG.onExpand = function(node)
|
||||||
{
|
{
|
||||||
|
@ -77,7 +77,7 @@ table.stickyheaders td:nth-child(2) { width: 25%; }
|
||||||
TG.initCellEditing();
|
TG.initCellEditing();
|
||||||
|
|
||||||
// Cell selection by mouse drag example
|
// Cell selection by mouse drag example
|
||||||
TG.initCellSelection();
|
TG.initCellSelection(true);
|
||||||
TG.onCellSelect = function(node, i, td)
|
TG.onCellSelect = function(node, i, td)
|
||||||
{
|
{
|
||||||
return i > 0;
|
return i > 0;
|
||||||
|
|
Loading…
Reference in New Issue