import { Area, AreaChart, CartesianGrid, Line, LineChart, XAxis, YAxis, type XAxisProps, type YAxisProps, } from "recharts"; import { ChartTooltip, ChartTooltipContent } from "~/components/primitives/charts/Chart"; import { CHART_MARGIN } from "./ChartBar"; import { useChartContext } from "./ChartContext"; import { ChartLineInvalid, ChartLineLoading, ChartLineNoData } from "./ChartLoading"; import { useHasNoData } from "./ChartRoot"; import { defaultYAxisTickFormatter, useYAxisWidth } from "./useYAxisWidth"; // Legend is now rendered by ChartRoot outside the chart container type CurveType = | "basis" | "basisClosed" | "basisOpen" | "linear" | "linearClosed" | "natural" | "monotoneX" | "monotoneY" | "monotone" | "step" | "stepBefore" | "stepAfter"; // ============================================================================ // COMPOUND COMPONENT API // ============================================================================ export type ChartLineRendererProps = { /** Line curve type */ lineType?: CurveType; /** Custom X-axis props to merge with defaults */ xAxisProps?: Partial; /** Custom Y-axis props to merge with defaults */ yAxisProps?: Partial; /** Render as stacked area chart instead of line chart */ stacked?: boolean; /** Custom tooltip label formatter */ tooltipLabelFormatter?: (label: string, payload: any[]) => string; /** Optional formatter for numeric tooltip values (e.g. bytes, duration) */ tooltipValueFormatter?: (value: number) => string; /** Width injected by ResponsiveContainer */ width?: number; /** Height injected by ResponsiveContainer */ height?: number; }; /** * Line chart renderer for the compound component system. * Must be used within a Chart.Root. * * @example * ```tsx * * * * * ``` */ export function ChartLineRenderer({ lineType = "step", xAxisProps: xAxisPropsProp, yAxisProps: yAxisPropsProp, stacked = false, tooltipLabelFormatter, tooltipValueFormatter, width, height, }: ChartLineRendererProps) { const { config, data, dataKey, dataKeys: _dataKeys, visibleSeries, state, highlight, setActivePayload, showLegend, } = useChartContext(); const hasNoData = useHasNoData(); const yAxisTickFormatter = yAxisPropsProp?.tickFormatter ?? defaultYAxisTickFormatter; const computedYAxisWidth = useYAxisWidth(data, visibleSeries, yAxisTickFormatter); // Render loading/error states if (state === "loading") { return ; } else if (state === "noData" || hasNoData) { return ; } else if (state === "invalid") { return ; } // Get the x-axis ticks based on tooltip state const xAxisTicks = highlight.tooltipActive && data.length > 2 ? [data[0]?.[dataKey], data[data.length - 1]?.[dataKey]] : undefined; const xAxisConfig = { dataKey, tickLine: false, axisLine: false, tickMargin: 10, ticks: xAxisTicks, interval: "preserveStartEnd" as const, tick: { fill: "var(--color-text-dimmed)", fontSize: 11, style: { fontVariantNumeric: "tabular-nums" }, }, ...xAxisPropsProp, }; const yAxisConfig = { axisLine: false, tickLine: false, tickMargin: 8, width: computedYAxisWidth, tick: { fill: "var(--color-text-dimmed)", fontSize: 11, style: { fontVariantNumeric: "tabular-nums" }, }, tickFormatter: yAxisTickFormatter, ...yAxisPropsProp, }; // Handle mouse leave to also reset highlight const handleMouseLeave = () => { highlight.setTooltipActive(false); highlight.reset(); }; // Render stacked area chart if stacked prop is true if (stacked && visibleSeries.length > 1) { return ( { if (e?.activePayload?.length) { setActivePayload(e.activePayload, e.activeTooltipIndex); highlight.setTooltipActive(true); } else { highlight.setTooltipActive(false); } }} onMouseLeave={handleMouseLeave} > {/* When legend is shown below, render tooltip with cursor only (no content popup) */} null ) : ( ) } labelFormatter={tooltipLabelFormatter} /> {/* Note: Legend is now rendered by ChartRoot outside the chart container */} {visibleSeries.map((key) => ( ))} ); } return ( { if (e?.activePayload?.length) { setActivePayload(e.activePayload, e.activeTooltipIndex); highlight.setTooltipActive(true); } else { highlight.setTooltipActive(false); } }} onMouseLeave={handleMouseLeave} > {/* When legend is shown below, render tooltip with cursor only (no content popup) */} null : } labelFormatter={tooltipLabelFormatter} /> {/* Note: Legend is now rendered by ChartRoot outside the chart container */} {visibleSeries.map((key) => ( ))} ); }