react-advanced-cropper
react-advanced-cropper copied to clipboard
How to handle multiple images
Hello, so for my use case I want to handle multiple images. Here's a code snippet on what i am trying to do exactly. Also I followed the Twitter Carousel for this. Trying to implement something similar to instagram post.
"use client";
import React, {
useRef,
useState,
useEffect,
forwardRef,
useImperativeHandle,
} from "react";
import { FixedCropperRef } from "react-advanced-cropper";
import "react-advanced-cropper/dist/style.css";
import Image from "next/image";
import { Cropper } from "../common/cropper";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "../ui/button";
import {
Images,
RectangleHorizontal,
RectangleVerticalIcon,
Scaling,
Square,
} from "lucide-react";
import { dataURLToBlob } from "@/lib/helpers";
const MediaImageCropper = forwardRef(
(
{
media,
}: {
media: File[];
},
ref
) => {
const cropperRef = useRef<FixedCropperRef>(null);
const [currentImageIndex, setCurrentImageIndex] = useState(0);
const [stencilRatio, setStencilRatio] = useState(1);
useImperativeHandle(ref, () => ({
handleSaveAll,
}));
const handleSave = (item: File, cropper: FixedCropperRef | null) => {
if (cropper) {
cropper.setImage(URL.createObjectURL(item));
const canvas = cropper.getCanvas();
if (canvas) {
const dataURL = canvas.toDataURL("image/png");
const blob = dataURLToBlob(dataURL);
return { data: blob, src: dataURL };
}
}
return null;
};
const handleSaveAll = () => {
const allCroppedImages: any[] = [];
media.forEach((item) => {
const cropper = cropperRef.current;
const cropped = handleSave(item, cropper);
if (cropped) {
allCroppedImages.push(cropped);
}
});
return allCroppedImages;
};
useEffect(() => {
if (cropperRef.current) {
cropperRef.current.setImage(
URL.createObjectURL(media[currentImageIndex])
);
}
}, [currentImageIndex, media]);
return (
<div className="flex flex-col">
<Cropper
key={currentImageIndex}
cropperRef={cropperRef}
src={URL.createObjectURL(media[currentImageIndex])}
stencilRatio={stencilRatio}
/>
<div className="w-full flex items-center justify-between p-4">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size={"icon"} variant={"secondary"}>
<Scaling className="h-5 w-5" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" side="top">
<DropdownMenuLabel>Resize</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem
className="tracking-widest text-muted-foreground font-semibold"
onClick={() => setStencilRatio(1)}
>
1:1 <Square className="w-5 h-5 ml-2" />
</DropdownMenuItem>
<DropdownMenuItem
className="tracking-widest text-muted-foreground font-semibold"
onClick={() => setStencilRatio(4 / 5)}
>
4:5 <RectangleVerticalIcon className="w-5 h-5 ml-2" />
</DropdownMenuItem>
<DropdownMenuItem
className="tracking-widest text-muted-foreground font-semibold"
onClick={() => setStencilRatio(16 / 9)}
>
16:9 <RectangleHorizontal className="w-5 h-5 ml-2" />
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size={"icon"} variant={"secondary"}>
<Images className="h-5 w-5" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
side="top"
className="flex items-center gap-4 p-2"
>
{media.map((file, index) => (
<DropdownMenuItem
key={index}
className={`cursor-pointer ring-2 rounded-md p-0 ${
index === currentImageIndex ? "ring-blue-500" : "ring-muted"
}`}
onClick={() => setCurrentImageIndex(index)}
>
<Image
src={URL.createObjectURL(file)}
alt={`Thumbnail ${index}`}
width={32}
height={32}
className="w-16 h-16 object-cover rounded-md"
/>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
);
}
);
export default MediaImageCropper;