import { useState } from 'react' import { X, Type, Paintbrush, Palette, RotateCcw } from 'lucide-react' import { BLOCKS } from './blocks' import { ThemeBuilder } from './ThemeBuilder' import type { ThemeConfig, CanvasBlock, CustomBlockDef, DataSource } from '../App' type Tab = 'content' | 'style' | 'theme' function ColorControl({ label, value, onChange, themeVar, themeLabel }: { label: string value: string onChange: (v: string) => void themeVar?: string themeLabel?: string }) { const isTheme = value === themeVar const hexVal = /^#[0-9a-fA-F]{6}$/.test(value) ? value : '#888888' return (
{label} {themeVar && ( )}
{!isTheme && (
onChange(e.target.value)} className="w-7 h-7 rounded cursor-pointer border border-zinc-200 p-0.5 bg-white" /> onChange(e.target.value)} className="flex-1 text-xs font-mono px-2 py-1 border border-zinc-200 rounded outline-none focus:border-indigo-400" placeholder="#000000 or CSS value" maxLength={30} />
)}
) } function TextControl({ label, value, onChange, placeholder }: { label: string; value: string; onChange: (v: string) => void; placeholder?: string }) { return (
{label} onChange(e.target.value)} placeholder={placeholder} className="w-full text-xs px-2 py-1.5 border border-zinc-200 rounded outline-none focus:border-indigo-400 bg-white" />
) } export function BlockEditor({ block, customBlocks, theme, dataSources, onContentChange, onStyleChange, onStyleReset, onThemeChange, onDeselect, }: { block: CanvasBlock customBlocks: CustomBlockDef[] theme: ThemeConfig dataSources: DataSource[] onContentChange: (instanceId: string, key: string, value: string) => void onStyleChange: (instanceId: string, key: string, value: string) => void onStyleReset: (instanceId: string) => void onThemeChange: (t: ThemeConfig) => void onDeselect: () => void }) { const [tab, setTab] = useState('content') const isCustom = block.blockId.startsWith('custom:') const def = isCustom ? null : BLOCKS.find(b => b.id === block.blockId) const customDef = isCustom ? customBlocks.find(b => `custom:${b.id}` === block.blockId) : null const blockLabel = isCustom ? (customDef?.name ?? 'Custom') : (def?.label ?? block.blockId) const contentFields = def?.contentFields ?? [] const hasOverrides = Object.keys(block.styles).length > 0 const styleVal = (key: string) => block.styles[key] ?? '' const setStyle = (key: string, val: string) => onStyleChange(block.instanceId, key, val) const setContent = (key: string, val: string) => onContentChange(block.instanceId, key, val) const contentVal = (key: string, fallback: string) => block.content[key] ?? fallback return (
{/* Header */}

Editing

{blockLabel}

{/* Tabs */}
{([ { id: 'content' as Tab, label: 'Content', icon: Type }, { id: 'style' as Tab, label: 'Style', icon: Paintbrush }, { id: 'theme' as Tab, label: 'Theme', icon: Palette }, ]).map(({ id, label, icon: Icon }) => ( ))}
{/* Content tab */} {tab === 'content' && (
{isCustom ? (

Custom React components are edited in the code editor. Click the ✎ icon in the block library.

) : contentFields.length === 0 ? (

This block has no editable content fields.

) : (
{contentFields.map(field => { const val = contentVal(field.key, def?.defaultContent?.[field.key] ?? '') return (
{field.type === 'textarea' ? (