diff --git a/components/upload/Upload.tsx b/components/upload/Upload.tsx index 2153774645..61f11433a5 100644 --- a/components/upload/Upload.tsx +++ b/components/upload/Upload.tsx @@ -43,6 +43,7 @@ const InternalUpload: React.ForwardRefRenderFunction = (pr type, children, style, + itemRender, } = props; const [dragState, setDragState] = React.useState('drop'); @@ -261,6 +262,7 @@ const InternalUpload: React.ForwardRefRenderFunction = (pr isImageUrl={isImageUrl} progress={progress} appendAction={button} + itemRender={itemRender} /> ); }} diff --git a/components/upload/UploadList.tsx b/components/upload/UploadList.tsx index f156788f3b..ed6357eadd 100644 --- a/components/upload/UploadList.tsx +++ b/components/upload/UploadList.tsx @@ -37,6 +37,7 @@ const InternalUploadList: React.ForwardRefRenderFunction { @@ -210,7 +211,9 @@ const InternalUploadList: React.ForwardRefRenderFunction, + (typeof customRemoveIcon === 'function' ? customRemoveIcon(file) : customRemoveIcon) || ( + + ), () => handleClose(file), prefixCls, locale.removeFile, @@ -220,7 +223,9 @@ const InternalUploadList: React.ForwardRefRenderFunction, + (typeof customDownloadIcon === 'function' + ? customDownloadIcon(file) + : customDownloadIcon) || , () => handleDownload(file), prefixCls, locale.downloadFile, @@ -317,15 +322,17 @@ const InternalUploadList: React.ForwardRefRenderFunction node.parentNode as HTMLElement}> + {dom} + + ) : ( + {dom} + ); return (
- {file.status === 'error' ? ( - node.parentNode as HTMLElement}> - {dom} - - ) : ( - {dom} - )} + {itemRender ? itemRender(item, file, items) : item}
); }); diff --git a/components/upload/__tests__/__snapshots__/demo.test.js.snap b/components/upload/__tests__/__snapshots__/demo.test.js.snap index c799354617..56375009fd 100644 --- a/components/upload/__tests__/__snapshots__/demo.test.js.snap +++ b/components/upload/__tests__/__snapshots__/demo.test.js.snap @@ -690,6 +690,480 @@ exports[`renders ./components/upload/demo/drag.md correctly 1`] = ` `; +exports[`renders ./components/upload/demo/drag-sorting.md correctly 1`] = ` + +
+ + + + +
+
+
+
+ +
+
+ +
+ + + +
+ + image1.png + + + + +
+
+
+
+
+
+
+
+ +
+
+ +
+ + + +
+ + image2.png + + + + +
+
+
+
+
+
+
+
+ +
+
+ +
+ + + +
+ + image3.png + + + + +
+
+
+
+
+
+
+
+ +
+
+ +
+ + + +
+ + image4.png + + + + +
+
+
+
+
+
+
+
+
+
+ +
+ + + +
+ + image.png + + + + +
+
+
+
+
+
+
+`; + exports[`renders ./components/upload/demo/file-type.md correctly 1`] = ` `; +exports[`Upload List itemRender 1`] = ` +
+
+ + uid:-1 name: xxx.png status: removed url: https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png 1/2 + +
+
+ + uid:-2 name: yyy.png status: removed url: https://zos.alipayobjects.com/rmsportal/IQKRngzUuFzJzGzRJXUs.png 2/2 + +
+
+`; + exports[`Upload List should be uploading when upload a file 1`] = ` { jest.useRealTimers(); }); + + it('itemRender', () => { + const itemRender = (originNode, file, currFileList) => { + const { name, status, uid, url } = file; + const index = currFileList.indexOf(file); + return ( + + {`uid:${uid} name: ${name} status: ${status} url: ${url} ${index + 1}/${ + currFileList.length + }`} + + ); + }; + const wrapper = mount(); + expect(wrapper.render()).toMatchSnapshot(); + }); }); diff --git a/components/upload/demo/drag-sorting.md b/components/upload/demo/drag-sorting.md new file mode 100644 index 0000000000..f813ce7e1e --- /dev/null +++ b/components/upload/demo/drag-sorting.md @@ -0,0 +1,164 @@ +--- +order: 13 +title: + zh-CN: 上传列表拖拽排序 + en-US: Drag sorting of uploadList +--- + +## zh-CN + +使用 `itemRender` ,我们可以集成 react-dnd 来实现对上传列表拖拽排序。 + +## en-US + +By using `itemRender`, we can integrate upload with react-dnd to implement drag sorting of uploadList. + +```jsx +import React, { useState, useCallback, useRef } from 'react'; +import { Upload, Button, Tooltip } from 'antd'; +import { DndProvider, useDrag, useDrop, createDndContext } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; +import update from 'immutability-helper'; +import { UploadOutlined } from '@ant-design/icons'; + +const RNDContext = createDndContext(HTML5Backend); + +const type = 'DragableUploadList'; + +const DragableUploadListItem = ({ originNode, moveRow, file, fileList }) => { + const ref = React.useRef(); + 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 => { + moveRow(item.index, index); + }, + }); + const [, drag] = useDrag({ + item: { type, index }, + collect: monitor => ({ + isDragging: monitor.isDragging(), + }), + }); + drop(drag(ref)); + const errorNode = ( + document.body}> + {originNode.props.children} + + ); + return ( +
+ {file.status === 'error' ? errorNode : originNode} +
+ ); +}; + +const DragSortingUpload: React.FC = () => { + const [fileList, setFileList] = useState([ + { + uid: '-1', + name: 'image1.png', + status: 'done', + url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', + }, + { + uid: '-2', + name: 'image2.png', + status: 'done', + url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', + }, + { + uid: '-3', + name: 'image3.png', + status: 'done', + url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', + }, + { + uid: '-4', + name: 'image4.png', + status: 'done', + url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', + }, + { + uid: '-5', + name: 'image.png', + status: 'error', + }, + ]); + + const moveRow = useCallback( + (dragIndex, hoverIndex) => { + const dragRow = fileList[dragIndex]; + setFileList( + update(fileList, { + $splice: [ + [dragIndex, 1], + [hoverIndex, 0, dragRow], + ], + }), + ); + }, + [fileList], + ); + + const manager = useRef(RNDContext); + + const onChange = ({ fileList: newFileList }) => { + setFileList(newFileList); + }; + + return ( + + { + return ( + + ); + }} + > + + + + ); +}; + +ReactDOM.render(, mountNode); +``` + +```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; +} +``` diff --git a/components/upload/index.en-US.md b/components/upload/index.en-US.md index 2095c7f2f4..866c976da6 100644 --- a/components/upload/index.en-US.md +++ b/components/upload/index.en-US.md @@ -45,6 +45,7 @@ Uploading is the process of publishing information (web pages, text, pictures, v | transformFile   | Customize transform file before request | Function(file): string \| Blob \| File \| Promise<string \| Blob \| File> | - | | | iconRender | Custom show icon | (file: UploadFile, listType?: UploadListType) => ReactNode | - | | | progress | Custom progress bar | [ProgressProps](/components/progress/#API) (support `type="line"` only) | { strokeWidth: 2, showInfo: false } | 4.3.0 | +| itemRender | Custom item of uploadList | (originNode: ReactElement, file: UploadFile, fileList?: object\[]) => React.ReactNode | - | 4.7.0 | ### onChange diff --git a/components/upload/index.zh-CN.md b/components/upload/index.zh-CN.md index d3c14e1861..6564eb2bce 100644 --- a/components/upload/index.zh-CN.md +++ b/components/upload/index.zh-CN.md @@ -46,6 +46,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QaeBt_ZMg/Upload.svg | transformFile   | 在上传之前转换文件。支持返回一个 Promise 对象   | function(file): string \| Blob \| File \| Promise<string \| Blob \| File> | -   | | | iconRender | 自定义显示 icon | (file: UploadFile, listType?: UploadListType) => ReactNode | - | | | progress | 自定义进度条样式 | [ProgressProps](/components/progress/#API)(仅支持 `type="line"`) | { strokeWidth: 2, showInfo: false } | 4.3.0 | +| itemRender | 自定义上传列表项 | (originNode: ReactElement, file: UploadFile, fileList?: object\[]) => React.ReactNode | - | 4.7.0 | ### onChange diff --git a/components/upload/interface.tsx b/components/upload/interface.tsx index 05ef560675..28464a763f 100755 --- a/components/upload/interface.tsx +++ b/components/upload/interface.tsx @@ -111,6 +111,11 @@ export interface UploadProps { iconRender?: (file: UploadFile, listType?: UploadListType) => React.ReactNode; isImageUrl?: (file: UploadFile) => boolean; progress?: UploadListProgressProps; + itemRender?: ( + originNode: React.ReactElement, + file: UploadFile, + fileList?: Array>, + ) => React.ReactNode; } export interface UploadState { @@ -136,4 +141,9 @@ export interface UploadListProps { iconRender?: (file: UploadFile, listType?: UploadListType) => React.ReactNode; isImageUrl?: (file: UploadFile) => boolean; appendAction?: React.ReactNode; + itemRender?: ( + originNode: React.ReactElement, + file: UploadFile, + fileList?: Array>, + ) => React.ReactNode; }