Skip to content

Commit 8f45f31

Browse files
Merge pull request #9 from stereobooster/d2-graph
d2 graph
2 parents a797c43 + 236f13b commit 8f45f31

File tree

8 files changed

+129
-14
lines changed

8 files changed

+129
-14
lines changed

packages/docs/src/components/PageFrame.astro

+1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ import Default from "@astrojs/starlight/components/PageFrame.astro";
1313
import "./panzoom.ts";
1414
import "./vizdom.ts";
1515
import "./graphviz.ts";
16+
import "./d2.ts";
1617
</script>

packages/docs/src/components/d2.ts

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { json, alg } from "@dagrejs/graphlib";
2+
3+
// interactivity for d2 diagrams
4+
document.querySelectorAll(".d2.shadow").forEach((container) => {
5+
const data = container.getAttribute("data-beoe")
6+
? JSON.parse(container.getAttribute("data-beoe")!)
7+
: null;
8+
9+
if (!data) return;
10+
const graph = json.read(data);
11+
12+
function clear() {
13+
container
14+
.querySelectorAll("g[id]")
15+
.forEach((node) => node.classList.remove("shadow"));
16+
}
17+
18+
function highlight(id: string) {
19+
container
20+
.querySelectorAll("g[id]")
21+
.forEach((node) => node.classList.add("shadow"));
22+
alg.postorder(graph, [id]).forEach((node) => {
23+
container
24+
.querySelector(`#${CSS.escape(node)}`)
25+
?.classList.remove("shadow");
26+
graph.outEdges(node)?.forEach(({ name }) => {
27+
container
28+
.querySelector(`#${CSS.escape(name!)}`)
29+
?.classList.remove("shadow");
30+
});
31+
});
32+
}
33+
34+
// highlight on hover
35+
let currentHover: string | null = null;
36+
container.addEventListener("mouseover", (e) => {
37+
// @ts-expect-error
38+
const node = e.target?.closest(".shape");
39+
40+
if (node) {
41+
const id = node.parentElement.getAttribute("id");
42+
if (currentHover == id) return;
43+
clear();
44+
highlight(id);
45+
currentHover = id;
46+
} else {
47+
if (currentHover == null) return;
48+
clear();
49+
currentHover = null;
50+
}
51+
});
52+
});

packages/docs/src/content/docs/diagrams/d2.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ Or locally:
105105

106106
- [x] [Support links in connections](https://github.com/terrastruct/d2/pull/1955)
107107
- [x] [JS package](https://github.com/terrastruct/d2/discussions/234#discussioncomment-11286029)
108-
- [ ] [Export JSON graph](https://github.com/terrastruct/d2/discussions/2224)
108+
- [x] [Export JSON graph](https://github.com/terrastruct/d2/discussions/2224)
109109
- [ ] [Class-based dark mode](https://github.com/terrastruct/d2/pull/1803)
110110
- [ ] [Remove embeded fonts](https://github.com/terrastruct/d2/discussions/132)
111111
- [ ] [Smaller embeded icons](https://github.com/terrastruct/d2/discussions/2223)

packages/docs/src/content/docs/examples/d2-test.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ a2 -> b -> c -> d -> e
282282

283283
### `strategy=inline`
284284

285-
```d2 strategy=inline darkScheme=false
285+
```d2 strategy=inline darkScheme=false graphFormat=dagre class=shadow svgo=false
286286
direction: right
287287
a -> b -> c -> d -> e
288288
```

packages/docs/src/styles/custom.css

+10
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,13 @@ html[data-theme="dark"] .beoe-light {
133133
.vizdom .style-me :first-child {
134134
fill: lightblue;
135135
}
136+
137+
.d2[data-beoe].shadow {
138+
.shadow {
139+
opacity: 0.4;
140+
}
141+
142+
.shape {
143+
cursor: default;
144+
}
145+
}

packages/rehype-d2/src/d2.ts

+55-6
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,63 @@ export type D2Options = {
55
layout?: "dagre" | "elk";
66
sketch?: boolean;
77
themeID?: number;
8+
graphFormat?: "graphology" | "dagre";
89
};
910

10-
export async function d2(
11-
code: string,
12-
options?: D2Options
13-
): Promise<string> {
11+
export async function d2(code: string, options: D2Options) {
1412
const d2Instance = new D2();
1513
const result = await d2Instance.compile(code, options);
16-
const svg = await d2Instance.render(result.diagram, options);
17-
return svg
14+
15+
let data;
16+
if (options.graphFormat) {
17+
if (options.graphFormat === "graphology") {
18+
data = {
19+
attributes: { name: "g" },
20+
options: { allowSelfLoops: true, multi: true, type: "directed" },
21+
nodes: result.diagram.shapes.map((node: any) => ({
22+
key: node.id,
23+
// attributes: {
24+
// label: node.label,
25+
// x: node.pos.x,
26+
// y: node.pos.y,
27+
// width: node.width,
28+
// height: node.height,
29+
// url: node.url,
30+
// },
31+
})),
32+
edges: result.diagram.connections.map((edge: any) => ({
33+
key: edge.id,
34+
source: edge.src,
35+
target: edge.dst,
36+
// attributes: {
37+
// label: edge.label,
38+
// url: edge.url,
39+
// },
40+
})),
41+
};
42+
}
43+
44+
if (options.graphFormat === "dagre") {
45+
data = {
46+
options: {
47+
directed: true,
48+
multigraph: true,
49+
compound: false,
50+
},
51+
nodes: result.diagram.shapes.map((node: any) => ({
52+
v: node.id,
53+
// value: {},
54+
})),
55+
edges: result.diagram.connections.map((edge: any) => ({
56+
v: edge.src,
57+
w: edge.dst,
58+
name: edge.id,
59+
// value: {},
60+
})),
61+
};
62+
}
63+
}
64+
65+
const svg = await d2Instance.render(result.diagram, options) as string;
66+
return { svg, data };
1867
}

packages/rehype-d2/src/index.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,18 @@ export const rehypeD2 = rehypeCodeHookImg<RehypeD2Config>({
2121
// @ts-ignore
2222
newD2Options.themeID = parseFloat(newD2Options.theme);
2323
}
24-
const svg = await d2(code, newD2Options);
25-
const darkSvg = darkMode
26-
? await d2(code, {
24+
const { svg, data } = await d2(code, newD2Options);
25+
let darkSvg;
26+
if (darkMode) {
27+
darkSvg = (
28+
await d2(code, {
2729
...newD2Options,
2830
// @ts-ignore
2931
themeID: parseFloat(newD2Options?.darkTheme ?? "200"),
3032
})
31-
: undefined;
32-
return { svg, darkSvg };
33+
).svg;
34+
}
35+
return { svg, darkSvg, data };
3336
},
3437
});
3538

packages/rehype-vizdom/src/render.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export const render = async (code: string, options: RehypeVizdomConfig) => {
3030
// x: node.x,
3131
// y: node.y,
3232
// width: node.width,
33-
// height: node.width,
33+
// height: node.height,
3434
// url: node.url,
3535
// },
3636
})),

0 commit comments

Comments
 (0)