From 14d86f455531b8ed0586842f6baa4edccb7492dc Mon Sep 17 00:00:00 2001 From: kseniyakuzina Date: Fri, 7 Aug 2026 17:22:19 +0300 Subject: [PATCH] fix(TableDndRoot): improve column drag mechanics --- .../BaseDraggableHeaderCell.tsx | 9 +++- .../utils/closestLeadingColumnEdge.ts | 48 +++++++++++++++++++ .../utils/tableCollisionDetection.ts | 12 +++-- 3 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 src/components/TableDndRoot/utils/closestLeadingColumnEdge.ts diff --git a/src/components/BaseDraggableHeaderCell/BaseDraggableHeaderCell.tsx b/src/components/BaseDraggableHeaderCell/BaseDraggableHeaderCell.tsx index 3ebc564..9edb572 100644 --- a/src/components/BaseDraggableHeaderCell/BaseDraggableHeaderCell.tsx +++ b/src/components/BaseDraggableHeaderCell/BaseDraggableHeaderCell.tsx @@ -16,7 +16,14 @@ export const BaseDraggableHeaderCell = ({ }: BaseDraggableHeaderCellProps) => { const {activeColumnId, useSortable} = React.useContext(ColumnReorderingContext) ?? {}; - const {setNodeRef, listeners, isDragging = false} = useSortable?.({id: header.column.id}) ?? {}; + const { + setNodeRef, + listeners, + isDragging = false, + } = useSortable?.({ + id: header.column.id, + data: {columnGroup: header.column.getIsPinned() || 'center'}, + }) ?? {}; const isDragActive = Boolean(activeColumnId); diff --git a/src/components/TableDndRoot/utils/closestLeadingColumnEdge.ts b/src/components/TableDndRoot/utils/closestLeadingColumnEdge.ts new file mode 100644 index 0000000..d7fec65 --- /dev/null +++ b/src/components/TableDndRoot/utils/closestLeadingColumnEdge.ts @@ -0,0 +1,48 @@ +import type {CollisionDetection} from '@dnd-kit/core'; +import {closestCenter} from '@dnd-kit/core'; + +type CollisionArgs = Parameters[0]; +type CollisionResult = ReturnType; + +export function closestLeadingColumnEdge(args: CollisionArgs): CollisionResult { + const initialRect = args.active.rect.current.initial; + + if (!initialRect) { + return closestCenter(args); + } + + const deltaX = args.collisionRect.left - initialRect.left; + + if (deltaX === 0) { + return closestCenter(args); + } + + const movingRight = deltaX > 0; + const leadingEdge = movingRight ? args.collisionRect.right : args.collisionRect.left; + + const target = args.droppableContainers.find((container) => { + const rect = args.droppableRects.get(container.id); + + if (!rect) { + return false; + } + + return movingRight + ? leadingEdge >= rect.left && leadingEdge < rect.right + : leadingEdge > rect.left && leadingEdge <= rect.right; + }); + + if (!target) { + return closestCenter(args); + } + + return [ + { + id: target.id, + data: { + droppableContainer: target, + value: 0, + }, + }, + ]; +} diff --git a/src/components/TableDndRoot/utils/tableCollisionDetection.ts b/src/components/TableDndRoot/utils/tableCollisionDetection.ts index 9cad7e8..f544925 100644 --- a/src/components/TableDndRoot/utils/tableCollisionDetection.ts +++ b/src/components/TableDndRoot/utils/tableCollisionDetection.ts @@ -1,18 +1,24 @@ import type {CollisionDetection} from '@dnd-kit/core'; -import {closestCenter, rectIntersection} from '@dnd-kit/core'; +import {rectIntersection} from '@dnd-kit/core'; import {REORDER_TYPE_COLUMN} from '../constants'; +import {closestLeadingColumnEdge} from './closestLeadingColumnEdge'; + export function tableCollisionDetection( args: Parameters[0], ): ReturnType { const type = args.active.data.current?.reorderType; + const columnGroup = + type === REORDER_TYPE_COLUMN ? args.active.data.current?.columnGroup : undefined; const containers = args.droppableContainers.filter( - (container) => container.data.current?.reorderType === type, + (container) => + container.data.current?.reorderType === type && + (!columnGroup || container.data.current?.columnGroup === columnGroup), ); const filteredArgs = {...args, droppableContainers: containers}; return type === REORDER_TYPE_COLUMN - ? closestCenter(filteredArgs) + ? closestLeadingColumnEdge(filteredArgs) : rectIntersection(filteredArgs); }