UNPKG

27.5 kBJavaScriptView Raw
1import { rectToClientRect, autoPlacement as autoPlacement$1, shift as shift$1, flip as flip$1, size as size$1, hide as hide$1, arrow as arrow$1, inline as inline$1, limitShift as limitShift$1, computePosition as computePosition$1 } from '@floating-ui/core';
2export { detectOverflow, offset } from '@floating-ui/core';
3
4/**
5 * Custom positioning reference element.
6 * @see https://floating-ui.com/docs/virtual-elements
7 */
8
9const min = Math.min;
10const max = Math.max;
11const round = Math.round;
12const floor = Math.floor;
13const createCoords = v => ({
14 x: v,
15 y: v
16});
17
18function getNodeName(node) {
19 if (isNode(node)) {
20 return (node.nodeName || '').toLowerCase();
21 }
22 // Mocked nodes in testing environments may not be instances of Node. By
23 // returning `#document` an infinite loop won't occur.
24 // https://github.com/floating-ui/floating-ui/issues/2317
25 return '#document';
26}
27function getWindow(node) {
28 var _node$ownerDocument;
29 return (node == null || (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
30}
31function getDocumentElement(node) {
32 var _ref;
33 return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement;
34}
35function isNode(value) {
36 return value instanceof Node || value instanceof getWindow(value).Node;
37}
38function isElement(value) {
39 return value instanceof Element || value instanceof getWindow(value).Element;
40}
41function isHTMLElement(value) {
42 return value instanceof HTMLElement || value instanceof getWindow(value).HTMLElement;
43}
44function isShadowRoot(value) {
45 // Browsers without `ShadowRoot` support.
46 if (typeof ShadowRoot === 'undefined') {
47 return false;
48 }
49 return value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot;
50}
51function isOverflowElement(element) {
52 const {
53 overflow,
54 overflowX,
55 overflowY,
56 display
57 } = getComputedStyle(element);
58 return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !['inline', 'contents'].includes(display);
59}
60function isTableElement(element) {
61 return ['table', 'td', 'th'].includes(getNodeName(element));
62}
63function isContainingBlock(element) {
64 const webkit = isWebKit();
65 const css = getComputedStyle(element);
66
67 // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
68 return css.transform !== 'none' || css.perspective !== 'none' || (css.containerType ? css.containerType !== 'normal' : false) || !webkit && (css.backdropFilter ? css.backdropFilter !== 'none' : false) || !webkit && (css.filter ? css.filter !== 'none' : false) || ['transform', 'perspective', 'filter'].some(value => (css.willChange || '').includes(value)) || ['paint', 'layout', 'strict', 'content'].some(value => (css.contain || '').includes(value));
69}
70function getContainingBlock(element) {
71 let currentNode = getParentNode(element);
72 while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
73 if (isContainingBlock(currentNode)) {
74 return currentNode;
75 }
76 currentNode = getParentNode(currentNode);
77 }
78 return null;
79}
80function isWebKit() {
81 if (typeof CSS === 'undefined' || !CSS.supports) return false;
82 return CSS.supports('-webkit-backdrop-filter', 'none');
83}
84function isLastTraversableNode(node) {
85 return ['html', 'body', '#document'].includes(getNodeName(node));
86}
87function getComputedStyle(element) {
88 return getWindow(element).getComputedStyle(element);
89}
90function getNodeScroll(element) {
91 if (isElement(element)) {
92 return {
93 scrollLeft: element.scrollLeft,
94 scrollTop: element.scrollTop
95 };
96 }
97 return {
98 scrollLeft: element.pageXOffset,
99 scrollTop: element.pageYOffset
100 };
101}
102function getParentNode(node) {
103 if (getNodeName(node) === 'html') {
104 return node;
105 }
106 const result =
107 // Step into the shadow DOM of the parent of a slotted node.
108 node.assignedSlot ||
109 // DOM Element detected.
110 node.parentNode ||
111 // ShadowRoot detected.
112 isShadowRoot(node) && node.host ||
113 // Fallback.
114 getDocumentElement(node);
115 return isShadowRoot(result) ? result.host : result;
116}
117function getNearestOverflowAncestor(node) {
118 const parentNode = getParentNode(node);
119 if (isLastTraversableNode(parentNode)) {
120 return node.ownerDocument ? node.ownerDocument.body : node.body;
121 }
122 if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
123 return parentNode;
124 }
125 return getNearestOverflowAncestor(parentNode);
126}
127function getOverflowAncestors(node, list, traverseIframes) {
128 var _node$ownerDocument2;
129 if (list === void 0) {
130 list = [];
131 }
132 if (traverseIframes === void 0) {
133 traverseIframes = true;
134 }
135 const scrollableAncestor = getNearestOverflowAncestor(node);
136 const isBody = scrollableAncestor === ((_node$ownerDocument2 = node.ownerDocument) == null ? void 0 : _node$ownerDocument2.body);
137 const win = getWindow(scrollableAncestor);
138 if (isBody) {
139 return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], win.frameElement && traverseIframes ? getOverflowAncestors(win.frameElement) : []);
140 }
141 return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes));
142}
143
144function getCssDimensions(element) {
145 const css = getComputedStyle(element);
146 // In testing environments, the `width` and `height` properties are empty
147 // strings for SVG elements, returning NaN. Fallback to `0` in this case.
148 let width = parseFloat(css.width) || 0;
149 let height = parseFloat(css.height) || 0;
150 const hasOffset = isHTMLElement(element);
151 const offsetWidth = hasOffset ? element.offsetWidth : width;
152 const offsetHeight = hasOffset ? element.offsetHeight : height;
153 const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;
154 if (shouldFallback) {
155 width = offsetWidth;
156 height = offsetHeight;
157 }
158 return {
159 width,
160 height,
161 $: shouldFallback
162 };
163}
164
165function unwrapElement(element) {
166 return !isElement(element) ? element.contextElement : element;
167}
168
169function getScale(element) {
170 const domElement = unwrapElement(element);
171 if (!isHTMLElement(domElement)) {
172 return createCoords(1);
173 }
174 const rect = domElement.getBoundingClientRect();
175 const {
176 width,
177 height,
178 $
179 } = getCssDimensions(domElement);
180 let x = ($ ? round(rect.width) : rect.width) / width;
181 let y = ($ ? round(rect.height) : rect.height) / height;
182
183 // 0, NaN, or Infinity should always fallback to 1.
184
185 if (!x || !Number.isFinite(x)) {
186 x = 1;
187 }
188 if (!y || !Number.isFinite(y)) {
189 y = 1;
190 }
191 return {
192 x,
193 y
194 };
195}
196
197const noOffsets = /*#__PURE__*/createCoords(0);
198function getVisualOffsets(element) {
199 const win = getWindow(element);
200 if (!isWebKit() || !win.visualViewport) {
201 return noOffsets;
202 }
203 return {
204 x: win.visualViewport.offsetLeft,
205 y: win.visualViewport.offsetTop
206 };
207}
208function shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) {
209 if (isFixed === void 0) {
210 isFixed = false;
211 }
212 if (!floatingOffsetParent || isFixed && floatingOffsetParent !== getWindow(element)) {
213 return false;
214 }
215 return isFixed;
216}
217
218function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {
219 if (includeScale === void 0) {
220 includeScale = false;
221 }
222 if (isFixedStrategy === void 0) {
223 isFixedStrategy = false;
224 }
225 const clientRect = element.getBoundingClientRect();
226 const domElement = unwrapElement(element);
227 let scale = createCoords(1);
228 if (includeScale) {
229 if (offsetParent) {
230 if (isElement(offsetParent)) {
231 scale = getScale(offsetParent);
232 }
233 } else {
234 scale = getScale(element);
235 }
236 }
237 const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0);
238 let x = (clientRect.left + visualOffsets.x) / scale.x;
239 let y = (clientRect.top + visualOffsets.y) / scale.y;
240 let width = clientRect.width / scale.x;
241 let height = clientRect.height / scale.y;
242 if (domElement) {
243 const win = getWindow(domElement);
244 const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;
245 let currentWin = win;
246 let currentIFrame = currentWin.frameElement;
247 while (currentIFrame && offsetParent && offsetWin !== currentWin) {
248 const iframeScale = getScale(currentIFrame);
249 const iframeRect = currentIFrame.getBoundingClientRect();
250 const css = getComputedStyle(currentIFrame);
251 const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;
252 const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;
253 x *= iframeScale.x;
254 y *= iframeScale.y;
255 width *= iframeScale.x;
256 height *= iframeScale.y;
257 x += left;
258 y += top;
259 currentWin = getWindow(currentIFrame);
260 currentIFrame = currentWin.frameElement;
261 }
262 }
263 return rectToClientRect({
264 width,
265 height,
266 x,
267 y
268 });
269}
270
271const topLayerSelectors = [':popover-open', ':modal'];
272function isTopLayer(floating) {
273 return topLayerSelectors.some(selector => {
274 try {
275 return floating.matches(selector);
276 } catch (e) {
277 return false;
278 }
279 });
280}
281
282function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
283 let {
284 elements,
285 rect,
286 offsetParent,
287 strategy
288 } = _ref;
289 const isFixed = strategy === 'fixed';
290 const documentElement = getDocumentElement(offsetParent);
291 const topLayer = elements ? isTopLayer(elements.floating) : false;
292 if (offsetParent === documentElement || topLayer && isFixed) {
293 return rect;
294 }
295 let scroll = {
296 scrollLeft: 0,
297 scrollTop: 0
298 };
299 let scale = createCoords(1);
300 const offsets = createCoords(0);
301 const isOffsetParentAnElement = isHTMLElement(offsetParent);
302 if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
303 if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
304 scroll = getNodeScroll(offsetParent);
305 }
306 if (isHTMLElement(offsetParent)) {
307 const offsetRect = getBoundingClientRect(offsetParent);
308 scale = getScale(offsetParent);
309 offsets.x = offsetRect.x + offsetParent.clientLeft;
310 offsets.y = offsetRect.y + offsetParent.clientTop;
311 }
312 }
313 return {
314 width: rect.width * scale.x,
315 height: rect.height * scale.y,
316 x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x,
317 y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y
318 };
319}
320
321function getClientRects(element) {
322 return Array.from(element.getClientRects());
323}
324
325function getWindowScrollBarX(element) {
326 // If <html> has a CSS width greater than the viewport, then this will be
327 // incorrect for RTL.
328 return getBoundingClientRect(getDocumentElement(element)).left + getNodeScroll(element).scrollLeft;
329}
330
331// Gets the entire size of the scrollable document area, even extending outside
332// of the `<html>` and `<body>` rect bounds if horizontally scrollable.
333function getDocumentRect(element) {
334 const html = getDocumentElement(element);
335 const scroll = getNodeScroll(element);
336 const body = element.ownerDocument.body;
337 const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);
338 const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);
339 let x = -scroll.scrollLeft + getWindowScrollBarX(element);
340 const y = -scroll.scrollTop;
341 if (getComputedStyle(body).direction === 'rtl') {
342 x += max(html.clientWidth, body.clientWidth) - width;
343 }
344 return {
345 width,
346 height,
347 x,
348 y
349 };
350}
351
352function getViewportRect(element, strategy) {
353 const win = getWindow(element);
354 const html = getDocumentElement(element);
355 const visualViewport = win.visualViewport;
356 let width = html.clientWidth;
357 let height = html.clientHeight;
358 let x = 0;
359 let y = 0;
360 if (visualViewport) {
361 width = visualViewport.width;
362 height = visualViewport.height;
363 const visualViewportBased = isWebKit();
364 if (!visualViewportBased || visualViewportBased && strategy === 'fixed') {
365 x = visualViewport.offsetLeft;
366 y = visualViewport.offsetTop;
367 }
368 }
369 return {
370 width,
371 height,
372 x,
373 y
374 };
375}
376
377// Returns the inner client rect, subtracting scrollbars if present.
378function getInnerBoundingClientRect(element, strategy) {
379 const clientRect = getBoundingClientRect(element, true, strategy === 'fixed');
380 const top = clientRect.top + element.clientTop;
381 const left = clientRect.left + element.clientLeft;
382 const scale = isHTMLElement(element) ? getScale(element) : createCoords(1);
383 const width = element.clientWidth * scale.x;
384 const height = element.clientHeight * scale.y;
385 const x = left * scale.x;
386 const y = top * scale.y;
387 return {
388 width,
389 height,
390 x,
391 y
392 };
393}
394function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {
395 let rect;
396 if (clippingAncestor === 'viewport') {
397 rect = getViewportRect(element, strategy);
398 } else if (clippingAncestor === 'document') {
399 rect = getDocumentRect(getDocumentElement(element));
400 } else if (isElement(clippingAncestor)) {
401 rect = getInnerBoundingClientRect(clippingAncestor, strategy);
402 } else {
403 const visualOffsets = getVisualOffsets(element);
404 rect = {
405 ...clippingAncestor,
406 x: clippingAncestor.x - visualOffsets.x,
407 y: clippingAncestor.y - visualOffsets.y
408 };
409 }
410 return rectToClientRect(rect);
411}
412function hasFixedPositionAncestor(element, stopNode) {
413 const parentNode = getParentNode(element);
414 if (parentNode === stopNode || !isElement(parentNode) || isLastTraversableNode(parentNode)) {
415 return false;
416 }
417 return getComputedStyle(parentNode).position === 'fixed' || hasFixedPositionAncestor(parentNode, stopNode);
418}
419
420// A "clipping ancestor" is an `overflow` element with the characteristic of
421// clipping (or hiding) child elements. This returns all clipping ancestors
422// of the given element up the tree.
423function getClippingElementAncestors(element, cache) {
424 const cachedResult = cache.get(element);
425 if (cachedResult) {
426 return cachedResult;
427 }
428 let result = getOverflowAncestors(element, [], false).filter(el => isElement(el) && getNodeName(el) !== 'body');
429 let currentContainingBlockComputedStyle = null;
430 const elementIsFixed = getComputedStyle(element).position === 'fixed';
431 let currentNode = elementIsFixed ? getParentNode(element) : element;
432
433 // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
434 while (isElement(currentNode) && !isLastTraversableNode(currentNode)) {
435 const computedStyle = getComputedStyle(currentNode);
436 const currentNodeIsContaining = isContainingBlock(currentNode);
437 if (!currentNodeIsContaining && computedStyle.position === 'fixed') {
438 currentContainingBlockComputedStyle = null;
439 }
440 const shouldDropCurrentNode = elementIsFixed ? !currentNodeIsContaining && !currentContainingBlockComputedStyle : !currentNodeIsContaining && computedStyle.position === 'static' && !!currentContainingBlockComputedStyle && ['absolute', 'fixed'].includes(currentContainingBlockComputedStyle.position) || isOverflowElement(currentNode) && !currentNodeIsContaining && hasFixedPositionAncestor(element, currentNode);
441 if (shouldDropCurrentNode) {
442 // Drop non-containing blocks.
443 result = result.filter(ancestor => ancestor !== currentNode);
444 } else {
445 // Record last containing block for next iteration.
446 currentContainingBlockComputedStyle = computedStyle;
447 }
448 currentNode = getParentNode(currentNode);
449 }
450 cache.set(element, result);
451 return result;
452}
453
454// Gets the maximum area that the element is visible in due to any number of
455// clipping ancestors.
456function getClippingRect(_ref) {
457 let {
458 element,
459 boundary,
460 rootBoundary,
461 strategy
462 } = _ref;
463 const elementClippingAncestors = boundary === 'clippingAncestors' ? getClippingElementAncestors(element, this._c) : [].concat(boundary);
464 const clippingAncestors = [...elementClippingAncestors, rootBoundary];
465 const firstClippingAncestor = clippingAncestors[0];
466 const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => {
467 const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy);
468 accRect.top = max(rect.top, accRect.top);
469 accRect.right = min(rect.right, accRect.right);
470 accRect.bottom = min(rect.bottom, accRect.bottom);
471 accRect.left = max(rect.left, accRect.left);
472 return accRect;
473 }, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy));
474 return {
475 width: clippingRect.right - clippingRect.left,
476 height: clippingRect.bottom - clippingRect.top,
477 x: clippingRect.left,
478 y: clippingRect.top
479 };
480}
481
482function getDimensions(element) {
483 const {
484 width,
485 height
486 } = getCssDimensions(element);
487 return {
488 width,
489 height
490 };
491}
492
493function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
494 const isOffsetParentAnElement = isHTMLElement(offsetParent);
495 const documentElement = getDocumentElement(offsetParent);
496 const isFixed = strategy === 'fixed';
497 const rect = getBoundingClientRect(element, true, isFixed, offsetParent);
498 let scroll = {
499 scrollLeft: 0,
500 scrollTop: 0
501 };
502 const offsets = createCoords(0);
503 if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
504 if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
505 scroll = getNodeScroll(offsetParent);
506 }
507 if (isOffsetParentAnElement) {
508 const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent);
509 offsets.x = offsetRect.x + offsetParent.clientLeft;
510 offsets.y = offsetRect.y + offsetParent.clientTop;
511 } else if (documentElement) {
512 offsets.x = getWindowScrollBarX(documentElement);
513 }
514 }
515 const x = rect.left + scroll.scrollLeft - offsets.x;
516 const y = rect.top + scroll.scrollTop - offsets.y;
517 return {
518 x,
519 y,
520 width: rect.width,
521 height: rect.height
522 };
523}
524
525function getTrueOffsetParent(element, polyfill) {
526 if (!isHTMLElement(element) || getComputedStyle(element).position === 'fixed') {
527 return null;
528 }
529 if (polyfill) {
530 return polyfill(element);
531 }
532 return element.offsetParent;
533}
534
535// Gets the closest ancestor positioned element. Handles some edge cases,
536// such as table ancestors and cross browser bugs.
537function getOffsetParent(element, polyfill) {
538 const window = getWindow(element);
539 if (!isHTMLElement(element) || isTopLayer(element)) {
540 return window;
541 }
542 let offsetParent = getTrueOffsetParent(element, polyfill);
543 while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {
544 offsetParent = getTrueOffsetParent(offsetParent, polyfill);
545 }
546 if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static' && !isContainingBlock(offsetParent))) {
547 return window;
548 }
549 return offsetParent || getContainingBlock(element) || window;
550}
551
552const getElementRects = async function (data) {
553 const getOffsetParentFn = this.getOffsetParent || getOffsetParent;
554 const getDimensionsFn = this.getDimensions;
555 return {
556 reference: getRectRelativeToOffsetParent(data.reference, await getOffsetParentFn(data.floating), data.strategy),
557 floating: {
558 x: 0,
559 y: 0,
560 ...(await getDimensionsFn(data.floating))
561 }
562 };
563};
564
565function isRTL(element) {
566 return getComputedStyle(element).direction === 'rtl';
567}
568
569const platform = {
570 convertOffsetParentRelativeRectToViewportRelativeRect,
571 getDocumentElement,
572 getClippingRect,
573 getOffsetParent,
574 getElementRects,
575 getClientRects,
576 getDimensions,
577 getScale,
578 isElement,
579 isRTL
580};
581
582// https://samthor.au/2021/observing-dom/
583function observeMove(element, onMove) {
584 let io = null;
585 let timeoutId;
586 const root = getDocumentElement(element);
587 function cleanup() {
588 var _io;
589 clearTimeout(timeoutId);
590 (_io = io) == null || _io.disconnect();
591 io = null;
592 }
593 function refresh(skip, threshold) {
594 if (skip === void 0) {
595 skip = false;
596 }
597 if (threshold === void 0) {
598 threshold = 1;
599 }
600 cleanup();
601 const {
602 left,
603 top,
604 width,
605 height
606 } = element.getBoundingClientRect();
607 if (!skip) {
608 onMove();
609 }
610 if (!width || !height) {
611 return;
612 }
613 const insetTop = floor(top);
614 const insetRight = floor(root.clientWidth - (left + width));
615 const insetBottom = floor(root.clientHeight - (top + height));
616 const insetLeft = floor(left);
617 const rootMargin = -insetTop + "px " + -insetRight + "px " + -insetBottom + "px " + -insetLeft + "px";
618 const options = {
619 rootMargin,
620 threshold: max(0, min(1, threshold)) || 1
621 };
622 let isFirstUpdate = true;
623 function handleObserve(entries) {
624 const ratio = entries[0].intersectionRatio;
625 if (ratio !== threshold) {
626 if (!isFirstUpdate) {
627 return refresh();
628 }
629 if (!ratio) {
630 timeoutId = setTimeout(() => {
631 refresh(false, 1e-7);
632 }, 100);
633 } else {
634 refresh(false, ratio);
635 }
636 }
637 isFirstUpdate = false;
638 }
639
640 // Older browsers don't support a `document` as the root and will throw an
641 // error.
642 try {
643 io = new IntersectionObserver(handleObserve, {
644 ...options,
645 // Handle <iframe>s
646 root: root.ownerDocument
647 });
648 } catch (e) {
649 io = new IntersectionObserver(handleObserve, options);
650 }
651 io.observe(element);
652 }
653 refresh(true);
654 return cleanup;
655}
656
657/**
658 * Automatically updates the position of the floating element when necessary.
659 * Should only be called when the floating element is mounted on the DOM or
660 * visible on the screen.
661 * @returns cleanup function that should be invoked when the floating element is
662 * removed from the DOM or hidden from the screen.
663 * @see https://floating-ui.com/docs/autoUpdate
664 */
665function autoUpdate(reference, floating, update, options) {
666 if (options === void 0) {
667 options = {};
668 }
669 const {
670 ancestorScroll = true,
671 ancestorResize = true,
672 elementResize = typeof ResizeObserver === 'function',
673 layoutShift = typeof IntersectionObserver === 'function',
674 animationFrame = false
675 } = options;
676 const referenceEl = unwrapElement(reference);
677 const ancestors = ancestorScroll || ancestorResize ? [...(referenceEl ? getOverflowAncestors(referenceEl) : []), ...getOverflowAncestors(floating)] : [];
678 ancestors.forEach(ancestor => {
679 ancestorScroll && ancestor.addEventListener('scroll', update, {
680 passive: true
681 });
682 ancestorResize && ancestor.addEventListener('resize', update);
683 });
684 const cleanupIo = referenceEl && layoutShift ? observeMove(referenceEl, update) : null;
685 let reobserveFrame = -1;
686 let resizeObserver = null;
687 if (elementResize) {
688 resizeObserver = new ResizeObserver(_ref => {
689 let [firstEntry] = _ref;
690 if (firstEntry && firstEntry.target === referenceEl && resizeObserver) {
691 // Prevent update loops when using the `size` middleware.
692 // https://github.com/floating-ui/floating-ui/issues/1740
693 resizeObserver.unobserve(floating);
694 cancelAnimationFrame(reobserveFrame);
695 reobserveFrame = requestAnimationFrame(() => {
696 var _resizeObserver;
697 (_resizeObserver = resizeObserver) == null || _resizeObserver.observe(floating);
698 });
699 }
700 update();
701 });
702 if (referenceEl && !animationFrame) {
703 resizeObserver.observe(referenceEl);
704 }
705 resizeObserver.observe(floating);
706 }
707 let frameId;
708 let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;
709 if (animationFrame) {
710 frameLoop();
711 }
712 function frameLoop() {
713 const nextRefRect = getBoundingClientRect(reference);
714 if (prevRefRect && (nextRefRect.x !== prevRefRect.x || nextRefRect.y !== prevRefRect.y || nextRefRect.width !== prevRefRect.width || nextRefRect.height !== prevRefRect.height)) {
715 update();
716 }
717 prevRefRect = nextRefRect;
718 frameId = requestAnimationFrame(frameLoop);
719 }
720 update();
721 return () => {
722 var _resizeObserver2;
723 ancestors.forEach(ancestor => {
724 ancestorScroll && ancestor.removeEventListener('scroll', update);
725 ancestorResize && ancestor.removeEventListener('resize', update);
726 });
727 cleanupIo == null || cleanupIo();
728 (_resizeObserver2 = resizeObserver) == null || _resizeObserver2.disconnect();
729 resizeObserver = null;
730 if (animationFrame) {
731 cancelAnimationFrame(frameId);
732 }
733 };
734}
735
736/**
737 * Optimizes the visibility of the floating element by choosing the placement
738 * that has the most space available automatically, without needing to specify a
739 * preferred placement. Alternative to `flip`.
740 * @see https://floating-ui.com/docs/autoPlacement
741 */
742const autoPlacement = autoPlacement$1;
743
744/**
745 * Optimizes the visibility of the floating element by shifting it in order to
746 * keep it in view when it will overflow the clipping boundary.
747 * @see https://floating-ui.com/docs/shift
748 */
749const shift = shift$1;
750
751/**
752 * Optimizes the visibility of the floating element by flipping the `placement`
753 * in order to keep it in view when the preferred placement(s) will overflow the
754 * clipping boundary. Alternative to `autoPlacement`.
755 * @see https://floating-ui.com/docs/flip
756 */
757const flip = flip$1;
758
759/**
760 * Provides data that allows you to change the size of the floating element —
761 * for instance, prevent it from overflowing the clipping boundary or match the
762 * width of the reference element.
763 * @see https://floating-ui.com/docs/size
764 */
765const size = size$1;
766
767/**
768 * Provides data to hide the floating element in applicable situations, such as
769 * when it is not in the same clipping context as the reference element.
770 * @see https://floating-ui.com/docs/hide
771 */
772const hide = hide$1;
773
774/**
775 * Provides data to position an inner element of the floating element so that it
776 * appears centered to the reference element.
777 * @see https://floating-ui.com/docs/arrow
778 */
779const arrow = arrow$1;
780
781/**
782 * Provides improved positioning for inline reference elements that can span
783 * over multiple lines, such as hyperlinks or range selections.
784 * @see https://floating-ui.com/docs/inline
785 */
786const inline = inline$1;
787
788/**
789 * Built-in `limiter` that will stop `shift()` at a certain point.
790 */
791const limitShift = limitShift$1;
792
793/**
794 * Computes the `x` and `y` coordinates that will place the floating element
795 * next to a given reference element.
796 */
797const computePosition = (reference, floating, options) => {
798 // This caches the expensive `getClippingElementAncestors` function so that
799 // multiple lifecycle resets re-use the same result. It only lives for a
800 // single call. If other functions become expensive, we can add them as well.
801 const cache = new Map();
802 const mergedOptions = {
803 platform,
804 ...options
805 };
806 const platformWithCache = {
807 ...mergedOptions.platform,
808 _c: cache
809 };
810 return computePosition$1(reference, floating, {
811 ...mergedOptions,
812 platform: platformWithCache
813 });
814};
815
816export { arrow, autoPlacement, autoUpdate, computePosition, flip, getOverflowAncestors, hide, inline, limitShift, platform, shift, size };