wwwfiberdirekt/src/blocks/FDVideoBlock/Component.tsx

211 lines
8.0 KiB
TypeScript

'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<string, string> = {
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<string, string> = {
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<FDVideoBlockProps> = ({
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 = () => (
<div className="absolute inset-0 flex items-center justify-center bg-black/20 group-hover:bg-black/30 transition-colors">
<div className="w-20 h-20 md:w-24 md:h-24 rounded-full bg-fd-yellow flex items-center justify-center shadow-xl group-hover:scale-110 transition-transform">
<svg className="w-8 h-8 md:w-10 md:h-10 text-fd-navy ml-1" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z" />
</svg>
</div>
</div>
)
return (
<section id={anchorId || undefined} className={`relative w-full py-16 md:py-20 lg:py-[99px] ${bgClass} overflow-hidden`}>
<div className={`relative ${containerClass} mx-auto px-6 md:px-8`}>
{(heading || description) && (
<div className="mb-8 md:mb-12 text-center">
{heading && (
/* Priority #6: Was font-joey-medium — all other section headings use font-joey-heavy */
<h2 className={`font-joey-heavy text-fd-h1 mb-4 ${headingColor}`}>
{heading}
</h2>
)}
{description && (
<p className={`font-joey text-fd-body-lg max-w-[800px] mx-auto ${bodyColor}`}>
{description}
</p>
)}
</div>
)}
{/* Priority #5: Responsive radius instead of fixed rounded-[70px] */}
<div className={`relative w-full overflow-hidden bg-black transition-all duration-300 ${isPlaying ? 'rounded-none' : videoRadius}`}>
<div className="relative w-full" style={{ paddingBottom: aspectRatio === '16/10' ? '62.5%' : '56.25%' }}>
{videoSource === 'upload' && videoMedia?.url && (
<>
<video
src={videoMedia.url}
className="absolute inset-0 w-full h-full object-cover"
controls={!autoplay}
autoPlay={autoplay || undefined}
muted={autoplay || undefined}
loop={loop || undefined}
playsInline
poster={thumbnailUrl || undefined}
>
Din webbläsare stöder inte videouppspelning.
</video>
{!autoplay && hasThumbnail && !isPlaying && (
<button onClick={handlePlay} className="absolute inset-0 w-full h-full z-10 cursor-pointer group" aria-label={`Spela video: ${videoTitle}`}>
<Image src={thumbnailUrl} alt={(thumbnailMedia as any)?.alt || 'Video miniatyrböld'} fill className="object-cover" sizes="(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px" />
<PlayButton />
</button>
)}
</>
)}
{videoSource === 'youtube' && youtubeId && (
<>
{showThumbnailOverlay ? (
<button onClick={handlePlay} className="absolute inset-0 w-full h-full z-10 cursor-pointer group" aria-label={`Spela YouTube-video: ${videoTitle}`}>
<Image src={thumbnailUrl || autoThumbnailUrl} alt={(thumbnailMedia as any)?.alt || 'Video miniatyrbild'} fill className="object-cover" sizes="(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px" />
<PlayButton />
</button>
) : (
<iframe
src={`https://www.youtube.com/embed/${youtubeId}?autoplay=${isPlaying ? 1 : 0}&rel=0`}
title={videoTitle}
className="absolute inset-0 w-full h-full"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen
loading="lazy"
style={{ border: 'none' }}
/>
)}
</>
)}
{videoSource === 'vimeo' && vimeoId && (
<>
{showThumbnailOverlay ? (
<button onClick={handlePlay} className="absolute inset-0 w-full h-full z-10 cursor-pointer group" aria-label={`Spela Vimeo-video: ${videoTitle}`}>
<Image src={thumbnailUrl} alt={(thumbnailMedia as any)?.alt || 'Video miniatyrbild'} fill className="object-cover" sizes="(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px" />
<PlayButton />
</button>
) : (
<iframe
src={`https://player.vimeo.com/video/${vimeoId}?autoplay=${isPlaying ? 1 : 0}`}
title={videoTitle}
className="absolute inset-0 w-full h-full"
allow="autoplay; fullscreen; picture-in-picture"
allowFullScreen
loading="lazy"
style={{ border: 'none' }}
/>
)}
</>
)}
</div>
</div>
</div>
</section>
)
}