import React, { useState } from 'react'; import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, DragEndEvent, } from '@dnd-kit/core'; import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, } from '@dnd-kit/sortable'; import { cn } from '@/lib/utils'; import { Plus, Monitor, Smartphone, LayoutTemplate } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { CanvasSection } from './CanvasSection'; import { HeroRenderer, ContentRenderer, ImageTextRenderer, FeatureGridRenderer, CTABannerRenderer, ContactFormRenderer, } from './section-renderers'; interface Section { id: string; type: string; layoutVariant?: string; colorScheme?: string; props: Record; } interface CanvasRendererProps { sections: Section[]; selectedSectionId: string | null; deviceMode: 'desktop' | 'mobile'; onSelectSection: (id: string | null) => void; onAddSection: (type: string, index?: number) => void; onDeleteSection: (id: string) => void; onDuplicateSection: (id: string) => void; onMoveSection: (id: string, direction: 'up' | 'down') => void; onReorderSections: (sections: Section[]) => void; onDeviceModeChange: (mode: 'desktop' | 'mobile') => void; } const SECTION_TYPES = [ { type: 'hero', label: 'Hero', icon: LayoutTemplate }, { type: 'content', label: 'Content', icon: LayoutTemplate }, { type: 'image-text', label: 'Image + Text', icon: LayoutTemplate }, { type: 'feature-grid', label: 'Feature Grid', icon: LayoutTemplate }, { type: 'cta-banner', label: 'CTA Banner', icon: LayoutTemplate }, { type: 'contact-form', label: 'Contact Form', icon: LayoutTemplate }, ]; // Map section type to renderer component const SECTION_RENDERERS: Record> = { 'hero': HeroRenderer, 'content': ContentRenderer, 'image-text': ImageTextRenderer, 'feature-grid': FeatureGridRenderer, 'cta-banner': CTABannerRenderer, 'contact-form': ContactFormRenderer, }; export function CanvasRenderer({ sections, selectedSectionId, deviceMode, onSelectSection, onAddSection, onDeleteSection, onDuplicateSection, onMoveSection, onReorderSections, onDeviceModeChange, }: CanvasRendererProps) { const [hoveredSectionId, setHoveredSectionId] = useState(null); const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 8, }, }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates, }) ); const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; if (over && active.id !== over.id) { const oldIndex = sections.findIndex(s => s.id === active.id); const newIndex = sections.findIndex(s => s.id === over.id); const newSections = arrayMove(sections, oldIndex, newIndex); onReorderSections(newSections); } }; const handleCanvasClick = (e: React.MouseEvent) => { // Only deselect if clicking directly on canvas background if (e.target === e.currentTarget) { onSelectSection(null); } }; return (
{/* Device mode toggle */}
{/* Canvas viewport */}
{sections.length === 0 ? (

No sections yet

Add your first section to start building

{SECTION_TYPES.map((type) => ( onAddSection(type.type)} > {type.label} ))}
) : (
{/* Top Insertion Zone */} onAddSection(type)} // Implicitly index 0 is fine if we handle it in store, but wait store expects index. // Actually onAddSection in Props is (type) => void. I need to update Props too. // Let's check props interface above. /> s.id)} strategy={verticalListSortingStrategy} >
{sections.map((section, index) => { const Renderer = SECTION_RENDERERS[section.type]; return ( onSelectSection(section.id)} onHover={() => setHoveredSectionId(section.id)} onLeave={() => setHoveredSectionId(null)} onDelete={() => onDeleteSection(section.id)} onDuplicate={() => onDuplicateSection(section.id)} onMoveUp={() => onMoveSection(section.id, 'up')} onMoveDown={() => onMoveSection(section.id, 'down')} canMoveUp={index > 0} canMoveDown={index < sections.length - 1} > {Renderer ? ( ) : (
Unknown section type: {section.type}
)}
{/* Insertion Zone After Section */} onAddSection(type, index + 1)} />
); })}
)}
); } // Helper: Insertion Zone Component function InsertionZone({ index, onAdd }: { index: number; onAdd: (type: string) => void }) { return (
{/* Line */}
{/* Button */} {SECTION_TYPES.map((type) => ( onAdd(type.type)} > {type.label} ))}
); }