Files
t8y2--dbx/docs/components/landing/ChangelogList.tsx
T
2026-07-13 13:09:14 +08:00

115 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState, useEffect, useRef } from "react";
import type { ChangelogRelease } from "@/lib/changelog";
import { ChevronDown, Tag } from "lucide-react";
const PAGE_SIZE = 5;
const sectionLabels: Record<string, Record<string, string>> = {
added: { en: "New Features", cn: "新功能" },
improved: { en: "Improvements", cn: "改进" },
fixed: { en: "Bug Fixes", cn: "问题修复" },
changed: { en: "Changes", cn: "变更" },
removed: { en: "Removed", cn: "移除" },
};
function formatDate(dateStr: string, lang: string) {
const d = new Date(dateStr);
if (lang === "cn") {
return `${d.getFullYear()}${d.getMonth() + 1}${d.getDate()}日`;
}
return d.toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" });
}
function ReleaseCard({ release, lang }: { release: ChangelogRelease; lang: string }) {
const t = lang === "cn" ? { publishedOn: "发布于", download: "下载", seeGitHub: "查看 GitHub Release 获取详情" } : { publishedOn: "Published on", download: "Download", seeGitHub: "See GitHub Release for details" };
return (
<div className="py-12 border-t border-[rgba(155,176,205,0.18)]">
{/* Header */}
<div className="flex items-center justify-between gap-4 mb-8">
<div className="flex items-center gap-4">
<span className="flex items-center gap-1.5 px-3.5 py-1.5 rounded-full border border-[rgba(155,176,205,0.25)] text-sm font-semibold text-[#e2e8f0]">
<Tag size={13} className="text-[#6ea8ff]" />
{release.tag.replace("v", "")}
</span>
<span className="text-[15px] text-[#64748b]">
{t.publishedOn} {formatDate(release.date, lang)}
</span>
</div>
<a href={`https://github.com/t8y2/dbx/releases/tag/${release.tag}`} target="_blank" rel="noopener noreferrer" className="flex items-center gap-1.5 px-4 py-2 rounded-full border border-[rgba(155,176,205,0.25)] text-sm text-[#e2e8f0] hover:border-[rgba(155,176,205,0.4)] transition-colors">
{t.download}
<ChevronDown size={14} />
</a>
</div>
{/* Title */}
<h2 className="text-[28px] font-[720] text-[#f7fbff] mb-10">Release {release.tag}</h2>
{/* Sections */}
{release.sections.map((section, si) => (
<div key={si} className={si > 0 ? "mt-10" : ""}>
<h3 className="text-xl font-bold text-[#f7fbff] mb-5">{sectionLabels[section.type]?.[lang] || section.title}</h3>
<ul className="space-y-3">
{section.items.map((item, ii) => (
<li key={ii} className="flex gap-3 text-[15px] leading-relaxed text-[#b8c5d6]">
<span className="mt-2 w-1.5 h-1.5 rounded-full bg-[#475569] shrink-0" />
<span>
{item.desc ? (
<>
{item.title}{item.desc}
</>
) : (
item.title
)}
</span>
</li>
))}
</ul>
</div>
))}
{release.sections.length === 0 && <p className="text-[15px] text-[#64748b] italic">{t.seeGitHub}</p>}
</div>
);
}
export function ChangelogList({ releases, lang }: { releases: ChangelogRelease[]; lang: string }) {
const [count, setCount] = useState(PAGE_SIZE);
const sentinelRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const node = sentinelRef.current;
if (!node) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setCount((c) => Math.min(c + PAGE_SIZE, releases.length));
}
},
{ rootMargin: "200px" },
);
observer.observe(node);
return () => observer.disconnect();
}, [releases.length]);
const visible = releases.slice(0, count);
const hasMore = count < releases.length;
return (
<>
{visible.map((release) => (
<ReleaseCard key={release.tag} release={release} lang={lang} />
))}
{hasMore && (
<div ref={sentinelRef} className="flex justify-center py-12 text-[#64748b] text-sm">
{lang === "cn" ? "加载更多版本…" : "Loading more versions…"}
</div>
)}
</>
);
}