@docusaurus/theme-mermaid 3.0.0-alpha.0 → 3.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client/index.d.ts +5 -2
- package/lib/client/index.js +57 -34
- package/lib/theme/Mermaid/index.js +25 -7
- package/package.json +10 -10
- package/src/client/index.ts +78 -41
- package/src/theme/Mermaid/index.tsx +34 -8
package/lib/client/index.d.ts
CHANGED
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import type { RenderResult, MermaidConfig } from 'mermaid';
|
|
8
8
|
import type { ThemeConfig } from '@docusaurus/theme-mermaid';
|
|
9
9
|
export declare const MermaidContainerClassName = "docusaurus-mermaid-container";
|
|
10
10
|
export declare function useMermaidThemeConfig(): ThemeConfig['mermaid'];
|
|
11
11
|
export declare function useMermaidConfig(): MermaidConfig;
|
|
12
|
-
export declare function
|
|
12
|
+
export declare function useMermaidRenderResult({ text, config: providedConfig, }: {
|
|
13
|
+
text: string;
|
|
14
|
+
config?: MermaidConfig;
|
|
15
|
+
}): RenderResult | null;
|
package/lib/client/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
import { useMemo } from 'react';
|
|
7
|
+
import { useState, useEffect, useMemo, useRef } from 'react';
|
|
8
8
|
import { useColorMode, useThemeConfig } from '@docusaurus/theme-common';
|
|
9
9
|
import mermaid from 'mermaid';
|
|
10
10
|
// Stable className to allow users to easily target with CSS
|
|
@@ -19,42 +19,65 @@ export function useMermaidConfig() {
|
|
|
19
19
|
const { options } = mermaidThemeConfig;
|
|
20
20
|
return useMemo(() => ({ startOnLoad: false, ...options, theme }), [theme, options]);
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
function useMermaidId() {
|
|
23
|
+
/*
|
|
24
|
+
Random client-only id, we don't care much but mermaid want an id so...
|
|
25
|
+
Note: Mermaid doesn't like values provided by Rect.useId() and throws
|
|
26
|
+
*/
|
|
27
|
+
// return useId(); // tried that, doesn't work ('#d:re:' is not a valid selector.)
|
|
28
|
+
return useRef(`mermaid-svg-${Math.round(Math.random() * 10000000)}`).current;
|
|
29
|
+
}
|
|
30
|
+
async function renderMermaid({ id, text, config, }) {
|
|
31
|
+
/*
|
|
32
|
+
Mermaid API is really weird :s
|
|
33
|
+
It is a big mutable singleton with multiple config levels
|
|
34
|
+
Note: most recent API type definitions are missing
|
|
35
|
+
|
|
36
|
+
There are 2 kind of configs:
|
|
37
|
+
|
|
38
|
+
- siteConfig: some kind of global/protected shared config
|
|
39
|
+
you can only set with "initialize"
|
|
40
|
+
|
|
41
|
+
- config/currentConfig
|
|
42
|
+
the config the renderer will use
|
|
43
|
+
it is reset to siteConfig before each render
|
|
44
|
+
but it can be altered by the mermaid txt content itself through directives
|
|
45
|
+
|
|
46
|
+
To use a new mermaid config (on colorMode change for example) we should
|
|
47
|
+
update siteConfig, and it can only be done with initialize()
|
|
48
|
+
*/
|
|
49
|
+
mermaid.mermaidAPI.initialize(config);
|
|
50
|
+
try {
|
|
51
|
+
return await mermaid.render(id, text);
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
// Because Mermaid add a weird SVG/Message to the DOM on error
|
|
55
|
+
// https://github.com/mermaid-js/mermaid/issues/3205#issuecomment-1719620183
|
|
56
|
+
document.querySelector(`#d${id}`)?.remove();
|
|
57
|
+
throw e;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export function useMermaidRenderResult({ text, config: providedConfig, }) {
|
|
61
|
+
const [result, setResult] = useState(null);
|
|
62
|
+
const id = useMermaidId();
|
|
23
63
|
/*
|
|
24
64
|
For flexibility, we allow the hook to receive a custom Mermaid config
|
|
25
65
|
The user could inject a modified version of the default config for example
|
|
26
66
|
*/
|
|
27
67
|
const defaultMermaidConfig = useMermaidConfig();
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
but it can be altered by the mermaid txt content itself through directives
|
|
44
|
-
|
|
45
|
-
To use a new mermaid config (on colorMode change for example) we should
|
|
46
|
-
update siteConfig, and it can only be done with initialize()
|
|
47
|
-
*/
|
|
48
|
-
mermaid.mermaidAPI.initialize(mermaidConfig);
|
|
49
|
-
/*
|
|
50
|
-
Random client-only id, we don't care much about it
|
|
51
|
-
But mermaid want an id so...
|
|
52
|
-
*/
|
|
53
|
-
const mermaidId = `mermaid-svg-${Math.round(Math.random() * 10000000)}`;
|
|
54
|
-
/*
|
|
55
|
-
Not even documented: mermaid.render returns the svg string
|
|
56
|
-
Using the documented form is un-necessary
|
|
57
|
-
*/
|
|
58
|
-
return mermaid.render(mermaidId, txt);
|
|
59
|
-
}, [txt, mermaidConfig]);
|
|
68
|
+
const config = providedConfig ?? defaultMermaidConfig;
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
renderMermaid({ id, text, config })
|
|
71
|
+
// TODO maybe try to use Suspense here and throw the promise?
|
|
72
|
+
// See also https://github.com/pmndrs/suspend-react
|
|
73
|
+
.then(setResult)
|
|
74
|
+
.catch((e) => {
|
|
75
|
+
// Funky way to trigger parent React error boundary
|
|
76
|
+
// See https://twitter.com/sebastienlorber/status/1628340871899893768
|
|
77
|
+
setResult(() => {
|
|
78
|
+
throw e;
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}, [id, text, config]);
|
|
82
|
+
return result;
|
|
60
83
|
}
|
|
@@ -4,23 +4,41 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
import React from 'react';
|
|
8
|
-
import
|
|
7
|
+
import React, {useEffect, useRef} from 'react';
|
|
8
|
+
import ErrorBoundary from '@docusaurus/ErrorBoundary';
|
|
9
|
+
import {ErrorBoundaryErrorMessageFallback} from '@docusaurus/theme-common';
|
|
9
10
|
import {
|
|
10
11
|
MermaidContainerClassName,
|
|
11
|
-
|
|
12
|
+
useMermaidRenderResult,
|
|
12
13
|
} from '@docusaurus/theme-mermaid/client';
|
|
13
14
|
import styles from './styles.module.css';
|
|
14
|
-
function
|
|
15
|
-
const
|
|
15
|
+
function MermaidRenderResult({renderResult}) {
|
|
16
|
+
const ref = useRef(null);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const div = ref.current;
|
|
19
|
+
renderResult.bindFunctions?.(div);
|
|
20
|
+
}, [renderResult]);
|
|
16
21
|
return (
|
|
17
22
|
<div
|
|
23
|
+
ref={ref}
|
|
18
24
|
className={`${MermaidContainerClassName} ${styles.container}`}
|
|
19
25
|
// eslint-disable-next-line react/no-danger
|
|
20
|
-
dangerouslySetInnerHTML={{__html: svg}}
|
|
26
|
+
dangerouslySetInnerHTML={{__html: renderResult.svg}}
|
|
21
27
|
/>
|
|
22
28
|
);
|
|
23
29
|
}
|
|
30
|
+
function MermaidRenderer({value}) {
|
|
31
|
+
const renderResult = useMermaidRenderResult({text: value});
|
|
32
|
+
if (renderResult === null) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return <MermaidRenderResult renderResult={renderResult} />;
|
|
36
|
+
}
|
|
24
37
|
export default function Mermaid(props) {
|
|
25
|
-
return
|
|
38
|
+
return (
|
|
39
|
+
<ErrorBoundary
|
|
40
|
+
fallback={(params) => <ErrorBoundaryErrorMessageFallback {...params} />}>
|
|
41
|
+
<MermaidRenderer {...props} />
|
|
42
|
+
</ErrorBoundary>
|
|
43
|
+
);
|
|
26
44
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/theme-mermaid",
|
|
3
|
-
"version": "3.0.0-
|
|
3
|
+
"version": "3.0.0-rc.0",
|
|
4
4
|
"description": "Mermaid components for Docusaurus.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "src/theme-mermaid.d.ts",
|
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@docusaurus/core": "3.0.0-
|
|
37
|
-
"@docusaurus/module-type-aliases": "3.0.0-
|
|
38
|
-
"@docusaurus/theme-common": "3.0.0-
|
|
39
|
-
"@docusaurus/types": "3.0.0-
|
|
40
|
-
"@docusaurus/utils-validation": "3.0.0-
|
|
41
|
-
"mermaid": "^
|
|
42
|
-
"tslib": "^2.
|
|
36
|
+
"@docusaurus/core": "3.0.0-rc.0",
|
|
37
|
+
"@docusaurus/module-type-aliases": "3.0.0-rc.0",
|
|
38
|
+
"@docusaurus/theme-common": "3.0.0-rc.0",
|
|
39
|
+
"@docusaurus/types": "3.0.0-rc.0",
|
|
40
|
+
"@docusaurus/utils-validation": "3.0.0-rc.0",
|
|
41
|
+
"mermaid": "^10.4.0",
|
|
42
|
+
"tslib": "^2.6.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/mdx-js__react": "^1.5.5",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"react-dom": "^18.0.0"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
|
-
"node": ">=
|
|
53
|
+
"node": ">=18.0"
|
|
54
54
|
},
|
|
55
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "e66dfe04d2972edce66f431908470c61340f8ffc"
|
|
56
56
|
}
|
package/src/client/index.ts
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {useMemo} from 'react';
|
|
8
|
+
import {useState, useEffect, useMemo, useRef} from 'react';
|
|
9
9
|
import {useColorMode, useThemeConfig} from '@docusaurus/theme-common';
|
|
10
|
-
import mermaid
|
|
10
|
+
import mermaid from 'mermaid';
|
|
11
|
+
import type {RenderResult, MermaidConfig} from 'mermaid';
|
|
11
12
|
import type {ThemeConfig} from '@docusaurus/theme-mermaid';
|
|
12
13
|
|
|
13
14
|
// Stable className to allow users to easily target with CSS
|
|
@@ -30,48 +31,84 @@ export function useMermaidConfig(): MermaidConfig {
|
|
|
30
31
|
);
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
function useMermaidId(): string {
|
|
35
|
+
/*
|
|
36
|
+
Random client-only id, we don't care much but mermaid want an id so...
|
|
37
|
+
Note: Mermaid doesn't like values provided by Rect.useId() and throws
|
|
38
|
+
*/
|
|
39
|
+
// return useId(); // tried that, doesn't work ('#d:re:' is not a valid selector.)
|
|
40
|
+
return useRef(`mermaid-svg-${Math.round(Math.random() * 10000000)}`).current!;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function renderMermaid({
|
|
44
|
+
id,
|
|
45
|
+
text,
|
|
46
|
+
config,
|
|
47
|
+
}: {
|
|
48
|
+
id: string;
|
|
49
|
+
text: string;
|
|
50
|
+
config: MermaidConfig;
|
|
51
|
+
}): Promise<RenderResult> {
|
|
52
|
+
/*
|
|
53
|
+
Mermaid API is really weird :s
|
|
54
|
+
It is a big mutable singleton with multiple config levels
|
|
55
|
+
Note: most recent API type definitions are missing
|
|
56
|
+
|
|
57
|
+
There are 2 kind of configs:
|
|
58
|
+
|
|
59
|
+
- siteConfig: some kind of global/protected shared config
|
|
60
|
+
you can only set with "initialize"
|
|
61
|
+
|
|
62
|
+
- config/currentConfig
|
|
63
|
+
the config the renderer will use
|
|
64
|
+
it is reset to siteConfig before each render
|
|
65
|
+
but it can be altered by the mermaid txt content itself through directives
|
|
66
|
+
|
|
67
|
+
To use a new mermaid config (on colorMode change for example) we should
|
|
68
|
+
update siteConfig, and it can only be done with initialize()
|
|
69
|
+
*/
|
|
70
|
+
mermaid.mermaidAPI.initialize(config);
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
return await mermaid.render(id, text);
|
|
74
|
+
} catch (e) {
|
|
75
|
+
// Because Mermaid add a weird SVG/Message to the DOM on error
|
|
76
|
+
// https://github.com/mermaid-js/mermaid/issues/3205#issuecomment-1719620183
|
|
77
|
+
document.querySelector(`#d${id}`)?.remove();
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function useMermaidRenderResult({
|
|
83
|
+
text,
|
|
84
|
+
config: providedConfig,
|
|
85
|
+
}: {
|
|
86
|
+
text: string;
|
|
87
|
+
config?: MermaidConfig;
|
|
88
|
+
}): RenderResult | null {
|
|
89
|
+
const [result, setResult] = useState<RenderResult | null>(null);
|
|
90
|
+
const id = useMermaidId();
|
|
91
|
+
|
|
37
92
|
/*
|
|
38
93
|
For flexibility, we allow the hook to receive a custom Mermaid config
|
|
39
94
|
The user could inject a modified version of the default config for example
|
|
40
95
|
*/
|
|
41
96
|
const defaultMermaidConfig = useMermaidConfig();
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
To use a new mermaid config (on colorMode change for example) we should
|
|
61
|
-
update siteConfig, and it can only be done with initialize()
|
|
62
|
-
*/
|
|
63
|
-
mermaid.mermaidAPI.initialize(mermaidConfig);
|
|
64
|
-
|
|
65
|
-
/*
|
|
66
|
-
Random client-only id, we don't care much about it
|
|
67
|
-
But mermaid want an id so...
|
|
68
|
-
*/
|
|
69
|
-
const mermaidId = `mermaid-svg-${Math.round(Math.random() * 10000000)}`;
|
|
70
|
-
|
|
71
|
-
/*
|
|
72
|
-
Not even documented: mermaid.render returns the svg string
|
|
73
|
-
Using the documented form is un-necessary
|
|
74
|
-
*/
|
|
75
|
-
return mermaid.render(mermaidId, txt);
|
|
76
|
-
}, [txt, mermaidConfig]);
|
|
97
|
+
const config = providedConfig ?? defaultMermaidConfig;
|
|
98
|
+
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
renderMermaid({id, text, config})
|
|
101
|
+
// TODO maybe try to use Suspense here and throw the promise?
|
|
102
|
+
// See also https://github.com/pmndrs/suspend-react
|
|
103
|
+
.then(setResult)
|
|
104
|
+
.catch((e) => {
|
|
105
|
+
// Funky way to trigger parent React error boundary
|
|
106
|
+
// See https://twitter.com/sebastienlorber/status/1628340871899893768
|
|
107
|
+
setResult(() => {
|
|
108
|
+
throw e;
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}, [id, text, config]);
|
|
112
|
+
|
|
113
|
+
return result;
|
|
77
114
|
}
|
|
@@ -5,28 +5,54 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import
|
|
8
|
+
import React, {useEffect, useRef} from 'react';
|
|
9
|
+
import type {ReactNode} from 'react';
|
|
10
|
+
import ErrorBoundary from '@docusaurus/ErrorBoundary';
|
|
11
|
+
import {ErrorBoundaryErrorMessageFallback} from '@docusaurus/theme-common';
|
|
10
12
|
import {
|
|
11
13
|
MermaidContainerClassName,
|
|
12
|
-
|
|
14
|
+
useMermaidRenderResult,
|
|
13
15
|
} from '@docusaurus/theme-mermaid/client';
|
|
14
|
-
|
|
15
16
|
import type {Props} from '@theme/Mermaid';
|
|
17
|
+
import type {RenderResult} from 'mermaid';
|
|
16
18
|
|
|
17
19
|
import styles from './styles.module.css';
|
|
18
20
|
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
+
function MermaidRenderResult({
|
|
22
|
+
renderResult,
|
|
23
|
+
}: {
|
|
24
|
+
renderResult: RenderResult;
|
|
25
|
+
}): JSX.Element {
|
|
26
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const div = ref.current!;
|
|
30
|
+
renderResult.bindFunctions?.(div);
|
|
31
|
+
}, [renderResult]);
|
|
32
|
+
|
|
21
33
|
return (
|
|
22
34
|
<div
|
|
35
|
+
ref={ref}
|
|
23
36
|
className={`${MermaidContainerClassName} ${styles.container}`}
|
|
24
37
|
// eslint-disable-next-line react/no-danger
|
|
25
|
-
dangerouslySetInnerHTML={{__html: svg}}
|
|
38
|
+
dangerouslySetInnerHTML={{__html: renderResult.svg}}
|
|
26
39
|
/>
|
|
27
40
|
);
|
|
28
41
|
}
|
|
29
42
|
|
|
43
|
+
function MermaidRenderer({value}: Props): ReactNode {
|
|
44
|
+
const renderResult = useMermaidRenderResult({text: value});
|
|
45
|
+
if (renderResult === null) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return <MermaidRenderResult renderResult={renderResult} />;
|
|
49
|
+
}
|
|
50
|
+
|
|
30
51
|
export default function Mermaid(props: Props): JSX.Element {
|
|
31
|
-
return
|
|
52
|
+
return (
|
|
53
|
+
<ErrorBoundary
|
|
54
|
+
fallback={(params) => <ErrorBoundaryErrorMessageFallback {...params} />}>
|
|
55
|
+
<MermaidRenderer {...props} />
|
|
56
|
+
</ErrorBoundary>
|
|
57
|
+
);
|
|
32
58
|
}
|