Use renderer and fast rectangle selection
parent
07319d4a5b
commit
ede717a8f1
10
treegrid.css
10
treegrid.css
|
@ -95,6 +95,16 @@ table.grid .collapser-expanded
|
|||
display: none;
|
||||
}
|
||||
|
||||
.disable-text-select
|
||||
{
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* cell editor */
|
||||
table.grid .celleditor
|
||||
{
|
||||
|
|
142
treegrid.js
142
treegrid.js
|
@ -1,17 +1,19 @@
|
|||
/**
|
||||
* Very simple and fast tree grid/table, compatible with dynamic loading and jQuery fixedHeaderTable
|
||||
* License: MPL 2.0+, (c) Vitaliy Filippov 2016+
|
||||
* Version: 2016-05-18
|
||||
* Version: 2016-06-05
|
||||
*/
|
||||
|
||||
/**
|
||||
* USAGE:
|
||||
*
|
||||
* var TG = new TreeGrid(items, header);
|
||||
* var TG = new TreeGrid({ items: items, header: header, renderer: renderer });
|
||||
* document.body.appendChild(TG.table);
|
||||
*
|
||||
* // renderer is a function that generates cell HTML properties for a node
|
||||
* renderer = function(node) { return [ { innerHTML, style, className, title }, ... ] }
|
||||
*
|
||||
* items: [ node={
|
||||
* cells: [ 'html' or { innerHTML, style, className, title }, ... ],
|
||||
* children: [ node... ],
|
||||
* leaf: true/false,
|
||||
* collapsed: true/false,
|
||||
|
@ -36,6 +38,9 @@
|
|||
* // expand/collapse a node
|
||||
* node.toggle();
|
||||
*
|
||||
* // change node.data and re-render node
|
||||
* node.render();
|
||||
*
|
||||
* // use simple cell editing
|
||||
* TG.initCellEditing();
|
||||
*
|
||||
|
@ -60,8 +65,9 @@
|
|||
* TG.onStopCellEdit = function (node, colIndex, value, td) { return { html: <new cell content> } }
|
||||
*
|
||||
*/
|
||||
function TreeGrid(items, header)
|
||||
function TreeGrid(options)
|
||||
{
|
||||
this.renderer = options.renderer;
|
||||
this.bindProps = { 'style': 1, 'className': 1, 'title': 1, 'innerHTML': 1 };
|
||||
this.levelIndent = 20;
|
||||
this.table = document.createElement('table');
|
||||
|
@ -71,8 +77,8 @@ function TreeGrid(items, header)
|
|||
this.table.appendChild(this.thead);
|
||||
this.table.appendChild(this.tbody);
|
||||
this.thead.appendChild(document.createElement('tr'));
|
||||
this.setHeader(header);
|
||||
new TreeGridNode({ children: items }, this);
|
||||
this.setHeader(options.header);
|
||||
new TreeGridNode({ children: options.items }, this);
|
||||
}
|
||||
|
||||
TreeGrid.prototype.setHeader = function(newHeader)
|
||||
|
@ -113,7 +119,7 @@ TreeGrid.prototype._setProps = function(el, props)
|
|||
|
||||
// Simple cell editing
|
||||
|
||||
TreeGrid.prototype.getNodeIndex = function(n)
|
||||
TreeGrid.prototype._getCellIndex = function(n)
|
||||
{
|
||||
var i = 0;
|
||||
while ((n = n.previousSibling))
|
||||
|
@ -133,7 +139,7 @@ TreeGrid.prototype.initCellEditing = function()
|
|||
td = td.parentNode;
|
||||
if (td.nodeName == 'TD' && td.parentNode._node && td.previousSibling && td.className != 'celleditor')
|
||||
{
|
||||
var params = self.onStartCellEdit && self.onStartCellEdit(td.parentNode._node, self.getNodeIndex(td), td) || {};
|
||||
var params = self.onStartCellEdit && self.onStartCellEdit(td.parentNode._node, self._getCellIndex(td), td) || {};
|
||||
if (params.abort)
|
||||
return;
|
||||
self.editedCells.push(td);
|
||||
|
@ -166,14 +172,11 @@ TreeGrid.prototype.initCellEditing = function()
|
|||
|
||||
TreeGrid.prototype.stopCellEditing = function(td, _int)
|
||||
{
|
||||
var i = this.getNodeIndex(td);
|
||||
var params = this.onStopCellEdit && this.onStopCellEdit(td.parentNode._node, i, td.firstChild.value, td) || {};
|
||||
td.innerHTML = params.html === undefined || params.html === null ? td.firstChild.value : params.html;
|
||||
if (typeof td.parentNode._node.cells[i] == 'object')
|
||||
td.parentNode._node.cells[i].innerHTML = td.innerHTML;
|
||||
else
|
||||
td.parentNode._node.cells[i] = td.innerHTML;
|
||||
td.className = '';
|
||||
var i = this._getCellIndex(td);
|
||||
var node = td.parentNode._node;
|
||||
node._oldCells[i] = undefined;
|
||||
var params = this.onStopCellEdit && this.onStopCellEdit(node, i, td.firstChild.value, td) || {};
|
||||
node.render(i);
|
||||
if (!_int)
|
||||
{
|
||||
for (var i = 0; i < this.editedCells.length; i++)
|
||||
|
@ -194,19 +197,20 @@ TreeGrid.prototype.initCellSelection = function(restrictToNode)
|
|||
var startDrag, dragDiv;
|
||||
|
||||
var self = this;
|
||||
self.table.className += ' disable-text-select';
|
||||
|
||||
self.selectCell = function(cell)
|
||||
{
|
||||
if (!cell.parentNode._node || cell.className.indexOf(' selected') >= 0)
|
||||
return;
|
||||
var i = self.getNodeIndex(cell);
|
||||
var i = self._getCellIndex(cell);
|
||||
if (!self.onCellSelect || self.onCellSelect(cell.parentNode._node, i, cell))
|
||||
cell.className += ' selected';
|
||||
};
|
||||
|
||||
self.deselectCell = function(cell)
|
||||
{
|
||||
self.onCellDeselect && self.onCellDeselect(cell.parentNode._node, self.getNodeIndex(cell), cell);
|
||||
self.onCellDeselect && self.onCellDeselect(cell.parentNode._node, self._getCellIndex(cell), cell);
|
||||
cell.className = cell.className.replace(' selected', '');
|
||||
};
|
||||
|
||||
|
@ -288,7 +292,43 @@ TreeGrid.prototype.initCellSelection = function(restrictToNode)
|
|||
}
|
||||
startDrag = null;
|
||||
if (x2 > x1+10 && y2 > y1+10)
|
||||
rectangleSelect(self.table.getElementsByTagName('td'), x1, y1, x2, y2, self.selectCell);
|
||||
{
|
||||
var bodyPos = getOffset(self.tbody);
|
||||
if (x1 < bodyPos.left+self.tbody.offsetWidth &&
|
||||
y1 < bodyPos.top+self.tbody.offsetHeight &&
|
||||
x2 > bodyPos.left && y2 > bodyPos.top)
|
||||
{
|
||||
if (x1 <= bodyPos.left)
|
||||
x1 = bodyPos.left+1;
|
||||
if (y1 <= bodyPos.top)
|
||||
y1 = bodyPos.top+1;
|
||||
if (x2 >= bodyPos.left+self.tbody.offsetWidth)
|
||||
x2 = bodyPos.left+self.tbody.offsetWidth-1;
|
||||
if (y2 >= bodyPos.top+self.tbody.offsetHeight)
|
||||
y2 = bodyPos.top+self.tbody.offsetHeight-1;
|
||||
var e1 = document.elementFromPoint(x1, y1);
|
||||
var e2 = document.elementFromPoint(x2, y2);
|
||||
while (e1 && e1.nodeName != 'TD')
|
||||
e1 = e1.parentNode;
|
||||
while (e2 && e2.nodeName != 'TD')
|
||||
e2 = e2.parentNode;
|
||||
if (e1 && e2)
|
||||
{
|
||||
var col1 = self._getCellIndex(e1);
|
||||
var col2 = self._getCellIndex(e2);
|
||||
var row = e1.parentNode;
|
||||
do
|
||||
{
|
||||
if (row.style.display != 'none')
|
||||
for (var i = col1; i <= col2; i++)
|
||||
self.selectCell(row.cells[i]);
|
||||
if (row == e2.parentNode)
|
||||
break;
|
||||
row = row.nextSibling;
|
||||
} while (row);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var t = evt.target || evt.srcElement;
|
||||
|
@ -303,18 +343,6 @@ TreeGrid.prototype.initCellSelection = function(restrictToNode)
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
function rectangleSelect(elements, x1, y1, x2, y2, cb)
|
||||
{
|
||||
for (var i = 0; i < elements.length; i++)
|
||||
{
|
||||
var offset = getOffset(elements[i]);
|
||||
var w = elements[i].offsetWidth;
|
||||
var h = elements[i].offsetHeight;
|
||||
if (offset.left+w >= x1 && offset.top+h >= y1 && offset.left <= x2 && offset.top <= y2)
|
||||
cb(elements[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tree grid node class
|
||||
|
@ -332,19 +360,12 @@ function TreeGridNode(node, grid, level, insertBefore, startHidden)
|
|||
{
|
||||
this.leaf = node.leaf;
|
||||
this.collapsed = node.collapsed || !node.leaf && (!node.children || !node.children.length);
|
||||
this.cells = node.cells || [];
|
||||
this.tr = document.createElement('tr');
|
||||
if (startHidden)
|
||||
this.tr.style.display = 'none';
|
||||
this.tr._node = this;
|
||||
for (var i = 0; i < this.grid.header.length; i++)
|
||||
{
|
||||
var td = document.createElement('td');
|
||||
if (this.cells[i])
|
||||
grid._setProps(td, this.cells[i]);
|
||||
this.tr.appendChild(td);
|
||||
}
|
||||
this.addCollapser();
|
||||
this._oldCells = [];
|
||||
this.render();
|
||||
insertBefore ? grid.tbody.insertBefore(this.tr, insertBefore) : grid.tbody.appendChild(this.tr);
|
||||
}
|
||||
this.children = [];
|
||||
|
@ -353,7 +374,7 @@ function TreeGridNode(node, grid, level, insertBefore, startHidden)
|
|||
this.children.push(new TreeGridNode(node.children[i], grid, this.level+1, insertBefore, this.collapsed));
|
||||
}
|
||||
|
||||
TreeGridNode.prototype.addCollapser = function()
|
||||
TreeGridNode.prototype._addCollapser = function()
|
||||
{
|
||||
var collapser = document.createElement('div');
|
||||
if (this.leaf)
|
||||
|
@ -377,11 +398,44 @@ TreeGridNode.prototype.addCollapser = function()
|
|||
c0.style.paddingLeft = (this.grid._initialPadding + this.grid.levelIndent * this.level + 20) + 'px';
|
||||
}
|
||||
|
||||
TreeGridNode.prototype.refreshCells = function()
|
||||
TreeGridNode.prototype._hashEqual = function(a, b)
|
||||
{
|
||||
for (var i in a)
|
||||
if (b[i] !== a[i])
|
||||
return false;
|
||||
for (var i in b)
|
||||
if (b[i] !== a[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
TreeGridNode.prototype._renderCell = function(i, cell)
|
||||
{
|
||||
if (!this.tr.cells[i])
|
||||
this.tr.appendChild(document.createElement('td'));
|
||||
// virtualDOM-like approach: compare old HTML properties
|
||||
if (!this._oldCells[i] || cell && !this._hashEqual(this._oldCells[i], cell))
|
||||
{
|
||||
this.grid._setProps(this.tr.cells[i], cell);
|
||||
if (i == 0)
|
||||
this._addCollapser();
|
||||
}
|
||||
}
|
||||
|
||||
TreeGridNode.prototype.render = function(colidx)
|
||||
{
|
||||
var cells = this.grid.renderer(this, colidx);
|
||||
if (colidx !== null && colidx !== undefined && colidx >= 0 && colidx < this.grid.header.length)
|
||||
{
|
||||
this._renderCell(colidx, cells[colidx]);
|
||||
this._oldCells[colidx] = cells[colidx];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < this.grid.header.length; i++)
|
||||
this.grid._setProps(this.tr.cells[i], this.cells[i]);
|
||||
this.addCollapser();
|
||||
this._renderCell(i, cells[i]);
|
||||
this._oldCells = cells;
|
||||
}
|
||||
}
|
||||
|
||||
TreeGridNode.prototype._getToggleHandler = function()
|
||||
|
|
|
@ -17,16 +17,22 @@
|
|||
onDomReady(function()
|
||||
{
|
||||
var i = {
|
||||
cells: [ 'Payment from Gazprom to Shell 2', '4', '5' ]
|
||||
data: [ 'Payment from Gazprom to Shell 2', '4', '5' ]
|
||||
};
|
||||
var TG = new TreeGrid([], []);
|
||||
var TG = new TreeGrid({ header: [], items: [], renderer: function(node)
|
||||
{
|
||||
var cells = [];
|
||||
for (var i = 0; i < node.data.length; i++)
|
||||
cells[i] = { innerHTML: node.data[i] };
|
||||
return cells;
|
||||
} });
|
||||
TG.table.className = 'grid';
|
||||
document.getElementById('innerdiv').appendChild(TG.table);
|
||||
$(TG.table).fixedHeaderTable({});
|
||||
setTimeout(function() {
|
||||
TG.setHeader([ '', '15 фев', '16 фев', '17 фев', '18 фев', '19 фев', '20 фев', '21 фев' ]);
|
||||
TG.root.setChildren(false, [ {
|
||||
cells: [ 'Payment from Gazprom to Shell', '2', '3' ]
|
||||
data: [ 'Payment from Gazprom to Shell', '2', '3' ]
|
||||
} ]);
|
||||
$(TG.table).fixedHeaderTable({});
|
||||
}, 1000);
|
||||
|
|
Loading…
Reference in New Issue