new-personal-site/components/lib/form/ImageUpload.tsx
2024-12-09 16:36:17 +01:00

110 lines
3.8 KiB
TypeScript

import Button from "@/components/lib/layout/Button";
import Stack from "@/components/lib/layout/Stack";
import { ImagePlus, X } from "lucide-react";
import React, { DetailedHTMLProps } from "react";
import Card from "@/components/lib/elements/Card";
import Span from "@/components/lib/layout/Span";
import Center from "@/components/lib/layout/Center";
import imageInputToBase64, {
ImageInputToBase64FunctionReturn,
} from "../utils/form/imageInputToBase64";
import { twMerge } from "tailwind-merge";
type ImageUploadProps = {
onChange?: (imgData: ImageInputToBase64FunctionReturn | undefined) => any;
fileInputProps?: DetailedHTMLProps<
React.InputHTMLAttributes<HTMLInputElement>,
HTMLInputElement
>;
wrapperProps?: DetailedHTMLProps<
React.HTMLAttributes<HTMLDivElement>,
HTMLDivElement
>;
placeHolderWrapper?: DetailedHTMLProps<
React.HTMLAttributes<HTMLDivElement>,
HTMLDivElement
>;
previewImageWrapperProps?: DetailedHTMLProps<
React.HTMLAttributes<HTMLDivElement>,
HTMLDivElement
>;
previewImageProps?: DetailedHTMLProps<
React.ImgHTMLAttributes<HTMLImageElement>,
HTMLImageElement
>;
};
export default function ImageUpload({
onChange,
fileInputProps,
wrapperProps,
placeHolderWrapper,
previewImageWrapperProps,
previewImageProps,
}: ImageUploadProps) {
const [src, setSrc] = React.useState<string | undefined>(undefined);
const inputRef = React.useRef<HTMLInputElement>();
return (
<Stack
className={twMerge("w-full", wrapperProps?.className)}
{...wrapperProps}
>
<input
type="file"
className={twMerge("hidden", fileInputProps?.className)}
{...fileInputProps}
onChange={(e) => {
imageInputToBase64({ imageInput: e.target }).then((res) => {
setSrc(res.imageBase64Full);
onChange?.(res);
fileInputProps?.onChange?.(e);
});
}}
ref={inputRef as any}
/>
{src ? (
<Card className="w-full relative" {...previewImageWrapperProps}>
<img
src={src}
className="w-full h-[300px] object-contain"
{...previewImageProps}
/>
<Button
variant="ghost"
className="absolute p-2 top-2 right-2 z-20"
onClick={(e) => {
setSrc(undefined);
onChange?.(undefined);
}}
>
<X className="text-slate-950" />
</Button>
</Card>
) : (
<Card
className={twMerge(
"w-full h-[300px] cursor-pointer hover:bg-slate-100 dark:hover:bg-white/20",
placeHolderWrapper?.className
)}
onClick={(e) => {
inputRef.current?.click();
placeHolderWrapper?.onClick?.(e);
}}
{...placeHolderWrapper}
>
<Center>
<Stack className="items-center gap-2">
<ImagePlus className="text-slate-400" />
<Span size="smaller" variant="faded">
Click to Upload Image
</Span>
</Stack>
</Center>
</Card>
)}
</Stack>
);
}