improvement changelog pages

This commit is contained in:
Wildan Nursahidan
2025-05-12 19:06:24 +07:00
parent 6aeb790cb8
commit ba2963ae41
8 changed files with 117 additions and 94 deletions

View File

@@ -51,7 +51,7 @@ export function FloatingVersionToc({ versions }: FloatingVersionTocProps) {
};
return (
<div className="fixed bottom-4 right-4 lg:hidden z-50">
<div className="fixed bottom-4 right-4 md:hidden z-50">
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button variant="outline" className="rounded-full shadow-lg px-4 py-2 flex items-center gap-2">

View File

@@ -2,8 +2,9 @@
import { useEffect, useState } from "react";
import { cn, formatDate2 } from "@/lib/utils";
import { History } from "lucide-react";
import { History, PanelLeftOpen, PanelLeftClose } from "lucide-react";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Button } from "@/components/ui/button";
interface VersionTocProps {
versions: Array<{
@@ -14,33 +15,30 @@ interface VersionTocProps {
export function VersionToc({ versions }: VersionTocProps) {
const [activeId, setActiveId] = useState<string | null>(null);
const [collapsed, setCollapsed] = useState(false);
useEffect(() => {
// Handle initial hash
const hash = window.location.hash.slice(1);
if (hash) {
setActiveId(hash);
}
// Set up intersection observer
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const id = entry.target.id;
setActiveId(id);
// Use pushState instead of replaceState to maintain history
window.history.pushState(null, '', `#${id}`);
window.history.pushState(null, "", `#${id}`);
}
});
},
{
threshold: 0.2,
rootMargin: '-20% 0px -60% 0px'
rootMargin: "-20% 0px -60% 0px",
}
);
// Observe version elements
versions.forEach(({ version }) => {
const element = document.getElementById(`v${version}`);
if (element) observer.observe(element);
@@ -50,41 +48,64 @@ export function VersionToc({ versions }: VersionTocProps) {
}, [versions]);
return (
<aside className="lg:flex hidden toc flex-[1.5] min-w-[238px] pt-8 sticky top-16 h-[calc(100vh-4rem)]">
<div className="flex flex-col gap-2 w-full">
<div className="flex items-center gap-2 mb-2">
<History className="w-4 h-4" />
<h3 className="font-medium text-sm">Version History</h3>
</div>
<ScrollArea className="h-full">
<div className="flex flex-col gap-1.5 text-sm dark:text-stone-300/85 text-stone-800 pr-4">
{versions.map(({ version, date }) => (
<a
key={version}
href={`#v${version}`}
className={cn(
"hover:text-foreground transition-colors py-1",
activeId === `v${version}` && "font-medium text-primary"
)}
onClick={(e) => {
e.preventDefault();
const element = document.getElementById(`v${version}`);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
setActiveId(`v${version}`);
window.history.pushState(null, '', `#v${version}`);
}
}}
>
v{version}
<span className="text-xs text-muted-foreground ml-2">
{formatDate2(date)}
</span>
</a>
))}
</div>
</ScrollArea>
<aside
className={cn(
"sticky top-16 h-[calc(100vh-4rem)] border-r bg-background transition-all duration-300 z-20 hidden md:flex",
collapsed ? "w-[48px]" : "w-[250px]"
)}
>
{/* Toggle Button */}
<div className="absolute top-0 right-0 py-2 px-0 ml-6 z-30">
<Button
size="icon"
variant="outline"
className="hover:bg-transparent hover:text-inherit border-none text-muted-foreground"
onClick={() => setCollapsed((prev) => !prev)}
>
{collapsed ? <PanelLeftOpen size={18} /> : <PanelLeftClose size={18} />}
</Button>
</div>
{/* Content */}
{!collapsed && (
<div className="flex flex-col gap-2 w-full pt-8 pr-2">
<div className="flex mb-2">
<h2 className="font-semibold text-lg">Changelog</h2>
</div>
<div className="flex items-center gap-2 mb-2">
<History className="w-4 h-4" />
<h3 className="font-medium text-sm">Version History</h3>
</div>
<ScrollArea className="h-full pr-2">
<div className="flex flex-col gap-1.5 text-sm dark:text-stone-300/85 text-stone-800 pr-4">
{versions.map(({ version, date }) => (
<a
key={version}
href={`#v${version}`}
className={cn(
"hover:text-foreground transition-colors py-1",
activeId === `v${version}` && "font-medium text-primary"
)}
onClick={(e) => {
e.preventDefault();
const element = document.getElementById(`v${version}`);
if (element) {
element.scrollIntoView({ behavior: "smooth" });
setActiveId(`v${version}`);
window.history.pushState(null, "", `#v${version}`);
}
}}
>
v{version}
<span className="text-xs text-muted-foreground ml-2">
{formatDate2(date)}
</span>
</a>
))}
</div>
</ScrollArea>
</div>
)}
</aside>
);
}

View File

@@ -9,7 +9,7 @@ import {
} from "@/components/ui/sheet";
import { Logo, NavMenu } from "@/components/navbar";
import { Button } from "@/components/ui/button";
import { AlignLeftIcon, ArrowLeftFromLine, ArrowRightFromLine } from "lucide-react";
import { AlignLeftIcon, PanelLeftClose, PanelLeftOpen } from "lucide-react";
import { FooterButtons } from "@/components/footer";
import { DialogTitle } from "@/components/ui/dialog";
import { ScrollArea } from "@/components/ui/scroll-area";
@@ -22,7 +22,7 @@ export function Leftbar() {
return (
<aside
className={`sticky lg:flex hidden top-16 h-[calc(100vh-4rem)] border-r bg-background transition-all duration-300
${collapsed ? "w-[0px]" : "w-[250px]"} flex flex-col pr-2`}
${collapsed ? "w-[48px]" : "w-[250px]"} flex flex-col pr-2`}
>
{/* Toggle Button */}
<div className="absolute top-0 right-0 py-2 px-0 ml-6 z-10">
@@ -33,9 +33,9 @@ export function Leftbar() {
onClick={() => setCollapsed((prev) => !prev)}
>
{collapsed ? (
<ArrowRightFromLine size={18} />
<PanelLeftOpen size={18} />
) : (
<ArrowLeftFromLine size={18} />
<PanelLeftClose size={18} />
)}
</Button>
</div>