import { FlipHorizontal, FlipVertical, Minus, Plus, RotateCcw, RotateCw } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { ProgressCard } from "@/components/common/progress-card"; import { useToolProcessor } from "@/hooks/use-tool-processor"; import { useFileStore } from "@/stores/file-store"; export interface PreviewTransform { rotate: number; flipH: boolean; flipV: boolean; } export interface RotateControlsProps { onChange?: (settings: Record) => void; onPreviewTransform?: (transform: PreviewTransform) => void; resetSignal?: number; } export function RotateControls({ onChange, onPreviewTransform, resetSignal }: RotateControlsProps) { // Quick rotation in 90° steps: 0, 90, 180, 270 const [rotation, setRotation] = useState(0); // Fine straighten adjustment: -45 to +45 const [straighten, setStraighten] = useState(0); const [flipH, setFlipH] = useState(false); const [flipV, setFlipV] = useState(false); const totalAngle = rotation + straighten; const onChangeRef = useRef(onChange); useEffect(() => { onChangeRef.current = onChange; }); // Report settings on change useEffect(() => { const backendAngle = ((totalAngle % 360) + 360) % 360; onChangeRef.current?.({ angle: backendAngle, horizontal: flipH, vertical: flipV }); }, [totalAngle, flipH, flipV]); // Emit preview transform on every change useEffect(() => { onPreviewTransform?.({ rotate: totalAngle, flipH, flipV }); }, [totalAngle, flipH, flipV, onPreviewTransform]); // Reset controls when resetSignal increments useEffect(() => { if (resetSignal !== undefined && resetSignal > 0) { setRotation(0); setStraighten(0); setFlipH(false); setFlipV(false); } }, [resetSignal]); // 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 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 hasChanges = totalAngle !== 0 || flipH || flipV; const handleReset = () => { setRotation(0); setStraighten(0); setFlipH(false); setFlipV(false); }; return (
{/* Quick rotate presets */}

Rotate

{/* Custom angle */}

Angle

setAngleInput(e.target.value)} onBlur={commitAngleInput} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); commitAngleInput(); } }} className="w-16 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" /> °
{/* Straighten */}
{straighten > 0 ? "+" : ""} {straighten}°
setStraighten(Number(e.target.value))} className="w-full mt-1" />
-45° +45°
{/* Flip buttons */}

Flip

{/* Reset all */} {hasChanges && ( )}
); } interface RotateSettingsProps { onPreviewTransform?: (transform: PreviewTransform) => void; } export function RotateSettings({ onPreviewTransform }: RotateSettingsProps) { const { files } = useFileStore(); const { processFiles, processAllFiles, processing, error, progress } = useToolProcessor("rotate"); const [settings, setSettings] = useState>({}); const [resetSignal, setResetSignal] = useState(0); // Reset controls after successful processing const prevProcessing = useRef(processing); useEffect(() => { if (prevProcessing.current && !processing && !error) { setResetSignal((s) => s + 1); } prevProcessing.current = processing; }, [processing, error]); const handleProcess = () => { if (files.length > 1) { processAllFiles(files, settings); } else { processFiles(files, settings); } }; const hasFile = files.length > 0; const hasChanges = (settings.angle !== undefined && settings.angle !== 0) || settings.horizontal === true || settings.vertical === true; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (hasFile && hasChanges && !processing) handleProcess(); }; return (
{/* Error */} {error &&

{error}

} {/* Process */} {processing ? ( ) : ( )} ); }