@grafana/ui
Version:
Grafana Components Library
258 lines (255 loc) • 9.25 kB
JavaScript
import { jsx, jsxs } from 'react/jsx-runtime';
import { cx, css } from '@emotion/css';
import { isString, uniqueId } from 'lodash';
import { useState, useCallback } from 'react';
import { useDropzone, ErrorCode } from 'react-dropzone';
import { getValueFormat, formattedValueToString } from '@grafana/data';
import { t, Trans } from '@grafana/i18n';
import { useTheme2 } from '../../themes/ThemeContext.mjs';
import { Alert } from '../Alert/Alert.mjs';
import { Icon } from '../Icon/Icon.mjs';
import { FileListItem } from './FileListItem.mjs';
function FileDropzone({ options, children, readAs, onLoad, fileListRenderer, onFileRemove }) {
const [files, setFiles] = useState([]);
const [fileErrors, setErrorMessages] = useState([]);
const formattedSize = getValueFormat("decbytes")((options == null ? void 0 : options.maxSize) ? options == null ? void 0 : options.maxSize : 0);
const setFileProperty = useCallback(
(customFile, action) => {
setFiles((oldFiles) => {
return oldFiles.map((oldFile) => {
if (oldFile.id === customFile.id) {
action(oldFile);
return oldFile;
}
return oldFile;
});
});
},
[]
);
const onDrop = useCallback(
(acceptedFiles, rejectedFiles, event) => {
let customFiles = acceptedFiles.map(mapToCustomFile);
if ((options == null ? void 0 : options.multiple) === false) {
setFiles(customFiles);
} else {
setFiles((oldFiles) => [...oldFiles, ...customFiles]);
}
setErrors(rejectedFiles);
if (options == null ? void 0 : options.onDrop) {
options.onDrop(acceptedFiles, rejectedFiles, event);
} else {
for (const customFile of customFiles) {
const reader = new FileReader();
const read = () => {
if (readAs) {
reader[readAs](customFile.file);
} else {
reader.readAsText(customFile.file);
}
};
setFileProperty(customFile, (fileToModify) => {
fileToModify.abortUpload = () => {
reader.abort();
};
fileToModify.retryUpload = () => {
setFileProperty(customFile, (fileToModify2) => {
fileToModify2.error = null;
fileToModify2.progress = void 0;
});
read();
};
});
reader.onabort = () => {
setFileProperty(customFile, (fileToModify) => {
fileToModify.error = new DOMException("Aborted");
});
};
reader.onprogress = (event2) => {
setFileProperty(customFile, (fileToModify) => {
fileToModify.progress = event2.loaded;
});
};
reader.onload = () => {
onLoad == null ? void 0 : onLoad(reader.result);
};
reader.onerror = () => {
setFileProperty(customFile, (fileToModify) => {
fileToModify.error = reader.error;
});
};
read();
}
}
},
[onLoad, options, readAs, setFileProperty]
);
const removeFile = (file) => {
const newFiles = files.filter((f) => file.id !== f.id);
setFiles(newFiles);
onFileRemove == null ? void 0 : onFileRemove(file);
};
const { getRootProps, getInputProps, isDragActive } = useDropzone({
...options,
useFsAccessApi: false,
onDrop,
accept: transformAcceptToNewFormat(options == null ? void 0 : options.accept)
});
const theme = useTheme2();
const styles = getStyles(theme, isDragActive);
const fileList = files.map((file) => {
if (fileListRenderer) {
return fileListRenderer(file, removeFile);
}
return /* @__PURE__ */ jsx(FileListItem, { file, removeFile }, file.id);
});
const setErrors = (rejectedFiles) => {
let errors = [];
rejectedFiles.map((rejectedFile) => {
rejectedFile.errors.map((newError) => {
if (errors.findIndex((presentError) => {
return presentError.code === newError.code && presentError.message === newError.message;
}) === -1) {
errors.push(newError);
}
});
});
setErrorMessages(errors);
};
const renderErrorMessages = (errors) => {
const size = formattedValueToString(formattedSize);
return /* @__PURE__ */ jsx("div", { className: styles.errorAlert, children: /* @__PURE__ */ jsx(
Alert,
{
title: t("grafana-ui.file-dropzone.error-title", "Upload failed"),
severity: "error",
onRemove: clearAlert,
children: errors.map((error) => {
switch (error.code) {
case ErrorCode.FileTooLarge:
return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(Trans, { i18nKey: "grafana-ui.file-dropzone.file-too-large", children: [
"File is larger than ",
{ size }
] }) }, error.message + error.code);
default:
return /* @__PURE__ */ jsx("div", { children: error.message }, error.message + error.code);
}
})
}
) });
};
const clearAlert = () => {
setErrorMessages([]);
};
return /* @__PURE__ */ jsxs("div", { className: styles.container, children: [
/* @__PURE__ */ jsxs("div", { "data-testid": "dropzone", ...getRootProps({ className: styles.dropzone }), children: [
/* @__PURE__ */ jsx("input", { ...getInputProps() }),
children != null ? children : /* @__PURE__ */ jsx(FileDropzoneDefaultChildren, { primaryText: getPrimaryText(files, options) })
] }),
fileErrors.length > 0 && renderErrorMessages(fileErrors),
/* @__PURE__ */ jsxs("small", { className: cx(styles.small, styles.acceptContainer), children: [
(options == null ? void 0 : options.maxSize) && `Max file size: ${formattedValueToString(formattedSize)}`,
(options == null ? void 0 : options.maxSize) && (options == null ? void 0 : options.accept) && /* @__PURE__ */ jsx("span", { className: styles.acceptSeparator, children: "|" }),
(options == null ? void 0 : options.accept) && getAcceptedFileTypeText(options.accept)
] }),
fileList
] });
}
function getMimeTypeByExtension(ext) {
if (["txt", "json", "csv", "xls", "yml"].some((e) => ext.match(e))) {
return "text/plain";
}
return "application/octet-stream";
}
function transformAcceptToNewFormat(accept) {
if (isString(accept)) {
return {
[getMimeTypeByExtension(accept)]: [accept]
};
}
if (Array.isArray(accept)) {
return accept.reduce((prev, current) => {
const mime = getMimeTypeByExtension(current);
prev[mime] = prev[mime] ? [...prev[mime], current] : [current];
return prev;
}, {});
}
return accept;
}
function FileDropzoneDefaultChildren({ primaryText = "Drop file here or click to upload", secondaryText = "" }) {
const theme = useTheme2();
const styles = getStyles(theme);
return /* @__PURE__ */ jsxs("div", { className: cx(styles.defaultDropZone), "data-testid": "file-drop-zone-default-children", children: [
/* @__PURE__ */ jsx(Icon, { className: cx(styles.icon), name: "upload", size: "xl" }),
/* @__PURE__ */ jsx("h6", { className: cx(styles.primaryText), children: primaryText }),
/* @__PURE__ */ jsx("small", { className: styles.small, children: secondaryText })
] });
}
function getPrimaryText(files, options) {
if ((options == null ? void 0 : options.multiple) === void 0 || (options == null ? void 0 : options.multiple)) {
return "Upload file";
}
return files.length ? "Replace file" : "Upload file";
}
function getAcceptedFileTypeText(accept) {
if (isString(accept)) {
return `Accepted file type: ${accept}`;
}
if (Array.isArray(accept)) {
return `Accepted file types: ${accept.join(", ")}`;
}
return `Accepted file types: ${Object.values(accept).flat().join(", ")}`;
}
function mapToCustomFile(file) {
return {
id: uniqueId("file"),
file,
error: null
};
}
function getStyles(theme, isDragActive) {
return {
container: css({
display: "flex",
flexDirection: "column",
width: "100%",
padding: theme.spacing(2),
borderRadius: theme.shape.radius.default,
border: `1px dashed ${theme.colors.border.strong}`,
backgroundColor: isDragActive ? theme.colors.background.secondary : theme.colors.background.primary,
cursor: "pointer",
alignItems: "center",
justifyContent: "center"
}),
dropzone: css({
height: "100%",
width: "100%",
display: "flex",
flexDirection: "column"
}),
defaultDropZone: css({
textAlign: "center"
}),
icon: css({
marginBottom: theme.spacing(1)
}),
primaryText: css({
marginBottom: theme.spacing(1)
}),
acceptContainer: css({
textAlign: "center",
margin: 0
}),
acceptSeparator: css({
margin: `0 ${theme.spacing(1)}`
}),
small: css({
color: theme.colors.text.secondary
}),
errorAlert: css({
paddingTop: "10px"
})
};
}
export { FileDropzone, FileDropzoneDefaultChildren, getMimeTypeByExtension, transformAcceptToNewFormat };
//# sourceMappingURL=FileDropzone.mjs.map