Post-refuck fixes
- fix cell editing when virtual scroll is in use - fix slight height targeting problems in Chrome and IE - fix node hover highlight - fix dynamic node list modificationmaster
parent
8f565469ac
commit
b5060d0296
|
@ -184,8 +184,8 @@ table.grid .celleditor input
|
||||||
{
|
{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 1px;
|
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
125
treegrid.js
125
treegrid.js
|
@ -6,7 +6,7 @@
|
||||||
* - dynamic loading of child nodes
|
* - dynamic loading of child nodes
|
||||||
*
|
*
|
||||||
* License: MPL 2.0+, (c) Vitaliy Filippov 2016+
|
* License: MPL 2.0+, (c) Vitaliy Filippov 2016+
|
||||||
* Version: 2017-07-10
|
* Version: 2017-07-12
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -180,6 +180,7 @@ function TreeGrid(options)
|
||||||
// table body wrapper
|
// table body wrapper
|
||||||
this.wrapper.className = 'grid-wrapper';
|
this.wrapper.className = 'grid-wrapper';
|
||||||
this.tableWrapper = document.createElement('div');
|
this.tableWrapper = document.createElement('div');
|
||||||
|
this.tableWrapper.tabIndex = 1;
|
||||||
this.tableWrapper.className = 'grid-body-wrapper';
|
this.tableWrapper.className = 'grid-body-wrapper';
|
||||||
this.tableSizer = document.createElement('div');
|
this.tableSizer = document.createElement('div');
|
||||||
this.tableSizer.className = 'grid-fixed-col-sizer';
|
this.tableSizer.className = 'grid-fixed-col-sizer';
|
||||||
|
@ -242,10 +243,8 @@ function TreeGridNode(node, grid, parentNode, insertIndex, skipSync)
|
||||||
if (node.children)
|
if (node.children)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < node.children.length; i++)
|
for (var i = 0; i < node.children.length; i++)
|
||||||
{
|
|
||||||
new TreeGridNode(node.children[i], grid, this, i, true);
|
new TreeGridNode(node.children[i], grid, this, i, true);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!skipSync)
|
if (!skipSync)
|
||||||
this.grid.syncView();
|
this.grid.syncView();
|
||||||
}
|
}
|
||||||
|
@ -403,18 +402,6 @@ TreeGrid.prototype._syncStart = function(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeGrid.prototype._setPlaceholder = function(name, height)
|
TreeGrid.prototype._setPlaceholder = function(name, height)
|
||||||
{
|
|
||||||
this.placeholders[name][0].firstChild.setAttribute('colspan', this.header.length-(this.fixedColBody ? 1 : 0));
|
|
||||||
this.placeholders[name][0].style.height = height+'px';
|
|
||||||
this.placeholders[name][0].style.display = height > 0 ? '' : 'none';
|
|
||||||
if (this.fixedColBody)
|
|
||||||
{
|
|
||||||
this.placeholders[name][1].style.height = height+'px';
|
|
||||||
this.placeholders[name][1].style.display = height > 0 ? '' : 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeGrid.prototype._addPlaceholder = function(name, height)
|
|
||||||
{
|
{
|
||||||
if (!this.placeholders[name])
|
if (!this.placeholders[name])
|
||||||
{
|
{
|
||||||
|
@ -427,6 +414,20 @@ TreeGrid.prototype._addPlaceholder = function(name, height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (height !== undefined)
|
if (height !== undefined)
|
||||||
|
{
|
||||||
|
this.placeholders[name][0].firstChild.setAttribute('colspan', this.header.length-(this.fixedColBody ? 1 : 0));
|
||||||
|
this.placeholders[name][0].style.height = height+'px';
|
||||||
|
this.placeholders[name][0].style.display = height > 0 ? '' : 'none';
|
||||||
|
if (this.fixedColBody)
|
||||||
|
{
|
||||||
|
this.placeholders[name][1].style.height = height+'px';
|
||||||
|
this.placeholders[name][1].style.display = height > 0 ? '' : 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeGrid.prototype._addPlaceholder = function(name, height)
|
||||||
|
{
|
||||||
this._setPlaceholder(name, height);
|
this._setPlaceholder(name, height);
|
||||||
this._syncStartNode(this.placeholders[name][0], this.placeholders[name][1]);
|
this._syncStartNode(this.placeholders[name][0], this.placeholders[name][1]);
|
||||||
}
|
}
|
||||||
|
@ -439,6 +440,13 @@ TreeGrid.prototype._finishSync = function()
|
||||||
{
|
{
|
||||||
if (!this.oldRenderedNodes[i]._reuse && this.oldRenderedNodes[i].tr)
|
if (!this.oldRenderedNodes[i]._reuse && this.oldRenderedNodes[i].tr)
|
||||||
{
|
{
|
||||||
|
if (this.oldRenderedNodes[i].unrenderHooks)
|
||||||
|
{
|
||||||
|
var hooks = this.oldRenderedNodes[i].unrenderHooks;
|
||||||
|
for (var k in hooks)
|
||||||
|
hooks[k]();
|
||||||
|
delete this.oldRenderedNodes[i].unrenderHooks;
|
||||||
|
}
|
||||||
this.oldRenderedNodes[i].tr = null;
|
this.oldRenderedNodes[i].tr = null;
|
||||||
this.oldRenderedNodes[i].col_tr = null;
|
this.oldRenderedNodes[i].col_tr = null;
|
||||||
this.oldRenderedNodes[i]._oldCells = [];
|
this.oldRenderedNodes[i]._oldCells = [];
|
||||||
|
@ -503,13 +511,17 @@ TreeGrid.prototype.syncView = function()
|
||||||
{
|
{
|
||||||
if (!this.table.offsetParent)
|
if (!this.table.offsetParent)
|
||||||
return;
|
return;
|
||||||
var offsetHeight = this.wrapper.getBoundingClientRect().height - (this.stickyRow ? this.thead.getBoundingClientRect().height : 0);
|
var headerHeight = (this.stickyRow ? this.thead.getBoundingClientRect().height : 0);
|
||||||
|
var offsetHeight = this.tableWrapper.clientHeight - headerHeight;
|
||||||
|
var scrollTop = this.tableWrapper.scrollTop;
|
||||||
var visibleItems = Math.ceil(offsetHeight/this.minItemHeight);
|
var visibleItems = Math.ceil(offsetHeight/this.minItemHeight);
|
||||||
var endStart = this.nodeCount - visibleItems;
|
var endStart = this.nodeCount - visibleItems;
|
||||||
|
var targetHeight;
|
||||||
if (endStart < 0)
|
if (endStart < 0)
|
||||||
endStart = 0;
|
endStart = 0;
|
||||||
// Always render last items
|
// Always render last items
|
||||||
var lastNodes = this._findNodes(this.root, endStart, this.nodeCount-endStart);
|
var lastNodes = this._findNodes(this.root, endStart, this.nodeCount-endStart);
|
||||||
|
this._setPlaceholder('top', 0);
|
||||||
this._beginSync();
|
this._beginSync();
|
||||||
this._renderNodes(lastNodes);
|
this._renderNodes(lastNodes);
|
||||||
this._syncEnd(lastNodes);
|
this._syncEnd(lastNodes);
|
||||||
|
@ -519,18 +531,18 @@ TreeGrid.prototype.syncView = function()
|
||||||
var lastFirst = endStart+this._findVisibleNodeOffset(lastNodes, offsetHeight);
|
var lastFirst = endStart+this._findVisibleNodeOffset(lastNodes, offsetHeight);
|
||||||
var avgItemHeight = this.endItemHeight / lastNodes.length;
|
var avgItemHeight = this.endItemHeight / lastNodes.length;
|
||||||
avgItemHeight = (avgItemHeight < this.minItemHeight ? this.minItemHeight : avgItemHeight);
|
avgItemHeight = (avgItemHeight < this.minItemHeight ? this.minItemHeight : avgItemHeight);
|
||||||
|
targetHeight = headerHeight + this.nodeCount*avgItemHeight;
|
||||||
if (this.stickyColumn)
|
if (this.stickyColumn)
|
||||||
this.fixedColSizer.style.height = (this.nodeCount*avgItemHeight)+'px';
|
this.fixedColSizer.style.height = targetHeight+'px';
|
||||||
this.tableSizer.style.height = (this.nodeCount*avgItemHeight)+'px';
|
this.tableSizer.style.height = targetHeight+'px';
|
||||||
var tbwh = this.tableWrapper.getBoundingClientRect().height;
|
var scrollPos = targetHeight > offsetHeight ? scrollTop / (targetHeight - offsetHeight) : 0;
|
||||||
var scrollPos = this.nodeCount*avgItemHeight > tbwh ?
|
|
||||||
this.tableWrapper.scrollTop / (this.nodeCount*avgItemHeight - tbwh) : 0;
|
|
||||||
if (scrollPos > 1)
|
if (scrollPos > 1)
|
||||||
scrollPos = 1;
|
scrollPos = 1;
|
||||||
var firstVisible = scrollPos*lastFirst;
|
var firstVisible = scrollPos*lastFirst;
|
||||||
var rangeStart = Math.floor(firstVisible);
|
var rangeStart = Math.floor(firstVisible);
|
||||||
var rangeCount = visibleItems;
|
var rangeCount = visibleItems;
|
||||||
this._addPlaceholder('top', 0);
|
this._addPlaceholder('top', 0);
|
||||||
|
this._setPlaceholder('mid', 0);
|
||||||
var firstItemHeight;
|
var firstItemHeight;
|
||||||
if (rangeStart >= endStart)
|
if (rangeStart >= endStart)
|
||||||
{
|
{
|
||||||
|
@ -554,7 +566,7 @@ TreeGrid.prototype.syncView = function()
|
||||||
this._addPlaceholder('mid', 0);
|
this._addPlaceholder('mid', 0);
|
||||||
firstItemHeight = getNodeHeight(visibleNodes[0])*(firstVisible-rangeStart);
|
firstItemHeight = getNodeHeight(visibleNodes[0])*(firstVisible-rangeStart);
|
||||||
}
|
}
|
||||||
this._setPlaceholder('top', this.tableWrapper.scrollTop-firstItemHeight);
|
this._setPlaceholder('top', scrollTop-firstItemHeight-headerHeight);
|
||||||
}
|
}
|
||||||
else if (this.stickyColumn)
|
else if (this.stickyColumn)
|
||||||
{
|
{
|
||||||
|
@ -571,12 +583,13 @@ TreeGrid.prototype.syncView = function()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.syncStickyRow();
|
this.syncStickyRow();
|
||||||
|
if (endStart > 0 && endStart > rangeStart+rangeCount)
|
||||||
for (var i = 0; i < this.tbody.rows.length; i++)
|
for (var i = 0; i < this.tbody.rows.length; i++)
|
||||||
total += (this.tbody.rows[i].getBoundingClientRect().height||0);
|
total += (this.tbody.rows[i].getBoundingClientRect().height||0);
|
||||||
}
|
}
|
||||||
if (endStart > 0 && endStart > rangeStart+rangeCount)
|
if (endStart > 0 && endStart > rangeStart+rangeCount)
|
||||||
{
|
{
|
||||||
this._setPlaceholder('mid', this.nodeCount*avgItemHeight - total - (this.tableWrapper.scrollTop-firstItemHeight));
|
this._setPlaceholder('mid', targetHeight - total - (scrollTop-firstItemHeight));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -594,9 +607,11 @@ TreeGrid.prototype.nodeOnHover = function(hover, ev)
|
||||||
e = e.previousSibling;
|
e = e.previousSibling;
|
||||||
subrow++;
|
subrow++;
|
||||||
}
|
}
|
||||||
|
if (!e._node.tr)
|
||||||
|
return;
|
||||||
if (hover)
|
if (hover)
|
||||||
{
|
{
|
||||||
if (this.hoveredNode)
|
if (this.hoveredNode && this.hoveredNode.tr)
|
||||||
{
|
{
|
||||||
this.hoveredNode.tr[this.hoveredRow].className = this.hoveredNode.tr[this.hoveredRow].className.replace(/ hover/g, '');
|
this.hoveredNode.tr[this.hoveredRow].className = this.hoveredNode.tr[this.hoveredRow].className.replace(/ hover/g, '');
|
||||||
if (this.stickyColumn)
|
if (this.stickyColumn)
|
||||||
|
@ -834,11 +849,13 @@ TreeGrid.prototype.initCellEditing = function()
|
||||||
td = td.parentNode;
|
td = td.parentNode;
|
||||||
if (td.nodeName == 'TD' && td.parentNode._node && td.className.indexOf('celleditor') < 0)
|
if (td.nodeName == 'TD' && td.parentNode._node && td.className.indexOf('celleditor') < 0)
|
||||||
{
|
{
|
||||||
var params = self.onStartCellEdit &&
|
var node = td.parentNode._node;
|
||||||
self.onStartCellEdit(td.parentNode._node, self._getSubrow(td), self._getCellIndex(td, true), td) || {};
|
var subrow = self._getSubrow(td);
|
||||||
|
var cellIndex = self._getCellIndex(td, true);
|
||||||
|
var params = self.onStartCellEdit && self.onStartCellEdit(node, subrow, cellIndex, td) || {};
|
||||||
if (params.abort)
|
if (params.abort)
|
||||||
return;
|
return;
|
||||||
self.startCellEditing(td, params);
|
self.startCellEditing(td, params, node, subrow, cellIndex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
addListener(document.body, 'click', function(evt)
|
addListener(document.body, 'click', function(evt)
|
||||||
|
@ -851,14 +868,25 @@ TreeGrid.prototype.initCellEditing = function()
|
||||||
if (td && /\bcelleditor\b/.exec(td.className))
|
if (td && /\bcelleditor\b/.exec(td.className))
|
||||||
return;
|
return;
|
||||||
for (var i = self.editedCells.length-1; i >= 0; i--)
|
for (var i = self.editedCells.length-1; i >= 0; i--)
|
||||||
self.stopCellEditing(self.editedCells[i], 1);
|
self.stopCellEditing(self.editedCells[i], true);
|
||||||
self.editedCells = [];
|
self.editedCells = [];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeGrid.prototype.startCellEditing = function(td, params)
|
TreeGrid.prototype.startCellEditing = function(td, params, node, subrow, cellIndex)
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
node = td.parentNode._node;
|
||||||
|
subrow = self._getSubrow(td);
|
||||||
|
cellIndex = self._getCellIndex(td, true);
|
||||||
|
}
|
||||||
|
node.unrenderHooks = node.unrenderHooks||{};
|
||||||
|
node.unrenderHooks.editing = function()
|
||||||
|
{
|
||||||
|
self.stopCellEditing(td, false, node, subrow, cellIndex);
|
||||||
|
};
|
||||||
self.editedCells.push(td);
|
self.editedCells.push(td);
|
||||||
if (params.value === undefined)
|
if (params.value === undefined)
|
||||||
params.value = td.innerHTML;
|
params.value = td.innerHTML;
|
||||||
|
@ -884,18 +912,25 @@ TreeGrid.prototype.startCellEditing = function(td, params)
|
||||||
inp.focus();
|
inp.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeGrid.prototype.stopCellEditing = function(td, _int)
|
TreeGrid.prototype.stopCellEditing = function(td, _int, node, subrow, cellIndex)
|
||||||
{
|
{
|
||||||
var i = this._getCellIndex(td, true);
|
if (td.offsetParent)
|
||||||
var subrow = this._getSubrow(td);
|
{
|
||||||
var node = td.parentNode._node;
|
node = td.parentNode._node;
|
||||||
node._oldCells[i] = undefined;
|
subrow = this._getSubrow(td);
|
||||||
|
cellIndex = this._getCellIndex(td, true);
|
||||||
|
}
|
||||||
|
delete node.unrenderHooks.editing;
|
||||||
td.style.width = td._origWidth;
|
td.style.width = td._origWidth;
|
||||||
td.style.minWidth = td._origMinWidth;
|
td.style.minWidth = td._origMinWidth;
|
||||||
var inp = td.getElementsByTagName('input')[0];
|
var inp = td.getElementsByTagName('input')[0];
|
||||||
|
node._oldCells[cellIndex] = undefined;
|
||||||
var params = this.onStopCellEdit &&
|
var params = this.onStopCellEdit &&
|
||||||
this.onStopCellEdit(node, subrow, i, inp ? inp.value : null, td) || {};
|
this.onStopCellEdit(node, subrow, cellIndex, inp ? inp.value : null, td) || {};
|
||||||
node.render(i, subrow, true, 0);
|
if (td.offsetParent)
|
||||||
|
{
|
||||||
|
node.render(cellIndex, subrow, true, 0);
|
||||||
|
}
|
||||||
if (!_int)
|
if (!_int)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < this.editedCells.length; i++)
|
for (var i = 0; i < this.editedCells.length; i++)
|
||||||
|
@ -910,6 +945,7 @@ TreeGrid.prototype.stopCellEditing = function(td, _int)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple cell selection
|
// Simple cell selection
|
||||||
|
// FIXME add to unrenderHooks?
|
||||||
|
|
||||||
TreeGrid.prototype.initCellSelection = function(useMultiple)
|
TreeGrid.prototype.initCellSelection = function(useMultiple)
|
||||||
{
|
{
|
||||||
|
@ -1252,20 +1288,30 @@ function setHiddenNodes(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeGridNode.prototype.toggle = function()
|
TreeGridNode.prototype.toggle = function(skipSync)
|
||||||
{
|
{
|
||||||
if (this.leaf)
|
if (this.leaf)
|
||||||
return;
|
return;
|
||||||
this.collapsed = !this.collapsed;
|
this.collapsed = !this.collapsed;
|
||||||
|
if (this.tr)
|
||||||
|
{
|
||||||
(this.col_tr || this.tr)[0].cells[0].firstChild.className =
|
(this.col_tr || this.tr)[0].cells[0].firstChild.className =
|
||||||
this.collapsed ? 'collapser collapser-collapsed' : 'collapser collapser-expanded';
|
this.collapsed ? 'collapser collapser-collapsed' : 'collapser collapser-expanded';
|
||||||
|
}
|
||||||
setHiddenNodes(this);
|
setHiddenNodes(this);
|
||||||
|
if (!skipSync)
|
||||||
this.grid.syncView();
|
this.grid.syncView();
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeGridNode.prototype.setChildren = function(isLeaf, newChildren, skipSync)
|
TreeGridNode.prototype.setChildren = function(isLeaf, newChildren, skipSync)
|
||||||
{
|
{
|
||||||
this.leaf = isLeaf;
|
this.leaf = isLeaf;
|
||||||
|
if (this.visible)
|
||||||
|
{
|
||||||
|
this.visible = false;
|
||||||
|
setHiddenNodes(this);
|
||||||
|
this.visible = true;
|
||||||
|
}
|
||||||
this.children = [];
|
this.children = [];
|
||||||
this.childrenByKey = {};
|
this.childrenByKey = {};
|
||||||
this.addChildren(newChildren, null, skipSync);
|
this.addChildren(newChildren, null, skipSync);
|
||||||
|
@ -1297,10 +1343,7 @@ TreeGridNode.prototype.addChildren = function(nodes, insertBefore, skipSync)
|
||||||
e = this.children[insertBefore];
|
e = this.children[insertBefore];
|
||||||
for (var i = 0; i < nodes.length; i++)
|
for (var i = 0; i < nodes.length; i++)
|
||||||
{
|
{
|
||||||
var child = new TreeGridNode(nodes[i], this.grid, this, i, true);
|
new TreeGridNode(nodes[i], this.grid, this, insertBefore+i, true);
|
||||||
this.children.splice(insertBefore+i, 0, child);
|
|
||||||
if (child.key !== undefined)
|
|
||||||
this.childrenByKey[child.key] = child;
|
|
||||||
}
|
}
|
||||||
if (!skipSync)
|
if (!skipSync)
|
||||||
this.grid.syncView();
|
this.grid.syncView();
|
||||||
|
|
Loading…
Reference in New Issue