function getCrossAxisPosition()

in packages/eui/src/services/popover/popover_positioning.ts [437:554]


function getCrossAxisPosition({
  crossAxisFirstSide,
  crossAxisSecondSide,
  crossAxisDimension,
  position,
  align,
  buffer,
  offset,
  windowBoundingBox,
  containerBoundingBox,
  popoverBoundingBox,
  anchorBoundingBox,
  arrowConfig,
}: GetCrossAxisPositionArgs): CrossAxisPosition {
  // how much of the popover overflows past either side of the anchor if its centered
  const popoverSizeOnCrossAxis = popoverBoundingBox[crossAxisDimension];
  const anchorSizeOnCrossAxis = anchorBoundingBox[crossAxisDimension];
  const anchorHalfSize = anchorSizeOnCrossAxis / 2;

  // the popover's original position on the cross-axis is determined by:
  const crossAxisPositionOriginal =
    anchorBoundingBox[crossAxisFirstSide] + // where the anchor is located
    anchorHalfSize - // plus half anchor dimension
    popoverSizeOnCrossAxis / 2; // less half the popover dimension

  // To fit the content within both the window and container,
  // compute the smaller of the two spaces along each edge
  const combinedBoundingBox = intersectBoundingBoxes(
    windowBoundingBox,
    containerBoundingBox
  );
  const availableSpace = getAvailableSpace(
    anchorBoundingBox,
    combinedBoundingBox,
    buffer,
    offset,
    position
  );
  const minimumSpace = arrowConfig ? arrowConfig.arrowBuffer : 0;

  const contentOverflowSize =
    (popoverSizeOnCrossAxis - anchorSizeOnCrossAxis) / 2;

  let alignAmount = 0;
  let alignDirection = 1;
  let amountOfShiftNeeded = 0;
  let shiftDirection = 1;

  if (align != null) {
    // no alignment, find how much the container boundary requires the content to shift
    alignDirection = align === 'top' || align === 'left' ? 1 : -1;
    alignAmount = contentOverflowSize;

    const alignedOverflowAmount = contentOverflowSize + alignAmount;
    const needsShift =
      alignedOverflowAmount > availableSpace[positionComplements[align]];
    amountOfShiftNeeded = needsShift
      ? alignedOverflowAmount - availableSpace[positionComplements[align]]
      : 0;
    shiftDirection = -1 * alignDirection;
  } else {
    // shifting the popover to one side may yield a better fit
    const spaceAvailableOnFirstSide = availableSpace[crossAxisFirstSide];
    const spaceAvailableOnSecondSide = availableSpace[crossAxisSecondSide];

    const isShiftTowardFirstSide =
      spaceAvailableOnFirstSide > spaceAvailableOnSecondSide;
    shiftDirection = isShiftTowardFirstSide ? -1 : 1;

    // determine which direction has more room and the popover should shift to
    const leastAvailableSpace = Math.min(
      spaceAvailableOnFirstSide,
      spaceAvailableOnSecondSide
    );

    const needsShift = contentOverflowSize > leastAvailableSpace;
    amountOfShiftNeeded = needsShift
      ? contentOverflowSize - leastAvailableSpace
      : 0;
  }

  // shift over the popover if necessary
  const shiftAmount = amountOfShiftNeeded * shiftDirection;
  let crossAxisPosition =
    crossAxisPositionOriginal + shiftAmount + alignAmount * alignDirection;

  // if an `arrowConfig` is specified, find where to position the arrow
  let crossAxisArrowPosition;
  if (arrowConfig) {
    const { arrowWidth } = arrowConfig;
    crossAxisArrowPosition =
      anchorBoundingBox[crossAxisFirstSide] + anchorHalfSize - arrowWidth / 2;

    // make sure there's enough buffer around the arrow
    // by calculating how how much the arrow would need to move
    // but instead of moving the arrow, shift the popover content
    if (crossAxisArrowPosition < crossAxisPosition + minimumSpace) {
      // arrow is too close to the minimum side
      const difference =
        crossAxisPosition + minimumSpace - crossAxisArrowPosition;
      crossAxisPosition -= difference;
    } else if (
      crossAxisArrowPosition + minimumSpace + arrowWidth >
      crossAxisPosition + popoverSizeOnCrossAxis
    ) {
      // arrow is too close to the maximum side
      const edge = crossAxisPosition + popoverSizeOnCrossAxis;
      const difference =
        crossAxisArrowPosition - (edge - minimumSpace - arrowWidth);
      crossAxisPosition += difference;
    }
  }

  return {
    crossAxisPosition,
    crossAxisArrowPosition,
  };
}