147 lines
5 KiB
JavaScript
147 lines
5 KiB
JavaScript
|
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;
|
||
|
}
|