treegrid/treegrid.js

179 lines
5.6 KiB
JavaScript
Raw Normal View History

2016-02-20 13:31:29 +03:00
/**
* Very simple and fast tree grid/table, compatible with dynamic loading and jQuery fixedHeaderTable
* License: MPL 2.0+, (c) Vitaliy Filippov 2016
*/
/**
* USAGE:
*
* var TG = new TreeGrid(items, header);
* document.body.appendChild(TG.table);
*
* items: [ node={
* cells: [ 'html' or { innerHTML, style, className, title }, ... ],
* children: [ node... ],
* leaf: true/false,
2016-02-24 17:08:00 +03:00
* collapsed: true/false,
* data: <user data>
2016-02-20 13:31:29 +03:00
* }, ... ]
*
* header: [ 'html' or { innerHTML, style, className, title }, ... ]
*/
function TreeGrid(items, header)
{
this.bindProps = { 'style': 1, 'className': 1, 'title': 1, 'innerHTML': 1 };
this.levelIndent = 20;
this.table = document.createElement('table');
this.table.className = 'grid';
this.thead = document.createElement('thead');
this.tbody = document.createElement('tbody');
this.table.appendChild(this.thead);
this.table.appendChild(this.tbody);
this.thead.appendChild(document.createElement('tr'));
this.setHeader(header);
new TreeGridNode({ children: items }, this);
}
TreeGrid.prototype.setHeader = function(newHeader)
{
var tr = this.thead.rows[0];
tr.innerHTML = '';
for (var i = 0; i < newHeader.length; i++)
{
var th = document.createElement('th');
this._setProps(th, newHeader[i]);
tr.appendChild(th);
}
this.header = newHeader;
// header change clears the whole grid by now
if (this.root)
this.root.setChildren(false, []);
2016-02-20 13:31:29 +03:00
}
TreeGrid.prototype.onExpand = function(node)
{
// handle node expand/collapse here
return true;
}
TreeGrid.prototype._setProps = function(el, props)
{
if (typeof props == 'string')
el.innerHTML = props;
else
for (var j in this.bindProps)
if (props[j])
el[j] = props[j];
}
function TreeGridNode(node, grid, level, insertBefore, startHidden)
{
this.grid = grid;
this.level = level;
2016-02-24 17:08:00 +03:00
this.data = node.data;
2016-02-20 13:31:29 +03:00
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.cells = node.cells || [];
this.tr = document.createElement('tr');
if (startHidden)
this.tr.style.display = 'none';
2016-02-24 18:48:09 +03:00
this.tr._node = this;
2016-02-20 13:31:29 +03:00
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);
}
var collapser = document.createElement('div');
if (this.leaf)
collapser.className = 'collapser collapser-inactive';
else
{
collapser.className = this.collapsed ? 'collapser collapser-collapsed' : 'collapser collapser-expanded';
var self = this;
addListener(collapser, 'click', function(e) { self._toggleHandler(); });
}
var c0 = this.tr.cells[0];
c0.childNodes.length ? c0.insertBefore(collapser, c0.firstChild) : c0.appendChild(collapser);
if (grid._initialPadding === undefined)
{
var p0 = curStyle(c0).paddingLeft || '';
if (p0.substr(p0.length-2) == 'px')
p0 = parseInt(p0.substr(0, p0.length-2));
else
p0 = 0;
grid._initialPadding = p0;
}
2016-03-09 12:36:51 +03:00
c0.style.paddingLeft = (grid._initialPadding + grid.levelIndent * level + 20) + 'px';
2016-02-20 13:31:29 +03:00
insertBefore ? grid.tbody.insertBefore(this.tr, insertBefore) : grid.tbody.appendChild(this.tr);
}
this.children = [];
if (node.children)
for (var i = 0; i < node.children.length; i++)
this.children.push(new TreeGridNode(node.children[i], grid, this.level+1, insertBefore, this.collapsed));
}
TreeGridNode.prototype._toggleHandler = function()
{
if (!this.grid.onExpand(this))
return;
this.toggle();
}
TreeGridNode.prototype.toggle = function()
{
if (this.leaf)
return;
this.collapsed = !this.collapsed;
this.tr.cells[0].firstChild.className = this.collapsed ? 'collapser collapser-collapsed' : 'collapser collapser-expanded';
if (this.collapsed)
{
// collapse all children
var c = this.tr.nextSibling;
2016-02-24 18:48:09 +03:00
while (c && c._node.level > this.level)
2016-02-20 13:31:29 +03:00
{
c.style.display = 'none';
c = c.nextSibling;
}
}
else
{
// expand all children except children of collapsed subnodes
var st = this.children.concat([]);
while (st.length)
{
var e = st.pop();
e.tr.style.display = '';
if (!e.collapsed)
st = st.concat(e.children);
}
}
}
TreeGridNode.prototype.setChildren = function(isLeaf, newChildren)
{
this.leaf = isLeaf;
2016-02-20 13:31:29 +03:00
if (!this.tr)
this.grid.tbody.innerHTML = '';
else
{
2016-02-24 18:48:09 +03:00
while (this.tr.nextSibling && this.tr.nextSibling._node.level > this.level)
2016-02-20 13:31:29 +03:00
this.grid.tbody.removeChild(this.tr.nextSibling);
if (this.leaf)
this.tr.cells[0].firstChild.className = 'collapser collapser-inactive';
else
this.tr.cells[0].firstChild.className = this.collapsed ? 'collapser collapser-collapsed' : 'collapser collapser-expanded';
}
2016-02-20 13:31:29 +03:00
this.children = [];
var insertBefore = this.tr && this.tr.nextSibling;
for (var i = 0; i < newChildren.length; i++)
this.children.push(new TreeGridNode(newChildren[i], this.grid, this.level+1, insertBefore, this.collapsed));
}