Skip to content

Commit aa99284

Browse files
authored
Merge branch 'main' into am-accessible-resizing
2 parents f185b0a + ba6c8ea commit aa99284

11 files changed

Lines changed: 256 additions & 301 deletions

File tree

src/Cell.tsx

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,9 @@ import { useRovingTabIndex } from './hooks';
55
import { createCellEvent, getCellClassname, getCellStyle, isCellEditableUtil } from './utils';
66
import type { CellRendererProps } from './types';
77

8-
const cellCopied = css`
9-
@layer rdg.Cell {
10-
background-color: #ccccff;
11-
}
12-
`;
13-
14-
const cellCopiedClassname = `rdg-cell-copied ${cellCopied}`;
15-
168
const cellDraggedOver = css`
179
@layer rdg.Cell {
1810
background-color: #ccccff;
19-
20-
&.${cellCopied} {
21-
background-color: #9999ff;
22-
}
2311
}
2412
`;
2513

@@ -29,7 +17,6 @@ function Cell<R, SR>({
2917
column,
3018
colSpan,
3119
isCellSelected,
32-
isCopied,
3320
isDraggedOver,
3421
row,
3522
rowIdx,
@@ -48,7 +35,6 @@ function Cell<R, SR>({
4835
className = getCellClassname(
4936
column,
5037
{
51-
[cellCopiedClassname]: isCopied,
5238
[cellDraggedOverClassname]: isDraggedOver
5339
},
5440
typeof cellClass === 'function' ? cellClass(row) : cellClass,

src/DataGrid.tsx

Lines changed: 23 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,19 @@ import {
3939
import type {
4040
CalculatedColumn,
4141
CellClickArgs,
42+
CellClipboardEvent,
43+
CellCopyEvent,
4244
CellKeyboardEvent,
4345
CellKeyDownArgs,
4446
CellMouseEvent,
4547
CellNavigationMode,
48+
CellPasteEvent,
4649
CellSelectArgs,
4750
Column,
4851
ColumnOrColumnGroup,
49-
CopyEvent,
5052
Direction,
5153
FillEvent,
5254
Maybe,
53-
PasteEvent,
5455
Position,
5556
Renderers,
5657
RowsChangeData,
@@ -175,8 +176,6 @@ export interface DataGridProps<R, SR = unknown, K extends Key = Key> extends Sha
175176
onSortColumnsChange?: Maybe<(sortColumns: SortColumn[]) => void>;
176177
defaultColumnOptions?: Maybe<DefaultColumnOptions<NoInfer<R>, NoInfer<SR>>>;
177178
onFill?: Maybe<(event: FillEvent<NoInfer<R>>) => NoInfer<R>>;
178-
onCopy?: Maybe<(event: CopyEvent<NoInfer<R>>) => void>;
179-
onPaste?: Maybe<(event: PasteEvent<NoInfer<R>>) => NoInfer<R>>;
180179

181180
/**
182181
* Event props
@@ -196,6 +195,12 @@ export interface DataGridProps<R, SR = unknown, K extends Key = Key> extends Sha
196195
onCellKeyDown?: Maybe<
197196
(args: CellKeyDownArgs<NoInfer<R>, NoInfer<SR>>, event: CellKeyboardEvent) => void
198197
>;
198+
onCellCopy?: Maybe<
199+
(args: CellCopyEvent<NoInfer<R>, NoInfer<SR>>, event: CellClipboardEvent) => void
200+
>;
201+
onCellPaste?: Maybe<
202+
(args: CellPasteEvent<NoInfer<R>, NoInfer<SR>>, event: CellClipboardEvent) => NoInfer<R>
203+
>;
199204
/** Function called whenever cell selection is changed */
200205
onSelectedCellChange?: Maybe<(args: CellSelectArgs<NoInfer<R>, NoInfer<SR>>) => void>;
201206
/** Called when the grid is scrolled */
@@ -260,8 +265,8 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
260265
onColumnResize,
261266
onColumnsReorder,
262267
onFill,
263-
onCopy,
264-
onPaste,
268+
onCellCopy,
269+
onCellPaste,
265270
// Toggles and modes
266271
enableVirtualization: rawEnableVirtualization,
267272
// Miscellaneous
@@ -310,7 +315,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
310315
const [measuredColumnWidths, setMeasuredColumnWidths] = useState(
311316
(): ReadonlyMap<string, number> => new Map()
312317
);
313-
const [copiedCell, setCopiedCell] = useState<{ row: R; columnKey: string } | null>(null);
314318
const [isDragging, setDragging] = useState(false);
315319
const [draggedOverRowIdx, setOverRowIdx] = useState<number | undefined>(undefined);
316320
const [scrollToPosition, setScrollToPosition] = useState<PartialPosition | null>(null);
@@ -611,39 +615,13 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
611615
);
612616
if (cellEvent.isGridDefaultPrevented()) return;
613617
}
618+
614619
if (!(event.target instanceof Element)) return;
615620
const isCellEvent = event.target.closest('.rdg-cell') !== null;
616621
const isRowEvent = isTreeGrid && event.target === focusSinkRef.current;
617622
if (!isCellEvent && !isRowEvent) return;
618623

619-
// eslint-disable-next-line @typescript-eslint/no-deprecated
620-
const { keyCode } = event;
621-
622-
if (
623-
selectedCellIsWithinViewportBounds &&
624-
(onPaste != null || onCopy != null) &&
625-
isCtrlKeyHeldDown(event)
626-
) {
627-
// event.key may differ by keyboard input language, so we use event.keyCode instead
628-
// event.nativeEvent.code cannot be used either as it would break copy/paste for the DVORAK layout
629-
const cKey = 67;
630-
const vKey = 86;
631-
if (keyCode === cKey) {
632-
// copy highlighted text only
633-
if (window.getSelection()?.isCollapsed === false) return;
634-
handleCopy();
635-
return;
636-
}
637-
if (keyCode === vKey) {
638-
handlePaste();
639-
return;
640-
}
641-
}
642-
643624
switch (event.key) {
644-
case 'Escape':
645-
setCopiedCell(null);
646-
return;
647625
case 'ArrowUp':
648626
case 'ArrowDown':
649627
case 'ArrowLeft':
@@ -687,31 +665,21 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
687665
updateRow(columns[selectedPosition.idx], selectedPosition.rowIdx, selectedPosition.row);
688666
}
689667

690-
function handleCopy() {
668+
function handleCellCopy(event: CellClipboardEvent) {
669+
if (!selectedCellIsWithinViewportBounds) return;
691670
const { idx, rowIdx } = selectedPosition;
692-
const sourceRow = rows[rowIdx];
693-
const sourceColumnKey = columns[idx].key;
694-
setCopiedCell({ row: sourceRow, columnKey: sourceColumnKey });
695-
onCopy?.({ sourceRow, sourceColumnKey });
671+
onCellCopy?.({ row: rows[rowIdx], column: columns[idx] }, event);
696672
}
697673

698-
function handlePaste() {
699-
if (!onPaste || !onRowsChange || copiedCell === null || !isCellEditable(selectedPosition)) {
674+
function handleCellPaste(event: CellClipboardEvent) {
675+
if (!onCellPaste || !onRowsChange || !isCellEditable(selectedPosition)) {
700676
return;
701677
}
702678

703679
const { idx, rowIdx } = selectedPosition;
704-
const targetColumn = columns[idx];
705-
const targetRow = rows[rowIdx];
706-
707-
const updatedTargetRow = onPaste({
708-
sourceRow: copiedCell.row,
709-
sourceColumnKey: copiedCell.columnKey,
710-
targetRow,
711-
targetColumnKey: targetColumn.key
712-
});
713-
714-
updateRow(targetColumn, rowIdx, updatedTargetRow);
680+
const column = columns[idx];
681+
const updatedRow = onCellPaste({ row: rows[rowIdx], column }, event);
682+
updateRow(column, rowIdx, updatedRow);
715683
}
716684

717685
function handleCellInput(event: KeyboardEvent<HTMLDivElement>) {
@@ -729,7 +697,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
729697
return;
730698
}
731699

732-
if (isCellEditable(selectedPosition) && isDefaultCellInput(event)) {
700+
if (isCellEditable(selectedPosition) && isDefaultCellInput(event, onCellPaste != null)) {
733701
setSelectedPosition(({ idx, rowIdx }) => ({
734702
idx,
735703
rowIdx,
@@ -1054,11 +1022,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
10541022
onCellContextMenu: onCellContextMenuLatest,
10551023
rowClass,
10561024
gridRowStart,
1057-
copiedCellIdx:
1058-
copiedCell !== null && copiedCell.row === row
1059-
? columns.findIndex((c) => c.key === copiedCell.columnKey)
1060-
: undefined,
1061-
10621025
selectedCellIdx: selectedRowIdx === rowIdx ? selectedIdx : undefined,
10631026
draggedOverCellIdx: getDraggedOverCellIdx(rowIdx),
10641027
setDraggedOverRowIdx: isDragging ? setDraggedOverRowIdx : undefined,
@@ -1138,6 +1101,8 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
11381101
ref={gridRef}
11391102
onScroll={handleScroll}
11401103
onKeyDown={handleKeyDown}
1104+
onCopy={handleCellCopy}
1105+
onPaste={handleCellPaste}
11411106
data-testid={testId}
11421107
data-cy={dataCy}
11431108
>

src/Row.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ function Row<R, SR>({
1414
selectedCellIdx,
1515
isRowSelectionDisabled,
1616
isRowSelected,
17-
copiedCellIdx,
1817
draggedOverCellIdx,
1918
lastFrozenColumnIndex,
2019
row,
@@ -72,7 +71,6 @@ function Row<R, SR>({
7271
colSpan,
7372
row,
7473
rowIdx,
75-
isCopied: copiedCellIdx === idx,
7674
isDraggedOver: draggedOverCellIdx === idx,
7775
isCellSelected,
7876
onClick: onCellClick,

src/TreeDataGrid.tsx

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ import { useCallback, useMemo } from 'react';
22
import type { Key } from 'react';
33

44
import { useLatestFunc } from './hooks';
5-
import { assertIsValidKeyGetter, isCtrlKeyHeldDown } from './utils';
5+
import { assertIsValidKeyGetter } from './utils';
66
import type {
7+
CellClipboardEvent,
8+
CellCopyEvent,
79
CellKeyboardEvent,
810
CellKeyDownArgs,
11+
CellPasteEvent,
912
Column,
1013
GroupRow,
1114
Maybe,
@@ -53,6 +56,8 @@ export function TreeDataGrid<R, SR = unknown, K extends Key = Key>({
5356
rowHeight: rawRowHeight,
5457
rowKeyGetter: rawRowKeyGetter,
5558
onCellKeyDown: rawOnCellKeyDown,
59+
onCellCopy: rawOnCellCopy,
60+
onCellPaste: rawOnCellPaste,
5661
onRowsChange,
5762
selectedRows: rawSelectedRows,
5863
onSelectedRowsChange: rawOnSelectedRowsChange,
@@ -319,14 +324,25 @@ export function TreeDataGrid<R, SR = unknown, K extends Key = Key>({
319324
selectCell({ idx, rowIdx: parentRowAndIndex[1] });
320325
}
321326
}
327+
}
322328

323-
// Prevent copy/paste on group rows
324-
// eslint-disable-next-line @typescript-eslint/no-deprecated
325-
if (isCtrlKeyHeldDown(event) && (event.keyCode === 67 || event.keyCode === 86)) {
326-
event.preventGridDefault();
329+
// Prevent copy/paste on group rows
330+
function handleCellCopy(
331+
{ row, column }: CellCopyEvent<NoInfer<R>, NoInfer<SR>>,
332+
event: CellClipboardEvent
333+
) {
334+
if (!isGroupRow(row)) {
335+
rawOnCellCopy?.({ row, column }, event);
327336
}
328337
}
329338

339+
function handleCellPaste(
340+
{ row, column }: CellPasteEvent<NoInfer<R>, NoInfer<SR>>,
341+
event: CellClipboardEvent
342+
) {
343+
return isGroupRow(row) ? row : rawOnCellPaste!({ row, column }, event);
344+
}
345+
330346
function handleRowsChange(updatedRows: R[], { indexes, column }: RowsChangeData<R, SR>) {
331347
if (!onRowsChange) return;
332348
const updatedRawRows = [...rawRows];
@@ -362,7 +378,6 @@ export function TreeDataGrid<R, SR = unknown, K extends Key = Key>({
362378
onCellContextMenu,
363379
onRowChange,
364380
lastFrozenColumnIndex,
365-
copiedCellIdx,
366381
draggedOverCellIdx,
367382
setDraggedOverRowIdx,
368383
selectedCellEditor,
@@ -401,7 +416,6 @@ export function TreeDataGrid<R, SR = unknown, K extends Key = Key>({
401416
onCellContextMenu,
402417
onRowChange,
403418
lastFrozenColumnIndex,
404-
copiedCellIdx,
405419
draggedOverCellIdx,
406420
setDraggedOverRowIdx,
407421
selectedCellEditor
@@ -423,6 +437,8 @@ export function TreeDataGrid<R, SR = unknown, K extends Key = Key>({
423437
selectedRows={selectedRows}
424438
onSelectedRowsChange={onSelectedRowsChange}
425439
onCellKeyDown={handleKeyDown}
440+
onCellCopy={handleCellCopy}
441+
onCellPaste={rawOnCellPaste ? handleCellPaste : undefined}
426442
renderers={{
427443
...renderers,
428444
renderRow

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ export type {
3434
SelectHeaderRowEvent,
3535
SelectRowEvent,
3636
FillEvent,
37-
CopyEvent,
38-
PasteEvent,
3937
SortDirection,
4038
SortColumn,
4139
ColSpanArgs,
@@ -49,5 +47,7 @@ export type {
4947
CellClickArgs,
5048
CellKeyDownArgs,
5149
CellKeyboardEvent,
50+
CellCopyEvent,
51+
CellPasteEvent,
5252
CellSelectArgs
5353
} from './types';

src/types.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ export interface CellRendererProps<TRow, TSummaryRow>
153153
Omit<React.ComponentProps<'div'>, 'children' | 'onClick' | 'onDoubleClick' | 'onContextMenu'> {
154154
column: CalculatedColumn<TRow, TSummaryRow>;
155155
colSpan: number | undefined;
156-
isCopied: boolean;
157156
isDraggedOver: boolean;
158157
isCellSelected: boolean;
159158
onClick: RenderRowProps<TRow, TSummaryRow>['onCellClick'];
@@ -171,25 +170,27 @@ export type CellMouseEvent = CellEvent<React.MouseEvent<HTMLDivElement>>;
171170

172171
export type CellKeyboardEvent = CellEvent<React.KeyboardEvent<HTMLDivElement>>;
173172

173+
export type CellClipboardEvent = React.ClipboardEvent<HTMLDivElement>;
174+
174175
export interface CellClickArgs<TRow, TSummaryRow = unknown> {
175-
rowIdx: number;
176-
row: TRow;
177176
column: CalculatedColumn<TRow, TSummaryRow>;
177+
row: TRow;
178+
rowIdx: number;
178179
selectCell: (enableEditor?: boolean) => void;
179180
}
180181

181182
interface SelectCellKeyDownArgs<TRow, TSummaryRow = unknown> {
182183
mode: 'SELECT';
183-
row: TRow;
184184
column: CalculatedColumn<TRow, TSummaryRow>;
185+
row: TRow;
185186
rowIdx: number;
186187
selectCell: (position: Position, enableEditor?: Maybe<boolean>) => void;
187188
}
188189

189190
export interface EditCellKeyDownArgs<TRow, TSummaryRow = unknown> {
190191
mode: 'EDIT';
191-
row: TRow;
192192
column: CalculatedColumn<TRow, TSummaryRow>;
193+
row: TRow;
193194
rowIdx: number;
194195
navigate: () => void;
195196
onClose: (commitChanges?: boolean, shouldFocusCell?: boolean) => void;
@@ -224,7 +225,6 @@ export interface RenderRowProps<TRow, TSummaryRow = unknown>
224225
extends BaseRenderRowProps<TRow, TSummaryRow> {
225226
row: TRow;
226227
lastFrozenColumnIndex: number;
227-
copiedCellIdx: number | undefined;
228228
draggedOverCellIdx: number | undefined;
229229
selectedCellEditor: ReactElement<RenderEditCellProps<TRow>> | undefined;
230230
onRowChange: (column: CalculatedColumn<TRow, TSummaryRow>, rowIdx: number, newRow: TRow) => void;
@@ -253,17 +253,13 @@ export interface FillEvent<TRow> {
253253
targetRow: TRow;
254254
}
255255

256-
export interface CopyEvent<TRow> {
257-
sourceColumnKey: string;
258-
sourceRow: TRow;
256+
interface CellCopyPasteEvent<TRow, TSummaryRow = unknown> {
257+
column: CalculatedColumn<TRow, TSummaryRow>;
258+
row: TRow;
259259
}
260260

261-
export interface PasteEvent<TRow> {
262-
sourceColumnKey: string;
263-
sourceRow: TRow;
264-
targetColumnKey: string;
265-
targetRow: TRow;
266-
}
261+
export type CellCopyEvent<TRow, TSummaryRow = unknown> = CellCopyPasteEvent<TRow, TSummaryRow>;
262+
export type CellPasteEvent<TRow, TSummaryRow = unknown> = CellCopyPasteEvent<TRow, TSummaryRow>;
267263

268264
export interface GroupRow<TRow> {
269265
readonly childRows: readonly TRow[];

0 commit comments

Comments
 (0)