import React, { useState, useCallback, useEffect } from 'react'; import Cropper from 'react-easy-crop'; function getCroppedImg(imageSrc, pixelCrop) { return new Promise((resolve, reject) => { const image = new Image(); image.crossOrigin = 'anonymous'; image.onload = () => { const canvas = document.createElement('canvas'); canvas.width = 256; canvas.height = 256; const ctx = canvas.getContext('2d'); ctx.drawImage( image, pixelCrop.x, pixelCrop.y, pixelCrop.width, pixelCrop.height, 0, 0, 256, 256 ); canvas.toBlob((blob) => { if (!blob) return reject(new Error('Canvas toBlob failed')); resolve(blob); }, 'image/png'); }; image.onerror = reject; image.src = imageSrc; }); } const AvatarCropModal = ({ imageUrl, onApply, onCancel, cropShape = 'round' }) => { const [crop, setCrop] = useState({ x: 0, y: 0 }); const [zoom, setZoom] = useState(1); const [croppedAreaPixels, setCroppedAreaPixels] = useState(null); const onCropComplete = useCallback((_croppedArea, croppedPixels) => { setCroppedAreaPixels(croppedPixels); }, []); const handleApply = useCallback(async () => { if (!croppedAreaPixels) return; const blob = await getCroppedImg(imageUrl, croppedAreaPixels); onApply(blob); }, [imageUrl, croppedAreaPixels, onApply]); useEffect(() => { const handleKey = (e) => { if (e.key === 'Escape') { e.stopPropagation(); onCancel(); } }; window.addEventListener('keydown', handleKey, true); return () => window.removeEventListener('keydown', handleKey, true); }, [onCancel]); return (