import { useState, useCallback } from 'react'; import Cropper from 'react-easy-crop'; import type { Area, Point } from 'react-easy-crop'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Slider } from '@/components/ui/slider'; import { ZoomIn, ZoomOut, RotateCcw } from 'lucide-react'; interface ImageCropDialogProps { open: boolean; imageSrc: string; aspect: number; title?: string; onCancel: () => void; onCrop: (croppedBlob: Blob) => void; } async function getCroppedBlob(imageSrc: string, pixelCrop: Area): Promise { const image = await createImageBitmap(await (await fetch(imageSrc)).blob()); const canvas = document.createElement('canvas'); canvas.width = pixelCrop.width; canvas.height = pixelCrop.height; const ctx = canvas.getContext('2d'); if (!ctx) throw new Error('Canvas context unavailable'); ctx.drawImage( image, pixelCrop.x, pixelCrop.y, pixelCrop.width, pixelCrop.height, 0, 0, pixelCrop.width, pixelCrop.height, ); return new Promise((resolve, reject) => { canvas.toBlob((blob) => { if (blob) resolve(blob); else reject(new Error('Failed to create blob')); }, 'image/jpeg', 0.92); }); } export function ImageCropDialog({ open, imageSrc, aspect, title = 'Crop Image', onCancel, onCrop }: ImageCropDialogProps) { const [crop, setCrop] = useState({ x: 0, y: 0 }); const [zoom, setZoom] = useState(1); const [croppedAreaPixels, setCroppedAreaPixels] = useState(null); const [isProcessing, setIsProcessing] = useState(false); const onCropComplete = useCallback((_: Area, areaPixels: Area) => { setCroppedAreaPixels(areaPixels); }, []); const handleReset = () => { setCrop({ x: 0, y: 0 }); setZoom(1); }; const handleConfirm = async () => { if (!croppedAreaPixels) return; setIsProcessing(true); try { const blob = await getCroppedBlob(imageSrc, croppedAreaPixels); onCrop(blob); } finally { setIsProcessing(false); } }; return ( { if (!v) onCancel(); }}> {title} {/* Cropper area */}
{/* Controls */}
setZoom(v)} className="flex-1" />

Drag to reposition · Pinch or scroll to zoom

); }