flip.js
4.41 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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import getOppositePlacement from '../utils/getOppositePlacement';
import getOppositeVariation from '../utils/getOppositeVariation';
import getPopperOffsets from '../utils/getPopperOffsets';
import runModifiers from '../utils/runModifiers';
import getBoundaries from '../utils/getBoundaries';
import isModifierEnabled from '../utils/isModifierEnabled';
import clockwise from '../utils/clockwise';
const BEHAVIORS = {
FLIP: 'flip',
CLOCKWISE: 'clockwise',
COUNTERCLOCKWISE: 'counterclockwise',
};
/**
* @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 flip(data, options) {
// if `inner` modifier is enabled, we can't use the `flip` modifier
if (isModifierEnabled(data.instance.modifiers, 'inner')) {
return data;
}
if (data.flipped && data.placement === data.originalPlacement) {
// seems like flip is trying to loop, probably there's not enough space on any of the flippable sides
return data;
}
const boundaries = getBoundaries(
data.instance.popper,
data.instance.reference,
options.padding,
options.boundariesElement
);
let placement = data.placement.split('-')[0];
let placementOpposite = getOppositePlacement(placement);
let variation = data.placement.split('-')[1] || '';
let flipOrder = [];
switch (options.behavior) {
case BEHAVIORS.FLIP:
flipOrder = [placement, placementOpposite];
break;
case BEHAVIORS.CLOCKWISE:
flipOrder = clockwise(placement);
break;
case BEHAVIORS.COUNTERCLOCKWISE:
flipOrder = clockwise(placement, true);
break;
default:
flipOrder = options.behavior;
}
flipOrder.forEach((step, index) => {
if (placement !== step || flipOrder.length === index + 1) {
return data;
}
placement = data.placement.split('-')[0];
placementOpposite = getOppositePlacement(placement);
const popperOffsets = data.offsets.popper;
const refOffsets = data.offsets.reference;
// using floor because the reference offsets may contain decimals we are not going to consider here
const floor = Math.floor;
const overlapsRef =
(placement === 'left' &&
floor(popperOffsets.right) > floor(refOffsets.left)) ||
(placement === 'right' &&
floor(popperOffsets.left) < floor(refOffsets.right)) ||
(placement === 'top' &&
floor(popperOffsets.bottom) > floor(refOffsets.top)) ||
(placement === 'bottom' &&
floor(popperOffsets.top) < floor(refOffsets.bottom));
const overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left);
const overflowsRight = floor(popperOffsets.right) > floor(boundaries.right);
const overflowsTop = floor(popperOffsets.top) < floor(boundaries.top);
const overflowsBottom =
floor(popperOffsets.bottom) > floor(boundaries.bottom);
const overflowsBoundaries =
(placement === 'left' && overflowsLeft) ||
(placement === 'right' && overflowsRight) ||
(placement === 'top' && overflowsTop) ||
(placement === 'bottom' && overflowsBottom);
// flip the variation if required
const isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
const flippedVariation =
!!options.flipVariations &&
((isVertical && variation === 'start' && overflowsLeft) ||
(isVertical && variation === 'end' && overflowsRight) ||
(!isVertical && variation === 'start' && overflowsTop) ||
(!isVertical && variation === 'end' && overflowsBottom));
if (overlapsRef || overflowsBoundaries || flippedVariation) {
// this boolean to detect any flip loop
data.flipped = true;
if (overlapsRef || overflowsBoundaries) {
placement = flipOrder[index + 1];
}
if (flippedVariation) {
variation = getOppositeVariation(variation);
}
data.placement = placement + (variation ? '-' + variation : '');
// this object contains `position`, we want to preserve it along with
// any additional property we may add in the future
data.offsets.popper = {
...data.offsets.popper,
...getPopperOffsets(
data.instance.popper,
data.offsets.reference,
data.placement
),
};
data = runModifiers(data.instance.modifiers, data, 'flip');
}
});
return data;
}