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 modification
master
Vitaliy Filippov 2017-07-12 19:09:39 +03:00
parent 8f565469ac
commit b5060d0296
2 changed files with 91 additions and 48 deletions

View File

@ -184,8 +184,8 @@ table.grid .celleditor input
{
position: absolute;
left: 0;
right: 0;
top: 0;
width: 1px;
z-index: 0;
}

View File

@ -6,7 +6,7 @@
* - dynamic loading of child nodes
*
* 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
this.wrapper.className = 'grid-wrapper';
this.tableWrapper = document.createElement('div');
this.tableWrapper.tabIndex = 1;
this.tableWrapper.className = 'grid-body-wrapper';
this.tableSizer = document.createElement('div');
this.tableSizer.className = 'grid-fixed-col-sizer';
@ -242,9 +243,7 @@ function TreeGridNode(node, grid, parentNode, insertIndex, skipSync)
if (node.children)
{
for (var i = 0; i < node.children.length; i++)
{
new TreeGridNode(node.children[i], grid, this, i, true);
}
}
if (!skipSync)
this.grid.syncView();
@ -403,18 +402,6 @@ TreeGrid.prototype._syncStart = function(nodes)
}
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])
{
@ -427,7 +414,21 @@ TreeGrid.prototype._addPlaceholder = function(name, height)
}
}
if (height !== undefined)
this._setPlaceholder(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)
{
this._setPlaceholder(name, height);
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].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].col_tr = null;
this.oldRenderedNodes[i]._oldCells = [];
@ -503,13 +511,17 @@ TreeGrid.prototype.syncView = function()
{
if (!this.table.offsetParent)
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 endStart = this.nodeCount - visibleItems;
var targetHeight;
if (endStart < 0)
endStart = 0;
// Always render last items
var lastNodes = this._findNodes(this.root, endStart, this.nodeCount-endStart);
this._setPlaceholder('top', 0);
this._beginSync();
this._renderNodes(lastNodes);
this._syncEnd(lastNodes);
@ -519,18 +531,18 @@ TreeGrid.prototype.syncView = function()
var lastFirst = endStart+this._findVisibleNodeOffset(lastNodes, offsetHeight);
var avgItemHeight = this.endItemHeight / lastNodes.length;
avgItemHeight = (avgItemHeight < this.minItemHeight ? this.minItemHeight : avgItemHeight);
targetHeight = headerHeight + this.nodeCount*avgItemHeight;
if (this.stickyColumn)
this.fixedColSizer.style.height = (this.nodeCount*avgItemHeight)+'px';
this.tableSizer.style.height = (this.nodeCount*avgItemHeight)+'px';
var tbwh = this.tableWrapper.getBoundingClientRect().height;
var scrollPos = this.nodeCount*avgItemHeight > tbwh ?
this.tableWrapper.scrollTop / (this.nodeCount*avgItemHeight - tbwh) : 0;
this.fixedColSizer.style.height = targetHeight+'px';
this.tableSizer.style.height = targetHeight+'px';
var scrollPos = targetHeight > offsetHeight ? scrollTop / (targetHeight - offsetHeight) : 0;
if (scrollPos > 1)
scrollPos = 1;
var firstVisible = scrollPos*lastFirst;
var rangeStart = Math.floor(firstVisible);
var rangeCount = visibleItems;
this._addPlaceholder('top', 0);
this._setPlaceholder('mid', 0);
var firstItemHeight;
if (rangeStart >= endStart)
{
@ -554,7 +566,7 @@ TreeGrid.prototype.syncView = function()
this._addPlaceholder('mid', 0);
firstItemHeight = getNodeHeight(visibleNodes[0])*(firstVisible-rangeStart);
}
this._setPlaceholder('top', this.tableWrapper.scrollTop-firstItemHeight);
this._setPlaceholder('top', scrollTop-firstItemHeight-headerHeight);
}
else if (this.stickyColumn)
{
@ -571,12 +583,13 @@ TreeGrid.prototype.syncView = function()
else
{
this.syncStickyRow();
for (var i = 0; i < this.tbody.rows.length; i++)
total += (this.tbody.rows[i].getBoundingClientRect().height||0);
if (endStart > 0 && endStart > rangeStart+rangeCount)
for (var i = 0; i < this.tbody.rows.length; i++)
total += (this.tbody.rows[i].getBoundingClientRect().height||0);
}
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;
subrow++;
}
if (!e._node.tr)
return;
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, '');
if (this.stickyColumn)
@ -834,11 +849,13 @@ TreeGrid.prototype.initCellEditing = function()
td = td.parentNode;
if (td.nodeName == 'TD' && td.parentNode._node && td.className.indexOf('celleditor') < 0)
{
var params = self.onStartCellEdit &&
self.onStartCellEdit(td.parentNode._node, self._getSubrow(td), self._getCellIndex(td, true), td) || {};
var node = td.parentNode._node;
var subrow = self._getSubrow(td);
var cellIndex = self._getCellIndex(td, true);
var params = self.onStartCellEdit && self.onStartCellEdit(node, subrow, cellIndex, td) || {};
if (params.abort)
return;
self.startCellEditing(td, params);
self.startCellEditing(td, params, node, subrow, cellIndex);
}
});
addListener(document.body, 'click', function(evt)
@ -851,14 +868,25 @@ TreeGrid.prototype.initCellEditing = function()
if (td && /\bcelleditor\b/.exec(td.className))
return;
for (var i = self.editedCells.length-1; i >= 0; i--)
self.stopCellEditing(self.editedCells[i], 1);
self.stopCellEditing(self.editedCells[i], true);
self.editedCells = [];
});
}
TreeGrid.prototype.startCellEditing = function(td, params)
TreeGrid.prototype.startCellEditing = function(td, params, node, subrow, cellIndex)
{
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);
if (params.value === undefined)
params.value = td.innerHTML;
@ -884,18 +912,25 @@ TreeGrid.prototype.startCellEditing = function(td, params)
inp.focus();
}
TreeGrid.prototype.stopCellEditing = function(td, _int)
TreeGrid.prototype.stopCellEditing = function(td, _int, node, subrow, cellIndex)
{
var i = this._getCellIndex(td, true);
var subrow = this._getSubrow(td);
var node = td.parentNode._node;
node._oldCells[i] = undefined;
if (td.offsetParent)
{
node = td.parentNode._node;
subrow = this._getSubrow(td);
cellIndex = this._getCellIndex(td, true);
}
delete node.unrenderHooks.editing;
td.style.width = td._origWidth;
td.style.minWidth = td._origMinWidth;
var inp = td.getElementsByTagName('input')[0];
node._oldCells[cellIndex] = undefined;
var params = this.onStopCellEdit &&
this.onStopCellEdit(node, subrow, i, inp ? inp.value : null, td) || {};
node.render(i, subrow, true, 0);
this.onStopCellEdit(node, subrow, cellIndex, inp ? inp.value : null, td) || {};
if (td.offsetParent)
{
node.render(cellIndex, subrow, true, 0);
}
if (!_int)
{
for (var i = 0; i < this.editedCells.length; i++)
@ -910,6 +945,7 @@ TreeGrid.prototype.stopCellEditing = function(td, _int)
}
// Simple cell selection
// FIXME add to unrenderHooks?
TreeGrid.prototype.initCellSelection = function(useMultiple)
{
@ -1252,20 +1288,30 @@ function setHiddenNodes(node)
}
}
TreeGridNode.prototype.toggle = function()
TreeGridNode.prototype.toggle = function(skipSync)
{
if (this.leaf)
return;
this.collapsed = !this.collapsed;
(this.col_tr || this.tr)[0].cells[0].firstChild.className =
this.collapsed ? 'collapser collapser-collapsed' : 'collapser collapser-expanded';
if (this.tr)
{
(this.col_tr || this.tr)[0].cells[0].firstChild.className =
this.collapsed ? 'collapser collapser-collapsed' : 'collapser collapser-expanded';
}
setHiddenNodes(this);
this.grid.syncView();
if (!skipSync)
this.grid.syncView();
}
TreeGridNode.prototype.setChildren = function(isLeaf, newChildren, skipSync)
{
this.leaf = isLeaf;
if (this.visible)
{
this.visible = false;
setHiddenNodes(this);
this.visible = true;
}
this.children = [];
this.childrenByKey = {};
this.addChildren(newChildren, null, skipSync);
@ -1297,10 +1343,7 @@ TreeGridNode.prototype.addChildren = function(nodes, insertBefore, skipSync)
e = this.children[insertBefore];
for (var i = 0; i < nodes.length; i++)
{
var child = new TreeGridNode(nodes[i], this.grid, this, i, true);
this.children.splice(insertBefore+i, 0, child);
if (child.key !== undefined)
this.childrenByKey[child.key] = child;
new TreeGridNode(nodes[i], this.grid, this, insertBefore+i, true);
}
if (!skipSync)
this.grid.syncView();