import { getCommonOptions, mergeEchartWidgetConfig, type EchartOptionsProps, } from '../echart' import type { BarConfig, BarWidgetConfig, BarWidgetData } from './types' import { flattenObjectArrayToCSV, buildLegendConfig, buildGridConfig, createTooltipPositioner, createTooltipFormatter, createChartDownloadConfig, applyXAxisFormatter, niceNum, buildSeriesLabelConfig, } from '../utils/chart-config' export const barDownloadConfig = createChartDownloadConfig( flattenObjectArrayToCSV, ) /** * Generates ECharts configuration for bar chart widgets (vertical and horizontal), including axis, tooltip, legend, and series options styled with the CARTO theme. * * @param props - Bar chart configuration including data and theme. * @returns Widget config with ECharts option object. */ export function barConfig(props: BarConfig): BarWidgetConfig { return { type: 'bar', option: mergeEchartWidgetConfig(getCommonOptions(props), getOption(props)), formatter: props.formatter, labelFormatter: props.labelFormatter, } } function getOption({ data = [], theme, formatter, labelFormatter, }: BarConfig): EchartOptionsProps { const hasLegend = (data?.length ?? 0) > 1 let niceMin = 0 let niceMax = 1 return { legend: buildLegendConfig({ hasLegend, labelFormatter }), grid: buildGridConfig(hasLegend, theme), xAxis: applyXAxisFormatter( { type: 'category', axisLine: { show: false, }, axisTick: { show: false, }, axisLabel: { padding: [parseInt(theme.spacing(0.5)), 0, 0, 0], margin: 0, showMinLabel: undefined, showMaxLabel: undefined, hideOverlap: true, }, }, labelFormatter, ), yAxis: { type: 'value' as const, min: (extent: { min: number }) => { niceMin = extent.min < 0 ? niceNum(extent.min) : 0 return niceMin }, max: (extent: { min: number; max: number }) => { niceMax = extent.max <= 0 ? 1 : niceNum(extent.max) return niceMax }, axisLabel: { fontSize: theme.typography.overlineDelicate.fontSize, fontFamily: theme.typography.overlineDelicate.fontFamily, margin: parseInt(theme.spacing(1)), show: true, showMaxLabel: true, showMinLabel: true, verticalAlign: 'bottom' as const, inside: true, formatter: (value: number) => { if (value !== niceMax && value !== niceMin) return '' if (value === 0) return '' return formatter ? formatter(value) : String(value) }, }, axisLine: { show: false, }, axisTick: { show: false, }, splitLine: { show: true, lineStyle: { color: theme.palette.black[4], }, }, }, tooltip: { position: createTooltipPositioner(theme), formatter: createTooltipFormatter((item) => { const value = item.value as Record const index = item.dimensionNames?.[item.encode?.y?.at(0) ?? 1] const _value = value[index ?? ''] const formattedValue = typeof _value === 'number' && formatter ? formatter(_value) : (_value ?? '') const marker = typeof item.marker === 'string' ? item.marker : '' const seriesName = item.seriesName ? `${item.seriesName}: ` : '' const name = labelFormatter ? String(labelFormatter(item.name ?? '')) : (item.name ?? '') return { name, seriesName, marker, value: formattedValue } }), }, series: data.map((_: unknown, index: number) => ({ datasetIndex: index, type: 'bar', barMaxWidth: 100, emphasis: { focus: 'series', }, ...buildSeriesLabelConfig(formatter), })), } as EchartOptionsProps }