Search code examples
javascriptd3.jspolymerpolymer-3.x

How to import the d3.js v5 module into a polymer 3 element?


I have created polymer 1.x custom elements using d3 v3 before. I want to update them to polymer 3 and d3 v5.

Here is a base polymer 3 element that I want to include d3 v5 into:

import {html, PolymerElement} from '@polymer/polymer/polymer-element.js';

/**
 * `foo-bar`
 * 
 *
 * @customElement
 * @polymer
 * @demo demo/index.html
 */
class FooBar extends PolymerElement {
  static get template() {
    return html`
      <style>
        :host {
          display: block;
        }
      </style>
      <h2>Hello [[prop1]]!</h2>
    `;
  }
  static get properties() {
    return {
      prop1: {
        type: String,
        value: 'foo-bar',
      },
    };
  }
  constructor() {
    super();
  }

  ready() {
    super.ready();
    console.log('foo-bar is ready!');
  }

}

window.customElements.define('foo-bar', FooBar);

I call npm install d3

How shall I import d3 into this PolymerElement? I have two different types of polymer elements using d3. I have done both force and hierarchy polymer elements.

I am figuring that I need to do something in the constructor() or ready() functions within the polymer element to utilize the d3 library.

I was trying the following import:

import 'd3/dist/d3.js';

Solution

  • Since d3 has ship with ES Modules. So you can import as you want.

    import { select, scaleOrdinal } from 'd3'
    // or
    import * as d3 from 'd3'
    

    Then you can use d3 as usual.

    Example:

    index.html

    <foo-bar></foo-bar>
    <script type='module' src='app.js'></script>
    

    app.js

    import { PolymerElement, html } from '@polymer/polymer/polymer-element.js'
    import * as d3 from 'd3'
    
    class FooBar extends PolymerElement {
      static get template () {
        return html`
          <style>
            .links line {
              stroke: #999;
              stroke-opacity: 0.6;
            }
    
            .nodes circle {
              stroke: #fff;
              stroke-width: 1.5px;
            }
          </style>
    
          <svg width='960' height='600'></svg>
        `
      }
    
      ready () {
        super.ready()
        this.initGraph()
      }
    
      initGraph () {
        let svg = d3.select(this.shadowRoot.querySelector('svg'))
        let width = +svg.attr('width')
        let height = +svg.attr('height')
    
        let color = d3.scaleOrdinal(d3.schemeCategory10)
    
        let simulation = d3.forceSimulation()
          .force('link', d3.forceLink().id(d => d.id))
          .force('charge', d3.forceManyBody())
          .force('center', d3.forceCenter(width / 2, height / 2))
    
        d3.json('miserables.json').then(graph => {
          let link = svg.append('g')
            .attr('class', 'links')
            .selectAll('line')
            .data(graph.links)
            .enter().append('line')
            .attr('stroke-width', d => Math.sqrt(d.value))
    
          let node = svg.append('g')
            .attr('class', 'nodes')
            .selectAll('circle')
            .data(graph.nodes)
            .enter().append('circle')
            .attr('r', 5)
            .attr('fill', d => color(d.group))
            .call(d3.drag()
              .on('start', dragstarted)
              .on('drag', dragged)
              .on('end', dragended))
    
          node.append('title').text(d => d.id)
    
          simulation.nodes(graph.nodes)
            .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)
            })
    
          simulation.force('link').links(graph.links)
        })
    
        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
        }
      }
    }
    
    customElements.define('foo-bar', FooBar)
    

    Note: Polymer use Shadow DOM which normal selector (e.g. d3.select) cannot go through.

    In this example I modify from Force-Directed Graph.