import { getCommonOptions, mergeEchartWidgetConfig, type EchartOptionsProps, } from '../echart' import type { ScatterplotConfig, ScatterplotWidgetConfig, ScatterplotWidgetData, } from './types' import { scatterplotDataToCSV, buildLegendConfig, createTooltipFormatter, createChartDownloadConfig, applyYAxisFormatter, applyXAxisFormatter, buildSeriesLabelConfig, } from '../utils/chart-config' export const scatterplotDownloadConfig = createChartDownloadConfig(scatterplotDataToCSV) /** * Generates ECharts configuration for X-Y scatter plot widgets with value axes, legend support, and CARTO qualitative color palette. * * @param props - Scatterplot configuration including data points and theme. * @returns Widget config with ECharts option object. */ export function scatterplotConfig( props: ScatterplotConfig, ): ScatterplotWidgetConfig { return { type: 'scatterplot', option: mergeEchartWidgetConfig(getCommonOptions(props), getOption(props)), formatter: props.formatter, labelFormatter: props.labelFormatter, } } function getOption({ data = [], theme, formatter, labelFormatter, }: ScatterplotConfig): EchartOptionsProps { const hasLegend = data.length > 1 const xAxis = { type: 'value' as const, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { fontSize: theme.typography.overlineDelicate.fontSize, fontFamily: theme.typography.overlineDelicate.fontFamily, showMinLabel: true, showMaxLabel: true, hideOverlap: true, padding: [ parseInt(theme.spacing(0.5)), parseInt(theme.spacing(0.5)), 0, parseInt(theme.spacing(0.5)), ], color: theme.palette.black[60], }, splitLine: { show: true, lineStyle: { color: theme.palette.black[4] }, }, } const yAxis = { type: 'value' as const, axisLabel: { fontSize: theme.typography.overlineDelicate.fontSize, fontFamily: theme.typography.overlineDelicate.fontFamily, inside: false, padding: [ 0, 0, parseInt(theme.spacing(1.25)), parseInt(theme.spacing(3.25)), ], margin: 0, show: true, showMaxLabel: true, showMinLabel: false, align: 'right', verticalAlign: 'bottom', }, axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: true, lineStyle: { color: theme.palette.black[4] }, }, } return { legend: buildLegendConfig({ hasLegend, labelFormatter }), grid: { ...(!hasLegend && { bottom: parseInt(theme.spacing(1)) }), ...(hasLegend && { bottom: parseInt(theme.spacing(10)) }), }, xAxis: applyXAxisFormatter(xAxis, labelFormatter), yAxis: applyYAxisFormatter(yAxis, formatter), tooltip: { trigger: 'item', // position: createTooltipPositioner(theme), formatter: createTooltipFormatter((item) => { const value = item.value as Record const index = item.encode?.y?.at(0) const _value = value[index ?? ''] const formattedValue = typeof _value === 'number' && formatter ? formatter(_value) : (_value ?? '') const marker = typeof item.marker === 'string' ? item.marker : '' const name = labelFormatter ? String(labelFormatter(item.seriesName ?? '')) : (item.seriesName ?? '') return { name, seriesName: '', marker, value: formattedValue } }), }, color: Object.values(theme.palette.qualitative.bold), series: data.map((_: unknown, index: number) => ({ datasetIndex: index, type: 'scatter', symbolSize: 8, ...buildSeriesLabelConfig(formatter), })), } as EchartOptionsProps }