in packages/charts/src/chart_types/xy_chart/legend/legend.ts [89:202]
export function computeLegend(
xDomain: XDomain,
dataSeries: DataSeries[],
seriesColors: Map<SeriesKey, Color>,
specs: BasicSeriesSpec[],
axesSpecs: AxisSpec[],
settingsSpec: SettingsSpec,
seriesIdentifierDataSeriesMap: Record<string, DataSeries>,
theme: Theme,
deselectedDataSeries: SeriesIdentifier[] = [],
): LegendItem[] {
const legendItems: LegendItem[] = [];
const defaultColor = theme.colors.defaultVizColor;
const legendValues = settingsSpec.legendValues ?? [];
dataSeries.forEach((series) => {
const { specId, yAccessor } = series;
const banded = isBandedSpec(series.spec);
const spec = getSpecsById<BasicSeriesSpec>(specs, specId);
const dataSeriesKey = getSeriesKey(
{
specId: series.specId,
yAccessor: series.yAccessor,
splitAccessors: series.splitAccessors,
},
series.groupId,
);
const color = seriesColors.get(dataSeriesKey) || defaultColor;
const hasSingleSeries = dataSeries.length === 1;
const name = getSeriesName(series, hasSingleSeries, false, spec);
const isSeriesHidden = deselectedDataSeries && getSeriesIndex(deselectedDataSeries, series) >= 0;
if (name === '' || !spec) return;
const postFixes = getPostfix(spec);
// Use this to get axis spec w/ tick formatter
const { yAxis } = getAxesSpecForSpecId(axesSpecs, spec.groupId, settingsSpec.rotation);
const formatter = spec.tickFormat ?? yAxis?.tickFormat ?? defaultTickFormatter;
const { hideInLegend } = spec;
const seriesIdentifier = getSeriesIdentifierFromDataSeries(series);
const pointStyle = getPointStyle(spec, theme);
const legendValuesItems = getLegendValues(series, xDomain, legendValues, y1Accessor(series.stackMode), formatter);
legendItems.push({
depth: 0,
color,
label: banded ? getBandedLegendItemLabel(name, BandedAccessorType.Y1, postFixes) : name,
seriesIdentifiers: [seriesIdentifier],
childId: BandedAccessorType.Y1,
isSeriesHidden,
isItemHidden: hideInLegend,
isToggleable: true,
values: legendValuesItems,
path: [{ index: 0, value: seriesIdentifier.key }],
keys: [specId, spec.groupId, yAccessor, ...series.splitAccessors.values()],
pointStyle,
});
if (banded) {
const bandedLegendValuesItems = getLegendValues(
series,
xDomain,
legendValues,
y0Accessor(series.stackMode),
formatter,
);
const labelY0 = getBandedLegendItemLabel(name, BandedAccessorType.Y0, postFixes);
legendItems.push({
depth: 0,
color,
label: labelY0,
seriesIdentifiers: [seriesIdentifier],
childId: BandedAccessorType.Y0,
isSeriesHidden,
isItemHidden: hideInLegend,
isToggleable: true,
values: bandedLegendValuesItems,
path: [{ index: 0, value: seriesIdentifier.key }],
keys: [specId, spec.groupId, yAccessor, ...series.splitAccessors.values()],
pointStyle,
});
}
});
const baseLegendSortFn: SeriesCompareFn = (a, b) => {
const aDs = seriesIdentifierDataSeriesMap[a.key];
const bDs = seriesIdentifierDataSeriesMap[b.key];
return defaultXYLegendSeriesSort(aDs, bDs);
};
const legendSort = settingsSpec.legendSort ?? baseLegendSortFn;
return groupBy(
legendItems.sort((a, b) =>
a.seriesIdentifiers[0] && b.seriesIdentifiers[0] ? legendSort(a.seriesIdentifiers[0], b.seriesIdentifiers[0]) : 0,
),
({ keys, childId }) => {
return [...keys, childId].join('__'); // childId is used for band charts
},
true,
)
.map((d) => {
if (!d[0]) return;
return {
...d[0],
seriesIdentifiers: d.map(({ seriesIdentifiers: [s] }) => s).filter(isDefined),
path: d.map(({ path: [p] }) => p).filter(isDefined),
};
})
.filter(isDefined);
}