'use client'; import { useState, useEffect, useRef, useCallback } from 'react'; import { Slider } from '@/components/ui/slider'; import { Button } from '@/components/ui/button'; import { useLighten, useDarken, useSaturate, useDesaturate, useRotate, useComplement } from '@/lib/api/queries'; import { toast } from 'sonner'; interface ManipulationPanelProps { color: string; onColorChange: (color: string) => void; } export function ManipulationPanel({ color, onColorChange }: ManipulationPanelProps) { // Track base color and reset sliders when color changes externally const [baseColor, setBaseColor] = useState(color); const [lightenAmount, setLightenAmount] = useState(0); const [darkenAmount, setDarkenAmount] = useState(0); const [saturateAmount, setSaturateAmount] = useState(0); const [desaturateAmount, setDesaturateAmount] = useState(0); const [rotateAmount, setRotateAmount] = useState(0); const lightenMutation = useLighten(); const darkenMutation = useDarken(); const saturateMutation = useSaturate(); const desaturateMutation = useDesaturate(); const rotateMutation = useRotate(); const complementMutation = useComplement(); // Debounce timer const debounceTimer = useRef(undefined); // Reset sliders when color changes from outside (e.g., color picker) useEffect(() => { setBaseColor(color); setLightenAmount(0); setDarkenAmount(0); setSaturateAmount(0); setDesaturateAmount(0); setRotateAmount(0); }, [color]); // Apply all manipulations to base color const applyManipulations = useCallback(async () => { let currentColor = baseColor; try { // Apply lighten if (lightenAmount > 0) { const result = await lightenMutation.mutateAsync({ colors: [currentColor], amount: lightenAmount, }); if (result.colors[0]) { currentColor = result.colors[0].output; } } // Apply darken if (darkenAmount > 0) { const result = await darkenMutation.mutateAsync({ colors: [currentColor], amount: darkenAmount, }); if (result.colors[0]) { currentColor = result.colors[0].output; } } // Apply saturate if (saturateAmount > 0) { const result = await saturateMutation.mutateAsync({ colors: [currentColor], amount: saturateAmount, }); if (result.colors[0]) { currentColor = result.colors[0].output; } } // Apply desaturate if (desaturateAmount > 0) { const result = await desaturateMutation.mutateAsync({ colors: [currentColor], amount: desaturateAmount, }); if (result.colors[0]) { currentColor = result.colors[0].output; } } // Apply rotate if (rotateAmount !== 0) { const result = await rotateMutation.mutateAsync({ colors: [currentColor], amount: rotateAmount, }); if (result.colors[0]) { currentColor = result.colors[0].output; } } // Only update if color changed if (currentColor !== baseColor) { onColorChange(currentColor); } } catch (error) { // Silent error during manipulation } }, [baseColor, lightenAmount, darkenAmount, saturateAmount, desaturateAmount, rotateAmount, lightenMutation, darkenMutation, saturateMutation, desaturateMutation, rotateMutation, onColorChange]); // Debounced effect to apply manipulations useEffect(() => { if (debounceTimer.current) clearTimeout(debounceTimer.current); debounceTimer.current = setTimeout(() => { applyManipulations(); }, 300); return () => { if (debounceTimer.current) clearTimeout(debounceTimer.current); }; }, [applyManipulations]); const handleComplement = async () => { try { const result = await complementMutation.mutateAsync([color]); if (result.colors[0]) { onColorChange(result.colors[0].output); toast.success('Generated complementary color'); } } catch (error) { toast.error('Failed to generate complement'); } }; const isLoading = lightenMutation.isPending || darkenMutation.isPending || saturateMutation.isPending || desaturateMutation.isPending || rotateMutation.isPending || complementMutation.isPending; return (
{/* Lighten */}
setLightenAmount(parseFloat(e.target.value))} suffix="%" showValue />
{/* Darken */}
setDarkenAmount(parseFloat(e.target.value))} suffix="%" showValue />
{/* Saturate */}
setSaturateAmount(parseFloat(e.target.value))} suffix="%" showValue />
{/* Desaturate */}
setDesaturateAmount(parseFloat(e.target.value))} suffix="%" showValue />
{/* Rotate Hue */}
setRotateAmount(parseInt(e.target.value))} suffix="°" showValue />
{/* Quick Actions */}

Quick Actions

); }