SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
2.4 KB · 54 lines tsx
Raw Blame History
1"use client";2import * as React from "react";34export interface TooltipEntry {5  name?: string | number;6  value?: number | string | ReadonlyArray<number | string>;7  color?: string;8  fill?: string;9  dataKey?: string | number;10  payload?: Record<string, unknown>;11}1213export interface ChartTooltipProps {14  active?: boolean;15  payload?: ReadonlyArray<TooltipEntry>;16  label?: unknown;17  /** Format the heading (bucket label). */18  labelFormatter?: (label: unknown, payload: ReadonlyArray<TooltipEntry>) => React.ReactNode;19  /** Format a series value. */20  valueFormatter?: (value: number, entry: TooltipEntry) => React.ReactNode;21  /** Optional display names per dataKey. */22  names?: Record<string, string>;23  /** Extra footer line (e.g. total). */24  footer?: (payload: ReadonlyArray<TooltipEntry>) => React.ReactNode;25}2627/** Theme-matched tooltip body; recharts clones it with `active`, `payload` and `label`. */28export function ChartTooltip({ active, payload, label, labelFormatter, valueFormatter, names, footer }: ChartTooltipProps) {29  if (!active || !payload || payload.length === 0) return null;30  const rows = payload.filter((p) => p.value !== undefined && p.value !== null);31  if (!rows.length) return null;32  return (33    <div className="pointer-events-none min-w-[150px] rounded-md border border-border bg-bg-elevated px-3 py-2 text-[12px] shadow-md">34      <div className="mb-1.5 font-medium text-fg">{labelFormatter ? labelFormatter(label, payload) : String(label ?? "")}</div>35      <ul className="space-y-1">36        {rows.map((p, i) => {37          const key = String(p.dataKey ?? p.name ?? i);38          const raw = Array.isArray(p.value) ? Number(p.value[0]) : Number(p.value);39          return (40            <li key={key} className="flex items-center justify-between gap-4">41              <span className="flex items-center gap-1.5 text-fg-muted">42                <span aria-hidden className="inline-block size-2 rounded-[2px]" style={{ backgroundColor: p.color ?? p.fill ?? "var(--fg-subtle)" }} />43                {names?.[key] ?? String(p.name ?? key)}44              </span>45              <span className="font-mono tabular text-fg">{valueFormatter ? valueFormatter(raw, p) : Number.isFinite(raw) ? raw.toLocaleString("en-US") : "—"}</span>46            </li>47          );48        })}49      </ul>50      {footer ? <div className="mt-1.5 border-t border-border pt-1.5 text-fg-muted">{footer(payload)}</div> : null}51    </div>52  );53}54