import getOppositePlacement from '../utils/getOppositePlacement'; import getOppositeVariation from '../utils/getOppositeVariation'; import getPopperOffsets from '../utils/getPopperOffsets'; import runModifiers from '../utils/runModifiers'; import getBoundaries from '../utils/getBoundaries'; import isModifierEnabled from '../utils/isModifierEnabled'; import clockwise from '../utils/clockwise'; const BEHAVIORS = { FLIP: 'flip', CLOCKWISE: 'clockwise', COUNTERCLOCKWISE: 'counterclockwise', }; /** * @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 flip(data, options) { // if `inner` modifier is enabled, we can't use the `flip` modifier if (isModifierEnabled(data.instance.modifiers, 'inner')) { return data; } if (data.flipped && data.placement === data.originalPlacement) { // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides return data; } const boundaries = getBoundaries( data.instance.popper, data.instance.reference, options.padding, options.boundariesElement, data.positionFixed ); let placement = data.placement.split('-')[0]; let placementOpposite = getOppositePlacement(placement); let variation = data.placement.split('-')[1] || ''; let flipOrder = []; switch (options.behavior) { case BEHAVIORS.FLIP: flipOrder = [placement, placementOpposite]; break; case BEHAVIORS.CLOCKWISE: flipOrder = clockwise(placement); break; case BEHAVIORS.COUNTERCLOCKWISE: flipOrder = clockwise(placement, true); break; default: flipOrder = options.behavior; } flipOrder.forEach((step, index) => { if (placement !== step || flipOrder.length === index + 1) { return data; } placement = data.placement.split('-')[0]; placementOpposite = getOppositePlacement(placement); const popperOffsets = data.offsets.popper; const refOffsets = data.offsets.reference; // using floor because the reference offsets may contain decimals we are not going to consider here const floor = Math.floor; const overlapsRef = (placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left)) || (placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right)) || (placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top)) || (placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom)); const overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left); const overflowsRight = floor(popperOffsets.right) > floor(boundaries.right); const overflowsTop = floor(popperOffsets.top) < floor(boundaries.top); const overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom); const overflowsBoundaries = (placement === 'left' && overflowsLeft) || (placement === 'right' && overflowsRight) || (placement === 'top' && overflowsTop) || (placement === 'bottom' && overflowsBottom); // flip the variation if required const isVertical = ['top', 'bottom'].indexOf(placement) !== -1; // flips variation if reference element overflows boundaries const flippedVariationByRef = !!options.flipVariations && ((isVertical && variation === 'start' && overflowsLeft) || (isVertical && variation === 'end' && overflowsRight) || (!isVertical && variation === 'start' && overflowsTop) || (!isVertical && variation === 'end' && overflowsBottom)); // flips variation if popper content overflows boundaries const flippedVariationByContent = !!options.flipVariationsByContent && ((isVertical && variation === 'start' && overflowsRight) || (isVertical && variation === 'end' && overflowsLeft) || (!isVertical && variation === 'start' && overflowsBottom) || (!isVertical && variation === 'end' && overflowsTop)); const flippedVariation = flippedVariationByRef || flippedVariationByContent; if (overlapsRef || overflowsBoundaries || flippedVariation) { // this boolean to detect any flip loop data.flipped = true; if (overlapsRef || overflowsBoundaries) { placement = flipOrder[index + 1]; } if (flippedVariation) { variation = getOppositeVariation(variation); } data.placement = placement + (variation ? '-' + variation : ''); // this object contains `position`, we want to preserve it along with // any additional property we may add in the future data.offsets.popper = { ...data.offsets.popper, ...getPopperOffsets( data.instance.popper, data.offsets.reference, data.placement ), }; data = runModifiers(data.instance.modifiers, data, 'flip'); } }); return data; }