feat: color binding splits bar chart into colored series with legend

Dragging a field to the 'color' slot now creates separate colored bars
per category value, with auto-generated legend. Also aggregates
multiple rows per category (sum) instead of taking first match only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hailin 2026-04-05 02:46:49 -07:00
parent bb9273706e
commit 10b50bb2e0
1 changed files with 15 additions and 9 deletions

View File

@ -149,16 +149,21 @@ export function buildBarOption(
const label = buildLabelConfig(style);
if ((isGrouped || isStacked) && seriesBinding) {
const seriesField = seriesBinding.columnName;
const seriesNames = [...new Set(data.map((row) => String(row[seriesField])))];
const colorBinding = getBinding(bindings, 'color');
const splitField = seriesBinding?.columnName ?? colorBinding?.columnName;
const hasSplit = !!splitField;
const seriesList = seriesNames.map((name, idx) => {
if (hasSplit) {
// Multi-series: split by series or color field
const splitNames = [...new Set(data.map((row) => String(row[splitField])))];
const seriesList = splitNames.map((name, idx) => {
const seriesData = categories.map((cat) => {
const row = data.find(
(r) => String(r[xField]) === cat && String(r[seriesField]) === name,
// Sum all matching rows (there may be multiple)
const matchingRows = data.filter(
(r) => String(r[xField]) === cat && String(r[splitField]) === name,
);
return row ? Number(row[yField]) : 0;
return matchingRows.reduce((sum, r) => sum + (Number(r[yField]) || 0), 0);
});
return {
@ -183,9 +188,10 @@ export function buildBarOption(
option.series = seriesList;
option.tooltip = { trigger: 'axis' };
} else {
// Single series: aggregate by x category
const seriesData = categories.map((cat) => {
const row = data.find((r) => String(r[xField]) === cat);
return row ? Number(row[yField]) : 0;
const matchingRows = data.filter((r) => String(r[xField]) === cat);
return matchingRows.reduce((sum, r) => sum + (Number(r[yField]) || 0), 0);
});
if (isHorizontal) {