import { type ReactNode, useEffect, useMemo, useState } from "react" import { Area, AreaChart, CartesianGrid, YAxis } from "recharts" import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, xAxis, } from "@/components/ui/chart" import { chartMargin, cn, formatShortDate } from "@/lib/utils" import type { ChartData, SystemStatsRecord } from "@/types" import { useYAxisWidth } from "./hooks" import type { AxisDomain } from "recharts/types/util/types" import { useIntersectionObserver } from "@/lib/use-intersection-observer" export type DataPoint = { label: string dataKey: (data: T) => number | null | undefined color: number | string opacity: number stackId?: string | number order?: number strokeOpacity?: number activeDot?: boolean } export default function AreaChartDefault({ chartData, customData, max, maxToggled, tickFormatter, contentFormatter, dataPoints, domain, legend, itemSorter, showTotal = false, reverseStackOrder = false, hideYAxis = false, filter, truncate = false, }: // logRender = false, { chartData: ChartData // biome-ignore lint/suspicious/noExplicitAny: accepts different data source types (systemStats or containerData) customData?: any[] max?: number maxToggled?: boolean tickFormatter: (value: number, index: number) => string // biome-ignore lint/suspicious/noExplicitAny: recharts tooltip item interop contentFormatter: (item: any, key: string) => ReactNode // biome-ignore lint/suspicious/noExplicitAny: accepts DataPoint with different generic types dataPoints?: DataPoint[] domain?: AxisDomain legend?: boolean showTotal?: boolean // biome-ignore lint/suspicious/noExplicitAny: recharts tooltip item interop itemSorter?: (a: any, b: any) => number reverseStackOrder?: boolean hideYAxis?: boolean filter?: string truncate?: boolean // logRender?: boolean }) { const { yAxisWidth, updateYAxisWidth } = useYAxisWidth() const { isIntersecting, ref } = useIntersectionObserver({ freeze: false }) const sourceData = customData ?? chartData.systemStats const [displayData, setDisplayData] = useState(sourceData) const [displayMaxToggled, setDisplayMaxToggled] = useState(maxToggled) // Reduce chart redraws by only updating while visible or when chart time changes useEffect(() => { const shouldPrimeData = sourceData.length && !displayData.length const sourceChanged = sourceData !== displayData const shouldUpdate = shouldPrimeData || (sourceChanged && isIntersecting) if (shouldUpdate) { setDisplayData(sourceData) } if (isIntersecting && maxToggled !== displayMaxToggled) { setDisplayMaxToggled(maxToggled) } }, [displayData, displayMaxToggled, isIntersecting, maxToggled, sourceData]) // Use a stable key derived from data point identities and visual properties const areasKey = dataPoints?.map((d) => `${d.label}:${d.opacity}`).join("\0") const Areas = useMemo(() => { return dataPoints?.map((dataPoint, i) => { let { color } = dataPoint if (typeof color === "number") { color = `var(--chart-${color})` } return ( ) }) }, [areasKey, displayMaxToggled]) return useMemo(() => { if (displayData.length === 0) { return null } // if (logRender) { // console.log("Rendered", dataPoints?.map((d) => d.label).join(", "), new Date()) // } return ( {!hideYAxis && ( updateYAxisWidth(tickFormatter(value, index))} tickLine={false} axisLine={false} /> )} {xAxis(chartData)} formatShortDate(data[0].payload.created)} contentFormatter={contentFormatter} showTotal={showTotal} filter={filter} truncate={truncate} /> } /> {Areas} {legend && } />} ) }, [displayData, yAxisWidth, filter, Areas]) }