2019-01-16 20:00:23 +08:00
|
|
|
---
|
|
|
|
order: 13
|
|
|
|
title:
|
|
|
|
zh-CN: 可拖拽标签
|
|
|
|
en-US: Draggable Tabs
|
|
|
|
---
|
|
|
|
|
|
|
|
## zh-CN
|
|
|
|
|
2022-02-10 17:31:38 +08:00
|
|
|
使用 `react-dnd@15+` 实现标签可拖拽。
|
2019-01-16 20:00:23 +08:00
|
|
|
|
|
|
|
## en-US
|
|
|
|
|
2022-02-10 17:31:38 +08:00
|
|
|
Use `react-dnd@15+` to make tabs draggable.
|
2019-01-16 20:00:23 +08:00
|
|
|
|
2019-05-07 14:57:32 +08:00
|
|
|
```jsx
|
2022-02-10 17:31:38 +08:00
|
|
|
import React, { useRef, cloneElement } from 'react';
|
2019-01-16 20:00:23 +08:00
|
|
|
import { Tabs } from 'antd';
|
2022-02-10 17:31:38 +08:00
|
|
|
import { DndProvider, useDrag, useDrop } from 'react-dnd';
|
2020-05-28 23:12:04 +08:00
|
|
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
2019-01-16 20:00:23 +08:00
|
|
|
|
2019-05-27 21:32:45 +08:00
|
|
|
const { TabPane } = Tabs;
|
2019-01-16 20:00:23 +08:00
|
|
|
|
2022-02-10 17:31:38 +08:00
|
|
|
const type = 'DraggableTabNode';
|
2019-01-16 20:00:23 +08:00
|
|
|
|
2022-02-10 17:31:38 +08:00
|
|
|
const DraggableTabNode = ({ index, children, moveNode }) => {
|
|
|
|
const ref = useRef();
|
|
|
|
const [{ isOver, dropClassName }, drop] = useDrop({
|
|
|
|
accept: type,
|
|
|
|
collect: monitor => {
|
|
|
|
const { index: dragIndex } = monitor.getItem() || {};
|
|
|
|
if (dragIndex === index) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
isOver: monitor.isOver(),
|
|
|
|
dropClassName: 'dropping',
|
|
|
|
};
|
|
|
|
},
|
|
|
|
drop: item => {
|
|
|
|
moveNode(item.index, index);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
const [, drag] = useDrag({
|
|
|
|
type,
|
|
|
|
item: { index },
|
|
|
|
collect: monitor => ({
|
|
|
|
isDragging: monitor.isDragging(),
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
drop(drag(ref));
|
|
|
|
return (
|
2022-03-31 14:06:44 +08:00
|
|
|
<div ref={ref} className={`dragnode ${isOver ? dropClassName : ''}`}>
|
2022-02-10 17:31:38 +08:00
|
|
|
{children}
|
|
|
|
</div>
|
|
|
|
);
|
2019-01-16 20:00:23 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
class DraggableTabs extends React.Component {
|
|
|
|
state = {
|
|
|
|
order: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
moveTabNode = (dragKey, hoverKey) => {
|
|
|
|
const newOrder = this.state.order.slice();
|
|
|
|
const { children } = this.props;
|
|
|
|
|
2019-05-07 14:57:32 +08:00
|
|
|
React.Children.forEach(children, c => {
|
2019-01-16 20:00:23 +08:00
|
|
|
if (newOrder.indexOf(c.key) === -1) {
|
|
|
|
newOrder.push(c.key);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const dragIndex = newOrder.indexOf(dragKey);
|
|
|
|
const hoverIndex = newOrder.indexOf(hoverKey);
|
|
|
|
|
|
|
|
newOrder.splice(dragIndex, 1);
|
|
|
|
newOrder.splice(hoverIndex, 0, dragKey);
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
order: newOrder,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
renderTabBar = (props, DefaultTabBar) => (
|
|
|
|
<DefaultTabBar {...props}>
|
|
|
|
{node => (
|
2022-02-10 17:31:38 +08:00
|
|
|
<DraggableTabNode key={node.key} index={node.key} moveNode={this.moveTabNode}>
|
2019-05-07 14:57:32 +08:00
|
|
|
{node}
|
2022-02-10 17:31:38 +08:00
|
|
|
</DraggableTabNode>
|
2019-01-16 20:00:23 +08:00
|
|
|
)}
|
|
|
|
</DefaultTabBar>
|
|
|
|
);
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const { order } = this.state;
|
|
|
|
const { children } = this.props;
|
|
|
|
|
|
|
|
const tabs = [];
|
2019-05-07 14:57:32 +08:00
|
|
|
React.Children.forEach(children, c => {
|
2019-01-16 20:00:23 +08:00
|
|
|
tabs.push(c);
|
|
|
|
});
|
|
|
|
|
|
|
|
const orderTabs = tabs.slice().sort((a, b) => {
|
|
|
|
const orderA = order.indexOf(a.key);
|
|
|
|
const orderB = order.indexOf(b.key);
|
|
|
|
|
|
|
|
if (orderA !== -1 && orderB !== -1) {
|
|
|
|
return orderA - orderB;
|
|
|
|
}
|
|
|
|
if (orderA !== -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (orderB !== -1) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ia = tabs.indexOf(a);
|
|
|
|
const ib = tabs.indexOf(b);
|
|
|
|
|
|
|
|
return ia - ib;
|
|
|
|
});
|
|
|
|
|
|
|
|
return (
|
2019-06-17 12:00:52 +08:00
|
|
|
<DndProvider backend={HTML5Backend}>
|
2019-05-07 14:57:32 +08:00
|
|
|
<Tabs renderTabBar={this.renderTabBar} {...this.props}>
|
2019-01-16 20:00:23 +08:00
|
|
|
{orderTabs}
|
|
|
|
</Tabs>
|
2019-06-17 12:00:52 +08:00
|
|
|
</DndProvider>
|
2019-01-16 20:00:23 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-03 23:27:45 +08:00
|
|
|
export default () => (
|
2019-01-16 20:00:23 +08:00
|
|
|
<DraggableTabs>
|
|
|
|
<TabPane tab="tab 1" key="1">
|
|
|
|
Content of Tab Pane 1
|
|
|
|
</TabPane>
|
|
|
|
<TabPane tab="tab 2" key="2">
|
|
|
|
Content of Tab Pane 2
|
|
|
|
</TabPane>
|
|
|
|
<TabPane tab="tab 3" key="3">
|
|
|
|
Content of Tab Pane 3
|
|
|
|
</TabPane>
|
2022-04-03 23:27:45 +08:00
|
|
|
</DraggableTabs>
|
2019-05-07 14:57:32 +08:00
|
|
|
);
|
|
|
|
```
|
2022-02-10 17:31:38 +08:00
|
|
|
|
|
|
|
```css
|
|
|
|
.dropping {
|
|
|
|
background: #fefefe;
|
|
|
|
transition: all 0.3s;
|
|
|
|
}
|
2022-03-31 14:06:44 +08:00
|
|
|
|
|
|
|
.dragnode {
|
|
|
|
margin-inline-end: 24px;
|
|
|
|
}
|
2022-02-10 17:31:38 +08:00
|
|
|
```
|