Files
spsays-training/components/social/collection-grid-card.tsx
T
2026-06-03 00:49:58 +05:30

89 lines
2.9 KiB
TypeScript

"use client";
/**
* CollectionGridCard — square collection card for the 2-per-row grid view.
*
* • With cover: cover fills the square; bottom-up overlay for legibility.
* • Without cover: the same flat palette-aware surface that post cards
* fall back to — pure white on light themes, palette-specific dark
* contrast on dark themes (see `--quote-card-bg`).
*
* Visibility badge (Globe/Lock) sits top-right. Title + count read
* bottom-centre in white over the cover image's dark overlay; on the
* flat fallback they use `text-foreground` so they invert cleanly with
* the theme.
*/
import Image from "next/image";
import Link from "next/link";
import { Globe, Lock } from "lucide-react";
import { cn } from "@/lib/utils";
import type { Collection } from "@/lib/social";
import { quoteCardBgStyle } from "./post-card-shared";
export function CollectionGridCard({
collection: c,
href,
}: {
collection: Collection;
href: string;
}) {
const hasCover = !!c.cover_url;
return (
<Link
href={href}
className="group relative block aspect-square rounded-2xl overflow-hidden ring-1 ring-foreground/10 hover:ring-foreground/25 hover:shadow-md transition-all select-none"
>
{hasCover ? (
<>
<Image
src={c.cover_url!}
alt=""
fill
sizes="(max-width: 640px) 50vw, 320px"
className="object-cover"
unoptimized
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/30 to-transparent" />
</>
) : (
<div className="absolute inset-0" style={quoteCardBgStyle()} />
)}
{/* Visibility badge — black pill works on both image overlay and
flat surface (white or dark). */}
{c.is_public ? null : (
<div className="absolute top-3 right-3 inline-flex items-center gap-1 rounded-full bg-black/40 backdrop-blur-sm px-2.5 py-1 text-[0.6rem] uppercase tracking-[0.18em] font-medium text-white">
<Lock className="size-3" />
{"Private"}
</div>
)}
{/* Title + count — text colour follows the surface beneath. */}
<div
className={cn(
"absolute inset-0 flex flex-col items-center justify-center text-center px-5 sm:px-7",
hasCover ? "text-white" : "text-foreground",
)}
>
<p
className={cn(
"font-[family-name:var(--font-title)] text-lg sm:text-2xl font-semibold leading-snug line-clamp-4 break-words",
hasCover && "drop-shadow-sm",
)}
>
{c.title}
</p>
<p
className={cn(
"mt-2 text-[0.65rem] uppercase tracking-[0.22em] font-medium",
hasCover ? "text-white/85" : "text-muted-foreground",
)}
>
{c.item_count} post{c.item_count === 1 ? "" : "s"}
</p>
</div>
</Link>
);
}