@@ -397,6 +397,21 @@ export function useGridCellSelection<R extends GridRowModel>(params: UseGridCell
397397 return field === undefined ? null : { id, field } ;
398398 } , [ ] ) ;
399399
400+ /** The row whose band holds `centerY` (px from the top of the unpinned rows), with `field`. */
401+ const headRowAtOffset = useCallback ( ( centerY : number , field : string ) : GridCellCoordinates | null => {
402+ const { context : ctx , layout, pinnedTopRowCount } = latestRef . current ;
403+ const heights = layout . cumulativeHeights ;
404+ if ( ! ctx || heights . length === 0 || centerY < 0 ) return null ;
405+ let lo = 0 ;
406+ let hi = heights . length - 1 ;
407+ while ( lo < hi ) {
408+ const mid = ( lo + hi ) >> 1 ;
409+ if ( heights [ mid ] > centerY ) hi = mid ; else lo = mid + 1 ;
410+ }
411+ const id = ctx . rowIds [ lo + pinnedTopRowCount ] ;
412+ return id === undefined ? null : { id, field } ;
413+ } , [ ] ) ;
414+
400415 // The auto-scroll loop schedules the next frame through this ref: a callback cannot name itself.
401416 const runDragFrameRef = useRef < ( allowScroll : boolean ) => void > ( ( ) => { } ) ;
402417 const runDragFrame = useCallback ( ( allowScroll : boolean ) => {
@@ -421,7 +436,14 @@ export function useGridCellSelection<R extends GridRowModel>(params: UseGridCell
421436 const x = clamp ( drag . x , box . left + 1 , box . left + width - 2 ) ;
422437 let y = drag . y ;
423438 if ( y < box . top || y > box . top + height || y < headerBottom ) y = clamp ( y , bodyTop + 1 , Math . max ( bodyTop + 1 , bodyBottom - 2 ) ) ;
424- const head = headAtPoint ( viewport , x , y ) ;
439+ // During fast auto-scroll the rows under the pointer may not be rendered yet (virtualization
440+ // lags a frame or more on a slow machine), so the hit test finds no cell. Work out the row from
441+ // the scroll position and the row heights instead, keeping the column the head already has.
442+ const canHitTest = typeof viewport . ownerDocument . elementFromPoint === 'function' ;
443+ const hit = headAtPoint ( viewport , x , y ) ;
444+ const head = hit ?? ( canHitTest && drag . moved
445+ ? headRowAtOffset ( y - box . top - ( stickyTop ? stickyTop . getBoundingClientRect ( ) . height : 0 ) + viewport . scrollTop , drag . head . field )
446+ : null ) ;
425447 if ( head && ! isSameCoordinates ( head , drag . head ) ) {
426448 drag . head = head ;
427449 if ( ! drag . moved && ! isSameCoordinates ( head , drag . anchor ) ) {
@@ -448,7 +470,7 @@ export function useGridCellSelection<R extends GridRowModel>(params: UseGridCell
448470 if ( canScrollX ) viewport . scrollLeft += dx ;
449471 // Keep going while the pointer rests in the edge zone; the next frame picks the new head.
450472 drag . frame = requestAnimationFrame ( ( ) => runDragFrameRef . current ( true ) ) ;
451- } , [ headAtPoint , setRange ] ) ;
473+ } , [ headAtPoint , headRowAtOffset , setRange ] ) ;
452474 useLayoutEffect ( ( ) => {
453475 runDragFrameRef . current = runDragFrame ;
454476 } , [ runDragFrame ] ) ;
0 commit comments