88 lines
3.4 KiB
TypeScript
88 lines
3.4 KiB
TypeScript
"use client";
|
|
import { useEffect, useState, useRef } from "react";
|
|
import mermaid from "mermaid";
|
|
import { motion, AnimatePresence } from "framer-motion";
|
|
import { Maximize2, Minimize2 } from "lucide-react";
|
|
|
|
export default function Mermaid({ chart }: { chart: string }) {
|
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
const [needsExpansion, setNeedsExpansion] = useState(false);
|
|
const contentRef = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
mermaid.initialize({
|
|
startOnLoad: true,
|
|
theme: "dark",
|
|
securityLevel: "loose",
|
|
fontFamily: "monospace",
|
|
});
|
|
mermaid.contentLoaded();
|
|
|
|
// Check if the rendered diagram is taller than 400px
|
|
if (contentRef.current) {
|
|
const height = contentRef.current.scrollHeight;
|
|
setNeedsExpansion(height > 400);
|
|
}
|
|
}, [chart]);
|
|
|
|
return (
|
|
<div className="relative max-w-4xl mx-auto group">
|
|
<motion.div
|
|
initial={false}
|
|
onClick={() => needsExpansion && setIsExpanded(!isExpanded)}
|
|
animate={{ height: isExpanded || !needsExpansion ? "auto" : "400px" }}
|
|
className={`relative bg-neutral-900/20 rounded-3xl border border-neutral-800/50 p-4 md:p-12 overflow-hidden transition-colors duration-500
|
|
${needsExpansion && !isExpanded ? "cursor-pointer hover:border-neutral-700" : "cursor-default"}`}
|
|
>
|
|
{/* Legend */}
|
|
<div className="absolute top-6 right-8 flex flex-col gap-2 z-20 bg-black/40 backdrop-blur-md p-3 rounded-xl border border-white/5">
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-2 h-2 rounded-full bg-blue-500" />
|
|
<span className="text-[9px] font-mono text-neutral-400 uppercase tracking-wider">Traffic Flow</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-2 h-2 rounded-full bg-green-500" />
|
|
<span className="text-[9px] font-mono text-neutral-400 uppercase tracking-wider">Service Node</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div ref={contentRef} className="mermaid flex justify-center">
|
|
{chart}
|
|
</div>
|
|
|
|
{/* The "Fade to Darkness" Overlay - only show if needs expansion */}
|
|
<AnimatePresence>
|
|
{needsExpansion && !isExpanded && (
|
|
<motion.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
className="absolute inset-x-0 bottom-0 h-40 bg-gradient-to-t from-[#0a0a0a] via-[#0a0a0a]/80 to-transparent pointer-events-none"
|
|
/>
|
|
)}
|
|
</AnimatePresence>
|
|
</motion.div>
|
|
|
|
{/* Expand/Collapse Button - only show if needs expansion */}
|
|
{needsExpansion && (
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
setIsExpanded(!isExpanded);
|
|
}}
|
|
className={`absolute -bottom-5 left-1/2 -translate-x-1/2 z-30 flex items-center gap-2 px-5 py-2.5 rounded-full text-[10px] font-mono uppercase tracking-[0.2em] transition-all border shadow-xl
|
|
${isExpanded
|
|
? "bg-neutral-800 border-neutral-700 text-white"
|
|
: "bg-neutral-900 border-neutral-800 text-neutral-500 group-hover:bg-neutral-800 group-hover:border-neutral-600 group-hover:text-white"
|
|
}`}
|
|
>
|
|
{isExpanded ? (
|
|
<> <Minimize2 size={12} /> Collapse Logic </>
|
|
) : (
|
|
<> <Maximize2 size={12} /> Expand Architecture </>
|
|
)}
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
} |