Skip to content
148 changes: 115 additions & 33 deletions src/DataGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
assertIsValidKeyGetter,
canExitGrid,
createCellEvent,
getCellStyle,
getColSpan,
getLeftRightKey,
getNextSelectedCellPosition,
Expand Down Expand Up @@ -67,14 +68,14 @@ import {
DataGridDefaultRenderersContext,
useDefaultRenderers
} from './DataGridDefaultRenderersContext';
import DragHandle from './DragHandle';
import EditCell from './EditCell';
import GroupedColumnHeaderRow from './GroupedColumnHeaderRow';
import HeaderRow from './HeaderRow';
import { defaultRenderRow } from './Row';
import type { PartialPosition } from './ScrollToCell';
import ScrollToCell from './ScrollToCell';
import { default as defaultRenderSortStatus } from './sortStatus';
import { cellDragHandleClassname, cellDragHandleFrozenClassname } from './style/cell';
import {
focusSinkClassname,
focusSinkHeaderAndSummaryClassname,
Expand Down Expand Up @@ -332,7 +333,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
);
const [isColumnResizing, setColumnResizing] = useState(false);
const [isDragging, setDragging] = useState(false);
const [draggedOverRowIdx, setOverRowIdx] = useState<number | undefined>(undefined);
const [draggedOverRowIdx, setDraggedOverRowIdx] = useState<number | undefined>(undefined);
const [scrollToPosition, setScrollToPosition] = useState<PartialPosition | null>(null);
const [shouldFocusCell, setShouldFocusCell] = useState(false);
const [previousRowIdx, setPreviousRowIdx] = useState(-1);
Expand Down Expand Up @@ -391,7 +392,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
/**
* refs
*/
const latestDraggedOverRowIdx = useRef(draggedOverRowIdx);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer needed as we are using react event handlers

const focusSinkRef = useRef<HTMLDivElement>(null);

/**
Expand Down Expand Up @@ -506,20 +506,22 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
/**
* callbacks
*/
const setDraggedOverRowIdx = useCallback((rowIdx?: number) => {
setOverRowIdx(rowIdx);
latestDraggedOverRowIdx.current = rowIdx;
}, []);
const focusCellOrCellContent = useCallback(
(shouldScroll = true) => {
const cell = getCellToScroll(gridRef.current!);
if (cell === null) return;

const focusCellOrCellContent = useCallback(() => {
const cell = getCellToScroll(gridRef.current!);
if (cell === null) return;
if (shouldScroll) {
scrollIntoView(cell);
}

scrollIntoView(cell);
// Focus cell content when available instead of the cell itself
const elementToFocus = cell.querySelector<Element & HTMLOrSVGElement>('[tabindex="0"]') ?? cell;
elementToFocus.focus({ preventScroll: true });
}, [gridRef]);
// Focus cell content when available instead of the cell itself
const elementToFocus =
cell.querySelector<Element & HTMLOrSVGElement>('[tabindex="0"]') ?? cell;
elementToFocus.focus({ preventScroll: true });
},
[gridRef]
);

/**
* effects
Expand Down Expand Up @@ -735,6 +737,80 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
}
}

function handleDragHandlePointerDown(event: React.PointerEvent<HTMLDivElement>) {
// keep the focus on the cell
event.preventDefault();
if (event.pointerType === 'mouse' && event.buttons !== 1) {
return;
}
setDragging(true);
event.currentTarget.setPointerCapture(event.pointerId);
}

function handleDragHandlePointerMove(event: React.PointerEvent<HTMLDivElement>) {
// find dragged over row using the pointer position
const gridEl = gridRef.current!;
const headerAndTopSummaryRowsHeight = headerRowsHeight + topSummaryRowsCount * summaryRowHeight;
const offset =
scrollTop -
headerAndTopSummaryRowsHeight +
event.clientY -
gridEl.getBoundingClientRect().top;
Comment on lines +754 to +758
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a better way?

const overRowIdx = findRowIdx(offset);
setDraggedOverRowIdx(overRowIdx);
const ariaRowIndex = headerAndTopSummaryRowsCount + overRowIdx + 1;
const el = gridEl.querySelector(
`:scope > [aria-rowindex="${ariaRowIndex}"] > [aria-colindex="${selectedPosition.idx + 1}"]`
);
scrollIntoView(el);
}

function handleDragHandleLostPointerCapture() {
setDragging(false);
if (draggedOverRowIdx === undefined) return;

const { rowIdx } = selectedPosition;
const [startRowIndex, endRowIndex] =
rowIdx < draggedOverRowIdx
? [rowIdx + 1, draggedOverRowIdx + 1]
: [draggedOverRowIdx, rowIdx];
updateRows(startRowIndex, endRowIndex);
setDraggedOverRowIdx(undefined);
}

function handleDragHandleClick() {
// keep the focus on the cell but do not scroll
focusCellOrCellContent(false);
}

function handleDragHandleDoubleClick(event: React.MouseEvent<HTMLDivElement>) {
event.stopPropagation();
updateRows(selectedPosition.rowIdx + 1, rows.length);
}

function updateRows(startRowIdx: number, endRowIdx: number) {
if (onRowsChange == null) return;

const { rowIdx, idx } = selectedPosition;
const column = columns[idx];
const sourceRow = rows[rowIdx];
const updatedRows = [...rows];
const indexes: number[] = [];
for (let i = startRowIdx; i < endRowIdx; i++) {
if (isCellEditable({ rowIdx: i, idx })) {
const updatedRow = onFill!({ columnKey: column.key, sourceRow, targetRow: rows[i] });
if (updatedRow !== rows[i]) {
updatedRows[i] = updatedRow;
indexes.push(i);
}
}
}

if (indexes.length > 0) {
onRowsChange(updatedRows, { indexes, column });
}
}

/**
* utils
*/
Expand Down Expand Up @@ -890,7 +966,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
return isDraggedOver ? selectedPosition.idx : undefined;
}

function renderDragHandle() {
function getDragHandle() {
if (
onFill == null ||
selectedPosition.mode === 'EDIT' ||
Expand All @@ -905,24 +981,31 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
return;
}

const isLastRow = rowIdx === maxRowIdx;
const columnWidth = getColumnWidth(column);
const colSpan = column.colSpan?.({ type: 'ROW', row: rows[rowIdx] }) ?? 1;
const { insetInlineStart, ...style } = getCellStyle(column, colSpan);
const marginEnd = 'calc(var(--rdg-drag-handle-size) * -0.5 + 1px)';
const isLastColumn = column.idx + colSpan - 1 === maxColIdx;
const dragHandleStyle: React.CSSProperties = {
...style,
gridRowStart: headerAndTopSummaryRowsCount + rowIdx + 1,
marginInlineEnd: isLastColumn ? undefined : marginEnd,
marginBlockEnd: isLastRow ? undefined : marginEnd,
insetInlineStart: insetInlineStart
? `calc(${insetInlineStart} + ${columnWidth}px + var(--rdg-drag-handle-size) * -0.5 - 1px)`
: undefined
};

return (
<DragHandle
gridRowStart={headerAndTopSummaryRowsCount + rowIdx + 1}
rows={rows}
column={column}
columnWidth={columnWidth}
maxColIdx={maxColIdx}
isLastRow={rowIdx === maxRowIdx}
selectedPosition={selectedPosition}
isCellEditable={isCellEditable}
latestDraggedOverRowIdx={latestDraggedOverRowIdx}
onRowsChange={onRowsChange}
onClick={focusCellOrCellContent}
onFill={onFill}
setDragging={setDragging}
setDraggedOverRowIdx={setDraggedOverRowIdx}
<div
style={dragHandleStyle}
className={clsx(cellDragHandleClassname, column.frozen && cellDragHandleFrozenClassname)}
onPointerDown={handleDragHandlePointerDown}
onPointerMove={isDragging ? handleDragHandlePointerMove : undefined}
onLostPointerCapture={isDragging ? handleDragHandleLostPointerCapture : undefined}
onClick={handleDragHandleClick}
onDoubleClick={handleDragHandleDoubleClick}
/>
);
}
Expand Down Expand Up @@ -1055,7 +1138,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
gridRowStart,
selectedCellIdx: selectedRowIdx === rowIdx ? selectedIdx : undefined,
draggedOverCellIdx: getDraggedOverCellIdx(rowIdx),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably add another div and get rid of draggedOverCellIdx

setDraggedOverRowIdx: isDragging ? setDraggedOverRowIdx : undefined,
lastFrozenColumnIndex,
onRowChange: handleFormatterRowChangeLatest,
selectCell: selectCellLatest,
Expand Down Expand Up @@ -1239,7 +1321,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
)}
</DataGridDefaultRenderersContext>

{renderDragHandle()}
{getDragHandle()}

{/* render empty cells that span only 1 column so we can safely measure column widths, regardless of colSpan */}
{renderMeasuringCells(viewportColumns)}
Expand Down
151 changes: 0 additions & 151 deletions src/DragHandle.tsx

This file was deleted.

Loading