add simple js treegrid implementation

rel-1.0
Vitaliy Filippov 2016-02-20 13:31:29 +03:00
parent 1ca49f6bf5
commit b3476556ed
4 changed files with 308 additions and 0 deletions

83
treegrid.css Normal file
View File

@ -0,0 +1,83 @@
/* some common clear */
html, body { font-size: 100%; font-family: sans-serif; width: 100%; height: 100%; padding: 0; margin: 0; }
select, input, textarea { box-sizing: border-box; font-size: 100%; }
table, td, th { vertical-align: top; font-size: 100%; }
img { vertical-align: middle; }
body { -moz-text-size-adjust: none; } /* do not scale fonts in mobile firefox */
table { border-collapse: collapse; }
/* scroll div styles */
.scroller
{
float: left;
position: relative;
height: 100%;
width: 100%;
}
.scroller > .inner
{
position: absolute;
overflow: hidden;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
/* fixed header table styles */
table.grid tbody tr:hover, table.grid tbody tr:hover td
{
background-color: #e2eff8;
}
table.grid tr.selected
{
background-color: #c1ddf1;
}
table.grid thead th
{
padding: 6px;
}
table.grid th
{
font-weight: normal;
color: #405060;
}
table.grid
{
width: auto;
}
table.grid td, table.grid th
{
border: 1px solid #ccc;
background-clip: padding-box; /* O_o firefox draws background over borders */
padding: 3px;
font-size: 13px;
position: relative;
overflow: visible;
}
/* treegrid styles */
table.grid .collapser
{
width: 16px;
height: 16px;
float: left;
margin: 0 4px 0 0;
cursor: pointer;
}
table.grid .collapser-collapsed
{
background: url(unfold2.png);
}
table.grid .collapser-expanded
{
background: url(fold2.png);
}

174
treegrid.js Normal file
View File

@ -0,0 +1,174 @@
/**
* 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,
* collapsed: true/false
* }, ... ]
*
* 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([]);
}
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;
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';
this.tr._level = this.level;
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;
}
c0.style.paddingLeft = (grid._initialPadding + grid.levelIndent * level) + 'px';
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;
while (c && c._level > this.level)
{
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)
{
if (!this.tr)
this.grid.tbody.innerHTML = '';
else
while (this.tr.nextSibling && this.tr.nextSibling._level > this.level)
this.grid.tbody.removeChild(this.tr.nextSibling);
this.leaf = isLeaf;
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';
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));
}

46
treegridtest.htm Normal file
View File

@ -0,0 +1,46 @@
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="treegrid.css" />
<link rel="stylesheet" type="text/css" href="../tplext/fht/defaultTheme.css">
<script type="text/javascript" src="../tplext/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="../tplext/fht/jquery.fixedheadertable.js"></script>
<script type="text/javascript" src="util.js"></script>
<script type="text/javascript" src="treegrid.js"></script>
</head>
<body>
<div class="scroller"><div class="inner" id="innerdiv">
</div></div>
<script>
<!--
onDomReady(function() {
var i = {
cells: [ 'Payment from Gazprom to Shell 2', '4', '5' ]
};
var TG = new TreeGrid([ {
cells: [ 'Payment from Gazprom to Shell', '2', '3' ]
} ], [ '', '15 фев', '16 фев', '17 фев', '18 фев', '19 фев', '20 фев', '21 фев' ]);
TG.table.className = 'grid';
TG.onExpand = function(node)
{
// Dynamic loading example
if (!node.children.length)
node.setChildren(false, [ i ]);
return true;
// Another example with delay (emulation of server interaction)
setTimeout(function() {
if (!node.children.length)
node.setChildren(false, [ i ]);
node.toggle();
}, 100);
return false;
};
document.getElementById('innerdiv').appendChild(TG.table);
$(TG.table).fixedHeaderTable({});
});
//-->
</script>
</body>
</html>

View File

@ -278,3 +278,8 @@ window.unmg = function(id, str)
str = decodeURIComponent(str.replace(/([\x20-\x24\x26-\x79])|(%..)/g, function(m, m1, m2) { return m2 || ('%'+(159-m1.charCodeAt(0)).toString(16)); }));
document.getElementById(id).innerHTML = '<a href="ma'+'ilt'+'o:'+str+'">'+str+'</a>';
};
window.curStyle = function(e)
{
return window.getComputedStyle ? getComputedStyle(e) : e.currentStyle;
};