UNPKG

@progress/kendo-react-upload

Version:

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

480 lines (479 loc) 15.1 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 t 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 p 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(u) { super(u), this._httpSubscriptions = {}, this._uploadNavigation = null, this.showLicenseWatermark = !1, this.focus = () => { this._uploadNavigation && this._uploadNavigation.focus(); }, this.triggerUpload = () => { this.onUpload(); }, this.uploadFiles = (i) => { const e = this.async; p.setFilesStatus(i, l.Uploading), p.groupForEach(i, (s, a) => { const n = f.cloneRequestHeaders(e.saveHeaders || {}), r = { target: this, files: s, headers: n, additionalData: {} }; this.props.onBeforeUpload && this.props.onBeforeUpload.call(void 0, r); const c = f.populateRequestOptions(r.headers, this.async), v = f.populateUploadFormData( s, e.saveField, r.additionalData ); if (this.isCustomSave) this.props.saveUrl( s, { formData: v, requestOptions: c }, this.onUploadProgress ).then((d) => this.onUploadSuccess(d.uid)).catch((d) => this.onUploadError(d.uid)); else { const d = S.CancelToken.source(); this._httpSubscriptions[a] = d, S({ method: e.saveMethod, url: e.saveUrl, data: v, cancelToken: d.token, ...c, onUploadProgress: (h) => this.onUploadProgress(a, h) }).then((h) => this.onUploadSuccess(a, h)).catch((h) => this.onUploadError(a, h)); } }); }, this.removeFiles = (i) => { const e = this.async; p.groupForEach(i, (s, a) => { const n = f.cloneRequestHeaders(e.removeHeaders || {}), r = { target: this, files: s, headers: n, additionalData: {} }; this.props.onBeforeRemove && this.props.onBeforeRemove.call(void 0, r); const c = s.map((h) => h.name), v = f.populateRequestOptions(r.headers, this.async), d = f.populateRemoveFormData( c, e.removeField, r.additionalData ); this.isCustomRemove ? this.props.removeUrl(s, { formData: d, requestOptions: v }).then((h) => this.onRemoveSuccess(h.uid)).catch((h) => this.onRemoveError(h.uid)) : S({ method: e.removeMethod, url: e.removeUrl, data: d, ...v }).then((h) => this.onRemoveSuccess(a, h)).catch((h) => this.onRemoveError(a, h)); }); }, this.onUpload = () => { const i = this.fileStateCopy, e = p.groupFilesByUid(i), s = p.filesForUpload(e); this.uploadFiles(s); const a = () => { if (this.props.onStatusChange) { const n = { target: this, newState: i, affectedFiles: p.flatFileGroup(s) }; this.props.onStatusChange.call(void 0, n); } }; this.isControlled ? a() : this.setState({ files: i }, a); }, this.onAdd = (i) => { let e = C.getAllFileInfo(i), s; if (e = C.assignGuidToFiles(e, this.async.batch), D.validateFiles(e, this.props.restrictions), this.props.multiple ? s = this.fileStateCopy : s = [], p.addMany(e, s), this.async.autoUpload) { const n = p.groupFilesByUid(s); this.uploadFiles(p.filesForUpload(n)); } const a = () => { if (this.props.onAdd) { const n = { target: this, newState: s, affectedFiles: e }; this.props.onAdd.call(void 0, n); } }; this.isControlled ? a() : this.setState({ files: s }, a); }, this.onUploadProgress = (i, e) => { const s = Math.round(100 * e.loaded / (e.total || 0)) || 0, a = () => { if (this.props.onProgress) { const n = this.fileStateCopy, o = n.filter((c) => c.uid === i); this.setFilesProgress(n, s); const r = { target: this, newState: n, affectedFiles: o }; this.props.onProgress.call(void 0, r); } }; this.isControlled ? a() : this.setState((n) => { const o = n.files, r = o.filter((c) => c.uid === i); if (this.setFilesProgress(r, s), !!r.length) return { files: o }; }, a); }, this.onUploadSuccess = (i, e) => { const s = this.fileStateCopy, a = s.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: s, affectedFiles: a, response: e ? f.convertAxiosResponse(e) : void 0 }; this.props.onStatusChange.call(void 0, o); } }; this.isControlled ? n() : this.setState({ files: s }, n); }, this.onUploadError = (i, e) => { const s = this.fileStateCopy, a = s.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: s, affectedFiles: a, response: e ? f.convertAxiosResponse(e) : void 0 }; this.props.onStatusChange.call(void 0, o); } }; this.isControlled ? n() : this.setState({ files: s }, n); }, this.onRemove = (i) => { const e = this.fileStateCopy, s = e.filter((o) => o.uid === i), a = e.filter((o) => o.uid !== i); if ([ l.Uploaded, l.Initial, l.RemoveFailed ].indexOf(s[0].status) > -1) { const o = { [i]: s }; p.setFilesStatus(o, l.Removing), this.removeFiles(o); const r = () => { if (this.props.onStatusChange) { const c = { target: this, newState: e, affectedFiles: s }; this.props.onStatusChange.call(void 0, c); } }; this.isControlled ? r() : this.setState({ files: e }, r); } else { const o = () => { if (this.props.onRemove) { const r = { target: this, newState: a, affectedFiles: s }; this.props.onRemove.call(void 0, r); } }; this.isControlled ? o() : this.setState({ files: a }, o); } }, this.onRemoveSuccess = (i, e) => { const s = this.fileStateCopy, a = s.filter((r) => r.uid === i), n = s.filter((r) => r.uid !== i), o = () => { if (this.props.onRemove) { const r = { target: this, newState: n, affectedFiles: a, response: e ? f.convertAxiosResponse(e) : void 0 }; this.props.onRemove.call(void 0, r); } }; this.isControlled ? o() : this.setState({ files: n }, o); }, this.onRemoveError = (i, e) => { const s = this.fileStateCopy, a = s.filter((o) => o.uid === i); a.forEach((o) => { o.status = l.RemoveFailed; }); const n = () => { if (this.props.onStatusChange) { const o = { target: this, newState: s, affectedFiles: a, response: e ? f.convertAxiosResponse(e) : void 0 }; this.props.onStatusChange.call(void 0, o); } }; this.isControlled ? n() : this.setState({ files: s }, n); }, this.onRetry = (i) => { const e = this.fileStateCopy, s = p.groupFilesByUid(e.filter((n) => n.uid === i)); p.setFilesStatus(s, l.Uploading), this.uploadFiles(s); const a = () => { if (this.props.onStatusChange) { const n = { target: this, newState: e, affectedFiles: p.flatFileGroup(s) }; this.props.onStatusChange.call(void 0, n); } }; this.isControlled ? a() : this.setState({ files: e }, a); }, this.onCancel = (i) => { const e = this.fileStateCopy, s = e.filter((o) => o.uid !== i), a = e.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: s, affectedFiles: a }; this.props.onRemove.call(void 0, o); } }; this.isControlled ? n() : this.setState({ files: s }, n); }, this.onClear = () => { if (!this.files.length) return; Object.keys(this._httpSubscriptions).forEach((e) => { this._httpSubscriptions[e].cancel(); }), this._httpSubscriptions = {}; const i = () => { if (this.props.onRemove) { const e = { target: this, newState: [], affectedFiles: this.fileStateCopy }; this.props.onRemove.call(void 0, e); } }; this.isControlled ? i() : this.setState({ files: [] }, i); }, this.showLicenseWatermark = !R(O, { component: "Upload" }), this.state = { files: u.defaultFiles || [] }; } get async() { const { autoUpload: u, batch: i, removeField: e, removeHeaders: s, removeMethod: a, removeUrl: n, responseType: o, saveField: r, saveHeaders: c, saveMethod: v, saveUrl: d, withCredentials: h } = this.props; return { autoUpload: u, batch: i, removeField: e, removeHeaders: s, removeMethod: a, removeUrl: n, responseType: o, saveField: r, saveHeaders: c, saveMethod: v, saveUrl: d, 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 ? p.copyState(this.props.files) : p.copyState(this.state.files); } /** * @hidden */ get actionElement() { if (this._uploadNavigation) return this._uploadNavigation.actionElement; } /** * @hidden */ setFilesProgress(u, i) { u.forEach((e) => { e.progress = i, e.status === l.Uploading && i === 100 && (e.status = l.Uploaded); }); } /** * @hidden */ render() { var F; const { showFileList: u, onAdd: i, onRemove: e, onCancel: s, autoUpload: a, showActionButtons: n, actionsLayout: o, tabIndex: r, disabled: c, ...v } = this.props, d = p.groupFilesByUid(this.files), h = p.filesForUpload(d); return /* @__PURE__ */ m.createElement(m.Fragment, null, /* @__PURE__ */ m.createElement( w, { groupedFiles: d, className: this.props.className, showFileList: u && !!Object.keys(d).length, showActionButtons: n && !a && (!!Object.keys(h).length || !!((F = this.props.defaultFiles) != null && F.length)), actionsLayout: o, autoUpload: a, disabled: c, onAdd: this.onAdd, onRemove: this.onRemove, onClear: this.onClear, onUpload: this.onUpload, onRetry: this.onRetry, onCancel: this.onCancel, tabIndex: b(r, c), ref: (y) => { this._uploadNavigation = y; }, ...v } ), 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: t.bool, batch: t.bool, withCredentials: t.bool, saveField: t.string, saveHeaders: t.object, saveMethod: t.string, saveUrl: t.oneOfType([t.string, t.func]), responseType: t.oneOf(["arraybuffer", "blob", "json", "text"]), removeField: t.string, removeHeaders: t.object, removeMethod: t.string, removeUrl: t.oneOfType([t.string, t.func]), multiple: t.bool, disabled: t.bool, showFileList: t.bool, showActionButtons: t.bool, actionsLayout: t.oneOf(["start", "center", "end", "stretched"]), tabIndex: t.number, accept: t.string, listItemUI: t.oneOfType([ t.func, t.string, t.shape({ render: t.func.isRequired }) ]), restrictions: t.shape({ allowedExtensions: t.arrayOf(t.string), maxFileSize: t.number, minFileSize: t.number }), files: t.arrayOf( t.shape({ uid: t.string, name: t.string, extension: t.string, size: t.number, validationErrors: t.arrayOf(t.string), status: t.oneOf([ l.Initial, l.RemoveFailed, l.Removing, l.Selected, l.UploadFailed, l.Uploaded, l.Uploading ]), progress: t.number, getRawFile: t.func }) ), defaultFiles: t.arrayOf( t.shape({ uid: t.string, name: t.string, extension: t.string, size: t.number, validationErrors: t.arrayOf(t.string), status: t.oneOf([ l.Initial, l.RemoveFailed, l.Removing, l.Selected, l.UploadFailed, l.Uploaded, l.Uploading ]), progress: t.number, getRawFile: t.func }) ) }; let U = g; export { U as Upload };