demo: remove react-dnd, use dnd-kit (#40674)

* demo: remove react-dnd, use dnd-kit

* change type
This commit is contained in:
linxianxi 2023-02-13 10:29:12 +08:00 committed by GitHub
parent 774bd8e6aa
commit c48c712371
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 295 additions and 296 deletions

View File

@ -1,14 +1,4 @@
const compileModules = [
'array-move',
'react-dnd',
'react-dnd-html5-backend',
'@react-dnd',
'dnd-core',
'react-sticky-box',
'tween-one',
'@babel',
'@ant-design',
];
const compileModules = ['dnd-core', 'react-sticky-box', 'tween-one', '@babel', '@ant-design'];
const ignoreList = [];

View File

@ -1089,54 +1089,60 @@ exports[`renders ./components/tabs/demo/custom-tab-bar-node.tsx extend context c
style="transform:translate(0px, 0px)"
>
<div
style="margin-right:24px"
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class="ant-tabs-tab ant-tabs-tab-active"
data-node-key="1"
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-tabs-tab ant-tabs-tab-active"
data-node-key="1"
aria-selected="true"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
<div
aria-selected="true"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
tab 1
</div>
Tab 1
</div>
</div>
<div
style="margin-right:24px"
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class="ant-tabs-tab"
data-node-key="2"
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-tabs-tab"
data-node-key="2"
aria-selected="false"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
<div
aria-selected="false"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
tab 2
</div>
Tab 2
</div>
</div>
<div
style="margin-right:24px"
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class="ant-tabs-tab"
data-node-key="3"
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-tabs-tab"
data-node-key="3"
aria-selected="false"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
<div
aria-selected="false"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
tab 3
</div>
Tab 3
</div>
</div>
<div

View File

@ -956,54 +956,60 @@ exports[`renders ./components/tabs/demo/custom-tab-bar-node.tsx correctly 1`] =
style="transform:translate(0px, 0px)"
>
<div
style="margin-right:24px"
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class="ant-tabs-tab ant-tabs-tab-active"
data-node-key="1"
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-tabs-tab ant-tabs-tab-active"
data-node-key="1"
aria-selected="true"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
<div
aria-selected="true"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
tab 1
</div>
Tab 1
</div>
</div>
<div
style="margin-right:24px"
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class="ant-tabs-tab"
data-node-key="2"
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-tabs-tab"
data-node-key="2"
aria-selected="false"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
<div
aria-selected="false"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
tab 2
</div>
Tab 2
</div>
</div>
<div
style="margin-right:24px"
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class="ant-tabs-tab"
data-node-key="3"
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-tabs-tab"
data-node-key="3"
aria-selected="false"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
<div
aria-selected="false"
class="ant-tabs-tab-btn"
role="tab"
tabindex="0"
>
tab 3
</div>
Tab 3
</div>
</div>
<div

View File

@ -1,13 +1,7 @@
## zh-CN
使用 `react-dnd@15+` 实现标签可拖拽。
使用 [dnd-kit](https://github.com/clauderic/dnd-kit) 实现标签可拖拽。
## en-US
Use `react-dnd@15+` to make tabs draggable.
```css
.dropping {
transition: all 0.3s;
}
```
Use [dnd-kit](https://github.com/clauderic/dnd-kit) to make tabs draggable.

View File

@ -1,125 +1,112 @@
import React, { useRef, useState } from 'react';
import type { TabsProps } from 'antd';
import type { DragEndEvent } from '@dnd-kit/core';
import { DndContext, PointerSensor, useSensor } from '@dnd-kit/core';
import {
arrayMove,
horizontalListSortingStrategy,
SortableContext,
useSortable,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { css } from '@emotion/css';
import { Tabs } from 'antd';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import React, { useEffect, useState } from 'react';
const type = 'DraggableTabNode';
interface DraggableTabPaneProps extends React.HTMLAttributes<HTMLDivElement> {
index: React.Key;
moveNode: (dragIndex: React.Key, hoverIndex: React.Key) => void;
'data-node-key': string;
onActiveBarTransform: (className: string) => void;
}
const DraggableTabNode = ({ index, children, moveNode }: DraggableTabPaneProps) => {
const ref = useRef<HTMLDivElement>(null);
const [{ isOver }, drop] = useDrop({
accept: type,
collect: (monitor) => {
const { index: dragIndex } = monitor.getItem() || {};
if (dragIndex === index) {
return {};
}
return {
isOver: monitor.isOver(),
};
},
drop: (item: { index: React.Key }) => {
moveNode(item.index, index);
},
const DraggableTabNode = ({ className, onActiveBarTransform, ...props }: DraggableTabPaneProps) => {
const { attributes, listeners, setNodeRef, transform, transition, isSorting } = useSortable({
id: props['data-node-key'],
});
const [, drag] = useDrag({
type,
item: { index },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
});
drop(drag(ref));
// Style
const style: React.CSSProperties = { marginRight: 24 };
if (isOver) {
style.transition = 'all 0.3s';
}
return (
<div ref={ref} style={style}>
{children}
</div>
);
};
const DraggableTabs: React.FC<TabsProps> = (props) => {
const { items = [] } = props;
const [order, setOrder] = useState<React.Key[]>([]);
const moveTabNode = (dragKey: React.Key, hoverKey: React.Key) => {
const newOrder = order.slice();
items.forEach((item) => {
if (item.key && newOrder.indexOf(item.key) === -1) {
newOrder.push(item.key);
}
});
const dragIndex = newOrder.indexOf(dragKey);
const hoverIndex = newOrder.indexOf(hoverKey);
newOrder.splice(dragIndex, 1);
newOrder.splice(hoverIndex, 0, dragKey);
setOrder(newOrder);
const style: React.CSSProperties = {
...props.style,
transform: CSS.Transform.toString(transform),
transition,
cursor: 'move',
};
const renderTabBar: TabsProps['renderTabBar'] = (tabBarProps, DefaultTabBar) => (
<DefaultTabBar {...tabBarProps}>
{(node) => (
<DraggableTabNode key={node.key} index={node.key!} moveNode={moveTabNode}>
{node}
</DraggableTabNode>
)}
</DefaultTabBar>
);
const orderItems = [...items].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;
useEffect(() => {
if (!isSorting) {
onActiveBarTransform('');
} else if (className?.includes('ant-tabs-tab-active')) {
onActiveBarTransform(
css`
.ant-tabs-ink-bar {
transform: ${CSS.Transform.toString(transform)};
transition: ${transition} !important;
}
`,
);
}
}, [className, isSorting, transform]);
const ia = items.indexOf(a);
const ib = items.indexOf(b);
return ia - ib;
return React.cloneElement(props.children as React.ReactElement, {
ref: setNodeRef,
style,
...attributes,
...listeners,
});
};
const App: React.FC = () => {
const [items, setItems] = useState([
{
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',
},
]);
const [className, setClassName] = useState('');
const sensor = useSensor(PointerSensor, { activationConstraint: { distance: 10 } });
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);
});
}
};
return (
<DndProvider backend={HTML5Backend}>
<Tabs renderTabBar={renderTabBar} {...props} items={orderItems} />
</DndProvider>
<Tabs
className={className}
items={items}
renderTabBar={(tabBarProps, DefaultTabBar) => (
<DndContext sensors={[sensor]} onDragEnd={onDragEnd}>
<SortableContext items={items.map((i) => i.key)} strategy={horizontalListSortingStrategy}>
<DefaultTabBar {...tabBarProps}>
{(node) => (
<DraggableTabNode
{...node.props}
key={node.key}
onActiveBarTransform={setClassName}
>
{node}
</DraggableTabNode>
)}
</DefaultTabBar>
</SortableContext>
</DndContext>
)}
/>
);
};
const App: React.FC = () => (
<DraggableTabs
items={new Array(3).fill(null).map((_, i) => {
const id = String(i + 1);
return {
label: `tab ${id}`,
key: id,
children: `Content of Tab Pane ${id}`,
};
})}
/>
);
export default App;

View File

@ -668,8 +668,13 @@ exports[`renders ./components/upload/demo/drag-sorting.tsx extend context correc
class="ant-upload-list-item-container"
>
<div
class="ant-upload-draggable-list-item "
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class=""
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
@ -743,8 +748,13 @@ exports[`renders ./components/upload/demo/drag-sorting.tsx extend context correc
class="ant-upload-list-item-container"
>
<div
class="ant-upload-draggable-list-item "
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class=""
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
@ -818,8 +828,13 @@ exports[`renders ./components/upload/demo/drag-sorting.tsx extend context correc
class="ant-upload-list-item-container"
>
<div
class="ant-upload-draggable-list-item "
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class=""
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
@ -893,8 +908,13 @@ exports[`renders ./components/upload/demo/drag-sorting.tsx extend context correc
class="ant-upload-list-item-container"
>
<div
class="ant-upload-draggable-list-item "
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class=""
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
@ -968,8 +988,13 @@ exports[`renders ./components/upload/demo/drag-sorting.tsx extend context correc
class="ant-upload-list-item-container"
>
<div
class="ant-upload-draggable-list-item "
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class=""
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-upload-list-item ant-upload-list-item-error"
@ -1053,7 +1078,7 @@ exports[`renders ./components/upload/demo/drag-sorting.tsx extend context correc
class="ant-tooltip-inner"
role="tooltip"
>
Upload Error
Upload error
</div>
</div>
</div>

View File

@ -644,8 +644,13 @@ exports[`renders ./components/upload/demo/drag-sorting.tsx correctly 1`] = `
class="ant-upload-list-item-container"
>
<div
class="ant-upload-draggable-list-item "
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class=""
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
@ -719,8 +724,13 @@ exports[`renders ./components/upload/demo/drag-sorting.tsx correctly 1`] = `
class="ant-upload-list-item-container"
>
<div
class="ant-upload-draggable-list-item "
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class=""
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
@ -794,8 +804,13 @@ exports[`renders ./components/upload/demo/drag-sorting.tsx correctly 1`] = `
class="ant-upload-list-item-container"
>
<div
class="ant-upload-draggable-list-item "
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class=""
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
@ -869,8 +884,13 @@ exports[`renders ./components/upload/demo/drag-sorting.tsx correctly 1`] = `
class="ant-upload-list-item-container"
>
<div
class="ant-upload-draggable-list-item "
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class=""
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-upload-list-item ant-upload-list-item-done"
@ -944,8 +964,13 @@ exports[`renders ./components/upload/demo/drag-sorting.tsx correctly 1`] = `
class="ant-upload-list-item-container"
>
<div
class="ant-upload-draggable-list-item "
aria-describedby="DndDescribedBy-0"
aria-disabled="false"
aria-roledescription="sortable"
class=""
role="button"
style="cursor:move"
tabindex="0"
>
<div
class="ant-upload-list-item ant-upload-list-item-error"

View File

@ -1,20 +1,7 @@
## zh-CN
使用 `itemRender` ,我们可以集成 react-dnd 来实现对上传列表拖拽排序。
使用 `itemRender` ,我们可以集成 [dnd-kit](https://github.com/clauderic/dnd-kit) 来实现对上传列表拖拽排序。
## en-US
By using `itemRender`, we can integrate upload with react-dnd to implement drag sorting of uploadList.
```css
#components-upload-demo-drag-sorting .ant-upload-draggable-list-item {
border-top: 2px dashed rgba(0, 0, 0, 0);
border-bottom: 2px dashed rgba(0, 0, 0, 0);
}
#components-upload-demo-drag-sorting .ant-upload-draggable-list-item.drop-over-downward {
border-bottom-color: #1890ff;
}
#components-upload-demo-drag-sorting .ant-upload-draggable-list-item.drop-over-upward {
border-top-color: #1890ff;
}
```
By using `itemRender`, we can integrate upload with [dnd-kit](https://github.com/clauderic/dnd-kit) to implement drag sorting of uploadList.

View File

@ -1,59 +1,47 @@
import { UploadOutlined } from '@ant-design/icons';
import { Button, Tooltip, Upload } from 'antd';
import type { DragEndEvent } from '@dnd-kit/core';
import { DndContext } from '@dnd-kit/core';
import {
arrayMove,
SortableContext,
useSortable,
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { css } from '@emotion/css';
import { Button, Upload } from 'antd';
import type { UploadFile, UploadProps } from 'antd/es/upload/interface';
import React, { useCallback, useRef, useState } from 'react';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
const type = 'DragableUploadList';
import React, { useState } from 'react';
interface DragableUploadListItemProps {
originNode: React.ReactElement<any, string | React.JSXElementConstructor<any>>;
file: UploadFile;
fileList: UploadFile[];
moveRow: (dragIndex: any, hoverIndex: any) => void;
file: UploadFile<any>;
}
const DragableUploadListItem = ({
originNode,
moveRow,
file,
fileList,
}: DragableUploadListItemProps) => {
const ref = useRef<HTMLDivElement>(null);
const index = fileList.indexOf(file);
const [{ isOver, dropClassName }, drop] = useDrop({
accept: type,
collect: (monitor) => {
const { index: dragIndex } = monitor.getItem() || {};
if (dragIndex === index) {
return {};
}
return {
isOver: monitor.isOver(),
dropClassName: dragIndex < index ? ' drop-over-downward' : ' drop-over-upward',
};
},
drop: (item: any) => {
moveRow(item.index, index);
},
const DragableUploadListItem = ({ originNode, file }: DragableUploadListItemProps) => {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
id: file.uid,
});
const [, drag] = useDrag({
type,
item: { index },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
});
drop(drag(ref));
const errorNode = <Tooltip title="Upload Error">{originNode.props.children}</Tooltip>;
const style: React.CSSProperties = {
transform: CSS.Transform.toString(transform),
transition,
cursor: 'move',
};
// prevent preview event when drag end
const className = isDragging
? css`
a {
pointer-events: none;
}
`
: '';
return (
<div
ref={ref}
className={`ant-upload-draggable-list-item ${isOver ? dropClassName : ''}`}
style={{ cursor: 'move' }}
>
{file.status === 'error' ? errorNode : originNode}
<div ref={setNodeRef} style={style} className={className} {...attributes} {...listeners}>
{/* hide error tooltip when dragging */}
{file.status === 'error' && isDragging ? originNode.props.children : originNode}
</div>
);
};
@ -91,41 +79,35 @@ const App: React.FC = () => {
},
]);
const moveRow = useCallback(
(dragIndex: number, hoverIndex: number) => {
const dragRow = fileList[dragIndex];
setFileList((currentFileList) => {
const newFileList = [...currentFileList];
newFileList.splice(dragIndex, 1);
newFileList.splice(hoverIndex, 0, dragRow);
return newFileList;
const onDragEnd = ({ active, over }: DragEndEvent) => {
if (active.id !== over?.id) {
setFileList((prev) => {
const activeIndex = prev.findIndex((i) => i.uid === active.id);
const overIndex = prev.findIndex((i) => i.uid === over?.id);
return arrayMove(prev, activeIndex, overIndex);
});
},
[fileList],
);
}
};
const onChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
setFileList(newFileList);
};
return (
<DndProvider backend={HTML5Backend}>
<Upload
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
fileList={fileList}
onChange={onChange}
itemRender={(originNode, file, currFileList) => (
<DragableUploadListItem
originNode={originNode}
file={file}
fileList={currFileList}
moveRow={moveRow}
/>
)}
>
<Button icon={<UploadOutlined />}>Click to Upload</Button>
</Upload>
</DndProvider>
<DndContext onDragEnd={onDragEnd}>
<SortableContext items={fileList.map((i) => i.uid)} strategy={verticalListSortingStrategy}>
<Upload
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
fileList={fileList}
onChange={onChange}
itemRender={(originNode, file) => (
<DragableUploadListItem originNode={originNode} file={file} />
)}
>
<Button icon={<UploadOutlined />}>Click to Upload</Button>
</Upload>
</SortableContext>
</DndContext>
);
};

View File

@ -196,7 +196,6 @@
"@typescript-eslint/parser": "^5.40.0",
"antd-img-crop": "^4.2.8",
"antd-token-previewer": "^1.1.0-21",
"array-move": "^4.0.0",
"chalk": "^4.0.0",
"cheerio": "1.0.0-rc.12",
"cross-env": "^7.0.0",
@ -256,8 +255,6 @@
"react-color": "^2.17.3",
"react-copy-to-clipboard": "^5.0.1",
"react-countup": "^6.4.0",
"react-dnd": "^16.0.0",
"react-dnd-html5-backend": "^16.0.0",
"react-dom": "^18.0.0",
"react-draggable": "^4.4.3",
"react-fast-marquee": "^1.2.1",