Use renderer and fast rectangle selection

rel-1.0
Vitaliy Filippov 2016-06-05 01:17:51 +03:00
parent 07319d4a5b
commit ede717a8f1
3 changed files with 118 additions and 48 deletions

View File

@ -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
{

View File

@ -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 = 0; i < this.grid.header.length; i++)
this.grid._setProps(this.tr.cells[i], this.cells[i]);
this.addCollapser();
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._renderCell(i, cells[i]);
this._oldCells = cells;
}
}
TreeGridNode.prototype._getToggleHandler = function()

View File

@ -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);