floating-ui.utils.dom.mjs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. function hasWindow() {
  2. return typeof window !== 'undefined';
  3. }
  4. function getNodeName(node) {
  5. if (isNode(node)) {
  6. return (node.nodeName || '').toLowerCase();
  7. }
  8. // Mocked nodes in testing environments may not be instances of Node. By
  9. // returning `#document` an infinite loop won't occur.
  10. // https://github.com/floating-ui/floating-ui/issues/2317
  11. return '#document';
  12. }
  13. function getWindow(node) {
  14. var _node$ownerDocument;
  15. return (node == null || (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
  16. }
  17. function getDocumentElement(node) {
  18. var _ref;
  19. return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement;
  20. }
  21. function isNode(value) {
  22. if (!hasWindow()) {
  23. return false;
  24. }
  25. return value instanceof Node || value instanceof getWindow(value).Node;
  26. }
  27. function isElement(value) {
  28. if (!hasWindow()) {
  29. return false;
  30. }
  31. return value instanceof Element || value instanceof getWindow(value).Element;
  32. }
  33. function isHTMLElement(value) {
  34. if (!hasWindow()) {
  35. return false;
  36. }
  37. return value instanceof HTMLElement || value instanceof getWindow(value).HTMLElement;
  38. }
  39. function isShadowRoot(value) {
  40. if (!hasWindow() || typeof ShadowRoot === 'undefined') {
  41. return false;
  42. }
  43. return value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot;
  44. }
  45. const invalidOverflowDisplayValues = /*#__PURE__*/new Set(['inline', 'contents']);
  46. function isOverflowElement(element) {
  47. const {
  48. overflow,
  49. overflowX,
  50. overflowY,
  51. display
  52. } = getComputedStyle(element);
  53. return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !invalidOverflowDisplayValues.has(display);
  54. }
  55. const tableElements = /*#__PURE__*/new Set(['table', 'td', 'th']);
  56. function isTableElement(element) {
  57. return tableElements.has(getNodeName(element));
  58. }
  59. const topLayerSelectors = [':popover-open', ':modal'];
  60. function isTopLayer(element) {
  61. return topLayerSelectors.some(selector => {
  62. try {
  63. return element.matches(selector);
  64. } catch (_e) {
  65. return false;
  66. }
  67. });
  68. }
  69. const transformProperties = ['transform', 'translate', 'scale', 'rotate', 'perspective'];
  70. const willChangeValues = ['transform', 'translate', 'scale', 'rotate', 'perspective', 'filter'];
  71. const containValues = ['paint', 'layout', 'strict', 'content'];
  72. function isContainingBlock(elementOrCss) {
  73. const webkit = isWebKit();
  74. const css = isElement(elementOrCss) ? getComputedStyle(elementOrCss) : elementOrCss;
  75. // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
  76. // https://drafts.csswg.org/css-transforms-2/#individual-transforms
  77. return transformProperties.some(value => css[value] ? css[value] !== 'none' : false) || (css.containerType ? css.containerType !== 'normal' : false) || !webkit && (css.backdropFilter ? css.backdropFilter !== 'none' : false) || !webkit && (css.filter ? css.filter !== 'none' : false) || willChangeValues.some(value => (css.willChange || '').includes(value)) || containValues.some(value => (css.contain || '').includes(value));
  78. }
  79. function getContainingBlock(element) {
  80. let currentNode = getParentNode(element);
  81. while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
  82. if (isContainingBlock(currentNode)) {
  83. return currentNode;
  84. } else if (isTopLayer(currentNode)) {
  85. return null;
  86. }
  87. currentNode = getParentNode(currentNode);
  88. }
  89. return null;
  90. }
  91. function isWebKit() {
  92. if (typeof CSS === 'undefined' || !CSS.supports) return false;
  93. return CSS.supports('-webkit-backdrop-filter', 'none');
  94. }
  95. const lastTraversableNodeNames = /*#__PURE__*/new Set(['html', 'body', '#document']);
  96. function isLastTraversableNode(node) {
  97. return lastTraversableNodeNames.has(getNodeName(node));
  98. }
  99. function getComputedStyle(element) {
  100. return getWindow(element).getComputedStyle(element);
  101. }
  102. function getNodeScroll(element) {
  103. if (isElement(element)) {
  104. return {
  105. scrollLeft: element.scrollLeft,
  106. scrollTop: element.scrollTop
  107. };
  108. }
  109. return {
  110. scrollLeft: element.scrollX,
  111. scrollTop: element.scrollY
  112. };
  113. }
  114. function getParentNode(node) {
  115. if (getNodeName(node) === 'html') {
  116. return node;
  117. }
  118. const result =
  119. // Step into the shadow DOM of the parent of a slotted node.
  120. node.assignedSlot ||
  121. // DOM Element detected.
  122. node.parentNode ||
  123. // ShadowRoot detected.
  124. isShadowRoot(node) && node.host ||
  125. // Fallback.
  126. getDocumentElement(node);
  127. return isShadowRoot(result) ? result.host : result;
  128. }
  129. function getNearestOverflowAncestor(node) {
  130. const parentNode = getParentNode(node);
  131. if (isLastTraversableNode(parentNode)) {
  132. return node.ownerDocument ? node.ownerDocument.body : node.body;
  133. }
  134. if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
  135. return parentNode;
  136. }
  137. return getNearestOverflowAncestor(parentNode);
  138. }
  139. function getOverflowAncestors(node, list, traverseIframes) {
  140. var _node$ownerDocument2;
  141. if (list === void 0) {
  142. list = [];
  143. }
  144. if (traverseIframes === void 0) {
  145. traverseIframes = true;
  146. }
  147. const scrollableAncestor = getNearestOverflowAncestor(node);
  148. const isBody = scrollableAncestor === ((_node$ownerDocument2 = node.ownerDocument) == null ? void 0 : _node$ownerDocument2.body);
  149. const win = getWindow(scrollableAncestor);
  150. if (isBody) {
  151. const frameElement = getFrameElement(win);
  152. return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], frameElement && traverseIframes ? getOverflowAncestors(frameElement) : []);
  153. }
  154. return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes));
  155. }
  156. function getFrameElement(win) {
  157. return win.parent && Object.getPrototypeOf(win.parent) ? win.frameElement : null;
  158. }
  159. export { getComputedStyle, getContainingBlock, getDocumentElement, getFrameElement, getNearestOverflowAncestor, getNodeName, getNodeScroll, getOverflowAncestors, getParentNode, getWindow, isContainingBlock, isElement, isHTMLElement, isLastTraversableNode, isNode, isOverflowElement, isShadowRoot, isTableElement, isTopLayer, isWebKit };