409 lines
12 KiB
JavaScript
409 lines
12 KiB
JavaScript
import { useContext, useEffect, useState } from "react";
|
|
import CanvasContext from "./Context/canvasContext/CanvasContext";
|
|
import { Card, CardTitle } from "./ui/card";
|
|
import { Button } from "./ui/button";
|
|
import { Trash2, UploadIcon } from "lucide-react";
|
|
import { Separator } from "./ui/separator";
|
|
import ColorComponent from "./ColorComponent";
|
|
import { Label } from "./ui/label";
|
|
import { Input } from "./ui/input";
|
|
import { Slider } from "./ui/slider";
|
|
import { fabric } from "fabric";
|
|
import { ScrollArea } from "./ui/scroll-area";
|
|
import { useNavigate, useParams } from "react-router-dom";
|
|
import { useToast } from "../hooks/use-toast";
|
|
import { useMutation } from "@tanstack/react-query";
|
|
import { deleteImage, uploadImage } from "../api/uploadApi";
|
|
import { createProject } from "../api/projectApi";
|
|
import SaveCanvas from "./SaveCanvas";
|
|
|
|
const CanvasSetting = () => {
|
|
const { canvas } = useContext(CanvasContext);
|
|
const params = useParams();
|
|
const { id } = params;
|
|
const { toast } = useToast();
|
|
const navigate = useNavigate();
|
|
const [bgImage, setBgImage] = useState(null);
|
|
const [bgOverLayImage, setBgOverLayImage] = useState(null);
|
|
|
|
// create empty project if no id is provided
|
|
useEffect(() => {
|
|
const createEmptyProject = async () => {
|
|
try {
|
|
const response = await createProject();
|
|
if (response?.status === 200) {
|
|
toast({
|
|
title: response?.status,
|
|
description: response?.message
|
|
})
|
|
}
|
|
if (response?.data?.id) {
|
|
navigate(`/${response.data.id}`);
|
|
}
|
|
} catch (error) {
|
|
console.error("Project creation failed:", error);
|
|
}
|
|
};
|
|
if (!id) {
|
|
createEmptyProject();
|
|
}
|
|
}, [id, navigate, toast]);
|
|
|
|
// upload bg-image handler
|
|
const { mutate: uploadBackgroundImage } = useMutation({
|
|
mutationFn: async ({ file, id }) => {
|
|
return await uploadImage({ file, id });
|
|
},
|
|
onSuccess: (data) => {
|
|
if (data?.status === 200) {
|
|
toast({
|
|
title: data?.status,
|
|
description: data?.message
|
|
});
|
|
setBgImage(data?.data[0]?.url);
|
|
|
|
// Create an image element
|
|
const imgElement = new Image();
|
|
|
|
// Set the crossOrigin attribute BEFORE setting the src
|
|
imgElement.crossOrigin = "anonymous"; // This ensures CORS headers are sent
|
|
imgElement.src = data?.data[0]?.url;
|
|
|
|
imgElement.onload = () => {
|
|
// Create a fabric.Image instance
|
|
const img = new fabric.Image(imgElement, {
|
|
scaleX: canvas.width / imgElement.width,
|
|
scaleY: canvas.height / imgElement.height,
|
|
});
|
|
|
|
// Set the background image on the canvas
|
|
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
|
|
};
|
|
|
|
imgElement.onerror = (error) => {
|
|
console.error('Failed to load image:', error);
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Image Load Error",
|
|
description: "Failed to load the image. Please try again."
|
|
})
|
|
};
|
|
}
|
|
else {
|
|
toast({
|
|
variant: "destructive",
|
|
title: data?.status,
|
|
description: data?.message
|
|
});
|
|
}
|
|
}
|
|
})
|
|
|
|
// handle bg-image remove
|
|
const { mutate: removeBackgroundMutate } = useMutation({
|
|
mutationFn: async (url) => {
|
|
return await deleteImage(url);
|
|
},
|
|
onSuccess: (data) => {
|
|
if (data?.status === 200) {
|
|
toast({
|
|
title: data?.status,
|
|
description: data?.message
|
|
})
|
|
canvas.backgroundImage = null;
|
|
canvas.renderAll();
|
|
setBgImage(null);
|
|
}
|
|
else {
|
|
toast({
|
|
variant: "destructive",
|
|
title: data?.status,
|
|
description: data?.message
|
|
})
|
|
}
|
|
}
|
|
});
|
|
|
|
// upload bg-overLayImage handler
|
|
const { mutate: uploadOverlayImage } = useMutation({
|
|
mutationFn: async ({ file, id }) => {
|
|
return await uploadImage({ file, id });
|
|
},
|
|
onSuccess: (data) => {
|
|
if (data?.status === 200) {
|
|
toast({
|
|
title: data?.status,
|
|
description: data?.message
|
|
});
|
|
setBgOverLayImage(data?.data[0]?.url);
|
|
// Create an image element
|
|
const imgElement = new Image();
|
|
|
|
// Set the crossOrigin attribute BEFORE setting the src
|
|
imgElement.crossOrigin = "anonymous"; // This ensures CORS headers are sent
|
|
imgElement.src = data?.data[0]?.url;
|
|
|
|
imgElement.onload = () => {
|
|
// Create a fabric.Image instance
|
|
const img = new fabric.Image(imgElement, {
|
|
scaleX: canvas.width / imgElement.width,
|
|
scaleY: canvas.height / imgElement.height,
|
|
});
|
|
|
|
// Set the background image on the canvas
|
|
canvas.setOverlayImage(img, canvas.renderAll.bind(canvas));
|
|
};
|
|
|
|
imgElement.onerror = (error) => {
|
|
console.error('Failed to load image:', error);
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Image Load Error",
|
|
description: "Failed to load the image. Please try again."
|
|
})
|
|
};
|
|
|
|
}
|
|
else {
|
|
toast({
|
|
variant: "destructive",
|
|
title: data?.status,
|
|
description: data?.message
|
|
});
|
|
}
|
|
}
|
|
})
|
|
|
|
// handle bg-overLayImage remove
|
|
const { mutate: removeOverLayMutate } = useMutation({
|
|
mutationFn: async (url) => {
|
|
return await deleteImage(url);
|
|
},
|
|
onSuccess: (data) => {
|
|
if (data?.status === 200) {
|
|
toast({
|
|
title: data?.status,
|
|
description: data?.message
|
|
})
|
|
canvas.overlayImage = null;
|
|
canvas.renderAll();
|
|
setBgOverLayImage(null);
|
|
}
|
|
else {
|
|
toast({
|
|
variant: "destructive",
|
|
title: data?.status,
|
|
description: data?.message
|
|
})
|
|
}
|
|
}
|
|
});
|
|
|
|
const setBackgroundImage = (e) => {
|
|
const file = e.target.files[0];
|
|
if (file) {
|
|
uploadBackgroundImage({ file, id })
|
|
}
|
|
else {
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Error",
|
|
description: "Please select a file",
|
|
})
|
|
}
|
|
};
|
|
|
|
const setBackgroundOverlayImage = (e) => {
|
|
const file = e.target.files[0];
|
|
if (file) {
|
|
uploadOverlayImage({ file, id })
|
|
}
|
|
else {
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Error",
|
|
description: "Please select a file",
|
|
})
|
|
}
|
|
};
|
|
|
|
const adjustBackgroundOpacity = (value) => {
|
|
if (canvas) {
|
|
if (canvas.backgroundImage) {
|
|
// Update the opacity of the background image if it exists
|
|
canvas.backgroundImage.set("opacity", value);
|
|
} else if (canvas.overlayImage) {
|
|
// Update the opacity of the overlay image if it exists
|
|
canvas.overlayImage.set("opacity", value);
|
|
} else {
|
|
console.error("No background or overlay image found on the canvas.");
|
|
return;
|
|
}
|
|
|
|
// Re-render the canvas to apply changes
|
|
canvas.renderAll();
|
|
} else {
|
|
console.error("Canvas is not initialized.");
|
|
}
|
|
};
|
|
|
|
const removeBackgroundImage = () => {
|
|
if (canvas) {
|
|
const bgUrl = canvas.backgroundImage?.getSrc();
|
|
console.log("background image from remove", bgUrl)
|
|
if (bgUrl) {
|
|
removeBackgroundMutate(bgUrl)
|
|
}
|
|
else {
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Error",
|
|
description: "No background image found",
|
|
})
|
|
}
|
|
}
|
|
};
|
|
|
|
const removeBackgroundOverlayImage = () => {
|
|
if (canvas) {
|
|
const overLayUrl = canvas.overlayImage?.getSrc();
|
|
console.log("overlay image from remove", overLayUrl);
|
|
if (overLayUrl) {
|
|
removeOverLayMutate(overLayUrl);
|
|
}
|
|
else {
|
|
toast({
|
|
variant: "destructive",
|
|
title: "Error",
|
|
description: "No overlay image found",
|
|
})
|
|
}
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (canvas) {
|
|
const bgUrl = canvas.backgroundImage?.getSrc();
|
|
setBgImage(bgUrl);
|
|
const overLayUrl = canvas.overlayImage?.getSrc();
|
|
setBgOverLayImage(overLayUrl);
|
|
}
|
|
}, [canvas, setBgImage, setBgOverLayImage])
|
|
|
|
return (
|
|
<div>
|
|
<Card className="xl:p-0 lg:p-0 md:p-0 p-2 border-none shadow-none">
|
|
<CardTitle className="flex items-center flex-wrap justify-between gap-1 xl:hidden lg:hidden md:hidden">
|
|
Canvas Setting
|
|
</CardTitle>
|
|
<Separator className="mt-4 block xl:hidden lg:hidden md:hidden" />
|
|
<ScrollArea className="h-[400px] xl:h-fit lg:h-fit md:h-fit">
|
|
<div className="rnd-escape">
|
|
<ColorComponent />
|
|
|
|
<Separator className="mt-2" />
|
|
|
|
<div className="flex flex-col my-2 gap-2 rnd-escape">
|
|
<div>
|
|
<Label>Background:</Label>
|
|
{
|
|
bgImage && <img src={bgImage} alt="canvas_bg_image" className="rounded-md mb-2" />
|
|
}
|
|
<div className="flex items-center w-fit gap-2 flex-wrap relative">
|
|
{
|
|
!bgImage &&
|
|
<Button className="top-0 absolute flex items-center w-[30px]">
|
|
<UploadIcon className="cursor-pointer" />
|
|
<Input
|
|
className="absolute top-0 opacity-0"
|
|
type="file"
|
|
accept="image/*"
|
|
onChange={setBackgroundImage}
|
|
/>
|
|
</Button>
|
|
}
|
|
|
|
{
|
|
bgImage &&
|
|
<Button
|
|
variant="secondary"
|
|
onClick={removeBackgroundImage}
|
|
>
|
|
<Trash2 />
|
|
</Button>
|
|
}
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{
|
|
bgImage ? <Separator className="mt-4" /> : <Separator className="mt-12" />
|
|
}
|
|
|
|
<div>
|
|
<Label>Background Overlay:</Label>
|
|
{
|
|
bgOverLayImage && <img src={bgOverLayImage} alt="canvas_bgOverLay_image" className="rounded-md mb-2" />
|
|
}
|
|
<div className="flex items-center w-fit gap-2 flex-wrap relative">
|
|
{
|
|
!bgOverLayImage &&
|
|
<Button className="top-0 absolute flex items-center w-[30px] mb">
|
|
<UploadIcon className="cursor-pointer" />
|
|
<Input
|
|
className="absolute top-0 opacity-0"
|
|
type="file"
|
|
accept="image/*"
|
|
onChange={setBackgroundOverlayImage}
|
|
/>
|
|
</Button>
|
|
}
|
|
|
|
{
|
|
bgOverLayImage &&
|
|
<Button
|
|
variant="secondary"
|
|
onClick={removeBackgroundOverlayImage}
|
|
>
|
|
<Trash2 />
|
|
</Button>
|
|
}
|
|
</div>
|
|
|
|
</div>
|
|
{
|
|
bgOverLayImage ? <Separator className="mt-4" /> : <Separator className="mt-12" />
|
|
}
|
|
|
|
{/* opacity */}
|
|
<div className="flex flex-col gap-2 rnd-escape mt-2">
|
|
<Label>Background Opacity:</Label>
|
|
<Slider
|
|
defaultValue={[1.0]} // Default value, you can set it to 0.0 or another value
|
|
min={0.0}
|
|
max={1.0}
|
|
step={0.01} // Step size for fine control
|
|
onValueChange={(value) => {
|
|
const newOpacity = value[0]; // Extract slider value
|
|
adjustBackgroundOpacity(newOpacity); // Adjust Fabric.js background opacity
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator className="mt-4" />
|
|
|
|
{/* Save canvas Component */}
|
|
{
|
|
id &&
|
|
<SaveCanvas />
|
|
}
|
|
|
|
</div>
|
|
</ScrollArea>
|
|
</Card>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default CanvasSetting;
|