157 lines
6.3 KiB
TypeScript
157 lines
6.3 KiB
TypeScript
'use client';
|
|
|
|
import { Button } from '@/components/ui/Button';
|
|
import Image from 'next/image';
|
|
import { useState, useEffect } from 'react';
|
|
import { usePathname } from 'next/navigation';
|
|
|
|
interface HeroVideoProps {
|
|
data?: any;
|
|
title?: string;
|
|
subtitle?: string;
|
|
description?: string;
|
|
videoUrl?: string;
|
|
posterImage?: any;
|
|
backgroundImage?: any;
|
|
ctaLabel?: string;
|
|
ctaHref?: string;
|
|
linkText?: string;
|
|
linkHref?: string;
|
|
secondaryCtaLabel?: string;
|
|
secondaryCtaHref?: string;
|
|
badge?: string;
|
|
}
|
|
|
|
import placeholders from '@/lib/placeholders.json';
|
|
|
|
export function HeroVideo(props: HeroVideoProps) {
|
|
const { data } = props;
|
|
const title = props.title || data?.title || 'DIE EXPERTEN FÜR KABELNETZBAU';
|
|
const subtitle = props.subtitle || props.description || data?.subtitle || 'Wir verbinden Infrastruktur mit Präzision. Von Horizontalbohrungen bis zu komplexen Leitungsnetzen.';
|
|
const explicitNoVideo = props.videoUrl === 'none' || props.videoUrl === '';
|
|
const videoUrl = explicitNoVideo ? null : (props.videoUrl || data?.videoUrl || '/assets/dummy-hero.mp4');
|
|
const pathname = usePathname();
|
|
|
|
let defaultPoster = "/assets/photos/DJI_0048.JPG";
|
|
if (videoUrl && videoUrl.endsWith('.mp4')) {
|
|
defaultPoster = videoUrl.replace('.mp4', '-poster.webp');
|
|
}
|
|
|
|
const posterSrc = props.backgroundImage?.url || props.posterImage?.url || data?.posterImage?.url || defaultPoster;
|
|
const posterAlt = props.backgroundImage?.alt || data?.posterImage?.alt || "E-TIB Gruppe Baustelle";
|
|
|
|
const ctaLabel = props.ctaLabel || props.linkText || data?.ctaLabel || 'Unternehmen entdecken';
|
|
const ctaHref = props.ctaHref || props.linkHref || data?.ctaHref || '#unternehmen';
|
|
|
|
const currentLocale = pathname?.startsWith('/en') ? 'en' : 'de';
|
|
|
|
const secondaryCtaLabel = props.secondaryCtaLabel || data?.secondaryCtaLabel || (currentLocale === 'de' ? 'Projekt anfragen' : 'Inquire Project');
|
|
const secondaryCtaHref = props.secondaryCtaHref || data?.secondaryCtaHref || `/${currentLocale}/contact`;
|
|
|
|
const [videoSrcLoaded, setVideoSrcLoaded] = useState(false);
|
|
useEffect(() => {
|
|
let interactionTriggered = false;
|
|
|
|
const handleInteraction = () => {
|
|
if (interactionTriggered) return;
|
|
interactionTriggered = true;
|
|
setVideoSrcLoaded(true);
|
|
|
|
// Cleanup listeners
|
|
['scroll', 'mousemove', 'touchstart', 'keydown'].forEach((event) =>
|
|
window.removeEventListener(event, handleInteraction)
|
|
);
|
|
};
|
|
|
|
// Listen for any user interaction
|
|
['scroll', 'mousemove', 'touchstart', 'keydown'].forEach((event) =>
|
|
window.addEventListener(event, handleInteraction, { passive: true, once: true })
|
|
);
|
|
|
|
return () => {
|
|
['scroll', 'mousemove', 'touchstart', 'keydown'].forEach((event) =>
|
|
window.removeEventListener(event, handleInteraction)
|
|
);
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<div className="relative w-full h-[85svh] md:h-[100svh] flex items-center justify-center overflow-hidden bg-neutral-dark">
|
|
{/* Background color while video loads */}
|
|
<div className="absolute inset-0 z-0 bg-neutral-dark" />
|
|
|
|
<img
|
|
key={`img-${pathname}-${posterSrc}`}
|
|
src={posterSrc}
|
|
srcSet={posterSrc.includes('-poster.webp') ? `${posterSrc.replace('-poster.webp', '-poster-mobile.webp')} 800w, ${posterSrc} 1920w` : undefined}
|
|
sizes={posterSrc.includes('-poster.webp') ? "100vw" : undefined}
|
|
alt={posterAlt}
|
|
className="absolute inset-0 w-full h-full object-cover z-[1] pointer-events-none"
|
|
decoding="sync"
|
|
fetchPriority="high"
|
|
/>
|
|
{/* Fast compositing layer to replace the heavy CSS filters on the image */}
|
|
<div className="absolute inset-0 bg-black/20 z-[1] pointer-events-none mix-blend-multiply" />
|
|
|
|
{/* Render video on top if available, but defer src to avoid blocking LCP */}
|
|
{videoUrl && (
|
|
<video
|
|
key={`${pathname}-${videoUrl}`}
|
|
className={`absolute inset-0 w-full h-full object-cover z-[2] pointer-events-none filter contrast-125 saturate-110 brightness-90 transition-opacity duration-1000 ${videoSrcLoaded ? 'opacity-100' : 'opacity-0'}`}
|
|
src={videoSrcLoaded ? videoUrl : undefined}
|
|
autoPlay
|
|
muted
|
|
loop
|
|
playsInline
|
|
preload="none"
|
|
/>
|
|
)}
|
|
|
|
{/* Cinematic Color Grading Overlay */}
|
|
<div className="absolute inset-0 bg-[#0a192f]/30 z-[2] mix-blend-multiply" />
|
|
|
|
{/* Dramatic Vignette & Fade to content */}
|
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-transparent via-black/20 to-black/80 z-[2]" />
|
|
<div className="absolute inset-0 bg-gradient-to-t from-neutral-dark via-neutral-dark/60 to-transparent z-[3]" />
|
|
|
|
{/* Top Fade for Header Navigation Readability */}
|
|
<div className="absolute top-0 left-0 w-full h-48 bg-gradient-to-b from-black/70 to-transparent z-[3] pointer-events-none" />
|
|
|
|
<div className="container relative z-[4] text-center px-4">
|
|
<div
|
|
key={`hero-content-${pathname}`}
|
|
className="relative"
|
|
>
|
|
<h1
|
|
className="font-heading font-bold text-3xl sm:text-4xl md:text-6xl lg:text-8xl text-white mb-6 uppercase tracking-tight drop-shadow-lg"
|
|
dangerouslySetInnerHTML={{ __html: title.replace(/KABELNETZBAU|Kabelnetzbau/g, '<span class="text-primary-light">Kabelnetzbau</span>').replace(/KABELNETZBAU|Kabelnetzbau/g, '<span class="text-primary-light">Kabelnetzbau</span>').replace(/\\n|\n/g, '<br />') }}
|
|
/>
|
|
|
|
<p className="text-lg md:text-xl lg:text-2xl text-white/90 max-w-3xl mx-auto mb-12 drop-shadow">
|
|
{subtitle}
|
|
</p>
|
|
|
|
<div className="flex flex-wrap items-center justify-center gap-4 md:gap-6">
|
|
<Button
|
|
href={ctaHref}
|
|
variant="primary"
|
|
size="lg"
|
|
>
|
|
{ctaLabel}
|
|
</Button>
|
|
|
|
<Button
|
|
href={secondaryCtaHref}
|
|
variant="outline"
|
|
size="lg"
|
|
className="border-white/60 text-white hover:text-primary hover:bg-white"
|
|
>
|
|
{secondaryCtaLabel}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|