mirror of
https://github.com/grafana/grafana.git
synced 2025-02-15 10:03:33 -06:00
157 lines
4.2 KiB
TypeScript
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;
|
|
`,
|
|
});
|