- Create LayoutPreviewPage component for full-screen layout previews - Add preview route /themes/:themeId/layouts/:layoutId/preview to App routing - Update theme components with preview links and improved navigation - Fix iframe sandbox error by adding allow-scripts permission - Enhance template renderer with layout metadata support - Replace PostCSS with regex-only CSS parsing for browser compatibility - Add comprehensive standards documentation for code quality - Clean up CSS slot indicators to be always visible with descriptions 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
69 lines
2.1 KiB
TypeScript
69 lines
2.1 KiB
TypeScript
import React, { useRef, useEffect } from 'react';
|
|
import type { SlideLayout, Theme } from '../../types/theme';
|
|
import { renderTemplateWithSampleData, createPreviewDocument } from '../../utils/templateRenderer';
|
|
|
|
interface LayoutPreviewProps {
|
|
layout: SlideLayout;
|
|
theme: Theme;
|
|
className?: string;
|
|
}
|
|
|
|
export const LayoutPreview: React.FC<LayoutPreviewProps> = ({
|
|
layout,
|
|
theme,
|
|
className = ''
|
|
}) => {
|
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (!iframeRef.current) return;
|
|
|
|
const iframe = iframeRef.current;
|
|
|
|
// Render the template with sample data
|
|
const renderedTemplate = renderTemplateWithSampleData(layout.htmlTemplate, layout);
|
|
|
|
// Create the preview document with theme CSS
|
|
const themeCssUrl = `${theme.basePath}/${theme.cssFile}`;
|
|
const previewDocument = createPreviewDocument(renderedTemplate, themeCssUrl);
|
|
|
|
// Write the document to the iframe
|
|
const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
|
|
if (iframeDoc) {
|
|
iframeDoc.open();
|
|
iframeDoc.write(previewDocument);
|
|
iframeDoc.close();
|
|
}
|
|
}, [layout, theme]);
|
|
|
|
return (
|
|
<div className={`layout-preview ${className}`}>
|
|
<div className="layout-preview-header">
|
|
<h5 className="layout-preview-title">{layout.name}</h5>
|
|
<span className="layout-preview-slots">
|
|
{layout.slots.length} slot{layout.slots.length !== 1 ? 's' : ''}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="layout-preview-container">
|
|
<iframe
|
|
ref={iframeRef}
|
|
className="layout-preview-frame"
|
|
title={`Preview of ${layout.name}`}
|
|
sandbox="allow-same-origin allow-scripts"
|
|
/>
|
|
</div>
|
|
|
|
<div className="layout-preview-details">
|
|
<p className="layout-preview-description">{layout.description}</p>
|
|
<div className="layout-preview-slot-types">
|
|
{Array.from(new Set(layout.slots.map(slot => slot.type))).map(type => (
|
|
<span key={type} className={`slot-type-badge slot-type-${type}`}>
|
|
{type}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}; |