TanStack
Catalog

Nested Flare package sizes

polar

154 lines · 2 files · 3.9 kB

cases/96-nested-donut/example.tsx83 lines · entry
cases/96-nested-donut/example.tsx
import { Chart } from '@tanstack/charts/react/tooltip'
import { tooltip as exampleTooltip } from '@tanstack/charts/tooltip'

import { flare } from '@tanstack/charts-data/flare'
import { defineChart } from '@tanstack/charts'
import { pie, polar, radialArc } from '@tanstack/charts/polar'
import { nestedFlareDonut } from './transform'

const names = [
  'flare.animate',
  'flare.data',
  'flare.animate.core',
  'flare.animate.interpolate',
  'flare.data.core',
  'flare.data.converters',
]
const colors = [
  '#38bdf8',
  '#8b5cf6',
  '#0284c7',
  '#0ea5e9',
  '#7c3aed',
  '#a855f7',
]

export const createExampleChart = (input: ChartOptions) => {
  const sourceRows =
    input.revision % 2 === 0
      ? flare
      : flare.filter((row) => row.size === null || row.size >= 1_000)
  const data = nestedFlareDonut(sourceRows)
  const innerArcs = pie(data.inner, { value: 'size' })
  const outerArcs = pie(data.outer, { value: 'size' })

  return defineChart(
    {
      marks: [
        polar({
          radiusRatio: 0.8,
          marks: [
            radialArc(innerArcs, {
              id: 'family-slices',
              key: 'name',
              innerRadius: ({ radius }) => radius * 0.12,
              outerRadius: ({ radius }) => radius * 0.46,
              color: 'name',
            }),
            radialArc(outerArcs, {
              id: 'detail-slices',
              key: 'name',
              innerRadius: ({ radius }) => radius * 0.56,
              color: 'name',
            }),
          ],
          scales: {
            angle: null,
            radius: null,
          },
        }),
      ],
      scales: {
        x: null,
        y: null,
      },
      color: { domain: names, range: colors },
      margin: 0,
    },
    { keyboard: true, tooltip: exampleTooltip },
  )
}
export interface ChartOptions {
  revision: number
}

export const exampleAriaLabel = 'Nested Flare package sizes'

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

export default function Example() {
  return <Chart ariaLabel={exampleAriaLabel} definition={chart} height={480} />
}
cases/96-nested-donut/transform.ts71 lines · dependency
cases/96-nested-donut/transform.ts
import type { FlareRow } from '@tanstack/charts-data/flare'

export interface FlareDonutSlice {
  name: string
  size: number
}

export interface FlareDonutDetail extends FlareDonutSlice {
  family: string
}

export interface FlareDonutData {
  inner: readonly FlareDonutSlice[]
  outer: readonly FlareDonutDetail[]
}

interface DetailGroup {
  name: string
  family: string
  includes: (name: string) => boolean
}

const detailGroups: readonly DetailGroup[] = [
  {
    name: 'flare.animate.core',
    family: 'flare.animate',
    includes: (name) =>
      name.startsWith('flare.animate.') &&
      !name.startsWith('flare.animate.interpolate.'),
  },
  {
    name: 'flare.animate.interpolate',
    family: 'flare.animate',
    includes: (name) => name.startsWith('flare.animate.interpolate.'),
  },
  {
    name: 'flare.data.core',
    family: 'flare.data',
    includes: (name) =>
      name.startsWith('flare.data.') &&
      !name.startsWith('flare.data.converters.'),
  },
  {
    name: 'flare.data.converters',
    family: 'flare.data',
    includes: (name) => name.startsWith('flare.data.converters.'),
  },
]

export function nestedFlareDonut(rows: readonly FlareRow[]): FlareDonutData {
  const leaves = rows.filter(
    (row): row is FlareRow & { readonly size: number } => row.size !== null,
  )
  const outer = detailGroups.map((group) => ({
    name: group.name,
    family: group.family,
    size: leaves.reduce(
      (total, row) => total + (group.includes(row.name) ? row.size : 0),
      0,
    ),
  }))
  const inner = ['flare.animate', 'flare.data'].map((family) => ({
    name: family,
    size: outer.reduce(
      (total, detail) => total + (detail.family === family ? detail.size : 0),
      0,
    ),
  }))

  return { inner, outer }
}