66import classNames from "classnames" ;
77import { GenericDocument } from "convex/server" ;
88import { HeaderGroup } from "react-table" ;
9- import { useDrop , useDrag } from "react- dnd" ;
10- import { useRef , useState } from "react" ;
9+ import { useSortable } from "@ dnd-kit/sortable " ;
10+ import { useRef , useState , RefObject } from "react" ;
1111import omit from "lodash/omit" ;
1212import { useContextMenuTrigger } from "@common/features/data/lib/useContextMenuTrigger" ;
1313import { useTableDensity } from "@common/features/data/lib/useTableDensity" ;
@@ -29,17 +29,16 @@ type ColumnHeaderProps = {
2929 hasFilters : boolean ;
3030 isSelectionExhaustive : boolean ;
3131 toggleAll : ( ) => void ;
32- reorder ( item : { index : number } , newIndex : number ) : void ;
3332 isResizingColumn ?: string ;
3433 isLastColumn : boolean ;
3534 openContextMenu : DataCellProps [ "onOpenContextMenu" ] ;
3635 sort ?: "asc" | "desc" ;
3736 activeSchema : any | null ;
3837 tableName : string ;
38+ tableContainerRef : RefObject < HTMLDivElement > ;
3939} ;
4040
4141export function ColumnHeader ( {
42- reorder,
4342 column,
4443 columnIndex,
4544 allRowsSelected = false ,
@@ -52,14 +51,27 @@ export function ColumnHeader({
5251 sort,
5352 activeSchema,
5453 tableName,
54+ tableContainerRef,
5555} : ColumnHeaderProps ) {
5656 const canDragOrDrop = columnIndex !== 0 && ! isResizingColumn ;
5757
5858 const headerNode = useRef < HTMLDivElement | null > ( null ) ;
5959
60- const { isDragging, isHovering, direction, drop, drag, dragPreview } =
61- useColumnDragAndDrop ( column , columnIndex , reorder , canDragOrDrop ) ;
6260 const columnName = column . Header as string ;
61+ const columnId = column . id ;
62+
63+ const { attributes, listeners, setNodeRef, isDragging, isOver, active } =
64+ useSortable ( {
65+ id : columnId ,
66+ disabled : ! canDragOrDrop ,
67+ } ) ;
68+
69+ // Always drop to the right of the hovered column
70+ const direction =
71+ isOver && ! isDragging && active && active . id !== columnId
72+ ? "right"
73+ : undefined ;
74+ const isHovering = isOver && ! isDragging && active ?. id !== columnId ;
6375 useContextMenuTrigger (
6476 headerNode ,
6577 ( pos ) =>
@@ -87,43 +99,43 @@ export function ColumnHeader({
8799 < div
88100 key = { column . getHeaderProps ( ) . key }
89101 { ...omit ( column . getHeaderProps ( { style : { width } } ) , "key" ) }
102+ ref = { setNodeRef }
90103 className = { classNames (
91- isDragging && "cursor-grabbing " ,
104+ isDragging && "opacity-50 " ,
92105 "font-semibold text-left text-xs bg-background-secondary text-content-secondary tracking-wider" ,
93106 "select-none duration-300 transition-colors" ,
94- ! isLastColumn && "border-r" ,
95- isResizingColumn === columnName && "border-r-util-accent" ,
107+ "border-r" ,
96108 "relative" ,
97109 ) }
98110 onMouseEnter = { ( ) => setIsHovered ( true ) }
99111 onMouseLeave = { ( ) => setIsHovered ( false ) }
100112 >
101- { /* Show a border on the side the column will be dropped */ }
102- { ! isDragging && isHovering && direction && (
113+ { /* Show a vertical line on the right side where the column will be dropped */ }
114+ { isHovering && direction && (
103115 < div
104- className = { classNames (
105- "absolute top-px h-full w-px bg-util-accent" ,
106- direction === "left" ? "left-0" : "right-0" ,
107- ) }
116+ className = "absolute top-0 right-0 z-10 w-0.5 bg-util-accent"
117+ style = { {
118+ height : tableContainerRef . current ?. offsetHeight || "100%" ,
119+ } }
120+ />
121+ ) }
122+ { /* Show a vertical line when resizing this column */ }
123+ { isResizingColumn === columnName && (
124+ < div
125+ className = "absolute top-0 right-0 z-10 w-0.5 bg-util-accent"
126+ style = { {
127+ height : tableContainerRef . current ?. offsetHeight || "100%" ,
128+ } }
108129 />
109130 ) }
110131 < ValidatorTooltip
111132 fieldSchema = { fieldSchema }
112133 columnName = { columnName }
113- disableTooltip = { ! ! isResizingColumn }
134+ disableTooltip = { ! ! isResizingColumn || isDragging }
114135 >
115136 < div
116- ref = { ( node ) => {
117- headerNode . current = node ;
118- if ( node ) {
119- drop ( node ) ;
120- dragPreview ( node ) ;
121- }
122- } }
123- className = { cn (
124- "flex w-full items-center justify-between space-x-2" ,
125- isDragging && "cursor-grabbing" ,
126- ) }
137+ ref = { headerNode }
138+ className = "flex w-full items-center space-x-2"
127139 style = { {
128140 padding : `${ densityValues . paddingY } px ${ columnIndex === 0 ? "12" : densityValues . paddingX } px` ,
129141 width,
@@ -173,9 +185,8 @@ export function ColumnHeader({
173185 </ div >
174186 { canDragOrDrop && isHovered && (
175187 < Button
176- ref = { ( node ) => {
177- node && drag ( node ) ;
178- } }
188+ { ...attributes }
189+ { ...listeners }
179190 className = { cn (
180191 "absolute right-1.5 animate-fadeInFromLoading cursor-grab items-center bg-background-secondary/50 text-content-secondary backdrop-blur-[2px]" ,
181192 isDragging && "cursor-grabbing" ,
@@ -184,12 +195,6 @@ export function ColumnHeader({
184195 variant = "neutral"
185196 inline
186197 size = "xs"
187- onKeyDown = { ( e ) => {
188- if ( e . key === " " || e . key === "Enter" ) {
189- e . preventDefault ( ) ;
190- // Optionally, trigger drag start here if needed for keyboard users
191- }
192- } }
193198 icon = { < DragHandleDots2Icon /> }
194199 />
195200 ) }
@@ -210,39 +215,3 @@ export function ColumnHeader({
210215 </ div >
211216 ) ;
212217}
213-
214- export function useColumnDragAndDrop (
215- column : HeaderGroup < GenericDocument > ,
216- columnIndex : number ,
217- reorder : ( item : { index : number } , newIndex : number ) => void ,
218- canDragOrDrop : boolean ,
219- ) {
220- const { id } = column ;
221- const [ { isHovering, offset } , drop ] = useDrop ( {
222- accept : "column" ,
223- canDrop : ( ) => canDragOrDrop ,
224- drop : ( item : { index : number } ) => {
225- reorder ( item , columnIndex ) ;
226- } ,
227- collect : ( monitor ) => ( {
228- isHovering : canDragOrDrop && monitor . isOver ( { shallow : true } ) ,
229- offset : monitor . getDifferenceFromInitialOffset ( ) ,
230- } ) ,
231- } ) ;
232-
233- const direction = offset ?. x ? ( offset . x > 0 ? "right" : "left" ) : undefined ;
234-
235- const [ { isDragging } , drag , dragPreview ] = useDrag ( {
236- type : "column" ,
237- canDrag : canDragOrDrop ,
238- item : ( ) => ( {
239- id,
240- index : columnIndex ,
241- } ) ,
242- collect : ( monitor ) => ( {
243- isDragging : monitor . isDragging ( ) ,
244- } ) ,
245- } ) ;
246-
247- return { isDragging, isHovering, direction, drop, drag, dragPreview } ;
248- }
0 commit comments