mirror of
https://github.com/apache/superset.git
synced 2026-05-12 19:35:17 +00:00
feat(bar-chart): add option to color bars by primary axis when no dimensions are set (#37531)
Co-authored-by: madhushree agarwal <madhushree_agarwal@apple.com>
This commit is contained in:
@@ -175,6 +175,7 @@ export default function transformProps(
|
||||
seriesType,
|
||||
showLegend,
|
||||
showValue,
|
||||
colorByPrimaryAxis,
|
||||
sliceId,
|
||||
sortSeriesType,
|
||||
sortSeriesAscending,
|
||||
@@ -421,6 +422,7 @@ export default function transformProps(
|
||||
timeShiftColor,
|
||||
theme,
|
||||
hasDimensions: (groupBy?.length ?? 0) > 0,
|
||||
colorByPrimaryAxis,
|
||||
},
|
||||
);
|
||||
if (transformedSeries) {
|
||||
@@ -438,6 +440,59 @@ export default function transformProps(
|
||||
}
|
||||
});
|
||||
|
||||
// Add x-axis color legend when colorByPrimaryAxis is enabled
|
||||
if (colorByPrimaryAxis && groupBy.length === 0 && series.length > 0) {
|
||||
// Hide original series from legend
|
||||
series.forEach(s => {
|
||||
s.legendHoverLink = false;
|
||||
});
|
||||
|
||||
// Get x-axis values from the first series
|
||||
const firstSeries = series[0];
|
||||
if (firstSeries && Array.isArray(firstSeries.data)) {
|
||||
const xAxisValues: (string | number)[] = [];
|
||||
|
||||
// Extract primary axis values (category axis)
|
||||
// For horizontal charts the category is at index 1, for vertical at index 0
|
||||
const primaryAxisIndex = isHorizontal ? 1 : 0;
|
||||
(firstSeries.data as any[]).forEach(point => {
|
||||
let xValue;
|
||||
if (point && typeof point === 'object' && 'value' in point) {
|
||||
const val = point.value;
|
||||
xValue = Array.isArray(val) ? val[primaryAxisIndex] : val;
|
||||
} else if (Array.isArray(point)) {
|
||||
xValue = point[primaryAxisIndex];
|
||||
} else {
|
||||
xValue = point;
|
||||
}
|
||||
xAxisValues.push(xValue);
|
||||
});
|
||||
|
||||
// Create hidden series for legend (using 'line' type to not affect bar width)
|
||||
// Deduplicate x-axis values to avoid duplicate legend entries and unnecessary series
|
||||
const uniqueXAxisValues = Array.from(
|
||||
new Set(xAxisValues.map(v => String(v))),
|
||||
);
|
||||
uniqueXAxisValues.forEach(xValue => {
|
||||
const colorKey = xValue;
|
||||
series.push({
|
||||
name: xValue,
|
||||
type: 'line', // Use line type to not affect bar positioning
|
||||
data: [], // Empty - doesn't render
|
||||
itemStyle: {
|
||||
color: colorScale(colorKey, sliceId),
|
||||
},
|
||||
lineStyle: {
|
||||
color: colorScale(colorKey, sliceId),
|
||||
},
|
||||
silent: true,
|
||||
legendHoverLink: false,
|
||||
showSymbol: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (stack === StackControlsValue.Stream) {
|
||||
const baselineSeries = getBaselineSeriesForStream(
|
||||
series.map(entry => entry.data) as [string | number, number][][],
|
||||
@@ -592,14 +647,43 @@ export default function transformProps(
|
||||
isHorizontal,
|
||||
);
|
||||
|
||||
const legendData = rawSeries
|
||||
.filter(
|
||||
entry =>
|
||||
extractForecastSeriesContext(entry.name || '').type ===
|
||||
ForecastSeriesEnum.Observation,
|
||||
)
|
||||
.map(entry => entry.name || '')
|
||||
.concat(extractAnnotationLabels(annotationLayers));
|
||||
const legendData =
|
||||
colorByPrimaryAxis && groupBy.length === 0 && series.length > 0
|
||||
? // When colorByPrimaryAxis is enabled, show only primary axis values (deduped + filtered)
|
||||
(() => {
|
||||
const firstSeries = series[0];
|
||||
// For horizontal charts the category is at index 1, for vertical at index 0
|
||||
const primaryAxisIndex = isHorizontal ? 1 : 0;
|
||||
if (firstSeries && Array.isArray(firstSeries.data)) {
|
||||
const names = (firstSeries.data as any[])
|
||||
.map(point => {
|
||||
if (point && typeof point === 'object' && 'value' in point) {
|
||||
const val = point.value;
|
||||
return String(
|
||||
Array.isArray(val) ? val[primaryAxisIndex] : val,
|
||||
);
|
||||
}
|
||||
if (Array.isArray(point)) {
|
||||
return String(point[primaryAxisIndex]);
|
||||
}
|
||||
return String(point);
|
||||
})
|
||||
.filter(
|
||||
name => name !== '' && name !== 'undefined' && name !== 'null',
|
||||
);
|
||||
return Array.from(new Set(names));
|
||||
}
|
||||
return [];
|
||||
})()
|
||||
: // Otherwise show original series names
|
||||
rawSeries
|
||||
.filter(
|
||||
entry =>
|
||||
extractForecastSeriesContext(entry.name || '').type ===
|
||||
ForecastSeriesEnum.Observation,
|
||||
)
|
||||
.map(entry => entry.name || '')
|
||||
.concat(extractAnnotationLabels(annotationLayers));
|
||||
|
||||
let xAxis: any = {
|
||||
type: xAxisType,
|
||||
@@ -818,10 +902,27 @@ export default function transformProps(
|
||||
padding,
|
||||
),
|
||||
scrollDataIndex: legendIndex || 0,
|
||||
data: legendData.sort((a: string, b: string) => {
|
||||
if (!legendSort) return 0;
|
||||
return legendSort === 'asc' ? a.localeCompare(b) : b.localeCompare(a);
|
||||
}) as string[],
|
||||
data:
|
||||
colorByPrimaryAxis && groupBy.length === 0
|
||||
? // When colorByPrimaryAxis, configure legend items with roundRect icons
|
||||
legendData.map(name => ({
|
||||
name,
|
||||
icon: 'roundRect',
|
||||
}))
|
||||
: // Otherwise use normal legend data
|
||||
legendData.sort((a: string, b: string) => {
|
||||
if (!legendSort) return 0;
|
||||
return legendSort === 'asc'
|
||||
? a.localeCompare(b)
|
||||
: b.localeCompare(a);
|
||||
}),
|
||||
// Disable legend selection and buttons when colorByPrimaryAxis is enabled
|
||||
...(colorByPrimaryAxis && groupBy.length === 0
|
||||
? {
|
||||
selectedMode: false, // Disable clicking legend items
|
||||
selector: false, // Hide All/Invert buttons
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
series: dedupSeries(reorderForecastSeries(series) as SeriesOption[]),
|
||||
toolbox: {
|
||||
|
||||
Reference in New Issue
Block a user