import React, { useState, useRef, useEffect } from 'react' import { Upload, Trash2, Image as ImageIcon, Palette, X, Sliders, RotateCcw, Check, } from 'lucide-react' import { useFormStore } from '../store/useFormStore' import BackgroundImageControls from './interaction/BackgroundImageControls' const COLOR_PRESETS = [ '#ffffff', '#f8fafc', '#f1f5f9', '#f0fdf4', '#eff6ff', '#faf5ff', '#fff1f2', '#0f172a', ] export default function InlineFormBackgroundEditor({ isOpen, onClose }) { const settings = useFormStore((state) => state.settings) const setSettings = useFormStore((state) => state.setSettings) const [activeTab, setActiveTab] = useState('background') // 'background' | 'border' | 'dimensions' const fileInputRef = useRef(null) // Escape closes the editor (not while the WordPress media modal is open: Escape closes that one). useEffect(() => { if (!isOpen) return undefined const onKey = (e) => { if (e.key !== 'Escape') return const mediaOpen = Array.from(document.querySelectorAll('.media-modal')).some((el) => el.getClientRects().length > 0) if (mediaOpen) return e.stopPropagation() onClose() } document.addEventListener('keydown', onKey, true) return () => document.removeEventListener('keydown', onKey, true) }, [isOpen, onClose]) if (!isOpen) return null const handleColorChange = (color) => { setSettings({ formBgColor: color }) } const handleUploadImage = (e) => { const file = e.target.files?.[0] if (!file) return // If WordPress media library is available in window if (window.wp && window.wp.media) { const mediaFrame = window.wp.media({ title: 'Select Form Background Image', button: { text: 'Use Image' }, multiple: false, }) mediaFrame.on('select', () => { const attachment = mediaFrame.state().get('selection').first().toJSON() setSettings({ backgroundImageUrl: attachment.url }) }) mediaFrame.open() return } // Fallback: Read local file as data URL const reader = new FileReader() reader.onload = () => { setSettings({ backgroundImageUrl: reader.result }) } reader.readAsDataURL(file) } const handleWpMediaSelect = () => { if (window.wp && window.wp.media) { const mediaFrame = window.wp.media({ title: 'Select Form Background Image', button: { text: 'Use Image' }, multiple: false, }) mediaFrame.on('select', () => { const attachment = mediaFrame.state().get('selection').first().toJSON() setSettings({ backgroundImageUrl: attachment.url }) }) mediaFrame.open() } else if (fileInputRef.current) { fileInputRef.current.click() } } const hasBackgroundImage = !!settings.backgroundImageUrl return (
e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()} > {/* Header with Tabs */}
{/* Body Content */}
{/* Tab 1: Background */} {activeTab === 'background' && (
{/* Color Swatches */}
{COLOR_PRESETS.map((color) => (
{/* Hex Input */}
handleColorChange(e.target.value)} className="w-24 px-2 py-1 text-xs border border-slate-200 rounded-md font-mono" placeholder="#ffffff" />
{/* Background Image */}
{hasBackgroundImage ? (
Form Background
{/* Size (cover/contain/auto/custom %), repeat and 9 positions: keys the published form renders */} { const next = {} if ('size' in patch) next.backgroundImageSize = patch.size if ('position' in patch) next.backgroundImagePosition = patch.position if ('repeat' in patch) next.backgroundImageRepeat = patch.repeat setSettings(next) }} /> {/* Opacity Slider */}
Opacity {Math.round((settings.backgroundImageOpacity ?? 1) * 100)}%
setSettings({ backgroundImageOpacity: parseFloat(e.target.value) }) } className="w-full h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-gray-900" />
) : ( )}
)} {/* Tab 2: Border & Shadow */} {activeTab === 'border' && (
Corner Radius {settings.formBorderRadius ?? settings.borderRadius ?? 8}px
setSettings({ formBorderRadius: parseInt(e.target.value) || 0 })} className="w-full h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-gray-900" />
Border Width {settings.formBorderWidth ?? 1}px
setSettings({ formBorderWidth: parseInt(e.target.value) || 0 })} className="w-full h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-gray-900" />
Border Style
Border Color
setSettings({ formBorderColor: e.target.value })} className="w-6 h-6 border border-slate-200 rounded cursor-pointer" /> setSettings({ formBorderColor: e.target.value })} className="w-16 px-1.5 py-0.5 border border-slate-200 rounded text-[11px] font-mono" />
Drop Shadow
{[ { label: 'None', val: 'none' }, { label: 'Subtle', val: 'sm' }, { label: 'Medium', val: 'md' }, { label: 'Large', val: 'xl' }, ].map((s) => ( ))}
)} {/* Tab 3: Dimensions & Padding */} {activeTab === 'dimensions' && (
{/* Form Width */}
Form Width
setSettings({ formWidth: parseInt(e.target.value) || 600 })} className="flex-1 h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-gray-900" /> setSettings({ formWidth: parseInt(e.target.value) || 600 })} className="w-14 px-1.5 py-0.5 border border-slate-200 rounded text-center text-xs font-mono" /> px
{/* Form Max Height */}
Max Height
setSettings({ formMaxHeight: `${e.target.value}px` })} className="flex-1 h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-gray-900" /> {settings.formMaxHeight || 'auto'}
{/* Container Padding */}
Container Padding {settings.formPadding || 24}px
setSettings({ formPadding: parseInt(e.target.value) || 24 })} className="w-full h-1.5 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-gray-900" />
)}
{/* Footer */}
Applies instantly to canvas
) }