2024-04-08 14:04:08 +08:00
|
|
|
import React, { useState } from 'react';
|
2023-02-13 10:29:12 +08:00
|
|
|
import type { DragEndEvent } from '@dnd-kit/core';
|
2024-11-24 14:52:51 +08:00
|
|
|
import { closestCenter, DndContext, PointerSensor, useSensor } from '@dnd-kit/core';
|
2023-02-13 10:29:12 +08:00
|
|
|
import {
|
|
|
|
arrayMove,
|
|
|
|
horizontalListSortingStrategy,
|
|
|
|
SortableContext,
|
|
|
|
useSortable,
|
|
|
|
} from '@dnd-kit/sortable';
|
|
|
|
import { CSS } from '@dnd-kit/utilities';
|
2023-07-28 16:17:43 +08:00
|
|
|
import { Tabs } from 'antd';
|
2024-11-24 14:52:51 +08:00
|
|
|
import type { TabsProps } from 'antd';
|
2022-11-09 12:28:04 +08:00
|
|
|
|
|
|
|
interface DraggableTabPaneProps extends React.HTMLAttributes<HTMLDivElement> {
|
2023-02-13 10:29:12 +08:00
|
|
|
'data-node-key': string;
|
2022-11-09 12:28:04 +08:00
|
|
|
}
|
|
|
|
|
2024-11-24 14:52:51 +08:00
|
|
|
const DraggableTabNode: React.FC<Readonly<DraggableTabPaneProps>> = ({ className, ...props }) => {
|
2023-07-20 19:27:11 +08:00
|
|
|
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({
|
2023-02-13 10:29:12 +08:00
|
|
|
id: props['data-node-key'],
|
2022-11-09 12:28:04 +08:00
|
|
|
});
|
|
|
|
|
2023-02-13 10:29:12 +08:00
|
|
|
const style: React.CSSProperties = {
|
|
|
|
...props.style,
|
2024-04-24 11:48:15 +08:00
|
|
|
transform: CSS.Translate.toString(transform),
|
2023-02-13 10:29:12 +08:00
|
|
|
transition,
|
|
|
|
cursor: 'move',
|
|
|
|
};
|
2022-11-09 12:28:04 +08:00
|
|
|
|
2023-02-13 10:29:12 +08:00
|
|
|
return React.cloneElement(props.children as React.ReactElement, {
|
|
|
|
ref: setNodeRef,
|
|
|
|
style,
|
|
|
|
...attributes,
|
|
|
|
...listeners,
|
|
|
|
});
|
|
|
|
};
|
2022-11-09 12:28:04 +08:00
|
|
|
|
2023-02-13 10:29:12 +08:00
|
|
|
const App: React.FC = () => {
|
2024-11-24 14:52:51 +08:00
|
|
|
const [items, setItems] = useState<NonNullable<TabsProps['items']>>([
|
|
|
|
{ key: '1', label: 'Tab 1', children: 'Content of Tab Pane 1' },
|
|
|
|
{ key: '2', label: 'Tab 2', children: 'Content of Tab Pane 2' },
|
|
|
|
{ key: '3', label: 'Tab 3', children: 'Content of Tab Pane 3' },
|
2023-02-13 10:29:12 +08:00
|
|
|
]);
|
2022-11-09 12:28:04 +08:00
|
|
|
|
2023-02-13 10:29:12 +08:00
|
|
|
const sensor = useSensor(PointerSensor, { activationConstraint: { distance: 10 } });
|
2022-11-09 12:28:04 +08:00
|
|
|
|
2023-02-13 10:29:12 +08:00
|
|
|
const onDragEnd = ({ active, over }: DragEndEvent) => {
|
|
|
|
if (active.id !== over?.id) {
|
|
|
|
setItems((prev) => {
|
|
|
|
const activeIndex = prev.findIndex((i) => i.key === active.id);
|
|
|
|
const overIndex = prev.findIndex((i) => i.key === over?.id);
|
|
|
|
return arrayMove(prev, activeIndex, overIndex);
|
|
|
|
});
|
2022-11-09 12:28:04 +08:00
|
|
|
}
|
2023-02-13 10:29:12 +08:00
|
|
|
};
|
2022-11-09 12:28:04 +08:00
|
|
|
|
|
|
|
return (
|
2023-02-13 10:29:12 +08:00
|
|
|
<Tabs
|
|
|
|
items={items}
|
|
|
|
renderTabBar={(tabBarProps, DefaultTabBar) => (
|
2024-06-25 13:34:28 +08:00
|
|
|
<DndContext sensors={[sensor]} onDragEnd={onDragEnd} collisionDetection={closestCenter}>
|
2023-02-13 10:29:12 +08:00
|
|
|
<SortableContext items={items.map((i) => i.key)} strategy={horizontalListSortingStrategy}>
|
|
|
|
<DefaultTabBar {...tabBarProps}>
|
|
|
|
{(node) => (
|
2023-07-20 19:27:11 +08:00
|
|
|
<DraggableTabNode {...node.props} key={node.key}>
|
2023-02-13 10:29:12 +08:00
|
|
|
{node}
|
|
|
|
</DraggableTabNode>
|
|
|
|
)}
|
|
|
|
</DefaultTabBar>
|
|
|
|
</SortableContext>
|
|
|
|
</DndContext>
|
|
|
|
)}
|
|
|
|
/>
|
2022-11-09 12:28:04 +08:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default App;
|