all files / popper/modifiers/ arrow.js

78.13% Statements 25/32
70% Branches 14/20
100% Functions 1/1
78.13% Lines 25/32
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                              125×         125×     125× 125×     125× 32×                     93× 93× 93× 93×   93× 93× 93× 93× 93×             93×       93×         93×     93×     93×   93× 93× 93× 93×   93×    
import getClientRect from '../utils/getClientRect';
import getOuterSizes from '../utils/getOuterSizes';
import isModifierRequired from '../utils/isModifierRequired';
 
/**
 * Modifier used to move the arrowElements on the edge of the popper to make sure them are always between the popper and the reference element
 * It will use the CSS outer size of the arrowElement element to know how many pixels of conjuction are needed
 * @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 arrow(data, options) {
    // arrow depends on keepTogether in order to work
    Iif (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) {
        console.warn('WARNING: `keepTogether` modifier is required by arrow modifier in order to work, be sure to include it before `arrow`!');
        return data;
    }
 
    let arrowElement  = options.element;
 
    // if arrowElement is a string, suppose it's a CSS selector
    Eif (typeof arrowElement === 'string') {
        arrowElement = data.instance.popper.querySelector(arrowElement);
 
        // if arrowElement is not found, don't run the modifier
        if (!arrowElement) {
            return data;
        }
    } else {
        // if the arrowElement isn't a query selector we must check that the
        // provided DOM node is child of its popper node
        if (!data.instance.popper.contains(arrowElement)) {
            console.warn('WARNING: `arrow.element` must be child of its popper element!');
            return data;
        }
    }
 
    const placement         = data.placement.split('-')[0];
    const popper            = getClientRect(data.offsets.popper);
    const reference         = data.offsets.reference;
    const isVertical        = ['left', 'right'].indexOf(placement) !== -1;
 
    const len               = isVertical ? 'height' : 'width';
    const side              = isVertical ? 'top' : 'left';
    const altSide           = isVertical ? 'left' : 'top';
    const opSide            = isVertical ? 'bottom' : 'right';
    const arrowElementSize  = getOuterSizes(arrowElement)[len];
 
    //
    // extends keepTogether behavior making sure the popper and its reference have enough pixels in conjuction
    //
 
    // top/left side
    Iif (reference[opSide] - arrowElementSize < popper[side]) {
        data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize);
    }
    // bottom/right side
    Iif (reference[side] + arrowElementSize > popper[opSide]) {
        data.offsets.popper[side] += (reference[side] + arrowElementSize) - popper[opSide];
    }
 
    // compute center of the popper
    const center = reference[side] + (reference[len] / 2) - (arrowElementSize / 2);
 
    // Compute the sideValue using the updated popper offsets
    let sideValue = center - getClientRect(data.offsets.popper)[side];
 
    // prevent arrowElement from being placed not contiguously to its popper
    sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0);
 
    data.arrowElement = arrowElement;
    data.offsets.arrow = {};
    data.offsets.arrow[side] = sideValue;
    data.offsets.arrow[altSide] = ''; // make sure to unset any eventual altSide value from the DOM node
 
    return data;
}