UNPKG

@progress/kendo-react-upload

Version:

React Upload component helps users transfer files from their file systems to dedicated server handlers. KendoReact Upload package

471 lines (470 loc) 14.9 kB
/** * @license *------------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the package root for more information *------------------------------------------------------------------------------------------- */ import * as m from "react"; import e from "prop-types"; import { validatePackage as R, getTabIndex as b, WatermarkOverlay as E } from "@progress/kendo-react-common"; import { UploadFileStatus as l } from "./interfaces/UploadFileStatus.mjs"; import { UploadNavigation as w } from "./UploadNavigation.mjs"; import S from "axios"; import C from "./utils/utils.mjs"; import d from "./utils/stateUtils.mjs"; import f from "./utils/connectionUtils.mjs"; import D from "./utils/validationUtils.mjs"; import { packageMetadata as O } from "./package-metadata.mjs"; const g = class g extends m.Component { constructor(v) { super(v), this._httpSubscriptions = {}, this._uploadNavigation = null, this.showLicenseWatermark = !1, this.focus = () => { this._uploadNavigation && this._uploadNavigation.focus(); }, this.triggerUpload = () => { this.onUpload(); }, this.uploadFiles = (i) => { const s = this.async; d.setFilesStatus(i, l.Uploading), d.groupForEach(i, (t, a) => { const n = f.cloneRequestHeaders(s.saveHeaders || {}), r = { target: this, files: t, headers: n, additionalData: {} }; this.props.onBeforeUpload && this.props.onBeforeUpload.call(void 0, r); const p = f.populateRequestOptions(r.headers, this.async), u = f.populateUploadFormData( t, s.saveField, r.additionalData ); if (this.isCustomSave) this.props.saveUrl( t, { formData: u, requestOptions: p }, this.onUploadProgress ).then((c) => this.onUploadSuccess(c.uid)).catch((c) => this.onUploadError(c.uid)); else { const c = S.CancelToken.source(); this._httpSubscriptions[a] = c, S({ method: s.saveMethod, url: s.saveUrl, data: u, cancelToken: c.token, ...p, onUploadProgress: (h) => this.onUploadProgress(a, h) }).then((h) => this.onUploadSuccess(a, h)).catch((h) => this.onUploadError(a, h)); } }); }, this.removeFiles = (i) => { const s = this.async; d.groupForEach(i, (t, a) => { const n = f.cloneRequestHeaders(s.removeHeaders || {}), r = { target: this, files: t, headers: n, additionalData: {} }; this.props.onBeforeRemove && this.props.onBeforeRemove.call(void 0, r); const p = t.map((h) => h.name), u = f.populateRequestOptions(r.headers, this.async), c = f.populateRemoveFormData( p, s.removeField, r.additionalData ); this.isCustomRemove ? this.props.removeUrl(t, { formData: c, requestOptions: u }).then((h) => this.onRemoveSuccess(h.uid)).catch((h) => this.onRemoveError(h.uid)) : S({ method: s.removeMethod, url: s.removeUrl, data: c, ...u }).then((h) => this.onRemoveSuccess(a, h)).catch((h) => this.onRemoveError(a, h)); }); }, this.onUpload = () => { const i = this.fileStateCopy, s = d.groupFilesByUid(i), t = d.filesForUpload(s); this.uploadFiles(t); const a = () => { if (this.props.onStatusChange) { const n = { target: this, newState: i, affectedFiles: d.flatFileGroup(t) }; this.props.onStatusChange.call(void 0, n); } }; this.isControlled ? a() : this.setState({ files: i }, a); }, this.onAdd = (i) => { let s = C.getAllFileInfo(i), t; if (s = C.assignGuidToFiles(s, this.async.batch), D.validateFiles(s, this.props.restrictions), this.props.multiple ? t = this.fileStateCopy : t = [], d.addMany(s, t), this.async.autoUpload) { const n = d.groupFilesByUid(t); this.uploadFiles(d.filesForUpload(n)); } const a = () => { if (this.props.onAdd) { const n = { target: this, newState: t, affectedFiles: s }; this.props.onAdd.call(void 0, n); } }; this.isControlled ? a() : this.setState({ files: t }, a); }, this.onUploadProgress = (i, s) => { let t = this.fileStateCopy, a = t.filter((o) => o.uid === i); const n = () => { if (this.props.onProgress) { const o = { target: this, newState: t, affectedFiles: a }; this.props.onProgress.call(void 0, o); } }; this.setState((o) => { const r = Math.round(100 * s.loaded / (s.total || 0)) || 0; if (t = o.files, a = t.filter((p) => p.uid === i), !!a.length) return a.forEach((p) => { p.progress = r, p.status === l.Uploading && r === 100 && (p.status = l.Uploaded); }), this.isControlled && n(), { files: t }; }, n); }, this.onUploadSuccess = (i, s) => { const t = this.fileStateCopy, a = t.filter((o) => o.uid === i); a.forEach((o) => { o.status = l.Uploaded, o.progress = 100; }), delete this._httpSubscriptions[i]; const n = () => { if (this.props.onStatusChange) { const o = { target: this, newState: t, affectedFiles: a, response: s ? f.convertAxiosResponse(s) : void 0 }; this.props.onStatusChange.call(void 0, o); } }; this.isControlled ? n() : this.setState({ files: t }, n); }, this.onUploadError = (i, s) => { const t = this.fileStateCopy, a = t.filter((o) => o.uid === i); if (a.forEach((o) => { o.status = l.UploadFailed; }), delete this._httpSubscriptions[i], !a.length) return; const n = () => { if (this.props.onStatusChange) { const o = { target: this, newState: t, affectedFiles: a, response: s ? f.convertAxiosResponse(s) : void 0 }; this.props.onStatusChange.call(void 0, o); } }; this.isControlled ? n() : this.setState({ files: t }, n); }, this.onRemove = (i) => { const s = this.fileStateCopy, t = s.filter((o) => o.uid === i), a = s.filter((o) => o.uid !== i); if ([ l.Uploaded, l.Initial, l.RemoveFailed ].indexOf(t[0].status) > -1) { const o = { [i]: t }; d.setFilesStatus(o, l.Removing), this.removeFiles(o); const r = () => { if (this.props.onStatusChange) { const p = { target: this, newState: s, affectedFiles: t }; this.props.onStatusChange.call(void 0, p); } }; this.isControlled ? r() : this.setState({ files: s }, r); } else { const o = () => { if (this.props.onRemove) { const r = { target: this, newState: a, affectedFiles: t }; this.props.onRemove.call(void 0, r); } }; this.isControlled ? o() : this.setState({ files: a }, o); } }, this.onRemoveSuccess = (i, s) => { const t = this.fileStateCopy, a = t.filter((r) => r.uid === i), n = t.filter((r) => r.uid !== i), o = () => { if (this.props.onRemove) { const r = { target: this, newState: n, affectedFiles: a, response: s ? f.convertAxiosResponse(s) : void 0 }; this.props.onRemove.call(void 0, r); } }; this.isControlled ? o() : this.setState({ files: n }, o); }, this.onRemoveError = (i, s) => { const t = this.fileStateCopy, a = t.filter((o) => o.uid === i); a.forEach((o) => { o.status = l.RemoveFailed; }); const n = () => { if (this.props.onStatusChange) { const o = { target: this, newState: t, affectedFiles: a, response: s ? f.convertAxiosResponse(s) : void 0 }; this.props.onStatusChange.call(void 0, o); } }; this.isControlled ? n() : this.setState({ files: t }, n); }, this.onRetry = (i) => { const s = this.fileStateCopy, t = d.groupFilesByUid(s.filter((n) => n.uid === i)); d.setFilesStatus(t, l.Uploading), this.uploadFiles(t); const a = () => { if (this.props.onStatusChange) { const n = { target: this, newState: s, affectedFiles: d.flatFileGroup(t) }; this.props.onStatusChange.call(void 0, n); } }; this.isControlled ? a() : this.setState({ files: s }, a); }, this.onCancel = (i) => { const s = this.fileStateCopy, t = s.filter((o) => o.uid !== i), a = s.filter((o) => o.uid === i); if (this._httpSubscriptions[i] && (this._httpSubscriptions[i].cancel(), delete this._httpSubscriptions[i]), this.props.onCancel) { const o = { target: this, uid: i }; this.props.onCancel.call(void 0, o); } const n = () => { if (this.props.onRemove) { const o = { target: this, newState: t, affectedFiles: a }; this.props.onRemove.call(void 0, o); } }; this.isControlled ? n() : this.setState({ files: t }, n); }, this.onClear = () => { if (!this.files.length) return; Object.keys(this._httpSubscriptions).forEach((s) => { this._httpSubscriptions[s].cancel(); }), this._httpSubscriptions = {}; const i = () => { if (this.props.onRemove) { const s = { target: this, newState: [], affectedFiles: this.fileStateCopy }; this.props.onRemove.call(void 0, s); } }; this.isControlled ? i() : this.setState({ files: [] }, i); }, this.showLicenseWatermark = !R(O, { component: "Upload" }), this.state = { files: v.defaultFiles || [] }; } get async() { const { autoUpload: v, batch: i, removeField: s, removeHeaders: t, removeMethod: a, removeUrl: n, responseType: o, saveField: r, saveHeaders: p, saveMethod: u, saveUrl: c, withCredentials: h } = this.props; return { autoUpload: v, batch: i, removeField: s, removeHeaders: t, removeMethod: a, removeUrl: n, responseType: o, saveField: r, saveHeaders: p, saveMethod: u, saveUrl: c, withCredentials: h }; } get files() { return (this.isControlled ? this.props.files : this.state.files) || []; } get isControlled() { return !this.props.defaultFiles; } get isCustomSave() { return this.props.saveUrl && typeof this.props.saveUrl == "function"; } get isCustomRemove() { return this.props.removeUrl && typeof this.props.removeUrl == "function"; } get fileStateCopy() { return this.isControlled ? d.copyState(this.props.files) : d.copyState(this.state.files); } /** * @hidden */ get actionElement() { if (this._uploadNavigation) return this._uploadNavigation.actionElement; } /** * @hidden */ render() { var F; const { showFileList: v, onAdd: i, onRemove: s, onCancel: t, autoUpload: a, showActionButtons: n, actionsLayout: o, tabIndex: r, disabled: p, ...u } = this.props, c = d.groupFilesByUid(this.files), h = d.filesForUpload(c); return /* @__PURE__ */ m.createElement(m.Fragment, null, /* @__PURE__ */ m.createElement( w, { groupedFiles: c, className: this.props.className, showFileList: v && !!Object.keys(c).length, showActionButtons: n && !a && (!!Object.keys(h).length || !!((F = this.props.defaultFiles) != null && F.length)), actionsLayout: o, autoUpload: a, disabled: p, onAdd: this.onAdd, onRemove: this.onRemove, onClear: this.onClear, onUpload: this.onUpload, onRetry: this.onRetry, onCancel: this.onCancel, tabIndex: b(r, p), ref: (y) => this._uploadNavigation = y, ...u } ), this.showLicenseWatermark && /* @__PURE__ */ m.createElement(E, null)); } }; g.defaultProps = { autoUpload: !0, batch: !1, removeField: "fileNames", removeHeaders: {}, removeMethod: "POST", removeUrl: "", responseType: "json", saveField: "files", saveHeaders: {}, saveMethod: "POST", saveUrl: "", withCredentials: !0, restrictions: { allowedExtensions: [], maxFileSize: 0, minFileSize: 0 }, multiple: !0, showFileList: !0, showActionButtons: !0, actionsLayout: "end", disabled: !1 }, g.propTypes = { autoUpload: e.bool, batch: e.bool, withCredentials: e.bool, saveField: e.string, saveHeaders: e.object, saveMethod: e.string, saveUrl: e.oneOfType([e.string, e.func]), responseType: e.oneOf(["arraybuffer", "blob", "json", "text"]), removeField: e.string, removeHeaders: e.object, removeMethod: e.string, removeUrl: e.oneOfType([e.string, e.func]), multiple: e.bool, disabled: e.bool, showFileList: e.bool, showActionButtons: e.bool, actionsLayout: e.oneOf(["start", "center", "end", "stretched"]), tabIndex: e.number, accept: e.string, listItemUI: e.oneOfType([ e.func, e.string, e.shape({ render: e.func.isRequired }) ]), restrictions: e.shape({ allowedExtensions: e.arrayOf(e.string), maxFileSize: e.number, minFileSize: e.number }), files: e.arrayOf( e.shape({ uid: e.string, name: e.string, extension: e.string, size: e.number, validationErrors: e.arrayOf(e.string), status: e.oneOf([ l.Initial, l.RemoveFailed, l.Removing, l.Selected, l.UploadFailed, l.Uploaded, l.Uploading ]), progress: e.number, getRawFile: e.func }) ), defaultFiles: e.arrayOf( e.shape({ uid: e.string, name: e.string, extension: e.string, size: e.number, validationErrors: e.arrayOf(e.string), status: e.oneOf([ l.Initial, l.RemoveFailed, l.Removing, l.Selected, l.UploadFailed, l.Uploaded, l.Uploading ]), progress: e.number, getRawFile: e.func }) ) }; let U = g; export { U as Upload };