Files
1037cfa6-1220-4959-b72b-25a…/src/components/sections/hero/HeroShowcaseSplitOverlay.tsx
2025-12-28 15:39:53 +02:00

215 lines
7.5 KiB
TypeScript

"use client";
import { Fragment } from "react";
import MediaContent from "@/components/shared/MediaContent";
import TextAnimation from "@/components/text/TextAnimation";
import Button from "@/components/button/Button";
import { cls } from "@/lib/utils";
import { getButtonProps } from "@/lib/buttonUtils";
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
import type { ButtonConfig } from "@/types/button";
interface HeroShowcaseSplitOverlayProps {
title: string;
description: string;
tags: string[];
buttons: ButtonConfig[];
showcaseImageSrc?: string;
showcaseVideoSrc?: string;
showcaseImageAlt?: string;
showcaseVideoAriaLabel?: string;
imageSrc?: string;
videoSrc?: string;
imageAlt?: string;
videoAriaLabel?: string;
showDimOverlay?: boolean;
ariaLabel?: string;
className?: string;
containerClassName?: string;
titleClassName?: string;
descriptionClassName?: string;
tagsContainerClassName?: string;
tagClassName?: string;
buttonContainerClassName?: string;
buttonClassName?: string;
buttonTextClassName?: string;
showcaseWrapperClassName?: string;
showcaseImageClassName?: string;
mediaWrapperClassName?: string;
imageClassName?: string;
dimOverlayClassName?: string;
}
const HeroShowcaseSplitOverlay = ({
title,
description,
tags,
buttons,
showcaseImageSrc,
showcaseVideoSrc,
showcaseImageAlt = "",
showcaseVideoAriaLabel = "Showcase video",
imageSrc,
videoSrc,
imageAlt = "",
videoAriaLabel = "Hero video",
showDimOverlay = false,
ariaLabel = "Hero section",
className = "",
containerClassName = "",
titleClassName = "",
descriptionClassName = "",
tagsContainerClassName = "",
tagClassName = "",
buttonContainerClassName = "",
buttonClassName = "",
buttonTextClassName = "",
showcaseWrapperClassName = "",
showcaseImageClassName = "",
mediaWrapperClassName = "",
imageClassName = "",
dimOverlayClassName = "",
}: HeroShowcaseSplitOverlayProps) => {
const theme = useTheme();
return (
<section
aria-label={ariaLabel}
className={cls("relative w-full h-fit md:h-svh overflow-hidden", className)}
>
{/* Background Media with mask fade */}
<div className={cls("absolute inset-0 w-full h-full opacity-50 mask-fade-bottom-long", mediaWrapperClassName)}>
{showDimOverlay && (
<div className={cls("absolute inset-0 z-[1] bg-background/20", dimOverlayClassName)} />
)}
<MediaContent
imageSrc={imageSrc}
videoSrc={videoSrc}
imageAlt={imageAlt}
videoAriaLabel={videoAriaLabel}
imageClassName={cls("w-full h-full object-cover !rounded-none", imageClassName)}
/>
</div>
{/* Mobile Layout */}
<div className={cls(
"relative z-10 w-content-width mx-auto h-full flex md:hidden flex-col gap-10 justify-between pt-hero-page-padding-1_5 pb-hero-page-padding",
containerClassName
)}>
<div className="relative flex flex-col gap-6 w-full">
<TextAnimation
type={theme.defaultTextAnimation}
text={title}
variant="words-trigger"
className={cls("text-5xl font-medium text-foreground text-balance text-start leading-[1]", titleClassName)}
/>
<div className={cls("flex gap-4", buttonContainerClassName)}>
{buttons.slice(0, 2).map((button, index) => (
<Button key={`${button.text}-${index}`} {...getButtonProps(button, index, theme.defaultButtonVariant, buttonClassName, buttonTextClassName)} />
))}
</div>
</div>
<div className="relative w-full">
<TextAnimation
type={theme.defaultTextAnimation}
text={description}
variant="words-trigger"
className={cls("text-base text-foreground/75 text-balance text-start leading-tight", descriptionClassName)}
/>
</div>
<div className="flex flex-col gap-6">
<div className={cls(
"relative w-full aspect-square overflow-hidden rounded-theme-capped card p-4",
showcaseWrapperClassName
)}>
<MediaContent
imageSrc={showcaseImageSrc}
videoSrc={showcaseVideoSrc}
imageAlt={showcaseImageAlt}
videoAriaLabel={showcaseVideoAriaLabel}
imageClassName={cls("h-full w-full object-cover z-1", showcaseImageClassName)}
/>
</div>
<div className={cls("w-full card rounded-theme-capped flex flex-row items-center justify-between gap-2 px-6 py-3", tagsContainerClassName)}>
{tags.slice(0, 4).map((tag, index) => (
<Fragment key={`${tag}-${index}`}>
<span className={cls("text-sm text-foreground truncate", tagClassName)}>
{tag}
</span>
{index < Math.min(tags.length, 4) - 1 && (
<span className="text-accent">·</span>
)}
</Fragment>
))}
</div>
</div>
</div>
{/* Desktop Layout */}
<div className={cls(
"relative z-10 w-content-width mx-auto h-full hidden md:flex flex-col justify-between pt-hero-page-padding-1_5 pb-hero-page-padding",
containerClassName
)}>
<div className="relative w-full flex flex-row gap-[var(--width-10)] 2xl:gap-[var(--width-15)] items-center">
<div className={cls(
"relative w-2/5 aspect-video overflow-hidden rounded-theme-capped card p-4",
showcaseWrapperClassName
)}>
<MediaContent
imageSrc={showcaseImageSrc}
videoSrc={showcaseVideoSrc}
imageAlt={showcaseImageAlt}
videoAriaLabel={showcaseVideoAriaLabel}
imageClassName={cls("h-full w-full object-cover z-1", showcaseImageClassName)}
/>
</div>
<div className="relative flex flex-col gap-6 w-3/5">
<TextAnimation
type={theme.defaultTextAnimation}
text={title}
variant="words-trigger"
className={cls("text-7xl font-medium text-foreground text-balance text-start leading-[1]", titleClassName)}
/>
<div className={cls("flex gap-4", buttonContainerClassName)}>
{buttons.slice(0, 2).map((button, index) => (
<Button key={`${button.text}-${index}`} {...getButtonProps(button, index, theme.defaultButtonVariant, buttonClassName, buttonTextClassName)} />
))}
</div>
</div>
</div>
<div className="relative w-full flex flex-row gap-10 items-end justify-between">
<div className={cls("flex flex-col gap-2", tagsContainerClassName)}>
{tags.slice(0, 6).map((tag, index) => (
<span
key={`${tag}-${index}`}
className={cls("text-base text-foreground", tagClassName)}
>
{tag}
</span>
))}
</div>
<div className="relative w-1/2">
<TextAnimation
type={theme.defaultTextAnimation}
text={description}
variant="words-trigger"
start="top 100%"
className={cls("text-lg text-foreground/75 text-balance text-start leading-[1.4]", descriptionClassName)}
/>
</div>
</div>
</div>
</section>
);
};
HeroShowcaseSplitOverlay.displayName = "HeroShowcaseSplitOverlay";
export default HeroShowcaseSplitOverlay;