preventOverflow.js 2.04 KB
import getOffsetParent from '../utils/getOffsetParent';
import getBoundaries from '../utils/getBoundaries';

/**
 * @function
 * @memberof Modifiers
 * @argument {Object} data - The data object generated by `update` method
 * @argument {Object} options - Modifiers configuration and options
 * @returns {Object} The data object, properly modified
 */
export default function preventOverflow(data, options) {
  let boundariesElement =
    options.boundariesElement || getOffsetParent(data.instance.popper);

  // If offsetParent is the reference element, we really want to
  // go one step up and use the next offsetParent as reference to
  // avoid to make this modifier completely useless and look like broken
  if (data.instance.reference === boundariesElement) {
    boundariesElement = getOffsetParent(boundariesElement);
  }

  const boundaries = getBoundaries(
    data.instance.popper,
    data.instance.reference,
    options.padding,
    boundariesElement
  );
  options.boundaries = boundaries;

  const order = options.priority;
  let popper = data.offsets.popper;

  const check = {
    primary(placement) {
      let value = popper[placement];
      if (
        popper[placement] < boundaries[placement] &&
        !options.escapeWithReference
      ) {
        value = Math.max(popper[placement], boundaries[placement]);
      }
      return { [placement]: value };
    },
    secondary(placement) {
      const mainSide = placement === 'right' ? 'left' : 'top';
      let value = popper[mainSide];
      if (
        popper[placement] > boundaries[placement] &&
        !options.escapeWithReference
      ) {
        value = Math.min(
          popper[mainSide],
          boundaries[placement] -
            (placement === 'right' ? popper.width : popper.height)
        );
      }
      return { [mainSide]: value };
    },
  };

  order.forEach(placement => {
    const side = ['left', 'top'].indexOf(placement) !== -1
      ? 'primary'
      : 'secondary';
    popper = { ...popper, ...check[side](placement) };
  });

  data.offsets.popper = popper;

  return data;
}