mirror of
https://github.com/complexcaresolutions/frontend.blogwoman.de.git
synced 2026-03-17 16:14:00 +00:00
Critical fixes: - Add group class to Card component for image zoom on hover - Create Skeleton, EmptyState, and Pagination UI components - Add proper empty state to PostsListBlock instead of returning null Visual consistency: - Fix Button hover states (subtler secondary/tertiary transitions) - Add badge variants for FavoritesBlock with German labels - Increase overlay opacity in HeroBlock/VideoBlock for better contrast Accessibility improvements: - Add skip-to-content link in layout for keyboard navigation - Add focus-visible states to FAQ accordion and Testimonials carousel - Implement focus trap in MobileMenu with proper ARIA attributes - Enhance 404 page with helpful navigation links Polish: - Fix DividerBlock text contrast - Fix lint errors (Link component, const declaration) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
97 lines
3.2 KiB
TypeScript
97 lines
3.2 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import Image from 'next/image'
|
|
import { cn, extractYouTubeId, getPrivacyYouTubeUrl, getYouTubeThumbnail } from '@/lib/utils'
|
|
import type { VideoBlock as VideoBlockType } from '@/lib/types'
|
|
|
|
type VideoBlockProps = Omit<VideoBlockType, 'blockType'>
|
|
|
|
export function VideoBlock({
|
|
title,
|
|
videoUrl,
|
|
thumbnailImage,
|
|
aspectRatio = '16:9',
|
|
}: VideoBlockProps) {
|
|
const [isPlaying, setIsPlaying] = useState(false)
|
|
|
|
const videoId = extractYouTubeId(videoUrl)
|
|
const thumbnailUrl = thumbnailImage?.url || (videoId ? getYouTubeThumbnail(videoId) : null)
|
|
const embedUrl = videoId ? getPrivacyYouTubeUrl(videoId) : null
|
|
|
|
const aspectClasses = {
|
|
'16:9': 'aspect-video',
|
|
'4:3': 'aspect-[4/3]',
|
|
'1:1': 'aspect-square',
|
|
}
|
|
|
|
if (!embedUrl) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<section className="py-12 md:py-16">
|
|
<div className="container">
|
|
{title && (
|
|
<h2 className="text-center mb-8">{title}</h2>
|
|
)}
|
|
|
|
<div className="max-w-4xl mx-auto">
|
|
<div
|
|
className={cn(
|
|
'relative rounded-2xl overflow-hidden bg-espresso',
|
|
aspectClasses[aspectRatio]
|
|
)}
|
|
>
|
|
{isPlaying ? (
|
|
<iframe
|
|
src={`${embedUrl}?autoplay=1&rel=0`}
|
|
title={title || 'Video'}
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
allowFullScreen
|
|
className="absolute inset-0 w-full h-full"
|
|
/>
|
|
) : (
|
|
<button
|
|
type="button"
|
|
onClick={() => setIsPlaying(true)}
|
|
className="absolute inset-0 w-full h-full group"
|
|
aria-label="Video abspielen"
|
|
>
|
|
{thumbnailUrl && (
|
|
<Image
|
|
src={thumbnailUrl}
|
|
alt={title || 'Video Thumbnail'}
|
|
fill
|
|
className="object-cover"
|
|
/>
|
|
)}
|
|
|
|
{/* Overlay */}
|
|
<div className="absolute inset-0 bg-espresso/50 group-hover:bg-espresso/40 transition-colors" />
|
|
|
|
{/* Play Button */}
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="w-16 h-16 md:w-20 md:h-20 rounded-full bg-soft-white/90 flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 24 24"
|
|
fill="currentColor"
|
|
className="w-8 h-8 md:w-10 md:h-10 text-brass ml-1"
|
|
>
|
|
<path
|
|
fillRule="evenodd"
|
|
d="M4.5 5.653c0-1.426 1.529-2.33 2.779-1.643l11.54 6.348c1.295.712 1.295 2.573 0 3.285L7.28 19.991c-1.25.687-2.779-.217-2.779-1.643V5.653z"
|
|
clipRule="evenodd"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|