import type { ComposeOption } from 'echarts/core'; import type { BarSeriesOption, LineSeriesOption, PieSeriesOption, ScatterSeriesOption, RadarSeriesOption, } from 'echarts/charts'; import type { ChartData, ChartType } from '@openmaic/dsl'; type EChartOption = ComposeOption< BarSeriesOption | LineSeriesOption | PieSeriesOption | ScatterSeriesOption | RadarSeriesOption >; export interface ChartOptionPayload { type: ChartType; data: ChartData; themeColors: string[]; textColor?: string; lineColor?: string; lineSmooth?: boolean; stack?: boolean; } export const getChartOption = ({ type, data, themeColors, textColor, lineColor, lineSmooth, stack, }: ChartOptionPayload): EChartOption | null => { const textStyle = textColor ? { color: textColor } : {}; const axisLine = textColor ? { lineStyle: { color: textColor } } : undefined; const axisLabel = { show: true, color: textColor ?? '#333333' }; const splitLine = lineColor ? { lineStyle: { color: lineColor } } : {}; if (!Array.isArray(data?.series) || data.series.length === 0 || !Array.isArray(data.labels)) { return null; } const categoryAxisLabel = { ...axisLabel, interval: data.labels.length <= 8 ? 0 : ('auto' as const), }; const legend = data.series.length > 1 ? { top: 'bottom' as const, textStyle } : undefined; if (type === 'bar') { return { color: themeColors, textStyle, legend, xAxis: { type: 'category', data: data.labels, axisLine, axisLabel: categoryAxisLabel }, yAxis: { type: 'value', axisLine, axisLabel, splitLine }, series: data.series.map((item, index) => { const seriesItem: BarSeriesOption = { data: item, name: data.legends[index], type: 'bar', label: { show: true }, itemStyle: { borderRadius: [2, 2, 0, 0] }, }; if (stack) seriesItem.stack = 'A'; return seriesItem; }), }; } if (type === 'column') { return { color: themeColors, textStyle, legend, yAxis: { type: 'category', data: data.labels, axisLine, axisLabel: categoryAxisLabel }, xAxis: { type: 'value', axisLine, axisLabel, splitLine }, series: data.series.map((item, index) => { const seriesItem: BarSeriesOption = { data: item, name: data.legends[index], type: 'bar', label: { show: true }, itemStyle: { borderRadius: [0, 2, 2, 0] }, }; if (stack) seriesItem.stack = 'A'; return seriesItem; }), }; } if (type === 'line') { return { color: themeColors, textStyle, legend, xAxis: { type: 'category', data: data.labels, axisLine, axisLabel: categoryAxisLabel }, yAxis: { type: 'value', axisLine, axisLabel, splitLine }, series: data.series.map((item, index) => { const seriesItem: LineSeriesOption = { data: item, name: data.legends[index], type: 'line', smooth: lineSmooth, label: { show: true }, }; if (stack) seriesItem.stack = 'A'; return seriesItem; }), }; } if (type === 'pie') { const series0 = data.series[0]; if (!Array.isArray(series0)) return null; return { color: themeColors, textStyle, legend: { top: 'bottom' as const, textStyle }, series: [ { data: series0.map((item, index) => ({ value: item, name: data.labels[index] })), label: textColor ? { color: textColor } : {}, type: 'pie', radius: '70%', emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' }, label: { show: true, fontSize: 14, fontWeight: 'bold' }, }, }, ], }; } if (type === 'ring') { const series0 = data.series[0]; if (!Array.isArray(series0)) return null; return { color: themeColors, textStyle, legend: { top: 'bottom' as const, textStyle }, series: [ { data: series0.map((item, index) => ({ value: item, name: data.labels[index] })), label: textColor ? { color: textColor } : {}, type: 'pie', radius: ['40%', '70%'], padAngle: 1, avoidLabelOverlap: false, itemStyle: { borderRadius: 4 }, emphasis: { label: { show: true, fontSize: 14, fontWeight: 'bold' } }, }, ], }; } if (type === 'area') { return { color: themeColors, textStyle, legend, xAxis: { type: 'category', boundaryGap: false, data: data.labels, axisLine, axisLabel: categoryAxisLabel, }, yAxis: { type: 'value', axisLine, axisLabel, splitLine }, series: data.series.map((item, index) => { const seriesItem: LineSeriesOption = { data: item, name: data.legends[index], type: 'line', areaStyle: {}, label: { show: true }, }; if (stack) seriesItem.stack = 'A'; return seriesItem; }), }; } if (type === 'radar') { return { color: themeColors, textStyle, legend, radar: { indicator: data.labels.map((item) => ({ name: item })), splitLine, axisLine: lineColor ? { lineStyle: { color: lineColor } } : undefined, }, series: [ { data: data.series.map((item, index) => ({ value: item, name: data.legends[index] })), type: 'radar', }, ], }; } if (type === 'scatter') { const series0 = data.series[0]; if (!Array.isArray(series0)) return null; const formatedData: number[][] = []; for (let i = 0; i < series0.length; i++) { const x = series0[i]; const y = data.series[1]?.[i] ?? x; formatedData.push([x, y]); } return { color: themeColors, textStyle, xAxis: { axisLine, axisLabel, splitLine }, yAxis: { axisLine, axisLabel, splitLine }, series: [{ symbolSize: 12, data: formatedData, type: 'scatter' }], }; } return null; };