@@ -7,6 +7,7 @@ import { Loader2 } from "lucide-react";
77import { useCallback , useEffect , useRef , useState } from "react" ;
88import { SymbolDefinition , useHoveredOverSymbolInfo } from "./useHoveredOverSymbolInfo" ;
99import { SymbolDefinitionPreview } from "./symbolDefinitionPreview" ;
10+ import { createPortal } from "react-dom" ;
1011
1112interface SymbolHoverPopupProps {
1213 editorRef : ReactCodeMirrorRef ;
@@ -55,9 +56,11 @@ export const SymbolHoverPopup: React.FC<SymbolHoverPopupProps> = ({
5556 crossAxis : false ,
5657 fallbackPlacements : [ 'bottom' ] ,
5758 boundary : editorRef . view ?. dom ,
59+ padding : 20 ,
5860 } ) ,
5961 shift ( {
6062 padding : 5 ,
63+ boundary : editorRef . view ?. dom ,
6164 } )
6265 ]
6366 } ) . then ( ( { x, y } ) => {
@@ -91,7 +94,14 @@ export const SymbolHoverPopup: React.FC<SymbolHoverPopupProps> = ({
9194 }
9295 } , [ symbolInfo , onGotoDefinition ] ) ;
9396
94- return symbolInfo ? (
97+ if ( ! symbolInfo ) {
98+ return null ;
99+ }
100+
101+ // We use a portal here to render the popup at the document body level.
102+ // This avoids clipping issues that occur when the popup is rendered inside scrollable or overflow-hidden containers (like the editor or its parent).
103+ // By rendering in a portal, the popup can be absolutely positioned anywhere in the viewport without being cut off by parent containers.
104+ return createPortal (
95105 < div
96106 ref = { ref }
97107 className = "absolute z-10 flex flex-col gap-2 bg-background border border-gray-300 dark:border-gray-700 rounded-md shadow-lg p-2 max-w-3xl"
@@ -133,6 +143,7 @@ export const SymbolHoverPopup: React.FC<SymbolHoverPopupProps> = ({
133143 Find references
134144 </ Button >
135145 </ div >
136- </ div >
137- ) : null ;
146+ </ div > ,
147+ document . body
148+ ) ;
138149} ;
0 commit comments