all files / popper/modifiers/ preventOverflow.js

93.33% Statements 28/30
80.77% Branches 21/26
100% Functions 4/4
93.33% Lines 28/30
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                                                    125× 125× 125×   125× 125×   125×   250× 250× 26×   250×     250× 250× 250×   250×       125× 500× 500×     125×   125×             33× 31×                                            
import getClientRect from '../utils/getClientRect';
import getOppositePlacement from '../utils/getOppositePlacement';
import getOffsetParent from '../utils/getOffsetParent';
import getBoundaries from '../utils/getBoundaries';
 
/**
 * Modifier used to prevent the popper from being positioned outside the boundary.
 *
 * An scenario exists where the reference itself is not within the boundaries. We can
 * say it has "escaped the boundaries" — or just "escaped". In this case we need to
 * decide whether the popper should either:
 *
 * - detach from the reference and remain "trapped" in the boundaries, or
 * - if it should be ignore the boundary and "escape with the reference"
 *
 * When `escapeWithReference` is `true`, and reference is completely outside the
 * boundaries, the popper will overflow (or completely leave) the boundaries in order
 * to remain attached to the edge of the reference.
 *
 * @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 preventOverflow(data, options) {
    const boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper);
    const boundaries = getBoundaries(data.instance.popper, options.padding, boundariesElement);
    options.boundaries = boundaries;
 
    const order = options.priority;
    let popper = getClientRect(data.offsets.popper);
 
    const check = {
        primary(placement) {
            let value = popper[placement];
            if (popper[placement] < boundaries[placement] && !shouldOverflowBoundary(data, options, placement)) {
                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] && !shouldOverflowBoundary(data, options, placement)) {
                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;
}
 
/**
 * Determine if the popper should overflow a boundary edge to stay together with the reference.
 */
function shouldOverflowBoundary(data, options, overflowDirection) {
    if (!options.escapeWithReference) {
        return false;
    }
 
    Iif (data.flipped && isSameAxis(data.originalPlacement, overflowDirection)) {
        return true;
    }
 
    Eif (!isSameAxis(data.originalPlacement, overflowDirection)) {
        return true;
    }
 
    return true;
}
 
/**
 * Determine if two placement values are on the same axis.
 */
function isSameAxis(a, b) {
    // placement syntax:
    //
    //     ( "top" | "right" | "bottom" | "left" ) ( "-start" | "" | "-end" )
    //     |------------- Direction -------------|
    //
    const aDirection = a.split('-')[0];
    const bDirection = b.split('-')[0];
 
    return aDirection === bDirection || aDirection === getOppositePlacement(b);
}