floating-ui.dom.umd.js 29 KB

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