Source

108 chart lines · 2 files · 2.8 kBBenchmark harness excluded · shared/mount.ts

Flare package hierarchy252 records · CSV · 8.0 kB
@charts-poc/demo-data/flare

Selection: Complete published snapshot

name
string
size
number | null

Flare visualization toolkit@observablehq/sample-datasets@1.0.1 · revision 732c0148de74 · flare.csv · ISC distribution; upstream source credited · SHA-256 704349b51b70Pinned snapshot

cases/36-hierarchy-tree/tanstack.ts101 lines · entry
cases/36-hierarchy-tree/tanstack.ts
import { defineChart, dot, link, text } from '@tanstack/charts'
import { flare } from '@charts-poc/demo-data/flare'
import { stratify, tree } from 'd3-hierarchy'
import { scaleLinear } from 'd3-scale'
import { selectHierarchyData } from './selection'
import { tanstackMount } from '../../shared/mount'
import type { FlareRow } from '@charts-poc/demo-data/flare'
import type { ConformanceInput } from '../../types'

interface TreeNodeRow {
  name: string
  x: number
  y: number
  internal: boolean
}

interface TreeLinkRow {
  source: string
  target: string
  x1: number
  y1: number
  x2: number
  y2: number
}

function hierarchyPath(name: string): string {
  return name.slice('flare.'.length).replaceAll('.', '/')
}

function hierarchyLabel(name: string): string {
  return name.slice(name.lastIndexOf('.') + 1)
}

const definition = (input: ConformanceInput) => {
  const root = stratify<FlareRow>().path((row) => hierarchyPath(row.name))(
    Array.from(selectHierarchyData(flare, input.revision)),
  )
  const layoutRoot = tree<FlareRow>().nodeSize([1, 1])(root)

  const nodes: readonly TreeNodeRow[] = layoutRoot
    .descendants()
    .map((node) => ({
      name: node.data.name,
      x: node.y,
      y: -node.x,
      internal: node.children !== undefined,
    }))
  const links: readonly TreeLinkRow[] = layoutRoot
    .links()
    .map(({ source, target }) => ({
      source: source.data.name,
      target: target.data.name,
      x1: source.y,
      y1: -source.x,
      x2: target.y,
      y2: -target.x,
    }))
  return defineChart({
    marks: [
      link(links, {
        x1: 'x1',
        y1: 'y1',
        x2: 'x2',
        y2: 'y2',
        stroke: '#94a3b8',
        strokeOpacity: 0.55,
        strokeWidth: 1.5,
      }),
      dot(nodes, {
        x: 'x',
        y: 'y',
        fill: '#2563eb',
        r: 3.5,
      }),
      text(nodes, {
        x: 'x',
        y: 'y',
        text: (node) => hierarchyLabel(node.name),
        fill: '#2563eb',
        fontSize: 10,
        anchor: (node) => (node.internal ? 'end' : 'start'),
        dx: (node) => (node.internal ? -6 : 6),
      }),
    ],
    x: { scale: scaleLinear },
    y: { scale: scaleLinear },
    guides: false,
    margin: { top: 22, right: 140, bottom: 22, left: 50 },
  })
}

export const mount = tanstackMount(
  definition,
  'Tidy Flare analytics hierarchy',
  {
    format: ({ datum }) =>
      'name' in datum
        ? `${datum.name} · ${datum.internal ? 'Group' : 'Leaf'}`
        : `${datum.source}${datum.target}`,
  },
)
cases/36-hierarchy-tree/selection.ts7 lines · support
cases/36-hierarchy-tree/selection.ts
import type { FlareRow } from '@charts-poc/demo-data/flare'

export function selectHierarchyData(rows: readonly FlareRow[], revision = 0) {
  return revision % 2 === 0
    ? rows.slice(1, 11)
    : [...rows.slice(1, 8), ...rows.slice(10, 13)]
}