UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

288 lines (287 loc) 14.5 kB
import React, { ReactNode, CSSProperties, RefObject, ChangeEvent, DragEvent } from 'react'; import PropTypes from 'prop-types'; import UploadFoundation from '@douyinfe/semi-foundation/lib/es/upload/foundation'; import FileCard from './fileCard'; import BaseComponent from '../_base/baseComponent'; import Cropper from '../cropper'; import { type ModalReactProps } from '../modal'; import type { FileItem, RenderFileItemProps, UploadListType, PromptPositionType, BeforeUploadProps, AfterUploadProps, OnChangeProps, customRequestArgs, CustomError, RenderPictureCloseProps, RenderFileListTitleProps, CropProps } from './interface'; import { Locale } from '../locale/interface'; import '@douyinfe/semi-foundation/lib/es/upload/upload.css'; import type { CustomFile, UploadAdapter, BeforeUploadObjectResult, AfterUploadResult, FileItemStatus } from '@douyinfe/semi-foundation/lib/es/upload/foundation'; import type { ValidateStatus } from '../_base/baseComponent'; import { ShowTooltip } from '../typography'; export type { FileItem, FileItemStatus, RenderFileItemProps, UploadListType, PromptPositionType, BeforeUploadProps, AfterUploadProps, OnChangeProps, customRequestArgs, CustomError, BeforeUploadObjectResult, AfterUploadResult, RenderFileListTitleProps, CropProps, }; export interface UploadProps { accept?: string; action: string; afterUpload?: (object: AfterUploadProps) => AfterUploadResult; beforeUpload?: (object: BeforeUploadProps) => BeforeUploadObjectResult | Promise<BeforeUploadObjectResult> | boolean; beforeClear?: (fileList: Array<FileItem>) => boolean | Promise<boolean>; beforeRemove?: (file: FileItem, fileList: Array<FileItem>) => boolean | Promise<boolean>; capture?: boolean | 'user' | 'environment' | undefined; children?: ReactNode; className?: string; customRequest?: (object: customRequestArgs) => void; data?: Record<string, any> | ((file: File) => Record<string, unknown>); defaultFileList?: Array<FileItem>; directory?: boolean; disabled?: boolean; dragIcon?: ReactNode; dragMainText?: ReactNode; dragSubText?: ReactNode; draggable?: boolean; addOnPasting?: boolean; fileList?: Array<FileItem>; fileName?: string; headers?: Record<string, any> | ((file: File) => Record<string, string>); hotSpotLocation?: 'start' | 'end'; itemStyle?: CSSProperties; limit?: number; listType?: UploadListType; maxSize?: number; minSize?: number; multiple?: boolean; name?: string; onAcceptInvalid?: (files: File[]) => void; onChange?: (object: OnChangeProps) => void; onClear?: () => void; onDrop?: (e: Event, files: Array<File>, fileList: Array<FileItem>) => void; onError?: (e: CustomError, file: CustomFile, fileList: Array<FileItem>, xhr: XMLHttpRequest) => void; onPastingError?: (error: Error | PermissionStatus) => void; onExceed?: (fileList: Array<File>) => void; onFileChange?: (files: Array<File>) => void; onOpenFileDialog?: () => void; onPreviewClick?: (fileItem: FileItem) => void; onProgress?: (percent: number, file: CustomFile, fileList: Array<FileItem>) => void; onRemove?: (currentFile: CustomFile, fileList: Array<FileItem>, currentFileItem: FileItem) => void; onRetry?: (fileItem: FileItem) => void; onSizeError?: (file: CustomFile, fileList: Array<FileItem>) => void; onSuccess?: (responseBody: any, file: CustomFile, fileList: Array<FileItem>) => void; previewFile?: (renderFileItemProps: RenderFileItemProps) => ReactNode; prompt?: ReactNode; promptPosition?: PromptPositionType; picHeight?: string | number; picWidth?: string | number; renderFileItem?: (renderFileItemProps: RenderFileItemProps) => ReactNode; renderPicInfo?: (renderFileItemProps: RenderFileItemProps) => ReactNode; renderThumbnail?: (renderFileItemProps: RenderFileItemProps) => ReactNode; renderPicPreviewIcon?: (renderFileItemProps: RenderFileItemProps) => ReactNode; renderPicClose?: (renderPicCloseProps: RenderPictureCloseProps) => ReactNode; renderFileOperation?: (fileItem: RenderFileItemProps) => ReactNode; fileListTitle?: ReactNode | ((props: RenderFileListTitleProps) => ReactNode); showClear?: boolean; showPicInfo?: boolean; showReplace?: boolean; showRetry?: boolean; showUploadList?: boolean; style?: CSSProperties; timeout?: number; transformFile?: (file: File) => FileItem; uploadTrigger?: 'auto' | 'custom'; validateMessage?: ReactNode; validateStatus?: ValidateStatus; withCredentials?: boolean; showTooltip?: boolean | ShowTooltip; /** * Enable image cropping. Pass `true` to use defaults, or a `CropProps` object * to customize aspect ratio, shape, output quality, etc. */ crop?: boolean | CropProps; /** * Callback invoked before opening the crop modal for each image. * Return `false` (or resolve to `false`) to skip cropping and upload directly. */ beforeCrop?: (file: File, fileList: File[]) => boolean | Promise<boolean>; /** Callback invoked when cropping fails. */ onCropError?: (error: Error) => void; /** Extra props forwarded to the underlying crop Modal. */ cropModalProps?: ModalReactProps; } export interface UploadState { dragAreaStatus: 'default' | 'legal' | 'illegal'; fileList: Array<FileItem>; inputKey: number; localUrls: Array<string>; replaceIdx: number; replaceInputKey: number; cropperVisible: boolean; /** The image file currently being cropped */ cropperFile: File | null; /** Object URL for the image currently being cropped */ cropperSrc: string; /** Remaining image files queued for cropping (cropped one at a time) */ pendingImageFiles: File[]; /** Non-image files that bypass cropping but are uploaded together */ nonImageFiles: File[]; /** Image files already cropped, accumulated until the queue drains */ croppedFiles: File[]; /** Flag to indicate this round was triggered by a replace action */ isReplaceOperation: boolean; } declare class Upload extends BaseComponent<UploadProps, UploadState> { static propTypes: { accept: PropTypes.Requireable<string>; action: PropTypes.Validator<string>; addOnPasting: PropTypes.Requireable<boolean>; afterUpload: PropTypes.Requireable<(...args: any[]) => any>; beforeClear: PropTypes.Requireable<(...args: any[]) => any>; beforeRemove: PropTypes.Requireable<(...args: any[]) => any>; beforeUpload: PropTypes.Requireable<(...args: any[]) => any>; children: PropTypes.Requireable<PropTypes.ReactNodeLike>; className: PropTypes.Requireable<string>; customRequest: PropTypes.Requireable<(...args: any[]) => any>; data: PropTypes.Requireable<object>; defaultFileList: PropTypes.Requireable<any[]>; directory: PropTypes.Requireable<boolean>; disabled: PropTypes.Requireable<boolean>; dragIcon: PropTypes.Requireable<PropTypes.ReactNodeLike>; dragMainText: PropTypes.Requireable<PropTypes.ReactNodeLike>; dragSubText: PropTypes.Requireable<PropTypes.ReactNodeLike>; draggable: PropTypes.Requireable<boolean>; fileList: PropTypes.Requireable<any[]>; fileName: PropTypes.Requireable<string>; headers: PropTypes.Requireable<object>; hotSpotLocation: PropTypes.Requireable<string>; itemStyle: PropTypes.Requireable<object>; limit: PropTypes.Requireable<number>; listType: PropTypes.Requireable<"none" | "picture" | "list">; maxSize: PropTypes.Requireable<number>; minSize: PropTypes.Requireable<number>; multiple: PropTypes.Requireable<boolean>; name: PropTypes.Requireable<string>; onAcceptInvalid: PropTypes.Requireable<(...args: any[]) => any>; onChange: PropTypes.Requireable<(...args: any[]) => any>; onClear: PropTypes.Requireable<(...args: any[]) => any>; onDrop: PropTypes.Requireable<(...args: any[]) => any>; onError: PropTypes.Requireable<(...args: any[]) => any>; onExceed: PropTypes.Requireable<(...args: any[]) => any>; onFileChange: PropTypes.Requireable<(...args: any[]) => any>; onOpenFileDialog: PropTypes.Requireable<(...args: any[]) => any>; onPreviewClick: PropTypes.Requireable<(...args: any[]) => any>; onProgress: PropTypes.Requireable<(...args: any[]) => any>; onRemove: PropTypes.Requireable<(...args: any[]) => any>; onRetry: PropTypes.Requireable<(...args: any[]) => any>; onSizeError: PropTypes.Requireable<(...args: any[]) => any>; onSuccess: PropTypes.Requireable<(...args: any[]) => any>; onPastingError: PropTypes.Requireable<(...args: any[]) => any>; previewFile: PropTypes.Requireable<(...args: any[]) => any>; prompt: PropTypes.Requireable<PropTypes.ReactNodeLike>; promptPosition: PropTypes.Requireable<"left" | "right" | "bottom">; picWidth: PropTypes.Requireable<NonNullable<string | number>>; picHeight: PropTypes.Requireable<NonNullable<string | number>>; renderFileItem: PropTypes.Requireable<(...args: any[]) => any>; renderPicPreviewIcon: PropTypes.Requireable<(...args: any[]) => any>; renderFileOperation: PropTypes.Requireable<(...args: any[]) => any>; renderPicClose: PropTypes.Requireable<(...args: any[]) => any>; renderPicInfo: PropTypes.Requireable<(...args: any[]) => any>; renderThumbnail: PropTypes.Requireable<(...args: any[]) => any>; fileListTitle: PropTypes.Requireable<NonNullable<PropTypes.ReactNodeLike | ((...args: any[]) => any)>>; showClear: PropTypes.Requireable<boolean>; showPicInfo: PropTypes.Requireable<boolean>; showReplace: PropTypes.Requireable<boolean>; showRetry: PropTypes.Requireable<boolean>; showUploadList: PropTypes.Requireable<boolean>; style: PropTypes.Requireable<object>; timeout: PropTypes.Requireable<number>; transformFile: PropTypes.Requireable<(...args: any[]) => any>; uploadTrigger: PropTypes.Requireable<"auto" | "custom">; validateMessage: PropTypes.Requireable<PropTypes.ReactNodeLike>; validateStatus: PropTypes.Requireable<"default" | "error" | "warning" | "success">; withCredentials: PropTypes.Requireable<boolean>; showTooltip: PropTypes.Requireable<NonNullable<boolean | object>>; crop: PropTypes.Requireable<NonNullable<boolean | object>>; beforeCrop: PropTypes.Requireable<(...args: any[]) => any>; onCropError: PropTypes.Requireable<(...args: any[]) => any>; cropModalProps: PropTypes.Requireable<object>; }; static defaultProps: Partial<UploadProps>; static FileCard: typeof FileCard; constructor(props: UploadProps); /** * Notes: * The input parameter and return value here do not declare the type, otherwise tsc may report an error in form/fields.tsx when wrap after withField * `The types of the parameters "props" and "nextProps" are incompatible. The attribute "action" is missing in the type "Readonly<any>", but it is required in the type "UploadProps".` * which seems to be a bug, remove props type declare here */ static getDerivedStateFromProps(props: any): { fileList: any; }; get adapter(): UploadAdapter<UploadProps, UploadState>; foundation: UploadFoundation; inputRef: RefObject<HTMLInputElement>; replaceInputRef: RefObject<HTMLInputElement>; cropperRef: RefObject<Cropper>; pastingCb: null | ((params: any) => void); pasteEventCb: null | ((params: any) => void); componentDidMount(): void; componentWillUnmount(): void; onClick: () => void; onChange: (e: ChangeEvent<HTMLInputElement>) => void; /** * Check if file is an image */ isImageFile: (file: File) => boolean; /** * Entry point that decides whether incoming files need cropping. * Image files are queued for sequential cropping; non-image files are kept aside * and uploaded together with the cropped results once the queue drains. */ handleCropFiles: (files: File[], isReplaceOperation?: boolean) => Promise<void>; /** * Run the user-provided beforeCrop hook (if any) and return whether cropping should proceed. * Errors fall back to skipping cropping so users are never blocked. */ shouldCropFile: (file: File, files: File[]) => Promise<boolean>; /** * Forward files to the appropriate foundation handler based on operation type. */ dispatchUpload: (files: File[], isReplaceOperation: boolean) => void; /** * Confirm the current crop. If more images are queued, advance to the next one; * otherwise dispatch the accumulated cropped files together with any non-image files. */ handleCropOk: () => Promise<void>; /** * Cancel the current crop session, revoke object URLs and reset state. */ handleCropCancel: () => void; /** Internal helper that tears down crop state and resets the file input. */ closeCropperAndReset: () => void; replace: (index: number) => void; onReplaceChange: (e: ChangeEvent<HTMLInputElement>) => void; clear: () => void; remove: (fileItem: FileItem) => void; /** * ref method * insert files at index * @param files Array<CustomFile> * @param index number * @returns */ insert: (files: Array<CustomFile>, index?: number) => void; /** * ref method * manual upload by user */ upload: () => void; /** * ref method * manual open file select dialog */ openFileDialog: () => void; renderFile: (file: FileItem, index: number, locale: Locale['Upload']) => ReactNode; renderFileList: () => ReactNode; renderFileListPic: () => React.JSX.Element; renderFileListDefault: () => React.JSX.Element; onDrop: (e: DragEvent<HTMLDivElement>) => void; onDragOver: (e: DragEvent<HTMLDivElement>) => void; onDragLeave: (e: DragEvent<HTMLDivElement>) => void; onDragEnter: (e: DragEvent<HTMLDivElement>) => void; renderAddContent: () => string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element; renderDragArea: () => ReactNode; renderCropperModal: () => ReactNode; render(): ReactNode; } export default Upload;