Source

143 chart lines · 2 files · 3.9 kB

Apple daily stock prices1,260 records · CSV · 92.4 kB
@charts-poc/demo-data/aapl

Selection: Complete published snapshot

Date
Date
Open
number
High
number
Low
number
Close
number
Adj Close
number
Volume
number

Yahoo! Finance@observablehq/sample-datasets@1.0.1 · revision 732c0148de74 · aapl.csv · ISC distribution; upstream source credited · SHA-256 18dc8bf6542dPinned snapshot

cases/34-pointer-tooltip/tanstack.ts134 lines · entry
cases/34-pointer-tooltip/tanstack.ts
import { defineChart, dot, lineY, mountChart } from '@tanstack/charts'
import type { ChartHostOptions } from '@tanstack/charts'
import { tooltip } from '@tanstack/charts/tooltip'
import { scaleLinear, scaleUtc } from 'd3-scale'
import { aapl } from '@charts-poc/demo-data/aapl'
import type { AaplRow } from '@charts-poc/demo-data/aapl'
import type {
  ConformanceHandle,
  ConformanceInput,
  ConformanceTestDriver,
} from '../../types'
import { selectPointerTooltipData } from './selection'

const definition = (input: ConformanceInput) => {
  const rows = selectPointerTooltipData(aapl, input.revision)
  return defineChart({
    marks: [
      lineY(rows, {
        x: 'Date',
        y: 'Close',
        stroke: '#2563eb',
      }),
      dot(rows, {
        x: 'Date',
        y: 'Close',
        fill: '#2563eb',
        r: 3,
        states: [
          {
            when: { focus: 'primary' },
            style: {
              r: 7,
              stroke: 'Canvas',
              strokeWidth: 2,
            },
            transition: { duration: 140, easing: 'ease-out' },
          },
        ],
      }),
    ],
    x: { scale: scaleUtc },
    y: { scale: scaleLinear, grid: true, axis: { label: 'Apple close (USD)' } },
  })
}

const configuredDefinition = (input: ConformanceInput) =>
  defineChart(definition(input), {
    animate: false,
    keyboard: true,
    tooltip: {
      use: tooltip,
      anchor: 'point',
      placement: ['top', 'right', 'left', 'bottom'],
      items: [
        {
          channel: 'y',
          label: 'Apple',
          text: (point) =>
            point.datum.Close.toLocaleString(undefined, {
              maximumFractionDigits: 2,
            }),
        },
        {
          channel: 'x',
          label: 'Date',
        },
      ],
    },
  })

export function mount(
  container: HTMLElement,
  input: ConformanceInput,
): ConformanceHandle {
  let focusedDates: string[] = []
  const options: ChartHostOptions<AaplRow> = {
    definition: configuredDefinition(input),
    width: input.width,
    height: input.height,
    ariaLabel: 'Interactive Apple closing price',
    onFocusGroupChange(points) {
      focusedDates = points.map((point) => dateKey(point.datum.Date))
    },
  }
  const host = mountChart(container, options)
  const driver: ConformanceTestDriver = {
    resolveTarget(target) {
      if (target.view && target.view !== 'main') return null
      const date = target.anchor.startsWith('date:')
        ? target.anchor.slice('date:'.length)
        : null
      const point = host
        .getScene()
        .points.find((candidate) => dateKey(candidate.datum.Date) === date)
      const svg = container.querySelector('svg')
      if (!point || !svg) return null
      const bounds = svg.getBoundingClientRect()
      return {
        x: bounds.left + (point.x / host.getScene().width) * bounds.width,
        y: bounds.top + (point.y / host.getScene().height) * bounds.height,
        focusElement: svg,
      }
    },
    readState() {
      const tooltip = container.querySelector<HTMLElement>('.ts-chart-tooltip')
      return {
        focus: { dates: [...focusedDates] },
        tooltip: {
          visible: Boolean(tooltip && !tooltip.hidden),
          text: tooltip?.textContent?.trim() ?? '',
        },
      }
    },
  }

  return {
    driver,
    update(nextInput) {
      host.update({
        ...options,
        definition: configuredDefinition(nextInput),
        width: nextInput.width,
        height: nextInput.height,
      })
    },
    destroy() {
      host.destroy()
    },
  }
}

function dateKey(date: Date) {
  return date.toISOString().slice(0, 10)
}
cases/34-pointer-tooltip/selection.ts9 lines · support
cases/34-pointer-tooltip/selection.ts
import type { AaplRow } from '@charts-poc/demo-data/aapl'

export function selectPointerTooltipData(
  rows: readonly AaplRow[],
  revision = 0,
) {
  const offset = Math.abs(revision) % 2
  return rows.slice(offset, offset + 36)
}