slideshare/src/components/presentations/SlidePreviewModal.tsx
Michael Mainguy dcc8d19282 Improve slide preview modal with full-screen presentation mode
- Remove padding and shadows for true full-screen experience
- Use theme background color to fill empty space around slides
- Optimize aspect ratio calculations to maximize screen usage
- Maintain proper aspect ratios without distortion
- Add theme-background class for proper CSS variable inheritance

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-21 07:15:40 -05:00

118 lines
3.1 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import type { SlideLayout } from '../../types/theme.ts';
import { sanitizeSlideTemplate } from '../../utils/htmlSanitizer.ts';
import './SlidePreviewModal.css';
interface SlidePreviewModalProps {
isOpen: boolean;
onClose: () => void;
layout: SlideLayout;
content: Record<string, string>;
aspectRatio: string;
themeName: string;
}
export const SlidePreviewModal: React.FC<SlidePreviewModalProps> = ({
isOpen,
onClose,
layout,
content,
aspectRatio,
themeName
}) => {
const [showHint, setShowHint] = useState(true);
// Handle ESC key press
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape' && isOpen) {
onClose();
}
};
if (isOpen) {
document.addEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'hidden'; // Prevent scrolling
// Hide hint after 3 seconds
const hintTimer = setTimeout(() => {
setShowHint(false);
}, 3000);
return () => {
document.removeEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'unset';
clearTimeout(hintTimer);
};
}
}, [isOpen, onClose]);
// Reset hint when modal opens
useEffect(() => {
if (isOpen) {
setShowHint(true);
}
}, [isOpen]);
// Render template with actual content
const renderTemplateWithContent = (layout: SlideLayout, content: Record<string, string>): string => {
let rendered = layout.htmlTemplate;
// Replace content placeholders
Object.entries(content).forEach(([slotId, value]) => {
const placeholder = new RegExp(`\\{\\{${slotId}\\}\\}`, 'g');
rendered = rendered.replace(placeholder, value || '');
});
// Clean up any remaining placeholders
rendered = rendered.replace(/\{\{[^}]+\}\}/g, '');
return rendered;
};
if (!isOpen) return null;
const handleOverlayClick = (event: React.MouseEvent) => {
if (event.target === event.currentTarget) {
onClose();
}
};
return (
<div className="slide-preview-modal theme-background" onClick={handleOverlayClick}>
{/* ESC hint */}
{showHint && (
<div className="preview-hint">
<span>Press ESC to exit preview</span>
</div>
)}
{/* Close button */}
<button
className="preview-close-button"
onClick={onClose}
type="button"
title="Close preview (ESC)"
>
</button>
{/* Theme info */}
<div className="preview-info">
<span className="theme-name">{themeName}</span>
<span className="layout-name">{layout.name}</span>
<span className="aspect-ratio">{aspectRatio}</span>
</div>
{/* Slide content */}
<div className="slide-preview-wrapper">
<div
className={`slide-container aspect-${aspectRatio.replace(':', '-')}`}
dangerouslySetInnerHTML={{
__html: sanitizeSlideTemplate(renderTemplateWithContent(layout, content))
}}
/>
</div>
</div>
);
};