Add supporting themes required for Lotusdocs

This commit is contained in:
Abner Coimbre
2026-01-11 16:48:19 -08:00
parent 8a4d04db58
commit f8d40c4e41
1289 changed files with 234948 additions and 0 deletions
@@ -0,0 +1 @@
export default function contains(parent: Element, child: Element): boolean;
@@ -0,0 +1,23 @@
import { isShadowRoot } from "./instanceOf.js";
export default function contains(parent, child) {
var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method
if (parent.contains(child)) {
return true;
} // then fallback to custom implementation with Shadow DOM support
else if (rootNode && isShadowRoot(rootNode)) {
var next = child;
do {
if (next && parent.isSameNode(next)) {
return true;
} // $FlowFixMe[prop-missing]: need a better way to handle this...
next = next.parentNode || next.host;
} while (next);
} // Give up, the result is false
return false;
}
@@ -0,0 +1,25 @@
// @flow
import { isShadowRoot } from './instanceOf';
export default function contains(parent: Element, child: Element) {
const rootNode = child.getRootNode && child.getRootNode();
// First, attempt with faster native method
if (parent.contains(child)) {
return true;
}
// then fallback to custom implementation with Shadow DOM support
else if (rootNode && isShadowRoot(rootNode)) {
let next = child;
do {
if (next && parent.isSameNode(next)) {
return true;
}
// $FlowFixMe[prop-missing]: need a better way to handle this...
next = next.parentNode || next.host;
} while (next);
}
// Give up, the result is false
return false;
}
@@ -0,0 +1,2 @@
import type { ClientRectObject, VirtualElement } from "../types";
export default function getBoundingClientRect(element: Element | VirtualElement, includeScale?: boolean): ClientRectObject;
@@ -0,0 +1,36 @@
import { isHTMLElement } from "./instanceOf.js";
import { round } from "../utils/math.js";
export default function getBoundingClientRect(element, includeScale) {
if (includeScale === void 0) {
includeScale = false;
}
var rect = element.getBoundingClientRect();
var scaleX = 1;
var scaleY = 1;
if (isHTMLElement(element) && includeScale) {
var offsetHeight = element.offsetHeight;
var offsetWidth = element.offsetWidth; // Do not attempt to divide by 0, otherwise we get `Infinity` as scale
// Fallback to 1 in case both values are `0`
if (offsetWidth > 0) {
scaleX = round(rect.width) / offsetWidth || 1;
}
if (offsetHeight > 0) {
scaleY = round(rect.height) / offsetHeight || 1;
}
}
return {
width: rect.width / scaleX,
height: rect.height / scaleY,
top: rect.top / scaleY,
right: rect.right / scaleX,
bottom: rect.bottom / scaleY,
left: rect.left / scaleX,
x: rect.left / scaleX,
y: rect.top / scaleY
};
}
@@ -0,0 +1,38 @@
// @flow
import type { ClientRectObject, VirtualElement } from '../types';
import { isHTMLElement } from './instanceOf';
import { round } from '../utils/math';
export default function getBoundingClientRect(
element: Element | VirtualElement,
includeScale: boolean = false
): ClientRectObject {
const rect = element.getBoundingClientRect();
let scaleX = 1;
let scaleY = 1;
if (isHTMLElement(element) && includeScale) {
const offsetHeight = element.offsetHeight;
const offsetWidth = element.offsetWidth;
// Do not attempt to divide by 0, otherwise we get `Infinity` as scale
// Fallback to 1 in case both values are `0`
if (offsetWidth > 0) {
scaleX = round(rect.width) / offsetWidth || 1;
}
if (offsetHeight > 0) {
scaleY = round(rect.height) / offsetHeight || 1;
}
}
return {
width: rect.width / scaleX,
height: rect.height / scaleY,
top: rect.top / scaleY,
right: rect.right / scaleX,
bottom: rect.bottom / scaleY,
left: rect.left / scaleX,
x: rect.left / scaleX,
y: rect.top / scaleY,
};
}
@@ -0,0 +1,3 @@
import type { ClientRectObject } from "../types";
import type { Boundary, RootBoundary } from "../enums";
export default function getClippingRect(element: Element, boundary: Boundary, rootBoundary: RootBoundary): ClientRectObject;
@@ -0,0 +1,70 @@
import { viewport } from "../enums.js";
import getViewportRect from "./getViewportRect.js";
import getDocumentRect from "./getDocumentRect.js";
import listScrollParents from "./listScrollParents.js";
import getOffsetParent from "./getOffsetParent.js";
import getDocumentElement from "./getDocumentElement.js";
import getComputedStyle from "./getComputedStyle.js";
import { isElement, isHTMLElement } from "./instanceOf.js";
import getBoundingClientRect from "./getBoundingClientRect.js";
import getParentNode from "./getParentNode.js";
import contains from "./contains.js";
import getNodeName from "./getNodeName.js";
import rectToClientRect from "../utils/rectToClientRect.js";
import { max, min } from "../utils/math.js";
function getInnerBoundingClientRect(element) {
var rect = getBoundingClientRect(element);
rect.top = rect.top + element.clientTop;
rect.left = rect.left + element.clientLeft;
rect.bottom = rect.top + element.clientHeight;
rect.right = rect.left + element.clientWidth;
rect.width = element.clientWidth;
rect.height = element.clientHeight;
rect.x = rect.left;
rect.y = rect.top;
return rect;
}
function getClientRectFromMixedType(element, clippingParent) {
return clippingParent === viewport ? rectToClientRect(getViewportRect(element)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent) : rectToClientRect(getDocumentRect(getDocumentElement(element)));
} // A "clipping parent" is an overflowable container with the characteristic of
// clipping (or hiding) overflowing elements with a position different from
// `initial`
function getClippingParents(element) {
var clippingParents = listScrollParents(getParentNode(element));
var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;
var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;
if (!isElement(clipperElement)) {
return [];
} // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414
return clippingParents.filter(function (clippingParent) {
return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body' && (canEscapeClipping ? getComputedStyle(clippingParent).position !== 'static' : true);
});
} // Gets the maximum area that the element is visible in due to any number of
// clipping parents
export default function getClippingRect(element, boundary, rootBoundary) {
var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);
var clippingParents = [].concat(mainClippingParents, [rootBoundary]);
var firstClippingParent = clippingParents[0];
var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {
var rect = getClientRectFromMixedType(element, clippingParent);
accRect.top = max(rect.top, accRect.top);
accRect.right = min(rect.right, accRect.right);
accRect.bottom = min(rect.bottom, accRect.bottom);
accRect.left = max(rect.left, accRect.left);
return accRect;
}, getClientRectFromMixedType(element, firstClippingParent));
clippingRect.width = clippingRect.right - clippingRect.left;
clippingRect.height = clippingRect.bottom - clippingRect.top;
clippingRect.x = clippingRect.left;
clippingRect.y = clippingRect.top;
return clippingRect;
}
@@ -0,0 +1,104 @@
// @flow
import type { ClientRectObject } from '../types';
import type { Boundary, RootBoundary } from '../enums';
import { viewport } from '../enums';
import getViewportRect from './getViewportRect';
import getDocumentRect from './getDocumentRect';
import listScrollParents from './listScrollParents';
import getOffsetParent from './getOffsetParent';
import getDocumentElement from './getDocumentElement';
import getComputedStyle from './getComputedStyle';
import { isElement, isHTMLElement } from './instanceOf';
import getBoundingClientRect from './getBoundingClientRect';
import getParentNode from './getParentNode';
import contains from './contains';
import getNodeName from './getNodeName';
import rectToClientRect from '../utils/rectToClientRect';
import { max, min } from '../utils/math';
function getInnerBoundingClientRect(element: Element) {
const rect = getBoundingClientRect(element);
rect.top = rect.top + element.clientTop;
rect.left = rect.left + element.clientLeft;
rect.bottom = rect.top + element.clientHeight;
rect.right = rect.left + element.clientWidth;
rect.width = element.clientWidth;
rect.height = element.clientHeight;
rect.x = rect.left;
rect.y = rect.top;
return rect;
}
function getClientRectFromMixedType(
element: Element,
clippingParent: Element | RootBoundary
): ClientRectObject {
return clippingParent === viewport
? rectToClientRect(getViewportRect(element))
: isElement(clippingParent)
? getInnerBoundingClientRect(clippingParent)
: rectToClientRect(getDocumentRect(getDocumentElement(element)));
}
// A "clipping parent" is an overflowable container with the characteristic of
// clipping (or hiding) overflowing elements with a position different from
// `initial`
function getClippingParents(element: Element): Array<Element> {
const clippingParents = listScrollParents(getParentNode(element));
const canEscapeClipping =
['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;
const clipperElement =
canEscapeClipping && isHTMLElement(element)
? getOffsetParent(element)
: element;
if (!isElement(clipperElement)) {
return [];
}
// $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414
return clippingParents.filter(
(clippingParent) =>
isElement(clippingParent) &&
contains(clippingParent, clipperElement) &&
getNodeName(clippingParent) !== 'body' &&
(canEscapeClipping
? getComputedStyle(clippingParent).position !== 'static'
: true)
);
}
// Gets the maximum area that the element is visible in due to any number of
// clipping parents
export default function getClippingRect(
element: Element,
boundary: Boundary,
rootBoundary: RootBoundary
): ClientRectObject {
const mainClippingParents =
boundary === 'clippingParents'
? getClippingParents(element)
: [].concat(boundary);
const clippingParents = [...mainClippingParents, rootBoundary];
const firstClippingParent = clippingParents[0];
const clippingRect = clippingParents.reduce((accRect, clippingParent) => {
const rect = getClientRectFromMixedType(element, clippingParent);
accRect.top = max(rect.top, accRect.top);
accRect.right = min(rect.right, accRect.right);
accRect.bottom = min(rect.bottom, accRect.bottom);
accRect.left = max(rect.left, accRect.left);
return accRect;
}, getClientRectFromMixedType(element, firstClippingParent));
clippingRect.width = clippingRect.right - clippingRect.left;
clippingRect.height = clippingRect.bottom - clippingRect.top;
clippingRect.x = clippingRect.left;
clippingRect.y = clippingRect.top;
return clippingRect;
}
@@ -0,0 +1,2 @@
import type { Rect, VirtualElement, Window } from "../types";
export default function getCompositeRect(elementOrVirtualElement: Element | VirtualElement, offsetParent: Element | Window, isFixed?: boolean): Rect;
@@ -0,0 +1,58 @@
import getBoundingClientRect from "./getBoundingClientRect.js";
import getNodeScroll from "./getNodeScroll.js";
import getNodeName from "./getNodeName.js";
import { isHTMLElement } from "./instanceOf.js";
import getWindowScrollBarX from "./getWindowScrollBarX.js";
import getDocumentElement from "./getDocumentElement.js";
import isScrollParent from "./isScrollParent.js";
import { round } from "../utils/math.js";
function isElementScaled(element) {
var rect = element.getBoundingClientRect();
var scaleX = round(rect.width) / element.offsetWidth || 1;
var scaleY = round(rect.height) / element.offsetHeight || 1;
return scaleX !== 1 || scaleY !== 1;
} // Returns the composite rect of an element relative to its offsetParent.
// Composite means it takes into account transforms as well as layout.
export default function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {
if (isFixed === void 0) {
isFixed = false;
}
var isOffsetParentAnElement = isHTMLElement(offsetParent);
var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);
var documentElement = getDocumentElement(offsetParent);
var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled);
var scroll = {
scrollLeft: 0,
scrollTop: 0
};
var offsets = {
x: 0,
y: 0
};
if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078
isScrollParent(documentElement)) {
scroll = getNodeScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
offsets = getBoundingClientRect(offsetParent, true);
offsets.x += offsetParent.clientLeft;
offsets.y += offsetParent.clientTop;
} else if (documentElement) {
offsets.x = getWindowScrollBarX(documentElement);
}
}
return {
x: rect.left + scroll.scrollLeft - offsets.x,
y: rect.top + scroll.scrollTop - offsets.y,
width: rect.width,
height: rect.height
};
}
@@ -0,0 +1,63 @@
// @flow
import type { Rect, VirtualElement, Window } from '../types';
import getBoundingClientRect from './getBoundingClientRect';
import getNodeScroll from './getNodeScroll';
import getNodeName from './getNodeName';
import { isHTMLElement } from './instanceOf';
import getWindowScrollBarX from './getWindowScrollBarX';
import getDocumentElement from './getDocumentElement';
import isScrollParent from './isScrollParent';
import { round } from '../utils/math';
function isElementScaled(element: HTMLElement) {
const rect = element.getBoundingClientRect();
const scaleX = round(rect.width) / element.offsetWidth || 1;
const scaleY = round(rect.height) / element.offsetHeight || 1;
return scaleX !== 1 || scaleY !== 1;
}
// Returns the composite rect of an element relative to its offsetParent.
// Composite means it takes into account transforms as well as layout.
export default function getCompositeRect(
elementOrVirtualElement: Element | VirtualElement,
offsetParent: Element | Window,
isFixed: boolean = false
): Rect {
const isOffsetParentAnElement = isHTMLElement(offsetParent);
const offsetParentIsScaled =
isHTMLElement(offsetParent) && isElementScaled(offsetParent);
const documentElement = getDocumentElement(offsetParent);
const rect = getBoundingClientRect(
elementOrVirtualElement,
offsetParentIsScaled
);
let scroll = { scrollLeft: 0, scrollTop: 0 };
let offsets = { x: 0, y: 0 };
if (isOffsetParentAnElement || (!isOffsetParentAnElement && !isFixed)) {
if (
getNodeName(offsetParent) !== 'body' ||
// https://github.com/popperjs/popper-core/issues/1078
isScrollParent(documentElement)
) {
scroll = getNodeScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
offsets = getBoundingClientRect(offsetParent, true);
offsets.x += offsetParent.clientLeft;
offsets.y += offsetParent.clientTop;
} else if (documentElement) {
offsets.x = getWindowScrollBarX(documentElement);
}
}
return {
x: rect.left + scroll.scrollLeft - offsets.x,
y: rect.top + scroll.scrollTop - offsets.y,
width: rect.width,
height: rect.height,
};
}
@@ -0,0 +1 @@
export default function getComputedStyle(element: Element): CSSStyleDeclaration;
@@ -0,0 +1,4 @@
import getWindow from "./getWindow.js";
export default function getComputedStyle(element) {
return getWindow(element).getComputedStyle(element);
}
@@ -0,0 +1,8 @@
// @flow
import getWindow from './getWindow';
export default function getComputedStyle(
element: Element
): CSSStyleDeclaration {
return getWindow(element).getComputedStyle(element);
}
@@ -0,0 +1,2 @@
import type { Window } from "../types";
export default function getDocumentElement(element: Element | Window): HTMLElement;
@@ -0,0 +1,6 @@
import { isElement } from "./instanceOf.js";
export default function getDocumentElement(element) {
// $FlowFixMe[incompatible-return]: assume body is always available
return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]
element.document) || window.document).documentElement;
}
@@ -0,0 +1,15 @@
// @flow
import { isElement } from './instanceOf';
import type { Window } from '../types';
export default function getDocumentElement(
element: Element | Window
): HTMLElement {
// $FlowFixMe[incompatible-return]: assume body is always available
return (
(isElement(element)
? element.ownerDocument
: // $FlowFixMe[prop-missing]
element.document) || window.document
).documentElement;
}
@@ -0,0 +1,2 @@
import type { Rect } from "../types";
export default function getDocumentRect(element: HTMLElement): Rect;
@@ -0,0 +1,29 @@
import getDocumentElement from "./getDocumentElement.js";
import getComputedStyle from "./getComputedStyle.js";
import getWindowScrollBarX from "./getWindowScrollBarX.js";
import getWindowScroll from "./getWindowScroll.js";
import { max } from "../utils/math.js"; // Gets the entire size of the scrollable document area, even extending outside
// of the `<html>` and `<body>` rect bounds if horizontally scrollable
export default function getDocumentRect(element) {
var _element$ownerDocumen;
var html = getDocumentElement(element);
var winScroll = getWindowScroll(element);
var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;
var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);
var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);
var x = -winScroll.scrollLeft + getWindowScrollBarX(element);
var y = -winScroll.scrollTop;
if (getComputedStyle(body || html).direction === 'rtl') {
x += max(html.clientWidth, body ? body.clientWidth : 0) - width;
}
return {
width: width,
height: height,
x: x,
y: y
};
}
@@ -0,0 +1,37 @@
// @flow
import type { Rect } from '../types';
import getDocumentElement from './getDocumentElement';
import getComputedStyle from './getComputedStyle';
import getWindowScrollBarX from './getWindowScrollBarX';
import getWindowScroll from './getWindowScroll';
import { max } from '../utils/math';
// Gets the entire size of the scrollable document area, even extending outside
// of the `<html>` and `<body>` rect bounds if horizontally scrollable
export default function getDocumentRect(element: HTMLElement): Rect {
const html = getDocumentElement(element);
const winScroll = getWindowScroll(element);
const body = element.ownerDocument?.body;
const width = max(
html.scrollWidth,
html.clientWidth,
body ? body.scrollWidth : 0,
body ? body.clientWidth : 0
);
const height = max(
html.scrollHeight,
html.clientHeight,
body ? body.scrollHeight : 0,
body ? body.clientHeight : 0
);
let x = -winScroll.scrollLeft + getWindowScrollBarX(element);
const y = -winScroll.scrollTop;
if (getComputedStyle(body || html).direction === 'rtl') {
x += max(html.clientWidth, body ? body.clientWidth : 0) - width;
}
return { width, height, x, y };
}
@@ -0,0 +1,4 @@
export default function getHTMLElementScroll(element: HTMLElement): {
scrollLeft: number;
scrollTop: number;
};
@@ -0,0 +1,6 @@
export default function getHTMLElementScroll(element) {
return {
scrollLeft: element.scrollLeft,
scrollTop: element.scrollTop
};
}
@@ -0,0 +1,8 @@
// @flow
export default function getHTMLElementScroll(element: HTMLElement) {
return {
scrollLeft: element.scrollLeft,
scrollTop: element.scrollTop,
};
}
@@ -0,0 +1,2 @@
import type { Rect } from "../types";
export default function getLayoutRect(element: HTMLElement): Rect;
@@ -0,0 +1,25 @@
import getBoundingClientRect from "./getBoundingClientRect.js"; // Returns the layout rect of an element relative to its offsetParent. Layout
// means it doesn't take into account transforms.
export default function getLayoutRect(element) {
var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.
// Fixes https://github.com/popperjs/popper-core/issues/1223
var width = element.offsetWidth;
var height = element.offsetHeight;
if (Math.abs(clientRect.width - width) <= 1) {
width = clientRect.width;
}
if (Math.abs(clientRect.height - height) <= 1) {
height = clientRect.height;
}
return {
x: element.offsetLeft,
y: element.offsetTop,
width: width,
height: height
};
}
@@ -0,0 +1,29 @@
// @flow
import type { Rect } from '../types';
import getBoundingClientRect from './getBoundingClientRect';
// Returns the layout rect of an element relative to its offsetParent. Layout
// means it doesn't take into account transforms.
export default function getLayoutRect(element: HTMLElement): Rect {
const clientRect = getBoundingClientRect(element);
// Use the clientRect sizes if it's not been transformed.
// Fixes https://github.com/popperjs/popper-core/issues/1223
let width = element.offsetWidth;
let height = element.offsetHeight;
if (Math.abs(clientRect.width - width) <= 1) {
width = clientRect.width;
}
if (Math.abs(clientRect.height - height) <= 1) {
height = clientRect.height;
}
return {
x: element.offsetLeft,
y: element.offsetTop,
width,
height,
};
}
@@ -0,0 +1,2 @@
import type { Window } from "../types";
export default function getNodeName(element: (Node | null | undefined) | Window): string | null | undefined;
@@ -0,0 +1,3 @@
export default function getNodeName(element) {
return element ? (element.nodeName || '').toLowerCase() : null;
}
@@ -0,0 +1,6 @@
// @flow
import type { Window } from '../types';
export default function getNodeName(element: ?Node | Window): ?string {
return element ? (element.nodeName || '').toLowerCase() : null;
}
@@ -0,0 +1,5 @@
import type { Window } from "../types";
export default function getNodeScroll(node: Node | Window): {
scrollLeft: any;
scrollTop: any;
};
@@ -0,0 +1,11 @@
import getWindowScroll from "./getWindowScroll.js";
import getWindow from "./getWindow.js";
import { isHTMLElement } from "./instanceOf.js";
import getHTMLElementScroll from "./getHTMLElementScroll.js";
export default function getNodeScroll(node) {
if (node === getWindow(node) || !isHTMLElement(node)) {
return getWindowScroll(node);
} else {
return getHTMLElementScroll(node);
}
}
@@ -0,0 +1,14 @@
// @flow
import getWindowScroll from './getWindowScroll';
import getWindow from './getWindow';
import { isHTMLElement } from './instanceOf';
import getHTMLElementScroll from './getHTMLElementScroll';
import type { Window } from '../types';
export default function getNodeScroll(node: Node | Window) {
if (node === getWindow(node) || !isHTMLElement(node)) {
return getWindowScroll(node);
} else {
return getHTMLElementScroll(node);
}
}
@@ -0,0 +1 @@
export default function getOffsetParent(element: Element): any;
@@ -0,0 +1,64 @@
import getWindow from "./getWindow.js";
import getNodeName from "./getNodeName.js";
import getComputedStyle from "./getComputedStyle.js";
import { isHTMLElement } from "./instanceOf.js";
import isTableElement from "./isTableElement.js";
import getParentNode from "./getParentNode.js";
function getTrueOffsetParent(element) {
if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837
getComputedStyle(element).position === 'fixed') {
return null;
}
return element.offsetParent;
} // `.offsetParent` reports `null` for fixed elements, while absolute elements
// return the containing block
function getContainingBlock(element) {
var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') !== -1;
var isIE = navigator.userAgent.indexOf('Trident') !== -1;
if (isIE && isHTMLElement(element)) {
// In IE 9, 10 and 11 fixed elements containing block is always established by the viewport
var elementCss = getComputedStyle(element);
if (elementCss.position === 'fixed') {
return null;
}
}
var currentNode = getParentNode(element);
while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {
var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that
// create a containing block.
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {
return currentNode;
} else {
currentNode = currentNode.parentNode;
}
}
return null;
} // Gets the closest ancestor positioned element. Handles some edge cases,
// such as table ancestors and cross browser bugs.
export default function getOffsetParent(element) {
var window = getWindow(element);
var offsetParent = getTrueOffsetParent(element);
while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {
offsetParent = getTrueOffsetParent(offsetParent);
}
if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {
return window;
}
return offsetParent || getContainingBlock(element) || window;
}
@@ -0,0 +1,88 @@
// @flow
import getWindow from './getWindow';
import getNodeName from './getNodeName';
import getComputedStyle from './getComputedStyle';
import { isHTMLElement } from './instanceOf';
import isTableElement from './isTableElement';
import getParentNode from './getParentNode';
function getTrueOffsetParent(element: Element): ?Element {
if (
!isHTMLElement(element) ||
// https://github.com/popperjs/popper-core/issues/837
getComputedStyle(element).position === 'fixed'
) {
return null;
}
return element.offsetParent;
}
// `.offsetParent` reports `null` for fixed elements, while absolute elements
// return the containing block
function getContainingBlock(element: Element) {
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') !== -1;
const isIE = navigator.userAgent.indexOf('Trident') !== -1;
if (isIE && isHTMLElement(element)) {
// In IE 9, 10 and 11 fixed elements containing block is always established by the viewport
const elementCss = getComputedStyle(element);
if (elementCss.position === 'fixed') {
return null;
}
}
let currentNode = getParentNode(element);
while (
isHTMLElement(currentNode) &&
['html', 'body'].indexOf(getNodeName(currentNode)) < 0
) {
const css = getComputedStyle(currentNode);
// This is non-exhaustive but covers the most common CSS properties that
// create a containing block.
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
if (
css.transform !== 'none' ||
css.perspective !== 'none' ||
css.contain === 'paint' ||
['transform', 'perspective'].indexOf(css.willChange) !== -1 ||
(isFirefox && css.willChange === 'filter') ||
(isFirefox && css.filter && css.filter !== 'none')
) {
return currentNode;
} else {
currentNode = currentNode.parentNode;
}
}
return null;
}
// Gets the closest ancestor positioned element. Handles some edge cases,
// such as table ancestors and cross browser bugs.
export default function getOffsetParent(element: Element) {
const window = getWindow(element);
let offsetParent = getTrueOffsetParent(element);
while (
offsetParent &&
isTableElement(offsetParent) &&
getComputedStyle(offsetParent).position === 'static'
) {
offsetParent = getTrueOffsetParent(offsetParent);
}
if (
offsetParent &&
(getNodeName(offsetParent) === 'html' ||
(getNodeName(offsetParent) === 'body' &&
getComputedStyle(offsetParent).position === 'static'))
) {
return window;
}
return offsetParent || getContainingBlock(element) || window;
}
@@ -0,0 +1 @@
export default function getParentNode(element: Node | ShadowRoot): Node;
@@ -0,0 +1,19 @@
import getNodeName from "./getNodeName.js";
import getDocumentElement from "./getDocumentElement.js";
import { isShadowRoot } from "./instanceOf.js";
export default function getParentNode(element) {
if (getNodeName(element) === 'html') {
return element;
}
return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle
// $FlowFixMe[incompatible-return]
// $FlowFixMe[prop-missing]
element.assignedSlot || // step into the shadow DOM of the parent of a slotted node
element.parentNode || ( // DOM Element detected
isShadowRoot(element) ? element.host : null) || // ShadowRoot detected
// $FlowFixMe[incompatible-call]: HTMLElement is a Node
getDocumentElement(element) // fallback
);
}
@@ -0,0 +1,21 @@
// @flow
import getNodeName from './getNodeName';
import getDocumentElement from './getDocumentElement';
import { isShadowRoot } from './instanceOf';
export default function getParentNode(element: Node | ShadowRoot): Node {
if (getNodeName(element) === 'html') {
return element;
}
return (
// this is a quicker (but less type safe) way to save quite some bytes from the bundle
// $FlowFixMe[incompatible-return]
// $FlowFixMe[prop-missing]
element.assignedSlot || // step into the shadow DOM of the parent of a slotted node
element.parentNode || // DOM Element detected
(isShadowRoot(element) ? element.host : null) || // ShadowRoot detected
// $FlowFixMe[incompatible-call]: HTMLElement is a Node
getDocumentElement(element) // fallback
);
}
@@ -0,0 +1 @@
export default function getScrollParent(node: Node): HTMLElement;
@@ -0,0 +1,16 @@
import getParentNode from "./getParentNode.js";
import isScrollParent from "./isScrollParent.js";
import getNodeName from "./getNodeName.js";
import { isHTMLElement } from "./instanceOf.js";
export default function getScrollParent(node) {
if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {
// $FlowFixMe[incompatible-return]: assume body is always available
return node.ownerDocument.body;
}
if (isHTMLElement(node) && isScrollParent(node)) {
return node;
}
return getScrollParent(getParentNode(node));
}
@@ -0,0 +1,18 @@
// @flow
import getParentNode from './getParentNode';
import isScrollParent from './isScrollParent';
import getNodeName from './getNodeName';
import { isHTMLElement } from './instanceOf';
export default function getScrollParent(node: Node): HTMLElement {
if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {
// $FlowFixMe[incompatible-return]: assume body is always available
return node.ownerDocument.body;
}
if (isHTMLElement(node) && isScrollParent(node)) {
return node;
}
return getScrollParent(getParentNode(node));
}
@@ -0,0 +1,6 @@
export default function getViewportRect(element: Element): {
width: number;
height: number;
x: number;
y: number;
};
@@ -0,0 +1,40 @@
import getWindow from "./getWindow.js";
import getDocumentElement from "./getDocumentElement.js";
import getWindowScrollBarX from "./getWindowScrollBarX.js";
export default function getViewportRect(element) {
var win = getWindow(element);
var html = getDocumentElement(element);
var visualViewport = win.visualViewport;
var width = html.clientWidth;
var height = html.clientHeight;
var x = 0;
var y = 0; // NB: This isn't supported on iOS <= 12. If the keyboard is open, the popper
// can be obscured underneath it.
// Also, `html.clientHeight` adds the bottom bar height in Safari iOS, even
// if it isn't open, so if this isn't available, the popper will be detected
// to overflow the bottom of the screen too early.
if (visualViewport) {
width = visualViewport.width;
height = visualViewport.height; // Uses Layout Viewport (like Chrome; Safari does not currently)
// In Chrome, it returns a value very close to 0 (+/-) but contains rounding
// errors due to floating point numbers, so we need to check precision.
// Safari returns a number <= 0, usually < -1 when pinch-zoomed
// Feature detection fails in mobile emulation mode in Chrome.
// Math.abs(win.innerWidth / visualViewport.scale - visualViewport.width) <
// 0.001
// Fallback here: "Not Safari" userAgent
if (!/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
x = visualViewport.offsetLeft;
y = visualViewport.offsetTop;
}
}
return {
width: width,
height: height,
x: x + getWindowScrollBarX(element),
y: y
};
}
@@ -0,0 +1,46 @@
// @flow
import getWindow from './getWindow';
import getDocumentElement from './getDocumentElement';
import getWindowScrollBarX from './getWindowScrollBarX';
export default function getViewportRect(element: Element) {
const win = getWindow(element);
const html = getDocumentElement(element);
const visualViewport = win.visualViewport;
let width = html.clientWidth;
let height = html.clientHeight;
let x = 0;
let y = 0;
// NB: This isn't supported on iOS <= 12. If the keyboard is open, the popper
// can be obscured underneath it.
// Also, `html.clientHeight` adds the bottom bar height in Safari iOS, even
// if it isn't open, so if this isn't available, the popper will be detected
// to overflow the bottom of the screen too early.
if (visualViewport) {
width = visualViewport.width;
height = visualViewport.height;
// Uses Layout Viewport (like Chrome; Safari does not currently)
// In Chrome, it returns a value very close to 0 (+/-) but contains rounding
// errors due to floating point numbers, so we need to check precision.
// Safari returns a number <= 0, usually < -1 when pinch-zoomed
// Feature detection fails in mobile emulation mode in Chrome.
// Math.abs(win.innerWidth / visualViewport.scale - visualViewport.width) <
// 0.001
// Fallback here: "Not Safari" userAgent
if (!/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
x = visualViewport.offsetLeft;
y = visualViewport.offsetTop;
}
}
return {
width,
height,
x: x + getWindowScrollBarX(element),
y,
};
}
@@ -0,0 +1 @@
export default function getWindow(node: any): any;
@@ -0,0 +1,12 @@
export default function getWindow(node) {
if (node == null) {
return window;
}
if (node.toString() !== '[object Window]') {
var ownerDocument = node.ownerDocument;
return ownerDocument ? ownerDocument.defaultView || window : window;
}
return node;
}
@@ -0,0 +1,16 @@
// @flow
import type { Window } from '../types';
declare function getWindow(node: Node | Window): Window;
export default function getWindow(node) {
if (node == null) {
return window;
}
if (node.toString() !== '[object Window]') {
const ownerDocument = node.ownerDocument;
return ownerDocument ? ownerDocument.defaultView || window : window;
}
return node;
}
@@ -0,0 +1,5 @@
import type { Window } from "../types";
export default function getWindowScroll(node: Node | Window): {
scrollLeft: any;
scrollTop: any;
};
@@ -0,0 +1,10 @@
import getWindow from "./getWindow.js";
export default function getWindowScroll(node) {
var win = getWindow(node);
var scrollLeft = win.pageXOffset;
var scrollTop = win.pageYOffset;
return {
scrollLeft: scrollLeft,
scrollTop: scrollTop
};
}
@@ -0,0 +1,14 @@
// @flow
import getWindow from './getWindow';
import type { Window } from '../types';
export default function getWindowScroll(node: Node | Window) {
const win = getWindow(node);
const scrollLeft = win.pageXOffset;
const scrollTop = win.pageYOffset;
return {
scrollLeft,
scrollTop,
};
}
@@ -0,0 +1 @@
export default function getWindowScrollBarX(element: Element): number;
@@ -0,0 +1,13 @@
import getBoundingClientRect from "./getBoundingClientRect.js";
import getDocumentElement from "./getDocumentElement.js";
import getWindowScroll from "./getWindowScroll.js";
export default function getWindowScrollBarX(element) {
// If <html> has a CSS width greater than the viewport, then this will be
// incorrect for RTL.
// Popper 1 is broken in this case and never had a bug report so let's assume
// it's not an issue. I don't think anyone ever specifies width on <html>
// anyway.
// Browsers where the left scrollbar doesn't cause an issue report `0` for
// this (e.g. Edge 2019, IE11, Safari)
return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;
}
@@ -0,0 +1,18 @@
// @flow
import getBoundingClientRect from './getBoundingClientRect';
import getDocumentElement from './getDocumentElement';
import getWindowScroll from './getWindowScroll';
export default function getWindowScrollBarX(element: Element): number {
// If <html> has a CSS width greater than the viewport, then this will be
// incorrect for RTL.
// Popper 1 is broken in this case and never had a bug report so let's assume
// it's not an issue. I don't think anyone ever specifies width on <html>
// anyway.
// Browsers where the left scrollbar doesn't cause an issue report `0` for
// this (e.g. Edge 2019, IE11, Safari)
return (
getBoundingClientRect(getDocumentElement(element)).left +
getWindowScroll(element).scrollLeft
);
}
@@ -0,0 +1,4 @@
declare function isElement(node: unknown): boolean;
declare function isHTMLElement(node: unknown): boolean;
declare function isShadowRoot(node: unknown): boolean;
export { isElement, isHTMLElement, isShadowRoot };
@@ -0,0 +1,23 @@
import getWindow from "./getWindow.js";
function isElement(node) {
var OwnElement = getWindow(node).Element;
return node instanceof OwnElement || node instanceof Element;
}
function isHTMLElement(node) {
var OwnElement = getWindow(node).HTMLElement;
return node instanceof OwnElement || node instanceof HTMLElement;
}
function isShadowRoot(node) {
// IE 11 has no ShadowRoot
if (typeof ShadowRoot === 'undefined') {
return false;
}
var OwnElement = getWindow(node).ShadowRoot;
return node instanceof OwnElement || node instanceof ShadowRoot;
}
export { isElement, isHTMLElement, isShadowRoot };
@@ -0,0 +1,29 @@
// @flow
import getWindow from './getWindow';
declare function isElement(node: mixed): boolean %checks(node instanceof
Element);
function isElement(node) {
const OwnElement = getWindow(node).Element;
return node instanceof OwnElement || node instanceof Element;
}
declare function isHTMLElement(node: mixed): boolean %checks(node instanceof
HTMLElement);
function isHTMLElement(node) {
const OwnElement = getWindow(node).HTMLElement;
return node instanceof OwnElement || node instanceof HTMLElement;
}
declare function isShadowRoot(node: mixed): boolean %checks(node instanceof
ShadowRoot);
function isShadowRoot(node) {
// IE 11 has no ShadowRoot
if (typeof ShadowRoot === 'undefined') {
return false;
}
const OwnElement = getWindow(node).ShadowRoot;
return node instanceof OwnElement || node instanceof ShadowRoot;
}
export { isElement, isHTMLElement, isShadowRoot };
@@ -0,0 +1 @@
export default function isScrollParent(element: HTMLElement): boolean;
@@ -0,0 +1,10 @@
import getComputedStyle from "./getComputedStyle.js";
export default function isScrollParent(element) {
// Firefox wants us to check `-x` and `-y` variations as well
var _getComputedStyle = getComputedStyle(element),
overflow = _getComputedStyle.overflow,
overflowX = _getComputedStyle.overflowX,
overflowY = _getComputedStyle.overflowY;
return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);
}
@@ -0,0 +1,8 @@
// @flow
import getComputedStyle from './getComputedStyle';
export default function isScrollParent(element: HTMLElement): boolean {
// Firefox wants us to check `-x` and `-y` variations as well
const { overflow, overflowX, overflowY } = getComputedStyle(element);
return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);
}
@@ -0,0 +1 @@
export default function isTableElement(element: Element): boolean;
@@ -0,0 +1,4 @@
import getNodeName from "./getNodeName.js";
export default function isTableElement(element) {
return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;
}
@@ -0,0 +1,6 @@
// @flow
import getNodeName from './getNodeName';
export default function isTableElement(element: Element): boolean {
return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;
}
@@ -0,0 +1,2 @@
import type { Window, VisualViewport } from "../types";
export default function listScrollParents(element: Node, list?: Array<Element | Window>): Array<Element | Window | VisualViewport>;
@@ -0,0 +1,26 @@
import getScrollParent from "./getScrollParent.js";
import getParentNode from "./getParentNode.js";
import getWindow from "./getWindow.js";
import isScrollParent from "./isScrollParent.js";
/*
given a DOM element, return the list of all scroll parents, up the list of ancesors
until we get to the top window object. This list is what we attach scroll listeners
to, because if any of these parent elements scroll, we'll need to re-calculate the
reference element's position.
*/
export default function listScrollParents(element, list) {
var _element$ownerDocumen;
if (list === void 0) {
list = [];
}
var scrollParent = getScrollParent(element);
var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);
var win = getWindow(scrollParent);
var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;
var updatedList = list.concat(target);
return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here
updatedList.concat(listScrollParents(getParentNode(target)));
}
@@ -0,0 +1,33 @@
// @flow
import getScrollParent from './getScrollParent';
import getParentNode from './getParentNode';
import getWindow from './getWindow';
import type { Window, VisualViewport } from '../types';
import isScrollParent from './isScrollParent';
/*
given a DOM element, return the list of all scroll parents, up the list of ancesors
until we get to the top window object. This list is what we attach scroll listeners
to, because if any of these parent elements scroll, we'll need to re-calculate the
reference element's position.
*/
export default function listScrollParents(
element: Node,
list: Array<Element | Window> = []
): Array<Element | Window | VisualViewport> {
const scrollParent = getScrollParent(element);
const isBody = scrollParent === element.ownerDocument?.body;
const win = getWindow(scrollParent);
const target = isBody
? [win].concat(
win.visualViewport || [],
isScrollParent(scrollParent) ? scrollParent : []
)
: scrollParent;
const updatedList = list.concat(target);
return isBody
? updatedList
: // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here
updatedList.concat(listScrollParents(getParentNode(target)));
}