diff --git a/playground/CustomTest.tsx b/playground/CustomTest.tsx
new file mode 100644
index 00000000..87541b65
--- /dev/null
+++ b/playground/CustomTest.tsx
@@ -0,0 +1,86 @@
+import React from "react";
+
+import { useExcalidraw } from "./ExcalidrawWrapper.tsx";
+import { Mermaid } from "./Mermaid.tsx";
+
+function CustomTest() {
+ const excalidraw = useExcalidraw();
+ const [parsedMermaid, setParsedMermaid] = React.useState<{
+ data: string | null;
+ error: string | null;
+ definition: string | null;
+ }>({
+ data: null,
+ error: null,
+ definition: null,
+ });
+
+ return (
+ <>
+
+
+ {parsedMermaid.definition && (
+
+ )}
+
+ {parsedMermaid.data && (
+
+ {"Parsed data from parseMermaid"}
+ {parsedMermaid.data}
+
+ )}
+
+ {parsedMermaid.error && {parsedMermaid.error}
}
+ >
+ );
+}
+
+export default CustomTest;
diff --git a/playground/ExcalidrawWrapper.tsx b/playground/ExcalidrawWrapper.tsx
index c2cf339f..99c829a3 100644
--- a/playground/ExcalidrawWrapper.tsx
+++ b/playground/ExcalidrawWrapper.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useCallback, useContext, useMemo } from "react";
import {
Excalidraw,
convertToExcalidrawElements,
@@ -8,36 +8,113 @@ import {
ExcalidrawImperativeAPI,
} from "@excalidraw/excalidraw/types/types.js";
import { ExcalidrawElementSkeleton } from "@excalidraw/excalidraw/types/data/transform.js";
+import { parseMermaid } from "../src/parseMermaid";
+import { graphToExcalidraw } from "../src/graphToExcalidraw";
+import { DEFAULT_FONT_SIZE } from "../src/constants";
-interface ExcalidrawWrapperProps {
- elements: ExcalidrawElementSkeleton[];
- files?: BinaryFiles;
-}
+const ExcalidrawContext = React.createContext<{
+ excalidrawAPI?: ExcalidrawImperativeAPI;
+ addFiles: (files: BinaryFiles) => void;
+ updateElements: (elements: ExcalidrawElementSkeleton[]) => void;
+ translateMermaidToExcalidraw: (mermaidSyntax: any) => Promise<{
+ mermaid: ReturnType;
+ excalidraw: { elements: ExcalidrawElementSkeleton[]; files: BinaryFiles };
+ }>;
+ setApi: (api: ExcalidrawImperativeAPI) => void;
+} | null>(null);
-const ExcalidrawWrapper = (props: ExcalidrawWrapperProps) => {
- const [excalidrawAPI, setExcalidrawAPI] =
- React.useState(null);
+export const useExcalidraw = () => {
+ const context = useContext(ExcalidrawContext);
- React.useEffect(() => {
- if (!props.elements || !excalidrawAPI) {
- return;
- }
+ if (!context) {
+ throw new Error("useExcalidraw must be used within a ExcalidrawProvider");
+ }
+
+ return context;
+};
+
+export const ExcalidrawProvider = ({
+ children,
+}: {
+ children: React.ReactNode;
+}) => {
+ const excalidrawAPI = React.useRef();
+
+ const updateElements = useCallback(
+ (elements: ExcalidrawElementSkeleton[]) => {
+ if (!excalidrawAPI.current) {
+ return;
+ }
- excalidrawAPI.updateScene({
- elements: convertToExcalidrawElements(props.elements),
- });
- excalidrawAPI.scrollToContent(excalidrawAPI.getSceneElements(), {
- fitToContent: true,
- });
- }, [props.elements]);
+ excalidrawAPI.current.updateScene({
+ elements: convertToExcalidrawElements(elements),
+ });
- React.useEffect(() => {
- if (!props.files || !excalidrawAPI) {
+ excalidrawAPI.current.scrollToContent(
+ excalidrawAPI.current.getSceneElements(),
+ {
+ fitToContent: true,
+ }
+ );
+ },
+ []
+ );
+
+ const addFiles = useCallback((files: BinaryFiles) => {
+ if (!excalidrawAPI.current) {
return;
}
- excalidrawAPI.addFiles(Object.values(props.files));
- }, [props.files]);
+ excalidrawAPI.current.addFiles(Object.values(files));
+ }, []);
+
+ const setApi = useCallback((api: ExcalidrawImperativeAPI) => {
+ excalidrawAPI.current = api;
+ }, []);
+
+ const translateMermaidToExcalidraw = useCallback(
+ async (mermaidSyntax: string) => {
+ if (!excalidrawAPI.current) {
+ return;
+ }
+
+ const mermaid = await parseMermaid(mermaidSyntax);
+
+ const { elements, files } = graphToExcalidraw(mermaid, {
+ fontSize: DEFAULT_FONT_SIZE,
+ });
+
+ updateElements(elements);
+
+ if (files) {
+ addFiles(files);
+ }
+
+ return { mermaid, excalidraw: { elements, files } };
+ },
+ [updateElements, addFiles]
+ );
+
+ const context = useMemo(
+ () => ({
+ excalidrawAPI: excalidrawAPI.current,
+ addFiles,
+ updateElements,
+ setApi,
+ translateMermaidToExcalidraw,
+ }),
+ [addFiles, updateElements, setApi, translateMermaidToExcalidraw]
+ );
+
+ return (
+
+ {children}
+
+ );
+};
+
+const ExcalidrawWrapper = () => {
+ const excalidraw = useExcalidraw();
return (
@@ -48,7 +125,7 @@ const ExcalidrawWrapper = (props: ExcalidrawWrapperProps) => {
currentItemFontFamily: 1,
},
}}
- excalidrawAPI={(api) => setExcalidrawAPI(api)}
+ excalidrawAPI={excalidraw?.setApi}
/>
);
diff --git a/playground/Mermaid.tsx b/playground/Mermaid.tsx
new file mode 100644
index 00000000..6dbd1bfd
--- /dev/null
+++ b/playground/Mermaid.tsx
@@ -0,0 +1,32 @@
+import mermaid from "mermaid";
+import React from "react";
+
+interface MermaidProps {
+ id: string;
+ definition: string;
+}
+
+export function Mermaid({ definition, id }: MermaidProps) {
+ const [svg, setSvg] = React.useState("");
+ const [, startTransition] = React.useTransition();
+
+ React.useEffect(() => {
+ const render = async (id: string, definition: string) => {
+ startTransition(() => {
+ mermaid.render(`mermaid-diagram-${id}`, definition).then(({ svg }) => {
+ setSvg(svg);
+ });
+ });
+ };
+
+ render(id, definition);
+ }, [definition, id]);
+
+ return (
+
+ );
+}
diff --git a/playground/Testcases.tsx b/playground/Testcases.tsx
new file mode 100644
index 00000000..89082f2f
--- /dev/null
+++ b/playground/Testcases.tsx
@@ -0,0 +1,106 @@
+import React from "react";
+
+import { FLOWCHART_DIAGRAM_TESTCASES } from "./testcases/flowchart";
+import { SEQUENCE_DIAGRAM_TESTCASES } from "./testcases/sequence.ts";
+import { CLASS_DIAGRAM_TESTCASES } from "./testcases/class.ts";
+import { UNSUPPORTED_DIAGRAM_TESTCASES } from "./testcases/unsupported.ts";
+import { useExcalidraw } from "./ExcalidrawWrapper.tsx";
+import { Mermaid } from "./Mermaid";
+
+interface TestCaseProps {
+ name: string;
+ baseId: string;
+ testcases: { name: string; definition: string }[];
+}
+
+function Testcase({ name, baseId, testcases }: TestCaseProps) {
+ const excalidraw = useExcalidraw();
+ const activeTestcase = React.useRef();
+
+ React.useEffect(() => {
+ const testcase = activeTestcase.current;
+
+ if (testcase !== undefined) {
+ const { definition } = testcases[testcase];
+
+ excalidraw.translateMermaidToExcalidraw(definition);
+ }
+ }, [excalidraw.translateMermaidToExcalidraw, testcases]);
+
+ return (
+ <>
+ {`${name} Diagrams`}
+
+ {`${name} Examples`}
+
+ {testcases.map(({ name, definition }, index) => {
+ const id = `${baseId}-${index}`;
+
+ return (
+
+
{name}
+
+
+ {definition}
+
+
+
+
+
+
+ );
+ })}
+
+
+ >
+ );
+}
+
+function Testcases() {
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+}
+
+export default Testcases;
diff --git a/playground/index.html b/playground/index.html
index 7bcfeb27..bf3505c2 100644
--- a/playground/index.html
+++ b/playground/index.html
@@ -5,73 +5,8 @@
-
-
- Custom Test
- Supports only
- Flowchart
-
-
- Sequence
-
- and
-
- class diagrams.
-
-
-
-
-
- Parsed data from parseMermaid
-
-
-
-
-
-
- Flowchart Diagrams
-
- Flowchart Examples
-
-
-
- Sequence Diagrams
-
- Sequence Diagram Examples
-
-
-
- Class Diagrams
-
- Class Diagram Examples
-
-
-
- Unsupported diagrams
-
- Unsupported Diagram Examples
-
-
-
-
+
+