import { useState } from "react"; import type { TreeNode } from "./tree"; function fmt(x: number): string { if (Number.isNaN(x)) return "NaN"; if (!Number.isFinite(x)) return x > 0 ? "Inf" : "-Inf"; return Number.isInteger(x) ? String(x) : Number(x.toPrecision(6)).toString(); } // Hard cap on how many values/rows are ever rendered as text, to keep large // arrays (e.g. big surf grids) from freezing the UI. const MAX_RENDER = 5000; const STEP = 1000; function DataValues({ data }: { data: unknown }) { const [limit, setLimit] = useState(200); if (!Array.isArray(data) || data.length === 0) return

No data.

; const isNumber = (e: unknown) => typeof e === "number"; const allNumbers = (data as unknown[]).every(isNumber); const total = data.length; const renderCap = Math.min(total, MAX_RENDER); const shown = Math.min(limit, renderCap); const hasMore = shown < renderCap; const capped = total > MAX_RENDER && shown >= MAX_RENDER; const unit = allNumbers ? "values" : "rows"; let body: string; if (allNumbers) { body = (data as number[]).slice(0, shown).map(fmt).join(", "); } else { body = (data as unknown[]) .slice(0, shown) .map((r, i) => { const text = Array.isArray(r) ? `[${(r as number[]).map(fmt).join(", ")}]` : fmt(r as number); return `${i}: ${text}`; }) .join("\n"); } if (shown < total) body += allNumbers ? ", …" : "\n…"; return ( <>
{body}
{shown < total ? `Showing ${shown} of ${total} ${unit}` : `${total} ${unit}`} {capped ? " — display capped" : ""} {hasMore && ( )} {!capped && shown < total && total <= MAX_RENDER && ( )}
); } export function InfoPanel({ node }: { node: TreeNode | null }) { if (!node) { return (

Select an object in the tree to inspect it.

); } const { detail } = node; return (
{node.label} {detail.type === "dataset" ? "dataset" : detail.kind}
{detail.type === "object" ? ( detail.properties.length === 0 ? (

No properties.

) : ( {detail.properties.map(p => ( ))}
{p.key} {p.swatch && ( )} {p.value}
) ) : (
{detail.stats && ( <> {detail.stats.nan > 0 && ( )} )}
shape {detail.shapeLabel}
count {detail.count}
min {fmt(detail.stats.min)}
max {fmt(detail.stats.max)}
mean {fmt(detail.stats.mean)}
NaN {detail.stats.nan}
values
)}
); }