Positioning and logic fixes

This commit is contained in:
James Brunton 2025-09-19 15:44:41 +01:00
parent 5a24e00497
commit 03eda04aec
3 changed files with 17 additions and 67 deletions

View File

@ -43,7 +43,7 @@ const CropAreaSelector: React.FC<CropAreaSelectorProps> = ({
// Convert PDF crop area to DOM coordinates for display // Convert PDF crop area to DOM coordinates for display
const domRect = pdfToDOMCoordinates(cropArea, pdfBounds); const domRect = pdfToDOMCoordinates(cropArea, pdfBounds);
// Handle mouse down on overlay (start dragging) // Handle mouse down on overlay (start dragging or resizing)
const handleOverlayMouseDown = useCallback((e: React.MouseEvent) => { const handleOverlayMouseDown = useCallback((e: React.MouseEvent) => {
if (disabled || !containerRef.current) return; if (disabled || !containerRef.current) return;
@ -54,18 +54,18 @@ const CropAreaSelector: React.FC<CropAreaSelectorProps> = ({
const x = e.clientX - rect.left; const x = e.clientX - rect.left;
const y = e.clientY - rect.top; const y = e.clientY - rect.top;
// Check if we're clicking on a resize handle // Check if we're clicking on a resize handle first (higher priority)
const handle = getResizeHandle(x, y, domRect); const handle = getResizeHandle(x, y, domRect);
if (handle) { if (handle) {
setIsResizing(handle); setIsResizing(handle);
setInitialCropArea(cropArea); setInitialCropArea(cropArea);
} else { setIsDragging(false); // Ensure we're not dragging when resizing
// Check if we're clicking within the crop area for dragging } else if (isPointInCropArea(x, y, domRect)) {
if (isPointInCropArea(x, y, domRect)) { // Only allow dragging if we're not on a resize handle
setIsDragging(true); setIsDragging(true);
setDragStart({ x: x - domRect.x, y: y - domRect.y }); setIsResizing(null); // Ensure we're not resizing when dragging
} setDragStart({ x: x - domRect.x, y: y - domRect.y });
} }
}, [disabled, cropArea, domRect]); }, [disabled, cropArea, domRect]);
@ -211,15 +211,15 @@ const CropAreaSelector: React.FC<CropAreaSelectorProps> = ({
function getResizeHandle(x: number, y: number, domRect: DOMRect): ResizeHandle { function getResizeHandle(x: number, y: number, domRect: DOMRect): ResizeHandle {
const handleSize = 8; const handleSize = 8;
const tolerance = 4; const tolerance = handleSize;
// Corner handles // Corner handles (check these first, they have priority)
if (isNear(x, domRect.x, tolerance) && isNear(y, domRect.y, tolerance)) return 'nw'; if (isNear(x, domRect.x, tolerance) && isNear(y, domRect.y, tolerance)) return 'nw';
if (isNear(x, domRect.x + domRect.width, tolerance) && isNear(y, domRect.y, tolerance)) return 'ne'; if (isNear(x, domRect.x + domRect.width, tolerance) && isNear(y, domRect.y, tolerance)) return 'ne';
if (isNear(x, domRect.x, tolerance) && isNear(y, domRect.y + domRect.height, tolerance)) return 'sw'; if (isNear(x, domRect.x, tolerance) && isNear(y, domRect.y + domRect.height, tolerance)) return 'sw';
if (isNear(x, domRect.x + domRect.width, tolerance) && isNear(y, domRect.y + domRect.height, tolerance)) return 'se'; if (isNear(x, domRect.x + domRect.width, tolerance) && isNear(y, domRect.y + domRect.height, tolerance)) return 'se';
// Edge handles // Edge handles (only if not in corner area)
if (isNear(x, domRect.x + domRect.width / 2, tolerance) && isNear(y, domRect.y, tolerance)) return 'n'; if (isNear(x, domRect.x + domRect.width / 2, tolerance) && isNear(y, domRect.y, tolerance)) return 'n';
if (isNear(x, domRect.x + domRect.width, tolerance) && isNear(y, domRect.y + domRect.height / 2, tolerance)) return 'e'; if (isNear(x, domRect.x + domRect.width, tolerance) && isNear(y, domRect.y + domRect.height / 2, tolerance)) return 'e';
if (isNear(x, domRect.x + domRect.width / 2, tolerance) && isNear(y, domRect.y + domRect.height, tolerance)) return 's'; if (isNear(x, domRect.x + domRect.width / 2, tolerance) && isNear(y, domRect.y + domRect.height, tolerance)) return 's';

View File

@ -1,28 +1,24 @@
import React, { useMemo, useState, useEffect } from "react"; import { useMemo, useState, useEffect } from "react";
import { Stack, Text, Box, Group, NumberInput, ActionIcon, Center, Alert } from "@mantine/core"; import { Stack, Text, Box, Group, NumberInput, ActionIcon, Center, Alert } from "@mantine/core";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import RestartAltIcon from "@mui/icons-material/RestartAlt"; import RestartAltIcon from "@mui/icons-material/RestartAlt";
import InfoIcon from "@mui/icons-material/Info";
import { CropParametersHook } from "../../../hooks/tools/crop/useCropParameters"; import { CropParametersHook } from "../../../hooks/tools/crop/useCropParameters";
import { useSelectedFiles } from "../../../contexts/file/fileHooks"; import { useSelectedFiles } from "../../../contexts/file/fileHooks";
import { useFileContext } from "../../../contexts/FileContext";
import DocumentThumbnail from "../../shared/filePreview/DocumentThumbnail";
import CropAreaSelector from "./CropAreaSelector"; import CropAreaSelector from "./CropAreaSelector";
import { import {
calculatePDFBounds, calculatePDFBounds,
PDFBounds, PDFBounds,
CropArea, CropArea
getPDFAspectRatio,
createFullPDFCropArea
} from "../../../utils/cropCoordinates"; } from "../../../utils/cropCoordinates";
import { pdfWorkerManager } from "../../../services/pdfWorkerManager"; import { pdfWorkerManager } from "../../../services/pdfWorkerManager";
import DocumentThumbnail from "../../shared/filePreview/DocumentThumbnail";
interface CropSettingsProps { interface CropSettingsProps {
parameters: CropParametersHook; parameters: CropParametersHook;
disabled?: boolean; disabled?: boolean;
} }
const CONTAINER_SIZE = 400; // Larger container for better crop precision const CONTAINER_SIZE = 250; // Fit within actual pane width
const CropSettings = ({ parameters, disabled = false }: CropSettingsProps) => { const CropSettings = ({ parameters, disabled = false }: CropSettingsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -41,7 +37,6 @@ const CropSettings = ({ parameters, disabled = false }: CropSettingsProps) => {
// Get thumbnail for the selected file // Get thumbnail for the selected file
const [thumbnail, setThumbnail] = useState<string | null>(null); const [thumbnail, setThumbnail] = useState<string | null>(null);
const [pdfBounds, setPdfBounds] = useState<PDFBounds | null>(null); const [pdfBounds, setPdfBounds] = useState<PDFBounds | null>(null);
const [loading, setLoading] = useState(false);
useEffect(() => { useEffect(() => {
const loadPDFDimensions = async () => { const loadPDFDimensions = async () => {
@ -52,7 +47,6 @@ const CropSettings = ({ parameters, disabled = false }: CropSettingsProps) => {
} }
setThumbnail(selectedStub.thumbnailUrl || null); setThumbnail(selectedStub.thumbnailUrl || null);
setLoading(true);
try { try {
// Get PDF dimensions from the actual file // Get PDF dimensions from the actual file
@ -95,8 +89,6 @@ const CropSettings = ({ parameters, disabled = false }: CropSettingsProps) => {
if (parameters.parameters.width === 595 && parameters.parameters.height === 842) { if (parameters.parameters.width === 595 && parameters.parameters.height === 842) {
parameters.resetToFullPDF(bounds); parameters.resetToFullPDF(bounds);
} }
} finally {
setLoading(false);
} }
}; };
@ -106,6 +98,7 @@ const CropSettings = ({ parameters, disabled = false }: CropSettingsProps) => {
// Current crop area // Current crop area
const cropArea = parameters.getCropArea(); const cropArea = parameters.getCropArea();
// Handle crop area changes from the selector // Handle crop area changes from the selector
const handleCropAreaChange = (newCropArea: CropArea) => { const handleCropAreaChange = (newCropArea: CropArea) => {
if (pdfBounds) { if (pdfBounds) {
@ -139,19 +132,6 @@ const CropSettings = ({ parameters, disabled = false }: CropSettingsProps) => {
return Math.round((cropAreaSize / totalArea) * 100); return Math.round((cropAreaSize / totalArea) * 100);
}, [cropArea, pdfBounds]); }, [cropArea, pdfBounds]);
// Get aspect ratio information
const aspectRatioInfo = useMemo(() => {
if (!pdfBounds) return null;
const pdfRatio = getPDFAspectRatio(pdfBounds);
const cropRatio = cropArea.width / cropArea.height;
return {
pdf: pdfRatio.toFixed(2),
crop: cropRatio.toFixed(2),
orientation: pdfRatio > 1 ? 'Landscape' : 'Portrait'
};
}, [pdfBounds, cropArea]);
if (!selectedStub || !pdfBounds) { if (!selectedStub || !pdfBounds) {
return ( return (
<Center style={{ height: '200px' }}> <Center style={{ height: '200px' }}>
@ -222,14 +202,6 @@ const CropSettings = ({ parameters, disabled = false }: CropSettingsProps) => {
<Text size="xs" color="dimmed"> <Text size="xs" color="dimmed">
{t("crop.info.percentage", "Area: {{percentage}}%", { percentage: cropPercentage })} {t("crop.info.percentage", "Area: {{percentage}}%", { percentage: cropPercentage })}
</Text> </Text>
{aspectRatioInfo && (
<Text size="xs" color="dimmed">
{t("crop.info.ratio", "Ratio: {{ratio}} ({{orientation}})", {
ratio: aspectRatioInfo.crop,
orientation: parseFloat(aspectRatioInfo.crop) > 1 ? 'Landscape' : 'Portrait'
})}
</Text>
)}
</Group> </Group>
</Stack> </Stack>
@ -289,28 +261,6 @@ const CropSettings = ({ parameters, disabled = false }: CropSettingsProps) => {
/> />
</Group> </Group>
{/* PDF Dimensions Info */}
<Alert
icon={<InfoIcon />}
color="blue"
variant="light"
style={{ fontSize: '0.75rem' }}
>
<Text size="xs">
{t("crop.info.pdfDimensions", "PDF Size: {{width}} × {{height}} points", {
width: Math.round(pdfBounds.actualWidth),
height: Math.round(pdfBounds.actualHeight)
})}
</Text>
{aspectRatioInfo && (
<Text size="xs">
{t("crop.info.aspectRatio", "Aspect Ratio: {{ratio}} ({{orientation}})", {
ratio: aspectRatioInfo.pdf,
orientation: aspectRatioInfo.orientation
})}
</Text>
)}
</Alert>
{/* Validation Alert */} {/* Validation Alert */}
{!isCropValid && ( {!isCropValid && (

View File

@ -59,7 +59,7 @@ export const calculatePDFBounds = (
const thumbnailWidth = actualPDFWidth * scale; const thumbnailWidth = actualPDFWidth * scale;
const thumbnailHeight = actualPDFHeight * scale; const thumbnailHeight = actualPDFHeight * scale;
// Calculate centering offsets // Calculate centering offsets - these represent where the thumbnail is positioned within the container
const offsetX = (containerWidth - thumbnailWidth) / 2; const offsetX = (containerWidth - thumbnailWidth) / 2;
const offsetY = (containerHeight - thumbnailHeight) / 2; const offsetY = (containerHeight - thumbnailHeight) / 2;