diff --git a/package-lock.json b/package-lock.json
index 15991ce..4bccc22 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@acpaas-ui/react-editorial-components",
- "version": "1.5.6",
+ "version": "1.5.9",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -24692,9 +24692,9 @@
}
},
"sanitize-html": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.5.2.tgz",
- "integrity": "sha512-sJ1rO2YixFIqs2kIcEUb6PTrCjvz8DMq1XqWWuy0kjgjrn58GNLK1DKSIRybFZDO1WNgsEgD+WiEzTEYS8xEug==",
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.4.0.tgz",
+ "integrity": "sha512-Y1OgkUiTPMqwZNRLPERSEi39iOebn2XJLbeiGOBhaJD/yLqtLGu6GE5w7evx177LeGgSE+4p4e107LMiydOf6A==",
"requires": {
"deepmerge": "^4.2.2",
"escape-string-regexp": "^4.0.0",
@@ -24715,15 +24715,30 @@
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
},
+ "nanoid": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
"postcss": {
- "version": "8.3.9",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.9.tgz",
- "integrity": "sha512-f/ZFyAKh9Dnqytx5X62jgjhhzttjZS7hMsohcI7HEI5tjELX/HxCy3EFhsRxyzGvrzFF+82XPvCS8T9TFleVJw==",
+ "version": "8.4.21",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
+ "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
"requires": {
- "nanoid": "^3.1.28",
- "picocolors": "^0.2.1",
- "source-map-js": "^0.6.2"
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
}
+ },
+ "source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
}
}
},
diff --git a/package.json b/package.json
index 6abc233..bb49610 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@acpaas-ui/react-editorial-components",
- "version": "1.5.6",
+ "version": "1.5.9",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"scripts": {
@@ -46,7 +46,7 @@
"react-dom": "^16.13.0",
"react-popper": "^2.2.4",
"rxjs": "^6.6.3",
- "sanitize-html": "^2.4.0"
+ "sanitize-html": "2.4.0"
},
"devDependencies": {
"@a-ui/flexboxgrid": "^1.0.1",
diff --git a/src/components/FileUpload/FileUpload.const.js b/src/components/FileUpload/FileUpload.const.js
index 654465d..9fab7c0 100644
--- a/src/components/FileUpload/FileUpload.const.js
+++ b/src/components/FileUpload/FileUpload.const.js
@@ -11,4 +11,5 @@ export const UPLOAD_OPTIONS_DEFAULT = {
maxFileSize: 0, // 0 is infinite
url: '',
messages: VALIDATION_MESSAGES_DEFAULT,
+ autoUpload: true,
};
diff --git a/src/components/FileUpload/FileUpload.jsx b/src/components/FileUpload/FileUpload.jsx
index e480020..bf8f853 100644
--- a/src/components/FileUpload/FileUpload.jsx
+++ b/src/components/FileUpload/FileUpload.jsx
@@ -1,5 +1,8 @@
+/* eslint-disable react/require-default-props */
import PropTypes from 'prop-types';
-import React, { useEffect, useMemo, useState } from 'react';
+import React, {
+ forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState,
+} from 'react';
import { isNumber } from '../../helpers';
import { useSlot } from '../../hooks/useSlot';
@@ -10,16 +13,17 @@ import { FileUploadZone } from './FileUploadZone';
import { Uploader } from './Uploader';
import { ValidationList } from './ValidationList';
-const FileUpload = ({
+const FileUpload = forwardRef(({
id = '',
ariaLabelRemove = 'Verwijder',
disabled = false,
files = [],
options = UPLOAD_OPTIONS_DEFAULT,
selectUploadedFiles = () => null,
+ selectQueuedFiles = () => null,
removeFile = () => null,
children,
-}) => {
+}, ref) => {
/**
* Hooks
*/
@@ -27,6 +31,14 @@ const FileUpload = ({
const fileUploadMessageSlot = useSlot(FileUploadMessage, children);
const [uploader, setUploader] = useState(null);
const [invalidFiles, setInvalidFiles] = useState([]);
+ const [queuedFiles, setQueuedFiles] = useState([]);
+ const uploadZoneRef = useRef();
+
+ useImperativeHandle(ref, () => ({
+ startUpload(extraHeaders) {
+ return uploadZoneRef.current.uploadFiles(queuedFiles, extraHeaders);
+ },
+ }));
useEffect(() => {
if (!uploader) {
@@ -42,6 +54,14 @@ const FileUpload = ({
return true;
}, [options.fileLimit, files]);
+ useEffect(() => {
+ if (!selectQueuedFiles) {
+ return;
+ }
+
+ selectQueuedFiles(queuedFiles);
+ }, [queuedFiles, selectQueuedFiles]);
+
/**
* Methods
*/
@@ -49,6 +69,10 @@ const FileUpload = ({
setInvalidFiles(invFiles);
};
+ const onQueuedFiles = (qFiles) => {
+ setQueuedFiles(qFiles);
+ };
+
const onRequestError = (error) => {
setInvalidFiles(error.files.map((file) => ({
file,
@@ -60,6 +84,15 @@ const FileUpload = ({
setInvalidFiles(invalidFiles.filter((file, i) => i !== index));
};
+ const onRemoveFile = (fileId, index) => {
+ // If the file is queued, just delete it.
+ if (!fileId && queuedFiles?.[index]) {
+ return setQueuedFiles(queuedFiles.filter((_, fIndex) => index !== fIndex));
+ }
+
+ removeFile(fileId, index);
+ };
+
/**
* Render
*/
@@ -79,7 +112,7 @@ const FileUpload = ({
{file.name}
-
@@ -91,7 +124,10 @@ const FileUpload = ({
return (
) }
- { renderFiles(files) }
+ { renderFiles([...files, ...queuedFiles]) }
);
-};
+});
FileUpload.propTypes = {
id: PropTypes.string.isRequired,
disabled: PropTypes.bool,
ariaLabelRemove: PropTypes.string,
options: PropTypes.shape({
+ autoUpload: PropTypes.bool,
allowedMimeTypes: PropTypes.arrayOf(PropTypes.string),
allowedFileTypes: PropTypes.arrayOf(PropTypes.string),
maxFileSize: PropTypes.number,
@@ -146,12 +183,17 @@ FileUpload.propTypes = {
key: PropTypes.string,
value: PropTypes.string,
}),
+ requestHeaders: PropTypes.arrayOf(PropTypes.shape({
+ key: PropTypes.string,
+ value: PropTypes.string,
+ })),
}),
files: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
})),
selectUploadedFiles: PropTypes.func,
+ selectQueuedFiles: PropTypes.func,
removeFile: PropTypes.func,
children: PropTypes.node,
};
diff --git a/src/components/FileUpload/FileUpload.stories.mdx b/src/components/FileUpload/FileUpload.stories.mdx
index ab8132b..b2c9d63 100644
--- a/src/components/FileUpload/FileUpload.stories.mdx
+++ b/src/components/FileUpload/FileUpload.stories.mdx
@@ -1,4 +1,4 @@
-import { useState, useMemo } from 'react';
+import { useState, useMemo, useRef } from 'react';
import { action } from '@storybook/addon-actions'
import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks';
@@ -183,6 +183,43 @@ A file upload component
)
}}
+
+ {() => {
+ const [files, setFiles] = useState([]);
+ const ref = useRef();
+ return (
+ <>
+ console.log(files)}
+ files={files}
+ removeFile={(id, index) => setFiles(files.filter(file => file.id === id))}
+ selectUploadedFiles={action()}
+ >
+ Drag your files here or click to upload
+ Optional description message
+
+ ref.current.startUpload()}>Upload
+ >
+ )
+ }}
+
## Props
diff --git a/src/components/FileUpload/FileUploadZone/FileUploadZone.jsx b/src/components/FileUpload/FileUploadZone/FileUploadZone.jsx
index d403231..3ae9222 100644
--- a/src/components/FileUpload/FileUploadZone/FileUploadZone.jsx
+++ b/src/components/FileUpload/FileUploadZone/FileUploadZone.jsx
@@ -1,13 +1,16 @@
+/* eslint-disable react/require-default-props */
import classnames from 'classnames';
import PropTypes from 'prop-types';
-import React, { useMemo, useRef, useState } from 'react';
+import React, {
+ forwardRef, useImperativeHandle, useMemo, useRef, useState,
+} from 'react';
import { useSlot } from '../../../hooks/useSlot';
import { ProgressBar } from '../../ProgressBar';
import { FileUploadDescription, FileUploadMessage } from '../FileUpload.slots';
import { Uploader } from '../Uploader';
-const FileUploadZone = ({
+const FileUploadZone = forwardRef(({
autoUpload = true,
id = '',
ariaId = '',
@@ -18,12 +21,12 @@ const FileUploadZone = ({
onCustomDrop,
uploadedFiles = () => null,
invalidFiles = () => null,
+ queuedFiles = () => null,
onRequestError = () => null,
allowedMimeTypes = [],
allowedFileTypes = [],
children,
-
-}) => {
+}, ref) => {
/**
* Hooks
*/
@@ -36,6 +39,13 @@ const FileUploadZone = ({
const accept = useMemo(() => allowedFileTypes.map((type) => `.${type}`).concat(allowedMimeTypes).join(','),
[allowedFileTypes, allowedMimeTypes]);
+ useImperativeHandle(ref, () => ({
+ uploadFiles(files, extraHeaders) {
+ // eslint-disable-next-line no-use-before-define
+ return uploadFiles(files, extraHeaders);
+ },
+ }));
+
/**
* Methods
*/
@@ -47,13 +57,13 @@ const FileUploadZone = ({
}
};
- const uploadFiles = (files) => {
+ const uploadFiles = (files, extraHeaders) => new Promise((resolve, reject) => {
// Reset progress
setUploadProgress(0);
setUploadingFiles(files);
// upload
- uploader.uploadFiles(files).subscribe(
+ uploader.uploadFiles(files, extraHeaders).subscribe(
(response) => {
if (response.progress) {
setUploadProgress(Math.floor(response.progress * 100));
@@ -79,14 +89,18 @@ const FileUploadZone = ({
files,
error,
});
+
+ reject(error);
},
() => {
setUploadProgress(0);
setUploadingFiles([]);
clearFileInput();
+
+ resolve();
},
);
- };
+ });
const handleFiles = async (files, customHandler) => {
const response = await Promise.resolve(uploader.validateFiles(files));
@@ -95,9 +109,14 @@ const FileUploadZone = ({
if (customHandler) {
customHandler(response.validFiles);
}
+
if (autoUpload && response.validFiles.length > 0) {
uploadFiles(response.validFiles);
}
+
+ if (!autoUpload && response.validFiles.length > 0) {
+ queuedFiles(response.validFiles);
+ }
};
const handleCustomClick = (e) => {
@@ -200,7 +219,7 @@ const FileUploadZone = ({
>
);
-};
+});
FileUploadZone.propTypes = {
autoUpload: PropTypes.bool,
@@ -211,6 +230,7 @@ FileUploadZone.propTypes = {
ariaId: PropTypes.string,
uploadedFiles: PropTypes.func,
invalidFiles: PropTypes.func,
+ queuedFiles: PropTypes.func,
onRequestError: PropTypes.func,
onCustomClick: PropTypes.func,
onCustomDrop: PropTypes.func,
diff --git a/src/components/FileUpload/Uploader/uploader.class.js b/src/components/FileUpload/Uploader/uploader.class.js
index 0763439..d065663 100644
--- a/src/components/FileUpload/Uploader/uploader.class.js
+++ b/src/components/FileUpload/Uploader/uploader.class.js
@@ -16,7 +16,7 @@ export class Uploader {
};
}
- uploadFiles(files = []) {
+ uploadFiles(files = [], extraHeaders) {
const formData = this.filesToFormData(files);
return new Observable((observer) => {
@@ -52,6 +52,18 @@ export class Uploader {
xhr.setRequestHeader(this.options.requestHeader.key, this.options.requestHeader.value);
}
+ if (this.options.requestHeaders && Array.isArray(this.options.requestHeaders)) {
+ this.options.requestHeaders.forEach(({ key, value }) => {
+ xhr.setRequestHeader(key, value);
+ });
+ }
+
+ if (extraHeaders && Array.isArray(extraHeaders)) {
+ extraHeaders.forEach(({ key, value }) => {
+ xhr.setRequestHeader(key, value);
+ });
+ }
+
xhr.send(formData);
});
}