Fix Tooltip clipping in overflow-hidden containers — use fixed positioning
Switches from absolute (clipped by ancestor overflow:hidden) to fixed with viewport-relative coords from getBoundingClientRect(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+11
-21
@@ -13,7 +13,8 @@
|
|||||||
let wrapperEl = $state<HTMLDivElement | null>(null);
|
let wrapperEl = $state<HTMLDivElement | null>(null);
|
||||||
let showTooltip = $state(false);
|
let showTooltip = $state(false);
|
||||||
let placement = $state<"top" | "bottom">("top");
|
let placement = $state<"top" | "bottom">("top");
|
||||||
let leftPx = $state<number | null>(null); // null = centered (CSS), number = clamped px from wrapper left
|
let fixedLeft = $state(0);
|
||||||
|
let fixedTop = $state(0);
|
||||||
|
|
||||||
function updatePosition() {
|
function updatePosition() {
|
||||||
if (!wrapperEl) return;
|
if (!wrapperEl) return;
|
||||||
@@ -25,20 +26,13 @@
|
|||||||
const spaceBelow = vh - rect.bottom;
|
const spaceBelow = vh - rect.bottom;
|
||||||
placement =
|
placement =
|
||||||
preferredPlacement === "top"
|
preferredPlacement === "top"
|
||||||
? spaceAbove >= spaceBelow
|
? spaceAbove >= spaceBelow ? "top" : "bottom"
|
||||||
? "top"
|
: spaceBelow >= spaceAbove ? "bottom" : "top";
|
||||||
: "bottom"
|
|
||||||
: spaceBelow >= spaceAbove
|
|
||||||
? "bottom"
|
|
||||||
: "top";
|
|
||||||
|
|
||||||
const iconCenterX = rect.left + rect.width / 2;
|
const iconCenterX = rect.left + rect.width / 2;
|
||||||
const idealLeft = iconCenterX - TOOLTIP_MAX_WIDTH_PX / 2;
|
const idealLeft = iconCenterX - TOOLTIP_MAX_WIDTH_PX / 2;
|
||||||
const clampedLeft = Math.max(
|
fixedLeft = Math.max(TOOLTIP_GAP, Math.min(idealLeft, vw - TOOLTIP_MAX_WIDTH_PX - TOOLTIP_GAP));
|
||||||
TOOLTIP_GAP,
|
fixedTop = placement === "top" ? rect.top - TOOLTIP_GAP : rect.bottom + TOOLTIP_GAP;
|
||||||
Math.min(idealLeft, vw - TOOLTIP_MAX_WIDTH_PX - TOOLTIP_GAP),
|
|
||||||
);
|
|
||||||
leftPx = clampedLeft - rect.left;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleShow() {
|
function handleShow() {
|
||||||
@@ -48,16 +42,12 @@
|
|||||||
|
|
||||||
function handleHide() {
|
function handleHide() {
|
||||||
showTooltip = false;
|
showTooltip = false;
|
||||||
leftPx = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const placementClasses = $derived(
|
const tooltipStyle = $derived(
|
||||||
placement === "top"
|
placement === "top"
|
||||||
? "bottom-full mb-2"
|
? `position:fixed;top:${fixedTop}px;left:${fixedLeft}px;transform:translateY(-100%);`
|
||||||
: "top-full mt-2",
|
: `position:fixed;top:${fixedTop}px;left:${fixedLeft}px;`,
|
||||||
);
|
|
||||||
const horizontalStyle = $derived(
|
|
||||||
leftPx != null ? `left: ${leftPx}px; transform: none;` : "left: 50%; transform: translateX(-50%);",
|
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -74,8 +64,8 @@
|
|||||||
{#if showTooltip}
|
{#if showTooltip}
|
||||||
<div
|
<div
|
||||||
role="tooltip"
|
role="tooltip"
|
||||||
class="absolute z-50 {placementClasses} px-3 py-2 text-xs font-normal text-stein-0 bg-stein-800 rounded-xs shadow-lg min-w-56 max-w-88 text-center pointer-events-none whitespace-normal"
|
class="z-[9999] px-3 py-2 text-xs font-normal text-stein-0 bg-stein-800 rounded-xs shadow-lg min-w-56 max-w-88 text-center pointer-events-none whitespace-normal"
|
||||||
style={horizontalStyle}
|
style={tooltipStyle}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user