Upload.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. "use strict";
  2. "use client";
  3. var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
  4. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
  5. Object.defineProperty(exports, "__esModule", {
  6. value: true
  7. });
  8. exports.default = exports.LIST_IGNORE = void 0;
  9. var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
  10. var React = _interopRequireWildcard(require("react"));
  11. var _reactDom = require("react-dom");
  12. var _classnames = _interopRequireDefault(require("classnames"));
  13. var _rcUpload = _interopRequireDefault(require("rc-upload"));
  14. var _useMergedState = _interopRequireDefault(require("rc-util/lib/hooks/useMergedState"));
  15. var _warning = require("../_util/warning");
  16. var _configProvider = require("../config-provider");
  17. var _DisabledContext = _interopRequireDefault(require("../config-provider/DisabledContext"));
  18. var _locale = require("../locale");
  19. var _en_US = _interopRequireDefault(require("../locale/en_US"));
  20. var _style = _interopRequireDefault(require("./style"));
  21. var _UploadList = _interopRequireDefault(require("./UploadList"));
  22. var _utils = require("./utils");
  23. var _context = require("../config-provider/context");
  24. var __awaiter = void 0 && (void 0).__awaiter || function (thisArg, _arguments, P, generator) {
  25. function adopt(value) {
  26. return value instanceof P ? value : new P(function (resolve) {
  27. resolve(value);
  28. });
  29. }
  30. return new (P || (P = Promise))(function (resolve, reject) {
  31. function fulfilled(value) {
  32. try {
  33. step(generator.next(value));
  34. } catch (e) {
  35. reject(e);
  36. }
  37. }
  38. function rejected(value) {
  39. try {
  40. step(generator["throw"](value));
  41. } catch (e) {
  42. reject(e);
  43. }
  44. }
  45. function step(result) {
  46. result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
  47. }
  48. step((generator = generator.apply(thisArg, _arguments || [])).next());
  49. });
  50. };
  51. const LIST_IGNORE = exports.LIST_IGNORE = `__LIST_IGNORE_${Date.now()}__`;
  52. const InternalUpload = (props, ref) => {
  53. const config = (0, _context.useComponentConfig)('upload');
  54. const {
  55. fileList,
  56. defaultFileList,
  57. onRemove,
  58. showUploadList = true,
  59. listType = 'text',
  60. onPreview,
  61. onDownload,
  62. onChange,
  63. onDrop,
  64. previewFile,
  65. disabled: customDisabled,
  66. locale: propLocale,
  67. iconRender,
  68. isImageUrl,
  69. progress,
  70. prefixCls: customizePrefixCls,
  71. className,
  72. type = 'select',
  73. children,
  74. style,
  75. itemRender,
  76. maxCount,
  77. data = {},
  78. multiple = false,
  79. hasControlInside = true,
  80. action = '',
  81. accept = '',
  82. supportServerRender = true,
  83. rootClassName
  84. } = props;
  85. // ===================== Disabled =====================
  86. const disabled = React.useContext(_DisabledContext.default);
  87. const mergedDisabled = customDisabled !== null && customDisabled !== void 0 ? customDisabled : disabled;
  88. const customRequest = props.customRequest || config.customRequest;
  89. const [mergedFileList, setMergedFileList] = (0, _useMergedState.default)(defaultFileList || [], {
  90. value: fileList,
  91. postState: list => list !== null && list !== void 0 ? list : []
  92. });
  93. const [dragState, setDragState] = React.useState('drop');
  94. const upload = React.useRef(null);
  95. const wrapRef = React.useRef(null);
  96. if (process.env.NODE_ENV !== 'production') {
  97. const warning = (0, _warning.devUseWarning)('Upload');
  98. process.env.NODE_ENV !== "production" ? warning('fileList' in props || !('value' in props), 'usage', '`value` is not a valid prop, do you mean `fileList`?') : void 0;
  99. warning.deprecated(!('transformFile' in props), 'transformFile', 'beforeUpload');
  100. }
  101. // Control mode will auto fill file uid if not provided
  102. React.useMemo(() => {
  103. const timestamp = Date.now();
  104. (fileList || []).forEach((file, index) => {
  105. if (!file.uid && !Object.isFrozen(file)) {
  106. file.uid = `__AUTO__${timestamp}_${index}__`;
  107. }
  108. });
  109. }, [fileList]);
  110. const onInternalChange = (file, changedFileList, event) => {
  111. let cloneList = (0, _toConsumableArray2.default)(changedFileList);
  112. let exceedMaxCount = false;
  113. // Cut to match count
  114. if (maxCount === 1) {
  115. cloneList = cloneList.slice(-1);
  116. } else if (maxCount) {
  117. exceedMaxCount = cloneList.length > maxCount;
  118. cloneList = cloneList.slice(0, maxCount);
  119. }
  120. // Prevent React18 auto batch since input[upload] trigger process at same time
  121. // which makes fileList closure problem
  122. // eslint-disable-next-line react-dom/no-flush-sync
  123. (0, _reactDom.flushSync)(() => {
  124. setMergedFileList(cloneList);
  125. });
  126. const changeInfo = {
  127. file: file,
  128. fileList: cloneList
  129. };
  130. if (event) {
  131. changeInfo.event = event;
  132. }
  133. if (!exceedMaxCount || file.status === 'removed' ||
  134. // We should ignore event if current file is exceed `maxCount`
  135. cloneList.some(f => f.uid === file.uid)) {
  136. // eslint-disable-next-line react-dom/no-flush-sync
  137. (0, _reactDom.flushSync)(() => {
  138. onChange === null || onChange === void 0 ? void 0 : onChange(changeInfo);
  139. });
  140. }
  141. };
  142. const mergedBeforeUpload = (file, fileListArgs) => __awaiter(void 0, void 0, void 0, function* () {
  143. const {
  144. beforeUpload,
  145. transformFile
  146. } = props;
  147. let parsedFile = file;
  148. if (beforeUpload) {
  149. const result = yield beforeUpload(file, fileListArgs);
  150. if (result === false) {
  151. return false;
  152. }
  153. // Hack for LIST_IGNORE, we add additional info to remove from the list
  154. delete file[LIST_IGNORE];
  155. if (result === LIST_IGNORE) {
  156. Object.defineProperty(file, LIST_IGNORE, {
  157. value: true,
  158. configurable: true
  159. });
  160. return false;
  161. }
  162. if (typeof result === 'object' && result) {
  163. parsedFile = result;
  164. }
  165. }
  166. if (transformFile) {
  167. parsedFile = yield transformFile(parsedFile);
  168. }
  169. return parsedFile;
  170. });
  171. const onBatchStart = batchFileInfoList => {
  172. // Skip file which marked as `LIST_IGNORE`, these file will not add to file list
  173. const filteredFileInfoList = batchFileInfoList.filter(info => !info.file[LIST_IGNORE]);
  174. // Nothing to do since no file need upload
  175. if (!filteredFileInfoList.length) {
  176. return;
  177. }
  178. const objectFileList = filteredFileInfoList.map(info => (0, _utils.file2Obj)(info.file));
  179. // Concat new files with prev files
  180. let newFileList = (0, _toConsumableArray2.default)(mergedFileList);
  181. objectFileList.forEach(fileObj => {
  182. // Replace file if exist
  183. newFileList = (0, _utils.updateFileList)(fileObj, newFileList);
  184. });
  185. objectFileList.forEach((fileObj, index) => {
  186. // Repeat trigger `onChange` event for compatible
  187. let triggerFileObj = fileObj;
  188. if (!filteredFileInfoList[index].parsedFile) {
  189. // `beforeUpload` return false
  190. const {
  191. originFileObj
  192. } = fileObj;
  193. let clone;
  194. try {
  195. clone = new File([originFileObj], originFileObj.name, {
  196. type: originFileObj.type
  197. });
  198. } catch (_a) {
  199. clone = new Blob([originFileObj], {
  200. type: originFileObj.type
  201. });
  202. clone.name = originFileObj.name;
  203. clone.lastModifiedDate = new Date();
  204. clone.lastModified = new Date().getTime();
  205. }
  206. clone.uid = fileObj.uid;
  207. triggerFileObj = clone;
  208. } else {
  209. // Inject `uploading` status
  210. fileObj.status = 'uploading';
  211. }
  212. onInternalChange(triggerFileObj, newFileList);
  213. });
  214. };
  215. const onSuccess = (response, file, xhr) => {
  216. try {
  217. if (typeof response === 'string') {
  218. response = JSON.parse(response);
  219. }
  220. } catch (_a) {
  221. /* do nothing */
  222. }
  223. // removed
  224. if (!(0, _utils.getFileItem)(file, mergedFileList)) {
  225. return;
  226. }
  227. const targetItem = (0, _utils.file2Obj)(file);
  228. targetItem.status = 'done';
  229. targetItem.percent = 100;
  230. targetItem.response = response;
  231. targetItem.xhr = xhr;
  232. const nextFileList = (0, _utils.updateFileList)(targetItem, mergedFileList);
  233. onInternalChange(targetItem, nextFileList);
  234. };
  235. const onProgress = (e, file) => {
  236. // removed
  237. if (!(0, _utils.getFileItem)(file, mergedFileList)) {
  238. return;
  239. }
  240. const targetItem = (0, _utils.file2Obj)(file);
  241. targetItem.status = 'uploading';
  242. targetItem.percent = e.percent;
  243. const nextFileList = (0, _utils.updateFileList)(targetItem, mergedFileList);
  244. onInternalChange(targetItem, nextFileList, e);
  245. };
  246. const onError = (error, response, file) => {
  247. // removed
  248. if (!(0, _utils.getFileItem)(file, mergedFileList)) {
  249. return;
  250. }
  251. const targetItem = (0, _utils.file2Obj)(file);
  252. targetItem.error = error;
  253. targetItem.response = response;
  254. targetItem.status = 'error';
  255. const nextFileList = (0, _utils.updateFileList)(targetItem, mergedFileList);
  256. onInternalChange(targetItem, nextFileList);
  257. };
  258. const handleRemove = file => {
  259. let currentFile;
  260. Promise.resolve(typeof onRemove === 'function' ? onRemove(file) : onRemove).then(ret => {
  261. var _a;
  262. // Prevent removing file
  263. if (ret === false) {
  264. return;
  265. }
  266. const removedFileList = (0, _utils.removeFileItem)(file, mergedFileList);
  267. if (removedFileList) {
  268. currentFile = Object.assign(Object.assign({}, file), {
  269. status: 'removed'
  270. });
  271. mergedFileList === null || mergedFileList === void 0 ? void 0 : mergedFileList.forEach(item => {
  272. const matchKey = currentFile.uid !== undefined ? 'uid' : 'name';
  273. if (item[matchKey] === currentFile[matchKey] && !Object.isFrozen(item)) {
  274. item.status = 'removed';
  275. }
  276. });
  277. (_a = upload.current) === null || _a === void 0 ? void 0 : _a.abort(currentFile);
  278. onInternalChange(currentFile, removedFileList);
  279. }
  280. });
  281. };
  282. const onFileDrop = e => {
  283. setDragState(e.type);
  284. if (e.type === 'drop') {
  285. onDrop === null || onDrop === void 0 ? void 0 : onDrop(e);
  286. }
  287. };
  288. // Test needs
  289. React.useImperativeHandle(ref, () => ({
  290. onBatchStart,
  291. onSuccess,
  292. onProgress,
  293. onError,
  294. fileList: mergedFileList,
  295. upload: upload.current,
  296. nativeElement: wrapRef.current
  297. }));
  298. const {
  299. getPrefixCls,
  300. direction,
  301. upload: ctxUpload
  302. } = React.useContext(_configProvider.ConfigContext);
  303. const prefixCls = getPrefixCls('upload', customizePrefixCls);
  304. const rcUploadProps = Object.assign(Object.assign({
  305. onBatchStart,
  306. onError,
  307. onProgress,
  308. onSuccess
  309. }, props), {
  310. customRequest,
  311. data,
  312. multiple,
  313. action,
  314. accept,
  315. supportServerRender,
  316. prefixCls,
  317. disabled: mergedDisabled,
  318. beforeUpload: mergedBeforeUpload,
  319. onChange: undefined,
  320. hasControlInside
  321. });
  322. delete rcUploadProps.className;
  323. delete rcUploadProps.style;
  324. // Remove id to avoid open by label when trigger is hidden
  325. // !children: https://github.com/ant-design/ant-design/issues/14298
  326. // disabled: https://github.com/ant-design/ant-design/issues/16478
  327. // https://github.com/ant-design/ant-design/issues/24197
  328. if (!children || mergedDisabled) {
  329. delete rcUploadProps.id;
  330. }
  331. const wrapperCls = `${prefixCls}-wrapper`;
  332. const [wrapCSSVar, hashId, cssVarCls] = (0, _style.default)(prefixCls, wrapperCls);
  333. const [contextLocale] = (0, _locale.useLocale)('Upload', _en_US.default.Upload);
  334. const {
  335. showRemoveIcon,
  336. showPreviewIcon,
  337. showDownloadIcon,
  338. removeIcon,
  339. previewIcon,
  340. downloadIcon,
  341. extra
  342. } = typeof showUploadList === 'boolean' ? {} : showUploadList;
  343. // use showRemoveIcon if it is specified explicitly
  344. const realShowRemoveIcon = typeof showRemoveIcon === 'undefined' ? !mergedDisabled : showRemoveIcon;
  345. const renderUploadList = (button, buttonVisible) => {
  346. if (!showUploadList) {
  347. return button;
  348. }
  349. return /*#__PURE__*/React.createElement(_UploadList.default, {
  350. prefixCls: prefixCls,
  351. listType: listType,
  352. items: mergedFileList,
  353. previewFile: previewFile,
  354. onPreview: onPreview,
  355. onDownload: onDownload,
  356. onRemove: handleRemove,
  357. showRemoveIcon: realShowRemoveIcon,
  358. showPreviewIcon: showPreviewIcon,
  359. showDownloadIcon: showDownloadIcon,
  360. removeIcon: removeIcon,
  361. previewIcon: previewIcon,
  362. downloadIcon: downloadIcon,
  363. iconRender: iconRender,
  364. extra: extra,
  365. locale: Object.assign(Object.assign({}, contextLocale), propLocale),
  366. isImageUrl: isImageUrl,
  367. progress: progress,
  368. appendAction: button,
  369. appendActionVisible: buttonVisible,
  370. itemRender: itemRender,
  371. disabled: mergedDisabled
  372. });
  373. };
  374. const mergedCls = (0, _classnames.default)(wrapperCls, className, rootClassName, hashId, cssVarCls, ctxUpload === null || ctxUpload === void 0 ? void 0 : ctxUpload.className, {
  375. [`${prefixCls}-rtl`]: direction === 'rtl',
  376. [`${prefixCls}-picture-card-wrapper`]: listType === 'picture-card',
  377. [`${prefixCls}-picture-circle-wrapper`]: listType === 'picture-circle'
  378. });
  379. const mergedStyle = Object.assign(Object.assign({}, ctxUpload === null || ctxUpload === void 0 ? void 0 : ctxUpload.style), style);
  380. // ======================== Render ========================
  381. if (type === 'drag') {
  382. const dragCls = (0, _classnames.default)(hashId, prefixCls, `${prefixCls}-drag`, {
  383. [`${prefixCls}-drag-uploading`]: mergedFileList.some(file => file.status === 'uploading'),
  384. [`${prefixCls}-drag-hover`]: dragState === 'dragover',
  385. [`${prefixCls}-disabled`]: mergedDisabled,
  386. [`${prefixCls}-rtl`]: direction === 'rtl'
  387. });
  388. return wrapCSSVar(/*#__PURE__*/React.createElement("span", {
  389. className: mergedCls,
  390. ref: wrapRef
  391. }, /*#__PURE__*/React.createElement("div", {
  392. className: dragCls,
  393. style: mergedStyle,
  394. onDrop: onFileDrop,
  395. onDragOver: onFileDrop,
  396. onDragLeave: onFileDrop
  397. }, /*#__PURE__*/React.createElement(_rcUpload.default, Object.assign({}, rcUploadProps, {
  398. ref: upload,
  399. className: `${prefixCls}-btn`
  400. }), /*#__PURE__*/React.createElement("div", {
  401. className: `${prefixCls}-drag-container`
  402. }, children))), renderUploadList()));
  403. }
  404. const uploadBtnCls = (0, _classnames.default)(prefixCls, `${prefixCls}-select`, {
  405. [`${prefixCls}-disabled`]: mergedDisabled,
  406. [`${prefixCls}-hidden`]: !children
  407. });
  408. const uploadButton = /*#__PURE__*/React.createElement("div", {
  409. className: uploadBtnCls,
  410. style: mergedStyle
  411. }, /*#__PURE__*/React.createElement(_rcUpload.default, Object.assign({}, rcUploadProps, {
  412. ref: upload
  413. })));
  414. if (listType === 'picture-card' || listType === 'picture-circle') {
  415. return wrapCSSVar(/*#__PURE__*/React.createElement("span", {
  416. className: mergedCls,
  417. ref: wrapRef
  418. }, renderUploadList(uploadButton, !!children)));
  419. }
  420. return wrapCSSVar(/*#__PURE__*/React.createElement("span", {
  421. className: mergedCls,
  422. ref: wrapRef
  423. }, uploadButton, renderUploadList()));
  424. };
  425. const Upload = /*#__PURE__*/React.forwardRef(InternalUpload);
  426. if (process.env.NODE_ENV !== 'production') {
  427. Upload.displayName = 'Upload';
  428. }
  429. var _default = exports.default = Upload;