Skip to content

Commit 5bc91a8

Browse files
committed
Merge branch 'main' into am-refactor-minmax-logic
2 parents b79becf + ba6c8ea commit 5bc91a8

File tree

13 files changed

+321
-353
lines changed

13 files changed

+321
-353
lines changed

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);
@@ -608,39 +612,13 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
608612
);
609613
if (cellEvent.isGridDefaultPrevented()) return;
610614
}
615+
611616
if (!(event.target instanceof Element)) return;
612617
const isCellEvent = event.target.closest('.rdg-cell') !== null;
613618
const isRowEvent = isTreeGrid && event.target === focusSinkRef.current;
614619
if (!isCellEvent && !isRowEvent) return;
615620

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

687-
function handleCopy() {
665+
function handleCellCopy(event: CellClipboardEvent) {
666+
if (!selectedCellIsWithinViewportBounds) return;
688667
const { idx, rowIdx } = selectedPosition;
689-
const sourceRow = rows[rowIdx];
690-
const sourceColumnKey = columns[idx].key;
691-
setCopiedCell({ row: sourceRow, columnKey: sourceColumnKey });
692-
onCopy?.({ sourceRow, sourceColumnKey });
668+
onCellCopy?.({ row: rows[rowIdx], column: columns[idx] }, event);
693669
}
694670

695-
function handlePaste() {
696-
if (!onPaste || !onRowsChange || copiedCell === null || !isCellEditable(selectedPosition)) {
671+
function handleCellPaste(event: CellClipboardEvent) {
672+
if (!onCellPaste || !onRowsChange || !isCellEditable(selectedPosition)) {
697673
return;
698674
}
699675

700676
const { idx, rowIdx } = selectedPosition;
701-
const targetColumn = columns[idx];
702-
const targetRow = rows[rowIdx];
703-
704-
const updatedTargetRow = onPaste({
705-
sourceRow: copiedCell.row,
706-
sourceColumnKey: copiedCell.columnKey,
707-
targetRow,
708-
targetColumnKey: targetColumn.key
709-
});
710-
711-
updateRow(targetColumn, rowIdx, updatedTargetRow);
677+
const column = columns[idx];
678+
const updatedRow = onCellPaste({ row: rows[rowIdx], column }, event);
679+
updateRow(column, rowIdx, updatedRow);
712680
}
713681

714682
function handleCellInput(event: KeyboardEvent<HTMLDivElement>) {
@@ -726,7 +694,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
726694
return;
727695
}
728696

729-
if (isCellEditable(selectedPosition) && isDefaultCellInput(event)) {
697+
if (isCellEditable(selectedPosition) && isDefaultCellInput(event, onCellPaste != null)) {
730698
setSelectedPosition(({ idx, rowIdx }) => ({
731699
idx,
732700
rowIdx,
@@ -1051,11 +1019,6 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
10511019
onCellContextMenu: onCellContextMenuLatest,
10521020
rowClass,
10531021
gridRowStart,
1054-
copiedCellIdx:
1055-
copiedCell !== null && copiedCell.row === row
1056-
? columns.findIndex((c) => c.key === copiedCell.columnKey)
1057-
: undefined,
1058-
10591022
selectedCellIdx: selectedRowIdx === rowIdx ? selectedIdx : undefined,
10601023
draggedOverCellIdx: getDraggedOverCellIdx(rowIdx),
10611024
setDraggedOverRowIdx: isDragging ? setDraggedOverRowIdx : undefined,
@@ -1135,6 +1098,8 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
11351098
ref={gridRef}
11361099
onScroll={handleScroll}
11371100
onKeyDown={handleKeyDown}
1101+
onCopy={handleCellCopy}
1102+
onPaste={handleCellPaste}
11381103
data-testid={testId}
11391104
data-cy={dataCy}
11401105
>

src/HeaderCell.tsx

Lines changed: 58 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from 'react';
1+
import { useRef, useState } from 'react';
22
import { css } from '@linaria/core';
33

44
import { useRovingTabIndex } from './hooks';
@@ -86,7 +86,6 @@ export default function HeaderCell<R, SR>({
8686
}: HeaderCellProps<R, SR>) {
8787
const [isDragging, setIsDragging] = useState(false);
8888
const [isOver, setIsOver] = useState(false);
89-
const isRtl = direction === 'rtl';
9089
const rowSpan = getHeaderCellRowSpan(column, rowIdx);
9190
const { tabIndex, childTabIndex, onFocus } = useRovingTabIndex(isCellSelected);
9291
const sortIndex = sortColumns?.findIndex((sort) => sort.columnKey === column.key);
@@ -106,42 +105,6 @@ export default function HeaderCell<R, SR>({
106105
[cellOverClassname]: isOver
107106
});
108107

109-
function onPointerDown(event: React.PointerEvent<HTMLDivElement>) {
110-
if (event.pointerType === 'mouse' && event.buttons !== 1) {
111-
return;
112-
}
113-
114-
// Fix column resizing on a draggable column in FF
115-
event.preventDefault();
116-
117-
const { currentTarget, pointerId } = event;
118-
const headerCell = currentTarget.parentElement!;
119-
const { right, left } = headerCell.getBoundingClientRect();
120-
const offset = isRtl ? event.clientX - left : right - event.clientX;
121-
function onPointerMove(event: PointerEvent) {
122-
const { width, right, left } = headerCell.getBoundingClientRect();
123-
const newWidth = isRtl ? right + offset - event.clientX : event.clientX + offset - left;
124-
if (width > 0 && newWidth !== width) {
125-
onColumnResize(column, newWidth);
126-
}
127-
}
128-
129-
function onLostPointerCapture() {
130-
currentTarget.removeEventListener('pointermove', onPointerMove);
131-
currentTarget.removeEventListener('lostpointercapture', onLostPointerCapture);
132-
}
133-
134-
currentTarget.setPointerCapture(pointerId);
135-
currentTarget.addEventListener('pointermove', onPointerMove);
136-
// we are not using pointerup because it does not fire in some cases
137-
// pointer down -> alt+tab -> pointer up over another window -> pointerup event not fired
138-
currentTarget.addEventListener('lostpointercapture', onLostPointerCapture);
139-
}
140-
141-
function onDoubleClick() {
142-
onColumnResize(column, 'max-content');
143-
}
144-
145108
function onSort(ctrlClick: boolean) {
146109
if (onSortColumnsChange == null) return;
147110
const { sortDescendingFirst } = column;
@@ -289,17 +252,68 @@ export default function HeaderCell<R, SR>({
289252
})}
290253

291254
{resizable && (
292-
<div
293-
className={resizeHandleClassname}
294-
onClick={stopPropagation}
295-
onPointerDown={onPointerDown}
296-
onDoubleClick={onDoubleClick}
297-
/>
255+
<ResizeHandle column={column} onColumnResize={onColumnResize} direction={direction} />
298256
)}
299257
</div>
300258
);
301259
}
302260

261+
type ResizeHandleProps<R, SR> = Pick<
262+
HeaderCellProps<R, SR>,
263+
'column' | 'onColumnResize' | 'direction'
264+
>;
265+
266+
function ResizeHandle<R, SR>({ column, onColumnResize, direction }: ResizeHandleProps<R, SR>) {
267+
const resizingOffsetRef = useRef<number>(undefined);
268+
const isRtl = direction === 'rtl';
269+
270+
function onPointerDown(event: React.PointerEvent<HTMLDivElement>) {
271+
if (event.pointerType === 'mouse' && event.buttons !== 1) {
272+
return;
273+
}
274+
275+
// Fix column resizing on a draggable column in FF
276+
event.preventDefault();
277+
278+
const { currentTarget, pointerId } = event;
279+
currentTarget.setPointerCapture(pointerId);
280+
const headerCell = currentTarget.parentElement!;
281+
const { right, left } = headerCell.getBoundingClientRect();
282+
resizingOffsetRef.current = isRtl ? event.clientX - left : right - event.clientX;
283+
}
284+
285+
function onPointerMove(event: React.PointerEvent<HTMLDivElement>) {
286+
const offset = resizingOffsetRef.current;
287+
if (offset === undefined) return;
288+
const { width, right, left } = event.currentTarget.parentElement!.getBoundingClientRect();
289+
const newWidth = isRtl ? right + offset - event.clientX : event.clientX + offset - left;
290+
if (width > 0 && newWidth !== width) {
291+
onColumnResize(column, newWidth);
292+
}
293+
}
294+
295+
function onLostPointerCapture() {
296+
resizingOffsetRef.current = undefined;
297+
}
298+
299+
function onDoubleClick() {
300+
onColumnResize(column, 'max-content');
301+
}
302+
303+
return (
304+
<div
305+
className={resizeHandleClassname}
306+
onClick={stopPropagation}
307+
onPointerDown={onPointerDown}
308+
onPointerMove={onPointerMove}
309+
// we are not using pointerup because it does not fire in some cases
310+
// pointer down -> alt+tab -> pointer up over another window -> pointerup event not fired
311+
onLostPointerCapture={onLostPointerCapture}
312+
onDoubleClick={onDoubleClick}
313+
/>
314+
);
315+
}
316+
303317
// only accept pertinent drag events:
304318
// - ignore drag events going from the container to an element inside the container
305319
// - ignore drag events going from an element inside the container to the container

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,

0 commit comments

Comments
 (0)