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 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 { IconAlertCircle, IconClose, IconFile, IconRefresh } from '@douyinfe/semi-icons'; const prefixCls = cssClasses.PREFIX; const ErrorSvg: FC> = (props = {}) => ( ); const ReplaceSvg: FC> = (props = {}) => ( ); const DirectorySvg: FC> = (props = {}) => ( ); export interface FileCardProps { className?: string; disabled?: boolean; listType?: 'picture' | 'list'; name?: string; onPreviewClick?: MouseEventHandler; onRemove?: (props: FileCardProps, e: MouseEvent) => void; onReplace?: (props: FileCardProps, e: MouseEvent) => void; onRetry?: (props: FileCardProps, e: MouseEvent) => void; percent?: number; preview?: boolean; previewFile?: (props: FileCardProps) => ReactNode; showReplace?: boolean; showRetry?: boolean; size?: string; status?: string; style?: CSSProperties; url?: string; validateMessage?: ReactNode; } 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, }; 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 } = this.props; const filePicCardCls = cls({ [`${prefixCls }-picture-file-card`]: true, [`${prefixCls }-picture-file-card-disabled`]: disabled, [`${prefixCls }-picture-file-card-show-pointer`]: typeof onPreviewClick !== 'undefined', }); 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 closeCls = `${prefixCls }-picture-file-card-close`; const retry = (
this.onRetry(e)}>
); const replace = (
this.onReplace(e)}>
); return (
{showProgress ? : null} {showRetry ? retry : null} {showReplace && replace} {disabled ? null : (
this.onRemove(e)}>
)} {this.renderPicValidateMsg()}
); } 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 { name, size, percent, url, listType, 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 && this.props.showRetry; const showReplace = status === strings.FILE_STATUS_SUCCESS && this.props.showReplace; if (listType === strings.FILE_LIST_PIC) { return ( {(locale: Locale['Upload']): ReactNode => (this.renderPic(locale))} ); } const fileSize = this.transSize(size); let previewContent: ReactNode = preview ? () : (); if (previewFile) { previewContent = previewFile(this.props); } return ( {(locale: Locale['Upload']): ReactNode => ( )} ); } } export default FileCard;