import { Download } from "lucide-react"; import type React from "react"; import { useCallback, 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"; // ── Presets ────────────────────────────────────────────────────────── interface BorderPreset { name: string; borderWidth: number; borderColor: string; padding: number; paddingColor: string; cornerRadius: number; shadow: boolean; shadowBlur: number; shadowOffsetX: number; shadowOffsetY: number; shadowColor: string; shadowOpacity: number; } const PRESETS: BorderPreset[] = [ { name: "Clean White", borderWidth: 40, borderColor: "#FFFFFF", padding: 0, paddingColor: "#FFFFFF", cornerRadius: 0, shadow: false, shadowBlur: 15, shadowOffsetX: 0, shadowOffsetY: 5, shadowColor: "#000000", shadowOpacity: 40, }, { name: "Gallery Black", borderWidth: 30, borderColor: "#1A1A1A", padding: 12, paddingColor: "#FFFFFF", cornerRadius: 0, shadow: false, shadowBlur: 15, shadowOffsetX: 0, shadowOffsetY: 5, shadowColor: "#000000", shadowOpacity: 40, }, { name: "Shadow", borderWidth: 0, borderColor: "#FFFFFF", padding: 20, paddingColor: "#FFFFFF", cornerRadius: 0, shadow: true, shadowBlur: 20, shadowOffsetX: 0, shadowOffsetY: 8, shadowColor: "#000000", shadowOpacity: 40, }, { name: "Rounded", borderWidth: 20, borderColor: "#FFFFFF", padding: 0, paddingColor: "#FFFFFF", cornerRadius: 30, shadow: true, shadowBlur: 15, shadowOffsetX: 0, shadowOffsetY: 4, shadowColor: "#000000", shadowOpacity: 30, }, { name: "Polaroid", borderWidth: 0, borderColor: "#FFFFFF", padding: 40, paddingColor: "#FFFFFF", cornerRadius: 4, shadow: true, shadowBlur: 12, shadowOffsetX: 2, shadowOffsetY: 6, shadowColor: "#000000", shadowOpacity: 35, }, { name: "Vintage", borderWidth: 12, borderColor: "#8B7355", padding: 8, paddingColor: "#F5F0E8", cornerRadius: 0, shadow: false, shadowBlur: 15, shadowOffsetX: 0, shadowOffsetY: 5, shadowColor: "#000000", shadowOpacity: 40, }, { name: "Minimal", borderWidth: 2, borderColor: "#D4D4D4", padding: 0, paddingColor: "#FFFFFF", cornerRadius: 0, shadow: false, shadowBlur: 15, shadowOffsetX: 0, shadowOffsetY: 5, shadowColor: "#000000", shadowOpacity: 40, }, { name: "Cinematic", borderWidth: 2, borderColor: "#333333", padding: 30, paddingColor: "#0A0A0A", cornerRadius: 0, shadow: false, shadowBlur: 15, shadowOffsetX: 0, shadowOffsetY: 5, shadowColor: "#000000", shadowOpacity: 40, }, ]; // ── Color Swatches ─────────────────────────────────────────────────── const COLOR_SWATCHES = ["#FFFFFF", "#000000", "#333333", "#F5F5F5", "#8B7355"]; function ColorSwatches({ value, onChange, id, }: { value: string; onChange: (color: string) => void; id: string; }) { return (
{COLOR_SWATCHES.map((swatch) => (
); } // ── Helpers ────────────────────────────────────────────────────────── function hexToRgb(hex: string) { 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 }; } function buildPreviewStyle(s: { borderWidth: number; borderColor: string; padding: number; paddingColor: string; cornerRadius: number; shadow: boolean; shadowBlur: number; shadowOffsetX: number; shadowOffsetY: number; shadowColor: string; shadowOpacity: number; }): React.CSSProperties { const sc = hexToRgb(s.shadowColor); return { border: s.borderWidth > 0 ? `${s.borderWidth}px solid ${s.borderColor}` : undefined, padding: s.padding > 0 ? `${s.padding}px` : undefined, backgroundColor: s.padding > 0 ? s.paddingColor : undefined, borderRadius: s.cornerRadius > 0 ? `${s.cornerRadius}px` : undefined, boxShadow: s.shadow ? `${s.shadowOffsetX}px ${s.shadowOffsetY}px ${s.shadowBlur}px rgba(${sc.r},${sc.g},${sc.b},${s.shadowOpacity / 100})` : undefined, }; } // ── Controls ───────────────────────────────────────────────────────── export interface BorderControlsProps { settings?: Record; onChange?: (settings: Record) => void; onImageStyle?: (style: React.CSSProperties | null) => void; } export function BorderControls({ settings: initialSettings, onChange, onImageStyle, }: BorderControlsProps) { const [selectedPreset, setSelectedPreset] = useState(null); const [borderWidth, setBorderWidth] = useState(10); const [borderColor, setBorderColor] = useState("#000000"); const [padding, setPadding] = useState(0); const [paddingColor, setPaddingColor] = useState("#FFFFFF"); const [cornerRadius, setCornerRadius] = useState(0); const [shadow, setShadow] = useState(false); const [shadowBlur, setShadowBlur] = useState(15); const [shadowOffsetX, setShadowOffsetX] = useState(0); const [shadowOffsetY, setShadowOffsetY] = useState(5); const [shadowColor, setShadowColor] = useState("#000000"); const [shadowOpacity, setShadowOpacity] = useState(40); const initializedRef = useRef(false); useEffect(() => { if (!initialSettings || initializedRef.current) return; initializedRef.current = true; if (initialSettings.borderWidth != null) setBorderWidth(Number(initialSettings.borderWidth)); if (initialSettings.borderColor != null) setBorderColor(String(initialSettings.borderColor)); if (initialSettings.padding != null) setPadding(Number(initialSettings.padding)); if (initialSettings.paddingColor != null) setPaddingColor(String(initialSettings.paddingColor)); if (initialSettings.cornerRadius != null) setCornerRadius(Number(initialSettings.cornerRadius)); if (initialSettings.shadow != null) setShadow(Boolean(initialSettings.shadow)); if (initialSettings.shadowBlur != null) setShadowBlur(Number(initialSettings.shadowBlur)); if (initialSettings.shadowOffsetX != null) setShadowOffsetX(Number(initialSettings.shadowOffsetX)); if (initialSettings.shadowOffsetY != null) setShadowOffsetY(Number(initialSettings.shadowOffsetY)); if (initialSettings.shadowColor != null) setShadowColor(String(initialSettings.shadowColor)); if (initialSettings.shadowOpacity != null) setShadowOpacity(Number(initialSettings.shadowOpacity)); }, [initialSettings]); const onChangeRef = useRef(onChange); useEffect(() => { onChangeRef.current = onChange; }); const onImageStyleRef = useRef(onImageStyle); useEffect(() => { onImageStyleRef.current = onImageStyle; }); useEffect(() => { const vals = { borderWidth, borderColor, padding, paddingColor, cornerRadius, shadow, shadowBlur, shadowOffsetX, shadowOffsetY, shadowColor, shadowOpacity, }; onChangeRef.current?.(vals); onImageStyleRef.current?.(buildPreviewStyle(vals)); }, [ borderWidth, borderColor, padding, paddingColor, cornerRadius, shadow, shadowBlur, shadowOffsetX, shadowOffsetY, shadowColor, shadowOpacity, ]); const applyPreset = (preset: BorderPreset) => { setSelectedPreset(preset.name); setBorderWidth(preset.borderWidth); setBorderColor(preset.borderColor); setPadding(preset.padding); setPaddingColor(preset.paddingColor); setCornerRadius(preset.cornerRadius); setShadow(preset.shadow); setShadowBlur(preset.shadowBlur); setShadowOffsetX(preset.shadowOffsetX); setShadowOffsetY(preset.shadowOffsetY); setShadowColor(preset.shadowColor); setShadowOpacity(preset.shadowOpacity); }; const clearPreset = () => setSelectedPreset(null); return (
{/* Presets */}

Presets

{PRESETS.map((preset) => ( ))}
{/* Border */}
{borderWidth}px
{ setBorderWidth(Number(e.target.value)); clearPreset(); }} className="w-full mt-1" />
{ setBorderColor(c); clearPreset(); }} />
{/* Padding */}
{padding}px
{ setPadding(Number(e.target.value)); clearPreset(); }} className="w-full mt-1" />
{ setPaddingColor(c); clearPreset(); }} />
{/* Corner Radius */}
{cornerRadius}px
{ setCornerRadius(Number(e.target.value)); clearPreset(); }} className="w-full mt-1" />
{/* Shadow */}
Shadow
{shadow && (
{shadowBlur}px
{ setShadowBlur(Number(e.target.value)); clearPreset(); }} className="w-full mt-1" />
{shadowOffsetX}
{ setShadowOffsetX(Number(e.target.value)); clearPreset(); }} className="w-full mt-1" />
{shadowOffsetY}
{ setShadowOffsetY(Number(e.target.value)); clearPreset(); }} className="w-full mt-1" />
{ setShadowColor(c); clearPreset(); }} />
{shadowOpacity}%
{ setShadowOpacity(Number(e.target.value)); clearPreset(); }} className="w-full mt-1" />
)}
); } // ── Settings Panel ─────────────────────────────────────────────────── export function BorderSettings({ onImageStyle, }: { onImageStyle?: (style: React.CSSProperties | null) => void; }) { const { files } = useFileStore(); const { processFiles, processAllFiles, processing, error, downloadUrl, progress } = useToolProcessor("border"); const [settings, setSettings] = useState>({}); const handleSettingsChange = useCallback((s: Record) => { setSettings(s); }, []); const hasFile = files.length > 0; const hasEffect = Number(settings.borderWidth) > 0 || Number(settings.padding) > 0 || Number(settings.cornerRadius) > 0 || Boolean(settings.shadow); const canProcess = hasFile && !processing && hasEffect; const handleProcess = () => { if (files.length > 1) { processAllFiles(files, settings); } else { processFiles(files, settings); } }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (canProcess) handleProcess(); }; return (
{error &&

{error}

} {processing ? ( ) : ( )} {downloadUrl && ( Download )} ); }