fix(app): keep tooltips inside the fullscreen element (#1968)

This commit is contained in:
Hampus
2026-08-24 12:55:00 +02:00
committed by GitHub
parent 51e2eee15a
commit f56ccf5ef5
2 changed files with 30 additions and 4 deletions
@@ -2,7 +2,7 @@
import Accessibility from '@app/features/accessibility/state/Accessibility';
import {Logger} from '@app/features/platform/utils/AppLogger';
import {useTooltipPortalRoot} from '@app/features/ui/tooltip/Tooltip';
import {getFullscreenOverflowBoundary, useTooltipPortalRoot} from '@app/features/ui/tooltip/Tooltip';
import {appZoomCssPx, appZoomLayoutPx} from '@app/features/ui/utils/AppZoomUtils';
import {
getReducedMotionProps,
@@ -101,9 +101,17 @@ export function useSliderTooltip({
left: '-9999px',
top: '-9999px',
});
const middleware = [offset(8), flip(), shift({padding: 8}), arrow({element: arrowRef})];
const overflowBoundary = getFullscreenOverflowBoundary(target);
const boundaryOptions = overflowBoundary ? {boundary: overflowBoundary} : undefined;
const middleware = [
offset(8),
flip(boundaryOptions),
shift({padding: 8, ...boundaryOptions}),
arrow({element: arrowRef}),
];
const {x, y, middlewareData} = await computePosition(target, tooltip, {
placement: 'top',
strategy: 'fixed',
middleware,
});
Object.assign(tooltip.style, {
+20 -2
View File
@@ -26,6 +26,7 @@ import {
subscribeWindowHoverControlsChange,
} from '@app/features/ui/utils/WindowFocusInteractionGuard';
import {elementSupportsRef} from '@app/lib/react';
import type {ExtendedDocument} from '@app/types/browser.d';
import {FloatingPortal} from '@floating-ui/react';
import {arrow, autoUpdate, computePosition, flip, offset, shift} from '@floating-ui/react-dom';
import {clsx} from 'clsx';
@@ -47,6 +48,20 @@ const TOOLTIP_AUTO_UPDATE_OPTIONS = {
layoutShift: true,
} as const;
export const getFullscreenOverflowBoundary = (target: HTMLElement): HTMLElement | null => {
const ownerDocument = target.ownerDocument as ExtendedDocument;
const ownerWindow = ownerDocument.defaultView;
if (!ownerWindow) return null;
const fullscreenElement =
ownerDocument.fullscreenElement ??
ownerDocument.webkitFullscreenElement ??
ownerDocument.mozFullScreenElement ??
ownerDocument.msFullscreenElement ??
null;
if (!(fullscreenElement instanceof ownerWindow.HTMLElement)) return null;
return fullscreenElement.contains(target) ? fullscreenElement : null;
};
const tooltipPortalRoots = new WeakMap<Document, HTMLElement>();
const getTooltipPortalRoot = (ownerDocument: Document): HTMLElement => {
@@ -389,14 +404,17 @@ export const Tooltip = observer(
left: '-9999px',
top: '-9999px',
} as CSSStyleDeclaration);
const overflowBoundary = getFullscreenOverflowBoundary(target);
const boundaryOptions = overflowBoundary ? {boundary: overflowBoundary} : undefined;
const middleware = [
offset(padding + nudge),
flip(),
shift({padding: 8}),
flip(boundaryOptions),
shift({padding: 8, ...boundaryOptions}),
...(arrowRef.current ? [arrow({element: arrowRef.current})] : []),
];
const {x, y, placement, middlewareData} = await computePosition(target, tooltip, {
placement: position,
strategy: 'fixed',
middleware,
});
if (