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;
}
|