| 1 | export type ElementLayoutSize = { |
| 2 | width: number; |
| 3 | height: number; |
| 4 | }; |
| 5 | |
| 6 | export const MAX_INITIAL_OVERLAY_MEASUREMENT_FRAMES = 8; |
| 7 | |
| 8 | export function isElementExplicitlyHidden(element: HTMLElement): boolean { |
| 9 | for (let current: HTMLElement | null = element; current; current = current.parentElement) { |
| 10 | if (current.hidden || current.hasAttribute("inert")) return true; |
| 11 | const style = window.getComputedStyle(current); |
| 12 | if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse") return true; |
| 13 | } |
| 14 | return false; |
| 15 | } |
| 16 | |
| 17 | export function validAnchorRect(element: HTMLElement | null): DOMRect | null { |
| 18 | if (!element?.isConnected || isElementExplicitlyHidden(element)) return null; |
| 19 | const rect = element.getBoundingClientRect(); |
| 20 | if ( |
| 21 | !Number.isFinite(rect.left) || |
| 22 | !Number.isFinite(rect.top) || |
| 23 | !Number.isFinite(rect.width) || |
| 24 | !Number.isFinite(rect.height) || |
| 25 | rect.width <= 0 || |
| 26 | rect.height <= 0 |
| 27 | ) return null; |
| 28 | return rect; |
| 29 | } |
| 30 | |
| 31 | // offsetWidth/offsetHeight are layout dimensions and do not include the |
| 32 | // popover's entry/exit transform. JSDOM and a few embedders may only expose a |
| 33 | // useful bounding rect, so retain that as a fallback. |
| 34 | export function elementLayoutSize(element: HTMLElement): ElementLayoutSize | null { |
| 35 | const rect = element.getBoundingClientRect(); |
| 36 | const width = element.offsetWidth || rect.width; |
| 37 | const height = element.offsetHeight || rect.height; |
| 38 | if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return null; |
| 39 | return { width, height }; |
| 40 | } |
| 41 |