@acronis-platform/ui-react 5.2.0 → 5.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/components/ui/area-chart/area-chart.js +107 -107
  2. package/dist/components/ui/area-chart/area-chart.js.map +1 -1
  3. package/dist/components/ui/bar-chart/bar-chart.js +162 -162
  4. package/dist/components/ui/bar-chart/bar-chart.js.map +1 -1
  5. package/dist/components/ui/category-bar/category-bar.js +50 -50
  6. package/dist/components/ui/category-bar/category-bar.js.map +1 -1
  7. package/dist/components/ui/chart/chart.js +53 -40
  8. package/dist/components/ui/chart/chart.js.map +1 -1
  9. package/dist/components/ui/composed-chart/composed-chart.js +146 -146
  10. package/dist/components/ui/composed-chart/composed-chart.js.map +1 -1
  11. package/dist/components/ui/confidence-cone/confidence-cone.js +97 -97
  12. package/dist/components/ui/confidence-cone/confidence-cone.js.map +1 -1
  13. package/dist/components/ui/data-table/data-table-bulk-actions-bar.js +1 -1
  14. package/dist/components/ui/data-table/data-table-bulk-actions-bar.js.map +1 -1
  15. package/dist/components/ui/data-table/data-table-view-options.js +3 -2
  16. package/dist/components/ui/data-table/data-table-view-options.js.map +1 -1
  17. package/dist/components/ui/data-table/data-table.js +136 -132
  18. package/dist/components/ui/data-table/data-table.js.map +1 -1
  19. package/dist/components/ui/funnel-chart/funnel-chart.js +120 -120
  20. package/dist/components/ui/funnel-chart/funnel-chart.js.map +1 -1
  21. package/dist/components/ui/histogram/histogram.js +58 -58
  22. package/dist/components/ui/histogram/histogram.js.map +1 -1
  23. package/dist/components/ui/line-chart/line-chart.js +91 -91
  24. package/dist/components/ui/line-chart/line-chart.js.map +1 -1
  25. package/dist/components/ui/pie-chart/pie-chart.js +92 -92
  26. package/dist/components/ui/pie-chart/pie-chart.js.map +1 -1
  27. package/dist/components/ui/radar-chart/radar-chart.js +37 -37
  28. package/dist/components/ui/radar-chart/radar-chart.js.map +1 -1
  29. package/dist/components/ui/radial-bar-chart/radial-bar-chart.js +112 -112
  30. package/dist/components/ui/radial-bar-chart/radial-bar-chart.js.map +1 -1
  31. package/dist/components/ui/sankey-chart/sankey-chart.js +78 -78
  32. package/dist/components/ui/sankey-chart/sankey-chart.js.map +1 -1
  33. package/dist/components/ui/scatter-chart/scatter-chart.js +57 -57
  34. package/dist/components/ui/scatter-chart/scatter-chart.js.map +1 -1
  35. package/dist/components/ui/table/table-view-options.js +88 -33
  36. package/dist/components/ui/table/table-view-options.js.map +1 -1
  37. package/dist/components/ui/table/table.js.map +1 -1
  38. package/dist/components/ui/treemap/treemap.js +87 -87
  39. package/dist/components/ui/treemap/treemap.js.map +1 -1
  40. package/dist/index.js +111 -111
  41. package/dist/llms/data-table.md +33 -7
  42. package/dist/llms/table.md +56 -23
  43. package/dist/llms.txt +1 -1
  44. package/dist/react.js +111 -111
  45. package/dist/src/components/ui/chart/chart.d.ts +1 -0
  46. package/dist/src/components/ui/chart/index.d.ts +1 -1
  47. package/dist/src/components/ui/data-table/data-table-view-options.d.ts +1 -1
  48. package/dist/src/components/ui/data-table/data-table.d.ts +11 -1
  49. package/dist/src/components/ui/data-table/data-table.docs.d.ts +6 -0
  50. package/dist/src/components/ui/radial-bar-chart/radial-bar-chart.d.ts +3 -2
  51. package/dist/src/components/ui/sankey-chart/sankey-chart.d.ts +1 -1
  52. package/dist/src/components/ui/table/table-view-options.d.ts +9 -1
  53. package/dist/src/components/ui/treemap/treemap.d.ts +3 -2
  54. package/dist/ui-react.css +1 -1
  55. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"line-chart.js","names":[],"sources":["../../../../src/components/ui/line-chart/line-chart.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n Area,\n Brush,\n CartesianGrid,\n ComposedChart,\n LabelList,\n Line,\n LineChart as RechartsLineChart,\n ReferenceLine,\n Text,\n XAxis,\n YAxis,\n useXAxisScale,\n usePlotArea,\n} from 'recharts';\n\nimport { cn } from '@/lib/utils';\nimport {\n CHART_LABEL_FONT_SIZE,\n CHART_LABEL_MARGIN,\n ChartContainer,\n ChartLegend,\n ChartLegendContent,\n ChartTooltip,\n ChartTooltipContent,\n resolveAnimation,\n resolveAxisDomain,\n resolveBrushProps,\n dropProjectionPayload,\n resolveChartReferenceValue,\n resolveLabelFillClass,\n resolveReferenceLineProps,\n resolveRotatedTickAnchor,\n resolveXAxisHeight,\n resolveXAxisTitle,\n resolveYAxisTitle,\n toLabelFormatter,\n toReferenceLineList,\n type ChartConfig,\n type ChartPalette,\n type ChartLegendContentProps,\n type ChartTooltipContentProps,\n type CartesianChartProps,\n type ChartAnimationProps,\n type ChartBrushProps,\n type ChartCurveType,\n type ChartDataLabelProps,\n type ChartReferenceLine,\n type CartesianLabelPosition,\n} from '../chart';\n\n// A typed recharts composition over the shared `Chart` primitives. The two CVA\n// axes are the design's Line-chart variant set: `curve` (how the segments\n// interpolate between points — straight, smoothed, or stepped; the seven values\n// are documented on the shared `ChartCurveType`) and `lineStyle` (a solid or\n// dashed stroke). \"single\" vs \"multi\" line is not a variant — it\n// falls out of how many `dataKeys` the caller plots. The classes stay empty\n// because recharts' SVG — not CSS — draws the lines: `curve` drives each\n// `<Line type>` and `lineStyle` drives its `strokeDasharray`. CVA is kept so the\n// variant set is a first-class, spec-conformant part of the API (matched against\n// ui-spec's api.yaml enums) and exposed via `VariantProps`; the resolved values\n// are also mirrored onto `data-curve` / `data-line-style` for styling hooks and\n// tests.\nconst lineChartVariants = cva('', {\n variants: {\n curve: {\n linear: '',\n monotone: '',\n natural: '',\n basis: '',\n step: '',\n stepBefore: '',\n stepAfter: '',\n },\n lineStyle: {\n solid: '',\n dashed: '',\n },\n },\n defaultVariants: {\n curve: 'monotone',\n lineStyle: 'solid',\n },\n});\n\n// Renders two <clipPath> defs (actual / projection), a CSS <style> that clips\n// each recharts Line layer to its half, and a dashed separator line at the\n// visual midpoint between the last actual tick and the first projection tick.\n//\n// recharts' StaticCurve explicitly overwrites any clipPath prop passed to\n// <Line> with its own plot-area URL, so the prop-based approach used by\n// <Area> cannot be used here. Instead we apply CSS clip-path to the outer <g>\n// that recharts wraps each Line series in (targeted via a unique className we\n// set on the <Line> component). CSS clip-path on a class selector (specificity\n// 0,1,0) beats the SVG presentation attribute recharts sets on the inner\n// <path> (specificity 0), so the two clips compose: outer = our half, inner =\n// recharts' plot-area — net effect is the correct half within the plot area.\nfunction ProjectionClip({\n projectionStart,\n prevTick,\n clipId,\n dataKeys,\n}: {\n projectionStart: string | number;\n prevTick: string | number;\n clipId: string;\n dataKeys: string[];\n}) {\n const xScale = useXAxisScale();\n const plotArea = usePlotArea();\n if (!xScale || !plotArea) return null;\n const prevX = xScale(prevTick as string);\n const currX = xScale(projectionStart as string);\n if (prevX == null || currX == null) return null;\n const midX = (prevX + currX) / 2;\n const { x, y, width, height } = plotArea;\n const css = dataKeys\n .map(\n (_, i) =>\n `.actual-${clipId}-${i}{clip-path:url(#${clipId}-actual)}` +\n `.proj-${clipId}-${i}{clip-path:url(#${clipId}-projection)}`\n )\n .join('');\n return (\n <>\n <defs>\n <clipPath id={`${clipId}-actual`}>\n <rect x={x} y={y} width={midX - x} height={height} />\n </clipPath>\n <clipPath id={`${clipId}-projection`}>\n <rect x={midX} y={y} width={x + width - midX} height={height} />\n </clipPath>\n </defs>\n <style>{css}</style>\n <line\n x1={midX}\n y1={y}\n x2={midX}\n y2={y + height}\n stroke=\"var(--ui-border-on-surface-border)\"\n strokeDasharray=\"4 4\"\n />\n </>\n );\n}\n\n// Reserved field prefix for the synthetic delta-band range series. Each band\n// mints one `__band_<n>` field that feeds an <Area>; it must never surface in\n// the tooltip or legend (those describe only real, caller-supplied series).\nconst BAND_FIELD_PREFIX = '__band_';\n\n/**\n * Drop the synthetic delta-band range series from a recharts tooltip/legend\n * payload, keeping the real series (and their order). recharts already excludes\n * the band via `legendType`/`tooltipType=\"none\"`, so this is a second, explicit\n * guard — hence it's unit-tested rather than relying on that behavior.\n */\nexport function dropBandSeries<T extends { dataKey?: unknown }>(\n payload: readonly T[] | undefined\n): T[] | undefined {\n return payload?.filter(\n (item) => !String(item.dataKey).startsWith(BAND_FIELD_PREFIX)\n );\n}\n\ntype TooltipContentType = NonNullable<\n React.ComponentProps<typeof ChartTooltip>['content']\n>;\ntype TooltipContentFn = Extract<TooltipContentType, (...args: never[]) => unknown>;\ntype LegendContentFn = Extract<\n React.ComponentProps<typeof ChartLegend>['content'],\n (...args: never[]) => unknown\n>;\ntype LegendContentProps = Parameters<LegendContentFn>[0];\ntype TooltipRenderProps = Parameters<TooltipContentFn>[0];\n\n/**\n * Wrap a caller-supplied `tooltipContent` so the synthetic delta bands are\n * stripped from the payload before it renders, while preserving recharts' own\n * mount semantics — a function is mounted via `createElement` (its own component\n * identity + hook state), an element via `cloneElement`. This mirrors what the\n * pass-through charts get by handing `content` straight to recharts, so a\n * function-form tooltip keeps its state across re-renders here too. The returned\n * component is a stable content type (memoize it on `tooltipContent`).\n */\nexport function createBandStrippedTooltip(tooltipContent: TooltipContentType) {\n return function BandStrippedTooltip(props: TooltipRenderProps) {\n const merged = {\n ...props,\n payload: dropBandSeries(props.payload),\n } as TooltipRenderProps;\n if (typeof tooltipContent === 'function') {\n const Comp = tooltipContent as React.FunctionComponent<TooltipRenderProps>;\n return <Comp {...merged} />;\n }\n // Element-form tooltip: cloneElement is the React-idiomatic way to\n // re-render an existing element with new props (preserves refs & keys).\n return React.cloneElement(tooltipContent, merged);\n };\n}\n\nfunction createProjectionTooltip(tooltipContent: TooltipContentType) {\n return function ProjectionTooltip(props: TooltipRenderProps) {\n const merged = {\n ...props,\n payload: dropProjectionPayload(props.payload),\n } as TooltipRenderProps;\n if (typeof tooltipContent === 'function') {\n const Comp = tooltipContent as React.FunctionComponent<TooltipRenderProps>;\n return <Comp {...merged} />;\n }\n // Element-form tooltip: cloneElement is the React-idiomatic way to\n // re-render an existing element with new props (preserves refs & keys).\n return React.cloneElement(tooltipContent, merged);\n };\n}\n\n/**\n * Style override for one series, keyed by its `dataKeys` entry. Anything left\n * unset falls back to the chart-wide prop, so an entry only states what differs\n * — e.g. one thicker, dashed \"target\" line among otherwise identical series.\n *\n * A series listed in `comparisonKeys` keeps its dashed, dimmed, dot-less\n * treatment: `color`, `strokeWidth` and `curveType` still apply to it, but\n * `showDots` / `dotSize` do not (the overlay is defined by having no dots).\n */\nexport interface LineChartLineSettings {\n /** Stroke color. Defaults to the series' `config` color. */\n color?: string;\n /** Stroke width. Defaults to the chart's `strokeWidth`. */\n strokeWidth?: number;\n /** Dash this series' stroke, whatever the chart-wide `lineStyle` is. */\n dashed?: boolean;\n /** Interpolation for this series only. Defaults to the chart's `curve`. */\n curveType?: ChartCurveType;\n /** Render this series' per-point dots. Defaults to the chart's `showDots`. */\n showDots?: boolean;\n /** Dot radius for this series, in px. Defaults to the chart's `dotSize`. */\n dotSize?: number;\n /** Show/hide this series' value labels, overriding the chart's `showLabels`. */\n showLabel?: boolean;\n /** Position of this series' value labels. Defaults to the chart's `labelPosition`. */\n labelPosition?: CartesianLabelPosition;\n}\n\nexport interface LineChartProps\n extends Omit<React.ComponentProps<'div'>, 'children'>,\n VariantProps<typeof lineChartVariants>,\n CartesianChartProps,\n ChartAnimationProps,\n ChartBrushProps,\n ChartDataLabelProps {\n /**\n * The dataviz palette this chart's series are painted from. Series that\n * state no `color` of their own take a stop of it. See `ChartPalette`.\n */\n palette?: ChartPalette;\n /** Row-per-point data. Each object holds the category key + one numeric field per series (`null` breaks the line unless `connectNulls`). */\n data: ReadonlyArray<Record<string, string | number | null>>;\n /**\n * Per-series map of `label` / `icon` / `tone` (from the shared `Chart`\n * primitives). Series take their colour from the container's `palette`; each\n * entry maps a key to a `label` and an optional `tone`.\n */\n config: ChartConfig;\n /** Series to plot — one `<Line>` per key. Each must exist in `config` and in every data row. */\n dataKeys: string[];\n /**\n * Subset of `dataKeys` to render as comparison/trend overlays (e.g. a\n * previous quarter or year) — dashed, dimmed, and dot-less, so they read as\n * secondary to the current-period lines. Keeps each series' own `config` color.\n */\n comparisonKeys?: string[];\n /**\n * Pairs of `[currentKey, comparisonKey]` to shade a delta band between — a\n * dimmed area filling the gap between the two series at each point,\n * visualizing the QoQ/YoY difference. Tinted with the first key's `config`\n * color. Rows where either value isn't numeric are left un-banded.\n *\n * Each pair mints an internal `__band_<n>` field (kept out of the tooltip and\n * legend), so avoid data/`config` keys with that reserved prefix.\n */\n deltaBands?: Array<[string, string]>;\n /** Category axis key (the shared dimension across rows, e.g. `\"month\"`). */\n xKey: string;\n /** Stroke width of each line. */\n strokeWidth?: number;\n /** Render a dot at each data point. */\n showDots?: boolean;\n /** Radius of each point's dot, in px. Its hover dot is 2px larger. */\n dotSize?: number;\n /**\n * Enlarge the hovered point's dot. Defaults to following `showDots`, so pass\n * it explicitly to get hover dots on a dot-less line (`showDots={false}`\n * with `showActiveDot`) or a static-only line (dots without the hover one).\n */\n showActiveDot?: boolean;\n /** Bridge `null` gaps in the data instead of breaking the line. */\n connectNulls?: boolean;\n /**\n * Per-series style overrides, keyed by `dataKeys` entry. Series with no entry\n * render from the chart-wide props.\n */\n lineSettings?: Record<string, LineChartLineSettings>;\n /**\n * One or more dashed reference/average lines on the value axis — a target, a\n * threshold, or a series mean. Pass a single object or an array to draw\n * several at once.\n */\n referenceLine?: ChartReferenceLine | ChartReferenceLine[];\n /**\n * The category value (matching an `xKey` entry) at which the projection\n * zone starts. Ticks from this point onward render in the disabled text\n * color (`--ui-text-on-surface-disabled`). The chart data and series\n * themselves are not affected — only the X-axis tick appearance changes.\n *\n * Use this when displaying a forecast/projection range where future\n * data points should be visually de-emphasized on the axis.\n */\n projectionStart?: string | number;\n showLegend?: boolean;\n /** Position of the value labels when `showLabels` is on. Defaults to `top`. */\n labelPosition?: CartesianLabelPosition;\n}\n\nconst LineChart = React.forwardRef<HTMLDivElement, LineChartProps>(\n (\n {\n className,\n config,\n palette,\n data,\n dataKeys,\n comparisonKeys,\n deltaBands,\n xKey,\n xAxisLabel,\n yAxisLabel,\n yUnit,\n curve = 'monotone',\n lineStyle = 'solid',\n strokeWidth = 2,\n showDots = false,\n dotSize = 3,\n showActiveDot,\n connectNulls = false,\n lineSettings,\n referenceLine,\n projectionStart,\n showGrid = true,\n showTooltip = true,\n showLegend = true,\n showXAxis = true,\n showYAxis = true,\n xTickFormatter,\n yTickFormatter,\n xAxisAngle,\n xAxisInterval,\n yAxisTickCount,\n yAxisDomain,\n gridDashed,\n gridHorizontal,\n gridVertical,\n tooltipContent,\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n showLabels = false,\n labelPosition,\n labelFormatter,\n showBrush = false,\n brushHeight,\n brushAriaLabel,\n ...props\n },\n ref\n ) => {\n const animation = resolveAnimation({\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n });\n const lineLabelPosition = labelPosition ?? 'top';\n const yDomain = resolveAxisDomain(yAxisDomain);\n const referenceLines = toReferenceLineList(referenceLine);\n\n // A series' own `color` and `curveType` overrides also drive its delta band,\n // so the band keeps following the line it belongs to instead of the\n // chart-wide defaults.\n const colorFor = (key: string) =>\n lineSettings?.[key]?.color ?? `var(--color-${key})`;\n const curveFor = (key: string) =>\n lineSettings?.[key]?.curveType ?? curve ?? 'monotone';\n\n // The plot inset is needed as soon as *any* series carries outside labels —\n // a per-series `showLabel` counts, not only the chart-wide `showLabels`.\n const hasLabels =\n showLabels ||\n Object.values(lineSettings ?? {}).some((settings) => settings.showLabel);\n\n const xAxisHeight = resolveXAxisHeight(xAxisLabel, xAxisAngle);\n\n const projStartIndex = React.useMemo(() => {\n if (projectionStart === undefined) return -1;\n return data.findIndex((row) => row[xKey] === projectionStart);\n }, [projectionStart, data, xKey]);\n // projStartIndex === 0 (boundary at the first tick) is excluded: there is no\n // \"previous tick\" to define the clip midpoint, so clipPath defs can't be\n // emitted, which would make all series invisible. Treat it as no-projection.\n const hasProjection = projStartIndex > 0;\n\n // Build the projection tick renderer when projectionStart is set. Each tick\n // past (and including) the start value renders in the disabled text color;\n // ticks before it stay in the muted-foreground color. Returns undefined when\n // projectionStart is absent or not found so recharts uses its own renderer.\n const projectionTick = React.useMemo(() => {\n if (!hasProjection) return undefined;\n const projectedValues = new Set(\n data.slice(projStartIndex).map((row) => row[xKey])\n );\n const ProjectionTick = ({\n payload,\n index,\n ...tickProps\n }: {\n payload: { value: string | number };\n index?: number;\n [key: string]: unknown;\n }) => (\n <Text\n {...(tickProps as React.ComponentProps<typeof Text>)}\n fontSize={12}\n className={\n projectedValues.has(payload.value)\n ? 'fill-[var(--ui-text-on-surface-disabled)]'\n : 'fill-muted-foreground'\n }\n >\n {xTickFormatter\n ? xTickFormatter(payload.value as never, index)\n : payload.value}\n </Text>\n );\n return ProjectionTick;\n }, [hasProjection, projStartIndex, data, xKey, xTickFormatter]);\n\n // When projection is active, copy each series value to `_proj_${key}` — same\n // data, no nulls. Both the actual and projection <Line> see the identical curve;\n // the clipPath (computed in ProjectionClip) restricts which half each paints.\n const projectionData = React.useMemo(() => {\n if (!hasProjection) return data;\n return data.map((row) => ({\n ...row,\n ...Object.fromEntries(\n dataKeys.map((k) => [`_proj_${k}`, row[k]])\n ),\n }));\n }, [hasProjection, data, dataKeys]);\n\n // The tick immediately before the projection boundary — needed to compute the\n // visual midpoint where the clip edge and separator line are placed.\n const prevTickValue = React.useMemo(() => {\n if (projStartIndex <= 0) return undefined;\n return data[projStartIndex - 1]?.[xKey];\n }, [projStartIndex, data, xKey]);\n\n const clipId = `line-proj-${React.useId().replace(/:/g, '')}`;\n\n // Memoized so recharts sees a stable content type across renders — a fresh\n // wrapper each render would remount the caller's tooltip and reset its state.\n const customTooltip = React.useMemo(() => {\n if (!tooltipContent) return undefined;\n const bandStripped = createBandStrippedTooltip(tooltipContent);\n return hasProjection ? createProjectionTooltip(bandStripped) : bandStripped;\n }, [tooltipContent, hasProjection]);\n\n // Axis titles: the X title sits below the ticks; the Y title is rotated in\n // the left gutter. Passed to recharts' native `label` (themed via the\n // `.recharts-label` fill selector on the container).\n const xAxisTitle = resolveXAxisTitle(xAxisLabel);\n const yAxisTitle = resolveYAxisTitle(yAxisLabel);\n\n // Each delta band becomes a synthetic `[min, max]` range field per row that\n // a recharts <Area> shades. Rows where either series isn't numeric are left\n // un-banded (the area breaks there).\n const bands = (deltaBands ?? []).map(([current, comparison], index) => ({\n field: `${BAND_FIELD_PREFIX}${index}`,\n current,\n comparison,\n }));\n\n // A delta band is a synthetic `__band_*` series feeding an <Area>, not a\n // trend of its own — so it has to be stripped from the tooltip rows and the\n // legend entries before either is rendered. A caller's own tooltip is\n // already wrapped for that above; these cover the two default contents.\n const hasBands = bands.length > 0;\n const tooltipNode =\n customTooltip ??\n (hasBands || hasProjection ? (\n (props: TooltipRenderProps) => (\n <ChartTooltipContent\n active={props.active}\n label={props.label}\n payload={\n dropProjectionPayload(\n dropBandSeries(props.payload)\n ) as ChartTooltipContentProps['payload']\n }\n />\n )\n ) : (\n <ChartTooltipContent />\n ));\n const legendNode = hasBands || hasProjection ? (\n (props: LegendContentProps) => (\n <ChartLegendContent\n verticalAlign={props.verticalAlign}\n payload={\n dropProjectionPayload(\n dropBandSeries(props.payload)\n ) as ChartLegendContentProps['payload']\n }\n />\n )\n ) : (\n <ChartLegendContent />\n );\n // Bands are computed over the full (projection-augmented) rows; when\n // projection is active the band <Area> is clipped to the actual zone.\n const chartData = bands.length\n ? projectionData.map((row) => {\n const augmented: Record<string, unknown> = { ...row };\n for (const { field, current, comparison } of bands) {\n const a = row[current];\n const b = row[comparison];\n augmented[field] =\n typeof a === 'number' && typeof b === 'number'\n ? [Math.min(a, b), Math.max(a, b)]\n : undefined;\n }\n return augmented;\n })\n : projectionData;\n\n // Only a delta band needs an <Area>, which recharts renders under\n // ComposedChart, not LineChart. Escalate to ComposedChart only then, so\n // plain line charts keep the LineChart base (and their baselines) untouched.\n const RootChart = bands.length > 0 ? ComposedChart : RechartsLineChart;\n\n return (\n <div\n ref={ref}\n data-curve={curve}\n data-line-style={lineStyle}\n className={cn(lineChartVariants({ curve, lineStyle }), className)}\n {...props}\n >\n <ChartContainer\n config={config}\n palette={palette}\n className=\"size-full [&_.recharts-label]:fill-foreground\"\n >\n <RootChart\n data={chartData as readonly unknown[]}\n margin={hasLabels ? CHART_LABEL_MARGIN : undefined}\n >\n {showGrid && (\n <CartesianGrid\n horizontal={gridHorizontal ?? true}\n vertical={gridVertical ?? false}\n strokeDasharray={gridDashed ? '3 3' : undefined}\n />\n )}\n <XAxis\n dataKey={xKey}\n type=\"category\"\n hide={!showXAxis}\n tickLine={false}\n axisLine={false}\n tickMargin={8}\n tickFormatter={projectionTick ? undefined : xTickFormatter}\n tick={projectionTick}\n angle={xAxisAngle}\n interval={xAxisInterval}\n textAnchor={resolveRotatedTickAnchor(xAxisAngle)}\n height={xAxisHeight}\n label={xAxisTitle}\n />\n <YAxis\n type=\"number\"\n hide={!showYAxis}\n tickLine={false}\n axisLine={false}\n unit={yUnit}\n tickFormatter={yTickFormatter}\n tickCount={yAxisTickCount}\n domain={yDomain}\n width={yAxisLabel ? 72 : undefined}\n label={yAxisTitle}\n />\n {showTooltip && <ChartTooltip content={tooltipNode} />}\n {showLegend && <ChartLegend content={legendNode} />}\n {hasProjection && prevTickValue != null && (\n <ProjectionClip\n projectionStart={projectionStart!}\n prevTick={prevTickValue}\n clipId={clipId}\n dataKeys={dataKeys}\n />\n )}\n {/* Delta bands render before the lines so the lines draw on top.\n When projection is active, clip bands to the actual zone — the\n comparison series they measure against is absent past the\n boundary, so an unclipped band would shade a region with no\n visible far edge. */}\n {bands.map(({ field, current }) => (\n <Area\n key={field}\n dataKey={field}\n // An <Area> takes one interpolation, so a pair whose two series\n // set different `curveType`s can only follow the current one.\n type={curveFor(current)}\n stroke=\"none\"\n fill={colorFor(current)}\n fillOpacity={0.12}\n connectNulls={connectNulls}\n dot={false}\n activeDot={false}\n clipPath={hasProjection ? `url(#${clipId}-actual)` : undefined}\n {...animation}\n legendType=\"none\"\n tooltipType=\"none\"\n />\n ))}\n {dataKeys.map((key, index) => {\n // Comparison series read as secondary: always dashed, dimmed, and\n // dot-less, regardless of the global lineStyle / showDots.\n const isComparison = comparisonKeys?.includes(key);\n const settings = lineSettings?.[key];\n const seriesDots =\n !isComparison && (settings?.showDots ?? showDots);\n const dotRadius = settings?.dotSize ?? dotSize;\n // The hover dot follows the static dots unless asked otherwise, so\n // a chart that sets neither renders exactly as before.\n const activeDot = !isComparison && (showActiveDot ?? seriesDots);\n const seriesDashed = settings?.dashed ?? lineStyle === 'dashed';\n const seriesLabel = settings?.showLabel ?? showLabels;\n const seriesLabelPosition =\n settings?.labelPosition ?? lineLabelPosition;\n // Comparison series have no projection counterpart — they render\n // unclipped across the full width so they don't vanish at the\n // projection boundary.\n return (\n <Line\n key={key}\n className={hasProjection && !isComparison ? `actual-${clipId}-${index}` : undefined}\n type={curveFor(key)}\n dataKey={key}\n stroke={colorFor(key)}\n strokeWidth={settings?.strokeWidth ?? strokeWidth}\n strokeDasharray={isComparison || seriesDashed ? '5 5' : undefined}\n strokeOpacity={isComparison ? 0.5 : undefined}\n dot={seriesDots ? { r: dotRadius } : false}\n activeDot={activeDot ? { r: dotRadius + 2 } : false}\n connectNulls={connectNulls}\n {...animation}\n >\n {seriesLabel && (\n <LabelList\n dataKey={key}\n position={seriesLabelPosition}\n formatter={toLabelFormatter(labelFormatter)}\n className={resolveLabelFillClass(seriesLabelPosition)}\n fontSize={CHART_LABEL_FONT_SIZE}\n />\n )}\n </Line>\n );\n })}\n {hasProjection &&\n dataKeys.map((key, index) => {\n const isComparison = comparisonKeys?.includes(key);\n if (isComparison) return null;\n const settings = lineSettings?.[key];\n return (\n <Line\n key={`_proj_${key}`}\n className={`proj-${clipId}-${index}`}\n type={curveFor(key)}\n dataKey={`_proj_${key}`}\n name={key}\n stroke={colorFor(key)}\n strokeWidth={settings?.strokeWidth ?? strokeWidth}\n strokeDasharray=\"5 5\"\n dot={false}\n activeDot={false}\n legendType=\"none\"\n tooltipType=\"none\"\n {...animation}\n />\n );\n })}\n {/* Annotations draw over the series they describe. */}\n {referenceLines.map((ref, index) => {\n const value = resolveChartReferenceValue(ref, data, dataKeys);\n if (value === undefined) return null;\n return (\n <ReferenceLine\n key={`${ref.label ?? 'ref'}-${index}`}\n y={value}\n {...resolveReferenceLineProps(ref.label, ref.labelPosition)}\n />\n );\n })}\n {showBrush && (\n <Brush\n dataKey={xKey}\n tickFormatter={xTickFormatter}\n {...resolveBrushProps({ brushHeight, brushAriaLabel })}\n />\n )}\n </RootChart>\n </ChartContainer>\n </div>\n );\n }\n);\nLineChart.displayName = 'LineChart';\n\nexport { LineChart, lineChartVariants };\n"],"mappings":"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"}
1
+ {"version":3,"file":"line-chart.js","names":[],"sources":["../../../../src/components/ui/line-chart/line-chart.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n Area,\n Brush,\n CartesianGrid,\n ComposedChart,\n LabelList,\n Line,\n LineChart as RechartsLineChart,\n ReferenceLine,\n Text,\n XAxis,\n YAxis,\n useXAxisScale,\n usePlotArea,\n} from 'recharts';\n\nimport { cn } from '@/lib/utils';\nimport {\n CHART_LABEL_FONT_SIZE,\n CHART_LABEL_MARGIN,\n ChartContainer,\n ChartLegend,\n ChartLegendContent,\n ChartTooltip,\n ChartTooltipContent,\n resolveAnimation,\n resolveAxisDomain,\n resolveBrushProps,\n dropProjectionPayload,\n resolveChartReferenceValue,\n resolveLabelFillClass,\n resolveReferenceLineProps,\n resolveRotatedTickAnchor,\n resolveXAxisHeight,\n resolveXAxisTitle,\n resolveYAxisTitle,\n toLabelFormatter,\n toReferenceLineList,\n type ChartConfig,\n type ChartPalette,\n type ChartLegendContentProps,\n type ChartTooltipContentProps,\n type CartesianChartProps,\n type ChartAnimationProps,\n type ChartBrushProps,\n type ChartCurveType,\n type ChartDataLabelProps,\n toCssKey,\n type ChartReferenceLine,\n type CartesianLabelPosition,\n} from '../chart';\n\n// A typed recharts composition over the shared `Chart` primitives. The two CVA\n// axes are the design's Line-chart variant set: `curve` (how the segments\n// interpolate between points — straight, smoothed, or stepped; the seven values\n// are documented on the shared `ChartCurveType`) and `lineStyle` (a solid or\n// dashed stroke). \"single\" vs \"multi\" line is not a variant — it\n// falls out of how many `dataKeys` the caller plots. The classes stay empty\n// because recharts' SVG — not CSS — draws the lines: `curve` drives each\n// `<Line type>` and `lineStyle` drives its `strokeDasharray`. CVA is kept so the\n// variant set is a first-class, spec-conformant part of the API (matched against\n// ui-spec's api.yaml enums) and exposed via `VariantProps`; the resolved values\n// are also mirrored onto `data-curve` / `data-line-style` for styling hooks and\n// tests.\nconst lineChartVariants = cva('', {\n variants: {\n curve: {\n linear: '',\n monotone: '',\n natural: '',\n basis: '',\n step: '',\n stepBefore: '',\n stepAfter: '',\n },\n lineStyle: {\n solid: '',\n dashed: '',\n },\n },\n defaultVariants: {\n curve: 'monotone',\n lineStyle: 'solid',\n },\n});\n\n// Renders two <clipPath> defs (actual / projection), a CSS <style> that clips\n// each recharts Line layer to its half, and a dashed separator line at the\n// visual midpoint between the last actual tick and the first projection tick.\n//\n// recharts' StaticCurve explicitly overwrites any clipPath prop passed to\n// <Line> with its own plot-area URL, so the prop-based approach used by\n// <Area> cannot be used here. Instead we apply CSS clip-path to the outer <g>\n// that recharts wraps each Line series in (targeted via a unique className we\n// set on the <Line> component). CSS clip-path on a class selector (specificity\n// 0,1,0) beats the SVG presentation attribute recharts sets on the inner\n// <path> (specificity 0), so the two clips compose: outer = our half, inner =\n// recharts' plot-area — net effect is the correct half within the plot area.\nfunction ProjectionClip({\n projectionStart,\n prevTick,\n clipId,\n dataKeys,\n}: {\n projectionStart: string | number;\n prevTick: string | number;\n clipId: string;\n dataKeys: string[];\n}) {\n const xScale = useXAxisScale();\n const plotArea = usePlotArea();\n if (!xScale || !plotArea) return null;\n const prevX = xScale(prevTick as string);\n const currX = xScale(projectionStart as string);\n if (prevX == null || currX == null) return null;\n const midX = (prevX + currX) / 2;\n const { x, y, width, height } = plotArea;\n const css = dataKeys\n .map(\n (_, i) =>\n `.actual-${clipId}-${i}{clip-path:url(#${clipId}-actual)}` +\n `.proj-${clipId}-${i}{clip-path:url(#${clipId}-projection)}`\n )\n .join('');\n return (\n <>\n <defs>\n <clipPath id={`${clipId}-actual`}>\n <rect x={x} y={y} width={midX - x} height={height} />\n </clipPath>\n <clipPath id={`${clipId}-projection`}>\n <rect x={midX} y={y} width={x + width - midX} height={height} />\n </clipPath>\n </defs>\n <style>{css}</style>\n <line\n x1={midX}\n y1={y}\n x2={midX}\n y2={y + height}\n stroke=\"var(--ui-border-on-surface-border)\"\n strokeDasharray=\"4 4\"\n />\n </>\n );\n}\n\n// Reserved field prefix for the synthetic delta-band range series. Each band\n// mints one `__band_<n>` field that feeds an <Area>; it must never surface in\n// the tooltip or legend (those describe only real, caller-supplied series).\nconst BAND_FIELD_PREFIX = '__band_';\n\n/**\n * Drop the synthetic delta-band range series from a recharts tooltip/legend\n * payload, keeping the real series (and their order). recharts already excludes\n * the band via `legendType`/`tooltipType=\"none\"`, so this is a second, explicit\n * guard — hence it's unit-tested rather than relying on that behavior.\n */\nexport function dropBandSeries<T extends { dataKey?: unknown }>(\n payload: readonly T[] | undefined\n): T[] | undefined {\n return payload?.filter(\n (item) => !String(item.dataKey).startsWith(BAND_FIELD_PREFIX)\n );\n}\n\ntype TooltipContentType = NonNullable<\n React.ComponentProps<typeof ChartTooltip>['content']\n>;\ntype TooltipContentFn = Extract<TooltipContentType, (...args: never[]) => unknown>;\ntype LegendContentFn = Extract<\n React.ComponentProps<typeof ChartLegend>['content'],\n (...args: never[]) => unknown\n>;\ntype LegendContentProps = Parameters<LegendContentFn>[0];\ntype TooltipRenderProps = Parameters<TooltipContentFn>[0];\n\n/**\n * Wrap a caller-supplied `tooltipContent` so the synthetic delta bands are\n * stripped from the payload before it renders, while preserving recharts' own\n * mount semantics — a function is mounted via `createElement` (its own component\n * identity + hook state), an element via `cloneElement`. This mirrors what the\n * pass-through charts get by handing `content` straight to recharts, so a\n * function-form tooltip keeps its state across re-renders here too. The returned\n * component is a stable content type (memoize it on `tooltipContent`).\n */\nexport function createBandStrippedTooltip(tooltipContent: TooltipContentType) {\n return function BandStrippedTooltip(props: TooltipRenderProps) {\n const merged = {\n ...props,\n payload: dropBandSeries(props.payload),\n } as TooltipRenderProps;\n if (typeof tooltipContent === 'function') {\n const Comp = tooltipContent as React.FunctionComponent<TooltipRenderProps>;\n return <Comp {...merged} />;\n }\n // Element-form tooltip: cloneElement is the React-idiomatic way to\n // re-render an existing element with new props (preserves refs & keys).\n return React.cloneElement(tooltipContent, merged);\n };\n}\n\nfunction createProjectionTooltip(tooltipContent: TooltipContentType) {\n return function ProjectionTooltip(props: TooltipRenderProps) {\n const merged = {\n ...props,\n payload: dropProjectionPayload(props.payload),\n } as TooltipRenderProps;\n if (typeof tooltipContent === 'function') {\n const Comp = tooltipContent as React.FunctionComponent<TooltipRenderProps>;\n return <Comp {...merged} />;\n }\n // Element-form tooltip: cloneElement is the React-idiomatic way to\n // re-render an existing element with new props (preserves refs & keys).\n return React.cloneElement(tooltipContent, merged);\n };\n}\n\n/**\n * Style override for one series, keyed by its `dataKeys` entry. Anything left\n * unset falls back to the chart-wide prop, so an entry only states what differs\n * — e.g. one thicker, dashed \"target\" line among otherwise identical series.\n *\n * A series listed in `comparisonKeys` keeps its dashed, dimmed, dot-less\n * treatment: `color`, `strokeWidth` and `curveType` still apply to it, but\n * `showDots` / `dotSize` do not (the overlay is defined by having no dots).\n */\nexport interface LineChartLineSettings {\n /** Stroke color. Defaults to the series' `config` color. */\n color?: string;\n /** Stroke width. Defaults to the chart's `strokeWidth`. */\n strokeWidth?: number;\n /** Dash this series' stroke, whatever the chart-wide `lineStyle` is. */\n dashed?: boolean;\n /** Interpolation for this series only. Defaults to the chart's `curve`. */\n curveType?: ChartCurveType;\n /** Render this series' per-point dots. Defaults to the chart's `showDots`. */\n showDots?: boolean;\n /** Dot radius for this series, in px. Defaults to the chart's `dotSize`. */\n dotSize?: number;\n /** Show/hide this series' value labels, overriding the chart's `showLabels`. */\n showLabel?: boolean;\n /** Position of this series' value labels. Defaults to the chart's `labelPosition`. */\n labelPosition?: CartesianLabelPosition;\n}\n\nexport interface LineChartProps\n extends Omit<React.ComponentProps<'div'>, 'children'>,\n VariantProps<typeof lineChartVariants>,\n CartesianChartProps,\n ChartAnimationProps,\n ChartBrushProps,\n ChartDataLabelProps {\n /**\n * The dataviz palette this chart's series are painted from. Series that\n * state no `color` of their own take a stop of it. See `ChartPalette`.\n */\n palette?: ChartPalette;\n /** Row-per-point data. Each object holds the category key + one numeric field per series (`null` breaks the line unless `connectNulls`). */\n data: ReadonlyArray<Record<string, string | number | null>>;\n /**\n * Per-series map of `label` / `icon` / `tone` (from the shared `Chart`\n * primitives). Series take their colour from the container's `palette`; each\n * entry maps a key to a `label` and an optional `tone`.\n */\n config: ChartConfig;\n /** Series to plot — one `<Line>` per key. Each must exist in `config` and in every data row. */\n dataKeys: string[];\n /**\n * Subset of `dataKeys` to render as comparison/trend overlays (e.g. a\n * previous quarter or year) — dashed, dimmed, and dot-less, so they read as\n * secondary to the current-period lines. Keeps each series' own `config` color.\n */\n comparisonKeys?: string[];\n /**\n * Pairs of `[currentKey, comparisonKey]` to shade a delta band between — a\n * dimmed area filling the gap between the two series at each point,\n * visualizing the QoQ/YoY difference. Tinted with the first key's `config`\n * color. Rows where either value isn't numeric are left un-banded.\n *\n * Each pair mints an internal `__band_<n>` field (kept out of the tooltip and\n * legend), so avoid data/`config` keys with that reserved prefix.\n */\n deltaBands?: Array<[string, string]>;\n /** Category axis key (the shared dimension across rows, e.g. `\"month\"`). */\n xKey: string;\n /** Stroke width of each line. */\n strokeWidth?: number;\n /** Render a dot at each data point. */\n showDots?: boolean;\n /** Radius of each point's dot, in px. Its hover dot is 2px larger. */\n dotSize?: number;\n /**\n * Enlarge the hovered point's dot. Defaults to following `showDots`, so pass\n * it explicitly to get hover dots on a dot-less line (`showDots={false}`\n * with `showActiveDot`) or a static-only line (dots without the hover one).\n */\n showActiveDot?: boolean;\n /** Bridge `null` gaps in the data instead of breaking the line. */\n connectNulls?: boolean;\n /**\n * Per-series style overrides, keyed by `dataKeys` entry. Series with no entry\n * render from the chart-wide props.\n */\n lineSettings?: Record<string, LineChartLineSettings>;\n /**\n * One or more dashed reference/average lines on the value axis — a target, a\n * threshold, or a series mean. Pass a single object or an array to draw\n * several at once.\n */\n referenceLine?: ChartReferenceLine | ChartReferenceLine[];\n /**\n * The category value (matching an `xKey` entry) at which the projection\n * zone starts. Ticks from this point onward render in the disabled text\n * color (`--ui-text-on-surface-disabled`). The chart data and series\n * themselves are not affected — only the X-axis tick appearance changes.\n *\n * Use this when displaying a forecast/projection range where future\n * data points should be visually de-emphasized on the axis.\n */\n projectionStart?: string | number;\n showLegend?: boolean;\n /** Position of the value labels when `showLabels` is on. Defaults to `top`. */\n labelPosition?: CartesianLabelPosition;\n}\n\nconst LineChart = React.forwardRef<HTMLDivElement, LineChartProps>(\n (\n {\n className,\n config,\n palette,\n data,\n dataKeys,\n comparisonKeys,\n deltaBands,\n xKey,\n xAxisLabel,\n yAxisLabel,\n yUnit,\n curve = 'monotone',\n lineStyle = 'solid',\n strokeWidth = 2,\n showDots = false,\n dotSize = 3,\n showActiveDot,\n connectNulls = false,\n lineSettings,\n referenceLine,\n projectionStart,\n showGrid = true,\n showTooltip = true,\n showLegend = true,\n showXAxis = true,\n showYAxis = true,\n xTickFormatter,\n yTickFormatter,\n xAxisAngle,\n xAxisInterval,\n yAxisTickCount,\n yAxisDomain,\n gridDashed,\n gridHorizontal,\n gridVertical,\n tooltipContent,\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n showLabels = false,\n labelPosition,\n labelFormatter,\n showBrush = false,\n brushHeight,\n brushAriaLabel,\n ...props\n },\n ref\n ) => {\n const animation = resolveAnimation({\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n });\n const lineLabelPosition = labelPosition ?? 'top';\n const yDomain = resolveAxisDomain(yAxisDomain);\n const referenceLines = toReferenceLineList(referenceLine);\n\n // A series' own `color` and `curveType` overrides also drive its delta band,\n // so the band keeps following the line it belongs to instead of the\n // chart-wide defaults.\n const colorFor = (key: string) =>\n lineSettings?.[key]?.color ?? `var(--color-${toCssKey(key)})`;\n const curveFor = (key: string) =>\n lineSettings?.[key]?.curveType ?? curve ?? 'monotone';\n\n // The plot inset is needed as soon as *any* series carries outside labels —\n // a per-series `showLabel` counts, not only the chart-wide `showLabels`.\n const hasLabels =\n showLabels ||\n Object.values(lineSettings ?? {}).some((settings) => settings.showLabel);\n\n const xAxisHeight = resolveXAxisHeight(xAxisLabel, xAxisAngle);\n\n const projStartIndex = React.useMemo(() => {\n if (projectionStart === undefined) return -1;\n return data.findIndex((row) => row[xKey] === projectionStart);\n }, [projectionStart, data, xKey]);\n // projStartIndex === 0 (boundary at the first tick) is excluded: there is no\n // \"previous tick\" to define the clip midpoint, so clipPath defs can't be\n // emitted, which would make all series invisible. Treat it as no-projection.\n const hasProjection = projStartIndex > 0;\n\n // Build the projection tick renderer when projectionStart is set. Each tick\n // past (and including) the start value renders in the disabled text color;\n // ticks before it stay in the muted-foreground color. Returns undefined when\n // projectionStart is absent or not found so recharts uses its own renderer.\n const projectionTick = React.useMemo(() => {\n if (!hasProjection) return undefined;\n const projectedValues = new Set(\n data.slice(projStartIndex).map((row) => row[xKey])\n );\n const ProjectionTick = ({\n payload,\n index,\n ...tickProps\n }: {\n payload: { value: string | number };\n index?: number;\n [key: string]: unknown;\n }) => (\n <Text\n {...(tickProps as React.ComponentProps<typeof Text>)}\n fontSize={12}\n className={\n projectedValues.has(payload.value)\n ? 'fill-[var(--ui-text-on-surface-disabled)]'\n : 'fill-muted-foreground'\n }\n >\n {xTickFormatter\n ? xTickFormatter(payload.value as never, index)\n : payload.value}\n </Text>\n );\n return ProjectionTick;\n }, [hasProjection, projStartIndex, data, xKey, xTickFormatter]);\n\n // When projection is active, copy each series value to `_proj_${key}` — same\n // data, no nulls. Both the actual and projection <Line> see the identical curve;\n // the clipPath (computed in ProjectionClip) restricts which half each paints.\n const projectionData = React.useMemo(() => {\n if (!hasProjection) return data;\n return data.map((row) => ({\n ...row,\n ...Object.fromEntries(\n dataKeys.map((k) => [`_proj_${k}`, row[k]])\n ),\n }));\n }, [hasProjection, data, dataKeys]);\n\n // The tick immediately before the projection boundary — needed to compute the\n // visual midpoint where the clip edge and separator line are placed.\n const prevTickValue = React.useMemo(() => {\n if (projStartIndex <= 0) return undefined;\n return data[projStartIndex - 1]?.[xKey];\n }, [projStartIndex, data, xKey]);\n\n const clipId = `line-proj-${React.useId().replace(/:/g, '')}`;\n\n // Memoized so recharts sees a stable content type across renders — a fresh\n // wrapper each render would remount the caller's tooltip and reset its state.\n const customTooltip = React.useMemo(() => {\n if (!tooltipContent) return undefined;\n const bandStripped = createBandStrippedTooltip(tooltipContent);\n return hasProjection ? createProjectionTooltip(bandStripped) : bandStripped;\n }, [tooltipContent, hasProjection]);\n\n // Axis titles: the X title sits below the ticks; the Y title is rotated in\n // the left gutter. Passed to recharts' native `label` (themed via the\n // `.recharts-label` fill selector on the container).\n const xAxisTitle = resolveXAxisTitle(xAxisLabel);\n const yAxisTitle = resolveYAxisTitle(yAxisLabel);\n\n // Each delta band becomes a synthetic `[min, max]` range field per row that\n // a recharts <Area> shades. Rows where either series isn't numeric are left\n // un-banded (the area breaks there).\n const bands = (deltaBands ?? []).map(([current, comparison], index) => ({\n field: `${BAND_FIELD_PREFIX}${index}`,\n current,\n comparison,\n }));\n\n // A delta band is a synthetic `__band_*` series feeding an <Area>, not a\n // trend of its own — so it has to be stripped from the tooltip rows and the\n // legend entries before either is rendered. A caller's own tooltip is\n // already wrapped for that above; these cover the two default contents.\n const hasBands = bands.length > 0;\n const tooltipNode =\n customTooltip ??\n (hasBands || hasProjection ? (\n (props: TooltipRenderProps) => (\n <ChartTooltipContent\n active={props.active}\n label={props.label}\n payload={\n dropProjectionPayload(\n dropBandSeries(props.payload)\n ) as ChartTooltipContentProps['payload']\n }\n />\n )\n ) : (\n <ChartTooltipContent />\n ));\n const legendNode = hasBands || hasProjection ? (\n (props: LegendContentProps) => (\n <ChartLegendContent\n verticalAlign={props.verticalAlign}\n payload={\n dropProjectionPayload(\n dropBandSeries(props.payload)\n ) as ChartLegendContentProps['payload']\n }\n />\n )\n ) : (\n <ChartLegendContent />\n );\n // Bands are computed over the full (projection-augmented) rows; when\n // projection is active the band <Area> is clipped to the actual zone.\n const chartData = bands.length\n ? projectionData.map((row) => {\n const augmented: Record<string, unknown> = { ...row };\n for (const { field, current, comparison } of bands) {\n const a = row[current];\n const b = row[comparison];\n augmented[field] =\n typeof a === 'number' && typeof b === 'number'\n ? [Math.min(a, b), Math.max(a, b)]\n : undefined;\n }\n return augmented;\n })\n : projectionData;\n\n // Only a delta band needs an <Area>, which recharts renders under\n // ComposedChart, not LineChart. Escalate to ComposedChart only then, so\n // plain line charts keep the LineChart base (and their baselines) untouched.\n const RootChart = bands.length > 0 ? ComposedChart : RechartsLineChart;\n\n return (\n <div\n ref={ref}\n data-curve={curve}\n data-line-style={lineStyle}\n className={cn(lineChartVariants({ curve, lineStyle }), className)}\n {...props}\n >\n <ChartContainer\n config={config}\n palette={palette}\n className=\"size-full [&_.recharts-label]:fill-foreground\"\n >\n <RootChart\n data={chartData as readonly unknown[]}\n margin={hasLabels ? CHART_LABEL_MARGIN : undefined}\n >\n {showGrid && (\n <CartesianGrid\n horizontal={gridHorizontal ?? true}\n vertical={gridVertical ?? false}\n strokeDasharray={gridDashed ? '3 3' : undefined}\n />\n )}\n <XAxis\n dataKey={xKey}\n type=\"category\"\n hide={!showXAxis}\n tickLine={false}\n axisLine={false}\n tickMargin={8}\n tickFormatter={projectionTick ? undefined : xTickFormatter}\n tick={projectionTick}\n angle={xAxisAngle}\n interval={xAxisInterval}\n textAnchor={resolveRotatedTickAnchor(xAxisAngle)}\n height={xAxisHeight}\n label={xAxisTitle}\n />\n <YAxis\n type=\"number\"\n hide={!showYAxis}\n tickLine={false}\n axisLine={false}\n unit={yUnit}\n tickFormatter={yTickFormatter}\n tickCount={yAxisTickCount}\n domain={yDomain}\n width={yAxisLabel ? 72 : undefined}\n label={yAxisTitle}\n />\n {showTooltip && <ChartTooltip content={tooltipNode} />}\n {showLegend && <ChartLegend content={legendNode} />}\n {hasProjection && prevTickValue != null && (\n <ProjectionClip\n projectionStart={projectionStart!}\n prevTick={prevTickValue}\n clipId={clipId}\n dataKeys={dataKeys}\n />\n )}\n {/* Delta bands render before the lines so the lines draw on top.\n When projection is active, clip bands to the actual zone — the\n comparison series they measure against is absent past the\n boundary, so an unclipped band would shade a region with no\n visible far edge. */}\n {bands.map(({ field, current }) => (\n <Area\n key={field}\n dataKey={field}\n // An <Area> takes one interpolation, so a pair whose two series\n // set different `curveType`s can only follow the current one.\n type={curveFor(current)}\n stroke=\"none\"\n fill={colorFor(current)}\n fillOpacity={0.12}\n connectNulls={connectNulls}\n dot={false}\n activeDot={false}\n clipPath={hasProjection ? `url(#${clipId}-actual)` : undefined}\n {...animation}\n legendType=\"none\"\n tooltipType=\"none\"\n />\n ))}\n {dataKeys.map((key, index) => {\n // Comparison series read as secondary: always dashed, dimmed, and\n // dot-less, regardless of the global lineStyle / showDots.\n const isComparison = comparisonKeys?.includes(key);\n const settings = lineSettings?.[key];\n const seriesDots =\n !isComparison && (settings?.showDots ?? showDots);\n const dotRadius = settings?.dotSize ?? dotSize;\n // The hover dot follows the static dots unless asked otherwise, so\n // a chart that sets neither renders exactly as before.\n const activeDot = !isComparison && (showActiveDot ?? seriesDots);\n const seriesDashed = settings?.dashed ?? lineStyle === 'dashed';\n const seriesLabel = settings?.showLabel ?? showLabels;\n const seriesLabelPosition =\n settings?.labelPosition ?? lineLabelPosition;\n // Comparison series have no projection counterpart — they render\n // unclipped across the full width so they don't vanish at the\n // projection boundary.\n return (\n <Line\n key={key}\n className={hasProjection && !isComparison ? `actual-${clipId}-${index}` : undefined}\n type={curveFor(key)}\n dataKey={key}\n stroke={colorFor(key)}\n strokeWidth={settings?.strokeWidth ?? strokeWidth}\n strokeDasharray={isComparison || seriesDashed ? '5 5' : undefined}\n strokeOpacity={isComparison ? 0.5 : undefined}\n dot={seriesDots ? { r: dotRadius } : false}\n activeDot={activeDot ? { r: dotRadius + 2 } : false}\n connectNulls={connectNulls}\n {...animation}\n >\n {seriesLabel && (\n <LabelList\n dataKey={key}\n position={seriesLabelPosition}\n formatter={toLabelFormatter(labelFormatter)}\n className={resolveLabelFillClass(seriesLabelPosition)}\n fontSize={CHART_LABEL_FONT_SIZE}\n />\n )}\n </Line>\n );\n })}\n {hasProjection &&\n dataKeys.map((key, index) => {\n const isComparison = comparisonKeys?.includes(key);\n if (isComparison) return null;\n const settings = lineSettings?.[key];\n return (\n <Line\n key={`_proj_${key}`}\n className={`proj-${clipId}-${index}`}\n type={curveFor(key)}\n dataKey={`_proj_${key}`}\n name={key}\n stroke={colorFor(key)}\n strokeWidth={settings?.strokeWidth ?? strokeWidth}\n strokeDasharray=\"5 5\"\n dot={false}\n activeDot={false}\n legendType=\"none\"\n tooltipType=\"none\"\n {...animation}\n />\n );\n })}\n {/* Annotations draw over the series they describe. */}\n {referenceLines.map((ref, index) => {\n const value = resolveChartReferenceValue(ref, data, dataKeys);\n if (value === undefined) return null;\n return (\n <ReferenceLine\n key={`${ref.label ?? 'ref'}-${index}`}\n y={value}\n {...resolveReferenceLineProps(ref.label, ref.labelPosition)}\n />\n );\n })}\n {showBrush && (\n <Brush\n dataKey={xKey}\n tickFormatter={xTickFormatter}\n {...resolveBrushProps({ brushHeight, brushAriaLabel })}\n />\n )}\n </RootChart>\n </ChartContainer>\n </div>\n );\n }\n);\nLineChart.displayName = 'LineChart';\n\nexport { LineChart, lineChartVariants };\n"],"mappings":"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"}
@@ -2,24 +2,24 @@
2
2
  import { cn as e } from "../../../lib/utils.js";
3
3
  import { cva as t } from "../../../node_modules/.pnpm/class-variance-authority@0.7.1/node_modules/class-variance-authority/dist/index.js";
4
4
  import { CHART_DEFAULT_PALETTE as n } from "../chart/chart-palette.js";
5
- import { ChartContainer as r, ChartLegendContent as i, ChartTooltip as a, ChartTooltipContent as o, resolveChartColors as s } from "../chart/chart.js";
6
- import { CHART_LABEL_FILL_CLASS as c, resolveAnimation as l, resolveLabelFillClass as ee } from "../chart/chart-format.js";
7
- import * as u from "react";
8
- import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
9
- import { Cell as te, Curve as m, Label as ne, LabelList as re, Pie as ie, PieChart as ae } from "recharts";
5
+ import { ChartContainer as r, ChartLegendContent as i, ChartTooltip as a, ChartTooltipContent as o, resolveChartColors as s, toCssKey as c } from "../chart/chart.js";
6
+ import { CHART_LABEL_FILL_CLASS as l, resolveAnimation as u, resolveLabelFillClass as d } from "../chart/chart-format.js";
7
+ import * as f from "react";
8
+ import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
9
+ import { Cell as g, Curve as ee, Label as te, LabelList as ne, Pie as re, PieChart as ie } from "recharts";
10
10
  //#region src/components/ui/pie-chart/pie-chart.tsx
11
- var h = t("", {
11
+ var _ = t("", {
12
12
  variants: { shape: {
13
13
  pie: "",
14
14
  donut: ""
15
15
  } },
16
16
  defaultVariants: { shape: "pie" }
17
- }), g = (e) => typeof e == "number" && Number.isFinite(e) ? e : void 0, _ = (e, t) => {
18
- let n = g(e);
17
+ }), v = (e) => typeof e == "number" && Number.isFinite(e) ? e : void 0, y = (e, t) => {
18
+ let n = v(e);
19
19
  if (!(n === void 0 || t <= 0)) return `${(n / t * 100).toFixed(1)}%`;
20
20
  };
21
- function v(e) {
22
- let { format: t, name: n, value: r, total: i, formatter: a } = e, o = r == null ? "" : a ? a(r) : String(r), s = _(r, i);
21
+ function b(e) {
22
+ let { format: t, name: n, value: r, total: i, formatter: a } = e, o = r == null ? "" : a ? a(r) : String(r), s = y(r, i);
23
23
  switch (t) {
24
24
  case "name-value": return o ? `${n}: ${o}` : `${n}`;
25
25
  case "name-percent": return s ? `${n}: ${s}` : `${n}`;
@@ -27,48 +27,48 @@ function v(e) {
27
27
  default: return o;
28
28
  }
29
29
  }
30
- function y(e) {
30
+ function x(e) {
31
31
  let { config: t, total: n } = e;
32
32
  return function(e, r, i) {
33
- let a = t[String(r)], o = _(e, n), s = e?.toLocaleString() ?? "";
34
- return /* @__PURE__ */ p(d, { children: [a?.icon ? /* @__PURE__ */ f(a.icon, {}) : /* @__PURE__ */ f("div", {
33
+ let a = t[String(r)], o = y(e, n), s = e?.toLocaleString() ?? "";
34
+ return /* @__PURE__ */ h(p, { children: [a?.icon ? /* @__PURE__ */ m(a.icon, {}) : /* @__PURE__ */ m("div", {
35
35
  className: "h-2.5 w-2.5 shrink-0 rounded-full",
36
36
  style: { backgroundColor: i.payload?.fill ?? i.color }
37
- }), /* @__PURE__ */ p("div", {
37
+ }), /* @__PURE__ */ h("div", {
38
38
  className: "flex flex-1 items-center justify-between gap-4 leading-none",
39
- children: [/* @__PURE__ */ f("span", {
39
+ children: [/* @__PURE__ */ m("span", {
40
40
  className: "text-muted-foreground",
41
41
  children: a?.label ?? r
42
- }), /* @__PURE__ */ f("span", {
42
+ }), /* @__PURE__ */ m("span", {
43
43
  className: "font-medium tabular-nums text-foreground",
44
44
  children: o ? `${s} (${o})` : s
45
45
  })]
46
46
  })] });
47
47
  };
48
48
  }
49
- function b(e) {
49
+ function S(e) {
50
50
  let { nameKey: t, config: n, total: r } = e;
51
- return /* @__PURE__ */ f(o, {
51
+ return /* @__PURE__ */ m(o, {
52
52
  nameKey: t,
53
53
  hideLabel: !0,
54
- formatter: y({
54
+ formatter: x({
55
55
  config: n,
56
56
  total: r
57
57
  })
58
58
  });
59
59
  }
60
- function x({ centerLabel: e }) {
60
+ function ae({ centerLabel: e }) {
61
61
  return function({ viewBox: t }) {
62
62
  if (!t || !("cx" in t)) return null;
63
63
  let { cx: n = 0, cy: r = 0 } = t, i = e.value != null, a = e.label != null, o = i && a;
64
- return /* @__PURE__ */ p("g", { children: [i && /* @__PURE__ */ f("text", {
64
+ return /* @__PURE__ */ h("g", { children: [i && /* @__PURE__ */ m("text", {
65
65
  x: n,
66
66
  y: o ? r - 10 : r,
67
67
  textAnchor: "middle",
68
68
  dominantBaseline: "central",
69
69
  className: "fill-foreground text-2xl font-semibold",
70
70
  children: e.value
71
- }), a && /* @__PURE__ */ f("text", {
71
+ }), a && /* @__PURE__ */ m("text", {
72
72
  x: n,
73
73
  y: o ? r + 13 : r,
74
74
  textAnchor: "middle",
@@ -78,117 +78,117 @@ function x({ centerLabel: e }) {
78
78
  })] });
79
79
  };
80
80
  }
81
- var S = u.forwardRef(({ className: t, config: d, palette: _, data: y, dataKey: S, nameKey: C, shape: w = "donut", centerLabel: T, innerRadius: E = 48, outerRadius: D, paddingAngle: O = 2, cornerRadius: k, startAngle: A = 90, endAngle: j = -270, minAngle: M, sliceSettings: N, showTooltip: P = !0, showLegend: F = !0, legendValueFormatter: I, margin: L, tooltipContent: R, tooltipFormat: z = "value", animate: B, animationDuration: oe, animationBegin: V, animationEasing: H, showLabels: U = !1, labelPosition: W, labelFormat: G = "value", labelFormatter: K, labelLine: q = !1, ...se }, J) => {
82
- let ce = l({
83
- animate: B,
81
+ var C = f.forwardRef(({ className: t, config: p, palette: y, data: x, dataKey: C, nameKey: w, shape: T = "donut", centerLabel: E, innerRadius: D = 48, outerRadius: O, paddingAngle: k = 2, cornerRadius: A, startAngle: j = 90, endAngle: M = -270, minAngle: N, sliceSettings: P, showTooltip: F = !0, showLegend: I = !0, legendValueFormatter: L, margin: R, tooltipContent: z, tooltipFormat: B = "value", animate: V, animationDuration: oe, animationBegin: H, animationEasing: U, showLabels: W = !1, labelPosition: G, labelFormat: K = "value", labelFormatter: q, labelLine: se = !1, ...ce }, le) => {
82
+ let ue = u({
83
+ animate: V,
84
84
  animationDuration: oe,
85
- animationBegin: V,
86
- animationEasing: H
87
- }), Y = W ?? "outside", le = w === "donut" ? E : 0, X = u.useMemo(() => F ? s(d, _ ?? n) : null, [
88
- F,
89
- d,
90
- _
91
- ]), ue = u.useMemo(() => X ? y.map((e) => ({
92
- value: String(e[C]),
93
- dataKey: C,
94
- color: X[String(e[C])]?.color ?? `var(--color-${String(e[C])})`,
85
+ animationBegin: H,
86
+ animationEasing: U
87
+ }), J = G ?? "outside", Y = T === "donut" ? D : 0, X = f.useMemo(() => I ? s(p, y ?? n) : null, [
88
+ I,
89
+ p,
90
+ y
91
+ ]), de = f.useMemo(() => X ? x.map((e) => ({
92
+ value: String(e[w]),
93
+ dataKey: w,
94
+ color: X[String(e[w])]?.color ?? `var(--color-${c(String(e[w]))})`,
95
95
  type: "rect",
96
96
  payload: e
97
97
  })) : [], [
98
98
  X,
99
- y,
100
- C
101
- ]), Z = y.reduce((e, t) => e + (g(t[S]) ?? 0), 0), Q = (e) => {
99
+ x,
100
+ w
101
+ ]), Z = x.reduce((e, t) => e + (v(t[C]) ?? 0), 0), Q = (e) => {
102
102
  if (!e) return null;
103
- let t = N?.[String(e[C])];
104
- return t?.hideLabel ? null : v({
105
- format: t?.labelFormat ?? G,
106
- name: e[C],
107
- value: e[S],
103
+ let t = P?.[String(e[w])];
104
+ return t?.hideLabel ? null : b({
105
+ format: t?.labelFormat ?? K,
106
+ name: e[w],
107
+ value: e[C],
108
108
  total: Z,
109
- formatter: K
109
+ formatter: q
110
110
  }) || null;
111
- }, $ = U && q, de = /* @__PURE__ */ p(ae, {
112
- margin: L ?? {
111
+ }, $ = W && se, fe = /* @__PURE__ */ h(ie, {
112
+ margin: R ?? {
113
113
  top: 0,
114
114
  right: 0,
115
115
  bottom: 0,
116
116
  left: 0
117
117
  },
118
- children: [P && /* @__PURE__ */ f(a, { content: R ?? (z === "value-percent" ? b({
119
- nameKey: C,
120
- config: d,
118
+ children: [F && /* @__PURE__ */ m(a, { content: z ?? (B === "value-percent" ? S({
119
+ nameKey: w,
120
+ config: p,
121
121
  total: Z
122
- }) : /* @__PURE__ */ f(o, {
123
- nameKey: C,
122
+ }) : /* @__PURE__ */ m(o, {
123
+ nameKey: w,
124
124
  hideLabel: !0
125
- })) }), /* @__PURE__ */ p(ie, {
126
- data: y,
127
- dataKey: S,
128
- nameKey: C,
129
- innerRadius: le,
130
- outerRadius: D ?? 60,
131
- paddingAngle: O,
132
- cornerRadius: k,
133
- startAngle: A,
134
- endAngle: j,
135
- minAngle: M,
125
+ })) }), /* @__PURE__ */ h(re, {
126
+ data: x,
127
+ dataKey: C,
128
+ nameKey: w,
129
+ innerRadius: Y,
130
+ outerRadius: O ?? 60,
131
+ paddingAngle: k,
132
+ cornerRadius: A,
133
+ startAngle: j,
134
+ endAngle: M,
135
+ minAngle: N,
136
136
  label: $ ? (e) => {
137
137
  let t = Q(e.payload);
138
- return t ? /* @__PURE__ */ f("text", {
138
+ return t ? /* @__PURE__ */ m("text", {
139
139
  x: e.x,
140
140
  y: e.y,
141
141
  textAnchor: e.textAnchor,
142
142
  dominantBaseline: "central",
143
- className: c,
143
+ className: l,
144
144
  fontSize: 12,
145
145
  children: t
146
- }) : /* @__PURE__ */ f("g", {});
146
+ }) : /* @__PURE__ */ m("g", {});
147
147
  } : !1,
148
- labelLine: $ ? ({ key: e, payload: t, ...n }) => Q(t) ? /* @__PURE__ */ f(m, {
148
+ labelLine: $ ? ({ key: e, payload: t, ...n }) => Q(t) ? /* @__PURE__ */ m(ee, {
149
149
  ...n,
150
150
  type: "linear",
151
151
  className: "recharts-pie-label-line"
152
- }) : /* @__PURE__ */ f("g", {}) : !1,
153
- ...ce,
152
+ }) : /* @__PURE__ */ m("g", {}) : !1,
153
+ ...ue,
154
154
  children: [
155
- y.map((e, t) => /* @__PURE__ */ f(te, { fill: N?.[String(e[C])]?.color ?? `var(--color-${e[C]})` }, t)),
156
- w === "donut" && T && /* @__PURE__ */ f(ne, { content: x({ centerLabel: T }) }),
157
- U && !$ && /* @__PURE__ */ f(re, {
155
+ x.map((e, t) => /* @__PURE__ */ m(g, { fill: P?.[String(e[w])]?.color ?? `var(--color-${c(String(e[w]))})` }, t)),
156
+ T === "donut" && E && /* @__PURE__ */ m(te, { content: ae({ centerLabel: E }) }),
157
+ W && !$ && /* @__PURE__ */ m(ne, {
158
158
  valueAccessor: (e) => Q(e.payload),
159
- position: Y,
160
- className: ee(Y),
159
+ position: J,
160
+ className: d(J),
161
161
  fontSize: 12
162
162
  })
163
163
  ]
164
164
  })]
165
- }), fe = U ? $ ? "size-[320px]" : Y === "outside" || Y === "end" ? "size-[200px]" : "size-[120px]" : "size-[120px]";
166
- return /* @__PURE__ */ p("div", {
167
- ref: J,
168
- "data-shape": w,
169
- className: e("flex flex-row items-center gap-4", !F && "justify-center", h({ shape: w }), t),
170
- ...se,
171
- children: [/* @__PURE__ */ f("div", {
172
- className: e(fe, "shrink-0"),
173
- children: /* @__PURE__ */ f(r, {
174
- config: d,
175
- palette: _,
165
+ }), pe = W ? $ ? "size-[320px]" : J === "outside" || J === "end" ? "size-[200px]" : "size-[120px]" : "size-[120px]";
166
+ return /* @__PURE__ */ h("div", {
167
+ ref: le,
168
+ "data-shape": T,
169
+ className: e("flex flex-row items-center gap-4", !I && "justify-center", _({ shape: T }), t),
170
+ ...ce,
171
+ children: [/* @__PURE__ */ m("div", {
172
+ className: e(pe, "shrink-0"),
173
+ children: /* @__PURE__ */ m(r, {
174
+ config: p,
175
+ palette: y,
176
176
  className: "size-full",
177
- children: de
177
+ children: fe
178
178
  })
179
- }), F && X && /* @__PURE__ */ f(i, {
179
+ }), I && X && /* @__PURE__ */ m(i, {
180
180
  variant: "list",
181
- payload: ue,
181
+ payload: de,
182
182
  config: X,
183
- nameKey: C,
184
- valueKey: S,
185
- valueFormatter: I,
183
+ nameKey: w,
184
+ valueKey: C,
185
+ valueFormatter: L,
186
186
  className: "min-w-0 flex-1"
187
187
  })]
188
188
  });
189
189
  });
190
- S.displayName = "PieChart";
190
+ C.displayName = "PieChart";
191
191
  //#endregion
192
- export { S as PieChart, v as pieChartLabelText, y as pieChartValuePercentRow, b as pieChartValuePercentTooltip, h as pieChartVariants };
192
+ export { C as PieChart, b as pieChartLabelText, x as pieChartValuePercentRow, S as pieChartValuePercentTooltip, _ as pieChartVariants };
193
193
 
194
194
  //# sourceMappingURL=pie-chart.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pie-chart.js","names":[],"sources":["../../../../src/components/ui/pie-chart/pie-chart.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n Cell,\n Curve,\n Label,\n LabelList,\n Pie,\n PieChart as RechartsPieChart,\n type CurveProps,\n type PieLabelRenderProps,\n} from 'recharts';\n\nimport type { LegendPayload } from 'recharts/types/component/DefaultLegendContent';\n\nimport { cn } from '@/lib/utils';\nimport {\n ChartContainer,\n ChartLegendContent,\n ChartTooltip,\n ChartTooltipContent,\n resolveAnimation,\n resolveChartColors,\n resolveLabelFillClass,\n CHART_DEFAULT_PALETTE,\n CHART_LABEL_FILL_CLASS,\n CHART_LABEL_FONT_SIZE,\n type ChartConfig,\n type ChartPalette,\n type ChartTooltipContentProps,\n type ChartAnimationProps,\n type ChartDataLabelProps,\n type PolarLabelPosition,\n type TickFormatter,\n} from '../chart';\n\n// A typed recharts composition over the shared `Chart` primitives. The single\n// CVA axis is the design's Pie-chart variant set: `shape` (a filled pie vs a\n// hollow-centre donut). The class stays empty because recharts' SVG — not CSS —\n// draws the arc: `shape` drives the `<Pie>`'s `innerRadius` (0 for a pie, the\n// `innerRadius` prop for a donut). CVA is kept so the variant set is a\n// first-class, spec-conformant part of the API (matched against ui-spec's\n// api.yaml enums) and exposed via `VariantProps`; the resolved value is also\n// mirrored onto `data-shape`.\nconst pieChartVariants = cva('', {\n variants: {\n shape: {\n pie: '',\n donut: '',\n },\n },\n defaultVariants: {\n shape: 'pie',\n },\n});\n\n/** Row-per-slice datum: the `nameKey` label plus the `dataKey` value. */\ntype PieChartDatum = Record<string, string | number>;\n\n/**\n * What a slice's data label reads. `percent` is the slice's share of the sum of\n * every slice value, to one decimal.\n */\nexport type PieChartLabelFormat =\n | 'value'\n | 'name-value'\n | 'name-percent'\n | 'percent';\n\n/** What the tooltip's value reads — the raw value, or the value and its share. */\nexport type PieChartTooltipFormat = 'value' | 'value-percent';\n\n/** Per-slice overrides, keyed by the slice's `nameKey` value. */\nexport interface PieChartSliceSettings {\n /**\n * Fill for this slice, overriding its `config` color. Reference an existing\n * semantic `--ui-*` token, same as `config`.\n */\n color?: string;\n /** Drop this slice's data label while every other slice keeps its own. */\n hideLabel?: boolean;\n /** Label format for this slice only — overrides the chart-level `labelFormat`. */\n labelFormat?: PieChartLabelFormat;\n}\n\nconst toNumericValue = (value: unknown): number | undefined =>\n typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n\n/**\n * A slice's share of the chart's total, to one decimal. The single place the\n * share is computed, so a slice's label and its tooltip row can't disagree.\n * `undefined` when there is nothing to divide — a non-numeric slice value, or a\n * total of zero — which is what lets the callers degrade instead of printing\n * `NaN%`.\n */\nconst slicePercentText = (\n value: unknown,\n total: number\n): string | undefined => {\n const numeric = toNumericValue(value);\n if (numeric === undefined || total <= 0) return undefined;\n return `${((numeric / total) * 100).toFixed(1)}%`;\n};\n\n/**\n * Compose one slice's data-label text. Exported for unit tests; not part of the\n * package's public API.\n *\n * A percent needs a numeric slice value *and* a non-zero total, and a value\n * label needs a value at all — so every format degrades rather than printing\n * `NaN%` or a dangling `\"Chrome: \"`. The two name-carrying formats fall back to\n * the bare name; the value-only ones to `''`, which `resolveSliceLabel`\n * normalizes to `null` (the one value recharts renders no `<text>` for).\n *\n * `labelFormatter` formats the slice's *value*, so it reaches `value` and\n * `name-value` only. A percent is always composed by `slicePercentText` and\n * never passes through the formatter.\n */\nexport function pieChartLabelText(options: {\n format: PieChartLabelFormat;\n name: string | number;\n value: string | number | undefined;\n total: number;\n formatter?: TickFormatter;\n}): string {\n const { format, name, value, total, formatter } = options;\n const valueText =\n value == null ? '' : formatter ? formatter(value) : String(value);\n const percentText = slicePercentText(value, total);\n\n switch (format) {\n case 'name-value':\n return valueText ? `${name}: ${valueText}` : `${name}`;\n case 'name-percent':\n return percentText ? `${name}: ${percentText}` : `${name}`;\n case 'percent':\n return percentText ?? '';\n default:\n return valueText;\n }\n}\n\n/**\n * The row renderer behind `tooltipFormat=\"value-percent\"` — a slice's value\n * followed by its share. Exported for unit tests; not part of the package's\n * public API.\n *\n * A `formatter` replaces the default row wholesale, so the swatch and the name\n * are rebuilt here to match it: the shared `ChartTooltipContent` prefers a\n * `config` entry's `icon` over the color swatch, and this has to do the same or\n * a chart with icons would silently lose them under this preset — and the\n * name/value `gap-4`, which has to be repeated here because a `formatter`\n * replaces the shared row that carries it.\n */\nexport function pieChartValuePercentRow(options: {\n config: ChartConfig;\n total: number;\n}): NonNullable<ChartTooltipContentProps['formatter']> {\n const { config, total } = options;\n // Named rather than an arrow: it returns JSX, so an anonymous function reads\n // to eslint-plugin-react as a component with no display name.\n return function ValuePercentRow(value, name, item) {\n const itemConfig = config[String(name)];\n const percent = slicePercentText(value, total);\n const valueText = value?.toLocaleString() ?? '';\n return (\n <>\n {itemConfig?.icon ? (\n <itemConfig.icon />\n ) : (\n <div\n // Dotted, not the legend's square swatch: the shared row's\n // \"a row is always dotted, whatever marker the legend gives that\n // series\" invariant applies here too.\n className=\"h-2.5 w-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: item.payload?.fill ?? item.color }}\n />\n )}\n {/*\n * Mirrors the shared default row's `gap-4` (see `ChartTooltipContent`):\n * a `formatter` replaces that row wholesale, so the floor under the\n * name/value separation has to be restated here. Without it a value\n * *and* its share outgrows the tooltip's `min-w-[8rem]`, leaving\n * `justify-between` no free space to distribute.\n */}\n <div className=\"flex flex-1 items-center justify-between gap-4 leading-none\">\n <span className=\"text-muted-foreground\">\n {itemConfig?.label ?? name}\n </span>\n <span className=\"font-medium tabular-nums text-foreground\">\n {percent ? `${valueText} (${percent})` : valueText}\n </span>\n </div>\n </>\n );\n };\n}\n\n/**\n * The configured `ChartTooltipContent` for `tooltipFormat=\"value-percent\"`.\n * Exported so the visual-regression story renders the very same element the\n * component does — recharts can only open a hover tooltip statically via\n * `defaultIndex` on a raw composition, so the preset has to be reachable from\n * outside the component to be snapshotted at all.\n */\nexport function pieChartValuePercentTooltip(options: {\n nameKey: string;\n config: ChartConfig;\n total: number;\n}): React.ReactElement {\n const { nameKey, config, total } = options;\n return (\n <ChartTooltipContent\n nameKey={nameKey}\n hideLabel\n formatter={pieChartValuePercentRow({ config, total })}\n />\n );\n}\n\nexport interface PieChartCenterLabel {\n // Rendered as SVG <text>, which only lays out text — hence string | number,\n // not ReactNode.\n /** Headline metric rendered large in the donut hole (e.g. a total). */\n value?: string | number;\n /** Caption rendered under the value. */\n label?: string | number;\n}\n\nexport interface PieChartProps\n extends Omit<React.ComponentProps<'div'>, 'children'>,\n VariantProps<typeof pieChartVariants>,\n ChartAnimationProps,\n ChartDataLabelProps {\n /**\n * The dataviz palette this chart's series are painted from. Series that\n * state no `color` of their own take a stop of it. See `ChartPalette`.\n */\n palette?: ChartPalette;\n /** Row-per-slice data. Each object holds the slice's `nameKey` label + its `dataKey` numeric value. */\n data: ReadonlyArray<PieChartDatum>;\n /**\n * Per-slice map of `label` / `icon` / `tone`, keyed by the slice's `nameKey`\n * value (imported from the shared `Chart` primitives). Turned into\n * `--color-<name>` custom properties. Series take their colour from the\n * container's `palette`; each entry maps a key to a `label` and an optional\n * `tone`.\n */\n config: ChartConfig;\n /** Numeric field that sizes each slice. */\n dataKey: string;\n /**\n * Label field that names each slice (drives the legend, tooltip, and\n * `--color-<name>` lookup). Values should be unique per chart — rows sharing a\n * name share one `config`/color entry.\n */\n nameKey: string;\n /** Inner radius of the arc when `shape=\"donut\"` (ignored for `shape=\"pie\"`). */\n innerRadius?: number;\n /**\n * Custom content for the donut hole — a headline `value` and/or a `label`\n * caption, stacked and centered. Rendered only for `shape=\"donut\"` (a filled\n * pie has no hole).\n */\n centerLabel?: PieChartCenterLabel;\n /** Outer radius of the arc. Omit to use recharts' responsive default. */\n outerRadius?: number;\n /** Gap between slices, in degrees. */\n paddingAngle?: number;\n /** Corner radius of each slice. */\n cornerRadius?: number;\n /**\n * Angle the sweep starts at, in degrees (0 is 3 o'clock, counter-clockwise).\n * Pair with `endAngle` for a semicircle (`180` → `0`) or an arc.\n *\n * A partial sweep still centres on the full plot area, so a half turn fills\n * only half its box — give the chart a short, wide one rather than expecting\n * the arc to grow into a square.\n */\n startAngle?: number;\n /** Angle the sweep ends at, in degrees. Defaults to a full `360` circle. */\n endAngle?: number;\n /**\n * Smallest angle a non-zero slice may occupy, in degrees — keeps a tiny slice\n * visible (and hoverable) at the cost of being drawn out of proportion.\n */\n minAngle?: number;\n /**\n * Per-slice overrides keyed by the slice's `nameKey` value: its `color`, and\n * whether/how its data label reads. A name with no entry keeps the chart-level\n * behavior.\n */\n sliceSettings?: Record<string, PieChartSliceSettings>;\n showTooltip?: boolean;\n showLegend?: boolean;\n /**\n * Format each slice's value in the list legend. When omitted the raw value is\n * stringified with `String()`.\n */\n legendValueFormatter?: (value: string | number) => string;\n /** Plot-area margin, in px. Omit to use recharts' default (5 on every side). */\n margin?: { top?: number; right?: number; bottom?: number; left?: number };\n /**\n * Replace the default tooltip. Pass a configured `ChartTooltipContent`\n * (imported from this library) — e.g. with a `formatter` / `labelFormatter` —\n * to customize formatting, per-slice rows, or extra fields without composing\n * recharts yourself. Takes precedence over `tooltipFormat`. Ignored when\n * `showTooltip` is false.\n */\n tooltipContent?: React.ComponentProps<typeof ChartTooltip>['content'];\n /**\n * How the default tooltip reads a slice: its `value` alone (the default), or\n * the value followed by its share of the total (`value-percent`). The preset\n * covers the common case without hand-rolling a `tooltipContent`.\n *\n * The share is rendered as `NN.N%`, same as a data label's — not locale-aware.\n * Pass your own `tooltipContent` if you need to format it.\n */\n tooltipFormat?: PieChartTooltipFormat;\n /**\n * Position of the value labels when `showLabels` is on. Defaults to `outside`,\n * which keeps them on the chart surface — the only placement that reliably\n * has contrast, since slice fills are caller-supplied saturated colors. The\n * on-arc placements are available but switch to the on-fill label token.\n * Ignored when `labelLine` is on — a leader line only ever ends outside the arc.\n */\n labelPosition?: PolarLabelPosition;\n /**\n * What each data label reads when `showLabels` is on — the value alone (the\n * default), or the slice name and/or its share of the total. Overridable per\n * slice via `sliceSettings`.\n *\n * `labelFormatter` formats the slice's *value*, so it reaches `value` and\n * `name-value` only: a share is always rendered as `NN.N%` (a `.` decimal and\n * a bare `%`, not locale-aware) and never passes through the formatter.\n */\n labelFormat?: PieChartLabelFormat;\n /**\n * Draw a leader line from each slice to its data label, in the slice's own\n * color. Moves the labels outside the arc (see `labelPosition`); slices whose\n * label is hidden by `sliceSettings` get no line either.\n *\n * A label reaching out past the arc needs horizontal room — give the chart a\n * box wider than it is tall, or shrink the arc (`outerRadius`/`margin`), or\n * the outermost labels are clipped at the surface edge.\n */\n labelLine?: boolean;\n}\n\nfunction renderCenterLabel({ centerLabel }: { centerLabel: PieChartCenterLabel }) {\n return function CenterLabelContent({ viewBox }: { viewBox?: object }) {\n if (!viewBox || !('cx' in viewBox)) return null;\n const { cx = 0, cy = 0 } = viewBox as { cx?: number; cy?: number };\n const hasValue = centerLabel.value != null;\n const hasLabel = centerLabel.label != null;\n const both = hasValue && hasLabel;\n return (\n <g>\n {hasValue && (\n <text\n x={cx}\n y={both ? cy - 10 : cy}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className=\"fill-foreground text-2xl font-semibold\"\n >\n {centerLabel.value}\n </text>\n )}\n {hasLabel && (\n <text\n x={cx}\n y={both ? cy + 13 : cy}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className=\"fill-muted-foreground text-sm\"\n >\n {centerLabel.label}\n </text>\n )}\n </g>\n );\n };\n}\n\nconst PieChart = React.forwardRef<HTMLDivElement, PieChartProps>(\n (\n {\n className,\n config,\n palette,\n data,\n dataKey,\n nameKey,\n shape = 'donut',\n centerLabel,\n innerRadius = 48,\n outerRadius,\n paddingAngle = 2,\n cornerRadius,\n startAngle = 90,\n endAngle = -270,\n minAngle,\n sliceSettings,\n showTooltip = true,\n showLegend = true,\n legendValueFormatter,\n margin,\n tooltipContent,\n tooltipFormat = 'value',\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n showLabels = false,\n labelPosition,\n labelFormat = 'value',\n labelFormatter,\n labelLine = false,\n ...props\n },\n ref\n ) => {\n const animation = resolveAnimation({\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n });\n const pieLabelPosition = labelPosition ?? 'outside';\n const resolvedInnerRadius = shape === 'donut' ? innerRadius : 0;\n\n // Resolve colors so the external list legend has the same values as the chart\n // plot without needing to be inside ChartContainer's context.\n const resolvedConfigForLegend = React.useMemo(\n () => (showLegend ? resolveChartColors(config, palette ?? CHART_DEFAULT_PALETTE) : null),\n [showLegend, config, palette]\n );\n const rightLegendPayload: LegendPayload[] = React.useMemo(() => {\n if (!resolvedConfigForLegend) return [];\n return data.map((row) => ({\n value: String(row[nameKey]),\n dataKey: nameKey,\n color:\n resolvedConfigForLegend[String(row[nameKey])]?.color ??\n `var(--color-${String(row[nameKey])})`,\n type: 'rect' as const,\n payload: row as Record<string, unknown>,\n }));\n }, [resolvedConfigForLegend, data, nameKey]);\n\n // The denominator behind every percent — the labels' and the tooltip's — so\n // the two always agree. Non-numeric cells count as nothing rather than NaN.\n const total = data.reduce(\n (sum, row) => sum + (toNumericValue(row[dataKey]) ?? 0),\n 0\n );\n\n // Always null, never '', for a slice with nothing to show — whether its\n // label was switched off or its format degraded to empty. recharts renders\n // no <text> at all for a nullish label value (`Label` bails on `isNullish`),\n // where an empty string still emits one wrapping an empty <tspan>.\n const resolveSliceLabel = (row: PieChartDatum | undefined): string | null => {\n if (!row) return null;\n const settings = sliceSettings?.[String(row[nameKey])];\n if (settings?.hideLabel) return null;\n return (\n pieChartLabelText({\n format: settings?.labelFormat ?? labelFormat,\n name: row[nameKey],\n value: row[dataKey],\n total,\n formatter: labelFormatter,\n }) || null\n );\n };\n\n // Leader lines are the one thing a `LabelList` can't draw: recharts only\n // renders them alongside the `label` prop's own outside placement. So the\n // labels move to that path when `labelLine` is on, and stay on the\n // position-aware `LabelList` otherwise.\n const showLeaderLines = showLabels && labelLine;\n\n const renderLeaderLabel = (labelProps: PieLabelRenderProps) => {\n const text = resolveSliceLabel(labelProps.payload as PieChartDatum);\n // An empty <g>, not null: recharts only takes the render function's output\n // when it's an element — a nullish return falls through to its own <Text>\n // painting the raw value.\n if (!text) return <g />;\n return (\n <text\n x={labelProps.x}\n y={labelProps.y}\n textAnchor={labelProps.textAnchor}\n dominantBaseline=\"central\"\n // Always the on-surface fill: this path only ever places labels\n // outside the arc, never over a slice.\n className={CHART_LABEL_FILL_CLASS}\n fontSize={CHART_LABEL_FONT_SIZE}\n >\n {text}\n </text>\n );\n };\n\n // recharts draws a line per sector independently of what the label renders,\n // so a slice hidden via `sliceSettings` would keep a line pointing at\n // nothing. Rebuilding the default `Curve` here is what lets it be dropped.\n const renderLeaderLine = ({\n key,\n payload,\n ...lineProps\n }: CurveProps & { key?: React.Key; payload?: PieChartDatum }) => {\n if (!resolveSliceLabel(payload)) return <g />;\n return (\n <Curve {...lineProps} type=\"linear\" className=\"recharts-pie-label-line\" />\n );\n };\n\n // Shared recharts chart composition (used in both layout modes).\n const pieChartContent = (\n <RechartsPieChart margin={margin ?? { top: 0, right: 0, bottom: 0, left: 0 }}>\n {showTooltip && (\n <ChartTooltip\n content={\n tooltipContent ??\n (tooltipFormat === 'value-percent' ? (\n pieChartValuePercentTooltip({ nameKey, config, total })\n ) : (\n <ChartTooltipContent nameKey={nameKey} hideLabel />\n ))\n }\n />\n )}\n <Pie\n data={data as unknown[]}\n dataKey={dataKey}\n nameKey={nameKey}\n innerRadius={resolvedInnerRadius}\n outerRadius={outerRadius ?? 60}\n paddingAngle={paddingAngle}\n cornerRadius={cornerRadius}\n startAngle={startAngle}\n endAngle={endAngle}\n minAngle={minAngle}\n label={showLeaderLines ? renderLeaderLabel : false}\n labelLine={showLeaderLines ? renderLeaderLine : false}\n {...animation}\n >\n {data.map((entry, index) => (\n // Keyed by index, not the name: two rows may share a nameKey\n // value, which would collide as a React key. Same-named rows\n // intentionally share a color/config entry via `--color-<name>`.\n <Cell\n key={index}\n fill={\n sliceSettings?.[String(entry[nameKey])]?.color ??\n `var(--color-${entry[nameKey]})`\n }\n />\n ))}\n {shape === 'donut' && centerLabel && (\n <Label content={renderCenterLabel({ centerLabel })} />\n )}\n {showLabels && !showLeaderLines && (\n <LabelList\n // `valueAccessor` rather than `dataKey`: the label text can\n // carry the slice's name and share, which only the whole row\n // (and the chart's total) can compose. recharts ignores the\n // accessor when a dataKey is set, so the two can't be combined.\n valueAccessor={(entry) =>\n resolveSliceLabel(entry.payload as PieChartDatum)\n }\n // Always explicit: recharts' polar fallback for an unset\n // position is the sector centroid — inside the fill — which\n // is exactly the placement the on-surface token can't survive.\n position={pieLabelPosition}\n className={resolveLabelFillClass(pieLabelPosition)}\n fontSize={CHART_LABEL_FONT_SIZE}\n />\n )}\n </Pie>\n </RechartsPieChart>\n );\n\n // Grow the SVG container when labels need space outside the arc so they\n // stay within the chart boundary instead of escaping into the layout.\n const chartDivSize = showLabels\n ? showLeaderLines\n ? 'size-[320px]'\n : pieLabelPosition === 'outside' || pieLabelPosition === 'end'\n ? 'size-[200px]'\n : 'size-[120px]'\n : 'size-[120px]';\n\n return (\n <div\n ref={ref}\n data-shape={shape}\n className={cn(\n 'flex flex-row items-center gap-4',\n !showLegend && 'justify-center',\n pieChartVariants({ shape }),\n className\n )}\n {...props}\n >\n <div className={cn(chartDivSize, 'shrink-0')}>\n <ChartContainer config={config} palette={palette} className=\"size-full\">\n {pieChartContent}\n </ChartContainer>\n </div>\n {showLegend && resolvedConfigForLegend && (\n <ChartLegendContent\n variant=\"list\"\n payload={rightLegendPayload}\n config={resolvedConfigForLegend}\n nameKey={nameKey}\n valueKey={dataKey}\n valueFormatter={legendValueFormatter}\n className=\"min-w-0 flex-1\"\n />\n )}\n </div>\n );\n }\n);\nPieChart.displayName = 'PieChart';\n\nexport { PieChart, pieChartVariants };\n"],"mappings":"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"}
1
+ {"version":3,"file":"pie-chart.js","names":[],"sources":["../../../../src/components/ui/pie-chart/pie-chart.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n Cell,\n Curve,\n Label,\n LabelList,\n Pie,\n PieChart as RechartsPieChart,\n type CurveProps,\n type PieLabelRenderProps,\n} from 'recharts';\n\nimport type { LegendPayload } from 'recharts/types/component/DefaultLegendContent';\n\nimport { cn } from '@/lib/utils';\nimport {\n ChartContainer,\n ChartLegendContent,\n ChartTooltip,\n ChartTooltipContent,\n resolveAnimation,\n resolveChartColors,\n resolveLabelFillClass,\n CHART_DEFAULT_PALETTE,\n CHART_LABEL_FILL_CLASS,\n CHART_LABEL_FONT_SIZE,\n toCssKey,\n type ChartConfig,\n type ChartPalette,\n type ChartTooltipContentProps,\n type ChartAnimationProps,\n type ChartDataLabelProps,\n type PolarLabelPosition,\n type TickFormatter,\n} from '../chart';\n\n// A typed recharts composition over the shared `Chart` primitives. The single\n// CVA axis is the design's Pie-chart variant set: `shape` (a filled pie vs a\n// hollow-centre donut). The class stays empty because recharts' SVG — not CSS —\n// draws the arc: `shape` drives the `<Pie>`'s `innerRadius` (0 for a pie, the\n// `innerRadius` prop for a donut). CVA is kept so the variant set is a\n// first-class, spec-conformant part of the API (matched against ui-spec's\n// api.yaml enums) and exposed via `VariantProps`; the resolved value is also\n// mirrored onto `data-shape`.\nconst pieChartVariants = cva('', {\n variants: {\n shape: {\n pie: '',\n donut: '',\n },\n },\n defaultVariants: {\n shape: 'pie',\n },\n});\n\n/** Row-per-slice datum: the `nameKey` label plus the `dataKey` value. */\ntype PieChartDatum = Record<string, string | number>;\n\n/**\n * What a slice's data label reads. `percent` is the slice's share of the sum of\n * every slice value, to one decimal.\n */\nexport type PieChartLabelFormat =\n | 'value'\n | 'name-value'\n | 'name-percent'\n | 'percent';\n\n/** What the tooltip's value reads — the raw value, or the value and its share. */\nexport type PieChartTooltipFormat = 'value' | 'value-percent';\n\n/** Per-slice overrides, keyed by the slice's `nameKey` value. */\nexport interface PieChartSliceSettings {\n /**\n * Fill for this slice, overriding its `config` color. Reference an existing\n * semantic `--ui-*` token, same as `config`.\n */\n color?: string;\n /** Drop this slice's data label while every other slice keeps its own. */\n hideLabel?: boolean;\n /** Label format for this slice only — overrides the chart-level `labelFormat`. */\n labelFormat?: PieChartLabelFormat;\n}\n\nconst toNumericValue = (value: unknown): number | undefined =>\n typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n\n/**\n * A slice's share of the chart's total, to one decimal. The single place the\n * share is computed, so a slice's label and its tooltip row can't disagree.\n * `undefined` when there is nothing to divide — a non-numeric slice value, or a\n * total of zero — which is what lets the callers degrade instead of printing\n * `NaN%`.\n */\nconst slicePercentText = (\n value: unknown,\n total: number\n): string | undefined => {\n const numeric = toNumericValue(value);\n if (numeric === undefined || total <= 0) return undefined;\n return `${((numeric / total) * 100).toFixed(1)}%`;\n};\n\n/**\n * Compose one slice's data-label text. Exported for unit tests; not part of the\n * package's public API.\n *\n * A percent needs a numeric slice value *and* a non-zero total, and a value\n * label needs a value at all — so every format degrades rather than printing\n * `NaN%` or a dangling `\"Chrome: \"`. The two name-carrying formats fall back to\n * the bare name; the value-only ones to `''`, which `resolveSliceLabel`\n * normalizes to `null` (the one value recharts renders no `<text>` for).\n *\n * `labelFormatter` formats the slice's *value*, so it reaches `value` and\n * `name-value` only. A percent is always composed by `slicePercentText` and\n * never passes through the formatter.\n */\nexport function pieChartLabelText(options: {\n format: PieChartLabelFormat;\n name: string | number;\n value: string | number | undefined;\n total: number;\n formatter?: TickFormatter;\n}): string {\n const { format, name, value, total, formatter } = options;\n const valueText =\n value == null ? '' : formatter ? formatter(value) : String(value);\n const percentText = slicePercentText(value, total);\n\n switch (format) {\n case 'name-value':\n return valueText ? `${name}: ${valueText}` : `${name}`;\n case 'name-percent':\n return percentText ? `${name}: ${percentText}` : `${name}`;\n case 'percent':\n return percentText ?? '';\n default:\n return valueText;\n }\n}\n\n/**\n * The row renderer behind `tooltipFormat=\"value-percent\"` — a slice's value\n * followed by its share. Exported for unit tests; not part of the package's\n * public API.\n *\n * A `formatter` replaces the default row wholesale, so the swatch and the name\n * are rebuilt here to match it: the shared `ChartTooltipContent` prefers a\n * `config` entry's `icon` over the color swatch, and this has to do the same or\n * a chart with icons would silently lose them under this preset — and the\n * name/value `gap-4`, which has to be repeated here because a `formatter`\n * replaces the shared row that carries it.\n */\nexport function pieChartValuePercentRow(options: {\n config: ChartConfig;\n total: number;\n}): NonNullable<ChartTooltipContentProps['formatter']> {\n const { config, total } = options;\n // Named rather than an arrow: it returns JSX, so an anonymous function reads\n // to eslint-plugin-react as a component with no display name.\n return function ValuePercentRow(value, name, item) {\n const itemConfig = config[String(name)];\n const percent = slicePercentText(value, total);\n const valueText = value?.toLocaleString() ?? '';\n return (\n <>\n {itemConfig?.icon ? (\n <itemConfig.icon />\n ) : (\n <div\n // Dotted, not the legend's square swatch: the shared row's\n // \"a row is always dotted, whatever marker the legend gives that\n // series\" invariant applies here too.\n className=\"h-2.5 w-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: item.payload?.fill ?? item.color }}\n />\n )}\n {/*\n * Mirrors the shared default row's `gap-4` (see `ChartTooltipContent`):\n * a `formatter` replaces that row wholesale, so the floor under the\n * name/value separation has to be restated here. Without it a value\n * *and* its share outgrows the tooltip's `min-w-[8rem]`, leaving\n * `justify-between` no free space to distribute.\n */}\n <div className=\"flex flex-1 items-center justify-between gap-4 leading-none\">\n <span className=\"text-muted-foreground\">\n {itemConfig?.label ?? name}\n </span>\n <span className=\"font-medium tabular-nums text-foreground\">\n {percent ? `${valueText} (${percent})` : valueText}\n </span>\n </div>\n </>\n );\n };\n}\n\n/**\n * The configured `ChartTooltipContent` for `tooltipFormat=\"value-percent\"`.\n * Exported so the visual-regression story renders the very same element the\n * component does — recharts can only open a hover tooltip statically via\n * `defaultIndex` on a raw composition, so the preset has to be reachable from\n * outside the component to be snapshotted at all.\n */\nexport function pieChartValuePercentTooltip(options: {\n nameKey: string;\n config: ChartConfig;\n total: number;\n}): React.ReactElement {\n const { nameKey, config, total } = options;\n return (\n <ChartTooltipContent\n nameKey={nameKey}\n hideLabel\n formatter={pieChartValuePercentRow({ config, total })}\n />\n );\n}\n\nexport interface PieChartCenterLabel {\n // Rendered as SVG <text>, which only lays out text — hence string | number,\n // not ReactNode.\n /** Headline metric rendered large in the donut hole (e.g. a total). */\n value?: string | number;\n /** Caption rendered under the value. */\n label?: string | number;\n}\n\nexport interface PieChartProps\n extends Omit<React.ComponentProps<'div'>, 'children'>,\n VariantProps<typeof pieChartVariants>,\n ChartAnimationProps,\n ChartDataLabelProps {\n /**\n * The dataviz palette this chart's series are painted from. Series that\n * state no `color` of their own take a stop of it. See `ChartPalette`.\n */\n palette?: ChartPalette;\n /** Row-per-slice data. Each object holds the slice's `nameKey` label + its `dataKey` numeric value. */\n data: ReadonlyArray<PieChartDatum>;\n /**\n * Per-slice map of `label` / `icon` / `tone`, keyed by the slice's `nameKey`\n * value (imported from the shared `Chart` primitives). Turned into\n * `--color-<name>` custom properties. Series take their colour from the\n * container's `palette`; each entry maps a key to a `label` and an optional\n * `tone`.\n */\n config: ChartConfig;\n /** Numeric field that sizes each slice. */\n dataKey: string;\n /**\n * Label field that names each slice (drives the legend, tooltip, and\n * `--color-<name>` lookup). Values should be unique per chart — rows sharing a\n * name share one `config`/color entry.\n */\n nameKey: string;\n /** Inner radius of the arc when `shape=\"donut\"` (ignored for `shape=\"pie\"`). */\n innerRadius?: number;\n /**\n * Custom content for the donut hole — a headline `value` and/or a `label`\n * caption, stacked and centered. Rendered only for `shape=\"donut\"` (a filled\n * pie has no hole).\n */\n centerLabel?: PieChartCenterLabel;\n /** Outer radius of the arc. Omit to use recharts' responsive default. */\n outerRadius?: number;\n /** Gap between slices, in degrees. */\n paddingAngle?: number;\n /** Corner radius of each slice. */\n cornerRadius?: number;\n /**\n * Angle the sweep starts at, in degrees (0 is 3 o'clock, counter-clockwise).\n * Pair with `endAngle` for a semicircle (`180` → `0`) or an arc.\n *\n * A partial sweep still centres on the full plot area, so a half turn fills\n * only half its box — give the chart a short, wide one rather than expecting\n * the arc to grow into a square.\n */\n startAngle?: number;\n /** Angle the sweep ends at, in degrees. Defaults to a full `360` circle. */\n endAngle?: number;\n /**\n * Smallest angle a non-zero slice may occupy, in degrees — keeps a tiny slice\n * visible (and hoverable) at the cost of being drawn out of proportion.\n */\n minAngle?: number;\n /**\n * Per-slice overrides keyed by the slice's `nameKey` value: its `color`, and\n * whether/how its data label reads. A name with no entry keeps the chart-level\n * behavior.\n */\n sliceSettings?: Record<string, PieChartSliceSettings>;\n showTooltip?: boolean;\n showLegend?: boolean;\n /**\n * Format each slice's value in the list legend. When omitted the raw value is\n * stringified with `String()`.\n */\n legendValueFormatter?: (value: string | number) => string;\n /** Plot-area margin, in px. Omit to use recharts' default (5 on every side). */\n margin?: { top?: number; right?: number; bottom?: number; left?: number };\n /**\n * Replace the default tooltip. Pass a configured `ChartTooltipContent`\n * (imported from this library) — e.g. with a `formatter` / `labelFormatter` —\n * to customize formatting, per-slice rows, or extra fields without composing\n * recharts yourself. Takes precedence over `tooltipFormat`. Ignored when\n * `showTooltip` is false.\n */\n tooltipContent?: React.ComponentProps<typeof ChartTooltip>['content'];\n /**\n * How the default tooltip reads a slice: its `value` alone (the default), or\n * the value followed by its share of the total (`value-percent`). The preset\n * covers the common case without hand-rolling a `tooltipContent`.\n *\n * The share is rendered as `NN.N%`, same as a data label's — not locale-aware.\n * Pass your own `tooltipContent` if you need to format it.\n */\n tooltipFormat?: PieChartTooltipFormat;\n /**\n * Position of the value labels when `showLabels` is on. Defaults to `outside`,\n * which keeps them on the chart surface — the only placement that reliably\n * has contrast, since slice fills are caller-supplied saturated colors. The\n * on-arc placements are available but switch to the on-fill label token.\n * Ignored when `labelLine` is on — a leader line only ever ends outside the arc.\n */\n labelPosition?: PolarLabelPosition;\n /**\n * What each data label reads when `showLabels` is on — the value alone (the\n * default), or the slice name and/or its share of the total. Overridable per\n * slice via `sliceSettings`.\n *\n * `labelFormatter` formats the slice's *value*, so it reaches `value` and\n * `name-value` only: a share is always rendered as `NN.N%` (a `.` decimal and\n * a bare `%`, not locale-aware) and never passes through the formatter.\n */\n labelFormat?: PieChartLabelFormat;\n /**\n * Draw a leader line from each slice to its data label, in the slice's own\n * color. Moves the labels outside the arc (see `labelPosition`); slices whose\n * label is hidden by `sliceSettings` get no line either.\n *\n * A label reaching out past the arc needs horizontal room — give the chart a\n * box wider than it is tall, or shrink the arc (`outerRadius`/`margin`), or\n * the outermost labels are clipped at the surface edge.\n */\n labelLine?: boolean;\n}\n\nfunction renderCenterLabel({ centerLabel }: { centerLabel: PieChartCenterLabel }) {\n return function CenterLabelContent({ viewBox }: { viewBox?: object }) {\n if (!viewBox || !('cx' in viewBox)) return null;\n const { cx = 0, cy = 0 } = viewBox as { cx?: number; cy?: number };\n const hasValue = centerLabel.value != null;\n const hasLabel = centerLabel.label != null;\n const both = hasValue && hasLabel;\n return (\n <g>\n {hasValue && (\n <text\n x={cx}\n y={both ? cy - 10 : cy}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className=\"fill-foreground text-2xl font-semibold\"\n >\n {centerLabel.value}\n </text>\n )}\n {hasLabel && (\n <text\n x={cx}\n y={both ? cy + 13 : cy}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className=\"fill-muted-foreground text-sm\"\n >\n {centerLabel.label}\n </text>\n )}\n </g>\n );\n };\n}\n\nconst PieChart = React.forwardRef<HTMLDivElement, PieChartProps>(\n (\n {\n className,\n config,\n palette,\n data,\n dataKey,\n nameKey,\n shape = 'donut',\n centerLabel,\n innerRadius = 48,\n outerRadius,\n paddingAngle = 2,\n cornerRadius,\n startAngle = 90,\n endAngle = -270,\n minAngle,\n sliceSettings,\n showTooltip = true,\n showLegend = true,\n legendValueFormatter,\n margin,\n tooltipContent,\n tooltipFormat = 'value',\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n showLabels = false,\n labelPosition,\n labelFormat = 'value',\n labelFormatter,\n labelLine = false,\n ...props\n },\n ref\n ) => {\n const animation = resolveAnimation({\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n });\n const pieLabelPosition = labelPosition ?? 'outside';\n const resolvedInnerRadius = shape === 'donut' ? innerRadius : 0;\n\n // Resolve colors so the external list legend has the same values as the chart\n // plot without needing to be inside ChartContainer's context.\n const resolvedConfigForLegend = React.useMemo(\n () => (showLegend ? resolveChartColors(config, palette ?? CHART_DEFAULT_PALETTE) : null),\n [showLegend, config, palette]\n );\n const rightLegendPayload: LegendPayload[] = React.useMemo(() => {\n if (!resolvedConfigForLegend) return [];\n return data.map((row) => ({\n value: String(row[nameKey]),\n dataKey: nameKey,\n color:\n resolvedConfigForLegend[String(row[nameKey])]?.color ??\n `var(--color-${toCssKey(String(row[nameKey]))})`,\n type: 'rect' as const,\n payload: row as Record<string, unknown>,\n }));\n }, [resolvedConfigForLegend, data, nameKey]);\n\n // The denominator behind every percent — the labels' and the tooltip's — so\n // the two always agree. Non-numeric cells count as nothing rather than NaN.\n const total = data.reduce(\n (sum, row) => sum + (toNumericValue(row[dataKey]) ?? 0),\n 0\n );\n\n // Always null, never '', for a slice with nothing to show — whether its\n // label was switched off or its format degraded to empty. recharts renders\n // no <text> at all for a nullish label value (`Label` bails on `isNullish`),\n // where an empty string still emits one wrapping an empty <tspan>.\n const resolveSliceLabel = (row: PieChartDatum | undefined): string | null => {\n if (!row) return null;\n const settings = sliceSettings?.[String(row[nameKey])];\n if (settings?.hideLabel) return null;\n return (\n pieChartLabelText({\n format: settings?.labelFormat ?? labelFormat,\n name: row[nameKey],\n value: row[dataKey],\n total,\n formatter: labelFormatter,\n }) || null\n );\n };\n\n // Leader lines are the one thing a `LabelList` can't draw: recharts only\n // renders them alongside the `label` prop's own outside placement. So the\n // labels move to that path when `labelLine` is on, and stay on the\n // position-aware `LabelList` otherwise.\n const showLeaderLines = showLabels && labelLine;\n\n const renderLeaderLabel = (labelProps: PieLabelRenderProps) => {\n const text = resolveSliceLabel(labelProps.payload as PieChartDatum);\n // An empty <g>, not null: recharts only takes the render function's output\n // when it's an element — a nullish return falls through to its own <Text>\n // painting the raw value.\n if (!text) return <g />;\n return (\n <text\n x={labelProps.x}\n y={labelProps.y}\n textAnchor={labelProps.textAnchor}\n dominantBaseline=\"central\"\n // Always the on-surface fill: this path only ever places labels\n // outside the arc, never over a slice.\n className={CHART_LABEL_FILL_CLASS}\n fontSize={CHART_LABEL_FONT_SIZE}\n >\n {text}\n </text>\n );\n };\n\n // recharts draws a line per sector independently of what the label renders,\n // so a slice hidden via `sliceSettings` would keep a line pointing at\n // nothing. Rebuilding the default `Curve` here is what lets it be dropped.\n const renderLeaderLine = ({\n key,\n payload,\n ...lineProps\n }: CurveProps & { key?: React.Key; payload?: PieChartDatum }) => {\n if (!resolveSliceLabel(payload)) return <g />;\n return (\n <Curve {...lineProps} type=\"linear\" className=\"recharts-pie-label-line\" />\n );\n };\n\n // Shared recharts chart composition (used in both layout modes).\n const pieChartContent = (\n <RechartsPieChart margin={margin ?? { top: 0, right: 0, bottom: 0, left: 0 }}>\n {showTooltip && (\n <ChartTooltip\n content={\n tooltipContent ??\n (tooltipFormat === 'value-percent' ? (\n pieChartValuePercentTooltip({ nameKey, config, total })\n ) : (\n <ChartTooltipContent nameKey={nameKey} hideLabel />\n ))\n }\n />\n )}\n <Pie\n data={data as unknown[]}\n dataKey={dataKey}\n nameKey={nameKey}\n innerRadius={resolvedInnerRadius}\n outerRadius={outerRadius ?? 60}\n paddingAngle={paddingAngle}\n cornerRadius={cornerRadius}\n startAngle={startAngle}\n endAngle={endAngle}\n minAngle={minAngle}\n label={showLeaderLines ? renderLeaderLabel : false}\n labelLine={showLeaderLines ? renderLeaderLine : false}\n {...animation}\n >\n {data.map((entry, index) => (\n // Keyed by index, not the name: two rows may share a nameKey\n // value, which would collide as a React key. Same-named rows\n // intentionally share a color/config entry via `--color-<name>`.\n <Cell\n key={index}\n fill={\n sliceSettings?.[String(entry[nameKey])]?.color ??\n `var(--color-${toCssKey(String(entry[nameKey]))})`\n }\n />\n ))}\n {shape === 'donut' && centerLabel && (\n <Label content={renderCenterLabel({ centerLabel })} />\n )}\n {showLabels && !showLeaderLines && (\n <LabelList\n // `valueAccessor` rather than `dataKey`: the label text can\n // carry the slice's name and share, which only the whole row\n // (and the chart's total) can compose. recharts ignores the\n // accessor when a dataKey is set, so the two can't be combined.\n valueAccessor={(entry) =>\n resolveSliceLabel(entry.payload as PieChartDatum)\n }\n // Always explicit: recharts' polar fallback for an unset\n // position is the sector centroid — inside the fill — which\n // is exactly the placement the on-surface token can't survive.\n position={pieLabelPosition}\n className={resolveLabelFillClass(pieLabelPosition)}\n fontSize={CHART_LABEL_FONT_SIZE}\n />\n )}\n </Pie>\n </RechartsPieChart>\n );\n\n // Grow the SVG container when labels need space outside the arc so they\n // stay within the chart boundary instead of escaping into the layout.\n const chartDivSize = showLabels\n ? showLeaderLines\n ? 'size-[320px]'\n : pieLabelPosition === 'outside' || pieLabelPosition === 'end'\n ? 'size-[200px]'\n : 'size-[120px]'\n : 'size-[120px]';\n\n return (\n <div\n ref={ref}\n data-shape={shape}\n className={cn(\n 'flex flex-row items-center gap-4',\n !showLegend && 'justify-center',\n pieChartVariants({ shape }),\n className\n )}\n {...props}\n >\n <div className={cn(chartDivSize, 'shrink-0')}>\n <ChartContainer config={config} palette={palette} className=\"size-full\">\n {pieChartContent}\n </ChartContainer>\n </div>\n {showLegend && resolvedConfigForLegend && (\n <ChartLegendContent\n variant=\"list\"\n payload={rightLegendPayload}\n config={resolvedConfigForLegend}\n nameKey={nameKey}\n valueKey={dataKey}\n valueFormatter={legendValueFormatter}\n className=\"min-w-0 flex-1\"\n />\n )}\n </div>\n );\n }\n);\nPieChart.displayName = 'PieChart';\n\nexport { PieChart, pieChartVariants };\n"],"mappings":"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"}