@@ -191,15 +191,71 @@ function setMenuPosition(
191191 const floatingElemRect = floatingElem . getBoundingClientRect ( ) ;
192192 const anchorElementRect = anchorElem . getBoundingClientRect ( ) ;
193193
194- const top =
195- targetRect . top +
196- ( parseInt ( targetStyle . lineHeight , 10 ) - floatingElemRect . height ) / 2 -
197- anchorElementRect . top ;
194+ // BUGFIX: Find the actual scrolling container
195+ // The scrolling element could be anchorElem itself OR its parent, depending on the implementation
196+ // We need to check which element actually has scrollable overflow
197+ function getScrollingElement ( elem : HTMLElement ) : {
198+ element : HTMLElement ;
199+ scrollTop : number ;
200+ scrollLeft : number ;
201+ } {
202+ // Check if the element itself is scrollable
203+ const style = window . getComputedStyle ( elem ) ;
204+ const hasScrollY =
205+ style . overflowY === 'auto' || style . overflowY === 'scroll' ;
206+ const hasScrollX =
207+ style . overflowX === 'auto' || style . overflowX === 'scroll' ;
208+
209+ if ( hasScrollY || hasScrollX ) {
210+ return {
211+ element : elem ,
212+ scrollTop : elem . scrollTop || 0 ,
213+ scrollLeft : elem . scrollLeft || 0 ,
214+ } ;
215+ }
216+
217+ // Check parent element
218+ const parent = elem . parentElement ;
219+ if ( parent ) {
220+ const parentStyle = window . getComputedStyle ( parent ) ;
221+ const parentHasScrollY =
222+ parentStyle . overflowY === 'auto' || parentStyle . overflowY === 'scroll' ;
223+ const parentHasScrollX =
224+ parentStyle . overflowX === 'auto' || parentStyle . overflowX === 'scroll' ;
225+
226+ if ( parentHasScrollY || parentHasScrollX ) {
227+ return {
228+ element : parent ,
229+ scrollTop : parent . scrollTop || 0 ,
230+ scrollLeft : parent . scrollLeft || 0 ,
231+ } ;
232+ }
233+ }
234+
235+ // No scrolling container found
236+ return {
237+ element : elem ,
238+ scrollTop : 0 ,
239+ scrollLeft : 0 ,
240+ } ;
241+ }
242+
243+ const { scrollTop } = getScrollingElement ( anchorElem ) ;
244+
245+ // Calculate position
246+ const lineHeight = parseInt ( targetStyle . lineHeight , 10 ) ;
247+ const topOffset = ( lineHeight - floatingElemRect . height ) / 2 ;
248+
249+ // Original calculation (without scroll compensation)
250+ const topOriginal = targetRect . top + topOffset - anchorElementRect . top ;
251+
252+ // With scroll compensation
253+ const topWithScroll = topOriginal + scrollTop ;
198254
199255 const left = SPACE ;
200256
201257 floatingElem . style . opacity = '1' ;
202- floatingElem . style . transform = `translate(${ left } px, ${ top } px)` ;
258+ floatingElem . style . transform = `translate(${ left } px, ${ topWithScroll } px)` ;
203259}
204260
205261function setDragImage (
@@ -228,14 +284,34 @@ function setTargetLine(
228284 const { top : anchorTop , width : anchorWidth } =
229285 anchorElem . getBoundingClientRect ( ) ;
230286 const { marginTop, marginBottom } = getCollapsedMargins ( targetBlockElem ) ;
287+
288+ // BUGFIX: Get scroll offset using the same logic as setMenuPosition
289+ function getScrollTop ( elem : HTMLElement ) : number {
290+ const style = window . getComputedStyle ( elem ) ;
291+ const hasScroll =
292+ style . overflowY === 'auto' || style . overflowY === 'scroll' ;
293+ if ( hasScroll ) return elem . scrollTop || 0 ;
294+
295+ const parent = elem . parentElement ;
296+ if ( parent ) {
297+ const parentStyle = window . getComputedStyle ( parent ) ;
298+ const parentHasScroll =
299+ parentStyle . overflowY === 'auto' || parentStyle . overflowY === 'scroll' ;
300+ if ( parentHasScroll ) return parent . scrollTop || 0 ;
301+ }
302+ return 0 ;
303+ }
304+
305+ const scrollTop = getScrollTop ( anchorElem ) ;
306+
231307 let lineTop = targetBlockElemTop ;
232308 if ( mouseY >= targetBlockElemTop ) {
233309 lineTop += targetBlockElemHeight + marginBottom / 2 ;
234310 } else {
235311 lineTop -= marginTop / 2 ;
236312 }
237313
238- const top = lineTop - anchorTop - TARGET_LINE_HALF_HEIGHT ;
314+ const top = lineTop - anchorTop - TARGET_LINE_HALF_HEIGHT + scrollTop ;
239315 const left = TEXT_BOX_HORIZONTAL_PADDING - SPACE ;
240316
241317 targetLineElem . style . transform = `translate(${ left } px, ${ top } px)` ;
@@ -265,6 +341,11 @@ function useDraggableBlockMenu(
265341 const [ draggableBlockElem , setDraggableBlockElem ] =
266342 useState < HTMLElement | null > ( null ) ;
267343
344+ // Get theme classes from editor config
345+ const theme = editor . _config . theme ;
346+ const menuThemeClass = theme . draggableBlockMenu || '' ;
347+ const targetLineThemeClass = theme . draggableBlockTargetLine || '' ;
348+
268349 useEffect ( ( ) => {
269350 function onMouseMove ( event : MouseEvent ) {
270351 const target = event . target ;
@@ -286,12 +367,15 @@ function useDraggableBlockMenu(
286367 setDraggableBlockElem ( null ) ;
287368 }
288369
289- scrollerElem ?. addEventListener ( 'mousemove' , onMouseMove ) ;
290- scrollerElem ?. addEventListener ( 'mouseleave' , onMouseLeave ) ;
370+ // BUGFIX: Attach to document instead of scrollerElem to ensure mouse events
371+ // work even after scrolling. The scrollerElem approach fails when content scrolls
372+ // because the mouse event coordinates become misaligned with element positions.
373+ document . addEventListener ( 'mousemove' , onMouseMove ) ;
374+ document . addEventListener ( 'mouseleave' , onMouseLeave ) ;
291375
292376 return ( ) => {
293- scrollerElem ? .removeEventListener ( 'mousemove' , onMouseMove ) ;
294- scrollerElem ? .removeEventListener ( 'mouseleave' , onMouseLeave ) ;
377+ document . removeEventListener ( 'mousemove' , onMouseMove ) ;
378+ document . removeEventListener ( 'mouseleave' , onMouseLeave ) ;
295379 } ;
296380 } , [ scrollerElem , anchorElem , editor ] ) ;
297381
@@ -412,15 +496,18 @@ function useDraggableBlockMenu(
412496 return createPortal (
413497 < >
414498 < div
415- className = " icon draggable-block-menu"
499+ className = { ` icon draggable-block-menu ${ menuThemeClass } ` }
416500 ref = { menuRef }
417501 draggable
418502 onDragStart = { onDragStart }
419503 onDragEnd = { onDragEnd }
420504 >
421505 < div className = { isEditable ? 'icon' : '' } />
422506 </ div >
423- < div className = "draggable-block-target-line" ref = { targetLineRef } />
507+ < div
508+ className = { `draggable-block-target-line ${ targetLineThemeClass } ` }
509+ ref = { targetLineRef }
510+ />
424511 </ > ,
425512 anchorElem ,
426513 ) ;
0 commit comments