cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

Site: a diagram's box stays filled when the color mode switches quickly

Dami committed 94c36ef
Docusaurus's hook draws every version of a diagram under one id, and
Mermaid takes the element with that id off the page before drawing.
When the color mode switched twice before React committed, the last
drawing matched the markup React had already set, so React left the
box as Mermaid had emptied it. The component now draws each version
under its own id, one drawing at a time with its own configuration,
and drops drawings for a color mode no longer shown.
commit
94c36efa970d2f83db47d325cf39776c23a2e5b9
parent
ae77e5571cd989b9d23ea364693951264f4e94aa
author
Dami <dami.etoile@gmail.com>
kind
fix risk score 1.5 of 3, Jev's estimate
1 file changed, +58 -6
Mwebsite/src/theme/Mermaid/index.tsx64+++++++++++++++++++++++++++---
M website/src/theme/Mermaid/index.tsx +58 -6
@@ -2,15 +2,15 @@
22 // palette: Mermaid's base theme, its variables read from the colors33 // custom.css defines for the color mode shown, so the palette has one44 // home. Mermaid needs literal colors, not CSS variables, which is why5-// they are read from the page rather than passed through.5+// they are read from the page rather than passed through. It draws with6+// its own hook rather than Docusaurus's, which can leave a diagram's box7+// empty when the color mode changes quickly (see draw).68 79 import React, {useEffect, useMemo, useRef, useState, type ReactNode} from 'react';810 import ErrorBoundary from '@docusaurus/ErrorBoundary';911 import {ErrorBoundaryErrorMessageFallback, useColorMode} from '@docusaurus/theme-common';10-import {11- MermaidContainerClassName,12- useMermaidRenderResult,13-} from '@docusaurus/theme-mermaid/client';12+import {MermaidContainerClassName} from '@docusaurus/theme-mermaid/client';13+import {loadMermaid} from '@docusaurus/theme-mermaid/lib/client/loadMermaid.js';1414 import type {MermaidConfig, RenderResult} from 'mermaid';1515 1616 type Props = {value: string};
@@ -157,9 +157,61 @@ function MermaidRenderResult({renderResult}: {renderResult: RenderResult}): Reac
157157 );158158 }159159 160+// Mermaid is one object with one configuration, and it draws a diagram161+// through an element of the page named by the id it is given. So each162+// drawing gets its configuration set just before it and runs after the163+// one before has finished, and gets an id of its own: under the id of164+// the drawing shown, Mermaid would take that one off the page first, and165+// a drawing identical to the one React last put there (the color mode166+// switched twice quickly) would leave the box empty.167+let drawing: Promise<unknown> = Promise.resolve();168+let drawn = 0;169+170+function draw(text: string, config: MermaidConfig): Promise<RenderResult> {171+ const id = `mermaid-svg-${++drawn}`;172+ const result = drawing.then(async () => {173+ const mermaid = await loadMermaid();174+ mermaid.initialize(config);175+ try {176+ return await mermaid.render(id, text);177+ } catch (e) {178+ // On an error Mermaid leaves its error drawing on the page.179+ document.querySelector(`#d${id}`)?.remove();180+ throw e;181+ }182+ });183+ drawing = result.catch(() => undefined);184+ return result;185+}186+187+// The drawing for the latest configuration: one asked for before the188+// color mode changed again is dropped when it arrives. An error goes to189+// the error boundary, which shows it in place of the diagram.190+function useDrawing(text: string, config: MermaidConfig | undefined): RenderResult | null {191+ const [result, setResult] = useState<RenderResult | null>(null);192+ useEffect(() => {193+ if (config === undefined) {194+ return undefined;195+ }196+ let live = true;197+ draw(text, config).then(198+ (r) => live && setResult(r),199+ (e: unknown) =>200+ live &&201+ setResult(() => {202+ throw e;203+ }),204+ );205+ return () => {206+ live = false;207+ };208+ }, [text, config]);209+ return result;210+}211+160212 function MermaidRenderer({value}: Props): ReactNode {161213 const config = useMermaidConfig();162- const renderResult = useMermaidRenderResult({text: value, config});214+ const renderResult = useDrawing(value, config);163215 if (renderResult === null) {164216 return null;165217 }