grafana/public/app/features/storage/UploadView.tsx
Adela Almasan 2f942c57e8
Storage: Optionally overwrite existing files (#52067)
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2022-07-12 09:13:57 -05:00

157 lines
4.2 KiB
TypeScript

import { css } from '@emotion/css';
import React, { useState } from 'react';
import SVG from 'react-inlinesvg';
import { GrafanaTheme2 } from '@grafana/data';
import { Alert, Button, ButtonGroup, Checkbox, Field, FileDropzone, useStyles2 } from '@grafana/ui';
import { filenameAlreadyExists, getGrafanaStorage } from './helper';
import { UploadReponse } from './types';
interface Props {
folder: string;
onUpload: (rsp: UploadReponse) => void;
fileNames: string[];
}
interface ErrorResponse {
message: string;
}
const FileDropzoneCustomChildren = ({ secondaryText = 'Drag and drop here or browse' }) => {
const styles = useStyles2(getStyles);
return (
<div className={styles.iconWrapper}>
<small className={styles.small}>{secondaryText}</small>
</div>
);
};
export const UploadView = ({ folder, onUpload, fileNames }: Props) => {
const [file, setFile] = useState<File | undefined>(undefined);
const styles = useStyles2(getStyles);
const [error, setError] = useState<ErrorResponse>({ message: '' });
const [overwriteExistingFile, setOverwriteExistingFile] = useState(false);
const Preview = () => {
if (!file) {
return <></>;
}
const isImage = file.type?.startsWith('image/');
const isSvg = file.name?.endsWith('.svg');
const src = URL.createObjectURL(file);
return (
<Field label="Preview">
<div className={styles.iconPreview}>
{isSvg && <SVG src={src} className={styles.img} />}
{isImage && !isSvg && <img src={src} className={styles.img} />}
</div>
</Field>
);
};
const doUpload = async () => {
if (!file) {
setError({ message: 'please select a file' });
return;
}
const rsp = await getGrafanaStorage().upload(folder, file, overwriteExistingFile);
if (rsp.status !== 200) {
setError(rsp);
} else {
onUpload(rsp);
}
};
const filenameExists = file ? filenameAlreadyExists(file.name, fileNames) : false;
const isUploadDisabled = !file || (filenameExists && !overwriteExistingFile);
return (
<div>
<FileDropzone
readAs="readAsBinaryString"
onFileRemove={() => {
setFile(undefined);
}}
options={{
accept: { 'image/*': ['.jpg', '.jpeg', '.png', '.gif', '.webp'] },
multiple: false,
onDrop: (acceptedFiles: File[]) => {
setFile(acceptedFiles[0]);
},
}}
>
{error.message !== '' ? <p>{error.message}</p> : Boolean(file) ? <Preview /> : <FileDropzoneCustomChildren />}
</FileDropzone>
{file && filenameExists && (
<div className={styles.alert}>
<Alert title={`${file.name} already exists`} severity="error">
<Checkbox
value={overwriteExistingFile}
onChange={() => setOverwriteExistingFile(!overwriteExistingFile)}
label="Overwrite existing file"
/>
</Alert>
</div>
)}
<ButtonGroup>
<Button className={styles.button} variant={'primary'} disabled={isUploadDisabled} onClick={doUpload}>
Upload
</Button>
</ButtonGroup>
</div>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
resourcePickerPopover: css`
border-radius: ${theme.shape.borderRadius()};
box-shadow: ${theme.shadows.z3};
background: ${theme.colors.background.primary};
border: 1px solid ${theme.colors.border.medium};
`,
resourcePickerPopoverContent: css`
width: 315px;
font-size: ${theme.typography.bodySmall.fontSize};
min-height: 184px;
padding: ${theme.spacing(1)};
display: flex;
flex-direction: column;
`,
button: css`
margin: 12px 20px 5px;
`,
iconPreview: css`
width: 238px;
height: 198px;
border: 1px solid ${theme.colors.border.medium};
display: flex;
align-items: center;
justify-content: center;
`,
img: css`
width: 147px;
height: 147px;
fill: ${theme.colors.text.primary};
`,
iconWrapper: css`
display: flex;
flex-direction: column;
align-items: center;
`,
small: css`
color: ${theme.colors.text.secondary};
margin-bottom: ${theme.spacing(2)};
`,
alert: css`
padding-top: 10px;
`,
});