zui
Version:
一个基于 Bootstrap 深度定制开源前端实践方案,帮助你快速构建现代跨屏应用。
409 lines (364 loc) • 15.2 kB
JavaScript
/* ========================================================================
* ZUI: treemap.js
* http://zui.sexy
* ========================================================================
* Copyright (c) 2014-2016 cnezsoft.com; Licensed MIT
* ======================================================================== */
// Tree map data format
// {
// text: main text,
// html: main text as html format
// style: node style,
// textColor: text color,
// color: background color
// border: border style,
// cableWidth: 2,
// cableColor: '#808080'
// cableStyle: 'solid'
// }
(function($, window, document, Math, undefined) {
'use strict';
var NAME = 'zui.treemap',
DEFAULTS = {
data: [],
// direction: 'bottom', // or 'top', 'left', 'right'
cableWidth: 1,
cableColor: '#808080',
cableStyle: 'solid',
rowSpace: 30,
nodeSpace: 20,
listenNodeResize: true,
nodeTemplate: '<div class="treemap-node"><a class="treemap-node-wrapper"></a></div>',
foldable: true,
clickNodeToFold: true,
// sort: false, // Boolean or function
// tooltip: null,
// nodeStyle: null,
};
// var DEFAULT_NODE = {
// id: uuid(), // uuid
// text: '', // main text,
// html: '', // main text as html format
// style: null, // node element style
// textColor: '', // text color
// color: '', // background color
// border: '', // border style,
// tooltip: '' // node caption
// attrs: null // attrs
// title: '' // node title
// tooltip: '' // node tooltip
// };
var getDataFromUlList = function($list) {
return $list.children('li,.treemap-data-item').map(function() {
var $item = $(this),
item = $item.data(),
$text = $item.children('.text'),
$html = $item.children('.content'),
$children = $item.children('ul,.treemap-data-list');
if($text.length) item.text = $text.text();
if($html.length) item.html = $html.html();
if($children.length) {
item.children = getDataFromUlList($children);
}
if(!item.text && !item.html) {
var $content = $item.children(':not(ul,.treemap-data-list)');
var $itemClone = $item.clone();
$itemClone.find('ul,.treemap-data-list').remove();
if(!$content.length) {
item.text = $itemClone.text();
} else {
item.html = $itemClone.html();
}
}
return item;
}).get();
};
var Treemap = function(element, options) {
var $element = $(element);
if($.isArray(options)) {
options = {data: options};
}
options = $.extend({}, DEFAULTS, $element.data(), options);
var data = options.data || [];
if(!data.length) {
var $dataList = $element.children('.treemap-data');
if($dataList.length) {
data = getDataFromUlList($dataList.hide());
}
}
var $nodes = $element.children('.treemap-nodes');
if(!$nodes.length) {
$nodes = $('<div class="treemap-nodes" unselectable="on"/>').appendTo($element);
}
var that = this;
that.$ = $element;
that.$nodes = $nodes;
that.data = $.isArray(data) ? data : [data];
that.options = options;
that.offsetX = 0;
that.offsetY = 0;
that.scale = options.scale || 1;
// Bind events
that.render();
$nodes.on('resize', '.treemap-node-wrapper', function() {
that.delayDrawLines();
});
if(options.foldable) {
$nodes.on('click', options.clickNodeToFold ? '.treemap-node-wrapper' : '.treemap-node-fold-icon', function() {
that.toggle($(this).closest('.treemap-node'));
});
}
$nodes.on('click', '.treemap-node-wrapper', function() {
var $node = $(this).closest('.treemap-node');
that.callEvent('onNodeClick', $node.data('node'));
});
};
Treemap.prototype.toggle = function($node, toggle, ignoreAnimation) {
var that = this;
if(typeof $node === 'boolean') {
toggle = $node;
$node = null;
}
if(!$node) {
$node = that.$nodes.children('.treemap-node').first();
}
if($node)
{
if($node.data('node').foldable === false) {
return;
}
if(toggle === undefined) {
toggle = $node.hasClass('collapsed');
}
$node.toggleClass('collapsed', !toggle).find('[data-toggle="tooltip"]').tooltip('hide');
if (!ignoreAnimation) {
$node.addClass('tree-node-collapsing')
}
that.$nodes.find('.tooltip').remove();
that.drawLines();
if (!ignoreAnimation) {
$node.removeClass('tree-node-collapsing');
} else {
clearTimeout(that.toggleTimeTask);
that.toggleTimeTask = setTimeout(function() {
$node.removeClass('tree-node-collapsing');
}, 200);
}
}
};
Treemap.prototype.showLevel = function(level) {
var that = this;
that.$nodes.find('.treemap-node').each(function() {
var $node = $(this);
that.toggle($node, $node.data('level') < level, true);
});
};
Treemap.prototype.render = function(data) {
var that = this;
that.data = data ? ($.isArray(data) ? data : [data]) : that.data;
if(that.data) {
that.createNodes();
that.drawLines();
that.delayDrawLines(500);
}
that.callEvent('afterRender');
};
Treemap.prototype.createNodes = function(nodes, parent, callback) {
var that = this,
options = that.options,
rowSpace = options.rowSpace,
$nodes = that.$nodes;
if(!parent) {
$nodes.find('.treemap-node-wrapper').off('resize.' + NAME);
$nodes.empty();
}
if(options.sort) {
nodes.sort($.isFunction(options.sort) ? options.sort : function(nodeX, nodeY) {
return (nodeX.order || 0) - (nodeY.order);
});
}
var lastNode = null;
nodes = nodes || that.data;
if (!parent) {
that.maxLevel = 1;
}
$.each(nodes, function(idx, node) {
if(typeof node === 'string') {
node = {html: node};
nodes[idx] = node;
}
if(!node.id) node.id = $.zui.uuid();
node.level = parent ? (parent.level + 1) : 1;
that.maxLevel = Math.max(that.maxLevel, node.level);
// Create node element
var isCustomNodeTemplate = $.isFunction(options.nodeTemplate);
var $node = isCustomNodeTemplate ? options.nodeTemplate(node, that) : $(options.nodeTemplate);
// Create node wrapper element
var $wrapper = $node.find('.treemap-node-wrapper');
if(!$wrapper.length) {
$wrapper = $('<div class="treemap-node-wrapper"/>').appendTo($node);
}
var children = node.children;
var hasChild = children && children.length;
node.isOnlyOneChild = hasChild === 1;
// Set node data attributes
node.idx = idx;
var row = parent ? (parent.row + 1) : 0;
$node.toggleClass('treemap-node-has-child', !!hasChild)
.toggleClass('treemap-node-has-parent', !!parent)
.toggleClass('treemap-node-one-child', hasChild === 1)
.toggleClass('collapsed', !!node.collapsed && node.collapsed !== 'false')
.toggleClass('treemap-node-root', !row)
.attr({'data-id': node.id, 'data-level': node.level}).data('node', node);
if(node.className) {
$node.addClass(node.className);
}
node.row = row;
// Set node element attributes and sytle
var style = $.extend({}, options.nodeStyle, node.style);
if(node.textColor) style.color = node.textColor;
if(node.color) style.backgroundColor = node.color;
if(node.border) style.border = node.border;
var attrs = $.extend({}, node.attrs, {
title: node.caption
});
if(node.tooltip) {
attrs['data-toggle'] = 'tooltip';
attrs.title = node.tooltip;
}
$wrapper.attr(attrs).css(style);
if(lastNode) {
$node.css('padding-left', options.nodeSpace);
}
if(!isCustomNodeTemplate) {
if(node.html) $wrapper.append(node.html);
else if(node.text) $wrapper.text(node.text);
}
// append node element to ducument
$node.appendTo(parent ? parent.$children : $nodes);
// Save sizes
// node.bounds = {
// width : $wrapper.outerWidth(),
// height : $wrapper.outerHeight()
// };
if(lastNode) {
lastNode.next = node;
}
node.prev = lastNode;
node.parent = parent;
node.$ = $node;
node.$wrapper = $wrapper;
// Create children
if(hasChild) {
var $children = $node.find('.treemap-node-children');
if(!$children.length) {
$children = $('<div class="treemap-node-children"/>').appendTo($node);
}
$children.css('margin-top', rowSpace);
node.$children = $children;
that.createNodes(children, node);
}
if(options.listenNodeResize) {
$wrapper.on('resize.' + NAME, function() {
// node.bounds.width = $wrapper.outerWidth();
// node.bounds.height = $wrapper.outerHeight();
that.delayDrawLines();
});
}
lastNode = node;
callback && callback($node, node);
});
if(!parent) {
// Init tooltip
$nodes.find('[data-toggle="tooltip"]').tooltip(options.tooltip);
}
};
Treemap.prototype.delayDrawLines = function(delay) {
var that = this;
clearTimeout(that.delayDrawLinesTask);
that.delayDrawLinesTask = setTimeout(function() {
that.drawLines();
}, delay || 10);
};
Treemap.prototype.drawLines = function(nodes, parent) {
var that = this,
options = that.options,
rowSpace = options.rowSpace;
var cableStyle = {};
if(options.cableWidth) cableStyle.borderWidth = options.cableWidth;
if(options.cableStyle) cableStyle.borderStyle = options.cableStyle;
if(options.cableColor) cableStyle.borderColor = options.cableColor;
var rowSpaceHalf = Math.round(rowSpace/2);
var nodesOffsetLeft = that.$nodes.offset().left;
$.each(nodes || that.data, function(idx, node) {
var $wrapper = node.$wrapper;
var children = node.children;
var nodeCableStyle = $.extend({
height: rowSpaceHalf,
top: -rowSpaceHalf - 1,
left: Math.round(($wrapper.outerWidth() - cableStyle.borderWidth)/2),
color: cableStyle.borderColor
}, cableStyle);
if(parent && !parent.isOnlyOneChild) {
var $topLine = $wrapper.find('.treemap-line-top');
if(!$topLine.length) {
$topLine = $('<div class="treemap-line-top"/>').appendTo($wrapper);
}
$topLine.css(nodeCableStyle);
}
if(children && children.length) {
nodeCableStyle.top = $wrapper.outerHeight() - 1;
if(node.isOnlyOneChild) {
nodeCableStyle.height = rowSpace;
}
var $bottomLine = $wrapper.find('.treemap-line-bottom');
if(!$bottomLine.length) {
$bottomLine = $('<div class="treemap-line-bottom"/>').appendTo($wrapper);
if(options.foldable) {
$bottomLine.append('<i class="treemap-node-fold-icon icon" style="transform: translate(-' + Math.floor(nodeCableStyle.borderWidth/2) + 'px, ' + rowSpaceHalf + 'px)"/>');
}
}
$bottomLine.css(nodeCableStyle);
that.drawLines(children, node);
if(children.length > 1) {
var firstChild = children[0],
lastChild = children[children.length - 1];
var $centerLine = node.$.children('.treemap-line');
if(!$centerLine.length) {
$centerLine = $('<div class="treemap-line"/>').insertAfter($wrapper);
}
var lineLeft = Math.round(firstChild.$wrapper.offset().left - nodesOffsetLeft + firstChild.$wrapper.outerWidth()/2);
$centerLine.css($.extend({
marginTop: rowSpaceHalf,
left: lineLeft,
width: lastChild.$wrapper.offset().left - nodesOffsetLeft -lineLeft + lastChild.$wrapper.outerWidth()/2
}, cableStyle));
}
}
});
if(!parent) {
that.callEvent('afterDrawLines');
}
};
// Call event
Treemap.prototype.callEvent = function(name, params) {
var that = this;
if(!$.isArray(params)) params = [params];
that.$.trigger(name, params);
if($.isFunction(that.options[name])) {
return that.options[name].apply(that, params);
}
};
Treemap.DEFAULTS = DEFAULTS;
Treemap.NAME = NAME;
$.fn.treemap = function(option, param1, parma2) {
return this.each(function() {
var $this = $(this);
var data = $this.data(NAME);
var options = typeof option == 'object' && option;
if(!data) $this.data(NAME, (data = new Treemap(this, options)));
if(typeof option == 'string') data[option](param1, parma2);
});
};
$.fn.treemap.Constructor = Treemap;
}(jQuery, window, document, Math, undefined));