forked from 77media/video-flow
122 lines
4.4 KiB
TypeScript
122 lines
4.4 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { X } from 'lucide-react';
|
|
|
|
interface TemplatePreviewModalProps {
|
|
/** Control visibility */
|
|
open: boolean;
|
|
/** Video URL to preview */
|
|
videoUrl: string | null;
|
|
/** Close callback */
|
|
onClose: () => void;
|
|
/** Optional title shown over the video */
|
|
title?: string;
|
|
/** Optional description shown over the video */
|
|
description?: string;
|
|
/** Optional primary action (e.g., Try it) */
|
|
onPrimaryAction?: () => void;
|
|
/** Optional primary action label */
|
|
primaryLabel?: string;
|
|
}
|
|
|
|
/**
|
|
* Fullscreen, Tailwind-based template preview modal with video content.
|
|
* Overlays title/description if provided and supports an optional primary action.
|
|
*/
|
|
export function TemplatePreviewModal({
|
|
open,
|
|
videoUrl,
|
|
onClose,
|
|
title,
|
|
description,
|
|
onPrimaryAction,
|
|
primaryLabel = 'Try it',
|
|
}: TemplatePreviewModalProps) {
|
|
const [isReady, setIsReady] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (!open) {
|
|
setIsReady(false);
|
|
}
|
|
}, [open]);
|
|
|
|
if (!open || !videoUrl) return null;
|
|
|
|
return (
|
|
<div
|
|
data-alt="template-preview-modal"
|
|
className="fixed inset-0 z-50 bg-black/80 flex items-center justify-center p-4"
|
|
onClick={onClose}
|
|
>
|
|
<div
|
|
data-alt="template-preview-modal-content"
|
|
className="relative w-[70vw] min-h-[40vw] rounded-lg overflow-hidden border border-white/30 bg-black shadow-2xl"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<video
|
|
src={videoUrl}
|
|
autoPlay
|
|
loop
|
|
muted
|
|
playsInline
|
|
onCanPlay={() => setIsReady(true)}
|
|
className={`w-full h-auto transition-opacity duration-200 ${isReady ? 'opacity-100' : 'opacity-0'}`}
|
|
/>
|
|
|
|
{!isReady && (
|
|
<div
|
|
data-alt="template-preview-loading"
|
|
className="absolute inset-0 flex items-center justify-center"
|
|
>
|
|
<div className="h-10 w-10 rounded-full border-2 border-white/30 border-t-white/80 animate-spin" />
|
|
</div>
|
|
)}
|
|
|
|
{isReady && (title || description) && (
|
|
<div
|
|
data-alt="template-preview-header"
|
|
className="absolute top-0 left-0 right-0 bg-gradient-to-b from-black/90 via-black/80 to-transparent px-6 py-8 text-left"
|
|
>
|
|
{title && (
|
|
<div className="text-bold text-2xl text-white/90 line-clamp-2 mb-2">{title}</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{isReady && onPrimaryAction && (
|
|
<div
|
|
data-alt="template-preview-footer"
|
|
className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 via-black/80 to-transparent px-8 py-6 text-center"
|
|
>
|
|
{description && (
|
|
<div className="text-base text-white/80 line-clamp-2 text-left">{description}</div>
|
|
)}
|
|
<button
|
|
type="button"
|
|
className="inline-flex items-center justify-center rounded-full border border-white/30 bg-white/10 mt-3 px-6 py-2 text-white hover:border-white hover:bg-white hover:text-slate-900"
|
|
onClick={onPrimaryAction}
|
|
data-alt="template-preview-primary"
|
|
>
|
|
{primaryLabel}
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
<button
|
|
type="button"
|
|
data-alt="template-preview-modal-close"
|
|
className="absolute top-3 right-3 inline-flex items-center justify-center rounded-full border border-white/30 bg-white/10 p-2 text-white hover:border-white hover:bg-white hover:text-slate-900"
|
|
onClick={onClose}
|
|
>
|
|
<X className="h-4 w-4" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default TemplatePreviewModal;
|
|
|
|
|