docu version 1.8.5
This commit is contained in:
@@ -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">
|
||||
|
||||
@@ -24,13 +24,13 @@ interface VersionEntryProps {
|
||||
isLast?: boolean;
|
||||
}
|
||||
|
||||
export function VersionEntry({
|
||||
version,
|
||||
date,
|
||||
export function VersionEntry({
|
||||
version,
|
||||
date,
|
||||
description,
|
||||
image,
|
||||
image,
|
||||
changes,
|
||||
isLast
|
||||
isLast
|
||||
}: VersionEntryProps) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
@@ -45,7 +45,7 @@ export function VersionEntry({
|
||||
{formatDate2(date)}
|
||||
</time>
|
||||
</div>
|
||||
|
||||
|
||||
{description && (
|
||||
<p className="text-dark text-xl">{description}</p>
|
||||
)}
|
||||
@@ -69,8 +69,8 @@ export function VersionEntry({
|
||||
<div className="space-y-6">
|
||||
{Object.entries(changes).map(([type, items]) => (
|
||||
items && items.length > 0 && (
|
||||
<ChangeGroup
|
||||
key={type}
|
||||
<ChangeGroup
|
||||
key={type}
|
||||
type={type as keyof typeof changes}
|
||||
changes={items}
|
||||
expanded={expanded}
|
||||
@@ -82,10 +82,10 @@ export function VersionEntry({
|
||||
{/* Show more/less button */}
|
||||
{Object.values(changes).some(items => items && items.length > 5) && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
className="mt-4 text-muted-foreground hover:text-foreground"
|
||||
className="mt-4 text-muted-foreground hover:bg-transparent hover:text-accent border-none"
|
||||
>
|
||||
{expanded ? (
|
||||
<>
|
||||
@@ -110,4 +110,4 @@ export function VersionEntry({
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user