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, IconClear, IconFile, IconRefresh, IconEyeOpened } from '@douyinfe/semi-icons'; import LocaleConsumer from '../locale/localeConsumer'; import { Locale } from '../locale/interface'; import Button from '../button/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, renderPicPreviewIcon, 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 showPreview = 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 retry = (
this.onRetry(e)}>
); const replace = (
this.onReplace(e)}>
); const preview = (
{typeof renderPicPreviewIcon === 'function'? renderPicPreviewIcon(this.props): null}
); const close = (
this.onRemove(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} {showPreview && preview} {showPicInfo && picInfo} {!disabled && close} {this.renderPicValidateMsg()}
); } renderFile(locale: Locale["Upload"]) { const { name, size, percent, url, showRetry: propsShowRetry, showReplace: propsShowReplace, preview, previewFile, status, style, onPreviewClick, renderFileOperation } = 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); } const operation = typeof renderFileOperation === 'function'? renderFileOperation(this.props) :