'use client' import React, { useState, useCallback } from 'react' import Image from 'next/image' import type { FDVideoBlock as FDVideoBlockProps, Media } from '@/payload-types' const maxWidthClasses: Record = { default: 'max-w-[1200px]', narrow: 'max-w-[900px]', wide: 'max-w-[1400px]', } // Navy is always dark. White/gray adapt to OS dark mode. const bgClasses: Record = { white: 'bg-white dark:bg-fd-navy', navy: 'bg-fd-navy', gray: 'bg-fd-surface-alt dark:bg-fd-navy', yellow: 'bg-fd-yellow', transparent: 'bg-transparent', } function extractYouTubeId(url: string): string | null { if (!url) return null const patterns = [ /(?:youtube\.com\/watch\?v=)([a-zA-Z0-9_-]{11})/, /(?:youtu\.be\/)([a-zA-Z0-9_-]{11})/, /(?:youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/, /(?:youtube\.com\/v\/)([a-zA-Z0-9_-]{11})/, /(?:youtube\.com\/shorts\/)([a-zA-Z0-9_-]{11})/, ] for (const pattern of patterns) { const match = url.match(pattern) if (match) return match[1] } return null } function extractVimeoId(url: string): string | null { if (!url) return null const match = url.match(/vimeo\.com\/(?:video\/)?(\d+)/) return match ? match[1] : null } /* Priority #5: Responsive video radius */ const videoRadius = 'rounded-[32px] md:rounded-[50px] lg:rounded-[70px]' export const FDVideoBlockComponent: React.FC = ({ heading, description, videoSource = 'upload', videoFile, youtubeUrl, vimeoUrl, thumbnail, aspectRatio = '16/9', autoplay = false, loop = false, maxWidth = 'default', sectionBackground = 'white', textColor = 'auto', anchorId, }) => { const [isPlaying, setIsPlaying] = useState(false) const isDark = sectionBackground === 'navy' // Manual override takes priority; otherwise auto-adapt with dark: fallback const headingColor = textColor === 'white' ? 'text-white' : textColor === 'navy' ? 'text-fd-navy' : isDark ? 'text-fd-yellow' : 'text-fd-navy dark:text-fd-yellow' const bodyColor = textColor === 'white' ? 'text-white' : textColor === 'navy' ? 'text-fd-navy' : isDark ? 'text-white' : 'text-fd-navy dark:text-white' const bgClass = bgClasses[sectionBackground ?? 'white'] || 'bg-white dark:bg-fd-navy' const containerClass = maxWidthClasses[maxWidth ?? 'default'] || 'max-w-[1200px]' const videoMedia = videoFile as Media | undefined const thumbnailMedia = thumbnail as Media | undefined const thumbnailUrl = thumbnailMedia?.url || '' const hasThumbnail = Boolean(thumbnailUrl) const youtubeId = youtubeUrl ? extractYouTubeId(youtubeUrl) : null const vimeoId = vimeoUrl ? extractVimeoId(vimeoUrl) : null const autoThumbnailUrl = !hasThumbnail && youtubeId ? `https://img.youtube.com/vi/${youtubeId}/maxresdefault.jpg` : '' const showThumbnailOverlay = !isPlaying && (hasThumbnail || autoThumbnailUrl) && videoSource !== 'upload' const handlePlay = useCallback(() => { setIsPlaying(true) }, []) /* Accessibility: include video title in aria-label */ const videoTitle = heading || 'Video' const PlayButton = () => (
) return (
{(heading || description) && (
{heading && ( /* Priority #6: Was font-joey-medium — all other section headings use font-joey-heavy */

{heading}

)} {description && (

{description}

)}
)} {/* Priority #5: Responsive radius instead of fixed rounded-[70px] */}
{videoSource === 'upload' && videoMedia?.url && ( <> {!autoplay && hasThumbnail && !isPlaying && ( )} )} {videoSource === 'youtube' && youtubeId && ( <> {showThumbnailOverlay ? ( ) : (