import React, { PureComponent, ReactNode, MouseEventHandler, MouseEvent, CSSProperties, SVGProps, FC } from 'react'; import cls from 'classnames'; import PropTypes from 'prop-types'; import { cssClasses, strings } from '@douyinfe/semi-foundation/upload/constants'; import { getFileSize } from '@douyinfe/semi-foundation/upload/utils'; import { IconAlertCircle, IconClose, IconFile, IconRefresh } from '@douyinfe/semi-icons'; import LocaleConsumer from '../locale/localeConsumer'; import { Locale } from '../locale/interface'; import IconButton from '../iconButton/index'; import Progress from '../progress/index'; import Tooltip from '../tooltip/index'; import Spin from '../spin/index'; import { isElement } from '../_base/reactUtils'; import { RenderFileItemProps } from './interface'; const prefixCls = cssClasses.PREFIX; const ErrorSvg: FC> = (props = {}) => ( ); const ReplaceSvg: FC> = (props = {}) => ( ); const DirectorySvg: FC> = (props = {}) => ( ); export interface FileCardProps extends RenderFileItemProps { className?: string; style?: CSSProperties; } class FileCard extends PureComponent { static propTypes = { className: PropTypes.string, disabled: PropTypes.bool, listType: PropTypes.string, name: PropTypes.string, onPreviewClick: PropTypes.func, onRemove: PropTypes.func, onReplace: PropTypes.func, onRetry: PropTypes.func, percent: PropTypes.number, preview: PropTypes.bool, previewFile: PropTypes.func, showReplace: PropTypes.bool, showRetry: PropTypes.bool, size: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), status: PropTypes.string, style: PropTypes.object, url: PropTypes.string, validateMessage: PropTypes.node, index: PropTypes.number }; static defaultProps = { listType: strings.FILE_LIST_DEFAULT, name: '', onRemove: (): void => undefined, onRetry: (): void => undefined, preview: false, size: '', }; transSize(size: string | number): string { if (typeof size === 'number') { return getFileSize(size); } return size; } renderValidateMessage(): ReactNode { const { status, validateMessage } = this.props; let content = null; switch (true) { case typeof validateMessage === 'string' && status === strings.FILE_STATUS_VALIDATING: content = (<>{validateMessage}); break; case typeof validateMessage === 'string': content = (<>{validateMessage}); break; case isElement(validateMessage): content = validateMessage; break; default: break; } return content; } renderPicValidateMsg(): ReactNode { const { status, validateMessage } = this.props; let icon = null; switch (true) { case validateMessage && status === strings.FILE_STATUS_VALIDATING: icon = (); break; case validateMessage && (status === strings.FILE_STATUS_VALID_FAIL || status === strings.FILE_STATUS_UPLOAD_FAIL): icon = (
); break; default: break; } return icon ? {icon} : null; } renderPic(locale: Locale['Upload']): ReactNode { const { url, percent, status, disabled, style, onPreviewClick, showPicInfo, renderPicInfo, renderThumbnail, name, index } = this.props; const showProgress = status === strings.FILE_STATUS_UPLOADING && percent !== 100; const showRetry = status === strings.FILE_STATUS_UPLOAD_FAIL && this.props.showRetry; const showReplace = status === strings.FILE_STATUS_SUCCESS && this.props.showReplace; const filePicCardCls = cls({ [`${prefixCls}-picture-file-card`]: true, [`${prefixCls}-picture-file-card-disabled`]: disabled, [`${prefixCls}-picture-file-card-show-pointer`]: typeof onPreviewClick !== 'undefined', [`${prefixCls}-picture-file-card-error`]: status === strings.FILE_STATUS_UPLOAD_FAIL, [`${prefixCls}-picture-file-card-uploading`]: showProgress }); const closeCls = `${prefixCls}-picture-file-card-close`; const retry = (
this.onRetry(e)}>
); const replace = (
this.onReplace(e)}>
); const picInfo = typeof renderPicInfo === 'function' ? renderPicInfo(this.props) : (
{index + 1}
); const thumbnail = typeof renderThumbnail === 'function' ? renderThumbnail(this.props) : {name}; return (
{thumbnail} {showProgress ? : null} {showRetry ? retry : null} {showReplace && replace} {showPicInfo && picInfo} {!disabled && (
this.onRemove(e)} />
)} {this.renderPicValidateMsg()}
); } renderFile(locale: Locale["Upload"]) { const { name, size, percent, url, showRetry: propsShowRetry, showReplace: propsShowReplace, preview, previewFile, status, style, onPreviewClick } = this.props; const fileCardCls = cls({ [`${prefixCls}-file-card`]: true, [`${prefixCls}-file-card-fail`]: status === strings.FILE_STATUS_VALID_FAIL || status === strings.FILE_STATUS_UPLOAD_FAIL, [`${prefixCls}-file-card-show-pointer`]: typeof onPreviewClick !== 'undefined', }); const previewCls = cls({ [`${prefixCls}-file-card-preview`]: true, [`${prefixCls}-file-card-preview-placeholder`]: !preview || previewFile }); const infoCls = `${prefixCls}-file-card-info`; const closeCls = `${prefixCls}-file-card-close`; const replaceCls = `${prefixCls}-file-card-replace`; const showProgress = !(percent === 100 || typeof percent === 'undefined') && status === strings.FILE_STATUS_UPLOADING; // only show retry when upload fail & showRetry is true, no need to show during validate fail const showRetry = status === strings.FILE_STATUS_UPLOAD_FAIL && propsShowRetry; const showReplace = status === strings.FILE_STATUS_SUCCESS && propsShowReplace; const fileSize = this.transSize(size); let previewContent: ReactNode = preview ? ({name}) : (); if (previewFile) { previewContent = previewFile(this.props); } return (
{previewContent}
{name} {fileSize} {showReplace && ( this.onReplace(e)} type="tertiary" theme="borderless" size="small" icon={} className={replaceCls} /> )}
{showProgress ? () : null}
{this.renderValidateMessage()} {showRetry ? this.onRetry(e)}>{locale.retry} : null}
this.onRemove(e)} type="tertiary" icon={} theme="borderless" size="small" className={closeCls} />
); } onRemove(e: MouseEvent): void { e.stopPropagation(); this.props.onRemove(this.props, e); } onReplace(e: MouseEvent): void { e.stopPropagation(); this.props.onReplace(this.props, e); } onRetry(e: MouseEvent): void { e.stopPropagation(); this.props.onRetry(this.props, e); } render() { const { listType } = this.props; if (listType === strings.FILE_LIST_PIC) { return ( {(locale: Locale["Upload"]) => (this.renderPic(locale))} ); } if (listType === strings.FILE_LIST_DEFAULT) { return ( {(locale: Locale["Upload"]) => (this.renderFile(locale))} ); } return null; } } export default FileCard;