"use client"; import { Upload, X } from "lucide-react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Button } from "@/components/ui/button"; import { resizeImageToDataURL } from "@/lib/image/resize"; import { cn } from "@/lib/utils"; export interface ProcessedImage { id: string; originalName: string; previewUrl: string; dataUrl: string; mimeType: string; width: number; height: number; } interface ItemImageCaptureProps { maxImages?: number; className?: string; onImagesChange?: (images: ProcessedImage[]) => void; } export function ItemImageCapture({ maxImages = 2, className, onImagesChange, }: ItemImageCaptureProps) { const generateId = useCallback(() => { if (typeof crypto !== "undefined" && "randomUUID" in crypto) { return crypto.randomUUID(); } return `img-${Date.now()}-${Math.random().toString(16).slice(2)}`; }, []); const [images, setImages] = useState([]); const inputRef = useRef(null); const previewUrlsRef = useRef>(new Set()); const updateImages = useCallback( (next: ProcessedImage[]) => { setImages(next); onImagesChange?.(next); }, [onImagesChange], ); const handleFiles = useCallback( async (files: FileList | File[]) => { const fileArray = Array.from(files).slice(0, maxImages - images.length); if (fileArray.length === 0) return; const processed: ProcessedImage[] = []; for (const file of fileArray) { if (!file.type.startsWith("image/")) continue; try { const { dataUrl, width, height } = await resizeImageToDataURL(file, { mimeType: file.type === "image/png" ? "image/png" : "image/jpeg", }); const previewUrl = URL.createObjectURL(file); previewUrlsRef.current.add(previewUrl); processed.push({ id: generateId(), originalName: file.name, previewUrl, dataUrl, mimeType: file.type === "image/png" ? "image/png" : "image/jpeg", width, height, }); } catch (error) { console.error("Falha ao processar imagem", error); } } if (processed.length > 0) { updateImages([...images, ...processed].slice(0, maxImages)); } }, [generateId, images, maxImages, updateImages], ); const handleDrop = useCallback( async (event: React.DragEvent) => { event.preventDefault(); if (event.dataTransfer.files?.length) { await handleFiles(event.dataTransfer.files); } }, [handleFiles], ); const onPaste = useCallback( async (event: React.ClipboardEvent) => { const items = event.clipboardData?.items; if (!items) return; const files: File[] = []; for (const item of items) { if (item.type.startsWith("image/")) { const file = item.getAsFile(); if (file) files.push(file); } } if (files.length) { await handleFiles(files); } }, [handleFiles], ); const removeImage = useCallback( (id: string) => { const nextImage = images.find((image) => image.id === id); if (nextImage) { URL.revokeObjectURL(nextImage.previewUrl); previewUrlsRef.current.delete(nextImage.previewUrl); } const next = images.filter((image) => image.id !== id); updateImages(next); }, [images, updateImages], ); const canAddMore = images.length < maxImages; const dropLabel = useMemo(() => { if (images.length === 0) { return "Arraste, cole (Ctrl+V) ou escolha imagens"; } if (canAddMore) { return "Adicione mais imagens ou cole via Ctrl+V"; } return "Limite máximo de imagens atingido"; }, [images.length, canAddMore]); useEffect( () => () => { previewUrlsRef.current.forEach((url) => URL.revokeObjectURL(url)); previewUrlsRef.current.clear(); }, [], ); return (
{ event.preventDefault(); event.dataTransfer.dropEffect = "copy"; }} onDrop={handleDrop} onPaste={onPaste} role="presentation" >

{dropLabel}

{ if (event.target.files?.length) { await handleFiles(event.target.files); event.target.value = ""; } }} />
{images.length > 0 && (
{images.map((image) => (
Pronta {image.originalName}

{image.originalName}

{image.width}×{image.height}px ·{" "} {Math.round((image.dataUrl.length * 3) / 4 / 1024)}KB

))}
)}
); }