import { useLayoutEffect, useRef, useState, type ComponentProps } from "react" import { cn } from "cn" import { Label, LabelList, PolarAngleAxis, PolarGrid, PolarRadiusAxis, RadialBar, RadialBarChart } from "recharts" import { ChartContainer, ChartTooltip, ChartTooltipContent } from "./chart" import { CenterText, type HMDonutDatum } from "./donut-chart" import { seriesColor, seriesToConfig, type ChartSeries } from "./series" import { fitText } from "./text-fit" // Radial charts follow the official shadcn/ui examples (chart-radial-simple, -label, -grid, -text, -shape, -stacked): the same // angle ranges, radii, background tracks, corner rounding, grid and center text. Radii are pixels, as in the official source. // Adjustments for HM: values come from `value`/`id` rows with HM series colors; the angle axis domain is explicit (0 to the largest // value or stack total, the same range Recharts picks automatically) so an all-zero data set cannot produce NaN; names and center // text are shortened to fit. // Bar thickness is 1.5 times the earlier one (2026-11-06) in every radial chart. Recharts draws a bar over 80% of its band (10% // margin each side), so a single bar band 1.5 times as wide gives a 1.5 times thicker bar (text, shape, stacked: the outer radius // stays and the inner one moves in). // Ring charts with several bars (simple, label, grid) set the bar thickness (`barSize`) and the gap between rings directly: 18px // bars and 4px gaps (grid 16.5 and 3, the earlier gaps), and keep the earlier center hole (inner band radius 30), so the outer // radius grows to inner + bars x (bar + gap): 140 for five simple / label bars, 127.5 for five grid bars (2026-11-06, the outer // radius and chart height limits were lifted for this). Where the box is too narrow (320px screens) the three charts shrink all // radii, bar thickness and gaps in proportion (`useFitScale`). // The legend is drawn under the chart box, not inside it: the chart box is a fixed height that holds the largest ring, however // many lines the legend wraps to. const RADIAN = Math.PI / 180 /** Height of the chart box of every radial chart (px): the largest ring (outer radius 140) plus the 5px chart margins. */ const CHART_BOX = "aspect-auto h-[300px] w-full" const EDGE = 6 // px kept free on both sides of a ring (chart margin 5 + 1) const MIN_SCALE = 0.5 const LABEL_SIZE = 11 const MIN_LABEL_CHARS = 2 type PolarSector = { cx: number; cy: number; innerRadius: number; outerRadius: number; startAngle: number; endAngle: number } /** Name inside the start of its bar, curved along the arc like the official "insideStart" label, shortened to the bar length. */ function RadialNameLabel({ viewBox, value }: { viewBox?: unknown; value?: unknown }) { const sector = viewBox as Partial | undefined if (!sector || sector.innerRadius === undefined || sector.outerRadius === undefined || sector.startAngle === undefined || sector.endAngle === undefined) return null const radius = (sector.innerRadius + sector.outerRadius) / 2 const arcLength = radius * Math.abs(sector.endAngle - sector.startAngle) * RADIAN const text = fitText(String(value ?? ""), arcLength - 14, LABEL_SIZE) if ([...text].length < MIN_LABEL_CHARS) return null return