react-advanced-cropper icon indicating copy to clipboard operation
react-advanced-cropper copied to clipboard

How to handle multiple images

Open gaganbiswas opened this issue 6 months ago • 0 comments

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;

gaganbiswas avatar Aug 07 '24 16:08 gaganbiswas