TanStack
Catalog

Voronoi nearest-point interaction

interaction

94 lines · 1 file · 2.7 kB

cases/65-voronoi-nearest-tooltip/example.tsx94 lines · entry
cases/65-voronoi-nearest-tooltip/example.tsx
import { cars } from '@tanstack/charts-data/cars'
import type { CarsRow } from '@tanstack/charts-data/cars'
import { defineChart, dot } from '@tanstack/charts'
import { Chart } from '@tanstack/charts/react/tooltip'
import { voronoi } from '@tanstack/charts/spatial/voronoi'
import { tooltip } from '@tanstack/charts/tooltip'
import { scaleLinear } from 'd3-scale'
import type { ChartTooltipOptions } from '@tanstack/charts'

export type CompleteCar = CarsRow & { readonly 'economy (mpg)': number }
export interface ChartOptions {
  revision?: number
}

const colors = ['#2563eb', '#0d9488', '#d97706']
const interactiveTooltip: ChartTooltipOptions<CompleteCar> = {
  anchor: 'pointer',
  items: [
    {
      id: 'car',
      label: 'Car',
      text: (point) => `${point.datum.name} · ${cylinderLabel(point.datum)}`,
    },
  ],
}

export function createExampleChart({ revision = 0 }: ChartOptions = {}) {
  const rows = selectedCars(revision)
  return defineChart(
    {
      marks: [
        voronoi(rows, {
          id: 'nearest-cells',
          x: 'weight (lb)',
          y: 'economy (mpg)',
          key: carKey,
          color: cylinderLabel,
          fillOpacity: 0.14,
          stroke: '#ffffff',
          strokeWidth: 1,
        }),
        dot(rows, {
          id: 'voronoi-points',
          x: 'weight (lb)',
          y: 'economy (mpg)',
          key: carKey,
          color: cylinderLabel,
          stroke: '#ffffff',
          strokeWidth: 1,
          r: 4,
          states: [
            {
              when: { focus: 'primary' },
              style: { r: 7, stroke: 'Canvas', strokeWidth: 2 },
            },
            { when: { focus: 'unmatched' }, style: { opacity: 0.45 } },
          ],
        }),
      ],
      x: { scale: scaleLinear, grid: true, axis: { label: 'Weight (lb)' } },
      y: {
        scale: scaleLinear,
        grid: true,
        axis: { label: 'Fuel economy (mpg)' },
      },
      color: { range: colors },
    },
    {
      keyboard: true,
      tooltip: { use: tooltip, ...interactiveTooltip },
    },
  )
}

export const exampleAriaLabel = 'Voronoi nearest-point interaction'
export const chart = createExampleChart()

export default function Example() {
  return <Chart definition={chart} ariaLabel={exampleAriaLabel} height={480} />
}

export function selectedCars(revision: number): CompleteCar[] {
  return cars
    .filter((row): row is CompleteCar => row['economy (mpg)'] !== null)
    .slice(revision * 3, revision * 3 + 18)
}

export function cylinderLabel(row: CarsRow): string {
  return `${row.cylinders} cylinders`
}

export function carKey(row: CarsRow): string {
  return `${row.name}:${row.year}:${row['weight (lb)']}`
}