floating-ui.dom.umd.js 32 KB

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