UNPKG

@daryl110/go-chart

Version:

library to easily build charts

214 lines (189 loc) 6.42 kB
const { appendChart, d3 } = require('../others/d3.utils'); /** * @memberOf D3Module * @function * @name collapsableTreeChart * @desc function for create a collapsable tree chart * @param {HTMLBodyElement} htmlElementContainer - container html element, where the chart is inserted * @param {string} idElement - chart id * @param {object} data - data to be plotted within the chart, with the structure: * <code>{ name: string, children: array[data] }</code> * @param {number=} [width=1050] - chart width inside the container * @param {number=} [height=300] - chart height inside the container * @param {Object=} [margin={ top: 10, right: 120, bottom: 10, left: 120 }] - view box container * @param {string=} [backgroundColor='white'] - background color for the chart * @see <img src="https://i.imgur.com/cOzfwr9.jpg"></img> * @example D3.collapsableTreeChart( * document.getElementById('charts'), * 'collapsable_tree_chart', * { * name: 'root', * children: [ * { * name: 'node_1', * children: [ * { * name: 'node_1.1', * children: [ * { * name: 'node_1.1.1' * }, * { * name: 'node_1.1.2' * }, * { * name: 'node_1.1.3' * } * ] * } * ] * }, * { * name: 'node_2', * children: [ * { * name: 'node_2.1', * children: [ * { * name: 'node_2.1.1' * }, * { * name: 'node_2.1.2' * }, * { * name: 'node_2.1.3', * children: [ * { * name: 'node_2.1.3.1', * children: [ * { * name: 'node_2.1.3.1.1' * }, * { * name: 'node_2.1.3.1.2' * }, * { * name: 'node_2.1.3.1.3' * } * ] * } * ] * } * ] * }, * { * name: 'node_2.2' * }, * { * name: 'node_2.3' * }, * ] * } * ] * } * ); */ module.exports = ( htmlElementContainer, idElement, data, width = 1050, height = 300, margin = { top: 10, right: 120, bottom: 10, left: 120 }, backgroundColor = 'white' ) => { const root = d3.hierarchy(data); const dx = 10, dy = 159; const diagonal = d3.linkHorizontal().x(d => d.y).y(d => d.x); const tree = d3.tree().nodeSize([dx, dy]); root.x0 = dy / 2; root.y0 = 0; root.descendants().forEach((d, i) => { d.id = i; d._children = d.children; if (d.depth && d.data.name.length !== 7) d.children = null; }); const svg = d3.create('svg'); svg.attr('height', height); svg.attr('width', width); svg.style('background-color', backgroundColor); const gLink = svg.append('g') .attr('fill', 'none') .attr('stroke', '#555') .attr('stroke-opacity', 0.4) .attr('stroke-width', 1.5); const gNode = svg.append('g') .attr('cursor', 'pointer') .attr('pointer-events', 'all'); const update = (source) => { const duration = d3.event && d3.event.altKey ? 2500 : 250; const nodes = root.descendants().reverse(); const links = root.links(); tree(root); let left = root; let right = root; root.eachBefore(node => { if (node.x < left.x) left = node; if (node.x > right.x) right = node; }); const height1 = right.x - left.x + margin.top + margin.bottom; const transition = svg.transition() .duration(duration) .attr('viewBox', [-margin.left, left.x - margin.top, width, height1]) .tween('resize', window.ResizeObserver ? null : () => svg.dispatch('toggle')); const node = gNode.selectAll('g') .data(nodes, d => d.id); const nodeEnter = node.enter().append('g') .attr('transform', () => `translate(${source.y0},${source.x0})`) .attr('fill-opacity', 0) .attr('stroke-opacity', 0) .on('click', d => { d.children = d.children ? null : d._children; update(d); }); nodeEnter.append('circle') .attr('r', 2.5) .attr('fill', d => d._children ? '#555' : '#999') .attr('stroke-width', 10); nodeEnter.append('text') .attr('dy', '0.31em') .attr('x', d => d._children ? -6 : 6) .attr('text-anchor', d => d._children ? 'end' : 'start') .text(d => d.data.name) .clone(true).lower() .attr('stroke-linejoin', 'round') .attr('stroke-width', 3) .attr('stroke', 'white'); node.merge(nodeEnter).transition(transition) .attr('transform', d => `translate(${d.y},${d.x})`) .attr('fill-opacity', 1) .attr('stroke-opacity', 1); node.exit().transition(transition).remove() .attr('transform', () => `translate(${source.y},${source.x})`) .attr('fill-opacity', 0) .attr('stroke-opacity', 0); const link = gLink.selectAll('path') .data(links, d => d.target.id); const linkEnter = link.enter().append('path') .attr('d', () => { const o = { x: source.x0, y: source.y0 }; return diagonal({ source: o, target: o }); }); link.merge(linkEnter).transition(transition) .attr('d', diagonal); link.exit().transition(transition).remove() .attr('d', () => { const o = { x: source.x, y: source.y }; return diagonal({ source: o, target: o }); }); root.eachBefore(d => { d.x0 = d.x; d.y0 = d.y; }); }; update(root); appendChart(svg, idElement, htmlElementContainer); }