1- import {
2- useCallback ,
3- useImperativeHandle ,
4- useLayoutEffect ,
5- useMemo ,
6- useRef ,
7- useState
8- } from 'react' ;
1+ import { useCallback , useImperativeHandle , useLayoutEffect , useMemo , useState } from 'react' ;
92import type { Key , KeyboardEvent } from 'react' ;
103import { flushSync } from 'react-dom' ;
114
@@ -76,13 +69,7 @@ import type { PartialPosition } from './ScrollToCell';
7669import ScrollToCell from './ScrollToCell' ;
7770import { default as defaultRenderSortStatus } from './sortStatus' ;
7871import { cellDragHandleClassname , cellDragHandleFrozenClassname } from './style/cell' ;
79- import {
80- focusSinkClassname ,
81- focusSinkHeaderAndSummaryClassname ,
82- rootClassname ,
83- viewportDraggingClassname
84- } from './style/core' ;
85- import { rowSelected , rowSelectedWithFrozenCell } from './style/row' ;
72+ import { rootClassname , viewportDraggingClassname } from './style/core' ;
8673import SummaryRow from './SummaryRow' ;
8774
8875export interface SelectCellState extends Position {
@@ -386,11 +373,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
386373 ( ) : SelectCellState | EditCellState < R > => ( { idx : - 1 , rowIdx : minRowIdx - 1 , mode : 'SELECT' } )
387374 ) ;
388375
389- /**
390- * refs
391- */
392- const focusSinkRef = useRef < HTMLDivElement > ( null ) ;
393-
394376 /**
395377 * computed values
396378 */
@@ -499,37 +481,19 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
499481 const selectCellLatest = useLatestFunc ( selectCell ) ;
500482 const selectHeaderCellLatest = useLatestFunc ( selectHeaderCell ) ;
501483
502- /**
503- * callbacks
504- */
505- const focusCell = useCallback (
506- ( shouldScroll = true ) => {
507- const cell = getCellToScroll ( gridRef . current ! ) ;
508- if ( cell === null ) return ;
509-
510- if ( shouldScroll ) {
511- scrollIntoView ( cell ) ;
512- }
513-
514- cell . focus ( { preventScroll : true } ) ;
515- } ,
516- [ gridRef ]
517- ) ;
518-
519484 /**
520485 * effects
521486 */
522487 useLayoutEffect ( ( ) => {
523488 if ( shouldFocusCell ) {
524- if ( focusSinkRef . current !== null && selectedPosition . idx === - 1 ) {
525- focusSinkRef . current . focus ( { preventScroll : true } ) ;
526- scrollIntoView ( focusSinkRef . current ) ;
489+ if ( selectedPosition . idx === - 1 ) {
490+ focusRow ( gridRef . current ! ) ;
527491 } else {
528- focusCell ( ) ;
492+ focusCell ( gridRef . current ! ) ;
529493 }
530494 setShouldFocusCell ( false ) ;
531495 }
532- } , [ shouldFocusCell , focusCell , selectedPosition . idx ] ) ;
496+ } , [ shouldFocusCell , selectedPosition . idx , gridRef ] ) ;
533497
534498 useImperativeHandle (
535499 ref ,
@@ -632,9 +596,13 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
632596 if ( cellEvent . isGridDefaultPrevented ( ) ) return ;
633597 }
634598
635- if ( ! ( event . target instanceof Element ) ) return ;
636- const isCellEvent = event . target . closest ( '.rdg-cell' ) !== null ;
637- const isRowEvent = isTreeGrid && event . target === focusSinkRef . current ;
599+ const { target } = event ;
600+
601+ if ( ! ( target instanceof Element ) ) return ;
602+
603+ const isCellEvent = target . closest ( '.rdg-cell' ) !== null ;
604+ const isRowEvent = isTreeGrid && target . role === 'row' ;
605+
638606 if ( ! isCellEvent && ! isRowEvent ) return ;
639607
640608 switch ( event . key ) {
@@ -734,7 +702,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
734702 function handleDragHandlePointerDown ( event : React . PointerEvent < HTMLDivElement > ) {
735703 // keep the focus on the cell
736704 event . preventDefault ( ) ;
737- if ( event . pointerType === 'mouse' && event . buttons !== 1 ) {
705+ if ( event . pointerType === 'mouse' && event . button !== 0 ) {
738706 return ;
739707 }
740708 setDragging ( true ) ;
@@ -774,7 +742,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
774742
775743 function handleDragHandleClick ( ) {
776744 // keep the focus on the cell but do not scroll
777- focusCell ( false ) ;
745+ focusCell ( gridRef . current ! , false ) ;
778746 }
779747
780748 function handleDragHandleDoubleClick ( event : React . MouseEvent < HTMLDivElement > ) {
@@ -870,20 +838,32 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
870838 const isRowSelected = selectedCellIsWithinSelectionBounds && idx === - 1 ;
871839
872840 switch ( key ) {
873- case 'ArrowUp' :
874- return { idx, rowIdx : rowIdx - 1 } ;
841+ case 'ArrowUp' : {
842+ const nextRowIdx = rowIdx - 1 ;
843+ return {
844+ // avoid selecting header rows
845+ idx : idx === - 1 && nextRowIdx < - topSummaryRowsCount ? 0 : idx ,
846+ rowIdx : nextRowIdx
847+ } ;
848+ }
875849 case 'ArrowDown' :
876850 return { idx, rowIdx : rowIdx + 1 } ;
877- case leftKey :
878- return { idx : idx - 1 , rowIdx } ;
851+ case leftKey : {
852+ const nextIdx = idx - 1 ;
853+ return {
854+ // avoid selecting header rows
855+ idx : rowIdx < - topSummaryRowsCount && nextIdx < 0 ? 0 : nextIdx ,
856+ rowIdx
857+ } ;
858+ }
879859 case rightKey :
880860 return { idx : idx + 1 , rowIdx } ;
881861 case 'Tab' :
882862 return { idx : idx + ( shiftKey ? - 1 : 1 ) , rowIdx } ;
883863 case 'Home' :
884- // If row is selected then move focus to the first row
885- if ( isRowSelected ) return { idx, rowIdx : minRowIdx } ;
886- return { idx : 0 , rowIdx : ctrlKey ? minRowIdx : rowIdx } ;
864+ // If row is selected then move focus to the first header row's cell.
865+ if ( isRowSelected || ctrlKey ) return { idx : 0 , rowIdx : minRowIdx } ;
866+ return { idx : 0 , rowIdx } ;
887867 case 'End' :
888868 // If row is selected then move focus to the last row.
889869 if ( isRowSelected ) return { idx, rowIdx : maxRowIdx } ;
@@ -1149,7 +1129,8 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
11491129 lastFrozenColumnIndex,
11501130 onRowChange : handleFormatterRowChangeLatest ,
11511131 selectCell : selectCellLatest ,
1152- selectedCellEditor : getCellEditor ( rowIdx )
1132+ selectedCellEditor : getCellEditor ( rowIdx ) ,
1133+ isTreeGrid
11531134 } )
11541135 ) ;
11551136 }
@@ -1179,9 +1160,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
11791160 templateRows += ` repeat(${ bottomSummaryRowsCount } , ${ summaryRowHeight } px)` ;
11801161 }
11811162
1182- const isGroupRowFocused =
1183- selectedPosition . idx === - 1 && selectedPosition . rowIdx !== minRowIdx - 1 ;
1184-
11851163 return (
11861164 < div
11871165 role = { role }
@@ -1204,18 +1182,10 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
12041182 ) }
12051183 style = { {
12061184 ...style ,
1207- // set scrollPadding to correctly position non-sticky cells after scrolling
1208- scrollPaddingInlineStart :
1209- selectedPosition . idx > lastFrozenColumnIndex || scrollToPosition ?. idx !== undefined
1210- ? `${ totalFrozenColumnWidth } px`
1211- : undefined ,
1212- scrollPaddingBlock :
1213- isRowIdxWithinViewportBounds ( selectedPosition . rowIdx ) ||
1214- scrollToPosition ?. rowIdx !== undefined
1215- ? `${ headerRowsHeight + topSummaryRowsCount * summaryRowHeight } px ${
1216- bottomSummaryRowsCount * summaryRowHeight
1217- } px`
1218- : undefined ,
1185+ // set scrollPadding to correctly scroll to non-sticky cells/rows
1186+ scrollPaddingInlineStart : totalFrozenColumnWidth ,
1187+ scrollPaddingBlockStart : headerRowsHeight + topSummaryRowsCount * summaryRowHeight ,
1188+ scrollPaddingBlockEnd : bottomSummaryRowsCount * summaryRowHeight ,
12191189 gridTemplateColumns,
12201190 gridTemplateRows : templateRows ,
12211191 '--rdg-header-row-height' : `${ headerRowHeight } px` ,
@@ -1289,6 +1259,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
12891259 selectedCellIdx = { isSummaryRowSelected ? selectedPosition . idx : undefined }
12901260 isTop
12911261 selectCell = { selectCellLatest }
1262+ isTreeGrid = { isTreeGrid }
12921263 />
12931264 ) ;
12941265 } ) }
@@ -1322,6 +1293,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
13221293 selectedCellIdx = { isSummaryRowSelected ? selectedPosition . idx : undefined }
13231294 isTop = { false }
13241295 selectCell = { selectCellLatest }
1296+ isTreeGrid = { isTreeGrid }
13251297 />
13261298 ) ;
13271299 } ) }
@@ -1334,24 +1306,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
13341306 { /* render empty cells that span only 1 column so we can safely measure column widths, regardless of colSpan */ }
13351307 { renderMeasuringCells ( viewportColumns ) }
13361308
1337- { /* extra div is needed for row navigation in a treegrid */ }
1338- { isTreeGrid && (
1339- < div
1340- ref = { focusSinkRef }
1341- tabIndex = { isGroupRowFocused ? 0 : - 1 }
1342- className = { classnames ( focusSinkClassname , {
1343- [ focusSinkHeaderAndSummaryClassname ] : ! isRowIdxWithinViewportBounds (
1344- selectedPosition . rowIdx
1345- ) ,
1346- [ rowSelected ] : isGroupRowFocused ,
1347- [ rowSelectedWithFrozenCell ] : isGroupRowFocused && lastFrozenColumnIndex !== - 1
1348- } ) }
1349- style = { {
1350- gridRowStart : selectedPosition . rowIdx + headerAndTopSummaryRowsCount + 1
1351- } }
1352- />
1353- ) }
1354-
13551309 { scrollToPosition !== null && (
13561310 < ScrollToCell
13571311 scrollToPosition = { scrollToPosition }
@@ -1363,10 +1317,32 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
13631317 ) ;
13641318}
13651319
1320+ function getRowToScroll ( gridEl : HTMLDivElement ) {
1321+ return gridEl . querySelector < HTMLDivElement > ( ':scope > [role="row"][tabindex="0"]' ) ;
1322+ }
1323+
13661324function getCellToScroll ( gridEl : HTMLDivElement ) {
13671325 return gridEl . querySelector < HTMLDivElement > ( ':scope > [role="row"] > [tabindex="0"]' ) ;
13681326}
13691327
13701328function isSamePosition ( p1 : Position , p2 : Position ) {
13711329 return p1 . idx === p2 . idx && p1 . rowIdx === p2 . rowIdx ;
13721330}
1331+
1332+ function focusElement ( element : HTMLDivElement | null , shouldScroll : boolean ) {
1333+ if ( element === null ) return ;
1334+
1335+ if ( shouldScroll ) {
1336+ scrollIntoView ( element ) ;
1337+ }
1338+
1339+ element . focus ( { preventScroll : true } ) ;
1340+ }
1341+
1342+ function focusRow ( gridEl : HTMLDivElement ) {
1343+ focusElement ( getRowToScroll ( gridEl ) , true ) ;
1344+ }
1345+
1346+ function focusCell ( gridEl : HTMLDivElement , shouldScroll = true ) {
1347+ focusElement ( getCellToScroll ( gridEl ) , shouldScroll ) ;
1348+ }
0 commit comments