Publish a slot catalog so the dashboard can import keys, and keep local placeholders when a slot is empty. Co-authored-by: Cursor <cursoragent@cursor.com>
113 lines
4.7 KiB
TypeScript
113 lines
4.7 KiB
TypeScript
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import { SlotImage } from "@/components/SlotImage";
|
|
import { banners3, banners4 } from "@/lib/data";
|
|
import type { StaticImageWithUrl } from "@/lib/static-images";
|
|
import { slotAlt, slotAspectStyle } from "@/lib/static-images";
|
|
import { Media } from "./Media";
|
|
|
|
const banner4Overlay = {
|
|
background: "linear-gradient(to top, rgba(0,0,0,0.88) 0%, rgba(0,0,0,0.55) 45%, transparent 100%)",
|
|
};
|
|
|
|
type BannerSlotProps = {
|
|
images?: StaticImageWithUrl[];
|
|
aspectRatio?: string;
|
|
};
|
|
|
|
export function BannerGrid4({ images = [], aspectRatio }: BannerSlotProps) {
|
|
return (
|
|
<section className="container-site mt-12">
|
|
<div className="grid grid-cols-2 gap-3 md:gap-4">
|
|
{images.length > 0
|
|
? images.map((image) => {
|
|
const title = image.titleFa?.trim();
|
|
const english = image.titleEn?.trim();
|
|
const subtitle = image.subtext?.trim();
|
|
const hasCopy = Boolean(title || english || subtitle);
|
|
const frame = (
|
|
<div
|
|
className="relative overflow-hidden rounded-2xl"
|
|
style={slotAspectStyle(aspectRatio, "21:9")}
|
|
>
|
|
<SlotImage image={{ ...image, linkUrl: null }} fill alt={slotAlt(image)} />
|
|
{hasCopy ? (
|
|
<>
|
|
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-[70%]" style={banner4Overlay} />
|
|
<div className="pointer-events-none absolute inset-x-0 bottom-3 md:bottom-4 px-3 text-center text-white">
|
|
{title ? (
|
|
<p className="text-base md:text-xl font-extrabold leading-none">{title}</p>
|
|
) : null}
|
|
{english ? (
|
|
<p
|
|
dir="ltr"
|
|
className="mt-0.5 font-carter text-[9px] md:text-[10px] tracking-[0.4em] text-brand uppercase"
|
|
>
|
|
{english}
|
|
</p>
|
|
) : null}
|
|
{subtitle ? (
|
|
<p className="mt-0.5 text-sm md:text-base leading-tight text-white/90">{subtitle}</p>
|
|
) : null}
|
|
</div>
|
|
</>
|
|
) : null}
|
|
</div>
|
|
);
|
|
return image.linkUrl ? (
|
|
<a key={image.id} href={image.linkUrl} className="block">
|
|
{frame}
|
|
</a>
|
|
) : (
|
|
<div key={image.id}>{frame}</div>
|
|
);
|
|
})
|
|
: banners4.map((banner) => (
|
|
<Link key={banner.href} href={banner.href} className="relative block overflow-hidden rounded-2xl">
|
|
<div className="relative aspect-[21/9] min-h-[140px]">
|
|
<Image src={banner.image} alt={banner.title} fill sizes="50vw" className="object-cover object-center" />
|
|
</div>
|
|
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-[70%]" style={banner4Overlay} />
|
|
<div className="absolute inset-x-0 bottom-3 md:bottom-4 px-3 text-center text-white">
|
|
<p className="text-base md:text-xl font-extrabold leading-none">{banner.title}</p>
|
|
<p
|
|
dir="ltr"
|
|
className="mt-0.5 font-carter text-[9px] md:text-[10px] tracking-[0.4em] text-brand uppercase"
|
|
>
|
|
{banner.english}
|
|
</p>
|
|
<p className="mt-0.5 text-sm md:text-base leading-tight text-white/90">{banner.subtitle}</p>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
export function BannerRow3({ images = [], aspectRatio }: BannerSlotProps) {
|
|
return (
|
|
<section className="container-site mt-12">
|
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 md:gap-4">
|
|
{images.length > 0
|
|
? images.map((image) => (
|
|
<div key={image.id} className="overflow-hidden rounded-2xl">
|
|
<SlotImage
|
|
image={image}
|
|
alt={slotAlt(image)}
|
|
imgClassName="h-auto w-full object-cover"
|
|
aspectRatio={aspectRatio}
|
|
fallbackAspect="12:5"
|
|
/>
|
|
</div>
|
|
))
|
|
: banners3.map((banner) => (
|
|
<Link key={banner.href} href={banner.href} className="overflow-hidden rounded-2xl">
|
|
<Media src={banner.image} alt={banner.alt} className="aspect-[2.4/1]" sizes="33vw" />
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|