Skip to content

Commit

Permalink
fix: top layer elements with absolute strategy (#2786)
Browse files Browse the repository at this point in the history
Co-authored-by: GitHub Actions <github-actions@github.com>
  • Loading branch information
atomiks and GitHub Actions committed Feb 11, 2024
1 parent 86526a7 commit f87487e
Show file tree
Hide file tree
Showing 41 changed files with 296 additions and 268 deletions.
5 changes: 5 additions & 0 deletions .changeset/old-turkeys-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@floating-ui/dom": patch
---

fix: top layer element positioning and collision detection when using `absolute` strategy
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,24 @@ import {

import {getBoundingClientRect} from '../utils/getBoundingClientRect';
import {getScale} from './getScale';
import type {Platform} from '../types';
import {topLayer} from '../utils/topLayer';
import {isTopLayer} from '../utils/isTopLayer';

export function convertOffsetParentRelativeRectToViewportRelativeRect(
this: Platform,
{
elements,
rect,
offsetParent,
strategy,
}: {
elements?: Elements;
rect: Rect;
offsetParent: Element | Window;
strategy: Strategy;
},
): Rect {
export function convertOffsetParentRelativeRectToViewportRelativeRect({
elements,
rect,
offsetParent,
strategy,
}: {
elements?: Elements;
rect: Rect;
offsetParent: Element | Window;
strategy: Strategy;
}): Rect {
const isFixed = strategy === 'fixed';
const documentElement = getDocumentElement(offsetParent);
const [isTopLayer] = elements ? topLayer(elements.floating) : [false];
const topLayer = elements ? isTopLayer(elements.floating) : false;

if (offsetParent === documentElement || isTopLayer) {
if (offsetParent === documentElement || (topLayer && isFixed)) {
return rect;
}

Expand All @@ -39,10 +36,7 @@ export function convertOffsetParentRelativeRectToViewportRelativeRect(
const offsets = createCoords(0);
const isOffsetParentAnElement = isHTMLElement(offsetParent);

if (
isOffsetParentAnElement ||
(!isOffsetParentAnElement && strategy !== 'fixed')
) {
if (isOffsetParentAnElement || (!isOffsetParentAnElement && !isFixed)) {
if (
getNodeName(offsetParent) !== 'body' ||
isOverflowElement(documentElement)
Expand Down
1 change: 0 additions & 1 deletion packages/dom/src/platform/getElementRects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export const getElementRects: Platform['getElementRects'] = async function (
data.reference,
await getOffsetParentFn(data.floating),
data.strategy,
data.floating,
),
floating: {x: 0, y: 0, ...(await getDimensionsFn(data.floating))},
};
Expand Down
3 changes: 2 additions & 1 deletion packages/dom/src/platform/getOffsetParent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
isHTMLElement,
isTableElement,
} from '@floating-ui/utils/dom';
import {isTopLayer} from '../utils/isTopLayer';

type Polyfill = (element: HTMLElement) => Element | null;

Expand Down Expand Up @@ -36,7 +37,7 @@ export function getOffsetParent(
): Element | Window {
const window = getWindow(element);

if (!isHTMLElement(element)) {
if (!isHTMLElement(element) || isTopLayer(element)) {
return window;
}

Expand Down
18 changes: 2 additions & 16 deletions packages/dom/src/utils/getRectRelativeToOffsetParent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ import {
import {getDocumentElement} from '../platform/getDocumentElement';
import {getBoundingClientRect} from './getBoundingClientRect';
import {getWindowScrollBarX} from './getWindowScrollBarX';
import {topLayer} from './topLayer';

export function getRectRelativeToOffsetParent(
element: Element | VirtualElement,
offsetParent: Element | Window,
strategy: Strategy,
floating: HTMLElement,
): Rect {
const isOffsetParentAnElement = isHTMLElement(offsetParent);
const documentElement = getDocumentElement(offsetParent);
Expand Down Expand Up @@ -48,20 +46,8 @@ export function getRectRelativeToOffsetParent(
}
}

let x = rect.left + scroll.scrollLeft - offsets.x;
let y = rect.top + scroll.scrollTop - offsets.y;

const [isTopLayer, topLayerX, topLayerY] = topLayer(floating);

if (isTopLayer) {
x += topLayerX;
y += topLayerY;

if (isOffsetParentAnElement) {
x += offsetParent.clientLeft;
y += offsetParent.clientTop;
}
}
const x = rect.left + scroll.scrollLeft - offsets.x;
const y = rect.top + scroll.scrollTop - offsets.y;

return {
x,
Expand Down
11 changes: 11 additions & 0 deletions packages/dom/src/utils/isTopLayer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const topLayerSelectors = [':popover-open', ':modal'] as const;

export function isTopLayer(floating: HTMLElement) {
return topLayerSelectors.some((selector) => {
try {
return floating.matches(selector);
} catch (e) {
return false;
}
});
}
30 changes: 0 additions & 30 deletions packages/dom/src/utils/topLayer.ts

This file was deleted.

0 comments on commit f87487e

Please sign in to comment.