UNPKG

tnt.tree

Version:
237 lines (196 loc) 7.21 kB
var apijs = require("tnt.api"); var tree = {}; tree.label = function () { "use strict"; var dispatch = d3.dispatch ("click", "dblclick", "mouseover", "mouseout") // TODO: Not sure if we should be removing by default prev labels // or it would be better to have a separate remove method called by the vis // on update // We also have the problem that we may be transitioning from // text to img labels and we need to remove the label of a different type var label = function (node, layout_type, node_size) { if (typeof (node) !== 'function') { throw(node); } label.display().call(this, node, layout_type) .attr("class", "tnt_tree_label") .attr("transform", function (d) { var t = label.transform()(node, layout_type); return "translate (" + (t.translate[0] + node_size) + " " + t.translate[1] + ")rotate(" + t.rotate + ")"; }) // TODO: this click event is probably never fired since there is an onclick event in the node g element? .on("click", function () { dispatch.click.call(this, node) }) .on("dblclick", function () { dispatch.dblclick.call(this, node) }) .on("mouseover", function () { dispatch.mouseover.call(this, node) }) .on("mouseout", function () { dispatch.mouseout.call(this, node) }) }; var api = apijs (label) .getset ('width', function () { throw "Need a width callback" }) .getset ('height', function () { throw "Need a height callback" }) .getset ('display', function () { throw "Need a display callback" }) .getset ('transform', function () { throw "Need a transform callback" }) //.getset ('on_click'); return d3.rebind (label, dispatch, "on"); }; // Text based labels tree.label.text = function () { var label = tree.label(); var api = apijs (label) .getset ('fontsize', 10) .getset ('fontweight', "normal") .getset ('color', "#000") .getset ('text', function (d) { return d.data().name; }) label.display (function (node, layout_type) { var l = d3.select(this) .append("text") .attr("text-anchor", function (d) { if (layout_type === "radial") { return (d.x%360 < 180) ? "start" : "end"; } return "start"; }) .text(function(){ return label.text()(node) }) .style('font-size', function () { return d3.functor(label.fontsize())(node) + "px"; }) .style('font-weight', function () { return d3.functor(label.fontweight())(node); }) .style('fill', d3.functor(label.color())(node)); return l; }); label.transform (function (node, layout_type) { var d = node.data(); var t = { translate : [5, 5], rotate : 0 }; if (layout_type === "radial") { t.translate[1] = t.translate[1] - (d.x%360 < 180 ? 0 : label.fontsize()) t.rotate = (d.x%360 < 180 ? 0 : 180) } return t; }); // label.transform (function (node) { // var d = node.data(); // return "translate(10 5)rotate(" + (d.x%360 < 180 ? 0 : 180) + ")"; // }); label.width (function (node) { var svg = d3.select("body") .append("svg") .attr("height", 0) .style('visibility', 'hidden'); var text = svg .append("text") .style('font-size', d3.functor(label.fontsize())(node) + "px") .text(label.text()(node)); var width = text.node().getBBox().width; svg.remove(); return width; }); label.height (function (node) { return d3.functor(label.fontsize())(node); }); return label; }; // Image based labels tree.label.img = function () { var label = tree.label(); var api = apijs (label) .getset ('src', function () {}) label.display (function (node, layout_type) { if (label.src()(node)) { var l = d3.select(this) .append("image") .attr("width", label.width()()) .attr("height", label.height()()) .attr("xlink:href", label.src()(node)); return l; } // fallback text in case the img is not found? return d3.select(this) .append("text") .text(""); }); label.transform (function (node, layout_type) { var d = node.data(); var t = { translate : [10, (-label.height()() / 2)], rotate : 0 }; if (layout_type === 'radial') { t.translate[0] = t.translate[0] + (d.x%360 < 180 ? 0 : label.width()()), t.translate[1] = t.translate[1] + (d.x%360 < 180 ? 0 : label.height()()), t.rotate = (d.x%360 < 180 ? 0 : 180) } return t; }); return label; }; // Labels made of 2+ simple labels tree.label.composite = function () { var labels = []; var label = function (node, layout_type, node_size) { var curr_xoffset = 0; for (var i=0; i<labels.length; i++) { var display = labels[i]; (function (offset) { display.transform (function (node, layout_type) { var tsuper = display._super_.transform()(node, layout_type); var t = { translate : [offset + tsuper.translate[0], tsuper.translate[1]], rotate : tsuper.rotate }; return t; }) })(curr_xoffset); curr_xoffset += 10; curr_xoffset += display.width()(node); display.call(this, node, layout_type, node_size); } }; var api = apijs (label) api.method ('add_label', function (display, node) { display._super_ = {}; apijs (display._super_) .get ('transform', display.transform()); labels.push(display); return label; }); api.method ('width', function () { return function (node) { var tot_width = 0; for (var i=0; i<labels.length; i++) { tot_width += parseInt(labels[i].width()(node)); tot_width += parseInt(labels[i]._super_.transform()(node).translate[0]); } return tot_width; } }); api.method ('height', function () { return function (node) { var max_height = 0; for (var i=0; i<labels.length; i++) { var curr_height = labels[i].height()(node); if ( curr_height > max_height) { max_height = curr_height; } } return max_height; } }); return label; }; module.exports = exports = tree.label;