From e44b828eaf592e1789f0211f9b5047b4a15bae5e Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 6 Jun 2026 15:00:36 +0200 Subject: [PATCH] fix(furni-editor): render field tooltips in a portal (no clipping) The Tip bubble was still clipped by the card's overflow-auto scroll container when a section was scrolled near the viewport top. Render the tooltip via createPortal to document.body with position:fixed computed from the icon rect, so it escapes every overflow/transform ancestor. --- .../views/FurniEditorEditView.tsx | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/components/furni-editor/views/FurniEditorEditView.tsx b/src/components/furni-editor/views/FurniEditorEditView.tsx index 5b80193..4110fe7 100644 --- a/src/components/furni-editor/views/FurniEditorEditView.tsx +++ b/src/components/furni-editor/views/FurniEditorEditView.tsx @@ -1,4 +1,5 @@ import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; import { Button, Column, Flex, LayoutFurniIconImageView, Text } from '../../../common'; import { FurniDetail } from '../../../hooks/furni-editor'; @@ -51,15 +52,33 @@ const Section: FC = ({ title, children, defaultOpen = true }) => const Tip: FC<{ field: string }> = ({ field }) => { const tip = FIELD_TIPS[field]; + const ref = useRef(null); + const [ pos, setPos ] = useState<{ left: number; top: number } | null>(null); + + const show = useCallback(() => + { + const r = ref.current?.getBoundingClientRect(); + if(r) setPos({ left: r.left + (r.width / 2), top: r.top - 6 }); + }, []); + const hide = useCallback(() => setPos(null), []); if(!tip) return null; return ( - - ? - - { tip } - + + ? + { pos && createPortal( + + { tip } + , document.body) } ); };