import { useFieldValue, useTranslate } from "ra-core";
import { createRef, useCallback, useState } from "react";
import type { ReactCropperElement } from "react-cropper";
import { Cropper } from "react-cropper";
import { useDropzone } from "react-dropzone";
import { useFormContext } from "react-hook-form";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ds/ui/avatar";
import { Button } from "@/components/ds/ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ds/ui/dialog";
import "cropperjs/dist/cropper.css";
const ImageEditorField = (props: ImageEditorFieldProps) => {
const { getValues } = useFormContext();
const source = getValues(props.source);
const imageUrl = source?.src;
const [isDialogOpen, setIsDialogOpen] = useState(false);
const translate = useTranslate();
const { type = "image", emptyText, linkPosition = "none" } = props;
const commonProps = {
src: imageUrl,
onClick: () => setIsDialogOpen(true),
style: { cursor: "pointer" },
className: `${props.className || ""}`,
};
const width = props.width || (type === "avatar" ? 50 : 200);
const height = props.height || (type === "avatar" ? 50 : 200);
return (
<>
{props.type === "avatar" ? (
{emptyText}
) : (
![{translate("crm.image_editor.alt")}]()
)}
{linkPosition !== "none" && (
)}
setIsDialogOpen(false)}
{...props}
/>
>
);
};
const ImageEditorDialog = (props: ImageEditorDialogProps) => {
const { setValue, handleSubmit } = useFormContext();
const cropperRef = createRef();
const initialValue = useFieldValue({ source: props.source });
const [file, setFile] = useState();
const [imageSrc, setImageSrc] = useState(
initialValue?.src,
);
const translate = useTranslate();
const onDrop = useCallback((files: File[]) => {
const preview = URL.createObjectURL(files[0]);
setFile(files[0]);
setImageSrc(preview);
}, []);
const updateImage = () => {
const cropper = cropperRef.current?.cropper;
const croppedImage = cropper?.getCroppedCanvas().toDataURL();
if (croppedImage) {
setImageSrc(croppedImage);
const newFile = file ?? new File([], initialValue?.src);
setValue(
props.source,
{
src: croppedImage,
title: newFile.name,
rawFile: newFile,
},
{ shouldDirty: true },
);
props.onClose();
if (props.onSave) {
handleSubmit(props.onSave)();
}
}
};
const deleteImage = () => {
setValue(props.source, null, { shouldDirty: true });
if (props.onSave) {
handleSubmit(props.onSave)();
}
setImageSrc(undefined);
props.onClose();
};
const { getRootProps, getInputProps } = useDropzone({
accept: { "image/jpeg": [".jpeg", ".png"] },
onDrop,
maxFiles: 1,
});
return (
);
};
export default ImageEditorField;
export interface ImageEditorFieldProps {
source: string;
width?: number;
height?: number;
type?: "avatar" | "image";
onSave?: any;
linkPosition?: "right" | "bottom" | "none";
backgroundImageColor?: string;
className?: string;
emptyText?: string;
}
export interface ImageEditorDialogProps extends ImageEditorFieldProps {
open: boolean;
onClose: () => void;
}