"use client"; import { Button } from "@/components/ui/Button"; import { Link } from "@/i18n/routing"; import type { HeroBanner } from "@/lib/demo-data"; import { cn } from "@/lib/utils"; import { useTranslations } from "next-intl"; import { useCallback, useEffect, useRef, useState } from "react"; function isInternalHref(href: string) { return href.startsWith("/") && !href.startsWith("//"); } function BannerLink({ href, children, }: { href: string; children: React.ReactNode; }) { if (!href) return null; if (isInternalHref(href)) { return {children}; } return ( {children} ); } export function HeroCarousel({ banners }: { banners: HeroBanner[] }) { const t = useTranslations("hero"); const meta = useTranslations("meta"); const slides = banners.length ? banners : []; const [index, setIndex] = useState(0); const [paused, setPaused] = useState(false); const [playingId, setPlayingId] = useState(null); const videoRefs = useRef>({}); const current = slides[index] ?? slides[0]; const count = slides.length; const autoplayBlocked = paused || Boolean(playingId); const stopVideos = useCallback(() => { for (const video of Object.values(videoRefs.current)) { if (!video) continue; video.pause(); video.currentTime = 0; } setPlayingId(null); }, []); const go = useCallback( (next: number) => { if (!count) return; stopVideos(); setIndex((next + count) % count); }, [count, stopVideos], ); useEffect(() => { if (count < 2 || autoplayBlocked) return; const reduce = document.documentElement.dataset.motion === "reduce" || window.matchMedia("(prefers-reduced-motion: reduce)").matches; if (reduce) return; const duration = Math.max(4000, current?.durationMs || 7000); const timer = window.setTimeout(() => go(index + 1), duration); return () => window.clearTimeout(timer); }, [count, autoplayBlocked, current?.durationMs, go, index]); const toggleVideo = useCallback( (slide: HeroBanner) => { if (!slide.video) return; const video = videoRefs.current[slide.id]; if (!video) return; if (playingId === slide.id && !video.paused) { video.pause(); setPlayingId(null); return; } void video.play().then(() => setPlayingId(slide.id)).catch(() => setPlayingId(null)); }, [playingId], ); if (!current) return null; return (
setPaused(true)} onMouseLeave={() => setPaused(false)} onFocusCapture={() => setPaused(true)} onBlurCapture={(event) => { if (!event.currentTarget.contains(event.relatedTarget as Node | null)) { setPaused(false); } }} > {slides.map((slide, i) => ( // eslint-disable-next-line @next/next/no-img-element ))}

{current.eyebrow || t("brand")}

{meta("fullName")}

{current.title}

{current.subtitle ? (

{current.subtitle}

) : null}
{current.ctaPrimaryLabel ? ( ) : null} {current.ctaSecondaryLabel ? ( ) : null}
{(current.phone || current.address) && (

{current.phone ? ( {current.phone} ) : null} {current.phone && current.address ? ( · ) : null} {current.address}

)}
{slides.map((slide, i) => { const active = i === index; const playing = playingId === slide.id && active; return (
{slide.video ? (
); })}
{count > 1 ? (
{slides.map((slide, i) => (
) : null}
); }