1
This commit is contained in:
+38
@@ -0,0 +1,38 @@
|
||||
import React, { Component } from 'react';
|
||||
import type { RcFile, UploadProps } from './interface';
|
||||
interface ParsedFileInfo {
|
||||
origin: RcFile;
|
||||
action: string;
|
||||
data: Record<string, unknown>;
|
||||
parsedFile: RcFile;
|
||||
}
|
||||
declare class AjaxUploader extends Component<UploadProps> {
|
||||
state: {
|
||||
uid: string;
|
||||
};
|
||||
reqs: Record<string, any>;
|
||||
private fileInput;
|
||||
private _isMounted;
|
||||
private filterFile;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onClick: (event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>) => void;
|
||||
onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => void;
|
||||
onDataTransferFiles: (dataTransfer: DataTransfer, existFileCallback?: () => void) => Promise<void>;
|
||||
onFilePaste: (e: ClipboardEvent) => Promise<void>;
|
||||
onFileDragOver: (e: React.DragEvent<HTMLDivElement>) => void;
|
||||
onFileDrop: (e: React.DragEvent<HTMLDivElement>) => Promise<void>;
|
||||
componentDidMount(): void;
|
||||
componentWillUnmount(): void;
|
||||
componentDidUpdate(prevProps: UploadProps): void;
|
||||
uploadFiles: (files: File[]) => void;
|
||||
/**
|
||||
* Process file before upload. When all the file is ready, we start upload.
|
||||
*/
|
||||
processFile: (file: RcFile, fileList: RcFile[]) => Promise<ParsedFileInfo>;
|
||||
post({ data, origin, action, parsedFile }: ParsedFileInfo): void;
|
||||
reset(): void;
|
||||
abort(file?: any): void;
|
||||
saveFileInput: (node: HTMLInputElement) => void;
|
||||
render(): React.JSX.Element;
|
||||
}
|
||||
export default AjaxUploader;
|
||||
+402
@@ -0,0 +1,402 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _pickAttrs = _interopRequireDefault(require("@rc-component/util/lib/pickAttrs"));
|
||||
var _clsx = require("clsx");
|
||||
var _react = _interopRequireWildcard(require("react"));
|
||||
var _attrAccept = _interopRequireDefault(require("./attr-accept"));
|
||||
var _request = _interopRequireDefault(require("./request"));
|
||||
var _traverseFileTree = _interopRequireDefault(require("./traverseFileTree"));
|
||||
var _uid = _interopRequireDefault(require("./uid"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } /* eslint react/no-is-mounted:0,react/sort-comp:0,react/prop-types:0 */
|
||||
class AjaxUploader extends _react.Component {
|
||||
state = {
|
||||
uid: (0, _uid.default)()
|
||||
};
|
||||
reqs = {};
|
||||
fileInput;
|
||||
_isMounted;
|
||||
filterFile = (file, force = false) => {
|
||||
const {
|
||||
accept,
|
||||
directory
|
||||
} = this.props;
|
||||
let filterFn;
|
||||
let acceptFormat;
|
||||
if (typeof accept === 'string') {
|
||||
acceptFormat = accept;
|
||||
} else {
|
||||
const {
|
||||
filter,
|
||||
format
|
||||
} = accept || {};
|
||||
acceptFormat = format;
|
||||
if (filter === 'native') {
|
||||
filterFn = () => true;
|
||||
} else {
|
||||
filterFn = filter;
|
||||
}
|
||||
}
|
||||
const mergedFilter = filterFn || (directory || force ? currentFile => (0, _attrAccept.default)(currentFile, acceptFormat) : () => true);
|
||||
return mergedFilter(file);
|
||||
};
|
||||
onChange = e => {
|
||||
const {
|
||||
files
|
||||
} = e.target;
|
||||
const acceptedFiles = [...files].filter(file => this.filterFile(file));
|
||||
this.uploadFiles(acceptedFiles);
|
||||
this.reset();
|
||||
};
|
||||
onClick = event => {
|
||||
const el = this.fileInput;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
const target = event.target;
|
||||
const {
|
||||
onClick
|
||||
} = this.props;
|
||||
if (target && target.tagName === 'BUTTON') {
|
||||
const parent = el.parentNode;
|
||||
parent.focus();
|
||||
target.blur();
|
||||
}
|
||||
el.click();
|
||||
if (onClick) {
|
||||
onClick(event);
|
||||
}
|
||||
};
|
||||
onKeyDown = e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.onClick(e);
|
||||
}
|
||||
};
|
||||
onDataTransferFiles = async (dataTransfer, existFileCallback) => {
|
||||
const {
|
||||
multiple,
|
||||
directory
|
||||
} = this.props;
|
||||
const items = [...(dataTransfer.items || [])];
|
||||
let files = [...(dataTransfer.files || [])];
|
||||
if (files.length > 0 || items.some(item => item.kind === 'file')) {
|
||||
existFileCallback?.();
|
||||
}
|
||||
if (directory) {
|
||||
files = await (0, _traverseFileTree.default)(Array.prototype.slice.call(items), this.filterFile);
|
||||
this.uploadFiles(files);
|
||||
} else {
|
||||
let acceptFiles = [...files].filter(file => this.filterFile(file, true));
|
||||
if (multiple === false) {
|
||||
acceptFiles = files.slice(0, 1);
|
||||
}
|
||||
this.uploadFiles(acceptFiles);
|
||||
}
|
||||
};
|
||||
onFilePaste = async e => {
|
||||
const {
|
||||
pastable
|
||||
} = this.props;
|
||||
if (!pastable) {
|
||||
return;
|
||||
}
|
||||
if (e.type === 'paste') {
|
||||
const clipboardData = e.clipboardData;
|
||||
return this.onDataTransferFiles(clipboardData, () => {
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
};
|
||||
onFileDragOver = e => {
|
||||
e.preventDefault();
|
||||
};
|
||||
onFileDrop = async e => {
|
||||
e.preventDefault();
|
||||
if (e.type === 'drop') {
|
||||
const dataTransfer = e.dataTransfer;
|
||||
return this.onDataTransferFiles(dataTransfer);
|
||||
}
|
||||
};
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
const {
|
||||
pastable
|
||||
} = this.props;
|
||||
if (pastable) {
|
||||
document.addEventListener('paste', this.onFilePaste);
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
this.abort();
|
||||
document.removeEventListener('paste', this.onFilePaste);
|
||||
}
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
pastable
|
||||
} = this.props;
|
||||
if (pastable && !prevProps.pastable) {
|
||||
document.addEventListener('paste', this.onFilePaste);
|
||||
} else if (!pastable && prevProps.pastable) {
|
||||
document.removeEventListener('paste', this.onFilePaste);
|
||||
}
|
||||
}
|
||||
uploadFiles = files => {
|
||||
const originFiles = [...files];
|
||||
const postFiles = originFiles.map(file => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
file.uid = (0, _uid.default)();
|
||||
return this.processFile(file, originFiles);
|
||||
});
|
||||
|
||||
// Batch upload files
|
||||
Promise.all(postFiles).then(fileList => {
|
||||
const {
|
||||
onBatchStart
|
||||
} = this.props;
|
||||
onBatchStart?.(fileList.map(({
|
||||
origin,
|
||||
parsedFile
|
||||
}) => ({
|
||||
file: origin,
|
||||
parsedFile
|
||||
})));
|
||||
fileList.filter(file => file.parsedFile !== null).forEach(file => {
|
||||
this.post(file);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Process file before upload. When all the file is ready, we start upload.
|
||||
*/
|
||||
processFile = async (file, fileList) => {
|
||||
const {
|
||||
beforeUpload
|
||||
} = this.props;
|
||||
let transformedFile = file;
|
||||
if (beforeUpload) {
|
||||
try {
|
||||
transformedFile = await beforeUpload(file, fileList);
|
||||
} catch (e) {
|
||||
// Rejection will also trade as false
|
||||
transformedFile = false;
|
||||
}
|
||||
if (transformedFile === false) {
|
||||
return {
|
||||
origin: file,
|
||||
parsedFile: null,
|
||||
action: null,
|
||||
data: null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Get latest action
|
||||
const {
|
||||
action
|
||||
} = this.props;
|
||||
let mergedAction;
|
||||
if (typeof action === 'function') {
|
||||
mergedAction = await action(file);
|
||||
} else {
|
||||
mergedAction = action;
|
||||
}
|
||||
|
||||
// Get latest data
|
||||
const {
|
||||
data
|
||||
} = this.props;
|
||||
let mergedData;
|
||||
if (typeof data === 'function') {
|
||||
mergedData = await data(file);
|
||||
} else {
|
||||
mergedData = data;
|
||||
}
|
||||
const parsedData =
|
||||
// string type is from legacy `transformFile`.
|
||||
// Not sure if this will work since no related test case works with it
|
||||
(typeof transformedFile === 'object' || typeof transformedFile === 'string') && transformedFile ? transformedFile : file;
|
||||
let parsedFile;
|
||||
if (parsedData instanceof File) {
|
||||
parsedFile = parsedData;
|
||||
} else {
|
||||
parsedFile = new File([parsedData], file.name, {
|
||||
type: file.type
|
||||
});
|
||||
}
|
||||
const mergedParsedFile = parsedFile;
|
||||
mergedParsedFile.uid = file.uid;
|
||||
return {
|
||||
origin: file,
|
||||
data: mergedData,
|
||||
parsedFile: mergedParsedFile,
|
||||
action: mergedAction
|
||||
};
|
||||
};
|
||||
post({
|
||||
data,
|
||||
origin,
|
||||
action,
|
||||
parsedFile
|
||||
}) {
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
onStart,
|
||||
customRequest,
|
||||
name,
|
||||
headers,
|
||||
withCredentials,
|
||||
method
|
||||
} = this.props;
|
||||
const {
|
||||
uid
|
||||
} = origin;
|
||||
const request = customRequest || _request.default;
|
||||
const requestOption = {
|
||||
action,
|
||||
filename: name,
|
||||
data,
|
||||
file: parsedFile,
|
||||
headers,
|
||||
withCredentials,
|
||||
method: method || 'post',
|
||||
onProgress: e => {
|
||||
const {
|
||||
onProgress
|
||||
} = this.props;
|
||||
onProgress?.(e, parsedFile);
|
||||
},
|
||||
onSuccess: (ret, xhr) => {
|
||||
const {
|
||||
onSuccess
|
||||
} = this.props;
|
||||
onSuccess?.(ret, parsedFile, xhr);
|
||||
delete this.reqs[uid];
|
||||
},
|
||||
onError: (err, ret) => {
|
||||
const {
|
||||
onError
|
||||
} = this.props;
|
||||
onError?.(err, ret, parsedFile);
|
||||
delete this.reqs[uid];
|
||||
}
|
||||
};
|
||||
onStart(origin);
|
||||
this.reqs[uid] = request(requestOption, {
|
||||
defaultRequest: _request.default
|
||||
});
|
||||
}
|
||||
reset() {
|
||||
this.setState({
|
||||
uid: (0, _uid.default)()
|
||||
});
|
||||
}
|
||||
abort(file) {
|
||||
const {
|
||||
reqs
|
||||
} = this;
|
||||
if (file) {
|
||||
const uid = file.uid ? file.uid : file;
|
||||
if (reqs[uid] && reqs[uid].abort) {
|
||||
reqs[uid].abort();
|
||||
}
|
||||
delete reqs[uid];
|
||||
} else {
|
||||
Object.keys(reqs).forEach(uid => {
|
||||
if (reqs[uid] && reqs[uid].abort) {
|
||||
reqs[uid].abort();
|
||||
}
|
||||
delete reqs[uid];
|
||||
});
|
||||
}
|
||||
}
|
||||
saveFileInput = node => {
|
||||
this.fileInput = node;
|
||||
};
|
||||
render() {
|
||||
const {
|
||||
component: Tag,
|
||||
prefixCls,
|
||||
className,
|
||||
classNames = {},
|
||||
disabled,
|
||||
id,
|
||||
name,
|
||||
style,
|
||||
styles = {},
|
||||
multiple,
|
||||
accept,
|
||||
capture,
|
||||
children,
|
||||
directory,
|
||||
openFileDialogOnClick,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
hasControlInside,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
// Extract accept format for input element
|
||||
const acceptFormat = typeof accept === 'string' ? accept : accept?.format;
|
||||
const cls = (0, _clsx.clsx)(prefixCls, {
|
||||
[`${prefixCls}-disabled`]: disabled,
|
||||
[className]: className
|
||||
});
|
||||
// because input don't have directory/webkitdirectory type declaration
|
||||
const dirProps = directory ? {
|
||||
directory: 'directory',
|
||||
webkitdirectory: 'webkitdirectory'
|
||||
} : {};
|
||||
const events = disabled ? {} : {
|
||||
onClick: openFileDialogOnClick ? this.onClick : () => {},
|
||||
onKeyDown: openFileDialogOnClick ? this.onKeyDown : () => {},
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
onDrop: this.onFileDrop,
|
||||
onDragOver: this.onFileDragOver,
|
||||
tabIndex: hasControlInside ? undefined : '0'
|
||||
};
|
||||
return /*#__PURE__*/_react.default.createElement(Tag, _extends({}, events, {
|
||||
className: cls,
|
||||
role: hasControlInside ? undefined : 'button',
|
||||
style: style
|
||||
}), /*#__PURE__*/_react.default.createElement("input", _extends({}, (0, _pickAttrs.default)(otherProps, {
|
||||
aria: true,
|
||||
data: true
|
||||
}), {
|
||||
id: id
|
||||
/**
|
||||
* https://github.com/ant-design/ant-design/issues/50643,
|
||||
* https://github.com/react-component/upload/pull/575#issuecomment-2320646552
|
||||
*/,
|
||||
name: name,
|
||||
disabled: disabled,
|
||||
type: "file",
|
||||
ref: this.saveFileInput,
|
||||
onClick: e => e.stopPropagation() // https://github.com/ant-design/ant-design/issues/19948
|
||||
,
|
||||
key: this.state.uid,
|
||||
style: {
|
||||
display: 'none',
|
||||
...styles.input
|
||||
},
|
||||
className: classNames.input,
|
||||
accept: acceptFormat
|
||||
}, dirProps, {
|
||||
multiple: multiple,
|
||||
onChange: this.onChange
|
||||
}, capture != null ? {
|
||||
capture
|
||||
} : {})), children);
|
||||
}
|
||||
}
|
||||
var _default = exports.default = AjaxUploader;
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import React, { Component } from 'react';
|
||||
import AjaxUpload from './AjaxUploader';
|
||||
import type { UploadProps, RcFile } from './interface';
|
||||
declare function empty(): void;
|
||||
declare class Upload extends Component<UploadProps> {
|
||||
static defaultProps: {
|
||||
component: string;
|
||||
prefixCls: string;
|
||||
data: {};
|
||||
headers: {};
|
||||
name: string;
|
||||
multipart: boolean;
|
||||
onStart: typeof empty;
|
||||
onError: typeof empty;
|
||||
onSuccess: typeof empty;
|
||||
multiple: boolean;
|
||||
beforeUpload: any;
|
||||
customRequest: any;
|
||||
withCredentials: boolean;
|
||||
openFileDialogOnClick: boolean;
|
||||
hasControlInside: boolean;
|
||||
};
|
||||
private uploader;
|
||||
abort(file: RcFile): void;
|
||||
saveUploader: (node: AjaxUpload) => void;
|
||||
render(): React.JSX.Element;
|
||||
}
|
||||
export default Upload;
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _react = _interopRequireWildcard(require("react"));
|
||||
var _AjaxUploader = _interopRequireDefault(require("./AjaxUploader"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } /* eslint react/prop-types:0 */
|
||||
function empty() {}
|
||||
class Upload extends _react.Component {
|
||||
static defaultProps = {
|
||||
component: 'span',
|
||||
prefixCls: 'rc-upload',
|
||||
data: {},
|
||||
headers: {},
|
||||
name: 'file',
|
||||
multipart: false,
|
||||
onStart: empty,
|
||||
onError: empty,
|
||||
onSuccess: empty,
|
||||
multiple: false,
|
||||
beforeUpload: null,
|
||||
customRequest: null,
|
||||
withCredentials: false,
|
||||
openFileDialogOnClick: true,
|
||||
hasControlInside: false
|
||||
};
|
||||
uploader;
|
||||
abort(file) {
|
||||
this.uploader.abort(file);
|
||||
}
|
||||
saveUploader = node => {
|
||||
this.uploader = node;
|
||||
};
|
||||
render() {
|
||||
return /*#__PURE__*/_react.default.createElement(_AjaxUploader.default, _extends({}, this.props, {
|
||||
ref: this.saveUploader
|
||||
}));
|
||||
}
|
||||
}
|
||||
var _default = exports.default = Upload;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import type { RcFile } from './interface';
|
||||
declare const _default: (file: RcFile, acceptedFiles: string | string[]) => boolean;
|
||||
export default _default;
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _util = require("@rc-component/util");
|
||||
var _default = (file, acceptedFiles) => {
|
||||
if (file && acceptedFiles) {
|
||||
const acceptedFilesArray = Array.isArray(acceptedFiles) ? acceptedFiles : acceptedFiles.split(',');
|
||||
const fileName = file.name || '';
|
||||
const mimeType = file.type || '';
|
||||
const baseMimeType = mimeType.replace(/\/.*$/, '');
|
||||
return acceptedFilesArray.some(type => {
|
||||
const validType = type.trim();
|
||||
// This is something like */*,* allow all files
|
||||
if (/^\*(\/\*)?$/.test(type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// like .jpg, .png
|
||||
if (validType.charAt(0) === '.') {
|
||||
const lowerFileName = fileName.toLowerCase();
|
||||
const lowerType = validType.toLowerCase();
|
||||
let affixList = [lowerType];
|
||||
if (lowerType === '.jpg' || lowerType === '.jpeg') {
|
||||
affixList = ['.jpg', '.jpeg'];
|
||||
}
|
||||
return affixList.some(affix => lowerFileName.endsWith(affix));
|
||||
}
|
||||
|
||||
// This is something like a image/* mime type
|
||||
if (/\/\*$/.test(validType)) {
|
||||
return baseMimeType === validType.replace(/\/.*$/, '');
|
||||
}
|
||||
|
||||
// Full match
|
||||
if (mimeType === validType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Invalidate type should skip
|
||||
if (/^\w+$/.test(validType)) {
|
||||
(0, _util.warning)(false, `Upload takes an invalidate 'accept' type '${validType}'.Skip for check.`);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
};
|
||||
exports.default = _default;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import type { UploadProps } from './interface';
|
||||
import Upload from './Upload';
|
||||
export type { UploadProps };
|
||||
export default Upload;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _Upload = _interopRequireDefault(require("./Upload"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
var _default = exports.default = _Upload.default;
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import type * as React from 'react';
|
||||
export type BeforeUploadFileType = File | Blob | boolean | string;
|
||||
export type Action = string | ((file: RcFile) => string | PromiseLike<string>);
|
||||
export type AcceptConfig = {
|
||||
format: string;
|
||||
filter?: 'native' | ((file: RcFile) => boolean);
|
||||
};
|
||||
export interface UploadProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onError' | 'onProgress' | 'accept'> {
|
||||
name?: string;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
component?: React.ComponentType<any> | string;
|
||||
action?: Action;
|
||||
method?: UploadRequestMethod;
|
||||
directory?: boolean;
|
||||
data?: Record<string, unknown> | ((file: RcFile | string | Blob) => Record<string, unknown>);
|
||||
headers?: UploadRequestHeader;
|
||||
accept?: string | AcceptConfig;
|
||||
multiple?: boolean;
|
||||
onBatchStart?: (fileList: {
|
||||
file: RcFile;
|
||||
parsedFile: Exclude<BeforeUploadFileType, boolean>;
|
||||
}[]) => void;
|
||||
onStart?: (file: RcFile) => void;
|
||||
onError?: (error: Error, ret: Record<string, unknown>, file: RcFile) => void;
|
||||
onSuccess?: (response: Record<string, unknown>, file: RcFile, xhr: XMLHttpRequest) => void;
|
||||
onProgress?: (event: UploadProgressEvent, file: RcFile) => void;
|
||||
beforeUpload?: (file: RcFile, FileList: RcFile[]) => BeforeUploadFileType | Promise<void | BeforeUploadFileType> | void;
|
||||
customRequest?: CustomUploadRequestOption;
|
||||
withCredentials?: boolean;
|
||||
openFileDialogOnClick?: boolean;
|
||||
prefixCls?: string;
|
||||
id?: string;
|
||||
onMouseEnter?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||
onMouseLeave?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||
onClick?: (e: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>) => void;
|
||||
classNames?: {
|
||||
input?: string;
|
||||
};
|
||||
styles?: {
|
||||
input?: React.CSSProperties;
|
||||
};
|
||||
hasControlInside?: boolean;
|
||||
pastable?: boolean;
|
||||
}
|
||||
export interface UploadProgressEvent extends Partial<ProgressEvent> {
|
||||
percent?: number;
|
||||
}
|
||||
export type UploadRequestMethod = 'POST' | 'PUT' | 'PATCH' | 'post' | 'put' | 'patch';
|
||||
export type UploadRequestHeader = Record<string, string>;
|
||||
export type UploadRequestFile = Exclude<BeforeUploadFileType, File | boolean> | RcFile;
|
||||
export interface UploadRequestError extends Error {
|
||||
status?: number;
|
||||
method?: UploadRequestMethod;
|
||||
url?: string;
|
||||
}
|
||||
export interface UploadRequestOption<T = any> {
|
||||
onProgress?: (event: UploadProgressEvent, file?: UploadRequestFile) => void;
|
||||
onError?: (event: UploadRequestError | ProgressEvent, body?: T) => void;
|
||||
onSuccess?: (body: T, fileOrXhr?: UploadRequestFile | XMLHttpRequest) => void;
|
||||
data?: Record<string, unknown>;
|
||||
filename?: string;
|
||||
file: UploadRequestFile;
|
||||
withCredentials?: boolean;
|
||||
action: string;
|
||||
headers?: UploadRequestHeader;
|
||||
method: UploadRequestMethod;
|
||||
}
|
||||
export type CustomUploadRequestOption = (option: UploadRequestOption, info: {
|
||||
defaultRequest: (option: UploadRequestOption) => {
|
||||
abort: () => void;
|
||||
} | void;
|
||||
}) => void | {
|
||||
abort: () => void;
|
||||
};
|
||||
export interface RcFile extends File {
|
||||
uid: string;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import type { UploadRequestOption } from './interface';
|
||||
export default function upload(option: UploadRequestOption): {
|
||||
abort(): void;
|
||||
};
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = upload;
|
||||
function getError(option, xhr) {
|
||||
const msg = `cannot ${option.method} ${option.action} ${xhr.status}'`;
|
||||
const err = new Error(msg);
|
||||
err.status = xhr.status;
|
||||
err.method = option.method;
|
||||
err.url = option.action;
|
||||
return err;
|
||||
}
|
||||
function getBody(xhr) {
|
||||
const text = xhr.responseText || xhr.response;
|
||||
if (!text) {
|
||||
return text;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (e) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
function upload(option) {
|
||||
// eslint-disable-next-line no-undef
|
||||
const xhr = new XMLHttpRequest();
|
||||
if (option.onProgress && xhr.upload) {
|
||||
xhr.upload.onprogress = function progress(e) {
|
||||
if (e.total > 0) {
|
||||
e.percent = e.loaded / e.total * 100;
|
||||
}
|
||||
option.onProgress(e);
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const formData = new FormData();
|
||||
if (option.data) {
|
||||
Object.keys(option.data).forEach(key => {
|
||||
const value = option.data[key];
|
||||
// support key-value array data
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(item => {
|
||||
// { list: [ 11, 22 ] }
|
||||
// formData.append('list[]', 11);
|
||||
formData.append(`${key}[]`, item);
|
||||
});
|
||||
return;
|
||||
}
|
||||
formData.append(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
if (option.file instanceof Blob) {
|
||||
formData.append(option.filename, option.file, option.file.name);
|
||||
} else {
|
||||
formData.append(option.filename, option.file);
|
||||
}
|
||||
xhr.onerror = function error(e) {
|
||||
option.onError(e);
|
||||
};
|
||||
xhr.onload = function onload() {
|
||||
// allow success when 2xx status
|
||||
// see https://github.com/react-component/upload/issues/34
|
||||
if (xhr.status < 200 || xhr.status >= 300) {
|
||||
return option.onError(getError(option, xhr), getBody(xhr));
|
||||
}
|
||||
return option.onSuccess(getBody(xhr), xhr);
|
||||
};
|
||||
xhr.open(option.method, option.action, true);
|
||||
|
||||
// Has to be after `.open()`. See https://github.com/enyo/dropzone/issues/179
|
||||
if (option.withCredentials && 'withCredentials' in xhr) {
|
||||
xhr.withCredentials = true;
|
||||
}
|
||||
const headers = option.headers || {};
|
||||
|
||||
// when set headers['X-Requested-With'] = null , can close default XHR header
|
||||
// see https://github.com/react-component/upload/issues/33
|
||||
if (headers['X-Requested-With'] !== null) {
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
}
|
||||
Object.keys(headers).forEach(h => {
|
||||
if (headers[h] !== null) {
|
||||
xhr.setRequestHeader(h, headers[h]);
|
||||
}
|
||||
});
|
||||
xhr.send(formData);
|
||||
return {
|
||||
abort() {
|
||||
xhr.abort();
|
||||
}
|
||||
};
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import type { RcFile } from './interface';
|
||||
interface InternalDataTransferItem extends DataTransferItem {
|
||||
isFile: boolean;
|
||||
file: (cd: (file: RcFile & {
|
||||
webkitRelativePath?: string;
|
||||
}) => void) => void;
|
||||
createReader: () => any;
|
||||
fullPath: string;
|
||||
isDirectory: boolean;
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
declare const traverseFileTree: (files: InternalDataTransferItem[], isAccepted: any) => Promise<any[]>;
|
||||
export default traverseFileTree;
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
// https://github.com/ant-design/ant-design/issues/50080
|
||||
const traverseFileTree = async (files, isAccepted) => {
|
||||
const flattenFileList = [];
|
||||
const progressFileList = [];
|
||||
files.forEach(file => progressFileList.push(file.webkitGetAsEntry()));
|
||||
async function readDirectory(directory) {
|
||||
const dirReader = directory.createReader();
|
||||
const entries = [];
|
||||
while (true) {
|
||||
const results = await new Promise(resolve => {
|
||||
dirReader.readEntries(resolve, () => resolve([]));
|
||||
});
|
||||
const n = results.length;
|
||||
if (!n) {
|
||||
break;
|
||||
}
|
||||
for (let i = 0; i < n; i++) {
|
||||
entries.push(results[i]);
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
async function readFile(item) {
|
||||
return new Promise(reslove => {
|
||||
item.file(file => {
|
||||
if (isAccepted(file)) {
|
||||
// https://github.com/ant-design/ant-design/issues/16426
|
||||
if (item.fullPath && !file.webkitRelativePath) {
|
||||
Object.defineProperties(file, {
|
||||
webkitRelativePath: {
|
||||
writable: true
|
||||
}
|
||||
});
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
file.webkitRelativePath = item.fullPath.replace(/^\//, '');
|
||||
Object.defineProperties(file, {
|
||||
webkitRelativePath: {
|
||||
writable: false
|
||||
}
|
||||
});
|
||||
}
|
||||
reslove(file);
|
||||
} else {
|
||||
reslove(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const _traverseFileTree = async (item, path) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
item.path = path || '';
|
||||
if (item.isFile) {
|
||||
const file = await readFile(item);
|
||||
if (file) {
|
||||
flattenFileList.push(file);
|
||||
}
|
||||
} else if (item.isDirectory) {
|
||||
const entries = await readDirectory(item);
|
||||
progressFileList.push(...entries);
|
||||
}
|
||||
};
|
||||
let wipIndex = 0;
|
||||
while (wipIndex < progressFileList.length) {
|
||||
await _traverseFileTree(progressFileList[wipIndex]);
|
||||
wipIndex++;
|
||||
}
|
||||
return flattenFileList;
|
||||
};
|
||||
var _default = exports.default = traverseFileTree;
|
||||
+1
@@ -0,0 +1 @@
|
||||
export default function uid(): string;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = uid;
|
||||
const now = +new Date();
|
||||
let index = 0;
|
||||
function uid() {
|
||||
// eslint-disable-next-line no-plusplus
|
||||
return `rc-upload-${now}-${++index}`;
|
||||
}
|
||||
Reference in New Issue
Block a user