/** * ECharts 코어 래퍼 컴포넌트 * - 자동 리사이즈 (ResizeObserver) * - kcg-dark 테마 자동 적용 * - dispose 자동 정리 */ import { useRef, useEffect } from 'react'; import * as echarts from 'echarts/core'; import { BarChart, LineChart, PieChart, RadarChart, HeatmapChart } from 'echarts/charts'; import { GridComponent, TooltipComponent, LegendComponent, TitleComponent, DatasetComponent, PolarComponent, RadarComponent, } from 'echarts/components'; import { CanvasRenderer } from 'echarts/renderers'; import type { EChartsOption, ECharts } from 'echarts'; import './theme'; echarts.use([ BarChart, LineChart, PieChart, RadarChart, HeatmapChart, GridComponent, TooltipComponent, LegendComponent, TitleComponent, DatasetComponent, PolarComponent, RadarComponent, CanvasRenderer, ]); interface BaseChartProps { option: EChartsOption; className?: string; style?: React.CSSProperties; height?: number; notMerge?: boolean; onEvents?: Record void>; } export function BaseChart({ option, className = '', style, height = 200, notMerge = false, onEvents, }: BaseChartProps) { const containerRef = useRef(null); const chartRef = useRef(null); useEffect(() => { if (!containerRef.current) return; const chart = echarts.init(containerRef.current, 'kcg-dark'); chartRef.current = chart as unknown as ECharts; chart.setOption(option, notMerge); if (onEvents) { Object.entries(onEvents).forEach(([event, handler]) => { chart.on(event, handler); }); } const ro = new ResizeObserver(() => chart.resize()); ro.observe(containerRef.current); return () => { ro.disconnect(); chart.dispose(); chartRef.current = null; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { if (chartRef.current) { chartRef.current.setOption(option, notMerge); } }, [option, notMerge]); return (
); }