diff --git a/app/routes/CropSelector.tsx b/app/routes/CropSelector.tsx index 80ff578..9ee15a0 100644 --- a/app/routes/CropSelector.tsx +++ b/app/routes/CropSelector.tsx @@ -1,10 +1,10 @@ -import { useEffect, useRef, useState } from "react"; +import React, { ChangeEvent, useEffect, useRef, useState } from "react"; import DragableBox from "./DragableBox"; -import { PhAngle } from "./PhAngle"; +import { Coordinates } from "~/types"; -type Coordinates = { - x: number; - y: number; +type ContainerDimensions = { + height: number; + width: number; } export default function CropSelector() { @@ -20,10 +20,17 @@ export default function CropSelector() { x: 0, y: 0 }); + const [containerSize, setContainerSize] = useState({ + height: 0, + width: 0 + }); + const [userInputs, setUserInputs] = useState({ + height: 100, + width: 100 + }); const containerRef = useRef(null); - // Update selector position and dimensions based on handle positions useEffect(() => { setSelectorPosition({ x: Math.min(firstDragable.x, secondDragable.x), @@ -31,28 +38,83 @@ export default function CropSelector() { }); }, [firstDragable, secondDragable]); - // Calculate selector dimensions + useEffect(() => { + if (!containerRef.current) return; + + const resizeObserver = new ResizeObserver(entries => { + const entry = entries[0]; + setContainerSize({ + width: entry.contentRect.width, + height: entry.contentRect.height + }); + }); + + resizeObserver.observe(containerRef.current); + + return () => resizeObserver.disconnect(); + }, [containerRef]); + const selectorWidth = Math.abs(secondDragable.x - firstDragable.x); const selectorHeight = Math.abs(secondDragable.y - firstDragable.y); - // Handle selector drag const handleSelectorDrag = (newPosition: Coordinates) => { const deltaX = newPosition.x - selectorPosition.x; const deltaY = newPosition.y - selectorPosition.y; - setFirstDragable({ - x: Math.min(firstDragable.x, secondDragable.x) + deltaX, - y: Math.min(firstDragable.y, secondDragable.y) + deltaY - }); + const newFirstX = Math.min(firstDragable.x, secondDragable.x) + deltaX; + const newFirstY = Math.min(firstDragable.y, secondDragable.y) + deltaY; + const newSecondX = Math.max(firstDragable.x, secondDragable.x) + deltaX; + const newSecondY = Math.max(firstDragable.y, secondDragable.y) + deltaY; - setSecondDragable({ - x: Math.max(firstDragable.x, secondDragable.x) + deltaX, - y: Math.max(firstDragable.y, secondDragable.y) + deltaY - }); + // Ensure the selector stays within container bounds + if (newFirstX >= 0 && newSecondX <= containerSize.width && + newFirstY >= 0 && newSecondY <= containerSize.height) { + setFirstDragable({ + x: newFirstX, + y: newFirstY + }); + setSecondDragable({ + x: newSecondX, + y: newSecondY + }); + } }; + const handleDimensionChange = ( + dimension: 'width' | 'height', + event: React.ChangeEvent + ) => { + const value = Math.max(0, parseInt(event.target.value) || 0); + const maxValue = dimension === 'width' ? containerSize.width : containerSize.height; + const clampedValue = Math.min(value, maxValue); + + setUserInputs(prev => ({ + ...prev, + [dimension]: clampedValue + })); + + // Update selector dimensions + if (dimension === 'width') { + const newX = Math.min(firstDragable.x, containerSize.width - clampedValue); + setFirstDragable(prev => ({ ...prev, x: newX })); + setSecondDragable(prev => ({ ...prev, x: newX + clampedValue })); + } else { + const newY = Math.min(firstDragable.y, containerSize.height - clampedValue); + setFirstDragable(prev => ({ ...prev, y: newY })); + setSecondDragable(prev => ({ ...prev, y: newY + clampedValue })); + } + }; + + // Update user inputs when dragging + useEffect(() => { + setUserInputs({ + width: Math.round(selectorWidth), + height: Math.round(selectorHeight) + }); + }, [selectorWidth, selectorHeight]); + return ( -
+
- +
- +
+ +
+
+ + ) => handleDimensionChange('width', e)} + className="w-24" + /> +
+
+ + handleDimensionChange('height', e)} + className="w-24" + /> +
+
); } diff --git a/app/routes/RotatableAngle.tsx b/app/routes/RotatableAngle.tsx new file mode 100644 index 0000000..0979911 --- /dev/null +++ b/app/routes/RotatableAngle.tsx @@ -0,0 +1,29 @@ +import { Coordinates } from "~/types"; +import { PhAngle } from "./PhAngle"; + +interface ComponentProps { + position: Coordinates; + referencePosition: Coordinates; +} + +export default function RotatableAngle({ position, referencePosition }: ComponentProps) { + // Calculate angle between points + const getRotationAngle = () => { + const deltaX = referencePosition.x - position.x; + const deltaY = referencePosition.y - position.y; + // Convert from radians to degrees and adjust for SVG orientation + const degrees = Math.atan2(deltaY, deltaX) * (180 / Math.PI); + return degrees - 135; // Adjust baseline rotation to point right + }; + + return ( + + ); +}; diff --git a/app/types.ts b/app/types.ts new file mode 100644 index 0000000..cbdefcc --- /dev/null +++ b/app/types.ts @@ -0,0 +1,4 @@ +export type Coordinates = { + x: number; + y: number; +}