mirror of
https://github.com/ant-design/ant-design.git
synced 2024-12-15 00:29:12 +08:00
229 lines
5.7 KiB
TypeScript
229 lines
5.7 KiB
TypeScript
|
import React, { createContext, useContext, useState } from 'react';
|
||
|
import type { DragEndEvent, DragOverEvent, UniqueIdentifier } from '@dnd-kit/core';
|
||
|
import {
|
||
|
closestCenter,
|
||
|
DndContext,
|
||
|
DragOverlay,
|
||
|
PointerSensor,
|
||
|
useSensor,
|
||
|
useSensors,
|
||
|
} from '@dnd-kit/core';
|
||
|
import { restrictToHorizontalAxis } from '@dnd-kit/modifiers';
|
||
|
import {
|
||
|
arrayMove,
|
||
|
horizontalListSortingStrategy,
|
||
|
SortableContext,
|
||
|
useSortable,
|
||
|
} from '@dnd-kit/sortable';
|
||
|
import { Table } from 'antd';
|
||
|
import type { TableColumnsType } from 'antd';
|
||
|
|
||
|
interface DataType {
|
||
|
key: string;
|
||
|
name: string;
|
||
|
gender: string;
|
||
|
age: number;
|
||
|
email: string;
|
||
|
address: string;
|
||
|
}
|
||
|
|
||
|
interface HeaderCellProps extends React.HTMLAttributes<HTMLTableCellElement> {
|
||
|
id: string;
|
||
|
}
|
||
|
|
||
|
interface BodyCellProps extends React.HTMLAttributes<HTMLTableCellElement> {
|
||
|
id: string;
|
||
|
}
|
||
|
type DragIndexState = {
|
||
|
active: UniqueIdentifier;
|
||
|
over: UniqueIdentifier | undefined;
|
||
|
direction?: 'left' | 'right';
|
||
|
};
|
||
|
const DragIndexContext = createContext<DragIndexState>({ active: -1, over: -1 });
|
||
|
|
||
|
const dragActiveStyle = (dragState: DragIndexState, id: string) => {
|
||
|
const { active, over, direction } = dragState;
|
||
|
// drag active style
|
||
|
let style = {};
|
||
|
if (active && active === id) {
|
||
|
style = { backgroundColor: 'gray', opacity: 0.5 };
|
||
|
}
|
||
|
// dragover dashed style
|
||
|
else if (over && id === over && active !== over) {
|
||
|
style =
|
||
|
direction === 'right'
|
||
|
? { borderRight: '1px dashed gray' }
|
||
|
: { borderLeft: '1px dashed gray' };
|
||
|
}
|
||
|
return style;
|
||
|
};
|
||
|
|
||
|
const TableBodyCell = (props: BodyCellProps) => {
|
||
|
const dragState = useContext<DragIndexState>(DragIndexContext);
|
||
|
return (
|
||
|
<td
|
||
|
{...props}
|
||
|
style={{
|
||
|
...props.style,
|
||
|
...dragActiveStyle(dragState, props.id),
|
||
|
}}
|
||
|
/>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
const TableHeaderCell = (props: HeaderCellProps) => {
|
||
|
const dragState = useContext(DragIndexContext);
|
||
|
const { attributes, listeners, setNodeRef, isDragging } = useSortable({
|
||
|
id: props.id,
|
||
|
});
|
||
|
|
||
|
const style: React.CSSProperties = {
|
||
|
...props.style,
|
||
|
cursor: 'move',
|
||
|
...(isDragging ? { position: 'relative', zIndex: 9999, userSelect: 'none' } : {}),
|
||
|
...dragActiveStyle(dragState, props.id),
|
||
|
};
|
||
|
|
||
|
return <th {...props} ref={setNodeRef} style={style} {...attributes} {...listeners} />;
|
||
|
};
|
||
|
|
||
|
const dataSource: DataType[] = [
|
||
|
{
|
||
|
key: '1',
|
||
|
name: 'John Brown',
|
||
|
gender: 'male',
|
||
|
age: 32,
|
||
|
email: 'John Brown@example.com',
|
||
|
address: 'London No. 1 Lake Park',
|
||
|
},
|
||
|
{
|
||
|
key: '2',
|
||
|
name: 'Jim Green',
|
||
|
gender: 'female',
|
||
|
age: 42,
|
||
|
email: 'jimGreen@example.com',
|
||
|
address: 'London No. 1 Lake Park',
|
||
|
},
|
||
|
{
|
||
|
key: '3',
|
||
|
name: 'Joe Black',
|
||
|
gender: 'female',
|
||
|
age: 32,
|
||
|
email: 'JoeBlack@example.com',
|
||
|
address: 'Sidney No. 1 Lake Park',
|
||
|
},
|
||
|
{
|
||
|
key: '4',
|
||
|
name: 'George Hcc',
|
||
|
gender: 'male',
|
||
|
age: 20,
|
||
|
email: 'george@example.com',
|
||
|
address: 'Sidney No. 1 Lake Park',
|
||
|
},
|
||
|
];
|
||
|
const baseColumns: TableColumnsType<DataType> = [
|
||
|
{
|
||
|
title: 'Name',
|
||
|
dataIndex: 'name',
|
||
|
},
|
||
|
{
|
||
|
title: 'Gender',
|
||
|
dataIndex: 'gender',
|
||
|
},
|
||
|
{
|
||
|
title: 'Age',
|
||
|
dataIndex: 'age',
|
||
|
},
|
||
|
{
|
||
|
title: 'Email',
|
||
|
dataIndex: 'email',
|
||
|
},
|
||
|
{
|
||
|
title: 'Address',
|
||
|
dataIndex: 'address',
|
||
|
},
|
||
|
];
|
||
|
|
||
|
const App: React.FC = () => {
|
||
|
const [dragIndex, setDragIndex] = useState<DragIndexState>({ active: -1, over: -1 });
|
||
|
|
||
|
const [columns, setColumns] = useState(() =>
|
||
|
baseColumns.map((column, i) => ({
|
||
|
...column,
|
||
|
key: `${i}`,
|
||
|
onHeaderCell: () => ({
|
||
|
id: `${i}`,
|
||
|
}),
|
||
|
onCell: () => ({
|
||
|
id: `${i}`,
|
||
|
}),
|
||
|
})),
|
||
|
);
|
||
|
|
||
|
const sensors = useSensors(
|
||
|
useSensor(PointerSensor, {
|
||
|
activationConstraint: {
|
||
|
// https://docs.dndkit.com/api-documentation/sensors/pointer#activation-constraints
|
||
|
distance: 1,
|
||
|
},
|
||
|
}),
|
||
|
);
|
||
|
|
||
|
const onDragEnd = ({ active, over }: DragEndEvent) => {
|
||
|
if (active.id !== over?.id) {
|
||
|
setColumns((prev) => {
|
||
|
const activeIndex = prev.findIndex((i) => i.key === active.id);
|
||
|
const overIndex = prev.findIndex((i) => i.key === over?.id);
|
||
|
return arrayMove(prev, activeIndex, overIndex);
|
||
|
});
|
||
|
}
|
||
|
setDragIndex({ active: -1, over: -1 });
|
||
|
};
|
||
|
|
||
|
const onDrageOver = ({ active, over }: DragOverEvent) => {
|
||
|
const activeIndex = columns.findIndex((i) => i.key === active.id);
|
||
|
const overIndex = columns.findIndex((i) => i.key === over?.id);
|
||
|
setDragIndex({
|
||
|
active: active.id,
|
||
|
over: over?.id,
|
||
|
direction: overIndex > activeIndex ? 'right' : 'left',
|
||
|
});
|
||
|
};
|
||
|
|
||
|
return (
|
||
|
<DndContext
|
||
|
sensors={sensors}
|
||
|
modifiers={[restrictToHorizontalAxis]}
|
||
|
onDragEnd={onDragEnd}
|
||
|
onDragOver={onDrageOver}
|
||
|
collisionDetection={closestCenter}
|
||
|
>
|
||
|
<SortableContext items={columns.map((i) => i.key)} strategy={horizontalListSortingStrategy}>
|
||
|
<DragIndexContext.Provider value={dragIndex}>
|
||
|
<Table
|
||
|
components={{
|
||
|
header: {
|
||
|
cell: TableHeaderCell,
|
||
|
},
|
||
|
body: {
|
||
|
cell: TableBodyCell,
|
||
|
},
|
||
|
}}
|
||
|
rowKey="key"
|
||
|
columns={columns}
|
||
|
dataSource={dataSource}
|
||
|
/>
|
||
|
</DragIndexContext.Provider>
|
||
|
</SortableContext>
|
||
|
<DragOverlay>
|
||
|
{/* TODO: Since the tableheadercell custom component uses antd to render the cell style, it is currently not possible to copy the same component as tableheadercell. */}
|
||
|
<th style={{ backgroundColor: 'gray', padding: 16 }}>
|
||
|
{columns[columns.findIndex((i) => i.key === dragIndex.active)]?.title as React.ReactNode}
|
||
|
</th>
|
||
|
</DragOverlay>
|
||
|
</DndContext>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
export default App;
|