ant-design/components/upload/UploadList/ListItem.tsx

288 lines
8.4 KiB
TypeScript
Raw Normal View History

import * as React from 'react';
import CSSMotion from 'rc-motion';
import classNames from 'classnames';
import EyeOutlined from '@ant-design/icons/EyeOutlined';
import DeleteOutlined from '@ant-design/icons/DeleteOutlined';
import DownloadOutlined from '@ant-design/icons/DownloadOutlined';
import Tooltip from '../../tooltip';
import Progress from '../../progress';
import { ConfigContext } from '../../config-provider';
import {
ItemRender,
UploadFile,
UploadListProgressProps,
UploadListType,
UploadLocale,
} from '../interface';
export interface ListItemProps {
prefixCls: string;
className?: string;
style?: React.CSSProperties;
locale: UploadLocale;
file: UploadFile;
items: UploadFile[];
listType?: UploadListType;
isImgUrl?: (file: UploadFile) => boolean;
showRemoveIcon?: boolean;
showDownloadIcon?: boolean;
showPreviewIcon?: boolean;
removeIcon?: React.ReactNode | ((file: UploadFile) => React.ReactNode);
downloadIcon?: React.ReactNode | ((file: UploadFile) => React.ReactNode);
iconRender: (file: UploadFile) => React.ReactNode;
actionIconRender: (
customIcon: React.ReactNode,
callback: () => void,
prefixCls: string,
title?: string | undefined,
) => React.ReactNode;
itemRender?: ItemRender;
onPreview: (file: UploadFile, e: React.SyntheticEvent<HTMLElement>) => void;
onClose: (file: UploadFile) => void;
onDownload: (file: UploadFile) => void;
progress?: UploadListProgressProps;
}
const ListItem = React.forwardRef(
(
{
prefixCls,
className,
style,
locale,
listType,
file,
items,
progress: progressProps,
iconRender,
actionIconRender,
itemRender,
isImgUrl,
showPreviewIcon,
showRemoveIcon,
showDownloadIcon,
removeIcon: customRemoveIcon,
downloadIcon: customDownloadIcon,
onPreview,
onDownload,
onClose,
}: ListItemProps,
ref: React.Ref<HTMLDivElement>,
) => {
// Delay to show the progress bar
const [showProgress, setShowProgress] = React.useState(false);
const progressRafRef = React.useRef<any>();
React.useEffect(() => {
progressRafRef.current = setTimeout(() => {
setShowProgress(true);
}, 300);
return () => {
window.clearTimeout(progressRafRef.current);
};
}, []);
// This is used for legacy span make scrollHeight the wrong value.
// We will force these to be `display: block` with non `picture-card`
const spanClassName = `${prefixCls}-span`;
const iconNode = iconRender(file);
let icon = <div className={`${prefixCls}-text-icon`}>{iconNode}</div>;
if (listType === 'picture' || listType === 'picture-card') {
if (file.status === 'uploading' || (!file.thumbUrl && !file.url)) {
const uploadingClassName = classNames({
[`${prefixCls}-list-item-thumbnail`]: true,
[`${prefixCls}-list-item-file`]: file.status !== 'uploading',
});
icon = <div className={uploadingClassName}>{iconNode}</div>;
} else {
const thumbnail = isImgUrl?.(file) ? (
<img
src={file.thumbUrl || file.url}
alt={file.name}
className={`${prefixCls}-list-item-image`}
/>
) : (
iconNode
);
const aClassName = classNames({
[`${prefixCls}-list-item-thumbnail`]: true,
[`${prefixCls}-list-item-file`]: isImgUrl && !isImgUrl(file),
});
icon = (
<a
className={aClassName}
onClick={e => onPreview(file, e)}
href={file.url || file.thumbUrl}
target="_blank"
rel="noopener noreferrer"
>
{thumbnail}
</a>
);
}
}
const infoUploadingClass = classNames({
[`${prefixCls}-list-item`]: true,
[`${prefixCls}-list-item-${file.status}`]: true,
[`${prefixCls}-list-item-list-type-${listType}`]: true,
});
const linkProps =
typeof file.linkProps === 'string' ? JSON.parse(file.linkProps) : file.linkProps;
const removeIcon = showRemoveIcon
? actionIconRender(
(typeof customRemoveIcon === 'function' ? customRemoveIcon(file) : customRemoveIcon) || (
<DeleteOutlined />
),
() => onClose(file),
prefixCls,
locale.removeFile,
)
: null;
const downloadIcon =
showDownloadIcon && file.status === 'done'
? actionIconRender(
(typeof customDownloadIcon === 'function'
? customDownloadIcon(file)
: customDownloadIcon) || <DownloadOutlined />,
() => onDownload(file),
prefixCls,
locale.downloadFile,
)
: null;
const downloadOrDelete = listType !== 'picture-card' && (
<span
key="download-delete"
className={classNames(`${prefixCls}-list-item-card-actions`, {
picture: listType === 'picture',
})}
>
{downloadIcon}
{removeIcon}
</span>
);
const listItemNameClass = classNames(`${prefixCls}-list-item-name`);
const preview = file.url
? [
<a
key="view"
target="_blank"
rel="noopener noreferrer"
className={listItemNameClass}
title={file.name}
{...linkProps}
href={file.url}
onClick={e => onPreview(file, e)}
>
{file.name}
</a>,
downloadOrDelete,
]
: [
<span
key="view"
className={listItemNameClass}
onClick={e => onPreview(file, e)}
title={file.name}
>
{file.name}
</span>,
downloadOrDelete,
];
const previewStyle: 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 : previewStyle}
onClick={e => onPreview(file, e)}
title={locale.previewFile}
>
<EyeOutlined />
</a>
) : null;
const actions = listType === 'picture-card' && file.status !== 'uploading' && (
<span className={`${prefixCls}-list-item-actions`}>
{previewIcon}
{file.status === 'done' && downloadIcon}
{removeIcon}
</span>
);
let message;
if (file.response && typeof file.response === 'string') {
message = file.response;
} else {
message = file.error?.statusText || file.error?.message || locale.uploadError;
}
const iconAndPreview = (
<span className={spanClassName}>
{icon}
{preview}
</span>
);
const { getPrefixCls } = React.useContext(ConfigContext);
const rootPrefixCls = getPrefixCls();
const dom = (
<div className={infoUploadingClass}>
<div className={`${prefixCls}-list-item-info`}>{iconAndPreview}</div>
{actions}
{showProgress && (
2021-03-09 23:14:52 +08:00
<CSSMotion
motionName={`${rootPrefixCls}-fade`}
visible={file.status === 'uploading'}
motionDeadline={2000}
>
{({ className: motionClassName }) => {
// show loading icon if upload progress listener is disabled
const loadingProgress =
'percent' in file ? (
<Progress {...progressProps} type="line" percent={file.percent} />
) : null;
return (
<div className={classNames(`${prefixCls}-list-item-progress`, motionClassName)}>
{loadingProgress}
</div>
);
}}
</CSSMotion>
)}
</div>
);
const listContainerNameClass = classNames(`${prefixCls}-list-${listType}-container`, className);
const item =
file.status === 'error' ? (
<Tooltip title={message} getPopupContainer={node => node.parentNode as HTMLElement}>
{dom}
</Tooltip>
) : (
dom
);
return (
<div className={listContainerNameClass} style={style} ref={ref}>
merge featrue into master (#30636) * feat: add Table expandable fixed (#29959) * fix: Use flex gap of space (#30023) * fix: use flex gap when supported * test: update snapshot * refactor: Use single hooks * feat: Allow breadcrumb component in PageHeader (#30019) * Allow breadcrumb component in PageHeader * Allow breadcrumb component in PageHeader * Allow breadcrumb component in PageHeader * Rename variable rename var from _breadcrumbRender to breadcrumbRenderDomFromProps * feat: add onChange for Statistic.Countdown (#30265) * feat: add onChange for countdown * update the demo * feat(upload): add onDrop (#30319) * feat(upload): Add onDrop * Replace "if prop" logic with existential operator * Remove redundant conditional * feat(upload): itemRender add actions params (#30236) * feat(upload): itemRender add actions params * chore: optimize type definition * chore: update doc * chore: rename actions * chore: trigger ci * chore: rename method name of actions * feat: Add missing dutch translations (#30389) * feat: Add missing dutch translations * fix: Translate remaining english values * fix: Update snapshot for ui tests * test: increase code coverage to 100% (#30415) * test: fix Space code coverage * test: should use nl_BE locale for DatePicker * fix: Switch tabIndex type (#30416) * feat: updated Romanian internationalization (#30419) * feat: updated Romanian internationalization * fixed lint error * feat: Menu support accessibility & keyboard access (#30382) * chore: Use focus style * fix: prefixCls * fix: prefixCls * fix: inline tooltip * fix: inlineCollapse logic * fix: ts definition * test: Update snapshot * test: Update snapshot * fix: dropdown logic * test: Update snapshot * test: Fix some test case * bump rc-menu * test: More test case * fix test finder * test: fix test case * test: Update snapshot * test: Update snapshot * chore: Update ssr effect * test: Update ConfigProvider snapshot * test: Fix Table Filter test case * test: Fix table test case * chore: Update style * chore: beauti css * bump rc-menu * test: update snapshot * test: update snapshot * test: Fix menu test * test: Fix test case * test: Coverage * chore: clean up * bump rc-menu * ehance accessibility style * feat(radioGroup): support data-* and aria-* props (#30507) close #30501 * feat: Typography add italic type (#30458) * Typography增加斜体字支持 * update snapshot * 文档添加版本号 Co-authored-by: lidahao <lidahao@sisyphe.com.cn> * chore: alpha Menu fix merge (#30546) * chore: Update script * bump alpha version * chore: Update script desc * chore: bump rc-tabs & rc-mentions * chore: Adjust style * chore: 4.16.0-alpha.1 * test: Fix mention test case * fix: sider of layout width style * chore: bump 4.16.0-alpha.2 * fix: Tabs tabBarGutter should work as expected (#30545) close #30526 * feat: Table summary support sticky mode (#30631) * chore: Bump rc-table * feat: Patch summary fixed color * fix: style className * test: Update snapshot Co-authored-by: xrkffgg <xrkffgg@gmail.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: 二货机器人 <smith3816@gmail.com> Co-authored-by: gepd <guillermoepd@hotmail.com> Co-authored-by: appleshell <appleshell@outlook.com> Co-authored-by: Eric Bonow <ebonow@hotmail.com> Co-authored-by: xrkffgg <xrkffgg@vip.qq.com> Co-authored-by: Kermit <kermitlx@outlook.com> Co-authored-by: Lewis <lewisfidlers@gmail.com> Co-authored-by: afc163 <afc163@gmail.com> Co-authored-by: Ștefan Filip <stefy.filip@gmail.com> Co-authored-by: vldh <alwaysloseall@sina.com> Co-authored-by: lidahao <lidahao@sisyphe.com.cn>
2021-05-24 16:24:00 +08:00
{itemRender
? itemRender(item, file, items, {
download: onDownload.bind(null, file),
preview: onPreview.bind(null, file),
remove: onClose.bind(null, file),
})
: item}
</div>
);
},
);
export default ListItem;