{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chart-panel",
  "type": "registry:block",
  "title": "Chart panel",
  "description": "Recharts units and legends, missing values, recovery states and accessible source drill-down.",
  "dependencies": [
    "recharts@^3.10.1"
  ],
  "registryDependencies": [
    "https://shine-blond.vercel.app/r/async-state.json"
  ],
  "files": [
    {
      "path": "blocks/chart-panel.tsx",
      "type": "registry:component",
      "target": "components/shine/chart-panel.tsx",
      "content": "\"use client\";\nimport { useId } from \"react\";\nimport { ResponsiveContainer,BarChart,Bar,XAxis,YAxis,CartesianGrid,Tooltip,Legend } from \"recharts\";\nimport { AsyncState } from \"@/components/shine/async-state\";\nexport type ChartSeries={key:string;label:string;color:string};\nexport type ChartDatum={label:string;values:Record<string,number|null>;href:string};\n/** Visible source records remain the accessible equivalent and drill-down for every plotted value. */\nexport function ChartPanel({title,description,unit,data,series,loading=false,error,onRetry}:{title:string;description:string;unit:string;data:ChartDatum[];series:ChartSeries[];loading?:boolean;error?:string;onRetry:()=>void}) {\n const id=useId(),format=(value:number)=>new Intl.NumberFormat(undefined,{maximumFractionDigits:2}).format(value)+\" \"+unit;\n const rows=data.map(row=>({label:row.label,...Object.fromEntries(series.map(item=>[item.key,Number.isFinite(row.values[item.key])?row.values[item.key]:null]))}));\n return <section data-product-pattern=\"chart-panel\" aria-labelledby={id} className=\"min-w-0 space-y-4 rounded-lg border border-border bg-background p-5\"><header><h2 id={id} className=\"text-lg font-semibold\">{title}</h2><p className=\"text-sm text-muted-foreground\">{description} Unit: {unit}.</p></header>{loading?<AsyncState state=\"loading\"/>:error?<AsyncState state=\"error\" message={error} onRetry={onRetry}/>:!data.length?<AsyncState state=\"empty\" message=\"No chart data for this period.\"/>:<><div className=\"h-72 min-w-0\" aria-hidden=\"true\"><ResponsiveContainer width=\"100%\" height=\"100%\"><BarChart maxBarSize={80} data={rows} accessibilityLayer={false}><CartesianGrid vertical={false} stroke=\"var(--border)\"/><XAxis dataKey=\"label\" tick={{fill:\"var(--foreground)\"}}/><YAxis tickFormatter={value=>new Intl.NumberFormat(undefined,{notation:\"compact\"}).format(value)} tick={{fill:\"var(--foreground)\"}}/><Tooltip formatter={value=>typeof value===\"number\"?format(value):\"Unavailable\"}/><Legend/>{series.map(item=><Bar key={item.key} dataKey={item.key} name={item.label} fill={item.color} isAnimationActive={false}/>)}</BarChart></ResponsiveContainer></div><details><summary className=\"cursor-pointer py-3 text-sm font-medium\">View values and source records</summary><ul className=\"divide-y divide-border\">{data.map((row,i)=><li key={i} className=\"space-y-1 py-3\"><a href={row.href} className=\"text-sm font-medium underline\">{row.label}</a>{series.map(item=><p key={item.key} className=\"text-sm text-muted-foreground\">{item.label}: {Number.isFinite(row.values[item.key])?format(row.values[item.key] as number):\"Unavailable\"}</p>)}</li>)}</ul></details></>}</section>;\n}\n"
    }
  ]
}
