ai-chatbot/components/home/lovart/lovart-featured-gallery.tsx

126 lines
5.2 KiB
TypeScript

import { Gem, Heart, Loader2, PlayCircle } from "lucide-react";
import type { Ref } from "react";
import type { FeaturedImage } from "./types";
type LovartFeaturedGalleryProps = {
activeCategory: string;
categories: string[];
featuredItems: FeaturedImage[];
featuredLoadMoreRef: Ref<HTMLDivElement>;
featuredSectionRef: Ref<HTMLElement>;
isLoadingFeatured: boolean;
isLoadingMoreFeatured: boolean;
setActiveCategory: (category: string) => void;
};
function getFeaturedPreviewUrl(item: FeaturedImage) {
const source = item.thumbnailUrl || item.originalUrl;
if (!source || !/^https:\/\/yybb\.top\//i.test(source) || source.includes("?")) {
return source;
}
return `${source}?imageView2/2/w/900/q/78`;
}
export function LovartFeaturedGallery({
activeCategory,
categories,
featuredItems,
featuredLoadMoreRef,
featuredSectionRef,
isLoadingFeatured,
isLoadingMoreFeatured,
setActiveCategory,
}: LovartFeaturedGalleryProps) {
return (
<section ref={featuredSectionRef} className="mt-14">
<h2 className="text-[16px] font-semibold leading-none text-[#1f1f1f] dark:text-white">
灵感发现
</h2>
<div className="mt-4 flex flex-wrap items-center gap-x-3 gap-y-2">
{categories.map((category) => (
<button
key={category}
type="button"
onClick={() => setActiveCategory(category)}
className={`h-7 rounded-[7px] px-3 text-[12px] font-normal leading-none transition-colors duration-150 ${
activeCategory === category
? "bg-[#f2f2f2] text-[#202020] dark:bg-[#24242a] dark:text-white"
: "text-[#5f5f5f] hover:bg-[#f7f7f7] hover:text-[#202020] dark:text-white/55 dark:hover:bg-[#17171b]"
}`}
>
{category}
</button>
))}
</div>
{featuredItems.length === 0 ? (
<div className="mt-[22px] rounded-[10px] border border-dashed border-black/15 bg-[#f7f7f7] p-10 text-center text-[14px] text-black/35 dark:border-white/12 dark:bg-[#141416] dark:text-white/35">
{isLoadingFeatured ? "灵感内容加载中..." : "当前没有可展示的灵感数据。"}
</div>
) : (
<div className="mt-[14px] columns-1 gap-[10px] md:columns-2 lg:columns-5">
{featuredItems.map((item, index) => (
<button
key={item.id}
type="button"
onClick={() => window.open(item.originalUrl || item.thumbnailUrl, "_blank")}
className="group mb-[24px] block w-full cursor-pointer break-inside-avoid text-left"
>
<img
src={getFeaturedPreviewUrl(item)}
alt={item.prompt || "灵感图"}
loading="lazy"
decoding="async"
onError={(event) => {
const image = event.currentTarget;
const fallbackUrl = item.originalUrl || item.thumbnailUrl;
if (!fallbackUrl || image.dataset.fallbackApplied === "true") {
return;
}
image.dataset.fallbackApplied = "true";
image.src = fallbackUrl;
}}
className="block h-auto w-full rounded-[6px] bg-[#e8e8e8] object-cover transition-transform duration-150 ease-out will-change-transform group-hover:scale-[1.008] dark:bg-[#1b1c20]"
/>
<div className="mt-[8px] flex h-[22px] items-center justify-between text-[11px] font-normal leading-none text-[#b8b8b8] dark:text-white/45">
<div className="flex min-w-0 items-center gap-[6px] pr-2">
<span className="flex h-[16px] w-[16px] shrink-0 items-center justify-center overflow-hidden rounded-full bg-[#d8d8d8] text-[8px] font-medium text-white">
{(item.user?.name || "C").slice(0, 1).toUpperCase()}
</span>
<span className="line-clamp-1 text-[13px] font-normal leading-none text-[#4b4b4b] dark:text-white/80">
{item.user?.name || "创作者"}
</span>
</div>
<div className="inline-flex shrink-0 items-center gap-[7px] text-[11px] leading-none">
<span className="inline-flex items-center gap-[4px]">
<PlayCircle className="h-[11px] w-[11px]" strokeWidth={2} />
{(1200 + index * 37).toLocaleString()}
</span>
<span className="inline-flex items-center gap-[4px]">
<Heart className="h-[11px] w-[11px]" strokeWidth={2} />
{(20 + (index % 9) * 11).toLocaleString()}
</span>
<Gem className="h-[11px] w-[11px]" strokeWidth={2} />
</div>
</div>
</button>
))}
</div>
)}
<div ref={featuredLoadMoreRef} className="mt-3 flex h-10 items-center justify-center">
{isLoadingMoreFeatured ? (
<div className="inline-flex items-center gap-2 text-sm text-black/45 dark:text-white/45">
<Loader2 className="h-3.5 w-3.5 animate-spin" />
正在加载更多灵感...
</div>
) : null}
</div>
</section>
);
}