import React, { useState, useCallback, useRef, CSSProperties } from 'react'; import { useEditor } from '@craftjs/core'; import { RADIUS_PRESETS, } from '../../../constants/presets'; import { StylePanelProps, SectionLabel, PresetButtonGrid, TextInputField, uploadToWhp, } from './shared'; /* ---------- IMAGE (with upload/browse/drop) ---------- */ export const ImageStylePanel: React.FC = ({ selectedId, nodeProps }) => { const { actions } = useEditor(); const style: CSSProperties = nodeProps.style || {}; const fileInputRef = useRef(null); const [showBrowser, setShowBrowser] = useState(false); const [browserAssets, setBrowserAssets] = useState([]); const [browserLoading, setBrowserLoading] = useState(false); const [imgUrl, setImgUrl] = useState(nodeProps.src || ''); const PLACEHOLDER_SRC = "data:image/svg+xml,%3Csvg"; const isPlaceholder = !nodeProps.src || nodeProps.src.startsWith('data:image/svg'); const setPropStyle = useCallback( (property: string, value: string) => { actions.setProp(selectedId, (props: any) => { props.style = { ...props.style, [property]: value }; }); }, [actions, selectedId], ); const handleUpload = useCallback(async (file: File) => { const url = await uploadToWhp(file); if (url) { actions.setProp(selectedId, (props: any) => { props.src = url; }); setImgUrl(url); } }, [actions, selectedId]); const handleBrowse = useCallback(async () => { if (showBrowser) { setShowBrowser(false); return; } const cfg = (window as any).WHP_CONFIG; if (!cfg) return; setBrowserLoading(true); try { const resp = await fetch(`${cfg.apiUrl}?action=list_assets&site_id=${cfg.siteId}`); const data = await resp.json(); if (data.success && Array.isArray(data.assets)) { const images = data.assets.filter((a: any) => (a.type || '').startsWith('image')); setBrowserAssets(images); setShowBrowser(true); } } catch (e) { console.error('Browse failed:', e); } finally { setBrowserLoading(false); } }, [showBrowser]); const maxWidthPresets = [ { label: '25%', value: '25%' }, { label: '50%', value: '50%' }, { label: '75%', value: '75%' }, { label: '100%', value: '100%' }, ]; const getFriendlyName = (src: string) => { const match = src.match(/filename=([^&]+)/); if (match) return decodeURIComponent(match[1]).replace(/^\d+_[a-f0-9]+_/, ''); return src.split('/').pop() || 'image'; }; return ( <> {/* Image source with upload/browse */}
Image Source {!isPlaceholder && nodeProps.src ? ( <>
{getFriendlyName(nodeProps.src || '')}
) : (
{ e.preventDefault(); e.currentTarget.style.borderColor = '#3b82f6'; }} onDragLeave={(e) => { e.currentTarget.style.borderColor = '#3f3f46'; }} onDrop={async (e) => { e.preventDefault(); e.currentTarget.style.borderColor = '#3f3f46'; const file = e.dataTransfer.files?.[0]; if (file && file.type.startsWith('image/')) await handleUpload(file); }} onClick={() => fileInputRef.current?.click()} > Drop image here or click to upload
)} {/* Upload + Browse buttons */}
{/* Inline asset browser grid */} {showBrowser && (
{browserAssets.map((asset) => (
{ actions.setProp(selectedId, (props: any) => { props.src = asset.url; }); setImgUrl(asset.url); setShowBrowser(false); }} style={{ cursor: 'pointer', borderRadius: 4, overflow: 'hidden', border: '2px solid transparent', aspectRatio: '1', transition: 'border-color 0.15s' }} onMouseEnter={(e) => { e.currentTarget.style.borderColor = '#3b82f6'; }} onMouseLeave={(e) => { e.currentTarget.style.borderColor = 'transparent'; }} > {asset.name}
))} {browserAssets.length === 0 && (

No images uploaded yet. Use Upload above.

)}
)} { const file = e.target.files?.[0]; if (file) handleUpload(file); e.target.value = ''; }} /> {/* URL input for advanced users */}
setImgUrl(e.target.value)} style={{ fontSize: 11 }} />
{/* Alt Text */} { actions.setProp(selectedId, (props: any) => { props.alt = v; }); }} /> {/* Border Radius */}
Border Radius setPropStyle('borderRadius', v)} />
{/* Max Width */}
Max Width setPropStyle('maxWidth', v)} />
); };