all files / popper/modifiers/ flip.js

94.74% Statements 36/38
94.64% Branches 53/56
100% Functions 2/2
94.74% Lines 36/38
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108                                        103×     99×         99×   99× 99× 99×   99×   99× 99×               99× 198× 99×     99× 99×   99× 99×     99×   99×           99× 99× 99× 99×     99×             99× 99×             99×   22×   22× 14×     22×     22× 22×   22×     99×    
import getOppositePlacement from '../utils/getOppositePlacement';
import getOppositeVariation from '../utils/getOppositeVariation';
import getClientRect from '../utils/getClientRect';
import getPopperOffsets from '../utils/getPopperOffsets';
import runModifiers from '../utils/runModifiers';
import getBoundaries from '../utils/getBoundaries';
import isModifierEnabled from '../utils/isModifierEnabled';
 
/**
 * Modifier used to flip the placement of the popper when the latter is starting overlapping its reference element.
 * Requires the `preventOverflow` modifier before it in order to work.
 * **NOTE:** data.instance modifier will run all its previous modifiers everytime it tries to flip the popper!
 * @method
 * @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;
    }
 
    Iif (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, options.padding, options.boundariesElement);
 
    let placement = data.placement.split('-')[0];
    let placementOpposite = getOppositePlacement(placement);
    let variation = data.placement.split('-')[1] || '';
 
    let flipOrder = [];
 
    Eif (options.behavior === 'flip') {
        flipOrder = [
            placement,
            placementOpposite
        ];
    } else {
        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 = getClientRect(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;
        const flippedVariation = !!options.flipVariations && (
            (isVertical  && variation === 'start' && overflowsLeft) ||
            (isVertical  && variation === 'end'   && overflowsRight) ||
            (!isVertical && variation === 'start' && overflowsTop) ||
            (!isVertical && variation === 'end'   && overflowsBottom)
        );
 
        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 : '');
            data.offsets.popper = getPopperOffsets(data.instance.state, data.instance.popper, data.offsets.reference, data.placement);
 
            data = runModifiers(data.instance.modifiers, data, 'flip');
        }
    });
    return data;
}