0

I have an array of elements which I want to place around a central element. Think of a graph where every node is connected to a central element. How do I achieve this in LWC? I haven't come across any custom LWC element yet that lets me visualize data as mentioned. If this can't be achieved natively, does any one know of any third party library that helps do the same? Please see the attached picture to better understand what I mean.

enter image description here

1
  • I don't know if there is a component that satisfies your needs, but you can take a look at this library, and its network graph type: d3-graph-gallery.com/graph/network_basic.html I have never used it, but looks like you only need to import a js file, and that's it. hope it helps.
    – McCubo
    Commented Jun 1, 2022 at 7:31

1 Answer 1

2

Agreed with McCubo's comment, D3.js seems right. Salesforce has documentation on loading 3rd party libraries which D3.js is in the example: https://developer.salesforce.com/docs/component-library/documentation/en/lwc/js_third_party_library

Below is just the HTML & JS in their examples but is how I'd start. Once I got the example running, I'd take out unneeded code, like dragged, etc if users don't need to move nodes, then tie in my data instead.

Hope it helps.

libsD3.html

<template>
    <div class="slds-m-around_medium">
        <svg
            class="d3"
            width={svgWidth}
            height={svgHeight}
            lwc:dom="manual"
        ></svg>
    </div>
</template>

libsD3.js

/* global d3 */
import { LightningElement } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { loadScript, loadStyle } from 'lightning/platformResourceLoader';
import D3 from '@salesforce/resourceUrl/d3';
import DATA from './data';

export default class LibsD3 extends LightningElement {
    svgWidth = 400;
    svgHeight = 400;

    d3Initialized = false;

    renderedCallback() {
        if (this.d3Initialized) {
            return;
        }
        this.d3Initialized = true;

        Promise.all([
            loadScript(this, D3 + '/d3.v5.min.js'),
            loadStyle(this, D3 + '/style.css')
        ])
            .then(() => {
                this.initializeD3();
            })
            .catch(error => {
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error loading D3',
                        message: error.message,
                        variant: 'error'
                    })
                );
            });
    }

    initializeD3() {
        // Example adopted from https://bl.ocks.org/mbostock/2675ff61ea5e063ede2b5d63c08020c7
        const svg = d3.select(this.template.querySelector('svg.d3'));
        const width = this.svgWidth;
        const height = this.svgHeight;
        const color = d3.scaleOrdinal(d3.schemeDark2);

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

        const link = svg
            .append('g')
            .attr('class', 'links')
            .selectAll('line')
            .data(DATA.links)
            .enter()
            .append('line')
            .attr('stroke-width', d => {
                return Math.sqrt(d.value);
            });

        const node = svg
            .append('g')
            .attr('class', 'nodes')
            .selectAll('circle')
            .data(DATA.nodes)
            .enter()
            .append('circle')
            .attr('r', 5)
            .attr('fill', d => {
                return color(d.group);
            })
            .call(
                d3
                    .drag()
                    .on('start', dragstarted)
                    .on('drag', dragged)
                    .on('end', dragended),
            );

        node.append('title').text(d => {
            return d.id;
        });

        simulation.nodes(DATA.nodes).on('tick', ticked);

        simulation.force('link').links(DATA.links);

        function ticked() {
            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);
        }

        function dragstarted(d) {
            if (!d3.event.active) {
                simulation.alphaTarget(0.3).restart();
            }
            d.fx = d.x;
            d.fy = d.y;
        }

        function dragged(d) {
            d.fx = d3.event.x;
            d.fy = d3.event.y;
        }

        function dragended(d) {
            if (!d3.event.active) {
                simulation.alphaTarget(0);
            }
            d.fx = null;
            d.fy = null;
        }
    }
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .