|
- 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);
- }
-
- function isNode(value) {
- return value instanceof getWindow(value).Node;
- }
- 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 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;
- });
- }
-
- /**
- * Determines whether or not `.getBoundingClientRect()` is affected by visual
- * viewport offsets. In Safari, the `x`/`y` offsets are values relative to the
- * visual viewport, while in other engines, they are values relative to the
- * layout viewport.
- */
- function isClientRectVisualViewportBased() {
- // 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>
-
- // Is Safari.
- return /^((?!chrome|android).)*safari/i.test(getUAString());
- }
- function isLastTraversableNode(node) {
- return ['html', 'body', '#document'].includes(getNodeName(node));
- }
-
- 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 hasOffset = isHTMLElement(element);
- const offsetWidth = hasOffset ? element.offsetWidth : width;
- const offsetHeight = hasOffset ? element.offsetHeight : height;
- const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;
- if (shouldFallback) {
- width = offsetWidth;
- height = offsetHeight;
- }
- return {
- width,
- height,
- fallback: shouldFallback
- };
- }
-
- 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 = isClientRectVisualViewportBased() && 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 rectToClientRect({
- width,
- height,
- 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 visualViewportBased = isClientRectVisualViewportBased();
- if (!visualViewportBased || visualViewportBased && 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) {
- let rect;
- if (clippingAncestor === 'viewport') {
- rect = getViewportRect(element, strategy);
- } else if (clippingAncestor === 'document') {
- rect = getDocumentRect(getDocumentElement(element));
- } else if (isElement(clippingAncestor)) {
- rect = getInnerBoundingClientRect(clippingAncestor, strategy);
- } else {
- const mutableRect = {
- ...clippingAncestor
- };
- if (isClientRectVisualViewportBased()) {
- var _win$visualViewport, _win$visualViewport2;
- const win = getWindow(element);
- mutableRect.x -= ((_win$visualViewport = win.visualViewport) == null ? void 0 : _win$visualViewport.offsetLeft) || 0;
- mutableRect.y -= ((_win$visualViewport2 = win.visualViewport) == null ? void 0 : _win$visualViewport2.offsetTop) || 0;
- }
- rect = mutableRect;
- }
- return rectToClientRect(rect);
- }
-
- // 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);
- if (computedStyle.position === 'fixed') {
- currentContainingBlockComputedStyle = null;
- }
- 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) {
- return getCssDimensions(element);
- }
-
- function getTrueOffsetParent(element, polyfill) {
- if (!isHTMLElement(element) || getComputedStyle$1(element).position === 'fixed') {
- return null;
- }
- if (polyfill) {
- return polyfill(element);
- }
- 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, polyfill) {
- const window = getWindow(element);
- if (!isHTMLElement(element)) {
- return window;
- }
- let offsetParent = getTrueOffsetParent(element, polyfill);
- while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === 'static') {
- offsetParent = getTrueOffsetParent(offsetParent, polyfill);
- }
- 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.
- * Should only be called when the floating element is mounted on the DOM or
- * visible on the screen.
- * @returns cleanup function that should be invoked when the floating element is
- * removed from the DOM or hidden from the screen.
- * @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) {
- observer = new ResizeObserver(() => {
- update();
- });
- 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 };
|