Amiel's picture
Upload folder using huggingface_hub
676fc08 verified
import { useRef, useEffect, useState, type ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
import copy from "copy-to-clipboard";
import { customAlphabet } from "nanoid";
import {
Download,
Copy,
CopyCheck,
ZoomIn,
ZoomOut,
RefreshCcw,
} from "lucide-react";
import { Button } from "@/components/Internal/Button";
import { downloadFile } from "@/utils/file";
type Props = {
children: ReactNode;
};
const nanoid = customAlphabet("abcdefghijklmnopqrstuvwxyz", 8);
async function loadMermaid(element: HTMLElement, code: string) {
const { default: mermaid } = await import("mermaid");
mermaid.initialize({ startOnLoad: false });
const canParse = await mermaid.parse(code, { suppressErrors: true });
if (canParse) {
await mermaid.render(nanoid(), code).then(({ svg }) => {
element.innerHTML = svg;
});
}
}
function Mermaid({ children }: Props) {
const { t } = useTranslation();
const mermaidContainerRef = useRef<HTMLDivElement>(null);
const [content, setContent] = useState<string>("");
const [waitingCopy, setWaitingCopy] = useState<boolean>(false);
function downloadSvg() {
const target = mermaidContainerRef.current;
if (target) {
downloadFile(target.innerHTML, Date.now() + ".svg", "image/svg+xml");
}
}
const handleCopy = () => {
const target = mermaidContainerRef.current;
if (target) {
setWaitingCopy(true);
copy(content);
setTimeout(() => {
setWaitingCopy(false);
}, 1200);
}
};
useEffect(() => {
const target = mermaidContainerRef.current;
if (target) {
setContent(target.innerText);
loadMermaid(target, target.innerText);
}
}, [children]);
return (
<div className="relative cursor-pointer justify-center w-full overflow-auto rounded">
<TransformWrapper initialScale={2} smooth>
{({ zoomIn, zoomOut, resetTransform }) => (
<>
<div className="absolute top-0 right-0 z-50 flex gap-1 print:hidden">
<Button
className="w-6 h-6"
size="icon"
variant="ghost"
title={t("editor.mermaid.downloadSvg")}
onClick={() => downloadSvg()}
>
<Download />
</Button>
<Button
className="w-6 h-6"
size="icon"
variant="ghost"
title={t("editor.mermaid.copyText")}
onClick={() => handleCopy()}
>
{waitingCopy ? (
<CopyCheck className="h-full w-full text-green-500" />
) : (
<Copy className="h-full w-full" />
)}
</Button>
</div>
<div className="absolute bottom-0 right-0 z-50 flex gap-1 print:hidden">
<Button
className="w-6 h-6"
size="icon"
variant="ghost"
title={t("editor.mermaid.zoomIn")}
onClick={() => zoomIn()}
>
<ZoomIn />
</Button>
<Button
className="w-6 h-6"
size="icon"
variant="ghost"
title={t("editor.mermaid.zoomOut")}
onClick={() => zoomOut()}
>
<ZoomOut />
</Button>
<Button
className="w-6 h-6"
size="icon"
variant="ghost"
title={t("editor.mermaid.resize")}
onClick={() => resetTransform()}
>
<RefreshCcw />
</Button>
</div>
<TransformComponent>
<div className="mermaid" ref={mermaidContainerRef}>
{children}
</div>
</TransformComponent>
</>
)}
</TransformWrapper>
</div>
);
}
export default Mermaid;