arrow.js
3.08 KB
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
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 conjuction
//
// 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];
}
// 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 popperMarginSide = getStyleComputedProperty(
data.instance.popper,
`margin${sideCapitalized}`
).replace('px', '');
let sideValue =
center - getClientRect(data.offsets.popper)[side] - popperMarginSide;
// 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] = Math.round(sideValue);
data.offsets.arrow[altSide] = ''; // make sure to unset any eventual altSide value from the DOM node
return data;
}