1- import { useCallback , useImperativeHandle , useLayoutEffect , useMemo , useState } from 'react' ;
1+ import { useCallback , useImperativeHandle , useMemo , useRef , useState } from 'react' ;
22import type { Key , KeyboardEvent } from 'react' ;
33import { flushSync } from 'react-dom' ;
44
@@ -11,17 +11,22 @@ import {
1111 useColumnWidths ,
1212 useGridDimensions ,
1313 useLatestFunc ,
14+ useScrollState ,
15+ useScrollToPosition ,
1416 useViewportColumns ,
1517 useViewportRows ,
16- type HeaderRowSelectionContextValue
18+ type ActivePosition ,
19+ type HeaderRowSelectionContextValue ,
20+ type PartialPosition
1721} from './hooks' ;
1822import {
19- abs ,
2023 assertIsValidKeyGetter ,
2124 canExitGrid ,
2225 classnames ,
2326 createCellEvent ,
27+ focusCell ,
2428 getCellStyle ,
29+ getCellToScroll ,
2530 getColSpan ,
2631 getLeftRightKey ,
2732 getNextActivePosition ,
@@ -65,8 +70,6 @@ import EditCell from './EditCell';
6570import GroupedColumnHeaderRow from './GroupedColumnHeaderRow' ;
6671import HeaderRow from './HeaderRow' ;
6772import { defaultRenderRow } from './Row' ;
68- import type { PartialPosition } from './ScrollToCell' ;
69- import ScrollToCell from './ScrollToCell' ;
7073import { default as defaultRenderSortStatus } from './sortStatus' ;
7174import { cellDragHandleClassname , cellDragHandleFrozenClassname } from './style/cell' ;
7275import {
@@ -105,6 +108,7 @@ type SharedDivProps = Pick<
105108 | 'aria-rowcount'
106109 | 'className'
107110 | 'style'
111+ | 'onScroll'
108112> ;
109113
110114export interface DataGridProps < R , SR = unknown , K extends Key = Key > extends SharedDivProps {
@@ -189,8 +193,6 @@ export interface DataGridProps<R, SR = unknown, K extends Key = Key> extends Sha
189193 > ;
190194 /** Function called whenever the active position is changed */
191195 onActivePositionChange ?: Maybe < ( args : PositionChangeArgs < NoInfer < R > , NoInfer < SR > > ) => void > ;
192- /** Callback triggered when the grid is scrolled */
193- onScroll ?: Maybe < ( event : React . UIEvent < HTMLDivElement > ) => void > ;
194196 /** Callback triggered when column is resized */
195197 onColumnResize ?: Maybe < ( column : CalculatedColumn < R , SR > , width : number ) => void > ;
196198 /** Callback triggered when columns are reordered */
@@ -302,19 +304,22 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
302304 const enableVirtualization = rawEnableVirtualization ?? true ;
303305 const direction = rawDirection ?? 'ltr' ;
304306
307+ /**
308+ * ref
309+ */
310+ const gridRef = useRef < HTMLDivElement > ( null ) ;
311+
305312 /**
306313 * states
307314 */
308- const [ scrollTop , setScrollTop ] = useState ( 0 ) ;
309- const [ scrollLeft , setScrollLeft ] = useState ( 0 ) ;
315+ const { scrollTop, scrollLeft } = useScrollState ( gridRef ) ;
316+ const [ gridWidth , gridHeight ] = useGridDimensions ( { gridRef } ) ;
310317 const [ columnWidthsInternal , setColumnWidthsInternal ] = useState (
311318 ( ) : ColumnWidths => columnWidthsRaw ?? new Map ( )
312319 ) ;
313320 const [ isColumnResizing , setIsColumnResizing ] = useState ( false ) ;
314321 const [ isDragging , setIsDragging ] = useState ( false ) ;
315322 const [ draggedOverRowIdx , setDraggedOverRowIdx ] = useState < number | undefined > ( undefined ) ;
316- const [ scrollToPosition , setScrollToPosition ] = useState < PartialPosition | null > ( null ) ;
317- const [ shouldFocusPosition , setShouldFocusPosition ] = useState ( false ) ;
318323 const [ previousRowIdx , setPreviousRowIdx ] = useState ( - 1 ) ;
319324
320325 const isColumnWidthsControlled =
@@ -335,7 +340,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
335340 [ columnWidths ]
336341 ) ;
337342
338- const [ gridRef , gridWidth , gridHeight ] = useGridDimensions ( ) ;
339343 const {
340344 columns,
341345 colSpanColumns,
@@ -382,6 +386,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
382386 const {
383387 activePosition,
384388 setActivePosition,
389+ setPositionToFocus,
385390 activePositionIsInActiveBounds,
386391 activePositionIsInViewport,
387392 activePositionIsRow,
@@ -390,15 +395,16 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
390395 getActiveColumn,
391396 getActiveRow
392397 } = useActivePosition < R , SR > ( {
398+ gridRef,
393399 columns,
394400 rows,
395401 isTreeGrid,
396402 maxColIdx,
397403 minRowIdx,
398404 maxRowIdx,
399- setDraggedOverRowIdx,
400- setShouldFocusPosition
405+ setDraggedOverRowIdx
401406 } ) ;
407+ const { setScrollToPosition, scrollToPositionElement } = useScrollToPosition ( { gridRef } ) ;
402408
403409 const defaultGridComponents = useMemo (
404410 ( ) => ( {
@@ -495,20 +501,8 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
495501 const selectHeaderCellLatest = useLatestFunc ( selectHeaderCell ) ;
496502
497503 /**
498- * effects
504+ * Misc hooks
499505 */
500- useLayoutEffect ( ( ) => {
501- if ( shouldFocusPosition ) {
502- if ( activePositionIsRow ) {
503- focusRow ( gridRef . current ! ) ;
504- } else {
505- focusCell ( gridRef . current ! ) ;
506- }
507- // eslint-disable-next-line react-hooks/set-state-in-effect
508- setShouldFocusPosition ( false ) ;
509- }
510- } , [ shouldFocusPosition , activePositionIsRow , gridRef ] ) ;
511-
512506 useImperativeHandle (
513507 ref ,
514508 ( ) : DataGridHandle => ( {
@@ -636,16 +630,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
636630 }
637631 }
638632
639- function handleScroll ( event : React . UIEvent < HTMLDivElement > ) {
640- const { scrollTop, scrollLeft } = event . currentTarget ;
641- flushSync ( ( ) => {
642- setScrollTop ( scrollTop ) ;
643- // scrollLeft is nagative when direction is rtl
644- setScrollLeft ( abs ( scrollLeft ) ) ;
645- } ) ;
646- onScroll ?.( event ) ;
647- }
648-
649633 function updateRow ( column : CalculatedColumn < R , SR > , rowIdx : number , row : R ) {
650634 if ( typeof onRowsChange !== 'function' ) return ;
651635 if ( row === rows [ rowIdx ] ) return ;
@@ -810,8 +794,11 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
810794 // Avoid re-renders if the selected cell state is the same
811795 scrollIntoView ( getCellToScroll ( gridRef . current ! ) ) ;
812796 } else {
813- setShouldFocusPosition ( options ?. shouldFocus === true ) ;
814- setActivePosition ( { ...position , mode : 'ACTIVE' } ) ;
797+ const newPosition : ActivePosition = { ...position , mode : 'ACTIVE' } ;
798+ setActivePosition ( newPosition ) ;
799+ if ( options ?. shouldFocus ) {
800+ setPositionToFocus ( newPosition ) ;
801+ }
815802 }
816803
817804 if ( onActivePositionChange && ! samePosition ) {
@@ -994,8 +981,11 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
994981 const colSpan = getColSpan ( column , lastFrozenColumnIndex , { type : 'ROW' , row } ) ;
995982
996983 function closeEditor ( shouldFocus : boolean ) {
997- setShouldFocusPosition ( shouldFocus ) ;
998- setActivePosition ( ( { idx, rowIdx } ) => ( { idx, rowIdx, mode : 'ACTIVE' } ) ) ;
984+ const newPosition : ActivePosition = { idx : activePosition . idx , rowIdx, mode : 'ACTIVE' } ;
985+ setActivePosition ( newPosition ) ;
986+ if ( shouldFocus ) {
987+ setPositionToFocus ( newPosition ) ;
988+ }
999989 }
1000990
1001991 function onRowChange ( row : R , commitChanges : boolean , shouldFocus : boolean ) {
@@ -1133,7 +1123,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
11331123 } }
11341124 dir = { direction }
11351125 ref = { gridRef }
1136- onScroll = { handleScroll }
1126+ onScroll = { onScroll }
11371127 onKeyDown = { handleKeyDown }
11381128 onCopy = { handleCellCopy }
11391129 onPaste = { handleCellPaste }
@@ -1283,43 +1273,11 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
12831273 { /* render empty cells that span only 1 column so we can safely measure column widths, regardless of colSpan */ }
12841274 { renderMeasuringCells ( viewportColumns ) }
12851275
1286- { scrollToPosition !== null && (
1287- < ScrollToCell
1288- scrollToPosition = { scrollToPosition }
1289- setScrollToCellPosition = { setScrollToPosition }
1290- gridRef = { gridRef }
1291- />
1292- ) }
1276+ { scrollToPositionElement }
12931277 </ div >
12941278 ) ;
12951279}
12961280
1297- function getRowToScroll ( gridEl : HTMLDivElement ) {
1298- return gridEl . querySelector < HTMLDivElement > ( '& > [role="row"][tabindex="0"]' ) ;
1299- }
1300-
1301- function getCellToScroll ( gridEl : HTMLDivElement ) {
1302- return gridEl . querySelector < HTMLDivElement > ( '& > [role="row"] > [tabindex="0"]' ) ;
1303- }
1304-
13051281function isSamePosition ( p1 : Position , p2 : Position ) {
13061282 return p1 . idx === p2 . idx && p1 . rowIdx === p2 . rowIdx ;
13071283}
1308-
1309- function focusElement ( element : HTMLDivElement | null , shouldScroll : boolean ) {
1310- if ( element === null ) return ;
1311-
1312- if ( shouldScroll ) {
1313- scrollIntoView ( element ) ;
1314- }
1315-
1316- element . focus ( { preventScroll : true } ) ;
1317- }
1318-
1319- function focusRow ( gridEl : HTMLDivElement ) {
1320- focusElement ( getRowToScroll ( gridEl ) , true ) ;
1321- }
1322-
1323- function focusCell ( gridEl : HTMLDivElement , shouldScroll = true ) {
1324- focusElement ( getCellToScroll ( gridEl ) , shouldScroll ) ;
1325- }
0 commit comments