export function computeLegend()

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);
}