feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Floating UI contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+4
View File
@@ -0,0 +1,4 @@
# @floating-ui/dom
This is the library to use Floating UI on the web, wrapping `@floating-ui/core`
with DOM interface logic.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,635 @@
import { rectToClientRect, computePosition as computePosition$1 } from '@floating-ui/core';
export { arrow, autoPlacement, detectOverflow, flip, hide, inline, limitShift, offset, shift, size } from '@floating-ui/core';
function getWindow(node) {
var _node$ownerDocument;
return ((_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
}
function getComputedStyle$1(element) {
return getWindow(element).getComputedStyle(element);
}
const min = Math.min;
const max = Math.max;
const round = Math.round;
function getCssDimensions(element) {
const css = getComputedStyle$1(element);
let width = parseFloat(css.width);
let height = parseFloat(css.height);
const offsetWidth = element.offsetWidth;
const offsetHeight = element.offsetHeight;
const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;
if (shouldFallback) {
width = offsetWidth;
height = offsetHeight;
}
return {
width,
height,
fallback: shouldFallback
};
}
function getNodeName(node) {
return isNode(node) ? (node.nodeName || '').toLowerCase() : '';
}
let uaString;
function getUAString() {
if (uaString) {
return uaString;
}
const uaData = navigator.userAgentData;
if (uaData && Array.isArray(uaData.brands)) {
uaString = uaData.brands.map(item => item.brand + "/" + item.version).join(' ');
return uaString;
}
return navigator.userAgent;
}
function isHTMLElement(value) {
return value instanceof getWindow(value).HTMLElement;
}
function isElement(value) {
return value instanceof getWindow(value).Element;
}
function isNode(value) {
return value instanceof getWindow(value).Node;
}
function isShadowRoot(node) {
// Browsers without `ShadowRoot` support.
if (typeof ShadowRoot === 'undefined') {
return false;
}
const OwnElement = getWindow(node).ShadowRoot;
return node instanceof OwnElement || node instanceof ShadowRoot;
}
function isOverflowElement(element) {
const {
overflow,
overflowX,
overflowY,
display
} = getComputedStyle$1(element);
return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !['inline', 'contents'].includes(display);
}
function isTableElement(element) {
return ['table', 'td', 'th'].includes(getNodeName(element));
}
function isContainingBlock(element) {
// TODO: Try to use feature detection here instead.
const isFirefox = /firefox/i.test(getUAString());
const css = getComputedStyle$1(element);
const backdropFilter = css.backdropFilter || css.WebkitBackdropFilter;
// 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
return css.transform !== 'none' || css.perspective !== 'none' || (backdropFilter ? backdropFilter !== 'none' : false) || isFirefox && css.willChange === 'filter' || isFirefox && (css.filter ? css.filter !== 'none' : false) || ['transform', 'perspective'].some(value => css.willChange.includes(value)) || ['paint', 'layout', 'strict', 'content'].some(value => {
// Add type check for old browsers.
const contain = css.contain;
return contain != null ? contain.includes(value) : false;
});
}
function isLayoutViewport() {
// TODO: Try to use feature detection here instead. Feature detection for
// this can fail in various ways, making the userAgent check the most:
// reliable:
// • Always-visible scrollbar or not
// • Width of <html>
// Not Safari.
return !/^((?!chrome|android).)*safari/i.test(getUAString());
}
function isLastTraversableNode(node) {
return ['html', 'body', '#document'].includes(getNodeName(node));
}
function unwrapElement(element) {
return !isElement(element) ? element.contextElement : element;
}
const FALLBACK_SCALE = {
x: 1,
y: 1
};
function getScale(element) {
const domElement = unwrapElement(element);
if (!isHTMLElement(domElement)) {
return FALLBACK_SCALE;
}
const rect = domElement.getBoundingClientRect();
const {
width,
height,
fallback
} = getCssDimensions(domElement);
let x = (fallback ? round(rect.width) : rect.width) / width;
let y = (fallback ? round(rect.height) : rect.height) / height;
// 0, NaN, or Infinity should always fallback to 1.
if (!x || !Number.isFinite(x)) {
x = 1;
}
if (!y || !Number.isFinite(y)) {
y = 1;
}
return {
x,
y
};
}
function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {
var _win$visualViewport, _win$visualViewport2;
if (includeScale === void 0) {
includeScale = false;
}
if (isFixedStrategy === void 0) {
isFixedStrategy = false;
}
const clientRect = element.getBoundingClientRect();
const domElement = unwrapElement(element);
let scale = FALLBACK_SCALE;
if (includeScale) {
if (offsetParent) {
if (isElement(offsetParent)) {
scale = getScale(offsetParent);
}
} else {
scale = getScale(element);
}
}
const win = domElement ? getWindow(domElement) : window;
const addVisualOffsets = !isLayoutViewport() && isFixedStrategy;
let x = (clientRect.left + (addVisualOffsets ? ((_win$visualViewport = win.visualViewport) == null ? void 0 : _win$visualViewport.offsetLeft) || 0 : 0)) / scale.x;
let y = (clientRect.top + (addVisualOffsets ? ((_win$visualViewport2 = win.visualViewport) == null ? void 0 : _win$visualViewport2.offsetTop) || 0 : 0)) / scale.y;
let width = clientRect.width / scale.x;
let height = clientRect.height / scale.y;
if (domElement) {
const win = getWindow(domElement);
const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;
let currentIFrame = win.frameElement;
while (currentIFrame && offsetParent && offsetWin !== win) {
const iframeScale = getScale(currentIFrame);
const iframeRect = currentIFrame.getBoundingClientRect();
const css = getComputedStyle(currentIFrame);
iframeRect.x += (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;
iframeRect.y += (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;
x *= iframeScale.x;
y *= iframeScale.y;
width *= iframeScale.x;
height *= iframeScale.y;
x += iframeRect.x;
y += iframeRect.y;
currentIFrame = getWindow(currentIFrame).frameElement;
}
}
return {
width,
height,
top: y,
right: x + width,
bottom: y + height,
left: x,
x,
y
};
}
function getDocumentElement(node) {
return ((isNode(node) ? node.ownerDocument : node.document) || window.document).documentElement;
}
function getNodeScroll(element) {
if (isElement(element)) {
return {
scrollLeft: element.scrollLeft,
scrollTop: element.scrollTop
};
}
return {
scrollLeft: element.pageXOffset,
scrollTop: element.pageYOffset
};
}
function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
let {
rect,
offsetParent,
strategy
} = _ref;
const isOffsetParentAnElement = isHTMLElement(offsetParent);
const documentElement = getDocumentElement(offsetParent);
if (offsetParent === documentElement) {
return rect;
}
let scroll = {
scrollLeft: 0,
scrollTop: 0
};
let scale = {
x: 1,
y: 1
};
const offsets = {
x: 0,
y: 0
};
if (isOffsetParentAnElement || !isOffsetParentAnElement && strategy !== 'fixed') {
if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
scroll = getNodeScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
const offsetRect = getBoundingClientRect(offsetParent);
scale = getScale(offsetParent);
offsets.x = offsetRect.x + offsetParent.clientLeft;
offsets.y = offsetRect.y + offsetParent.clientTop;
}
}
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
};
}
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) {
const html = getDocumentElement(element);
const scroll = getNodeScroll(element);
const body = element.ownerDocument.body;
const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);
const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);
let x = -scroll.scrollLeft + getWindowScrollBarX(element);
const y = -scroll.scrollTop;
if (getComputedStyle$1(body).direction === 'rtl') {
x += max(html.clientWidth, body.clientWidth) - width;
}
return {
width,
height,
x,
y
};
}
function getParentNode(node) {
if (getNodeName(node) === 'html') {
return node;
}
const result =
// Step into the shadow DOM of the parent of a slotted node.
node.assignedSlot ||
// DOM Element detected.
node.parentNode ||
// ShadowRoot detected.
isShadowRoot(node) && node.host ||
// Fallback.
getDocumentElement(node);
return isShadowRoot(result) ? result.host : result;
}
function getNearestOverflowAncestor(node) {
const parentNode = getParentNode(node);
if (isLastTraversableNode(parentNode)) {
// `getParentNode` will never return a `Document` due to the fallback
// check, so it's either the <html> or <body> element.
return parentNode.ownerDocument.body;
}
if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
return parentNode;
}
return getNearestOverflowAncestor(parentNode);
}
function getOverflowAncestors(node, list) {
var _node$ownerDocument;
if (list === void 0) {
list = [];
}
const scrollableAncestor = getNearestOverflowAncestor(node);
const isBody = scrollableAncestor === ((_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.body);
const win = getWindow(scrollableAncestor);
if (isBody) {
return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : []);
}
return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor));
}
function getViewportRect(element, strategy) {
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;
if (visualViewport) {
width = visualViewport.width;
height = visualViewport.height;
const layoutViewport = isLayoutViewport();
if (layoutViewport || !layoutViewport && strategy === 'fixed') {
x = visualViewport.offsetLeft;
y = visualViewport.offsetTop;
}
}
return {
width,
height,
x,
y
};
}
// Returns the inner client rect, subtracting scrollbars if present.
function getInnerBoundingClientRect(element, strategy) {
const clientRect = getBoundingClientRect(element, true, strategy === 'fixed');
const top = clientRect.top + element.clientTop;
const left = clientRect.left + element.clientLeft;
const scale = isHTMLElement(element) ? getScale(element) : {
x: 1,
y: 1
};
const width = element.clientWidth * scale.x;
const height = element.clientHeight * scale.y;
const x = left * scale.x;
const y = top * scale.y;
return {
width,
height,
x,
y
};
}
function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {
if (clippingAncestor === 'viewport') {
return rectToClientRect(getViewportRect(element, strategy));
}
if (isElement(clippingAncestor)) {
return rectToClientRect(getInnerBoundingClientRect(clippingAncestor, strategy));
}
return rectToClientRect(getDocumentRect(getDocumentElement(element)));
}
// A "clipping ancestor" is an `overflow` element with the characteristic of
// clipping (or hiding) child elements. This returns all clipping ancestors
// of the given element up the tree.
function getClippingElementAncestors(element, cache) {
const cachedResult = cache.get(element);
if (cachedResult) {
return cachedResult;
}
let result = getOverflowAncestors(element).filter(el => isElement(el) && getNodeName(el) !== 'body');
let currentContainingBlockComputedStyle = null;
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$1(currentNode);
const containingBlock = isContainingBlock(currentNode);
const shouldDropCurrentNode = elementIsFixed ? !containingBlock && !currentContainingBlockComputedStyle : !containingBlock && computedStyle.position === 'static' && !!currentContainingBlockComputedStyle && ['absolute', 'fixed'].includes(currentContainingBlockComputedStyle.position);
if (shouldDropCurrentNode) {
// Drop non-containing blocks.
result = result.filter(ancestor => ancestor !== currentNode);
} else {
// Record last containing block for next iteration.
currentContainingBlockComputedStyle = computedStyle;
}
currentNode = getParentNode(currentNode);
}
cache.set(element, result);
return result;
}
// Gets the maximum area that the element is visible in due to any number of
// clipping ancestors.
function getClippingRect(_ref) {
let {
element,
boundary,
rootBoundary,
strategy
} = _ref;
const elementClippingAncestors = boundary === 'clippingAncestors' ? getClippingElementAncestors(element, this._c) : [].concat(boundary);
const clippingAncestors = [...elementClippingAncestors, rootBoundary];
const firstClippingAncestor = clippingAncestors[0];
const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => {
const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy);
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;
}, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy));
return {
width: clippingRect.right - clippingRect.left,
height: clippingRect.bottom - clippingRect.top,
x: clippingRect.left,
y: clippingRect.top
};
}
function getDimensions(element) {
if (isHTMLElement(element)) {
return getCssDimensions(element);
}
return element.getBoundingClientRect();
}
function getTrueOffsetParent(element) {
if (!isHTMLElement(element) || getComputedStyle$1(element).position === 'fixed') {
return null;
}
return element.offsetParent;
}
function getContainingBlock(element) {
let currentNode = getParentNode(element);
while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
if (isContainingBlock(currentNode)) {
return currentNode;
} else {
currentNode = getParentNode(currentNode);
}
}
return null;
}
// Gets the closest ancestor positioned element. Handles some edge cases,
// such as table ancestors and cross browser bugs.
function getOffsetParent(element) {
const window = getWindow(element);
let offsetParent = getTrueOffsetParent(element);
while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === 'static') {
offsetParent = getTrueOffsetParent(offsetParent);
}
if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle$1(offsetParent).position === 'static' && !isContainingBlock(offsetParent))) {
return window;
}
return offsetParent || getContainingBlock(element) || window;
}
function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
const isOffsetParentAnElement = isHTMLElement(offsetParent);
const documentElement = getDocumentElement(offsetParent);
const rect = getBoundingClientRect(element, true, strategy === 'fixed', offsetParent);
let scroll = {
scrollLeft: 0,
scrollTop: 0
};
const offsets = {
x: 0,
y: 0
};
if (isOffsetParentAnElement || !isOffsetParentAnElement && strategy !== 'fixed') {
if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
scroll = getNodeScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
const offsetRect = getBoundingClientRect(offsetParent, true);
offsets.x = offsetRect.x + offsetParent.clientLeft;
offsets.y = offsetRect.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
};
}
const platform = {
getClippingRect,
convertOffsetParentRelativeRectToViewportRelativeRect,
isElement,
getDimensions,
getOffsetParent,
getDocumentElement,
getScale,
async getElementRects(_ref) {
let {
reference,
floating,
strategy
} = _ref;
const getOffsetParentFn = this.getOffsetParent || getOffsetParent;
const getDimensionsFn = this.getDimensions;
return {
reference: getRectRelativeToOffsetParent(reference, await getOffsetParentFn(floating), strategy),
floating: {
x: 0,
y: 0,
...(await getDimensionsFn(floating))
}
};
},
getClientRects: element => Array.from(element.getClientRects()),
isRTL: element => getComputedStyle$1(element).direction === 'rtl'
};
/**
* Automatically updates the position of the floating element when necessary.
* @see https://floating-ui.com/docs/autoUpdate
*/
function autoUpdate(reference, floating, update, options) {
if (options === void 0) {
options = {};
}
const {
ancestorScroll: _ancestorScroll = true,
ancestorResize = true,
elementResize = true,
animationFrame = false
} = options;
const ancestorScroll = _ancestorScroll && !animationFrame;
const ancestors = ancestorScroll || ancestorResize ? [...(isElement(reference) ? getOverflowAncestors(reference) : reference.contextElement ? getOverflowAncestors(reference.contextElement) : []), ...getOverflowAncestors(floating)] : [];
ancestors.forEach(ancestor => {
ancestorScroll && ancestor.addEventListener('scroll', update, {
passive: true
});
ancestorResize && ancestor.addEventListener('resize', update);
});
let observer = null;
if (elementResize) {
let initialUpdate = true;
observer = new ResizeObserver(() => {
if (!initialUpdate) {
update();
}
initialUpdate = false;
});
isElement(reference) && !animationFrame && observer.observe(reference);
if (!isElement(reference) && reference.contextElement && !animationFrame) {
observer.observe(reference.contextElement);
}
observer.observe(floating);
}
let frameId;
let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;
if (animationFrame) {
frameLoop();
}
function frameLoop() {
const nextRefRect = getBoundingClientRect(reference);
if (prevRefRect && (nextRefRect.x !== prevRefRect.x || nextRefRect.y !== prevRefRect.y || nextRefRect.width !== prevRefRect.width || nextRefRect.height !== prevRefRect.height)) {
update();
}
prevRefRect = nextRefRect;
frameId = requestAnimationFrame(frameLoop);
}
update();
return () => {
var _observer;
ancestors.forEach(ancestor => {
ancestorScroll && ancestor.removeEventListener('scroll', update);
ancestorResize && ancestor.removeEventListener('resize', update);
});
(_observer = observer) == null ? void 0 : _observer.disconnect();
observer = null;
if (animationFrame) {
cancelAnimationFrame(frameId);
}
};
}
/**
* Computes the `x` and `y` coordinates that will place the floating element
* next to a reference element when it is given a certain CSS positioning
* strategy.
*/
const computePosition = (reference, floating, options) => {
// This caches the expensive `getClippingElementAncestors` function so that
// multiple lifecycle resets re-use the same result. It only lives for a
// single call. If other functions become expensive, we can add them as well.
const cache = new Map();
const mergedOptions = {
platform,
...options
};
const platformWithCache = {
...mergedOptions.platform,
_c: cache
};
return computePosition$1(reference, floating, {
...mergedOptions,
platform: platformWithCache
});
};
export { autoUpdate, computePosition, getOverflowAncestors, platform };
+635
View File
@@ -0,0 +1,635 @@
import { rectToClientRect, computePosition as computePosition$1 } from '@floating-ui/core';
export { arrow, autoPlacement, detectOverflow, flip, hide, inline, limitShift, offset, shift, size } from '@floating-ui/core';
function getWindow(node) {
var _node$ownerDocument;
return ((_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
}
function getComputedStyle$1(element) {
return getWindow(element).getComputedStyle(element);
}
const min = Math.min;
const max = Math.max;
const round = Math.round;
function getCssDimensions(element) {
const css = getComputedStyle$1(element);
let width = parseFloat(css.width);
let height = parseFloat(css.height);
const offsetWidth = element.offsetWidth;
const offsetHeight = element.offsetHeight;
const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;
if (shouldFallback) {
width = offsetWidth;
height = offsetHeight;
}
return {
width,
height,
fallback: shouldFallback
};
}
function getNodeName(node) {
return isNode(node) ? (node.nodeName || '').toLowerCase() : '';
}
let uaString;
function getUAString() {
if (uaString) {
return uaString;
}
const uaData = navigator.userAgentData;
if (uaData && Array.isArray(uaData.brands)) {
uaString = uaData.brands.map(item => item.brand + "/" + item.version).join(' ');
return uaString;
}
return navigator.userAgent;
}
function isHTMLElement(value) {
return value instanceof getWindow(value).HTMLElement;
}
function isElement(value) {
return value instanceof getWindow(value).Element;
}
function isNode(value) {
return value instanceof getWindow(value).Node;
}
function isShadowRoot(node) {
// Browsers without `ShadowRoot` support.
if (typeof ShadowRoot === 'undefined') {
return false;
}
const OwnElement = getWindow(node).ShadowRoot;
return node instanceof OwnElement || node instanceof ShadowRoot;
}
function isOverflowElement(element) {
const {
overflow,
overflowX,
overflowY,
display
} = getComputedStyle$1(element);
return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !['inline', 'contents'].includes(display);
}
function isTableElement(element) {
return ['table', 'td', 'th'].includes(getNodeName(element));
}
function isContainingBlock(element) {
// TODO: Try to use feature detection here instead.
const isFirefox = /firefox/i.test(getUAString());
const css = getComputedStyle$1(element);
const backdropFilter = css.backdropFilter || css.WebkitBackdropFilter;
// 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
return css.transform !== 'none' || css.perspective !== 'none' || (backdropFilter ? backdropFilter !== 'none' : false) || isFirefox && css.willChange === 'filter' || isFirefox && (css.filter ? css.filter !== 'none' : false) || ['transform', 'perspective'].some(value => css.willChange.includes(value)) || ['paint', 'layout', 'strict', 'content'].some(value => {
// Add type check for old browsers.
const contain = css.contain;
return contain != null ? contain.includes(value) : false;
});
}
function isLayoutViewport() {
// TODO: Try to use feature detection here instead. Feature detection for
// this can fail in various ways, making the userAgent check the most:
// reliable:
// • Always-visible scrollbar or not
// • Width of <html>
// Not Safari.
return !/^((?!chrome|android).)*safari/i.test(getUAString());
}
function isLastTraversableNode(node) {
return ['html', 'body', '#document'].includes(getNodeName(node));
}
function unwrapElement(element) {
return !isElement(element) ? element.contextElement : element;
}
const FALLBACK_SCALE = {
x: 1,
y: 1
};
function getScale(element) {
const domElement = unwrapElement(element);
if (!isHTMLElement(domElement)) {
return FALLBACK_SCALE;
}
const rect = domElement.getBoundingClientRect();
const {
width,
height,
fallback
} = getCssDimensions(domElement);
let x = (fallback ? round(rect.width) : rect.width) / width;
let y = (fallback ? round(rect.height) : rect.height) / height;
// 0, NaN, or Infinity should always fallback to 1.
if (!x || !Number.isFinite(x)) {
x = 1;
}
if (!y || !Number.isFinite(y)) {
y = 1;
}
return {
x,
y
};
}
function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {
var _win$visualViewport, _win$visualViewport2;
if (includeScale === void 0) {
includeScale = false;
}
if (isFixedStrategy === void 0) {
isFixedStrategy = false;
}
const clientRect = element.getBoundingClientRect();
const domElement = unwrapElement(element);
let scale = FALLBACK_SCALE;
if (includeScale) {
if (offsetParent) {
if (isElement(offsetParent)) {
scale = getScale(offsetParent);
}
} else {
scale = getScale(element);
}
}
const win = domElement ? getWindow(domElement) : window;
const addVisualOffsets = !isLayoutViewport() && isFixedStrategy;
let x = (clientRect.left + (addVisualOffsets ? ((_win$visualViewport = win.visualViewport) == null ? void 0 : _win$visualViewport.offsetLeft) || 0 : 0)) / scale.x;
let y = (clientRect.top + (addVisualOffsets ? ((_win$visualViewport2 = win.visualViewport) == null ? void 0 : _win$visualViewport2.offsetTop) || 0 : 0)) / scale.y;
let width = clientRect.width / scale.x;
let height = clientRect.height / scale.y;
if (domElement) {
const win = getWindow(domElement);
const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;
let currentIFrame = win.frameElement;
while (currentIFrame && offsetParent && offsetWin !== win) {
const iframeScale = getScale(currentIFrame);
const iframeRect = currentIFrame.getBoundingClientRect();
const css = getComputedStyle(currentIFrame);
iframeRect.x += (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;
iframeRect.y += (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;
x *= iframeScale.x;
y *= iframeScale.y;
width *= iframeScale.x;
height *= iframeScale.y;
x += iframeRect.x;
y += iframeRect.y;
currentIFrame = getWindow(currentIFrame).frameElement;
}
}
return {
width,
height,
top: y,
right: x + width,
bottom: y + height,
left: x,
x,
y
};
}
function getDocumentElement(node) {
return ((isNode(node) ? node.ownerDocument : node.document) || window.document).documentElement;
}
function getNodeScroll(element) {
if (isElement(element)) {
return {
scrollLeft: element.scrollLeft,
scrollTop: element.scrollTop
};
}
return {
scrollLeft: element.pageXOffset,
scrollTop: element.pageYOffset
};
}
function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
let {
rect,
offsetParent,
strategy
} = _ref;
const isOffsetParentAnElement = isHTMLElement(offsetParent);
const documentElement = getDocumentElement(offsetParent);
if (offsetParent === documentElement) {
return rect;
}
let scroll = {
scrollLeft: 0,
scrollTop: 0
};
let scale = {
x: 1,
y: 1
};
const offsets = {
x: 0,
y: 0
};
if (isOffsetParentAnElement || !isOffsetParentAnElement && strategy !== 'fixed') {
if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
scroll = getNodeScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
const offsetRect = getBoundingClientRect(offsetParent);
scale = getScale(offsetParent);
offsets.x = offsetRect.x + offsetParent.clientLeft;
offsets.y = offsetRect.y + offsetParent.clientTop;
}
}
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
};
}
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) {
const html = getDocumentElement(element);
const scroll = getNodeScroll(element);
const body = element.ownerDocument.body;
const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);
const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);
let x = -scroll.scrollLeft + getWindowScrollBarX(element);
const y = -scroll.scrollTop;
if (getComputedStyle$1(body).direction === 'rtl') {
x += max(html.clientWidth, body.clientWidth) - width;
}
return {
width,
height,
x,
y
};
}
function getParentNode(node) {
if (getNodeName(node) === 'html') {
return node;
}
const result =
// Step into the shadow DOM of the parent of a slotted node.
node.assignedSlot ||
// DOM Element detected.
node.parentNode ||
// ShadowRoot detected.
isShadowRoot(node) && node.host ||
// Fallback.
getDocumentElement(node);
return isShadowRoot(result) ? result.host : result;
}
function getNearestOverflowAncestor(node) {
const parentNode = getParentNode(node);
if (isLastTraversableNode(parentNode)) {
// `getParentNode` will never return a `Document` due to the fallback
// check, so it's either the <html> or <body> element.
return parentNode.ownerDocument.body;
}
if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
return parentNode;
}
return getNearestOverflowAncestor(parentNode);
}
function getOverflowAncestors(node, list) {
var _node$ownerDocument;
if (list === void 0) {
list = [];
}
const scrollableAncestor = getNearestOverflowAncestor(node);
const isBody = scrollableAncestor === ((_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.body);
const win = getWindow(scrollableAncestor);
if (isBody) {
return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : []);
}
return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor));
}
function getViewportRect(element, strategy) {
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;
if (visualViewport) {
width = visualViewport.width;
height = visualViewport.height;
const layoutViewport = isLayoutViewport();
if (layoutViewport || !layoutViewport && strategy === 'fixed') {
x = visualViewport.offsetLeft;
y = visualViewport.offsetTop;
}
}
return {
width,
height,
x,
y
};
}
// Returns the inner client rect, subtracting scrollbars if present.
function getInnerBoundingClientRect(element, strategy) {
const clientRect = getBoundingClientRect(element, true, strategy === 'fixed');
const top = clientRect.top + element.clientTop;
const left = clientRect.left + element.clientLeft;
const scale = isHTMLElement(element) ? getScale(element) : {
x: 1,
y: 1
};
const width = element.clientWidth * scale.x;
const height = element.clientHeight * scale.y;
const x = left * scale.x;
const y = top * scale.y;
return {
width,
height,
x,
y
};
}
function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {
if (clippingAncestor === 'viewport') {
return rectToClientRect(getViewportRect(element, strategy));
}
if (isElement(clippingAncestor)) {
return rectToClientRect(getInnerBoundingClientRect(clippingAncestor, strategy));
}
return rectToClientRect(getDocumentRect(getDocumentElement(element)));
}
// A "clipping ancestor" is an `overflow` element with the characteristic of
// clipping (or hiding) child elements. This returns all clipping ancestors
// of the given element up the tree.
function getClippingElementAncestors(element, cache) {
const cachedResult = cache.get(element);
if (cachedResult) {
return cachedResult;
}
let result = getOverflowAncestors(element).filter(el => isElement(el) && getNodeName(el) !== 'body');
let currentContainingBlockComputedStyle = null;
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$1(currentNode);
const containingBlock = isContainingBlock(currentNode);
const shouldDropCurrentNode = elementIsFixed ? !containingBlock && !currentContainingBlockComputedStyle : !containingBlock && computedStyle.position === 'static' && !!currentContainingBlockComputedStyle && ['absolute', 'fixed'].includes(currentContainingBlockComputedStyle.position);
if (shouldDropCurrentNode) {
// Drop non-containing blocks.
result = result.filter(ancestor => ancestor !== currentNode);
} else {
// Record last containing block for next iteration.
currentContainingBlockComputedStyle = computedStyle;
}
currentNode = getParentNode(currentNode);
}
cache.set(element, result);
return result;
}
// Gets the maximum area that the element is visible in due to any number of
// clipping ancestors.
function getClippingRect(_ref) {
let {
element,
boundary,
rootBoundary,
strategy
} = _ref;
const elementClippingAncestors = boundary === 'clippingAncestors' ? getClippingElementAncestors(element, this._c) : [].concat(boundary);
const clippingAncestors = [...elementClippingAncestors, rootBoundary];
const firstClippingAncestor = clippingAncestors[0];
const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => {
const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy);
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;
}, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy));
return {
width: clippingRect.right - clippingRect.left,
height: clippingRect.bottom - clippingRect.top,
x: clippingRect.left,
y: clippingRect.top
};
}
function getDimensions(element) {
if (isHTMLElement(element)) {
return getCssDimensions(element);
}
return element.getBoundingClientRect();
}
function getTrueOffsetParent(element) {
if (!isHTMLElement(element) || getComputedStyle$1(element).position === 'fixed') {
return null;
}
return element.offsetParent;
}
function getContainingBlock(element) {
let currentNode = getParentNode(element);
while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
if (isContainingBlock(currentNode)) {
return currentNode;
} else {
currentNode = getParentNode(currentNode);
}
}
return null;
}
// Gets the closest ancestor positioned element. Handles some edge cases,
// such as table ancestors and cross browser bugs.
function getOffsetParent(element) {
const window = getWindow(element);
let offsetParent = getTrueOffsetParent(element);
while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === 'static') {
offsetParent = getTrueOffsetParent(offsetParent);
}
if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle$1(offsetParent).position === 'static' && !isContainingBlock(offsetParent))) {
return window;
}
return offsetParent || getContainingBlock(element) || window;
}
function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
const isOffsetParentAnElement = isHTMLElement(offsetParent);
const documentElement = getDocumentElement(offsetParent);
const rect = getBoundingClientRect(element, true, strategy === 'fixed', offsetParent);
let scroll = {
scrollLeft: 0,
scrollTop: 0
};
const offsets = {
x: 0,
y: 0
};
if (isOffsetParentAnElement || !isOffsetParentAnElement && strategy !== 'fixed') {
if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
scroll = getNodeScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
const offsetRect = getBoundingClientRect(offsetParent, true);
offsets.x = offsetRect.x + offsetParent.clientLeft;
offsets.y = offsetRect.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
};
}
const platform = {
getClippingRect,
convertOffsetParentRelativeRectToViewportRelativeRect,
isElement,
getDimensions,
getOffsetParent,
getDocumentElement,
getScale,
async getElementRects(_ref) {
let {
reference,
floating,
strategy
} = _ref;
const getOffsetParentFn = this.getOffsetParent || getOffsetParent;
const getDimensionsFn = this.getDimensions;
return {
reference: getRectRelativeToOffsetParent(reference, await getOffsetParentFn(floating), strategy),
floating: {
x: 0,
y: 0,
...(await getDimensionsFn(floating))
}
};
},
getClientRects: element => Array.from(element.getClientRects()),
isRTL: element => getComputedStyle$1(element).direction === 'rtl'
};
/**
* Automatically updates the position of the floating element when necessary.
* @see https://floating-ui.com/docs/autoUpdate
*/
function autoUpdate(reference, floating, update, options) {
if (options === void 0) {
options = {};
}
const {
ancestorScroll: _ancestorScroll = true,
ancestorResize = true,
elementResize = true,
animationFrame = false
} = options;
const ancestorScroll = _ancestorScroll && !animationFrame;
const ancestors = ancestorScroll || ancestorResize ? [...(isElement(reference) ? getOverflowAncestors(reference) : reference.contextElement ? getOverflowAncestors(reference.contextElement) : []), ...getOverflowAncestors(floating)] : [];
ancestors.forEach(ancestor => {
ancestorScroll && ancestor.addEventListener('scroll', update, {
passive: true
});
ancestorResize && ancestor.addEventListener('resize', update);
});
let observer = null;
if (elementResize) {
let initialUpdate = true;
observer = new ResizeObserver(() => {
if (!initialUpdate) {
update();
}
initialUpdate = false;
});
isElement(reference) && !animationFrame && observer.observe(reference);
if (!isElement(reference) && reference.contextElement && !animationFrame) {
observer.observe(reference.contextElement);
}
observer.observe(floating);
}
let frameId;
let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;
if (animationFrame) {
frameLoop();
}
function frameLoop() {
const nextRefRect = getBoundingClientRect(reference);
if (prevRefRect && (nextRefRect.x !== prevRefRect.x || nextRefRect.y !== prevRefRect.y || nextRefRect.width !== prevRefRect.width || nextRefRect.height !== prevRefRect.height)) {
update();
}
prevRefRect = nextRefRect;
frameId = requestAnimationFrame(frameLoop);
}
update();
return () => {
var _observer;
ancestors.forEach(ancestor => {
ancestorScroll && ancestor.removeEventListener('scroll', update);
ancestorResize && ancestor.removeEventListener('resize', update);
});
(_observer = observer) == null ? void 0 : _observer.disconnect();
observer = null;
if (animationFrame) {
cancelAnimationFrame(frameId);
}
};
}
/**
* Computes the `x` and `y` coordinates that will place the floating element
* next to a reference element when it is given a certain CSS positioning
* strategy.
*/
const computePosition = (reference, floating, options) => {
// This caches the expensive `getClippingElementAncestors` function so that
// multiple lifecycle resets re-use the same result. It only lives for a
// single call. If other functions become expensive, we can add them as well.
const cache = new Map();
const mergedOptions = {
platform,
...options
};
const platformWithCache = {
...mergedOptions.platform,
_c: cache
};
return computePosition$1(reference, floating, {
...mergedOptions,
platform: platformWithCache
});
};
export { autoUpdate, computePosition, getOverflowAncestors, platform };
+635
View File
@@ -0,0 +1,635 @@
import { rectToClientRect, computePosition as computePosition$1 } from '@floating-ui/core';
export { arrow, autoPlacement, detectOverflow, flip, hide, inline, limitShift, offset, shift, size } from '@floating-ui/core';
function getWindow(node) {
var _node$ownerDocument;
return ((_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
}
function getComputedStyle$1(element) {
return getWindow(element).getComputedStyle(element);
}
const min = Math.min;
const max = Math.max;
const round = Math.round;
function getCssDimensions(element) {
const css = getComputedStyle$1(element);
let width = parseFloat(css.width);
let height = parseFloat(css.height);
const offsetWidth = element.offsetWidth;
const offsetHeight = element.offsetHeight;
const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;
if (shouldFallback) {
width = offsetWidth;
height = offsetHeight;
}
return {
width,
height,
fallback: shouldFallback
};
}
function getNodeName(node) {
return isNode(node) ? (node.nodeName || '').toLowerCase() : '';
}
let uaString;
function getUAString() {
if (uaString) {
return uaString;
}
const uaData = navigator.userAgentData;
if (uaData && Array.isArray(uaData.brands)) {
uaString = uaData.brands.map(item => item.brand + "/" + item.version).join(' ');
return uaString;
}
return navigator.userAgent;
}
function isHTMLElement(value) {
return value instanceof getWindow(value).HTMLElement;
}
function isElement(value) {
return value instanceof getWindow(value).Element;
}
function isNode(value) {
return value instanceof getWindow(value).Node;
}
function isShadowRoot(node) {
// Browsers without `ShadowRoot` support.
if (typeof ShadowRoot === 'undefined') {
return false;
}
const OwnElement = getWindow(node).ShadowRoot;
return node instanceof OwnElement || node instanceof ShadowRoot;
}
function isOverflowElement(element) {
const {
overflow,
overflowX,
overflowY,
display
} = getComputedStyle$1(element);
return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !['inline', 'contents'].includes(display);
}
function isTableElement(element) {
return ['table', 'td', 'th'].includes(getNodeName(element));
}
function isContainingBlock(element) {
// TODO: Try to use feature detection here instead.
const isFirefox = /firefox/i.test(getUAString());
const css = getComputedStyle$1(element);
const backdropFilter = css.backdropFilter || css.WebkitBackdropFilter;
// 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
return css.transform !== 'none' || css.perspective !== 'none' || (backdropFilter ? backdropFilter !== 'none' : false) || isFirefox && css.willChange === 'filter' || isFirefox && (css.filter ? css.filter !== 'none' : false) || ['transform', 'perspective'].some(value => css.willChange.includes(value)) || ['paint', 'layout', 'strict', 'content'].some(value => {
// Add type check for old browsers.
const contain = css.contain;
return contain != null ? contain.includes(value) : false;
});
}
function isLayoutViewport() {
// TODO: Try to use feature detection here instead. Feature detection for
// this can fail in various ways, making the userAgent check the most:
// reliable:
// • Always-visible scrollbar or not
// • Width of <html>
// Not Safari.
return !/^((?!chrome|android).)*safari/i.test(getUAString());
}
function isLastTraversableNode(node) {
return ['html', 'body', '#document'].includes(getNodeName(node));
}
function unwrapElement(element) {
return !isElement(element) ? element.contextElement : element;
}
const FALLBACK_SCALE = {
x: 1,
y: 1
};
function getScale(element) {
const domElement = unwrapElement(element);
if (!isHTMLElement(domElement)) {
return FALLBACK_SCALE;
}
const rect = domElement.getBoundingClientRect();
const {
width,
height,
fallback
} = getCssDimensions(domElement);
let x = (fallback ? round(rect.width) : rect.width) / width;
let y = (fallback ? round(rect.height) : rect.height) / height;
// 0, NaN, or Infinity should always fallback to 1.
if (!x || !Number.isFinite(x)) {
x = 1;
}
if (!y || !Number.isFinite(y)) {
y = 1;
}
return {
x,
y
};
}
function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {
var _win$visualViewport, _win$visualViewport2;
if (includeScale === void 0) {
includeScale = false;
}
if (isFixedStrategy === void 0) {
isFixedStrategy = false;
}
const clientRect = element.getBoundingClientRect();
const domElement = unwrapElement(element);
let scale = FALLBACK_SCALE;
if (includeScale) {
if (offsetParent) {
if (isElement(offsetParent)) {
scale = getScale(offsetParent);
}
} else {
scale = getScale(element);
}
}
const win = domElement ? getWindow(domElement) : window;
const addVisualOffsets = !isLayoutViewport() && isFixedStrategy;
let x = (clientRect.left + (addVisualOffsets ? ((_win$visualViewport = win.visualViewport) == null ? void 0 : _win$visualViewport.offsetLeft) || 0 : 0)) / scale.x;
let y = (clientRect.top + (addVisualOffsets ? ((_win$visualViewport2 = win.visualViewport) == null ? void 0 : _win$visualViewport2.offsetTop) || 0 : 0)) / scale.y;
let width = clientRect.width / scale.x;
let height = clientRect.height / scale.y;
if (domElement) {
const win = getWindow(domElement);
const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;
let currentIFrame = win.frameElement;
while (currentIFrame && offsetParent && offsetWin !== win) {
const iframeScale = getScale(currentIFrame);
const iframeRect = currentIFrame.getBoundingClientRect();
const css = getComputedStyle(currentIFrame);
iframeRect.x += (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;
iframeRect.y += (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;
x *= iframeScale.x;
y *= iframeScale.y;
width *= iframeScale.x;
height *= iframeScale.y;
x += iframeRect.x;
y += iframeRect.y;
currentIFrame = getWindow(currentIFrame).frameElement;
}
}
return {
width,
height,
top: y,
right: x + width,
bottom: y + height,
left: x,
x,
y
};
}
function getDocumentElement(node) {
return ((isNode(node) ? node.ownerDocument : node.document) || window.document).documentElement;
}
function getNodeScroll(element) {
if (isElement(element)) {
return {
scrollLeft: element.scrollLeft,
scrollTop: element.scrollTop
};
}
return {
scrollLeft: element.pageXOffset,
scrollTop: element.pageYOffset
};
}
function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
let {
rect,
offsetParent,
strategy
} = _ref;
const isOffsetParentAnElement = isHTMLElement(offsetParent);
const documentElement = getDocumentElement(offsetParent);
if (offsetParent === documentElement) {
return rect;
}
let scroll = {
scrollLeft: 0,
scrollTop: 0
};
let scale = {
x: 1,
y: 1
};
const offsets = {
x: 0,
y: 0
};
if (isOffsetParentAnElement || !isOffsetParentAnElement && strategy !== 'fixed') {
if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
scroll = getNodeScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
const offsetRect = getBoundingClientRect(offsetParent);
scale = getScale(offsetParent);
offsets.x = offsetRect.x + offsetParent.clientLeft;
offsets.y = offsetRect.y + offsetParent.clientTop;
}
}
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
};
}
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) {
const html = getDocumentElement(element);
const scroll = getNodeScroll(element);
const body = element.ownerDocument.body;
const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);
const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);
let x = -scroll.scrollLeft + getWindowScrollBarX(element);
const y = -scroll.scrollTop;
if (getComputedStyle$1(body).direction === 'rtl') {
x += max(html.clientWidth, body.clientWidth) - width;
}
return {
width,
height,
x,
y
};
}
function getParentNode(node) {
if (getNodeName(node) === 'html') {
return node;
}
const result =
// Step into the shadow DOM of the parent of a slotted node.
node.assignedSlot ||
// DOM Element detected.
node.parentNode ||
// ShadowRoot detected.
isShadowRoot(node) && node.host ||
// Fallback.
getDocumentElement(node);
return isShadowRoot(result) ? result.host : result;
}
function getNearestOverflowAncestor(node) {
const parentNode = getParentNode(node);
if (isLastTraversableNode(parentNode)) {
// `getParentNode` will never return a `Document` due to the fallback
// check, so it's either the <html> or <body> element.
return parentNode.ownerDocument.body;
}
if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
return parentNode;
}
return getNearestOverflowAncestor(parentNode);
}
function getOverflowAncestors(node, list) {
var _node$ownerDocument;
if (list === void 0) {
list = [];
}
const scrollableAncestor = getNearestOverflowAncestor(node);
const isBody = scrollableAncestor === ((_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.body);
const win = getWindow(scrollableAncestor);
if (isBody) {
return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : []);
}
return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor));
}
function getViewportRect(element, strategy) {
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;
if (visualViewport) {
width = visualViewport.width;
height = visualViewport.height;
const layoutViewport = isLayoutViewport();
if (layoutViewport || !layoutViewport && strategy === 'fixed') {
x = visualViewport.offsetLeft;
y = visualViewport.offsetTop;
}
}
return {
width,
height,
x,
y
};
}
// Returns the inner client rect, subtracting scrollbars if present.
function getInnerBoundingClientRect(element, strategy) {
const clientRect = getBoundingClientRect(element, true, strategy === 'fixed');
const top = clientRect.top + element.clientTop;
const left = clientRect.left + element.clientLeft;
const scale = isHTMLElement(element) ? getScale(element) : {
x: 1,
y: 1
};
const width = element.clientWidth * scale.x;
const height = element.clientHeight * scale.y;
const x = left * scale.x;
const y = top * scale.y;
return {
width,
height,
x,
y
};
}
function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {
if (clippingAncestor === 'viewport') {
return rectToClientRect(getViewportRect(element, strategy));
}
if (isElement(clippingAncestor)) {
return rectToClientRect(getInnerBoundingClientRect(clippingAncestor, strategy));
}
return rectToClientRect(getDocumentRect(getDocumentElement(element)));
}
// A "clipping ancestor" is an `overflow` element with the characteristic of
// clipping (or hiding) child elements. This returns all clipping ancestors
// of the given element up the tree.
function getClippingElementAncestors(element, cache) {
const cachedResult = cache.get(element);
if (cachedResult) {
return cachedResult;
}
let result = getOverflowAncestors(element).filter(el => isElement(el) && getNodeName(el) !== 'body');
let currentContainingBlockComputedStyle = null;
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$1(currentNode);
const containingBlock = isContainingBlock(currentNode);
const shouldDropCurrentNode = elementIsFixed ? !containingBlock && !currentContainingBlockComputedStyle : !containingBlock && computedStyle.position === 'static' && !!currentContainingBlockComputedStyle && ['absolute', 'fixed'].includes(currentContainingBlockComputedStyle.position);
if (shouldDropCurrentNode) {
// Drop non-containing blocks.
result = result.filter(ancestor => ancestor !== currentNode);
} else {
// Record last containing block for next iteration.
currentContainingBlockComputedStyle = computedStyle;
}
currentNode = getParentNode(currentNode);
}
cache.set(element, result);
return result;
}
// Gets the maximum area that the element is visible in due to any number of
// clipping ancestors.
function getClippingRect(_ref) {
let {
element,
boundary,
rootBoundary,
strategy
} = _ref;
const elementClippingAncestors = boundary === 'clippingAncestors' ? getClippingElementAncestors(element, this._c) : [].concat(boundary);
const clippingAncestors = [...elementClippingAncestors, rootBoundary];
const firstClippingAncestor = clippingAncestors[0];
const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => {
const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy);
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;
}, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy));
return {
width: clippingRect.right - clippingRect.left,
height: clippingRect.bottom - clippingRect.top,
x: clippingRect.left,
y: clippingRect.top
};
}
function getDimensions(element) {
if (isHTMLElement(element)) {
return getCssDimensions(element);
}
return element.getBoundingClientRect();
}
function getTrueOffsetParent(element) {
if (!isHTMLElement(element) || getComputedStyle$1(element).position === 'fixed') {
return null;
}
return element.offsetParent;
}
function getContainingBlock(element) {
let currentNode = getParentNode(element);
while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
if (isContainingBlock(currentNode)) {
return currentNode;
} else {
currentNode = getParentNode(currentNode);
}
}
return null;
}
// Gets the closest ancestor positioned element. Handles some edge cases,
// such as table ancestors and cross browser bugs.
function getOffsetParent(element) {
const window = getWindow(element);
let offsetParent = getTrueOffsetParent(element);
while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === 'static') {
offsetParent = getTrueOffsetParent(offsetParent);
}
if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle$1(offsetParent).position === 'static' && !isContainingBlock(offsetParent))) {
return window;
}
return offsetParent || getContainingBlock(element) || window;
}
function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
const isOffsetParentAnElement = isHTMLElement(offsetParent);
const documentElement = getDocumentElement(offsetParent);
const rect = getBoundingClientRect(element, true, strategy === 'fixed', offsetParent);
let scroll = {
scrollLeft: 0,
scrollTop: 0
};
const offsets = {
x: 0,
y: 0
};
if (isOffsetParentAnElement || !isOffsetParentAnElement && strategy !== 'fixed') {
if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
scroll = getNodeScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
const offsetRect = getBoundingClientRect(offsetParent, true);
offsets.x = offsetRect.x + offsetParent.clientLeft;
offsets.y = offsetRect.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
};
}
const platform = {
getClippingRect,
convertOffsetParentRelativeRectToViewportRelativeRect,
isElement,
getDimensions,
getOffsetParent,
getDocumentElement,
getScale,
async getElementRects(_ref) {
let {
reference,
floating,
strategy
} = _ref;
const getOffsetParentFn = this.getOffsetParent || getOffsetParent;
const getDimensionsFn = this.getDimensions;
return {
reference: getRectRelativeToOffsetParent(reference, await getOffsetParentFn(floating), strategy),
floating: {
x: 0,
y: 0,
...(await getDimensionsFn(floating))
}
};
},
getClientRects: element => Array.from(element.getClientRects()),
isRTL: element => getComputedStyle$1(element).direction === 'rtl'
};
/**
* Automatically updates the position of the floating element when necessary.
* @see https://floating-ui.com/docs/autoUpdate
*/
function autoUpdate(reference, floating, update, options) {
if (options === void 0) {
options = {};
}
const {
ancestorScroll: _ancestorScroll = true,
ancestorResize = true,
elementResize = true,
animationFrame = false
} = options;
const ancestorScroll = _ancestorScroll && !animationFrame;
const ancestors = ancestorScroll || ancestorResize ? [...(isElement(reference) ? getOverflowAncestors(reference) : reference.contextElement ? getOverflowAncestors(reference.contextElement) : []), ...getOverflowAncestors(floating)] : [];
ancestors.forEach(ancestor => {
ancestorScroll && ancestor.addEventListener('scroll', update, {
passive: true
});
ancestorResize && ancestor.addEventListener('resize', update);
});
let observer = null;
if (elementResize) {
let initialUpdate = true;
observer = new ResizeObserver(() => {
if (!initialUpdate) {
update();
}
initialUpdate = false;
});
isElement(reference) && !animationFrame && observer.observe(reference);
if (!isElement(reference) && reference.contextElement && !animationFrame) {
observer.observe(reference.contextElement);
}
observer.observe(floating);
}
let frameId;
let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;
if (animationFrame) {
frameLoop();
}
function frameLoop() {
const nextRefRect = getBoundingClientRect(reference);
if (prevRefRect && (nextRefRect.x !== prevRefRect.x || nextRefRect.y !== prevRefRect.y || nextRefRect.width !== prevRefRect.width || nextRefRect.height !== prevRefRect.height)) {
update();
}
prevRefRect = nextRefRect;
frameId = requestAnimationFrame(frameLoop);
}
update();
return () => {
var _observer;
ancestors.forEach(ancestor => {
ancestorScroll && ancestor.removeEventListener('scroll', update);
ancestorResize && ancestor.removeEventListener('resize', update);
});
(_observer = observer) == null ? void 0 : _observer.disconnect();
observer = null;
if (animationFrame) {
cancelAnimationFrame(frameId);
}
};
}
/**
* Computes the `x` and `y` coordinates that will place the floating element
* next to a reference element when it is given a certain CSS positioning
* strategy.
*/
const computePosition = (reference, floating, options) => {
// This caches the expensive `getClippingElementAncestors` function so that
// multiple lifecycle resets re-use the same result. It only lives for a
// single call. If other functions become expensive, we can add them as well.
const cache = new Map();
const mergedOptions = {
platform,
...options
};
const platformWithCache = {
...mergedOptions.platform,
_c: cache
};
return computePosition$1(reference, floating, {
...mergedOptions,
platform: platformWithCache
});
};
export { autoUpdate, computePosition, getOverflowAncestors, platform };
+685
View File
@@ -0,0 +1,685 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@floating-ui/core')) :
typeof define === 'function' && define.amd ? define(['exports', '@floating-ui/core'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.FloatingUIDOM = {}, global.FloatingUICore));
})(this, (function (exports, core) { 'use strict';
function getWindow(node) {
var _node$ownerDocument;
return ((_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
}
function getComputedStyle$1(element) {
return getWindow(element).getComputedStyle(element);
}
const min = Math.min;
const max = Math.max;
const round = Math.round;
function getCssDimensions(element) {
const css = getComputedStyle$1(element);
let width = parseFloat(css.width);
let height = parseFloat(css.height);
const offsetWidth = element.offsetWidth;
const offsetHeight = element.offsetHeight;
const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;
if (shouldFallback) {
width = offsetWidth;
height = offsetHeight;
}
return {
width,
height,
fallback: shouldFallback
};
}
function getNodeName(node) {
return isNode(node) ? (node.nodeName || '').toLowerCase() : '';
}
let uaString;
function getUAString() {
if (uaString) {
return uaString;
}
const uaData = navigator.userAgentData;
if (uaData && Array.isArray(uaData.brands)) {
uaString = uaData.brands.map(item => item.brand + "/" + item.version).join(' ');
return uaString;
}
return navigator.userAgent;
}
function isHTMLElement(value) {
return value instanceof getWindow(value).HTMLElement;
}
function isElement(value) {
return value instanceof getWindow(value).Element;
}
function isNode(value) {
return value instanceof getWindow(value).Node;
}
function isShadowRoot(node) {
// Browsers without `ShadowRoot` support.
if (typeof ShadowRoot === 'undefined') {
return false;
}
const OwnElement = getWindow(node).ShadowRoot;
return node instanceof OwnElement || node instanceof ShadowRoot;
}
function isOverflowElement(element) {
const {
overflow,
overflowX,
overflowY,
display
} = getComputedStyle$1(element);
return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !['inline', 'contents'].includes(display);
}
function isTableElement(element) {
return ['table', 'td', 'th'].includes(getNodeName(element));
}
function isContainingBlock(element) {
// TODO: Try to use feature detection here instead.
const isFirefox = /firefox/i.test(getUAString());
const css = getComputedStyle$1(element);
const backdropFilter = css.backdropFilter || css.WebkitBackdropFilter;
// 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
return css.transform !== 'none' || css.perspective !== 'none' || (backdropFilter ? backdropFilter !== 'none' : false) || isFirefox && css.willChange === 'filter' || isFirefox && (css.filter ? css.filter !== 'none' : false) || ['transform', 'perspective'].some(value => css.willChange.includes(value)) || ['paint', 'layout', 'strict', 'content'].some(value => {
// Add type check for old browsers.
const contain = css.contain;
return contain != null ? contain.includes(value) : false;
});
}
function isLayoutViewport() {
// TODO: Try to use feature detection here instead. Feature detection for
// this can fail in various ways, making the userAgent check the most:
// reliable:
// • Always-visible scrollbar or not
// • Width of <html>
// Not Safari.
return !/^((?!chrome|android).)*safari/i.test(getUAString());
}
function isLastTraversableNode(node) {
return ['html', 'body', '#document'].includes(getNodeName(node));
}
function unwrapElement(element) {
return !isElement(element) ? element.contextElement : element;
}
const FALLBACK_SCALE = {
x: 1,
y: 1
};
function getScale(element) {
const domElement = unwrapElement(element);
if (!isHTMLElement(domElement)) {
return FALLBACK_SCALE;
}
const rect = domElement.getBoundingClientRect();
const {
width,
height,
fallback
} = getCssDimensions(domElement);
let x = (fallback ? round(rect.width) : rect.width) / width;
let y = (fallback ? round(rect.height) : rect.height) / height;
// 0, NaN, or Infinity should always fallback to 1.
if (!x || !Number.isFinite(x)) {
x = 1;
}
if (!y || !Number.isFinite(y)) {
y = 1;
}
return {
x,
y
};
}
function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {
var _win$visualViewport, _win$visualViewport2;
if (includeScale === void 0) {
includeScale = false;
}
if (isFixedStrategy === void 0) {
isFixedStrategy = false;
}
const clientRect = element.getBoundingClientRect();
const domElement = unwrapElement(element);
let scale = FALLBACK_SCALE;
if (includeScale) {
if (offsetParent) {
if (isElement(offsetParent)) {
scale = getScale(offsetParent);
}
} else {
scale = getScale(element);
}
}
const win = domElement ? getWindow(domElement) : window;
const addVisualOffsets = !isLayoutViewport() && isFixedStrategy;
let x = (clientRect.left + (addVisualOffsets ? ((_win$visualViewport = win.visualViewport) == null ? void 0 : _win$visualViewport.offsetLeft) || 0 : 0)) / scale.x;
let y = (clientRect.top + (addVisualOffsets ? ((_win$visualViewport2 = win.visualViewport) == null ? void 0 : _win$visualViewport2.offsetTop) || 0 : 0)) / scale.y;
let width = clientRect.width / scale.x;
let height = clientRect.height / scale.y;
if (domElement) {
const win = getWindow(domElement);
const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;
let currentIFrame = win.frameElement;
while (currentIFrame && offsetParent && offsetWin !== win) {
const iframeScale = getScale(currentIFrame);
const iframeRect = currentIFrame.getBoundingClientRect();
const css = getComputedStyle(currentIFrame);
iframeRect.x += (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;
iframeRect.y += (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;
x *= iframeScale.x;
y *= iframeScale.y;
width *= iframeScale.x;
height *= iframeScale.y;
x += iframeRect.x;
y += iframeRect.y;
currentIFrame = getWindow(currentIFrame).frameElement;
}
}
return {
width,
height,
top: y,
right: x + width,
bottom: y + height,
left: x,
x,
y
};
}
function getDocumentElement(node) {
return ((isNode(node) ? node.ownerDocument : node.document) || window.document).documentElement;
}
function getNodeScroll(element) {
if (isElement(element)) {
return {
scrollLeft: element.scrollLeft,
scrollTop: element.scrollTop
};
}
return {
scrollLeft: element.pageXOffset,
scrollTop: element.pageYOffset
};
}
function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
let {
rect,
offsetParent,
strategy
} = _ref;
const isOffsetParentAnElement = isHTMLElement(offsetParent);
const documentElement = getDocumentElement(offsetParent);
if (offsetParent === documentElement) {
return rect;
}
let scroll = {
scrollLeft: 0,
scrollTop: 0
};
let scale = {
x: 1,
y: 1
};
const offsets = {
x: 0,
y: 0
};
if (isOffsetParentAnElement || !isOffsetParentAnElement && strategy !== 'fixed') {
if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
scroll = getNodeScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
const offsetRect = getBoundingClientRect(offsetParent);
scale = getScale(offsetParent);
offsets.x = offsetRect.x + offsetParent.clientLeft;
offsets.y = offsetRect.y + offsetParent.clientTop;
}
}
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
};
}
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) {
const html = getDocumentElement(element);
const scroll = getNodeScroll(element);
const body = element.ownerDocument.body;
const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);
const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);
let x = -scroll.scrollLeft + getWindowScrollBarX(element);
const y = -scroll.scrollTop;
if (getComputedStyle$1(body).direction === 'rtl') {
x += max(html.clientWidth, body.clientWidth) - width;
}
return {
width,
height,
x,
y
};
}
function getParentNode(node) {
if (getNodeName(node) === 'html') {
return node;
}
const result =
// Step into the shadow DOM of the parent of a slotted node.
node.assignedSlot ||
// DOM Element detected.
node.parentNode ||
// ShadowRoot detected.
isShadowRoot(node) && node.host ||
// Fallback.
getDocumentElement(node);
return isShadowRoot(result) ? result.host : result;
}
function getNearestOverflowAncestor(node) {
const parentNode = getParentNode(node);
if (isLastTraversableNode(parentNode)) {
// `getParentNode` will never return a `Document` due to the fallback
// check, so it's either the <html> or <body> element.
return parentNode.ownerDocument.body;
}
if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
return parentNode;
}
return getNearestOverflowAncestor(parentNode);
}
function getOverflowAncestors(node, list) {
var _node$ownerDocument;
if (list === void 0) {
list = [];
}
const scrollableAncestor = getNearestOverflowAncestor(node);
const isBody = scrollableAncestor === ((_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.body);
const win = getWindow(scrollableAncestor);
if (isBody) {
return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : []);
}
return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor));
}
function getViewportRect(element, strategy) {
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;
if (visualViewport) {
width = visualViewport.width;
height = visualViewport.height;
const layoutViewport = isLayoutViewport();
if (layoutViewport || !layoutViewport && strategy === 'fixed') {
x = visualViewport.offsetLeft;
y = visualViewport.offsetTop;
}
}
return {
width,
height,
x,
y
};
}
// Returns the inner client rect, subtracting scrollbars if present.
function getInnerBoundingClientRect(element, strategy) {
const clientRect = getBoundingClientRect(element, true, strategy === 'fixed');
const top = clientRect.top + element.clientTop;
const left = clientRect.left + element.clientLeft;
const scale = isHTMLElement(element) ? getScale(element) : {
x: 1,
y: 1
};
const width = element.clientWidth * scale.x;
const height = element.clientHeight * scale.y;
const x = left * scale.x;
const y = top * scale.y;
return {
width,
height,
x,
y
};
}
function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {
if (clippingAncestor === 'viewport') {
return core.rectToClientRect(getViewportRect(element, strategy));
}
if (isElement(clippingAncestor)) {
return core.rectToClientRect(getInnerBoundingClientRect(clippingAncestor, strategy));
}
return core.rectToClientRect(getDocumentRect(getDocumentElement(element)));
}
// A "clipping ancestor" is an `overflow` element with the characteristic of
// clipping (or hiding) child elements. This returns all clipping ancestors
// of the given element up the tree.
function getClippingElementAncestors(element, cache) {
const cachedResult = cache.get(element);
if (cachedResult) {
return cachedResult;
}
let result = getOverflowAncestors(element).filter(el => isElement(el) && getNodeName(el) !== 'body');
let currentContainingBlockComputedStyle = null;
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$1(currentNode);
const containingBlock = isContainingBlock(currentNode);
const shouldDropCurrentNode = elementIsFixed ? !containingBlock && !currentContainingBlockComputedStyle : !containingBlock && computedStyle.position === 'static' && !!currentContainingBlockComputedStyle && ['absolute', 'fixed'].includes(currentContainingBlockComputedStyle.position);
if (shouldDropCurrentNode) {
// Drop non-containing blocks.
result = result.filter(ancestor => ancestor !== currentNode);
} else {
// Record last containing block for next iteration.
currentContainingBlockComputedStyle = computedStyle;
}
currentNode = getParentNode(currentNode);
}
cache.set(element, result);
return result;
}
// Gets the maximum area that the element is visible in due to any number of
// clipping ancestors.
function getClippingRect(_ref) {
let {
element,
boundary,
rootBoundary,
strategy
} = _ref;
const elementClippingAncestors = boundary === 'clippingAncestors' ? getClippingElementAncestors(element, this._c) : [].concat(boundary);
const clippingAncestors = [...elementClippingAncestors, rootBoundary];
const firstClippingAncestor = clippingAncestors[0];
const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => {
const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy);
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;
}, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy));
return {
width: clippingRect.right - clippingRect.left,
height: clippingRect.bottom - clippingRect.top,
x: clippingRect.left,
y: clippingRect.top
};
}
function getDimensions(element) {
if (isHTMLElement(element)) {
return getCssDimensions(element);
}
return element.getBoundingClientRect();
}
function getTrueOffsetParent(element) {
if (!isHTMLElement(element) || getComputedStyle$1(element).position === 'fixed') {
return null;
}
return element.offsetParent;
}
function getContainingBlock(element) {
let currentNode = getParentNode(element);
while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
if (isContainingBlock(currentNode)) {
return currentNode;
} else {
currentNode = getParentNode(currentNode);
}
}
return null;
}
// Gets the closest ancestor positioned element. Handles some edge cases,
// such as table ancestors and cross browser bugs.
function getOffsetParent(element) {
const window = getWindow(element);
let offsetParent = getTrueOffsetParent(element);
while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === 'static') {
offsetParent = getTrueOffsetParent(offsetParent);
}
if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle$1(offsetParent).position === 'static' && !isContainingBlock(offsetParent))) {
return window;
}
return offsetParent || getContainingBlock(element) || window;
}
function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
const isOffsetParentAnElement = isHTMLElement(offsetParent);
const documentElement = getDocumentElement(offsetParent);
const rect = getBoundingClientRect(element, true, strategy === 'fixed', offsetParent);
let scroll = {
scrollLeft: 0,
scrollTop: 0
};
const offsets = {
x: 0,
y: 0
};
if (isOffsetParentAnElement || !isOffsetParentAnElement && strategy !== 'fixed') {
if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {
scroll = getNodeScroll(offsetParent);
}
if (isHTMLElement(offsetParent)) {
const offsetRect = getBoundingClientRect(offsetParent, true);
offsets.x = offsetRect.x + offsetParent.clientLeft;
offsets.y = offsetRect.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
};
}
const platform = {
getClippingRect,
convertOffsetParentRelativeRectToViewportRelativeRect,
isElement,
getDimensions,
getOffsetParent,
getDocumentElement,
getScale,
async getElementRects(_ref) {
let {
reference,
floating,
strategy
} = _ref;
const getOffsetParentFn = this.getOffsetParent || getOffsetParent;
const getDimensionsFn = this.getDimensions;
return {
reference: getRectRelativeToOffsetParent(reference, await getOffsetParentFn(floating), strategy),
floating: {
x: 0,
y: 0,
...(await getDimensionsFn(floating))
}
};
},
getClientRects: element => Array.from(element.getClientRects()),
isRTL: element => getComputedStyle$1(element).direction === 'rtl'
};
/**
* Automatically updates the position of the floating element when necessary.
* @see https://floating-ui.com/docs/autoUpdate
*/
function autoUpdate(reference, floating, update, options) {
if (options === void 0) {
options = {};
}
const {
ancestorScroll: _ancestorScroll = true,
ancestorResize = true,
elementResize = true,
animationFrame = false
} = options;
const ancestorScroll = _ancestorScroll && !animationFrame;
const ancestors = ancestorScroll || ancestorResize ? [...(isElement(reference) ? getOverflowAncestors(reference) : reference.contextElement ? getOverflowAncestors(reference.contextElement) : []), ...getOverflowAncestors(floating)] : [];
ancestors.forEach(ancestor => {
ancestorScroll && ancestor.addEventListener('scroll', update, {
passive: true
});
ancestorResize && ancestor.addEventListener('resize', update);
});
let observer = null;
if (elementResize) {
let initialUpdate = true;
observer = new ResizeObserver(() => {
if (!initialUpdate) {
update();
}
initialUpdate = false;
});
isElement(reference) && !animationFrame && observer.observe(reference);
if (!isElement(reference) && reference.contextElement && !animationFrame) {
observer.observe(reference.contextElement);
}
observer.observe(floating);
}
let frameId;
let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;
if (animationFrame) {
frameLoop();
}
function frameLoop() {
const nextRefRect = getBoundingClientRect(reference);
if (prevRefRect && (nextRefRect.x !== prevRefRect.x || nextRefRect.y !== prevRefRect.y || nextRefRect.width !== prevRefRect.width || nextRefRect.height !== prevRefRect.height)) {
update();
}
prevRefRect = nextRefRect;
frameId = requestAnimationFrame(frameLoop);
}
update();
return () => {
var _observer;
ancestors.forEach(ancestor => {
ancestorScroll && ancestor.removeEventListener('scroll', update);
ancestorResize && ancestor.removeEventListener('resize', update);
});
(_observer = observer) == null ? void 0 : _observer.disconnect();
observer = null;
if (animationFrame) {
cancelAnimationFrame(frameId);
}
};
}
/**
* Computes the `x` and `y` coordinates that will place the floating element
* next to a reference element when it is given a certain CSS positioning
* strategy.
*/
const computePosition = (reference, floating, options) => {
// This caches the expensive `getClippingElementAncestors` function so that
// multiple lifecycle resets re-use the same result. It only lives for a
// single call. If other functions become expensive, we can add them as well.
const cache = new Map();
const mergedOptions = {
platform,
...options
};
const platformWithCache = {
...mergedOptions.platform,
_c: cache
};
return core.computePosition(reference, floating, {
...mergedOptions,
platform: platformWithCache
});
};
Object.defineProperty(exports, 'arrow', {
enumerable: true,
get: function () { return core.arrow; }
});
Object.defineProperty(exports, 'autoPlacement', {
enumerable: true,
get: function () { return core.autoPlacement; }
});
Object.defineProperty(exports, 'detectOverflow', {
enumerable: true,
get: function () { return core.detectOverflow; }
});
Object.defineProperty(exports, 'flip', {
enumerable: true,
get: function () { return core.flip; }
});
Object.defineProperty(exports, 'hide', {
enumerable: true,
get: function () { return core.hide; }
});
Object.defineProperty(exports, 'inline', {
enumerable: true,
get: function () { return core.inline; }
});
Object.defineProperty(exports, 'limitShift', {
enumerable: true,
get: function () { return core.limitShift; }
});
Object.defineProperty(exports, 'offset', {
enumerable: true,
get: function () { return core.offset; }
});
Object.defineProperty(exports, 'shift', {
enumerable: true,
get: function () { return core.shift; }
});
Object.defineProperty(exports, 'size', {
enumerable: true,
get: function () { return core.size; }
});
exports.autoUpdate = autoUpdate;
exports.computePosition = computePosition;
exports.getOverflowAncestors = getOverflowAncestors;
exports.platform = platform;
Object.defineProperty(exports, '__esModule', { value: true });
}));
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
export * from './src/types';
+74
View File
@@ -0,0 +1,74 @@
{
"name": "@floating-ui/dom",
"version": "1.1.1",
"@rollingversions": {
"baseVersion": [
0,
1,
0
]
},
"description": "Floating UI for the web",
"publishConfig": {
"access": "public"
},
"main": "./dist/floating-ui.dom.umd.js",
"module": "./dist/floating-ui.dom.esm.js",
"unpkg": "./dist/floating-ui.dom.umd.min.js",
"types": "./index.d.ts",
"exports": {
".": {
"types": "./index.d.ts",
"import": {
"browser": {
"development": "./dist/floating-ui.dom.browser.mjs",
"default": "./dist/floating-ui.dom.browser.min.mjs"
},
"default": "./dist/floating-ui.dom.mjs"
},
"module": "./dist/floating-ui.dom.esm.js",
"default": "./dist/floating-ui.dom.umd.js"
},
"./package.json": "./package.json"
},
"sideEffects": false,
"files": [
"dist/",
"index.d.ts",
"src/**/*.d.ts"
],
"scripts": {
"dev": "parcel test/visual/index.html",
"build": "NODE_ENV=build rollup -c",
"test": "jest test/unit"
},
"author": "atomiks",
"license": "MIT",
"bugs": "https://github.com/floating-ui/floating-ui",
"repository": {
"type": "git",
"url": "https://github.com/floating-ui/floating-ui.git",
"directory": "packages/dom"
},
"homepage": "https://floating-ui.com",
"keywords": [
"tooltip",
"popover",
"dropdown",
"menu",
"popup",
"positioning"
],
"dependencies": {
"@floating-ui/core": "^1.1.0"
},
"devDependencies": {
"@playwright/test": "^1.16.3",
"@types/react": "^18.0.1",
"concurrently": "^6.4.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-router-dom": "^6.1.1",
"serve": "^13.0.2"
}
}
+31
View File
@@ -0,0 +1,31 @@
import type { FloatingElement, ReferenceElement } from './types';
export interface Options {
/**
* Whether to update the position when an overflow ancestor is scrolled.
* @default true
*/
ancestorScroll: boolean;
/**
* Whether to update the position when an overflow ancestor is resized. This
* uses the native `resize` event.
* @default true
*/
ancestorResize: boolean;
/**
* Whether to update the position when either the reference or floating
* elements resized. This uses a `ResizeObserver`.
* @default true
*/
elementResize: boolean;
/**
* Whether to update on every animation frame if necessary. Optimized for
* performance so updates are only called when necessary, but use sparingly.
* @default false
*/
animationFrame: boolean;
}
/**
* Automatically updates the position of the floating element when necessary.
* @see https://floating-ui.com/docs/autoUpdate
*/
export declare function autoUpdate(reference: ReferenceElement, floating: FloatingElement, update: () => void, options?: Partial<Options>): () => void;
+11
View File
@@ -0,0 +1,11 @@
import type { ComputePositionConfig, FloatingElement, ReferenceElement } from './types';
/**
* Computes the `x` and `y` coordinates that will place the floating element
* next to a reference element when it is given a certain CSS positioning
* strategy.
*/
export declare const computePosition: (reference: ReferenceElement, floating: FloatingElement, options?: Partial<ComputePositionConfig>) => Promise<import("@floating-ui/core").ComputePositionReturn>;
export { autoUpdate } from './autoUpdate';
export { platform } from './platform';
export { getOverflowAncestors } from './utils/getOverflowAncestors';
export { arrow, autoPlacement, detectOverflow, flip, hide, inline, limitShift, offset, shift, size, } from '@floating-ui/core';
+2
View File
@@ -0,0 +1,2 @@
import type { Platform } from './types';
export declare const platform: Platform;
+128
View File
@@ -0,0 +1,128 @@
import type { AutoPlacementOptions, ClientRectObject, ComputePositionConfig as CoreComputePositionConfig, DetectOverflowOptions as CoreDetectOverflowOptions, Dimensions, ElementRects, FlipOptions, HideOptions, Middleware as CoreMiddleware, MiddlewareArguments as CoreMiddlewareArguments, MiddlewareReturn, Padding, Rect, RootBoundary, ShiftOptions, SideObject, SizeOptions as CoreSizeOptions, Strategy } from '@floating-ui/core';
type Promisable<T> = T | Promise<T>;
export interface Platform {
getElementRects: (args: {
reference: ReferenceElement;
floating: FloatingElement;
strategy: Strategy;
}) => Promisable<ElementRects>;
getClippingRect: (args: {
element: Element;
boundary: Boundary;
rootBoundary: RootBoundary;
strategy: Strategy;
}) => Promisable<Rect>;
getDimensions: (element: Element) => Promisable<Dimensions>;
convertOffsetParentRelativeRectToViewportRelativeRect?: (args: {
rect: Rect;
offsetParent: Element;
strategy: Strategy;
}) => Promisable<Rect>;
getOffsetParent?: (element: Element) => Promisable<Element | Window>;
isElement?: (value: unknown) => Promisable<boolean>;
getDocumentElement?: (element: Element) => Promisable<HTMLElement>;
getClientRects?: (element: Element) => Promisable<Array<ClientRectObject>>;
isRTL?: (element: Element) => Promisable<boolean>;
getScale?: (element: HTMLElement) => Promisable<{
x: number;
y: number;
}>;
}
export interface NodeScroll {
scrollLeft: number;
scrollTop: number;
}
export type Boundary = 'clippingAncestors' | Element | Array<Element>;
export type DetectOverflowOptions = Omit<CoreDetectOverflowOptions, 'boundary'> & {
boundary: Boundary;
};
export type SizeOptions = Omit<CoreSizeOptions, 'apply'> & {
/**
* Function that is called to perform style mutations to the floating element
* to change its size.
* @default undefined
*/
apply(args: MiddlewareArguments & {
availableWidth: number;
availableHeight: number;
}): Promisable<void>;
};
export type ComputePositionConfig = Omit<CoreComputePositionConfig, 'middleware' | 'platform'> & {
middleware?: Array<Middleware | null | undefined | false>;
platform?: Platform;
};
/**
* Custom positioning reference element.
* @see https://floating-ui.com/docs/virtual-elements
*/
export interface VirtualElement {
getBoundingClientRect(): ClientRectObject;
contextElement?: Element;
}
export type ReferenceElement = Element | VirtualElement;
export type FloatingElement = HTMLElement;
export interface Elements {
reference: ReferenceElement;
floating: FloatingElement;
}
export type MiddlewareArguments = Omit<CoreMiddlewareArguments, 'elements'> & {
elements: Elements;
};
export type Middleware = Omit<CoreMiddleware, 'fn'> & {
fn(args: MiddlewareArguments): Promisable<MiddlewareReturn>;
};
/**
* Automatically chooses the `placement` which has the most space available.
* @see https://floating-ui.com/docs/autoPlacement
*/
declare const autoPlacement: (options?: Partial<AutoPlacementOptions & DetectOverflowOptions>) => Middleware;
/**
* Shifts the floating element in order to keep it in view when it will overflow
* a clipping boundary.
* @see https://floating-ui.com/docs/shift
*/
declare const shift: (options?: Partial<ShiftOptions & DetectOverflowOptions>) => Middleware;
/**
* Changes the placement of the floating element to one that will fit if the
* initially specified `placement` does not.
* @see https://floating-ui.com/docs/flip
*/
declare const flip: (options?: Partial<FlipOptions & DetectOverflowOptions>) => Middleware;
/**
* Provides data to change the size of the floating element. For instance,
* prevent it from overflowing its clipping boundary or match the width of the
* reference element.
* @see https://floating-ui.com/docs/size
*/
declare const size: (options?: Partial<SizeOptions & DetectOverflowOptions>) => Middleware;
/**
* Positions an inner element of the floating element such that it is centered
* to the reference element.
* @see https://floating-ui.com/docs/arrow
*/
declare const arrow: (options: {
element: Element;
padding?: Padding;
}) => Middleware;
/**
* Provides data to hide the floating element in applicable situations, such as
* when it is not in the same clipping context as the reference element.
* @see https://floating-ui.com/docs/hide
*/
declare const hide: (options?: Partial<HideOptions & DetectOverflowOptions>) => Middleware;
/**
* Resolves with an object of overflow side offsets that determine how much the
* element is overflowing a given clipping boundary.
* - positive = overflowing the boundary by that number of pixels
* - negative = how many pixels left before it will overflow
* - 0 = lies flush with the boundary
* @see https://floating-ui.com/docs/detectOverflow
*/
declare const detectOverflow: (middlewareArguments: MiddlewareArguments, options?: Partial<DetectOverflowOptions>) => Promise<SideObject>;
export { arrow, autoPlacement, detectOverflow, flip, hide, shift, size };
export { computePosition } from './';
export { autoUpdate, Options as AutoUpdateOptions } from './autoUpdate';
export { platform } from './platform';
export { getOverflowAncestors } from './utils/getOverflowAncestors';
export type { AlignedPlacement, Alignment, Axis, ClientRectObject, ComputePositionReturn, Coords, Dimensions, ElementContext, ElementRects, Length, MiddlewareData, MiddlewareReturn, Padding, Placement, Rect, RootBoundary, Side, SideObject, Strategy, } from '@floating-ui/core';
export { inline, limitShift, offset } from '@floating-ui/core';
@@ -0,0 +1,6 @@
import type { Rect, Strategy } from '@floating-ui/core';
export declare function convertOffsetParentRelativeRectToViewportRelativeRect({ rect, offsetParent, strategy, }: {
rect: Rect;
offsetParent: Element | Window;
strategy: Strategy;
}): Rect;
@@ -0,0 +1,2 @@
import type { ClientRectObject, VirtualElement } from '@floating-ui/core';
export declare function getBoundingClientRect(element: Element | VirtualElement, includeScale?: boolean, isFixedStrategy?: boolean, offsetParent?: Element | Window): ClientRectObject;
@@ -0,0 +1,12 @@
import type { Boundary, Rect, RootBoundary, Strategy } from '@floating-ui/core';
import { Platform, ReferenceElement } from '../types';
type PlatformWithCache = Platform & {
_c: Map<ReferenceElement, Element[]>;
};
export declare function getClippingRect(this: PlatformWithCache, { element, boundary, rootBoundary, strategy, }: {
element: Element;
boundary: Boundary;
rootBoundary: RootBoundary;
strategy: Strategy;
}): Rect;
export {};
@@ -0,0 +1 @@
export declare function getComputedStyle(element: Element): CSSStyleDeclaration;
@@ -0,0 +1,4 @@
import type { Dimensions } from '@floating-ui/core';
export declare function getCssDimensions(element: HTMLElement): Dimensions & {
fallback: boolean;
};
@@ -0,0 +1,2 @@
import type { Dimensions } from '@floating-ui/core';
export declare function getDimensions(element: Element): Dimensions;
@@ -0,0 +1 @@
export declare function getDocumentElement(node: Node | Window): HTMLElement;
@@ -0,0 +1,2 @@
import type { Rect } from '@floating-ui/core';
export declare function getDocumentRect(element: HTMLElement): Rect;
@@ -0,0 +1 @@
export declare function getNearestOverflowAncestor(node: Node): HTMLElement;
@@ -0,0 +1 @@
export declare function getNodeName(node: Node | Window): string;
@@ -0,0 +1,2 @@
import { NodeScroll } from '../types';
export declare function getNodeScroll(element: Element | Window): NodeScroll;
@@ -0,0 +1 @@
export declare function getOffsetParent(element: Element): Element | Window;
@@ -0,0 +1,3 @@
type OverflowAncestors = Array<Element | Window | VisualViewport>;
export declare function getOverflowAncestors(node: Node, list?: OverflowAncestors): OverflowAncestors;
export {};
@@ -0,0 +1 @@
export declare function getParentNode(node: Node): Node;
@@ -0,0 +1,2 @@
import type { Rect, Strategy, VirtualElement } from '@floating-ui/core';
export declare function getRectRelativeToOffsetParent(element: Element | VirtualElement, offsetParent: Element | Window, strategy: Strategy): Rect;
+7
View File
@@ -0,0 +1,7 @@
import type { Coords } from '@floating-ui/core';
import type { VirtualElement } from '../types';
export declare const FALLBACK_SCALE: {
x: number;
y: number;
};
export declare function getScale(element: Element | VirtualElement): Coords;
@@ -0,0 +1,2 @@
import type { Rect, Strategy } from '@floating-ui/core';
export declare function getViewportRect(element: Element, strategy: Strategy): Rect;
@@ -0,0 +1 @@
export declare function getWindowScrollBarX(element: Element): number;
+17
View File
@@ -0,0 +1,17 @@
declare global {
interface Window {
HTMLElement: any;
Element: any;
Node: any;
ShadowRoot: any;
}
}
export declare function isHTMLElement(value: any): value is HTMLElement;
export declare function isElement(value: any): value is Element;
export declare function isNode(value: any): value is Node;
export declare function isShadowRoot(node: Node): node is ShadowRoot;
export declare function isOverflowElement(element: Element): boolean;
export declare function isTableElement(element: Element): boolean;
export declare function isContainingBlock(element: Element): boolean;
export declare function isLayoutViewport(): boolean;
export declare function isLastTraversableNode(node: Node): boolean;
+3
View File
@@ -0,0 +1,3 @@
export declare const min: (...values: number[]) => number;
export declare const max: (...values: number[]) => number;
export declare const round: (x: number) => number;
@@ -0,0 +1,2 @@
import type { VirtualElement } from '../types';
export declare function unwrapElement(element: Element | VirtualElement): Element | undefined;
+1
View File
@@ -0,0 +1 @@
export declare function getUAString(): string;
+1
View File
@@ -0,0 +1 @@
export declare function getWindow(node: Node): Window;