From 601a02117f84bd411cb2550ecc37752f96af997c Mon Sep 17 00:00:00 2001 From: Christian Vidal Wolf Date: Mon, 27 Apr 2026 14:50:13 +0200 Subject: [PATCH] fix: use Portal for hover tooltips to prevent table container overflow clipping --- src/components/PricingView.tsx | 70 +++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/src/components/PricingView.tsx b/src/components/PricingView.tsx index 04a8db1..1cc4ae8 100644 --- a/src/components/PricingView.tsx +++ b/src/components/PricingView.tsx @@ -53,6 +53,58 @@ function findCol(headers: string[], ...keywords: string[]): number { ); } +function NoteTooltip({ note, isAnna, children }: { note: string, isAnna?: boolean, children: React.ReactNode }) { + const [show, setShow] = useState(false); + const [pos, setPos] = useState({ top: 0, left: 0 }); + const ref = useRef(null); + + if (!note) { + return <>{children}; + } + + const handleMouseEnter = () => { + if (ref.current) { + const rect = ref.current.getBoundingClientRect(); + setPos({ + top: rect.bottom + window.scrollY + 8, + left: rect.left + window.scrollX + rect.width / 2 + }); + setShow(true); + } + }; + + useEffect(() => { + const handleScroll = () => setShow(false); + if (show) { + window.addEventListener('scroll', handleScroll, true); + } + return () => window.removeEventListener('scroll', handleScroll, true); + }, [show]); + + return ( +
setShow(false)} + > + {children} + {show && createPortal( +
+ {note} +
, + document.body + )} +
+ ); +} + export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit, rowStatuses }: PricingViewProps) { const COLUMNS = useColumns(); const [filterMode, setFilterMode] = usePersistentState('pricing-filterMode', 'all_errors'); @@ -1928,7 +1980,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit, onChange={(e) => handleToggleCheck(dataIndex, e.target.checked)} className="w-4 h-4 rounded border-slate-600 bg-slate-900 text-blue-600 focus:ring-blue-500 focus:ring-offset-slate-800 cursor-pointer" /> -
+ - {note && ( -
- {note} -
- )} -
+ @@ -1962,7 +2009,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit, onChange={(e: React.ChangeEvent) => handleToggleAnnaCheck(dataIndex, e.target.checked)} className="w-4 h-4 rounded border-slate-600 bg-slate-900 text-pink-600 focus:ring-pink-500 focus:ring-offset-slate-800 cursor-pointer" /> -
+ - {annaNote && ( -
- {annaNote} -
- )} -
+ );