/*! * D3 Mind Map * v1.0.0 * author Dustdusk */ var d3 = require('d3'); (function(window,angular, d3){ 'use strict'; angular.module('material.components.mindmap', []) .provider('$mdMindMap', function(){ var loadMindMap = function(options){ var m = options.margin||[8, 16, 12, 16], //margin i = 0, body_id = options.body_id||'mind-map', root, _max_x = [0, 0], _max_y = [0, 0], _node_width = options.node_width||150, _node_height = options.node_height||24, _link_size = options.link_size||75; var selectNode = function(target){ if(target){ var sel = d3.selectAll('#'+ body_id + ' svg .node').filter(function(d){return d.id==target.id})[0][0]; if(sel){ select(sel); } } }; var select = function(node){ // Find previously selected, unselect d3.select(".selected").classed("selected", false); // Select current item d3.select(node).classed("selected", true); }; //node click var handleClick = function(d, index){ select(this); if(d._children === undefined && d.children === undefined){ if(typeof options.node.click === 'function'){ options.node.click(d, index, function(node_data){ if(!node_data) return; if(!Array.isArray(node_data)) node_data = [node_data]; if(node_data.length == 0) return; d.children = []; for(var i = 0; i < node_data.length;i++){ var _new_node = d3.hierarchy(node_data[i], function(d){ return d[options.node.children]; }); var _max_height = 0; d.ancestors().forEach(function(sd){ if(sd.height == 0){ _max_height = sd.height + _new_node.height+1; sd.height = _max_height; } else if(sd.height <= _max_height){ sd.height = _max_height; } _max_height +=1; }); _new_node.descendants().forEach(function(sd){ sd.depth = d.depth + 1 + sd.depth; if(!sd.parent) sd.parent = d; //sd.id = Date.now(); }); d.children.push(_new_node); } update(d); }); } } else { toggle(d); update(d); } }; var tree= d3.tree().nodeSize([_node_height+8, _node_width + _link_size]); // celc link path var connector = function (d, i){ var source = {x : d.source.x, y : d.source.y + _node_width}; var target = {x : d.target.x, y : d.target.y}; var hy = (target.y-source.y)/2; return "M" + source.y + "," + source.x + "H" + (source.y+hy) + "V" + target.x + "H" + target.y; }; //建立基本的SVG var _body = d3.select('#'+ body_id); var _vis = _body.html('').append('svg:svg'); var vis = _vis.append("svg:g") .attr("transform", "translate(" + (0+m[3]) + "," + (_vis.node().getBoundingClientRect().height/2+m[0]) + ")") //設定起始點 ; var toArray = function(item, arr){ if(arr == undefined){ _max_x = [0, 0]; _max_y = [0, 0]; arr = []; } var i = 0, l = item.children?item.children.length:0; arr.push(item); item.y = 1 * item.y; //_max_depth = _max_depth < item.depth?item.depth:_max_depth; if(item.x < 0 && item.x < _max_x[0]){ _max_x[0] = item.x; } else if(item.x > 0 && item.x > _max_x[1]){ _max_x[1] = item.x; } if(item.y < 0 && item.y < _max_y[0]){ _max_y[0] = item.y; } else if(item.y > 0 && item.y > _max_y[1]){ _max_y[1] = item.y; } for(; i < l; i++){ toArray(item.children[i], arr); } //if() return arr; }; // Toggle node children. function toggle(d) { if (d.children) { d._children = d.children; d.children = null; } else { d.children = d._children; d._children = null; } } // load JSON data var loadJSON = function(json){ if(json){ root = json; root = d3.hierarchy(root, function(d){ return d[options.node.children]; }); root.x0 = 0; root.y0 = 0; //update tree update(root, true); } }; // update node // Source : 資料來源 // slow : 展示速度 function update(source, slow) { var duration = (d3.event && d3.event.altKey) || slow ? 600 : 200; // Compute the new tree layout. // all nodes var _treeData = tree(root); var nodes = _treeData.descendants(), _links = _treeData.descendants().slice(1); //nodes.forEach(function(d){ d.y = d.depth * 180}); toArray(nodes[0]); var _height = _max_x[1] - _max_x[0] + _node_height; var _with = _max_y[1] - _max_y[0] + _node_width; //tree.size([h, _with]); _vis.transition() .duration(duration) .attr("width", _with + m[1] + m[3]) .attr("height", _height + m[0] + m[2]); vis.transition() .duration(duration) .attr("transform", "translate(" + (0+m[3]) + "," + (_node_height+m[0]-_max_x[0]) + ")"); // Update the nodes… var node = vis.selectAll("g.node") .data(nodes, function(d) { return d.id || (d.id = ++i); }); // Enter any new nodes at the parent's previous position. var nodeEnter = node.enter().append("svg:g") .attr("class", function(d){ return d.selected?"node selected":"node"; }) .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; }) .on("click", handleClick); //節點 nodeEnter.append("svg:rect") .attr("dx", "0") .attr("y", 0-(_node_height/2)) .attr("rx", "8") .attr("ry", "8" ) .attr("width", _node_width) .attr("height", _node_height); //.append("svg:circle").attr("r", 1e-6); //.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }); //節點文字 nodeEnter.append("svg:text") .attr("dx", 8) .attr("dy", 3) //文字下移 .attr("text-anchor", "left") //靠左 .text(function(d) { if(typeof options.node.label === 'function') return options.node.label(d.data); else return d.data[options.node.label]; }) .style("fill-opacity", 1); // Transition nodes to their new position. var nodeUpdate = nodeEnter.merge(node); nodeUpdate.transition() .duration(duration) .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; }); //nodeUpdate.select("text").text(function(d) { return (d.data.name); }); //nodeUpdate.select("rect"); // Transition exiting nodes to the parent's new position. var nodeExit = node.exit().transition() .duration(duration) .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; }) .remove(); nodeExit.select("rect"); //.attr("r", 1e-6); nodeExit.select("text") .style("fill-opacity", 1e-6); // Update the links… var link = vis.selectAll("path.link") .data(_links, function(d) { return d.id; }); // Enter any new links at the parent's previous position. var linkEnter = link.enter().insert("svg:path", "g") .attr("class", "link") .attr("d", function(d) { var o = {x: source.x0, y: source.y0}; return connector({source: o, target: o}); }); var linkUpdate = linkEnter.merge(link); // Transition links to their new position. linkUpdate.transition() .duration(duration) .attr("d", function(d) { return connector({source: d, target: d.parent}); }); // Transition exiting nodes to the parent's new position. var linkExit = link.exit().transition() .duration(duration) .attr("d", function(d) { var o = {x: source.x, y: source.y}; return connector({source: o, target: o}); }) .remove(); // Stash the old positions for transition. nodes.forEach(function(d) { d.x0 = d.x; d.y0 = d.y; }); } loadJSON(options.data); } this.$get = ['$rootScope', '$timeout', function($rootScope, $timeout){ return { load:loadMindMap }; }]; }); })(window, window.angular, d3);