export function computeRectAnnotationDimensions()

in packages/charts/src/chart_types/xy_chart/annotations/rect/dimensions.ts [37:174]


export function computeRectAnnotationDimensions(
  annotationSpec: RectAnnotationSpec,
  yScales: Map<GroupId, ScaleContinuous>,
  xScale: ScaleBand | ScaleContinuous,
  axesSpecs: AxisSpec[],
  smallMultiplesScales: SmallMultipleScales,
  chartRotation: Rotation,
  getAxisStyle: (id?: AxisId) => AxisStyle,
  isHistogram: boolean = false,
): AnnotationRectProps[] | null {
  const { dataValues, groupId, outside, id: annotationSpecId } = annotationSpec;
  const { xAxis, yAxis } = getAxesSpecForSpecId(axesSpecs, groupId, chartRotation);
  const yScale = yScales.get(groupId);
  const rectsProps: Omit<AnnotationRectProps, 'id' | 'panel'>[] = [];
  const panelSize = getPanelSize(smallMultiplesScales);

  dataValues.forEach((datum: RectAnnotationDatum) => {
    const { x0: initialX0, x1: initialX1, y0: initialY0, y1: initialY1 } = datum.coordinates;

    // if everything is null, return; otherwise we coerce the other coordinates
    if (initialX0 === null && initialX1 === null && initialY0 === null && initialY1 === null) {
      return;
    }
    let height: number | undefined;

    const [x0, x1] = limitValueToDomainRange(xScale, initialX0, initialX1, isHistogram);
    // something is wrong with the data types, don't draw this annotation
    if (x0 === null || x1 === null) {
      return;
    }

    let xAndWidth: { x: number; width: number } | null = null;

    if (isBandScale(xScale)) {
      xAndWidth = scaleXonBandScale(xScale, x0, x1);
    } else if (isContinuousScale(xScale)) {
      xAndWidth = scaleXonContinuousScale(xScale, x0, x1, isHistogram);
    }
    // something is wrong with scales, don't draw
    if (!xAndWidth) {
      return;
    }

    const hasYValues = isDefined(initialY0) || isDefined(initialY1);
    const outsideDim = annotationSpec.outsideDimension ?? getOutsideDimension(getAxisStyle(xAxis?.id ?? yAxis?.id));

    if (!yScale) {
      if (!hasYValues) {
        // only x values present, just fill full height of chart space
        const rectDimensions: Rect = {
          ...xAndWidth,
          ...(outside
            ? getXOutsideAnnotationDimensions(panelSize, chartRotation, xAxis?.position ?? 'bottom', outsideDim)
            : {
                y: 0,
                height: isHorizontalRotation(chartRotation) ? panelSize.height : panelSize.width,
              }),
        };
        rectsProps.push({
          specId: annotationSpecId,
          rect: rectDimensions,
          datum,
        });
      }
      return; // cannot scale y values without a scale
    }

    const hasXValues = isDefined(initialX0) || isDefined(initialX1);

    if (outside) {
      if (hasXValues && hasYValues) {
        Logger.warn(
          `The RectAnnotation (${annotationSpecId}) was defined as outside but has both x and y values defined.`,
        );
      } else if (hasXValues) {
        const rectDimensions: Rect = {
          ...xAndWidth,
          ...getXOutsideAnnotationDimensions(panelSize, chartRotation, xAxis?.position ?? 'bottom', outsideDim),
        };

        rectsProps.push({
          specId: annotationSpecId,
          rect: rectDimensions,
          datum,
        });
        return;
      }
    }

    const [y0, y1] = limitValueToDomainRange(yScale, initialY0, initialY1);

    // something is wrong with the data types, don't draw this annotation
    if (!Number.isFinite(y0) || !Number.isFinite(y1)) return;

    let scaledY1 = yScale.pureScale(y1);
    const scaledY0 = yScale.pureScale(y0);
    if (Number.isNaN(scaledY1) || Number.isNaN(scaledY0)) return;

    height = Math.abs(scaledY0 - scaledY1);

    // if the annotation height is 0, override it with the height from chart dimension and if the values in the domain are the same
    if (height === 0 && yScale.domain.length === 2 && yScale.domain[0] === yScale.domain[1]) {
      // eslint-disable-next-line prefer-destructuring
      height = panelSize.height;
      scaledY1 = 0;
    }

    const rectDimensions: Rect = {
      ...xAndWidth,
      y: scaledY1,
      height,
      ...(outside &&
        !(hasXValues && hasYValues) &&
        getYOutsideAnnotationDimensions(panelSize, chartRotation, yAxis?.position ?? 'left', outsideDim)),
    };

    rectsProps.push({
      specId: annotationSpecId,
      rect: rectDimensions,
      datum,
    });
  });

  return rectsProps.reduce<AnnotationRectProps[]>((acc, props, i) => {
    const duplicated: AnnotationRectProps[] = [];
    smallMultiplesScales.vertical.domain.forEach((vDomainValue) => {
      smallMultiplesScales.horizontal.domain.forEach((hDomainValue) => {
        const id = getAnnotationRectPropsId(annotationSpecId, props.datum, i, vDomainValue, hDomainValue);
        const top = smallMultiplesScales.vertical.scale(vDomainValue);
        const left = smallMultiplesScales.horizontal.scale(hDomainValue);
        if (Number.isNaN(top + left)) return;
        const panel = { ...panelSize, top, left };
        duplicated.push({ ...props, panel, id });
      });
    });
    return acc.concat(duplicated);
  }, []);
}