From 5646a1384374d3d0d0589722f4d6e589a45238f4 Mon Sep 17 00:00:00 2001 From: Aman Mahajan Date: Wed, 9 Apr 2025 13:21:32 -0500 Subject: [PATCH 1/5] Use react events --- src/HeaderCell.tsx | 36 +++++++++++++++++-------------- src/hooks/useCalculatedColumns.ts | 15 ++++++------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/HeaderCell.tsx b/src/HeaderCell.tsx index 720701795f..663943b741 100644 --- a/src/HeaderCell.tsx +++ b/src/HeaderCell.tsx @@ -86,6 +86,7 @@ export default function HeaderCell({ dragDropKey }: HeaderCellProps) { const [isDragging, setIsDragging] = useState(false); + const [resizingOffset, setResizingOffset] = useState(); const [isOver, setIsOver] = useState(false); const isRtl = direction === 'rtl'; const rowSpan = getHeaderCellRowSpan(column, rowIdx); @@ -116,28 +117,27 @@ export default function HeaderCell({ event.preventDefault(); const { currentTarget, pointerId } = event; + currentTarget.setPointerCapture(pointerId); const headerCell = currentTarget.parentElement!; const { right, left } = headerCell.getBoundingClientRect(); const offset = isRtl ? event.clientX - left : right - event.clientX; - function onPointerMove(event: PointerEvent) { - const { width, right, left } = headerCell.getBoundingClientRect(); - let newWidth = isRtl ? right + offset - event.clientX : event.clientX + offset - left; - newWidth = clampColumnWidth(newWidth, column); - if (width > 0 && newWidth !== width) { - onColumnResize(column, newWidth); - } - } + setResizingOffset(offset); + } - function onLostPointerCapture() { - currentTarget.removeEventListener('pointermove', onPointerMove); - currentTarget.removeEventListener('lostpointercapture', onLostPointerCapture); + function onPointerMove(event: React.PointerEvent) { + if (resizingOffset === undefined) return; + const { width, right, left } = event.currentTarget.parentElement!.getBoundingClientRect(); + let newWidth = isRtl + ? right + resizingOffset - event.clientX + : event.clientX + resizingOffset - left; + newWidth = clampColumnWidth(newWidth, column); + if (width > 0 && newWidth !== width) { + onColumnResize(column, newWidth); } + } - currentTarget.setPointerCapture(pointerId); - currentTarget.addEventListener('pointermove', onPointerMove); - // we are not using pointerup because it does not fire in some cases - // pointer down -> alt+tab -> pointer up over another window -> pointerup event not fired - currentTarget.addEventListener('lostpointercapture', onLostPointerCapture); + function onLostPointerCapture() { + setResizingOffset(undefined); } function onDoubleClick() { @@ -295,6 +295,10 @@ export default function HeaderCell({ className={resizeHandleClassname} onClick={stopPropagation} onPointerDown={onPointerDown} + onPointerMove={onPointerMove} + // we are not using pointerup because it does not fire in some cases + // pointer down -> alt+tab -> pointer up over another window -> pointerup event not fired + onLostPointerCapture={onLostPointerCapture} onDoubleClick={onDoubleClick} /> )} diff --git a/src/hooks/useCalculatedColumns.ts b/src/hooks/useCalculatedColumns.ts index 2ba770396d..0bbc33d0c9 100644 --- a/src/hooks/useCalculatedColumns.ts +++ b/src/hooks/useCalculatedColumns.ts @@ -5,7 +5,7 @@ import type { CalculatedColumn, CalculatedColumnParent, ColumnOrColumnGroup, Omi import { renderValue } from '../cellRenderers'; import { SELECT_COLUMN_KEY } from '../Columns'; import type { DataGridProps } from '../DataGrid'; -import defaultRenderHeaderCell from '../renderHeaderCell'; +import renderHeaderCell from '../renderHeaderCell'; type Mutable = { -readonly [P in keyof T]: T[P] extends ReadonlyArray ? Mutable[] : T[P]; @@ -48,9 +48,8 @@ export function useCalculatedColumns({ const defaultWidth = defaultColumnOptions?.width ?? DEFAULT_COLUMN_WIDTH; const defaultMinWidth = defaultColumnOptions?.minWidth ?? DEFAULT_COLUMN_MIN_WIDTH; const defaultMaxWidth = defaultColumnOptions?.maxWidth ?? undefined; - const defaultCellRenderer = defaultColumnOptions?.renderCell ?? renderValue; - const defaultHeaderCellRenderer = - defaultColumnOptions?.renderHeaderCell ?? defaultRenderHeaderCell; + const defaultRenderCell = defaultColumnOptions?.renderCell ?? renderValue; + const defaultRenderHeaderCell = defaultColumnOptions?.renderHeaderCell ?? renderHeaderCell; const defaultSortable = defaultColumnOptions?.sortable ?? false; const defaultResizable = defaultColumnOptions?.resizable ?? false; const defaultDraggable = defaultColumnOptions?.draggable ?? false; @@ -101,8 +100,8 @@ export function useCalculatedColumns({ sortable: rawColumn.sortable ?? defaultSortable, resizable: rawColumn.resizable ?? defaultResizable, draggable: rawColumn.draggable ?? defaultDraggable, - renderCell: rawColumn.renderCell ?? defaultCellRenderer, - renderHeaderCell: rawColumn.renderHeaderCell ?? defaultHeaderCellRenderer + renderCell: rawColumn.renderCell ?? defaultRenderCell, + renderHeaderCell: rawColumn.renderHeaderCell ?? defaultRenderHeaderCell }; columns.push(column); @@ -156,8 +155,8 @@ export function useCalculatedColumns({ defaultWidth, defaultMinWidth, defaultMaxWidth, - defaultCellRenderer, - defaultHeaderCellRenderer, + defaultRenderCell, + defaultRenderHeaderCell, defaultResizable, defaultSortable, defaultDraggable From c1dc28dc12bc61a2cdb672d2f5540e25cc473633 Mon Sep 17 00:00:00 2001 From: Aman Mahajan Date: Wed, 9 Apr 2025 13:42:37 -0500 Subject: [PATCH 2/5] Add `ResizeHandle` component --- src/HeaderCell.tsx | 108 +++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 48 deletions(-) diff --git a/src/HeaderCell.tsx b/src/HeaderCell.tsx index 663943b741..ad8e06234b 100644 --- a/src/HeaderCell.tsx +++ b/src/HeaderCell.tsx @@ -86,9 +86,7 @@ export default function HeaderCell({ dragDropKey }: HeaderCellProps) { const [isDragging, setIsDragging] = useState(false); - const [resizingOffset, setResizingOffset] = useState(); const [isOver, setIsOver] = useState(false); - const isRtl = direction === 'rtl'; const rowSpan = getHeaderCellRowSpan(column, rowIdx); const { tabIndex, childTabIndex, onFocus } = useRovingTabIndex(isCellSelected); const sortIndex = sortColumns?.findIndex((sort) => sort.columnKey === column.key); @@ -108,42 +106,6 @@ export default function HeaderCell({ [cellOverClassname]: isOver }); - function onPointerDown(event: React.PointerEvent) { - if (event.pointerType === 'mouse' && event.buttons !== 1) { - return; - } - - // Fix column resizing on a draggable column in FF - event.preventDefault(); - - const { currentTarget, pointerId } = event; - currentTarget.setPointerCapture(pointerId); - const headerCell = currentTarget.parentElement!; - const { right, left } = headerCell.getBoundingClientRect(); - const offset = isRtl ? event.clientX - left : right - event.clientX; - setResizingOffset(offset); - } - - function onPointerMove(event: React.PointerEvent) { - if (resizingOffset === undefined) return; - const { width, right, left } = event.currentTarget.parentElement!.getBoundingClientRect(); - let newWidth = isRtl - ? right + resizingOffset - event.clientX - : event.clientX + resizingOffset - left; - newWidth = clampColumnWidth(newWidth, column); - if (width > 0 && newWidth !== width) { - onColumnResize(column, newWidth); - } - } - - function onLostPointerCapture() { - setResizingOffset(undefined); - } - - function onDoubleClick() { - onColumnResize(column, 'max-content'); - } - function onSort(ctrlClick: boolean) { if (onSortColumnsChange == null) return; const { sortDescendingFirst } = column; @@ -291,21 +253,71 @@ export default function HeaderCell({ })} {resizable && ( -
alt+tab -> pointer up over another window -> pointerup event not fired - onLostPointerCapture={onLostPointerCapture} - onDoubleClick={onDoubleClick} - /> + )}
); } +type ResizeHandleProps = Pick< + HeaderCellProps, + 'column' | 'onColumnResize' | 'direction' +>; + +function ResizeHandle({ column, onColumnResize, direction }: ResizeHandleProps) { + const [resizingOffset, setResizingOffset] = useState(); + const isRtl = direction === 'rtl'; + + function onPointerDown(event: React.PointerEvent) { + if (event.pointerType === 'mouse' && event.buttons !== 1) { + return; + } + + // Fix column resizing on a draggable column in FF + event.preventDefault(); + + const { currentTarget, pointerId } = event; + currentTarget.setPointerCapture(pointerId); + const headerCell = currentTarget.parentElement!; + const { right, left } = headerCell.getBoundingClientRect(); + const offset = isRtl ? event.clientX - left : right - event.clientX; + setResizingOffset(offset); + } + + function onPointerMove(event: React.PointerEvent) { + if (resizingOffset === undefined) return; + const { width, right, left } = event.currentTarget.parentElement!.getBoundingClientRect(); + let newWidth = isRtl + ? right + resizingOffset - event.clientX + : event.clientX + resizingOffset - left; + newWidth = clampColumnWidth(newWidth, column); + if (width > 0 && newWidth !== width) { + onColumnResize(column, newWidth); + } + } + + function onLostPointerCapture() { + setResizingOffset(undefined); + } + + function onDoubleClick() { + onColumnResize(column, 'max-content'); + } + + return ( +
alt+tab -> pointer up over another window -> pointerup event not fired + onLostPointerCapture={onLostPointerCapture} + onDoubleClick={onDoubleClick} + /> + ); +} + // only accept pertinent drag events: // - ignore drag events going from the container to an element inside the container // - ignore drag events going from an element inside the container to the container From 2545ce272c5337ef130e8e4d9868c1dd02be08a3 Mon Sep 17 00:00:00 2001 From: Aman Mahajan Date: Thu, 10 Apr 2025 09:09:16 -0500 Subject: [PATCH 3/5] useRef --- src/HeaderCell.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/HeaderCell.tsx b/src/HeaderCell.tsx index ad8e06234b..03cfbfe617 100644 --- a/src/HeaderCell.tsx +++ b/src/HeaderCell.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useRef, useState } from 'react'; import { css } from '@linaria/core'; import { useRovingTabIndex } from './hooks'; @@ -265,7 +265,7 @@ type ResizeHandleProps = Pick< >; function ResizeHandle({ column, onColumnResize, direction }: ResizeHandleProps) { - const [resizingOffset, setResizingOffset] = useState(); + const resizingOffsetRef = useRef(undefined); const isRtl = direction === 'rtl'; function onPointerDown(event: React.PointerEvent) { @@ -280,16 +280,14 @@ function ResizeHandle({ column, onColumnResize, direction }: ResizeHandle currentTarget.setPointerCapture(pointerId); const headerCell = currentTarget.parentElement!; const { right, left } = headerCell.getBoundingClientRect(); - const offset = isRtl ? event.clientX - left : right - event.clientX; - setResizingOffset(offset); + resizingOffsetRef.current = isRtl ? event.clientX - left : right - event.clientX; } function onPointerMove(event: React.PointerEvent) { - if (resizingOffset === undefined) return; + const offset = resizingOffsetRef.current; + if (offset === undefined) return; const { width, right, left } = event.currentTarget.parentElement!.getBoundingClientRect(); - let newWidth = isRtl - ? right + resizingOffset - event.clientX - : event.clientX + resizingOffset - left; + let newWidth = isRtl ? right + offset - event.clientX : event.clientX + offset - left; newWidth = clampColumnWidth(newWidth, column); if (width > 0 && newWidth !== width) { onColumnResize(column, newWidth); @@ -297,7 +295,7 @@ function ResizeHandle({ column, onColumnResize, direction }: ResizeHandle } function onLostPointerCapture() { - setResizingOffset(undefined); + resizingOffsetRef.current = undefined; } function onDoubleClick() { From 5d664f4acf61022fc142f1eafd20136f747eb695 Mon Sep 17 00:00:00 2001 From: Aman Mahajan Date: Thu, 10 Apr 2025 09:10:41 -0500 Subject: [PATCH 4/5] rename --- src/HeaderCell.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/HeaderCell.tsx b/src/HeaderCell.tsx index 03cfbfe617..dc95037134 100644 --- a/src/HeaderCell.tsx +++ b/src/HeaderCell.tsx @@ -265,7 +265,7 @@ type ResizeHandleProps = Pick< >; function ResizeHandle({ column, onColumnResize, direction }: ResizeHandleProps) { - const resizingOffsetRef = useRef(undefined); + const offsetRef = useRef(undefined); const isRtl = direction === 'rtl'; function onPointerDown(event: React.PointerEvent) { @@ -280,11 +280,11 @@ function ResizeHandle({ column, onColumnResize, direction }: ResizeHandle currentTarget.setPointerCapture(pointerId); const headerCell = currentTarget.parentElement!; const { right, left } = headerCell.getBoundingClientRect(); - resizingOffsetRef.current = isRtl ? event.clientX - left : right - event.clientX; + offsetRef.current = isRtl ? event.clientX - left : right - event.clientX; } function onPointerMove(event: React.PointerEvent) { - const offset = resizingOffsetRef.current; + const offset = offsetRef.current; if (offset === undefined) return; const { width, right, left } = event.currentTarget.parentElement!.getBoundingClientRect(); let newWidth = isRtl ? right + offset - event.clientX : event.clientX + offset - left; @@ -295,7 +295,7 @@ function ResizeHandle({ column, onColumnResize, direction }: ResizeHandle } function onLostPointerCapture() { - resizingOffsetRef.current = undefined; + offsetRef.current = undefined; } function onDoubleClick() { From c20529dd14dc6fc41f7809fe407ec9b22152ae87 Mon Sep 17 00:00:00 2001 From: Aman Mahajan Date: Thu, 10 Apr 2025 09:12:41 -0500 Subject: [PATCH 5/5] Revert "rename" This reverts commit 5d664f4acf61022fc142f1eafd20136f747eb695. --- src/HeaderCell.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/HeaderCell.tsx b/src/HeaderCell.tsx index dc95037134..03cfbfe617 100644 --- a/src/HeaderCell.tsx +++ b/src/HeaderCell.tsx @@ -265,7 +265,7 @@ type ResizeHandleProps = Pick< >; function ResizeHandle({ column, onColumnResize, direction }: ResizeHandleProps) { - const offsetRef = useRef(undefined); + const resizingOffsetRef = useRef(undefined); const isRtl = direction === 'rtl'; function onPointerDown(event: React.PointerEvent) { @@ -280,11 +280,11 @@ function ResizeHandle({ column, onColumnResize, direction }: ResizeHandle currentTarget.setPointerCapture(pointerId); const headerCell = currentTarget.parentElement!; const { right, left } = headerCell.getBoundingClientRect(); - offsetRef.current = isRtl ? event.clientX - left : right - event.clientX; + resizingOffsetRef.current = isRtl ? event.clientX - left : right - event.clientX; } function onPointerMove(event: React.PointerEvent) { - const offset = offsetRef.current; + const offset = resizingOffsetRef.current; if (offset === undefined) return; const { width, right, left } = event.currentTarget.parentElement!.getBoundingClientRect(); let newWidth = isRtl ? right + offset - event.clientX : event.clientX + offset - left; @@ -295,7 +295,7 @@ function ResizeHandle({ column, onColumnResize, direction }: ResizeHandle } function onLostPointerCapture() { - offsetRef.current = undefined; + resizingOffsetRef.current = undefined; } function onDoubleClick() {