source: imaps-frontend/node_modules/@popperjs/core/lib/modifiers/preventOverflow.js.flow

main
Last change on this file was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 4 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 7.3 KB
Line 
1// @flow
2import { top, left, right, bottom, start } from '../enums';
3import type { Placement, Boundary, RootBoundary } from '../enums';
4import type { Rect, ModifierArguments, Modifier, Padding } from '../types';
5import getBasePlacement from '../utils/getBasePlacement';
6import getMainAxisFromPlacement from '../utils/getMainAxisFromPlacement';
7import getAltAxis from '../utils/getAltAxis';
8import { within, withinMaxClamp } from '../utils/within';
9import getLayoutRect from '../dom-utils/getLayoutRect';
10import getOffsetParent from '../dom-utils/getOffsetParent';
11import detectOverflow from '../utils/detectOverflow';
12import getVariation from '../utils/getVariation';
13import getFreshSideObject from '../utils/getFreshSideObject';
14import { min as mathMin, max as mathMax } from '../utils/math';
15
16type TetherOffset =
17 | (({
18 popper: Rect,
19 reference: Rect,
20 placement: Placement,
21 }) => number | { mainAxis: number, altAxis: number })
22 | number
23 | { mainAxis: number, altAxis: number };
24
25// eslint-disable-next-line import/no-unused-modules
26export type Options = {
27 /* Prevents boundaries overflow on the main axis */
28 mainAxis: boolean,
29 /* Prevents boundaries overflow on the alternate axis */
30 altAxis: boolean,
31 /* The area to check the popper is overflowing in */
32 boundary: Boundary,
33 /* If the popper is not overflowing the main area, fallback to this one */
34 rootBoundary: RootBoundary,
35 /* Use the reference's "clippingParents" boundary context */
36 altBoundary: boolean,
37 /**
38 * Allows the popper to overflow from its boundaries to keep it near its
39 * reference element
40 */
41 tether: boolean,
42 /* Offsets when the `tether` option should activate */
43 tetherOffset: TetherOffset,
44 /* Sets a padding to the provided boundary */
45 padding: Padding,
46};
47
48function preventOverflow({ state, options, name }: ModifierArguments<Options>) {
49 const {
50 mainAxis: checkMainAxis = true,
51 altAxis: checkAltAxis = false,
52 boundary,
53 rootBoundary,
54 altBoundary,
55 padding,
56 tether = true,
57 tetherOffset = 0,
58 } = options;
59
60 const overflow = detectOverflow(state, {
61 boundary,
62 rootBoundary,
63 padding,
64 altBoundary,
65 });
66 const basePlacement = getBasePlacement(state.placement);
67 const variation = getVariation(state.placement);
68 const isBasePlacement = !variation;
69 const mainAxis = getMainAxisFromPlacement(basePlacement);
70 const altAxis = getAltAxis(mainAxis);
71 const popperOffsets = state.modifiersData.popperOffsets;
72 const referenceRect = state.rects.reference;
73 const popperRect = state.rects.popper;
74 const tetherOffsetValue =
75 typeof tetherOffset === 'function'
76 ? tetherOffset({
77 ...state.rects,
78 placement: state.placement,
79 })
80 : tetherOffset;
81 const normalizedTetherOffsetValue =
82 typeof tetherOffsetValue === 'number'
83 ? { mainAxis: tetherOffsetValue, altAxis: tetherOffsetValue }
84 : { mainAxis: 0, altAxis: 0, ...tetherOffsetValue };
85 const offsetModifierState = state.modifiersData.offset
86 ? state.modifiersData.offset[state.placement]
87 : null;
88
89 const data = { x: 0, y: 0 };
90
91 if (!popperOffsets) {
92 return;
93 }
94
95 if (checkMainAxis) {
96 const mainSide = mainAxis === 'y' ? top : left;
97 const altSide = mainAxis === 'y' ? bottom : right;
98 const len = mainAxis === 'y' ? 'height' : 'width';
99 const offset = popperOffsets[mainAxis];
100
101 const min = offset + overflow[mainSide];
102 const max = offset - overflow[altSide];
103
104 const additive = tether ? -popperRect[len] / 2 : 0;
105
106 const minLen = variation === start ? referenceRect[len] : popperRect[len];
107 const maxLen = variation === start ? -popperRect[len] : -referenceRect[len];
108
109 // We need to include the arrow in the calculation so the arrow doesn't go
110 // outside the reference bounds
111 const arrowElement = state.elements.arrow;
112 const arrowRect =
113 tether && arrowElement
114 ? getLayoutRect(arrowElement)
115 : { width: 0, height: 0 };
116 const arrowPaddingObject = state.modifiersData['arrow#persistent']
117 ? state.modifiersData['arrow#persistent'].padding
118 : getFreshSideObject();
119 const arrowPaddingMin = arrowPaddingObject[mainSide];
120 const arrowPaddingMax = arrowPaddingObject[altSide];
121
122 // If the reference length is smaller than the arrow length, we don't want
123 // to include its full size in the calculation. If the reference is small
124 // and near the edge of a boundary, the popper can overflow even if the
125 // reference is not overflowing as well (e.g. virtual elements with no
126 // width or height)
127 const arrowLen = within(0, referenceRect[len], arrowRect[len]);
128
129 const minOffset = isBasePlacement
130 ? referenceRect[len] / 2 -
131 additive -
132 arrowLen -
133 arrowPaddingMin -
134 normalizedTetherOffsetValue.mainAxis
135 : minLen -
136 arrowLen -
137 arrowPaddingMin -
138 normalizedTetherOffsetValue.mainAxis;
139 const maxOffset = isBasePlacement
140 ? -referenceRect[len] / 2 +
141 additive +
142 arrowLen +
143 arrowPaddingMax +
144 normalizedTetherOffsetValue.mainAxis
145 : maxLen +
146 arrowLen +
147 arrowPaddingMax +
148 normalizedTetherOffsetValue.mainAxis;
149
150 const arrowOffsetParent =
151 state.elements.arrow && getOffsetParent(state.elements.arrow);
152 const clientOffset = arrowOffsetParent
153 ? mainAxis === 'y'
154 ? arrowOffsetParent.clientTop || 0
155 : arrowOffsetParent.clientLeft || 0
156 : 0;
157
158 const offsetModifierValue = offsetModifierState?.[mainAxis] ?? 0;
159 const tetherMin = offset + minOffset - offsetModifierValue - clientOffset;
160 const tetherMax = offset + maxOffset - offsetModifierValue;
161
162 const preventedOffset = within(
163 tether ? mathMin(min, tetherMin) : min,
164 offset,
165 tether ? mathMax(max, tetherMax) : max
166 );
167
168 popperOffsets[mainAxis] = preventedOffset;
169 data[mainAxis] = preventedOffset - offset;
170 }
171
172 if (checkAltAxis) {
173 const mainSide = mainAxis === 'x' ? top : left;
174 const altSide = mainAxis === 'x' ? bottom : right;
175 const offset = popperOffsets[altAxis];
176
177 const len = altAxis === 'y' ? 'height' : 'width';
178
179 const min = offset + overflow[mainSide];
180 const max = offset - overflow[altSide];
181
182 const isOriginSide = [top, left].indexOf(basePlacement) !== -1;
183
184 const offsetModifierValue = offsetModifierState?.[altAxis] ?? 0;
185 const tetherMin = isOriginSide
186 ? min
187 : offset -
188 referenceRect[len] -
189 popperRect[len] -
190 offsetModifierValue +
191 normalizedTetherOffsetValue.altAxis;
192 const tetherMax = isOriginSide
193 ? offset +
194 referenceRect[len] +
195 popperRect[len] -
196 offsetModifierValue -
197 normalizedTetherOffsetValue.altAxis
198 : max;
199
200 const preventedOffset =
201 tether && isOriginSide
202 ? withinMaxClamp(tetherMin, offset, tetherMax)
203 : within(tether ? tetherMin : min, offset, tether ? tetherMax : max);
204
205 popperOffsets[altAxis] = preventedOffset;
206 data[altAxis] = preventedOffset - offset;
207 }
208
209 state.modifiersData[name] = data;
210}
211
212// eslint-disable-next-line import/no-unused-modules
213export type PreventOverflowModifier = Modifier<'preventOverflow', Options>;
214export default ({
215 name: 'preventOverflow',
216 enabled: true,
217 phase: 'main',
218 fn: preventOverflow,
219 requiresIfExists: ['offset'],
220}: PreventOverflowModifier);
Note: See TracBrowser for help on using the repository browser.