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 ( + <> +
{ + event.preventDefault(); + + const data = new FormData(event.target as HTMLFormElement); + const mermaidSyntax = data.get("mermaid-input") as string; + + if (!mermaidSyntax) { + return; + } + + try { + setParsedMermaid({ + data: null, + definition: null, + error: null, + }); + + const { mermaid } = await excalidraw.translateMermaidToExcalidraw( + mermaidSyntax + ); + + setParsedMermaid({ + data: JSON.stringify(mermaid, null, 2), + definition: mermaidSyntax, + error: null, + }); + } catch (error) { + setParsedMermaid({ + data: null, + definition: null, + error: String(error), + }); + } + }} + > +
- -
-
- Parsed data from parseMermaid -

-      
-
- - - Loading Spinner -

Flowchart Diagrams

-
- Flowchart Examples -
-
- -

Sequence Diagrams

-
- Sequence Diagram Examples -
-
- -

Class Diagrams

-
- Class Diagram Examples -
-
- -

Unsupported diagrams

-
- Unsupported Diagram Examples -
-
- - +
+ diff --git a/playground/index.ts b/playground/index.ts deleted file mode 100644 index db84de08..00000000 --- a/playground/index.ts +++ /dev/null @@ -1,205 +0,0 @@ -import mermaid from "mermaid"; -import { parseMermaid } from "../src/parseMermaid.ts"; -import { FLOWCHART_DIAGRAM_TESTCASES } from "./testcases/flowchart.ts"; -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 "./initCustomTest.ts"; -import { renderExcalidraw } from "./initExcalidraw.tsx"; -import { DEFAULT_FONT_SIZE, MERMAID_CONFIG } from "../src/constants.ts"; -import { EXCALIDRAW_ACTIVE_ATTR, EXCALIDRAW_WRAPPER_ID } from "./constants.ts"; - -// Initialize Mermaid -mermaid.initialize({ - ...MERMAID_CONFIG, - themeVariables: { - fontSize: `${DEFAULT_FONT_SIZE}px`, - }, -}); - -const flowchartContainer = document.getElementById("flowchart-container")!; -const sequenceContainer = document.getElementById("sequence-container")!; -const classContainer = document.getElementById("class-container")!; -const unsupportedContainer = document.getElementById("unsupported")!; -const spinner = document.getElementById("diagram-loading-spinner")!; -unsupportedContainer.innerHTML = ` -

Unsupported diagram will be rendered as SVG image.

- `; - -(async () => { - // Render flowchart diagrams - await Promise.all( - FLOWCHART_DIAGRAM_TESTCASES.map(async ({ name, definition }, index) => { - await renderDiagram( - flowchartContainer, - name, - definition, - index, - "flowchart" - ); - }) - ); - // Render Sequence diagrams - await Promise.all( - SEQUENCE_DIAGRAM_TESTCASES.map(({ name, definition }, index) => { - return renderDiagram( - sequenceContainer, - name, - definition, - index, - "sequence" - ); - }) - ); - - // Render Class diagrams - await Promise.all( - CLASS_DIAGRAM_TESTCASES.map(({ name, definition }, index) => { - return renderDiagram(classContainer, name, definition, index, "class"); - }) - ); - - // Render unsupported diagrams - await Promise.all( - UNSUPPORTED_DIAGRAM_TESTCASES.map(async (testcase, index) => { - const { name, definition } = testcase; - - await renderDiagram( - unsupportedContainer, - name, - definition, - index, - "unsupported" - ); - }) - ); - - spinner.style.display = "none"; -})(); - -function generateDiagramId(base: string, number: number) { - return `${base}-${number}`; -} - -async function handleRenderExcalidraw(id: string, definition: string) { - const data = await parseMermaid(definition); - - // In HMR we use this attribute to trigger a rerender for active test case for Excalidraw - const excalidraw = document.getElementById(EXCALIDRAW_WRAPPER_ID); - excalidraw?.setAttribute(EXCALIDRAW_ACTIVE_ATTR, id); - - renderExcalidraw(JSON.stringify(data)); -} - -async function renderDiagram( - containerEl: HTMLElement, - name: string, - diagramDefinition: string, - i: number, - baseId: string -) { - const id = generateDiagramId(baseId, i); - - const diagramContainerEl = document.createElement("div"); - diagramContainerEl.setAttribute("id", `diagram-container-${id}`); - diagramContainerEl.innerHTML = `

${name} -

- -

-
-  
-  
`; - - const btn = diagramContainerEl.querySelector(`#diagram-btn-${id}`)!; - - btn.addEventListener("click", () => - handleRenderExcalidraw(id, diagramDefinition) - ); - - const diagramEl = diagramContainerEl.querySelector(`#diagram-${id}`)!; - const { svg } = await mermaid.render(`diagram-${id}`, diagramDefinition); - - diagramEl.innerHTML = svg; - containerEl.append(diagramContainerEl); - - // Render mermaid syntax - const mermaidSyntaxEl = diagramContainerEl.querySelector( - `#mermaid-syntax-${id}` - )!; - mermaidSyntaxEl.innerHTML = diagramDefinition; -} - -async function updateDiagram( - name: string, - diagramDefinition: string, - index: number, - baseId: string -) { - const id = generateDiagramId(baseId, index); - - const diagramContainerEl = document.getElementById( - `diagram-container-${id}` - )!; - const diagramEl = document.getElementById(`diagram-${id}`)!; - const titleEl = document.getElementById(`diagram-title-${id}`)!; - const { svg } = await mermaid.render(`diagram-${id}`, diagramDefinition); - const mermaidSyntaxEl = document.getElementById(`mermaid-syntax-${id}`)!; - const btn = document.getElementById(`diagram-btn-${id}`)!; - - // This allows us to remove all anonymous events listeners from btn - const btnClone = btn.cloneNode(true); - - btnClone.addEventListener("click", () => - handleRenderExcalidraw(id, diagramDefinition) - ); - - btn.replaceWith(btnClone); - titleEl.textContent = name; - diagramEl.innerHTML = svg; - diagramContainerEl.append(diagramEl); - mermaidSyntaxEl.innerHTML = diagramDefinition; -} - -if (import.meta.hot) { - function hmrUpdateTestcase( - baseId: string, - newTestCases: { name: string; definition: string }[] - ) { - if (!newTestCases) { - return import.meta.hot?.invalidate(); - } - - for (let i = 0; i < newTestCases.length; i++) { - const { name, definition } = newTestCases[i]; - - const excalidraw = document.getElementById(EXCALIDRAW_WRAPPER_ID); - - if ( - excalidraw?.getAttribute(EXCALIDRAW_ACTIVE_ATTR) === - generateDiagramId(baseId, i) - ) { - parseMermaid(definition).then((data) => { - renderExcalidraw(JSON.stringify(data)); - }); - } - - updateDiagram(name, definition, i, baseId); - } - } - - import.meta.hot.accept("./testcases/flowchart.ts", (newModule) => - hmrUpdateTestcase("flowchart", newModule?.FLOWCHART_DIAGRAM_TESTCASES) - ); - - import.meta.hot.accept("./testcases/class.ts", (newModule) => - hmrUpdateTestcase("class", newModule?.CLASS_DIAGRAM_TESTCASES) - ); - - import.meta.hot.accept("./testcases/sequence.ts", (newModule) => - hmrUpdateTestcase("sequence", newModule?.SEQUENCE_DIAGRAM_TESTCASES) - ); - - import.meta.hot.accept("./testcases/unsupported.ts", (newModule) => - hmrUpdateTestcase("unsupported", newModule?.UNSUPPORTED_DIAGRAM_TESTCASES) - ); -} diff --git a/playground/index.tsx b/playground/index.tsx new file mode 100644 index 00000000..f86bd922 --- /dev/null +++ b/playground/index.tsx @@ -0,0 +1,41 @@ +import CustomTest from "./CustomTest.tsx"; +import ExcalidrawWrapper, { ExcalidrawProvider } from "./ExcalidrawWrapper.tsx"; +import Testcases from "./Testcases.tsx"; + +function App() { + return ( + +
+

{"Custom Test"}

+ {"Supports only "} + + {"Flowchart"} + {" "} + + {"Sequence "} + + {"and "} + + {"class "} + + {"diagrams."} +
+ +
+ + + +
+ +
+
+ ); +} + +export default App; diff --git a/playground/initCustomTest.ts b/playground/initCustomTest.ts deleted file mode 100644 index 9cd5aba3..00000000 --- a/playground/initCustomTest.ts +++ /dev/null @@ -1,38 +0,0 @@ -import mermaid from "mermaid"; -import { parseMermaid } from "../src/parseMermaid.ts"; -import { renderExcalidraw } from "./initExcalidraw.tsx"; - -const customTestEl = document.getElementById("custom-test")!; -const btn = document.getElementById("render-excalidraw-btn")!; -const errorEl = customTestEl.querySelector("#error")!; - -// Handle render to Excalidraw event -btn.addEventListener("click", async () => { - errorEl.setAttribute("style", "display: none"); - - try { - const mermaidInput = document.getElementById( - "mermaid-input" - ) as HTMLInputElement; - - const diagramDefinition = mermaidInput.value; - - // Render Mermaid diagram - const diagramEl = document.getElementById("custom-diagram")!; - const { svg } = await mermaid.render("custom-digaram", diagramDefinition); - diagramEl.innerHTML = svg; - - // Parse Mermaid diagram and render to Excalidraw - const parsedData = await parseMermaid(diagramDefinition); - - const parsedDataEl = document.getElementById("custom-parsed-data")!; - parsedDataEl.parentElement!.style.display = "block"; - parsedDataEl.innerText = JSON.stringify(parsedData, null, 2); - - renderExcalidraw(JSON.stringify(parsedData)); - } catch (e) { - errorEl.setAttribute("style", "display: block"); - errorEl.innerHTML = String(e); - console.error("Custom Test Error:", e); - } -}); diff --git a/playground/initExcalidraw.tsx b/playground/initExcalidraw.tsx deleted file mode 100644 index e3daeaaf..00000000 --- a/playground/initExcalidraw.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import ExcalidrawWrapper from "./ExcalidrawWrapper.tsx"; -import { graphToExcalidraw } from "../src/graphToExcalidraw.ts"; -import { DEFAULT_FONT_SIZE } from "../src/constants.ts"; -import { EXCALIDRAW_WRAPPER_ID } from "./constants.ts"; - -// Create Excalidraw Wrapper element -const excalidrawWrapper = document.createElement("div"); -excalidrawWrapper.id = EXCALIDRAW_WRAPPER_ID; -document.body.appendChild(excalidrawWrapper); - -// Init Excalidraw -const root = ReactDOM.createRoot(excalidrawWrapper); -root.render(); - -// Render to Excalidraw -export const renderExcalidraw = ( - mermaidGraphDataString: string, - fontSize = DEFAULT_FONT_SIZE -) => { - const mermaidGraphData = JSON.parse(mermaidGraphDataString); - const { elements, files } = graphToExcalidraw(mermaidGraphData, { fontSize }); - - console.info("renderExcalidraw", elements); - - root.render(); -}; diff --git a/playground/main.tsx b/playground/main.tsx new file mode 100644 index 00000000..bb38d06f --- /dev/null +++ b/playground/main.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./index.tsx"; +import mermaid from "mermaid"; +import { DEFAULT_FONT_SIZE, MERMAID_CONFIG } from "../src/constants.ts"; + +// Initialize Mermaid +mermaid.initialize({ + ...MERMAID_CONFIG, + themeVariables: { + fontSize: `${DEFAULT_FONT_SIZE}px`, + }, +}); + +const root = ReactDOM.createRoot(document.getElementById("root")!); + +root.render( + + + +); diff --git a/playground/style.scss b/playground/style.scss index 0a0ba081..bc2a673f 100644 --- a/playground/style.scss +++ b/playground/style.scss @@ -6,6 +6,7 @@ body { max-width: 48%; } } + button { height: 40px; font-size: 16px; @@ -16,6 +17,7 @@ button { color: white; cursor: pointer; } + #excalidraw { width: 50vw; height: 100vh; @@ -46,11 +48,9 @@ button { #parsed-data-details { margin-top: 20px; - display: none; } #error { - display: none; color: red; margin-top: 20px; }