From e50e94327db54b82f809c7c03c3c3c58f7ddf50f Mon Sep 17 00:00:00 2001 From: denis0001-dev Date: Fri, 5 Sep 2025 18:18:40 +0300 Subject: [PATCH] Improve types --- frontend/src/core/types.d.ts | 5 ++ .../ui/components/profile/ImageCropper.tsx | 88 ++++++++++++------- 2 files changed, 61 insertions(+), 32 deletions(-) diff --git a/frontend/src/core/types.d.ts b/frontend/src/core/types.d.ts index e408693..5653ca5 100644 --- a/frontend/src/core/types.d.ts +++ b/frontend/src/core/types.d.ts @@ -32,6 +32,11 @@ export interface Size2D { y: number; } +export interface Rect extends Size2D { + width: number; + height: number; +} + // App types /** diff --git a/frontend/src/ui/components/profile/ImageCropper.tsx b/frontend/src/ui/components/profile/ImageCropper.tsx index 2955879..58baedc 100644 --- a/frontend/src/ui/components/profile/ImageCropper.tsx +++ b/frontend/src/ui/components/profile/ImageCropper.tsx @@ -1,4 +1,5 @@ import { useEffect, useRef, useState } from "react"; +import type { Size2D, Rect } from "../../../core/types"; interface ImageCropperProps { onCrop: (croppedImageData: string) => void; @@ -9,35 +10,47 @@ interface ImageCropperProps { export function ImageCropper({ onCrop, onCancel, imageFile }: ImageCropperProps) { const canvasRef = useRef(null); const imageRef = useRef(null); + const [src, setSrc] = useState(undefined); const [isLoaded, setIsLoaded] = useState(false); - const [cropArea, setCropArea] = useState({ x: 0, y: 0, width: 200, height: 200 }); + const [cropArea, setCropArea] = useState({ x: 0, y: 0, width: 200, height: 200 }); const [isDragging, setIsDragging] = useState(false); - const [dragStart, setDragStart] = useState({ x: 0, y: 0 }); + const [dragStart, setDragStart] = useState({ x: 0, y: 0 }); useEffect(() => { - if (!imageFile) return; + if (imageFile) { + const reader = new FileReader(); - const reader = new FileReader(); - reader.onload = (e) => { - if (imageRef.current) { - imageRef.current.src = e.target?.result as string; - imageRef.current.onload = () => { - setIsLoaded(true); - // Initialize crop area to center of image - if (imageRef.current) { - const img = imageRef.current; - const size = Math.min(img.naturalWidth, img.naturalHeight) * 0.8; - setCropArea({ - x: (img.naturalWidth - size) / 2, - y: (img.naturalHeight - size) / 2, - width: size, - height: size - }); - } - }; + function handleImageLoad() { + setIsLoaded(true); + // Initialize crop area to center of image + const img = imageRef.current; + if (img) { + const size = Math.min(img.naturalWidth, img.naturalHeight) * 0.8; + setCropArea({ + x: (img.naturalWidth - size) / 2, + y: (img.naturalHeight - size) / 2, + width: size, + height: size + }); + } } - }; - reader.readAsDataURL(imageFile); + + function handleReaderLoad() { + if (imageRef.current) { + setSrc(reader.result as string); + imageRef.current.addEventListener("load", handleImageLoad); + } + } + + reader.addEventListener("load", handleReaderLoad); + reader.readAsDataURL(imageFile); + + return () => { + reader.abort(); + reader.removeEventListener("load", handleReaderLoad); + imageRef.current?.removeEventListener("load", handleImageLoad); + } + } }, [imageFile]); function handleMouseDown(e: React.MouseEvent) { @@ -58,18 +71,28 @@ export function ImageCropper({ onCrop, onCancel, imageFile }: ImageCropperProps) }; function handleMouseMove(e: React.MouseEvent) { - if (!isDragging || !isLoaded) return; - const rect = canvasRef.current?.getBoundingClientRect(); - if (!rect || !imageRef.current) return; + if (isDragging && isLoaded && rect && imageRef.current) { + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; + const newX = Math.max( + 0, + Math.min( + x - dragStart.x, + imageRef.current.naturalWidth - cropArea.width + ) + ); + const newY = Math.max( + 0, + Math.min( + y - dragStart.y, + imageRef.current.naturalHeight - cropArea.height + ) + ); - const newX = Math.max(0, Math.min(x - dragStart.x, imageRef.current.naturalWidth - cropArea.width)); - const newY = Math.max(0, Math.min(y - dragStart.y, imageRef.current.naturalHeight - cropArea.height)); - - setCropArea(prev => ({ ...prev, x: newX, y: newY })); + setCropArea(prev => ({ ...prev, x: newX, y: newY })); + } }; function handleMouseUp() { @@ -152,6 +175,7 @@ export function ImageCropper({ onCrop, onCancel, imageFile }: ImageCropperProps) /> Crop source