Atomic Angular — Doc interne

Aggregation Heatmap

The canvas sibling of Heatmap, for a two-dimensional distribution too large for the DOM — same inputs, same output payloads, one import change.

<Heatmap> is the one to reach for first — it costs no dependency and is fully accessible. <AggregationHeatmap> exists for the case <Heatmap> cannot serve: a matrix large enough that hundreds of DOM nodes become the bottleneck. Canvas rendering is flat in the cell count; a DOM grid is not.

Two costs, one thing you do not lose

Switching to this component costs a dependency (chartjs-chart-matrix, on top of chart.js) and it costs accessibility — a canvas is opaque to screen readers and to text selection, and nothing brings that back. What it does not cost is the clickable axis label: createAxisClickPlugin() reproduces it on canvas, and (axisSelected) emits the exact same payload shape as <Heatmap>.

Minimal example

A matrix too large for the DOM
sample.component.ts
import { Component, signal } from "@angular/core";
import { AggregationHeatmapComponent } from "@sinequa/atomic-angular/charts/heatmap";
import type { AggregationLike, HeatmapAxisSelection, HeatmapCellSelection } from "@sinequa/atomic-angular";

@Component({
  selector: "sample-component",
  imports: [AggregationHeatmapComponent],
  template: `
    <div class="h-96">
      <AggregationHeatmap
        [aggregation]="matrix()"
        [maxX]="60"
        [maxY]="60"
        (cellSelected)="onCell($event)"
        (axisSelected)="onAxis($event)"
      />
    </div>
  `,
})
export class SampleComponent {
  readonly matrix = signal<AggregationLike | undefined>(undefined);

  protected onCell(selection: HeatmapCellSelection) {
    console.log(`${selection.x} / ${selection.y}: ${selection.count}`);
  }

  protected onAxis(selection: HeatmapAxisSelection) {
    console.log(`Filter the whole ${selection.axis} axis on`, selection.value);
  }
}
# only if you use @sinequa/atomic-angular/charts/heatmap
npm i chart.js chartjs-chart-matrix

How it works

chartjs-chart-matrix ships no other chart in this library needs, so it lives in its own tertiary entry point (@sinequa/atomic-angular/charts/heatmap) rather than the shared /charts one — an application drawing only bar charts never resolves it.

The same buildHeatmap() pure function <Heatmap> uses parses the aggregation here too, so the two components are interchangeable: identical inputs, identical (cellSelected)/(axisSelected) payload shapes. Swapping one for the other is an import change, nothing more.

Clickable axis labels, reproduced on canvas. Chart.js's own options.onClick never fires on an axis label — it only fires inside the chart area, and labels sit outside it by construction. createAxisClickPlugin() does its own hit test against the scale's box instead, walking getPixelForTick() rather than inverting getValueForPixel() (which assumes uniform spacing and breaks under offset/autoSkip, both of which this component sets).

Options

Prop

Type

Pitfalls

What's next

On this page