1- import { useRef , useState } from 'react' ;
1+ import { useId , useRef , useState } from 'react' ;
22import { css } from '@linaria/core' ;
33
44import { useRovingTabIndex } from './hooks' ;
@@ -43,17 +43,29 @@ export const resizeHandleClassname = css`
4343const cellDraggableClassname = 'rdg-cell-draggable' ;
4444
4545const cellDragging = css `
46- opacity : 0.5 ;
46+ @layer rdg.HeaderCell {
47+ background-color : var (--rdg-header-draggable-background-color );
48+ }
4749` ;
4850
4951const cellDraggingClassname = `rdg-cell-dragging ${ cellDragging } ` ;
5052
5153const cellOver = css `
52- background-color : var (--rdg-header-draggable-background-color );
54+ @layer rdg.HeaderCell {
55+ background-color : var (--rdg-header-draggable-background-color );
56+ }
5357` ;
5458
5559const cellOverClassname = `rdg-cell-drag-over ${ cellOver } ` ;
5660
61+ const dragImageClassname = css `
62+ @layer rdg.HeaderCell {
63+ border-radius : 4px ;
64+ width : fit-content;
65+ outline : 2px solid hsl (207 , 100% , 50% );
66+ }
67+ ` ;
68+
5769type SharedHeaderRowProps < R , SR > = Pick <
5870 HeaderRowProps < R , SR , React . Key > ,
5971 | 'sortColumns'
@@ -70,7 +82,8 @@ export interface HeaderCellProps<R, SR> extends SharedHeaderRowProps<R, SR> {
7082 colSpan : number | undefined ;
7183 rowIdx : number ;
7284 isCellSelected : boolean ;
73- dragDropKey : string ;
85+ draggedColumnKey : string | undefined ;
86+ setDraggedColumnKey : ( draggedColumnKey : string | undefined ) => void ;
7487}
7588
7689export default function HeaderCell < R , SR > ( {
@@ -85,10 +98,11 @@ export default function HeaderCell<R, SR>({
8598 onSortColumnsChange,
8699 selectCell,
87100 direction,
88- dragDropKey
101+ draggedColumnKey,
102+ setDraggedColumnKey
89103} : HeaderCellProps < R , SR > ) {
90- const [ isDragging , setIsDragging ] = useState ( false ) ;
91104 const [ isOver , setIsOver ] = useState ( false ) ;
105+ const isDragging = draggedColumnKey === column . key ;
92106 const rowSpan = getHeaderCellRowSpan ( column , rowIdx ) ;
93107 const { tabIndex, childTabIndex, onFocus } = useRovingTabIndex ( isCellSelected ) ;
94108 const sortIndex = sortColumns ?. findIndex ( ( sort ) => sort . columnKey === column . key ) ;
@@ -99,6 +113,7 @@ export default function HeaderCell<R, SR>({
99113 const ariaSort =
100114 sortDirection && ! priority ? ( sortDirection === 'ASC' ? 'ascending' : 'descending' ) : undefined ;
101115 const { sortable, resizable, draggable } = column ;
116+ const dragImageId = useId ( ) ;
102117
103118 const className = getCellClassname ( column , column . headerCellClass , {
104119 [ cellSortableClassname ] : sortable ,
@@ -180,13 +195,18 @@ export default function HeaderCell<R, SR>({
180195 }
181196
182197 function onDragStart ( event : React . DragEvent < HTMLDivElement > ) {
183- event . dataTransfer . setData ( dragDropKey , column . key ) ;
198+ const dragImage = event . currentTarget . cloneNode ( true ) as HTMLDivElement ;
199+ dragImage . classList . add ( dragImageClassname ) ;
200+ dragImage . id = dragImageId ;
201+ event . currentTarget . parentElement ! . insertBefore ( dragImage , event . currentTarget ) ;
202+ event . dataTransfer . setDragImage ( dragImage , 0 , 0 ) ;
184203 event . dataTransfer . dropEffect = 'move' ;
185- setIsDragging ( true ) ;
204+ setDraggedColumnKey ( column . key ) ;
186205 }
187206
188207 function onDragEnd ( ) {
189- setIsDragging ( false ) ;
208+ setDraggedColumnKey ( undefined ) ;
209+ document . getElementById ( dragImageId ) ?. remove ( ) ;
190210 }
191211
192212 function onDragOver ( event : React . DragEvent < HTMLDivElement > ) {
@@ -197,18 +217,9 @@ export default function HeaderCell<R, SR>({
197217
198218 function onDrop ( event : React . DragEvent < HTMLDivElement > ) {
199219 setIsOver ( false ) ;
200- // The dragDropKey is derived from the useId() hook, which can sometimes generate keys with uppercase letters.
201- // When setting data using event.dataTransfer.setData(), the key is automatically converted to lowercase in some browsers.
202- // To ensure consistent comparison, we normalize the dragDropKey to lowercase before checking its presence in the event's dataTransfer types.
203- // https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface
204- if ( event . dataTransfer . types . includes ( dragDropKey . toLowerCase ( ) ) ) {
205- const sourceKey = event . dataTransfer . getData ( dragDropKey . toLowerCase ( ) ) ;
206- if ( sourceKey !== column . key ) {
207- // prevent the browser from redirecting in some cases
208- event . preventDefault ( ) ;
209- onColumnsReorder ?.( sourceKey , column . key ) ;
210- }
211- }
220+ // prevent the browser from redirecting in some cases
221+ event . preventDefault ( ) ;
222+ onColumnsReorder ?.( draggedColumnKey ! , column . key ) ;
212223 }
213224
214225 function onDragEnter ( event : React . DragEvent < HTMLDivElement > ) {
@@ -223,19 +234,23 @@ export default function HeaderCell<R, SR>({
223234 }
224235 }
225236
226- let draggableProps : React . ComponentProps < 'div' > | undefined ;
237+ let dragTargetProps : React . ComponentProps < 'div' > | undefined ;
238+ let dropTargetProps : React . ComponentProps < 'div' > | undefined ;
227239 if ( draggable ) {
228- draggableProps = {
240+ dragTargetProps = {
229241 draggable : true ,
230- /* events fired on the draggable target */
231242 onDragStart,
232- onDragEnd,
233- /* events fired on the drop targets */
234- onDragOver,
235- onDragEnter,
236- onDragLeave,
237- onDrop
243+ onDragEnd
238244 } ;
245+
246+ if ( draggedColumnKey !== undefined && draggedColumnKey !== column . key ) {
247+ dropTargetProps = {
248+ onDragOver,
249+ onDragEnter,
250+ onDragLeave,
251+ onDrop
252+ } ;
253+ }
239254 }
240255
241256 return (
@@ -256,7 +271,8 @@ export default function HeaderCell<R, SR>({
256271 onFocus = { onFocus }
257272 onClick = { onClick }
258273 onKeyDown = { onKeyDown }
259- { ...draggableProps }
274+ { ...dragTargetProps }
275+ { ...dropTargetProps }
260276 >
261277 { column . renderHeaderCell ( {
262278 column,
0 commit comments