import React, { useMemo, useRef, useEffect } from 'react'; import { Frame, Element, useEditor } from '@craftjs/core'; import { Container } from '../components/layout/Container'; import { usePages } from '../state/PageContext'; import { DeviceMode } from '../types'; import { DEVICE_WIDTHS } from '../constants/presets'; import { exportBodyHtml } from '../utils/html-export'; import { useIsMobile } from '../hooks/useIsMobile'; interface CanvasProps { device: DeviceMode; /** Item 10: when false, applies `.guides-off` to hide the dashed drop-target guides. */ showGuides: boolean; } /** * Renders the actual header/footer content from the Craft.js state as a * non-interactive preview. This is the user's own authored content. */ const ZonePreview: React.FC<{ craftState: string | null; zone: 'header' | 'footer' }> = ({ craftState, zone, }) => { const containerRef = useRef(null); const renderedHtml = useMemo(() => { if (!craftState) return null; try { const result = exportBodyHtml(craftState); return result.html || null; } catch { return null; } }, [craftState]); // Set the rendered HTML into the container via ref (user-authored content) useEffect(() => { if (containerRef.current && renderedHtml) { containerRef.current.textContent = ''; const wrapper = document.createElement('div'); wrapper.innerHTML = renderedHtml; // user's own site content while (wrapper.firstChild) { containerRef.current.appendChild(wrapper.firstChild); } } }, [renderedHtml]); if (!renderedHtml) { return (
{zone === 'header' ? 'Header (empty -- click Edit Header in Pages tab)' : 'Footer (empty -- click Edit Footer in Pages tab)'}
{zone}
); } return (
); }; /** * First-run hint shown over the canvas drop area once the current page's * root node exists and has no children yet. Hidden the instant something * is dropped in, and while a drag is in progress (so it never fights the * drop-target UI). `pointer-events: none` (see .empty-canvas-hint in * editor.css) keeps it from intercepting clicks/drops meant for the * underlying empty canvas. */ export const EmptyCanvasHint: React.FC = () => { const { isEmpty, isDragging } = useEditor((state) => { const root = state.nodes['ROOT']; return { isEmpty: !!root && root.data.nodes.length === 0, isDragging: state.events.dragged.size > 0, }; }); const isMobile = useIsMobile(); if (!isEmpty || isDragging) return null; return (
{isMobile ? <>Tap Blocks below to add content, or pick a Template to start. : 'Drag blocks from the left panel, or pick a Template to start.'}
); }; export const Canvas: React.FC = ({ device, showGuides }) => { const width = DEVICE_WIDTHS[device]; const { isEditingHeader, isEditingFooter, headerPage, footerPage } = usePages(); const isEditingRegularPage = !isEditingHeader && !isEditingFooter; const frameStyle = isEditingHeader ? { minHeight: '60px', backgroundColor: '#ffffff', padding: '12px 24px', display: 'flex', alignItems: 'center' } : isEditingFooter ? { minHeight: '60px', backgroundColor: '#0f172a', color: '#94a3b8', padding: '40px 24px', textAlign: 'center' as const } : { minHeight: '100vh', backgroundColor: '#ffffff' }; const frameTag = isEditingHeader ? 'header' : isEditingFooter ? 'footer' : 'div'; return (
{(isEditingHeader || isEditingFooter) && (
Editing {isEditingHeader ? 'Header' : 'Footer'} -- This content will appear on all pages
)} {isEditingRegularPage && ( )}
{isEditingRegularPage && }
{isEditingRegularPage && ( )}
); };