import ActiveObjectContext from "@/components/Context/activeObject/ObjectContext"; import CanvasContext from "@/components/Context/canvasContext/CanvasContext"; import { useContext, useRef, useState } from "react"; import { Button } from "@/components/ui/button"; import { fabric } from "fabric"; import Resizer from "react-image-file-resizer"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Slider } from "@/components/ui/slider"; import { HardDriveUpload, ImagePlus, Upload, Crop, RotateCw, FileType, ChevronUp, ChevronDown, } from "lucide-react"; import { Card, CardContent } from "@/components/ui/card"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import CollapsibleComponent from "./CollapsibleComponent"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import { useToast } from "@/hooks/use-toast"; import { useMutation } from "@tanstack/react-query"; import useProject from "@/hooks/useProject"; import { uploadImage } from "@/api/uploadApi"; const features = [ { icon: Upload, title: "Auto-Resize", description: "Images larger than 1080px are automatically resized", }, { icon: Crop, title: "Custom Dimensions", description: "Set your preferred resize dimensions", }, { icon: RotateCw, title: "Quality & Rotation", description: "Adjust image quality and rotation as needed", }, { icon: FileType, title: "Format Conversion", description: "Convert between various image formats", }, ]; const AddImageIntoShape = () => { const { canvas } = useContext(CanvasContext); const { activeObject, setActiveObject } = useContext(ActiveObjectContext); const [errorMessage, setErrorMessage] = useState(""); const [width, setWidth] = useState(1080); const [height, setHeight] = useState(1080); const [quality, setQuality] = useState(100); const [rotation, setRotation] = useState("0"); const [format, setFormat] = useState("JPEG"); const fileInputRef = useRef(null); const [isOpen, setIsOpen] = useState(false); const { toast } = useToast(); const { id, projectData, projectUpdate } = useProject(); // Upload image handler const { mutate: uploadMutate } = useMutation({ mutationFn: async ({ file, id }) => await uploadImage({ file, id }), onSuccess: (data) => { if (data?.status === 200) { toast({ title: data?.status, description: data?.message }); handleImageInsert(data?.data[0]?.url); } else { toast({ variant: "destructive", title: data?.status, description: data?.message }); } }, }); const handleResize = (file, callback) => { Resizer.imageFileResizer( file, width, height, format, quality, parseInt(rotation), (resizedFile) => { callback(resizedFile); // Pass the resized file to the callback }, "file" // Output type ); }; const fileHandler = (e) => { if (!activeObject) { toast({ variant: "destructive", title: "No active object selected!" }); return; } const file = e.target.files[0]; if (!file && activeObject) { setErrorMessage("No file selected."); return; } // Check if the file is an SVG (skip compression) if (file.type === "image/svg+xml") { toast({ variant: "destructive", title: "SVG files are not supported!" }); clearFileInput(); return; } // Handle raster images (JPEG, PNG, etc.) const imgElement = new Image(); const blobUrl = URL.createObjectURL(file); imgElement.src = blobUrl; imgElement.onload = () => { if (imgElement.width > 1080) { handleResize(file, (compressedFile) => { uploadMutate({ file: compressedFile, id }); // Fixed key name clearFileInput(); }); } else { uploadMutate({ file, id }); // Direct upload if width is small clearFileInput(); } URL.revokeObjectURL(blobUrl); // Clean up }; imgElement.onerror = () => { console.error("Failed to load image."); setErrorMessage("Failed to load image."); URL.revokeObjectURL(blobUrl); clearFileInput(); }; }; const clearFileInput = () => { if (fileInputRef.current) { fileInputRef.current.value = ""; } }; // const handleImageInsert = (img) => { // if (!activeObject) { // setErrorMessage("No active object selected!"); // return; // } // // Ensure absolute positioning for the clipPath // activeObject.set({ // isClipPath: true, // Custom property // absolutePositioned: true, // }); // // Calculate scale factors based on clip object size // let scaleX = activeObject.width / img.width; // let scaleY = activeObject.height / img.height; // if (activeObject?.width < 100) { // scaleX = 0.2; // } // if (activeObject.height < 100) { // scaleY = 0.2; // } // // Create a fabric image object with scaling and clipPath // const fabricImage = new fabric.Image.fromURL(img, { // scaleX: scaleX, // scaleY: scaleY, // left: activeObject.left, // top: activeObject.top, // clipPath: activeObject, // Apply clipPath to the image // originX: activeObject.originX, // Match origin point // originY: activeObject.originY, // Match origin point // }, { crossOrigin: "anonymous" }); // // Adjust position based on the clipPath's transformations // fabricImage.set({ // left: activeObject.left, // top: activeObject.top, // angle: activeObject.angle, // Match rotation if any // }); // canvas.add(fabricImage); // canvas.setActiveObject(fabricImage); // setActiveObject(fabricImage); // canvas.renderAll(); // // Update the active object state // projectUpdate({ id, updateData: { ...projectData?.data, object: canvas.toJSON(['id', 'selectable']), preview_url: "" } }); // }; const handleImageInsert = (imgUrl) => { console.log(imgUrl); // Ensure absolute positioning for the clipPath activeObject.set({ isClipPath: true, // Custom property absolutePositioned: true, }); // Load the image asynchronously fabric.Image.fromURL( imgUrl, (img) => { // Ensure the image is fully loaded before applying transformations let scaleX = activeObject.width / img.width; let scaleY = activeObject.height / img.height; // Prevent the image from being too small if (activeObject.width < 100) scaleX = 0.2; if (activeObject.height < 100) scaleY = 0.2; // Set image properties img.set({ scaleX: scaleX, scaleY: scaleY, left: activeObject.left, top: activeObject.top, clipPath: activeObject, // Apply clipPath to the image originX: activeObject.originX, // Match origin point originY: activeObject.originY, // Match origin point crossOrigin: "anonymous", // Ensure CORS handling }); // Add image to canvas canvas.add(img); canvas.setActiveObject(img); setActiveObject(img); canvas.renderAll(); // Update project data projectUpdate({ id, updateData: { ...projectData?.data, object: canvas.toJSON(["id", "selectable"]), preview_url: "", }, }); }, { crossOrigin: "anonymous" } ); }; const content = () => { return (
Insert and customize images within shapes. Adjust image position and clipping after insertion.
{feature.description}
{errorMessage}
)}