Idea of the project: if someone wants to order a project development, here you can send an application.
import getClientRect from '../utils/getClientRect';
import getOuterSizes from '../utils/getOuterSizes';
import isModifierRequired from '../utils/isModifierRequired';
import getStyleComputedProperty from '../utils/getStyleComputedProperty';
/**
* @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 arrow(data, options) {
// arrow depends on keepTogether in order to work
if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) {
return data;
}
let arrowElement = options.element;
// if arrowElement is a string, suppose it's a CSS selector
if (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, reference } = data.offsets;
const isVertical = ['left', 'right'].indexOf(placement) !== -1;
const len = isVertical ? 'height' : 'width';
const sideCapitalized = isVertical ? 'Top' : 'Left';
const side = sideCapitalized.toLowerCase();
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 conjunction
//
// top/left side
if (reference[opSide] - arrowElementSize < popper[side]) {
data.offsets.popper[side] -=
popper[side] - (reference[opSide] - arrowElementSize);
}
// bottom/right side
if (reference[side] + arrowElementSize > popper[opSide]) {
data.offsets.popper[side] +=
reference[side] + arrowElementSize - popper[opSide];
}
data.offsets.popper = getClientRect(data.offsets.popper);
// compute center of the popper
const center = reference[side] + reference[len] / 2 - arrowElementSize / 2;
// Compute the sideValue using the updated popper offsets
// take popper margin in account because we don't have this info available
const css = getStyleComputedProperty(data.instance.popper);
const popperMarginSide = parseFloat(css[`margin${sideCapitalized}`], 10);
const popperBorderSide = parseFloat(css[`border${sideCapitalized}Width`], 10);
let sideValue =
center - data.offsets.popper[side] - popperMarginSide - popperBorderSide;
// 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 = {
[side]: Math.round(sideValue),
[altSide]: '', // make sure to unset any eventual altSide value from the DOM node
};
return data;
}