andyperlitch / ngx-d3-tooltip

Add tooltips to your d3 visualizations using Angular Components.
https://andyperlitch.github.io/ngx-d3-tooltip/demo/
4 stars 2 forks source link

ngx-d3-tooltip

Build Status codecov

Add tooltips to your d3 visualizations using Angular Components.

gif of tooltip

Why?

D3.js is a great visualization library, but building dynamic tooltips is a bit clunky. Angular is an app framework and has great html templating and data-binding, which are features that are ideal for more-than-trivial tooltips. This project aims to achieve the best of both worlds.

Installation

  1. Install ngx-d3-tooltip with npm

    npm install ngx-d3-tooltip
  2. Add D3TooltipModule to your app module's imports

    // ...
    import { D3TooltipModule } from 'ngx-d3-tooltip';
    
    // ...in @NgModule:
    imports: [
      D3TooltipModule
    ],
    // ...

Usage

The main idea is that the D3TooltipService.createFromComponent method returns a function which is then passed to your d3.Selection.call :

let tooltip = d3TooltipService.createFromComponent(MyComponent, inputs, outputs, options);
svg.selectAll('rect.example')
  .data(myData)
  .enter()
  .call(tooltip);

Here is a more in-depth example:

  1. Create the component you want to use as a tooltip

    // my-tooltip.component.ts:
    
    @Component({
    selector: 'my-tooltip',
    template: `
      <h3>{{ myData.name }}</h3>
      <p>{{ myData.someText }}</p>
    `
    })
    export class MyTooltipComponent {
    
    // Most commonly, you will be passing the d3 element datum
    // to this component, exemplified here (assuming the data
    // bound to the element with this the tooltip is of type 
    // IMyDatum)
    @Input()
    myDatum: IMyDatum;
    
    // Outputs work as well!
    @Output()
    update: EventEmitter<any>;
    }
    
  2. Add the component to your module's entryComponents array

    // app.module.ts:
    
    // ...
    import { MyTooltipComponent } from './components/d3-tooltip.component';
    import { MyOtherTooltipComponent } from './components/d3-other-tooltip.component';
    
    // ...in @NgModule:
    entryComponents: [
      MyTooltipComponent,
      MyOtherTooltipComponent
    ],
    // ...
  3. In your d3 code, create the tooltip function and invoke it with d3.Selection.call

    // my-d3-chart.component.ts:
    // Import the component tooltip
    import { MyTooltipComponent } from './components/d3-tooltip.component';
    @Component({ /* ... */ })
    export class MyD3ChartComponent {
    tooltip: (selection) => void;  
    constructor(private tipService: D3TooltipService) { // <-- inject the tooltip service
      // create the tooltip function, to be passed to .call()
      this.tooltip = tipService.createFromComponent(
        // The component to insert into the tooltip
        MyTooltipComponent,
        // A function which takes the d3 datum of the current element and returns an object
        // where the keys are @Input property names.
        (d: IMyDatum) => {
          return { myDatum: d }
        },
        // A function which returns an object where keys are @Output property names of the
        // tooltip component and values are handlers passed to the output's subscribe()
        () => {
          return {
            update: (d: IMyDatum) => {
              // do something here...
            }
          }
        },
        { /* other options available */}
      );
    }
    render() {
      // ...set up d3 elements...
      // d3 render code:
      let rect = svg.selectAll('rect.example')
        .data(myData);
      rect.enter()
        .append('rect')
        .classed('example', true)
        .call(this.tooltip); // <-- Use on a selection like this
    }
    }

Running the Demo

npm start

Documentation

Documentation

TODOs:

License

MIT