Source

modules/d3/charts/graph.chart.js

const {
    d3,
    drag,
    color,
    addTitleNode,
    addTitleArch,
    addArchFunctions,
    addNodeFunctions,
    appendChart
} = require('../others/d3.graphCharts.utils');

/**
 * @memberOf D3Module
 * @function
 * @name graphChart
 * @desc function for create a graph 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>{
 *     nodes: [{ id: string, group: number }],
 *     arches: [{ source: string, target: string, value: object, ... }]
 * }</code>
 * @param {array=} [nodeFunctions=[]] - functions of each node within the chart, with the structure:
 * <code>nodeFunctions: [{ event: string // event type, handler: function(node?) // action to take }]</code>
 * @param {array=} [archesFunctions=[]] - functions of each arch within the chart, with the structure:
 * <code>archesFunctions: [{ event: string // event type, handler: function(arch?) // action to take }]</code>
 * @param {number=} [width=500] - chart width inside the container
 * @param {number=} [height=500] - chart height inside the container
 * @param {string=} [backgroundColor='white'] - background color for the chart
 * @see <img src="https://i.imgur.com/byHuczW.jpg"></img>
 * @example D3.graphChart(
 *   document.getElementById('charts'),
 *   'graph_chart',
 *   {
 *      nodes: [
 *        { id: 'Armenia', group: 1 },
 *        { id: 'Calarca', group: 1 },
 *        { id: 'Circasia', group: 1 },
 *        { id: 'Pereira', group: 2 },
 *        { id: 'Cali', group: 3 },
 *        { id: 'Bogota', group: 4 },
 *        { id: 'Soacha', group: 4 },
 *        { id: 'Medellin', group: 5 },
 *        { id: 'Itagui', group: 5 },
 *        { id: 'Envigado', group: 5 }
 *      ],
 *      arches: [
 *        { source: 'Armenia', target: 'Calarca', value: 5 },
 *        { source: 'Armenia', target: 'Circasia', value: 5 },
 *        { source: 'Cali', target: 'Medellin', value: 5 },
 *        { source: 'Medellin', target: 'Itagui', value: 5 },
 *        { source: 'Armenia', target: 'Medellin', value: 5 },
 *        { source: 'Cali', target: 'Bogota', value: 5 },
 *        { source: 'Bogota', target: 'Soacha', value: 5 },
 *        { source: 'Armenia', target: 'Bogota', value: 5 },
 *        { source: 'Medellin', target: 'Envigado', value: 5 },
 *        { source: 'Armenia', target: 'Pereira', value: 5 }
 *      ]
 *    }
 * );
 */
module.exports = (
    htmlElementContainer,
    idElement,
    data,
    nodeFunctions = [],
    archesFunctions = [],
    width = 500,
    height = 500,
    backgroundColor = 'white'
) => {
    const svg = d3.create('svg')
        .attr('height', height)
        .attr('width', width)
        .style('background-color', backgroundColor);

    const links = data.arches.map(d => Object.create(d));
    const nodes = data.nodes.map(d => Object.create(d));

    const simulation = d3.forceSimulation(nodes)
        .force('link', d3.forceLink(links).id(d => d.id))
        .force('charge', d3.forceManyBody())
        .force('center', d3.forceCenter(width / 2, height / 2));

    const link = svg.append('g')
        .attr('stroke', '#999')
        .attr('stroke-opacity', 0.6)
        .selectAll('line')
        .data(links)
        .join('line')
        .attr('stroke-width', d => Math.sqrt(d.value));

    const node = svg.append('g')
        .attr('stroke', '#fff')
        .attr('stroke-width', 1.5)
        .selectAll('circle')
        .data(nodes)
        .join('circle')
        .attr('r', 5)
        .attr('fill', color())
        .call(drag(simulation));

    addTitleArch(link, data);
    addTitleNode(node);
    addNodeFunctions(nodeFunctions, node, data);
    addArchFunctions(archesFunctions, link, data);

    simulation.on('tick', () => {
        link.attr('x1', d => d.source.x)
            .attr('y1', d => d.source.y)
            .attr('x2', d => d.target.x)
            .attr('y2', d => d.target.y);

        node.attr('cx', d => d.x)
            .attr('cy', d => d.y);
    });

    appendChart(svg, idElement, htmlElementContainer);
};