New source found from dndbeyond.com
This commit is contained in:
+102
-40
@@ -1,10 +1,10 @@
|
||||
import { rectToClientRect, detectOverflow as detectOverflow$1, offset as offset$1, autoPlacement as autoPlacement$1, shift as shift$1, flip as flip$1, size as size$1, hide as hide$1, arrow as arrow$1, inline as inline$1, limitShift as limitShift$1, computePosition as computePosition$1 } from '@floating-ui/core';
|
||||
import { rectToClientRect, arrow as arrow$1, autoPlacement as autoPlacement$1, detectOverflow as detectOverflow$1, flip as flip$1, hide as hide$1, inline as inline$1, limitShift as limitShift$1, offset as offset$1, shift as shift$1, size as size$1, computePosition as computePosition$1 } from '@floating-ui/core';
|
||||
import { round, createCoords, max, min, floor } from '@floating-ui/utils';
|
||||
import { getComputedStyle, isHTMLElement, isElement, getWindow, isWebKit, getDocumentElement, getNodeName, isOverflowElement, getNodeScroll, getOverflowAncestors, getParentNode, isLastTraversableNode, isContainingBlock, isTableElement, getContainingBlock } from '@floating-ui/utils/dom';
|
||||
import { getComputedStyle as getComputedStyle$1, isHTMLElement, isElement, getWindow, isWebKit, getFrameElement, getNodeScroll, getDocumentElement, isTopLayer, getNodeName, isOverflowElement, getOverflowAncestors, getParentNode, isLastTraversableNode, isContainingBlock, isTableElement, getContainingBlock } from '@floating-ui/utils/dom';
|
||||
export { getOverflowAncestors } from '@floating-ui/utils/dom';
|
||||
|
||||
function getCssDimensions(element) {
|
||||
const css = getComputedStyle(element);
|
||||
const css = getComputedStyle$1(element);
|
||||
// In testing environments, the `width` and `height` properties are empty
|
||||
// strings for SVG elements, returning NaN. Fallback to `0` in this case.
|
||||
let width = parseFloat(css.width) || 0;
|
||||
@@ -105,11 +105,11 @@ function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetPar
|
||||
const win = getWindow(domElement);
|
||||
const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;
|
||||
let currentWin = win;
|
||||
let currentIFrame = currentWin.frameElement;
|
||||
let currentIFrame = getFrameElement(currentWin);
|
||||
while (currentIFrame && offsetParent && offsetWin !== currentWin) {
|
||||
const iframeScale = getScale(currentIFrame);
|
||||
const iframeRect = currentIFrame.getBoundingClientRect();
|
||||
const css = getComputedStyle(currentIFrame);
|
||||
const css = getComputedStyle$1(currentIFrame);
|
||||
const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;
|
||||
const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;
|
||||
x *= iframeScale.x;
|
||||
@@ -119,7 +119,7 @@ function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetPar
|
||||
x += left;
|
||||
y += top;
|
||||
currentWin = getWindow(currentIFrame);
|
||||
currentIFrame = currentWin.frameElement;
|
||||
currentIFrame = getFrameElement(currentWin);
|
||||
}
|
||||
}
|
||||
return rectToClientRect({
|
||||
@@ -130,15 +130,24 @@ function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetPar
|
||||
});
|
||||
}
|
||||
|
||||
const topLayerSelectors = [':popover-open', ':modal'];
|
||||
function isTopLayer(element) {
|
||||
return topLayerSelectors.some(selector => {
|
||||
try {
|
||||
return element.matches(selector);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// If <html> has a CSS width greater than the viewport, then this will be
|
||||
// incorrect for RTL.
|
||||
function getWindowScrollBarX(element, rect) {
|
||||
const leftScroll = getNodeScroll(element).scrollLeft;
|
||||
if (!rect) {
|
||||
return getBoundingClientRect(getDocumentElement(element)).left + leftScroll;
|
||||
}
|
||||
return rect.left + leftScroll;
|
||||
}
|
||||
|
||||
function getHTMLOffset(documentElement, scroll) {
|
||||
const htmlRect = documentElement.getBoundingClientRect();
|
||||
const x = htmlRect.left + scroll.scrollLeft - getWindowScrollBarX(documentElement, htmlRect);
|
||||
const y = htmlRect.top + scroll.scrollTop;
|
||||
return {
|
||||
x,
|
||||
y
|
||||
};
|
||||
}
|
||||
|
||||
function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
|
||||
@@ -172,11 +181,12 @@ function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
|
||||
offsets.y = offsetRect.y + offsetParent.clientTop;
|
||||
}
|
||||
}
|
||||
const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);
|
||||
return {
|
||||
width: rect.width * scale.x,
|
||||
height: rect.height * scale.y,
|
||||
x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x,
|
||||
y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y
|
||||
x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x + htmlOffset.x,
|
||||
y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y + htmlOffset.y
|
||||
};
|
||||
}
|
||||
|
||||
@@ -184,12 +194,6 @@ function getClientRects(element) {
|
||||
return Array.from(element.getClientRects());
|
||||
}
|
||||
|
||||
function getWindowScrollBarX(element) {
|
||||
// If <html> has a CSS width greater than the viewport, then this will be
|
||||
// incorrect for RTL.
|
||||
return getBoundingClientRect(getDocumentElement(element)).left + getNodeScroll(element).scrollLeft;
|
||||
}
|
||||
|
||||
// Gets the entire size of the scrollable document area, even extending outside
|
||||
// of the `<html>` and `<body>` rect bounds if horizontally scrollable.
|
||||
function getDocumentRect(element) {
|
||||
@@ -200,7 +204,7 @@ function getDocumentRect(element) {
|
||||
const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);
|
||||
let x = -scroll.scrollLeft + getWindowScrollBarX(element);
|
||||
const y = -scroll.scrollTop;
|
||||
if (getComputedStyle(body).direction === 'rtl') {
|
||||
if (getComputedStyle$1(body).direction === 'rtl') {
|
||||
x += max(html.clientWidth, body.clientWidth) - width;
|
||||
}
|
||||
return {
|
||||
@@ -211,6 +215,10 @@ function getDocumentRect(element) {
|
||||
};
|
||||
}
|
||||
|
||||
// Safety check: ensure the scrollbar space is reasonable in case this
|
||||
// calculation is affected by unusual styles.
|
||||
// Most scrollbars leave 15-18px of space.
|
||||
const SCROLLBAR_MAX = 25;
|
||||
function getViewportRect(element, strategy) {
|
||||
const win = getWindow(element);
|
||||
const html = getDocumentElement(element);
|
||||
@@ -228,6 +236,24 @@ function getViewportRect(element, strategy) {
|
||||
y = visualViewport.offsetTop;
|
||||
}
|
||||
}
|
||||
const windowScrollbarX = getWindowScrollBarX(html);
|
||||
// <html> `overflow: hidden` + `scrollbar-gutter: stable` reduces the
|
||||
// visual width of the <html> but this is not considered in the size
|
||||
// of `html.clientWidth`.
|
||||
if (windowScrollbarX <= 0) {
|
||||
const doc = html.ownerDocument;
|
||||
const body = doc.body;
|
||||
const bodyStyles = getComputedStyle(body);
|
||||
const bodyMarginInline = doc.compatMode === 'CSS1Compat' ? parseFloat(bodyStyles.marginLeft) + parseFloat(bodyStyles.marginRight) || 0 : 0;
|
||||
const clippingStableScrollbarWidth = Math.abs(html.clientWidth - body.clientWidth - bodyMarginInline);
|
||||
if (clippingStableScrollbarWidth <= SCROLLBAR_MAX) {
|
||||
width -= clippingStableScrollbarWidth;
|
||||
}
|
||||
} else if (windowScrollbarX <= SCROLLBAR_MAX) {
|
||||
// If the <body> scrollbar is on the left, the width needs to be extended
|
||||
// by the scrollbar amount so there isn't extra space on the right.
|
||||
width += windowScrollbarX;
|
||||
}
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
@@ -236,6 +262,7 @@ function getViewportRect(element, strategy) {
|
||||
};
|
||||
}
|
||||
|
||||
const absoluteOrFixed = /*#__PURE__*/new Set(['absolute', 'fixed']);
|
||||
// Returns the inner client rect, subtracting scrollbars if present.
|
||||
function getInnerBoundingClientRect(element, strategy) {
|
||||
const clientRect = getBoundingClientRect(element, true, strategy === 'fixed');
|
||||
@@ -264,9 +291,10 @@ function getClientRectFromClippingAncestor(element, clippingAncestor, strategy)
|
||||
} else {
|
||||
const visualOffsets = getVisualOffsets(element);
|
||||
rect = {
|
||||
...clippingAncestor,
|
||||
x: clippingAncestor.x - visualOffsets.x,
|
||||
y: clippingAncestor.y - visualOffsets.y
|
||||
y: clippingAncestor.y - visualOffsets.y,
|
||||
width: clippingAncestor.width,
|
||||
height: clippingAncestor.height
|
||||
};
|
||||
}
|
||||
return rectToClientRect(rect);
|
||||
@@ -276,7 +304,7 @@ function hasFixedPositionAncestor(element, stopNode) {
|
||||
if (parentNode === stopNode || !isElement(parentNode) || isLastTraversableNode(parentNode)) {
|
||||
return false;
|
||||
}
|
||||
return getComputedStyle(parentNode).position === 'fixed' || hasFixedPositionAncestor(parentNode, stopNode);
|
||||
return getComputedStyle$1(parentNode).position === 'fixed' || hasFixedPositionAncestor(parentNode, stopNode);
|
||||
}
|
||||
|
||||
// A "clipping ancestor" is an `overflow` element with the characteristic of
|
||||
@@ -289,17 +317,17 @@ function getClippingElementAncestors(element, cache) {
|
||||
}
|
||||
let result = getOverflowAncestors(element, [], false).filter(el => isElement(el) && getNodeName(el) !== 'body');
|
||||
let currentContainingBlockComputedStyle = null;
|
||||
const elementIsFixed = getComputedStyle(element).position === 'fixed';
|
||||
const elementIsFixed = getComputedStyle$1(element).position === 'fixed';
|
||||
let currentNode = elementIsFixed ? getParentNode(element) : element;
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
|
||||
while (isElement(currentNode) && !isLastTraversableNode(currentNode)) {
|
||||
const computedStyle = getComputedStyle(currentNode);
|
||||
const computedStyle = getComputedStyle$1(currentNode);
|
||||
const currentNodeIsContaining = isContainingBlock(currentNode);
|
||||
if (!currentNodeIsContaining && computedStyle.position === 'fixed') {
|
||||
currentContainingBlockComputedStyle = null;
|
||||
}
|
||||
const shouldDropCurrentNode = elementIsFixed ? !currentNodeIsContaining && !currentContainingBlockComputedStyle : !currentNodeIsContaining && computedStyle.position === 'static' && !!currentContainingBlockComputedStyle && ['absolute', 'fixed'].includes(currentContainingBlockComputedStyle.position) || isOverflowElement(currentNode) && !currentNodeIsContaining && hasFixedPositionAncestor(element, currentNode);
|
||||
const shouldDropCurrentNode = elementIsFixed ? !currentNodeIsContaining && !currentContainingBlockComputedStyle : !currentNodeIsContaining && computedStyle.position === 'static' && !!currentContainingBlockComputedStyle && absoluteOrFixed.has(currentContainingBlockComputedStyle.position) || isOverflowElement(currentNode) && !currentNodeIsContaining && hasFixedPositionAncestor(element, currentNode);
|
||||
if (shouldDropCurrentNode) {
|
||||
// Drop non-containing blocks.
|
||||
result = result.filter(ancestor => ancestor !== currentNode);
|
||||
@@ -362,6 +390,12 @@ function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
|
||||
scrollTop: 0
|
||||
};
|
||||
const offsets = createCoords(0);
|
||||
|
||||
// If the <body> scrollbar appears on the left (e.g. RTL systems). Use
|
||||
// Firefox with layout.scrollbar.side = 3 in about:config to test this.
|
||||
function setLeftRTLScrollbarOffset() {
|
||||
offsets.x = getWindowScrollBarX(documentElement);
|
||||
}
|
||||
if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
|
||||
if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
|
||||
scroll = getNodeScroll(offsetParent);
|
||||
@@ -371,11 +405,15 @@ function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
|
||||
offsets.x = offsetRect.x + offsetParent.clientLeft;
|
||||
offsets.y = offsetRect.y + offsetParent.clientTop;
|
||||
} else if (documentElement) {
|
||||
offsets.x = getWindowScrollBarX(documentElement);
|
||||
setLeftRTLScrollbarOffset();
|
||||
}
|
||||
}
|
||||
const x = rect.left + scroll.scrollLeft - offsets.x;
|
||||
const y = rect.top + scroll.scrollTop - offsets.y;
|
||||
if (isFixed && !isOffsetParentAnElement && documentElement) {
|
||||
setLeftRTLScrollbarOffset();
|
||||
}
|
||||
const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);
|
||||
const x = rect.left + scroll.scrollLeft - offsets.x - htmlOffset.x;
|
||||
const y = rect.top + scroll.scrollTop - offsets.y - htmlOffset.y;
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
@@ -385,17 +423,26 @@ function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
|
||||
}
|
||||
|
||||
function isStaticPositioned(element) {
|
||||
return getComputedStyle(element).position === 'static';
|
||||
return getComputedStyle$1(element).position === 'static';
|
||||
}
|
||||
|
||||
function getTrueOffsetParent(element, polyfill) {
|
||||
if (!isHTMLElement(element) || getComputedStyle(element).position === 'fixed') {
|
||||
if (!isHTMLElement(element) || getComputedStyle$1(element).position === 'fixed') {
|
||||
return null;
|
||||
}
|
||||
if (polyfill) {
|
||||
return polyfill(element);
|
||||
}
|
||||
return element.offsetParent;
|
||||
let rawOffsetParent = element.offsetParent;
|
||||
|
||||
// Firefox returns the <html> element as the offsetParent if it's non-static,
|
||||
// while Chrome and Safari return the <body> element. The <body> element must
|
||||
// be used to perform the correct calculations even if the <html> element is
|
||||
// non-static.
|
||||
if (getDocumentElement(element) === rawOffsetParent) {
|
||||
rawOffsetParent = rawOffsetParent.ownerDocument.body;
|
||||
}
|
||||
return rawOffsetParent;
|
||||
}
|
||||
|
||||
// Gets the closest ancestor positioned element. Handles some edge cases,
|
||||
@@ -441,7 +488,7 @@ const getElementRects = async function (data) {
|
||||
};
|
||||
|
||||
function isRTL(element) {
|
||||
return getComputedStyle(element).direction === 'rtl';
|
||||
return getComputedStyle$1(element).direction === 'rtl';
|
||||
}
|
||||
|
||||
const platform = {
|
||||
@@ -457,6 +504,10 @@ const platform = {
|
||||
isRTL
|
||||
};
|
||||
|
||||
function rectsAreEqual(a, b) {
|
||||
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
||||
}
|
||||
|
||||
// https://samthor.au/2021/observing-dom/
|
||||
function observeMove(element, onMove) {
|
||||
let io = null;
|
||||
@@ -476,12 +527,13 @@ function observeMove(element, onMove) {
|
||||
threshold = 1;
|
||||
}
|
||||
cleanup();
|
||||
const elementRectForRootMargin = element.getBoundingClientRect();
|
||||
const {
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height
|
||||
} = element.getBoundingClientRect();
|
||||
} = elementRectForRootMargin;
|
||||
if (!skip) {
|
||||
onMove();
|
||||
}
|
||||
@@ -514,6 +566,16 @@ function observeMove(element, onMove) {
|
||||
refresh(false, ratio);
|
||||
}
|
||||
}
|
||||
if (ratio === 1 && !rectsAreEqual(elementRectForRootMargin, element.getBoundingClientRect())) {
|
||||
// It's possible that even though the ratio is reported as 1, the
|
||||
// element is not actually fully within the IntersectionObserver's root
|
||||
// area anymore. This can happen under performance constraints. This may
|
||||
// be a bug in the browser's IntersectionObserver implementation. To
|
||||
// work around this, we compare the element's bounding rect now with
|
||||
// what it was at the time we created the IntersectionObserver. If they
|
||||
// are not equal then the element moved, so we refresh.
|
||||
refresh();
|
||||
}
|
||||
isFirstUpdate = false;
|
||||
}
|
||||
|
||||
@@ -525,7 +587,7 @@ function observeMove(element, onMove) {
|
||||
// Handle <iframe>s
|
||||
root: root.ownerDocument
|
||||
});
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
io = new IntersectionObserver(handleObserve, options);
|
||||
}
|
||||
io.observe(element);
|
||||
@@ -591,7 +653,7 @@ function autoUpdate(reference, floating, update, options) {
|
||||
}
|
||||
function frameLoop() {
|
||||
const nextRefRect = getBoundingClientRect(reference);
|
||||
if (prevRefRect && (nextRefRect.x !== prevRefRect.x || nextRefRect.y !== prevRefRect.y || nextRefRect.width !== prevRefRect.width || nextRefRect.height !== prevRefRect.height)) {
|
||||
if (prevRefRect && !rectsAreEqual(prevRefRect, nextRefRect)) {
|
||||
update();
|
||||
}
|
||||
prevRefRect = nextRefRect;
|
||||
|
||||
Reference in New Issue
Block a user