MoneyMgr/moneymgr_web/src/widgets/forms/UploadFileButton.tsx
2025-04-28 21:52:11 +02:00

100 lines
2.8 KiB
TypeScript

import UploadFileIcon from "@mui/icons-material/UploadFile";
import { Button, Tooltip, Typography } from "@mui/material";
import React from "react";
import { FileApi, UploadedFile } from "../../api/FileApi";
import { ServerApi } from "../../api/ServerApi";
import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
import { useLoadingMessage } from "../../hooks/context_providers/LoadingMessageProvider";
import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider";
// https://medium.com/@dprincecoder/creating-a-drag-and-drop-file-upload-component-in-react-a-step-by-step-guide-4d93b6cc21e0
export function UploadFileButton(p: {
onUploaded: (file: UploadedFile) => void;
label: string;
tooltip: string;
}): React.ReactElement {
const alert = useAlert();
const loadingMessage = useLoadingMessage();
const snackbar = useSnackbar();
const fileInput = React.useRef<HTMLInputElement>(null);
const [dragActive, setDragActive] = React.useState(false);
const forceFileSelect = () => fileInput.current?.click();
const dragEnter = () => {
setDragActive(true);
};
const dragLeave = () => {
setDragActive(false);
};
const handleUpload = async (file: File[]) => {
if (file.length < 1) return;
try {
loadingMessage.show("Uploading file...");
const result = await FileApi.UploadFile(file[0]);
snackbar("The file was successfully uploaded!");
p.onUploaded(result);
} catch (e) {
console.error("Failed to upload file!", e);
alert(`Failed to upload file! ${e}`);
} finally {
loadingMessage.hide();
}
};
const handleDrop = (ev: React.DragEvent) => {
ev.preventDefault();
handleUpload([...ev.dataTransfer.files]);
};
const handlefileChange = (ev: React.ChangeEvent) => {
ev.preventDefault();
if ((fileInput.current?.files?.length ?? 0) > 0) {
handleUpload([...fileInput.current!.files!]);
}
};
return (
<Tooltip
title={p.tooltip}
onDrop={handleDrop}
onDragOver={(event) => {
event.preventDefault();
}}
onDragEnter={dragEnter}
onDragLeave={dragLeave}
>
<Button
size="small"
startIcon={<UploadFileIcon fontSize="small" />}
variant={dragActive ? "outlined" : "text"}
onClick={forceFileSelect}
>
<input
ref={fileInput}
type="file"
accept={ServerApi.Config.constraints.file_allowed_types.join(",")}
style={{
border: "0",
height: "1px",
width: "1px",
padding: "0px",
position: "absolute",
clipPath: "inset(50%)",
}}
onChange={handlefileChange}
/>
<Typography variant="caption">{p.label}</Typography>
</Button>
</Tooltip>
);
}