mirror of
https://github.com/ant-design/ant-design.git
synced 2024-12-01 23:29:30 +08:00
efb23c1525
1. should support json string format 2. should be able to override props like rel, target, title and etc
226 lines
7.2 KiB
TypeScript
226 lines
7.2 KiB
TypeScript
import * as React from 'react';
|
|
import Animate from 'rc-animate';
|
|
import Icon from '../icon';
|
|
import Tooltip from '../tooltip';
|
|
import Progress from '../progress';
|
|
import classNames from 'classnames';
|
|
import { UploadListProps, UploadFile, UploadListType } from './interface';
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
|
|
const previewFile = (file: File, callback: Function) => {
|
|
const reader = new FileReader();
|
|
reader.onloadend = () => callback(reader.result);
|
|
reader.readAsDataURL(file);
|
|
};
|
|
|
|
const extname = (url: string) => {
|
|
if (!url) {
|
|
return '';
|
|
}
|
|
const temp = url.split('/');
|
|
const filename = temp[temp.length - 1];
|
|
const filenameWithoutSuffix = filename.split(/#|\?/)[0];
|
|
return (/\.[^./\\]*$/.exec(filenameWithoutSuffix) || [''])[0];
|
|
};
|
|
const imageTypes: string[] = ['image', 'webp', 'png', 'svg', 'gif', 'jpg', 'jpeg', 'bmp'];
|
|
const isImageUrl = (file: UploadFile): boolean => {
|
|
if (imageTypes.includes(file.type)) {
|
|
return true;
|
|
}
|
|
const url: string = (file.thumbUrl || file.url) as string;
|
|
const extension = extname(url);
|
|
if (/^data:image\//.test(url) || /(webp|svg|png|gif|jpg|jpeg|bmp)$/i.test(extension)) {
|
|
return true;
|
|
} else if (/^data:/.test(url)) { // other file types of base64
|
|
return false;
|
|
} else if (extension) { // other file types which have extension
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
export default class UploadList extends React.Component<UploadListProps, any> {
|
|
static defaultProps = {
|
|
listType: 'text' as UploadListType, // or picture
|
|
progressAttr: {
|
|
strokeWidth: 2,
|
|
showInfo: false,
|
|
},
|
|
prefixCls: 'ant-upload',
|
|
showRemoveIcon: true,
|
|
showPreviewIcon: true,
|
|
};
|
|
|
|
handleClose = (file: UploadFile) => {
|
|
const { onRemove } = this.props;
|
|
if (onRemove) {
|
|
onRemove(file);
|
|
}
|
|
}
|
|
|
|
handlePreview = (file: UploadFile, e: React.SyntheticEvent<HTMLElement>) => {
|
|
const { onPreview } = this.props;
|
|
if (!onPreview) {
|
|
return;
|
|
}
|
|
e.preventDefault();
|
|
return onPreview(file);
|
|
}
|
|
|
|
componentDidUpdate() {
|
|
if (this.props.listType !== 'picture' && this.props.listType !== 'picture-card') {
|
|
return;
|
|
}
|
|
(this.props.items || []).forEach(file => {
|
|
if (typeof document === 'undefined' ||
|
|
typeof window === 'undefined' ||
|
|
!(window as any).FileReader || !(window as any).File ||
|
|
!(file.originFileObj instanceof File) ||
|
|
file.thumbUrl !== undefined) {
|
|
return;
|
|
}
|
|
/*eslint-disable */
|
|
file.thumbUrl = '';
|
|
/*eslint-enable */
|
|
previewFile(file.originFileObj, (previewDataUrl: string) => {
|
|
/*eslint-disable */
|
|
file.thumbUrl = previewDataUrl;
|
|
/*eslint-enable */
|
|
this.forceUpdate();
|
|
});
|
|
});
|
|
}
|
|
|
|
render() {
|
|
const { prefixCls, items = [], listType, showPreviewIcon, showRemoveIcon, locale } = this.props;
|
|
const list = items.map(file => {
|
|
let progress;
|
|
let icon = <Icon type={file.status === 'uploading' ? 'loading' : 'paper-clip'} />;
|
|
|
|
if (listType === 'picture' || listType === 'picture-card') {
|
|
if (listType === 'picture-card' && file.status === 'uploading') {
|
|
icon = <div className={`${prefixCls}-list-item-uploading-text`}>{locale.uploading}</div>;
|
|
} else if (!file.thumbUrl && !file.url) {
|
|
icon = <Icon className={`${prefixCls}-list-item-thumbnail`} type="picture" />;
|
|
} else {
|
|
let thumbnail = isImageUrl(file)
|
|
? <img src={file.thumbUrl || file.url} alt={file.name} />
|
|
: <Icon type="file" className={`${prefixCls}-list-item-icon`} />;
|
|
icon = (
|
|
<a
|
|
className={`${prefixCls}-list-item-thumbnail`}
|
|
onClick={e => this.handlePreview(file, e)}
|
|
href={file.url || file.thumbUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
{thumbnail}
|
|
</a>
|
|
);
|
|
}
|
|
}
|
|
|
|
if (file.status === 'uploading') {
|
|
// show loading icon if upload progress listener is disabled
|
|
const loadingProgress = ('percent' in file) ? (
|
|
<Progress type="line" {...this.props.progressAttr} percent={file.percent} />
|
|
) : null;
|
|
|
|
progress = (
|
|
<div className={`${prefixCls}-list-item-progress`} key="progress">
|
|
{loadingProgress}
|
|
</div>
|
|
);
|
|
}
|
|
const infoUploadingClass = classNames({
|
|
[`${prefixCls}-list-item`]: true,
|
|
[`${prefixCls}-list-item-${file.status}`]: true,
|
|
});
|
|
const linkProps = typeof file.linkProps === 'string'
|
|
? JSON.parse(file.linkProps) : file.linkProps;
|
|
const preview = file.url ? (
|
|
<a
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className={`${prefixCls}-list-item-name`}
|
|
title={file.name}
|
|
{...linkProps}
|
|
href={file.url}
|
|
onClick={e => this.handlePreview(file, e)}
|
|
>
|
|
{file.name}
|
|
</a>
|
|
) : (
|
|
<span
|
|
className={`${prefixCls}-list-item-name`}
|
|
onClick={e => this.handlePreview(file, e)}
|
|
title={file.name}
|
|
>
|
|
{file.name}
|
|
</span>
|
|
);
|
|
const style: React.CSSProperties = {
|
|
pointerEvents: 'none',
|
|
opacity: 0.5,
|
|
};
|
|
const previewIcon = showPreviewIcon ? (
|
|
<a
|
|
href={file.url || file.thumbUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
style={(file.url || file.thumbUrl) ? undefined : style}
|
|
onClick={e => this.handlePreview(file, e)}
|
|
title={locale.previewFile}
|
|
>
|
|
<Icon type="eye-o" />
|
|
</a>
|
|
) : null;
|
|
const removeIcon = showRemoveIcon ? (
|
|
<Icon type="delete" title={locale.removeFile} onClick={() => this.handleClose(file)} />
|
|
) : null;
|
|
const removeIconClose = showRemoveIcon ? (
|
|
<Icon type="close" title={locale.removeFile} onClick={() => this.handleClose(file)} />
|
|
) : null;
|
|
const actions = (listType === 'picture-card' && file.status !== 'uploading')
|
|
? <span className={`${prefixCls}-list-item-actions`}>{previewIcon}{removeIcon}</span>
|
|
: removeIconClose;
|
|
let message;
|
|
if (file.response && typeof file.response === 'string') {
|
|
message = file.response;
|
|
} else {
|
|
message = (file.error && file.error.statusText) || locale.uploadError;
|
|
}
|
|
const iconAndPreview = (file.status === 'error')
|
|
? <Tooltip title={message}>{icon}{preview}</Tooltip>
|
|
: <span>{icon}{preview}</span>;
|
|
|
|
return (
|
|
<div className={infoUploadingClass} key={file.uid}>
|
|
<div className={`${prefixCls}-list-item-info`}>
|
|
{iconAndPreview}
|
|
</div>
|
|
{actions}
|
|
<Animate transitionName="fade" component="">
|
|
{progress}
|
|
</Animate>
|
|
</div>
|
|
);
|
|
});
|
|
const listClassNames = classNames({
|
|
[`${prefixCls}-list`]: true,
|
|
[`${prefixCls}-list-${listType}`]: true,
|
|
});
|
|
const animationDirection =
|
|
listType === 'picture-card' ? 'animate-inline' : 'animate';
|
|
return (
|
|
<Animate
|
|
transitionName={`${prefixCls}-${animationDirection}`}
|
|
component="div"
|
|
className={listClassNames}
|
|
>
|
|
{list}
|
|
</Animate>
|
|
);
|
|
}
|
|
}
|