Skip to content

Commit d2f40f7

Browse files
Set drag column image (adazzle#3783)
* Set drag column image * Few fixes * Add `draggedColumnKey` state * Update src/HeaderCell.tsx Co-authored-by: Nicolas Stepien <567105+nstepien@users.noreply.github.com> * newline --------- Co-authored-by: Nicolas Stepien <567105+nstepien@users.noreply.github.com>
1 parent e0a2337 commit d2f40f7

File tree

4 files changed

+73
-47
lines changed

4 files changed

+73
-47
lines changed

src/HeaderCell.tsx

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

44
import { useRovingTabIndex } from './hooks';
@@ -43,17 +43,29 @@ export const resizeHandleClassname = css`
4343
const cellDraggableClassname = 'rdg-cell-draggable';
4444

4545
const cellDragging = css`
46-
opacity: 0.5;
46+
@layer rdg.HeaderCell {
47+
background-color: var(--rdg-header-draggable-background-color);
48+
}
4749
`;
4850

4951
const cellDraggingClassname = `rdg-cell-dragging ${cellDragging}`;
5052

5153
const 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

5559
const 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+
5769
type 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

7689
export 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,

src/HeaderRow.tsx

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

@@ -60,7 +60,7 @@ function HeaderRow<R, SR, K extends React.Key>({
6060
selectCell,
6161
direction
6262
}: HeaderRowProps<R, SR, K>) {
63-
const dragDropKey = useId();
63+
const [draggedColumnKey, setDraggedColumnKey] = useState<string>();
6464

6565
const cells = [];
6666
for (let index = 0; index < columns.length; index++) {
@@ -84,7 +84,8 @@ function HeaderRow<R, SR, K extends React.Key>({
8484
sortColumns={sortColumns}
8585
selectCell={selectCell}
8686
direction={direction}
87-
dragDropKey={dragDropKey}
87+
draggedColumnKey={draggedColumnKey}
88+
setDraggedColumnKey={setDraggedColumnKey}
8889
/>
8990
);
9091
}

src/style/core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const root = css`
4242
4343
@layer rdg.Root {
4444
${lightTheme}
45-
--rdg-selection-color: #66afe9;
45+
--rdg-selection-color: hsl(207, 75%, 66%);
4646
--rdg-font-size: 14px;
4747
--rdg-cell-frozen-box-shadow: 2px 0 5px -2px rgba(136, 136, 136, 0.3);
4848

website/routes/ColumnsReordering.tsx

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -106,18 +106,27 @@ function ColumnsReordering() {
106106
}, [rows, sortColumns]);
107107

108108
function onColumnsReorder(sourceKey: string, targetKey: string) {
109-
setColumnsOrder((columnsOrder) => {
110-
const sourceColumnOrderIndex = columnsOrder.findIndex(
111-
(index) => columns[index].key === sourceKey
112-
);
113-
const targetColumnOrderIndex = columnsOrder.findIndex(
114-
(index) => columns[index].key === targetKey
115-
);
116-
const sourceColumnOrder = columnsOrder[sourceColumnOrderIndex];
117-
const newColumnsOrder = columnsOrder.toSpliced(sourceColumnOrderIndex, 1);
118-
newColumnsOrder.splice(targetColumnOrderIndex, 0, sourceColumnOrder);
119-
return newColumnsOrder;
120-
});
109+
function reorderColumns() {
110+
setColumnsOrder((columnsOrder) => {
111+
const sourceColumnOrderIndex = columnsOrder.findIndex(
112+
(index) => columns[index].key === sourceKey
113+
);
114+
const targetColumnOrderIndex = columnsOrder.findIndex(
115+
(index) => columns[index].key === targetKey
116+
);
117+
const sourceColumnOrder = columnsOrder[sourceColumnOrderIndex];
118+
const newColumnsOrder = columnsOrder.toSpliced(sourceColumnOrderIndex, 1);
119+
newColumnsOrder.splice(targetColumnOrderIndex, 0, sourceColumnOrder);
120+
return newColumnsOrder;
121+
});
122+
}
123+
124+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
125+
if (document.startViewTransition) {
126+
document.startViewTransition(reorderColumns);
127+
} else {
128+
reorderColumns();
129+
}
121130
}
122131

123132
function resetOrderAndWidths() {

0 commit comments

Comments
 (0)