feat: image preview

This commit is contained in:
redglow
2026-06-21 15:21:21 +02:00
parent f3dff4871f
commit 23dfba812a
2 changed files with 116 additions and 61 deletions
@@ -0,0 +1,114 @@
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import {
Button,
FormLabel,
TextareaAutosize,
TextField,
Typography,
} from "@mui/material";
import { Box, styled } from "@mui/system";
import { DatePicker } from "@mui/x-date-pickers";
import { type ChangeEvent, useState } from "react";
import type { Announcement } from "../services";
const VisuallyHiddenInput = styled("input")({
clip: "rect(0 0 0 0)",
clipPath: "inset(50%)",
height: 1,
overflow: "hidden",
position: "absolute",
bottom: 0,
left: 0,
whiteSpace: "nowrap",
width: 1,
});
export interface AddOrEditAnnouncementProps {
announcement?: Announcement;
}
function blobToDataURL(blob: Blob): Promise<string> {
return new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.onload = (_e) => resolve(reader.result as string);
reader.onerror = (_e) => reject(reader.error);
reader.onabort = (_e) => reject(new Error("Read aborted"));
reader.readAsDataURL(blob);
});
}
export function AddOrEditAnnouncement({
announcement,
}: AddOrEditAnnouncementProps) {
const [currentImageUrl, setCurrentImageUrl] = useState<string | undefined>(
undefined,
);
return (
<Box
component="form"
encType="multipart/form-data"
action="/api/announcements"
method="POST"
sx={{
display: "flex",
flexDirection: "column",
"& > :not(style)": { m: 1, width: "25ch" },
}}
noValidate
autoComplete="off"
>
<TextField
name="title"
label="Title"
defaultValue={announcement?.title}
></TextField>
<FormLabel>Text:</FormLabel>
<TextareaAutosize
name="text"
minRows={6}
defaultValue={announcement?.text}
/>
<FormLabel>Publication Date:</FormLabel>
<DatePicker
name="publication_date"
defaultValue={
announcement ? new Date(announcement.publication_date) : undefined
}
/>
<Button
component="label"
variant="contained"
tabIndex={-1}
startIcon={<CloudUploadIcon />}
>
Upload files
<VisuallyHiddenInput
type="file"
name="image"
onChange={(event: ChangeEvent<HTMLInputElement>) => {
console.log(event.target.files);
const file =
event.target.files && event.target.files.length > 0
? event.target.files[0]
: undefined;
if (file === undefined) {
setCurrentImageUrl(undefined);
return;
}
blobToDataURL(file).then(setCurrentImageUrl).catch(console.error);
}}
/>
</Button>
<Typography>Current image:</Typography>
{currentImageUrl ? (
<img src={currentImageUrl} alt="Current" />
) : (
<Typography>None</Typography>
)}
<Button variant="contained" type="submit">
Submit
</Button>
</Box>
);
}
+2 -61
View File
@@ -1,69 +1,10 @@
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import {
Box,
Button,
FormLabel,
styled,
TextareaAutosize,
TextField,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import type { ChangeEvent } from "react"; import { AddOrEditAnnouncement } from "../../components/AddOrEditAnnouncement";
const VisuallyHiddenInput = styled("input")({
clip: "rect(0 0 0 0)",
clipPath: "inset(50%)",
height: 1,
overflow: "hidden",
position: "absolute",
bottom: 0,
left: 0,
whiteSpace: "nowrap",
width: 1,
});
export const Route = createFileRoute("/add/")({ export const Route = createFileRoute("/add/")({
component: RouteComponent, component: RouteComponent,
}); });
function RouteComponent() { function RouteComponent() {
return ( return <AddOrEditAnnouncement />;
<Box
component="form"
encType="multipart/form-data"
action="/api/announcements"
method="POST"
sx={{
display: "flex",
flexDirection: "column",
"& > :not(style)": { m: 1, width: "25ch" },
}}
noValidate
autoComplete="off"
>
<TextField name="title" label="Title"></TextField>
<FormLabel>Text:</FormLabel>
<TextareaAutosize name="text" minRows={6}></TextareaAutosize>
<DatePicker name="publication_date" />
<Button
component="label"
variant="contained"
tabIndex={-1}
startIcon={<CloudUploadIcon />}
>
Upload files
<VisuallyHiddenInput
type="file"
name="image"
onChange={(event: ChangeEvent<HTMLInputElement>) =>
console.log(event.target.files)
}
/>
</Button>
<Button variant="contained" type="submit">
Submit
</Button>
</Box>
);
} }