drop
This commit is contained in:
108
src/components/FileDropzone.jsx
Normal file
108
src/components/FileDropzone.jsx
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { useState, useRef } from 'react';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An otherwise invisible dropzone wrapper.
|
||||||
|
* Renders seamlessly around children without altering layout or style,
|
||||||
|
* and highlights with an outline only while a file is dragged over it.
|
||||||
|
*/
|
||||||
|
export function FileDropzone({
|
||||||
|
accept = 'application/pdf,.pdf',
|
||||||
|
disabled = false,
|
||||||
|
onFileDrop,
|
||||||
|
children,
|
||||||
|
sx,
|
||||||
|
...props
|
||||||
|
}) {
|
||||||
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
|
const dragCounter = useRef(0);
|
||||||
|
|
||||||
|
const handleDragEnter = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (disabled) return;
|
||||||
|
dragCounter.current += 1;
|
||||||
|
if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
|
||||||
|
setIsDragging(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragLeave = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
dragCounter.current -= 1;
|
||||||
|
if (dragCounter.current <= 0) {
|
||||||
|
dragCounter.current = 0;
|
||||||
|
setIsDragging(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragOver = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (disabled) {
|
||||||
|
e.dataTransfer.dropEffect = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.dataTransfer.dropEffect = 'copy';
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrop = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
dragCounter.current = 0;
|
||||||
|
setIsDragging(false);
|
||||||
|
if (disabled) return;
|
||||||
|
|
||||||
|
const file = e.dataTransfer.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
if (accept) {
|
||||||
|
const allowed = accept
|
||||||
|
.split(',')
|
||||||
|
.map((s) => s.trim().toLowerCase())
|
||||||
|
.filter(Boolean);
|
||||||
|
const name = (file.name || '').toLowerCase();
|
||||||
|
const dotIndex = name.lastIndexOf('.');
|
||||||
|
const ext = dotIndex !== -1 ? name.slice(dotIndex) : '';
|
||||||
|
const mimeType = (file.type || '').toLowerCase();
|
||||||
|
|
||||||
|
const matches = allowed.some((item) => {
|
||||||
|
if (item.startsWith('.')) {
|
||||||
|
return ext === item;
|
||||||
|
}
|
||||||
|
return mimeType === item;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!matches) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileDrop?.(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
onDragEnter={handleDragEnter}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
sx={{
|
||||||
|
display: 'inline-flex',
|
||||||
|
position: 'relative',
|
||||||
|
borderRadius: 1,
|
||||||
|
flexShrink: 0,
|
||||||
|
// Keep outline inset so it never affects external geometry or causes scrollbars:
|
||||||
|
outline: isDragging ? '2px dashed' : 'none',
|
||||||
|
outlineColor: 'primary.main',
|
||||||
|
outlineOffset: -2,
|
||||||
|
transition: 'outline 0.15s ease-in-out',
|
||||||
|
...sx,
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{typeof children === 'function' ? children({ isDragging }) : children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileDropzone;
|
||||||
@@ -7,6 +7,7 @@ import TextField from '@mui/material/TextField';
|
|||||||
import UploadFileIcon from '@mui/icons-material/UploadFile';
|
import UploadFileIcon from '@mui/icons-material/UploadFile';
|
||||||
import SingleLineTextEdit from 'dashgui/SingleLineTextEdit';
|
import SingleLineTextEdit from 'dashgui/SingleLineTextEdit';
|
||||||
import { PanelTile } from 'dashgui/panels';
|
import { PanelTile } from 'dashgui/panels';
|
||||||
|
import FileDropzone from '../FileDropzone';
|
||||||
import { manifestConfig, partyDisplayName } from './manifestConfig';
|
import { manifestConfig, partyDisplayName } from './manifestConfig';
|
||||||
|
|
||||||
class ManifestHeader extends PureComponent {
|
class ManifestHeader extends PureComponent {
|
||||||
@@ -122,16 +123,42 @@ class ManifestHeader extends PureComponent {
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{onPdfUpload ? (
|
{onPdfUpload ? (
|
||||||
<Button
|
<FileDropzone
|
||||||
variant="outlined"
|
accept="application/pdf,.pdf"
|
||||||
size="small"
|
|
||||||
startIcon={pdfUploading ? <CircularProgress size={16} color="inherit" /> : <UploadFileIcon />}
|
|
||||||
disabled={saving || pdfUploading}
|
disabled={saving || pdfUploading}
|
||||||
onClick={this.handleUploadClick}
|
onFileDrop={onPdfUpload}
|
||||||
sx={{ whiteSpace: 'nowrap', height: 40, alignSelf: { xs: 'stretch', sm: 'center' }, px: 2 }}
|
sx={{
|
||||||
|
alignSelf: { xs: 'stretch', sm: 'center' },
|
||||||
|
flexShrink: 0,
|
||||||
|
width: { xs: '100%', sm: 175 },
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{pdfUploading ? 'Wird analysiert…' : 'PDF hochladen'}
|
{({ isDragging }) => (
|
||||||
</Button>
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
size="small"
|
||||||
|
startIcon={pdfUploading ? <CircularProgress size={16} color="inherit" /> : <UploadFileIcon />}
|
||||||
|
disabled={saving || pdfUploading}
|
||||||
|
onClick={this.handleUploadClick}
|
||||||
|
sx={{
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
height: 40,
|
||||||
|
width: '100%',
|
||||||
|
px: 2,
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
bgcolor: isDragging ? 'action.hover' : 'transparent',
|
||||||
|
borderColor: isDragging ? 'primary.main' : undefined,
|
||||||
|
transition: 'background-color 0.15s, border-color 0.15s',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{pdfUploading
|
||||||
|
? 'Wird analysiert…'
|
||||||
|
: isDragging
|
||||||
|
? 'PDF hier ablegen'
|
||||||
|
: 'PDF hochladen'}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</FileDropzone>
|
||||||
) : null}
|
) : null}
|
||||||
</Stack>
|
</Stack>
|
||||||
{onPdfUpload ? (
|
{onPdfUpload ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user