TanStack
Catalog

Survey agreement share gauge

polar

105 lines · 2 files · 2.7 kB

cases/78-gauge/example.tsx75 lines · entry
cases/78-gauge/example.tsx
import { Chart } from '@tanstack/charts/react/tooltip'
import { tooltip as exampleTooltip } from '@tanstack/charts/tooltip'

import { survey } from '@tanstack/charts-data/survey'
import { defineChart } from '@tanstack/charts'
import { pie, polar, radialArc } from '@tanstack/charts/polar'
import { agreementPercent, gaugeSegments } from './transform'
import type { GaugeDatum } from './transform'

const startAngle = (-Math.PI * 3) / 4
const endAngle = (Math.PI * 3) / 4
const ids: readonly GaugeDatum['id'][] = ['value', 'remainder']
const colors = ['#ef4444', '#e2e8f0']

export const createExampleChart = (input: ChartOptions) => {
  const question = `Q${(input.revision % 2) + 1}`
  const agreement = agreementPercent(survey, question)
  const segments = gaugeSegments(agreement)
  const arcs = pie(segments, {
    value: 'value',
    startAngle,
    endAngle,
  })

  return defineChart(
    {
      marks: [
        polar({
          inset: 0,
          radiusRatio: 0.8,
          marks: [
            radialArc(arcs, {
              id: 'gauge-segments',
              key: 'id',
              innerRadius: ({ radius }) => radius * 0.72,
              color: 'id',
            }),
          ],
          scales: {
            angle: null,
            radius: null,
          },
        }),
      ],
      scales: {
        x: null,
        y: null,
      },
      color: { domain: ids, range: colors },
      margin: 0,
    },
    {
      keyboard: true,
      tooltip: {
        use: exampleTooltip,
        ...{
          format: ({ datum }) => `${datum.label} · ${datum.value}%`,
        },
      },
    },
  )
}
export interface ChartOptions {
  revision: number
}

export const exampleAriaLabel = 'Survey agreement share gauge'

export const chart = createExampleChart({
  revision: 0,
})

export default function Example() {
  return <Chart ariaLabel={exampleAriaLabel} definition={chart} height={480} />
}
cases/78-gauge/transform.ts30 lines · dependency
cases/78-gauge/transform.ts
export interface GaugeDatum {
  id: 'value' | 'remainder'
  label: string
  value: number
}

export function gaugeSegments(value: number): readonly GaugeDatum[] {
  return [
    { id: 'value', label: 'Agree', value },
    {
      id: 'remainder',
      label: 'Other responses',
      value: 100 - value,
    },
  ]
}

export function agreementPercent(
  rows: readonly SurveyRow[],
  question: string,
): number {
  const responses = rows.filter((row) => row.Question === question)
  const agreements = responses.filter(
    (row) => row.Response === 'Agree' || row.Response === 'Strongly Agree',
  )
  return responses.length === 0
    ? 0
    : Math.round((agreements.length / responses.length) * 100)
}
import type { SurveyRow } from '@tanstack/charts-data/survey'