import React, { useState, useRef } from 'react'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Slider } from '@/components/ui/slider'; import { Switch } from '@/components/ui/switch'; import { GripVertical, Undo, RectangleHorizontal, Palette, CornerUpLeft, Wand2 } from 'lucide-react'; import { useMockupStore } from '@/contexts/MockupContext'; import { extractDominantColor } from '@/utils/colorExtractor'; interface ImageBorderPanelProps { onClose: () => void; } export const ImageBorderPanel: React.FC = ({ onClose }) => { const { imageBorder, setImageBorder, uploadedImage, } = useMockupStore(); // Parse RGBA to initialize isTransparent and opacity const parseRgba = (color: string): { r: number; g: number; b: number; a: number } => { const matches = color.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/); if (matches) { return { r: parseInt(matches[1]), g: parseInt(matches[2]), b: parseInt(matches[3]), a: parseFloat(matches[4]), }; } // Fallback to default color if parsing fails return { r: 156, g: 163, b: 137, a: 1 }; // #9CA389 }; const initialRgba = parseRgba(imageBorder.color); const [isDragging, setIsDragging] = useState(false); const [windowPosition, setWindowPosition] = useState({ x: 300, y: 100 }); const windowRef = useRef(null); const dragStartRef = useRef({ x: 0, y: 0 }); const [isTransparent, setIsTransparent] = useState(initialRgba.a < 1); const [isLoadingColor, setIsLoadingColor] = useState(false); const [opacity, setOpacity] = useState(Math.max(0.3, initialRgba.a)); const handleWindowDragStart = (e: React.MouseEvent) => { e.preventDefault(); setIsDragging(true); dragStartRef.current = { x: e.clientX - windowPosition.x, y: e.clientY - windowPosition.y }; }; const handleWindowDrag = (e: MouseEvent) => { if (!isDragging) return; setWindowPosition({ x: e.clientX - dragStartRef.current.x, y: e.clientY - dragStartRef.current.y }); }; const handleWindowDragEnd = () => { setIsDragging(false); }; React.useEffect(() => { if (isDragging) { window.addEventListener('mousemove', handleWindowDrag); window.addEventListener('mouseup', handleWindowDragEnd); return () => { window.removeEventListener('mousemove', handleWindowDrag); window.removeEventListener('mouseup', handleWindowDragEnd); }; } }, [isDragging]); const resetBorder = () => { setImageBorder({ width: 8, color: 'rgba(156, 163, 137, 1)', // #9CA389 radius: 22, enabled: false, shadow: 'rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px', }); setIsTransparent(false); setOpacity(1); }; const handleMagicWandClick = async () => { if (!uploadedImage) return; setIsLoadingColor(true); try { const dominantColor = await extractDominantColor(uploadedImage); const matches = dominantColor.match(/^#[0-9A-Fa-f]{6}$/) ? dominantColor : '#9CA389'; const { r, g, b } = hexToRgb(matches); setImageBorder({ color: `rgba(${r}, ${g}, ${b}, ${isTransparent ? opacity : 1})` }); } catch (error) { console.error('Error extracting dominant color:', error); setImageBorder({ color: `rgba(156, 163, 137, ${isTransparent ? opacity : 1})` // #9CA389 }); } finally { setIsLoadingColor(false); } }; const colorOptions = [ 'magic-wand', 'rgba(255, 255, 255, 1)', // #FFFFFF 'rgba(156, 163, 137, 1)', // #9CA389 'rgba(0, 0, 0, 1)', // #000000 'rgba(255, 107, 107, 1)', // #FF6B6B 'rgba(254, 202, 87, 1)', // #FECA57 'rgba(78, 205, 196, 1)', // #4ECDC4 'rgba(69, 183, 209, 1)', // #45B7D1 ]; const hexToRgb = (hex: string): { r: number; g: number; b: number } => { const r = parseInt(hex.slice(1, 3), 16); const g = parseInt(hex.slice(3, 5), 16); const b = parseInt(hex.slice(5, 7), 16); return { r, g, b }; }; const convertToRgba = (r: number, g: number, b: number, opacity: number): string => { return `rgba(${r}, ${g}, ${b}, ${opacity})`; }; const handleColorSelect = (color: string) => { if (color === 'magic-wand') return; const { r, g, b } = parseRgba(color); setImageBorder({ color: convertToRgba(r, g, b, isTransparent ? opacity : 1) }); }; const handleTransparencyToggle = (checked: boolean) => { setIsTransparent(checked); const newOpacity = checked ? 0.5 : 1; setOpacity(newOpacity); const { r, g, b } = parseRgba(imageBorder.color); setImageBorder({ color: convertToRgba(r, g, b, newOpacity) }); }; const handleOpacityChange = (value: number[]) => { const newOpacity = Math.max(0.3, value[0]); setOpacity(newOpacity); if (isTransparent) { const { r, g, b } = parseRgba(imageBorder.color); setImageBorder({ color: convertToRgba(r, g, b, newOpacity) }); } }; return (
Image Border
{/* Enable Border Section */}
Enable Border setImageBorder({ enabled: checked })} />
{imageBorder.enabled && ( <> {/* Transparent Border Section */}
Transparent Border
{isTransparent && (
Opacity {Math.round(opacity * 100)}%
)}
{/* Color Section */}
Color
{colorOptions.map((color) => ( color === 'magic-wand' ? ( ) : (
{/* Width Section */}
Width
{imageBorder.width}px
setImageBorder({ width: value[0] })} min={0} max={20} step={1} />
{/* Radius (Corners) Section */}
Radius
{imageBorder.radius}px
setImageBorder({ radius: value[0] })} min={0} max={50} step={1} />
)}
); };