mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(rotate): add editable angle input and fine-tune +/- buttons
Replace the static angle display between Left/Right buttons with an editable input field that accepts direct angle entry. Add - and + buttons for 1-degree fine-tuned control. All controls update the rotation state which triggers real-time CSS preview on the image.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { FlipHorizontal, FlipVertical, RotateCcw, RotateCw } from "lucide-react";
|
import { FlipHorizontal, FlipVertical, Minus, Plus, RotateCcw, RotateCw } from "lucide-react";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { ProgressCard } from "@/components/common/progress-card";
|
import { ProgressCard } from "@/components/common/progress-card";
|
||||||
import { useToolProcessor } from "@/hooks/use-tool-processor";
|
import { useToolProcessor } from "@/hooks/use-tool-processor";
|
||||||
@@ -44,9 +44,30 @@ export function RotateSettings({ onPreviewTransform }: RotateSettingsProps) {
|
|||||||
prevProcessing.current = processing;
|
prevProcessing.current = processing;
|
||||||
}, [processing, error]);
|
}, [processing, error]);
|
||||||
|
|
||||||
|
// Display angle normalized to 0-359
|
||||||
|
const displayAngle = ((totalAngle % 360) + 360) % 360;
|
||||||
|
|
||||||
|
const [angleInput, setAngleInput] = useState(String(displayAngle));
|
||||||
|
const prevDisplayAngle = useRef(displayAngle);
|
||||||
|
if (prevDisplayAngle.current !== displayAngle) {
|
||||||
|
prevDisplayAngle.current = displayAngle;
|
||||||
|
setAngleInput(String(displayAngle));
|
||||||
|
}
|
||||||
|
|
||||||
const rotateLeft = () => setRotation((r) => r - 90);
|
const rotateLeft = () => setRotation((r) => r - 90);
|
||||||
const rotateRight = () => setRotation((r) => r + 90);
|
const rotateRight = () => setRotation((r) => r + 90);
|
||||||
|
|
||||||
|
const commitAngleInput = () => {
|
||||||
|
const parsed = Number.parseInt(angleInput, 10);
|
||||||
|
if (!Number.isNaN(parsed)) {
|
||||||
|
const normalized = ((parsed % 360) + 360) % 360;
|
||||||
|
setAngleInput(String(normalized));
|
||||||
|
setRotation(normalized - straighten);
|
||||||
|
} else {
|
||||||
|
setAngleInput(String(displayAngle));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleProcess = () => {
|
const handleProcess = () => {
|
||||||
const backendAngle = ((totalAngle % 360) + 360) % 360;
|
const backendAngle = ((totalAngle % 360) + 360) % 360;
|
||||||
const settings = {
|
const settings = {
|
||||||
@@ -76,9 +97,6 @@ export function RotateSettings({ onPreviewTransform }: RotateSettingsProps) {
|
|||||||
setFlipV(false);
|
setFlipV(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Display angle normalized to 0-359
|
|
||||||
const displayAngle = ((totalAngle % 360) + 360) % 360;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
{/* Quick rotate */}
|
{/* Quick rotate */}
|
||||||
@@ -94,9 +112,41 @@ export function RotateSettings({ onPreviewTransform }: RotateSettingsProps) {
|
|||||||
<RotateCcw className="h-4 w-4" />
|
<RotateCcw className="h-4 w-4" />
|
||||||
Left
|
Left
|
||||||
</button>
|
</button>
|
||||||
<div className="px-3 py-1.5 rounded-md bg-background border border-border text-center min-w-[4rem]">
|
<button
|
||||||
<span className="text-sm font-mono font-medium tabular-nums">{displayAngle}°</span>
|
type="button"
|
||||||
|
onClick={() => setRotation((r) => r - 1)}
|
||||||
|
className="p-2 rounded-lg bg-muted text-muted-foreground hover:bg-primary hover:text-primary-foreground transition-colors"
|
||||||
|
title="Decrease 1°"
|
||||||
|
>
|
||||||
|
<Minus className="h-3.5 w-3.5" />
|
||||||
|
</button>
|
||||||
|
<div className="relative flex items-center">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
value={angleInput}
|
||||||
|
onChange={(e) => setAngleInput(e.target.value)}
|
||||||
|
onBlur={commitAngleInput}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
commitAngleInput();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="w-14 text-center text-sm font-mono font-medium tabular-nums py-1.5 rounded-md bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary/50 pr-4"
|
||||||
|
/>
|
||||||
|
<span className="absolute right-2 text-sm font-mono text-muted-foreground pointer-events-none">
|
||||||
|
°
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setRotation((r) => r + 1)}
|
||||||
|
className="p-2 rounded-lg bg-muted text-muted-foreground hover:bg-primary hover:text-primary-foreground transition-colors"
|
||||||
|
title="Increase 1°"
|
||||||
|
>
|
||||||
|
<Plus className="h-3.5 w-3.5" />
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={rotateRight}
|
onClick={rotateRight}
|
||||||
|
|||||||
Reference in New Issue
Block a user