diff --git a/README.md b/README.md index f08f568..f679b0e 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ I have implemented core packages and added some examples. However, I still need | Mermaid | [@beoe/rehype-mermaid](/packages/rehype-mermaid/) | | | Gnuplot | [@beoe/rehype-gnuplot](/packages/rehype-gnuplot/) | | | Vizdom | [@beoe/rehype-vizdom](/packages/rehype-vizdom/) | | +| D2 | | | | Penrose | | | | ... | | | diff --git a/packages/demo/src/components/graphviz.ts b/packages/demo/src/components/graphviz.ts new file mode 100644 index 0000000..131a423 --- /dev/null +++ b/packages/demo/src/components/graphviz.ts @@ -0,0 +1,130 @@ +import { json, alg, type Path } from "@dagrejs/graphlib"; + +type D = { [node: string]: Path }; + +// interactivity for graphviz diagrams +document.querySelectorAll(".graphviz").forEach((container) => { + const data = container.getAttribute("data-graph") + ? JSON.parse(container.getAttribute("data-graph")!) + : null; + + if (!data) return; + const graph = json.read(data); + + function clear() { + container.querySelectorAll(".node").forEach((node) => { + node.classList.remove("active"); + node.classList.remove("selected"); + }); + container + .querySelectorAll(".edge") + .forEach((node) => node.classList.remove("active")); + } + + function highlight(id: string) { + alg.postorder(graph, [id]).forEach((node) => { + container.querySelector(`#node${node}`)?.classList.add("active"); + graph.outEdges(node)?.forEach(({ name }) => { + container.querySelector(`#edge${name}`)?.classList.add("active"); + }); + }); + } + + function walkPath(d: D, node: string) { + container.querySelector(`#node${node}`)?.classList.add("active"); + if (d[node].distance === 0 || d[node].distance === Infinity) return; + graph.outEdges(d[node].predecessor, node)?.forEach(({ name }) => { + container.querySelector(`#edge${name}`)?.classList.add("active"); + }); + walkPath(d, d[node].predecessor); + } + + function drawShortestpath(a: string, b: string) { + const first = alg.dijkstra(graph, a); + if (first[b].distance != Infinity) { + walkPath(first, b); + return; + } + const second = alg.dijkstra(graph, b); + if (second[a].distance != Infinity) { + walkPath(second, a); + return; + } + } + + let selected = new Set(); + // highlight selected + // if two nodes selected will show shortest path + container.addEventListener("click", (e) => { + // @ts-expect-error + const node = e.target?.closest(".node"); + if (!node) return; + const id = node.getAttribute("id").replace("node", ""); + console.log(id); + clear(); + + if (selected.has(id)) { + selected.delete(id); + } else { + if (selected.size < 2) { + selected.add(id); + } else { + selected.delete([...selected][0]); + selected.add(id); + } + } + if (selected.size === 0) return; + if (selected.size === 1) { + const id = [...selected][0]; + container.querySelector(`#node${id}`)?.classList.add("selected"); + highlight(id); + return; + } + + const [a, b] = [...selected]; + container.querySelector(`#node${a}`)?.classList.add("selected"); + container.querySelector(`#node${b}`)?.classList.add("selected"); + drawShortestpath(a, b); + }); + + // highlight on hover + let currentHover: string | null = null; + container.addEventListener("mouseover", (e) => { + if (selected.size > 1) return; + // @ts-expect-error + const node = e.target?.closest(".node"); + if (selected.size === 0) { + if (node) { + const id = node.getAttribute("id").replace("node", ""); + if (currentHover == id) return; + clear(); + highlight(id); + currentHover = id; + } else { + if (currentHover == null) return; + clear(); + currentHover = null; + } + } else { + const selectedId = [...selected][0]; + if (node) { + const id = node.getAttribute("id").replace("node", ""); + if (currentHover == id) return; + clear(); + container + .querySelector(`#node${selectedId}`) + ?.classList.add("selected"); + drawShortestpath(selectedId, id); + currentHover = id; + } else { + if (currentHover == null) return; + clear(); + container + .querySelector(`#node${selectedId}`) + ?.classList.add("selected"); + highlight(selectedId); + currentHover = null; + } + } + }); +}); \ No newline at end of file diff --git a/packages/demo/src/components/vizdom.ts b/packages/demo/src/components/vizdom.ts index 1efa992..057a91c 100644 --- a/packages/demo/src/components/vizdom.ts +++ b/packages/demo/src/components/vizdom.ts @@ -3,7 +3,7 @@ import { json, alg, type Path } from "@dagrejs/graphlib"; type D = { [node: string]: Path }; // interactivity for vizdom diagrams -document.querySelectorAll(".vizdom").forEach((container) => { +document.querySelectorAll(".vizdom.ants").forEach((container) => { const data = container.getAttribute("data-graph") ? JSON.parse(container.getAttribute("data-graph")!) : null; @@ -128,8 +128,8 @@ document.querySelectorAll(".vizdom").forEach((container) => { }); }); -// interactivity for graphviz diagrams -document.querySelectorAll(".graphviz").forEach((container) => { +// interactivity for vizdom diagrams +document.querySelectorAll(".vizdom.shadow").forEach((container) => { const data = container.getAttribute("data-graph") ? JSON.parse(container.getAttribute("data-graph")!) : null; @@ -138,119 +138,39 @@ document.querySelectorAll(".graphviz").forEach((container) => { const graph = json.read(data); function clear() { - container.querySelectorAll(".node").forEach((node) => { - node.classList.remove("active"); - node.classList.remove("selected"); - }); container - .querySelectorAll(".edge") - .forEach((node) => node.classList.remove("active")); + .querySelectorAll(".node,.edge,.cluster") + .forEach((node) => node.classList.remove("shadow")); } function highlight(id: string) { + container + .querySelectorAll(".node,.edge,.cluster") + .forEach((node) => node.classList.add("shadow")); alg.postorder(graph, [id]).forEach((node) => { - container.querySelector(`#node${node}`)?.classList.add("active"); + container.querySelector(`#node-${node}`)?.classList.remove("shadow"); graph.outEdges(node)?.forEach(({ name }) => { - container.querySelector(`#edge${name}`)?.classList.add("active"); + container.querySelector(`#edge-${name}`)?.classList.remove("shadow"); }); }); } - function walkPath(d: D, node: string) { - container.querySelector(`#node${node}`)?.classList.add("active"); - if (d[node].distance === 0 || d[node].distance === Infinity) return; - graph.outEdges(d[node].predecessor, node)?.forEach(({ name }) => { - container.querySelector(`#edge${name}`)?.classList.add("active"); - }); - walkPath(d, d[node].predecessor); - } - - function drawShortestpath(a: string, b: string) { - const first = alg.dijkstra(graph, a); - if (first[b].distance != Infinity) { - walkPath(first, b); - return; - } - const second = alg.dijkstra(graph, b); - if (second[a].distance != Infinity) { - walkPath(second, a); - return; - } - } - - let selected = new Set(); - // highlight selected - // if two nodes selected will show shortest path - container.addEventListener("click", (e) => { - // @ts-expect-error - const node = e.target?.closest(".node"); - if (!node) return; - const id = node.getAttribute("id").replace("node", ""); - console.log(id); - clear(); - - if (selected.has(id)) { - selected.delete(id); - } else { - if (selected.size < 2) { - selected.add(id); - } else { - selected.delete([...selected][0]); - selected.add(id); - } - } - if (selected.size === 0) return; - if (selected.size === 1) { - const id = [...selected][0]; - container.querySelector(`#node${id}`)?.classList.add("selected"); - highlight(id); - return; - } - - const [a, b] = [...selected]; - container.querySelector(`#node${a}`)?.classList.add("selected"); - container.querySelector(`#node${b}`)?.classList.add("selected"); - drawShortestpath(a, b); - }); - // highlight on hover let currentHover: string | null = null; container.addEventListener("mouseover", (e) => { - if (selected.size > 1) return; // @ts-expect-error const node = e.target?.closest(".node"); - if (selected.size === 0) { - if (node) { - const id = node.getAttribute("id").replace("node", ""); - if (currentHover == id) return; - clear(); - highlight(id); - currentHover = id; - } else { - if (currentHover == null) return; - clear(); - currentHover = null; - } + + if (node) { + const id = node.getAttribute("id").replace("node-", ""); + if (currentHover == id) return; + clear(); + highlight(id); + currentHover = id; } else { - const selectedId = [...selected][0]; - if (node) { - const id = node.getAttribute("id").replace("node", ""); - if (currentHover == id) return; - clear(); - container - .querySelector(`#node${selectedId}`) - ?.classList.add("selected"); - drawShortestpath(selectedId, id); - currentHover = id; - } else { - if (currentHover == null) return; - clear(); - container - .querySelector(`#node${selectedId}`) - ?.classList.add("selected"); - highlight(selectedId); - currentHover = null; - } + if (currentHover == null) return; + clear(); + currentHover = null; } }); -}); \ No newline at end of file +}); diff --git a/packages/demo/src/content/docs/examples/vizdom.md b/packages/demo/src/content/docs/examples/vizdom.md index d42bc13..838c4d5 100644 --- a/packages/demo/src/content/docs/examples/vizdom.md +++ b/packages/demo/src/content/docs/examples/vizdom.md @@ -4,7 +4,9 @@ title: vizdom ## Simple -```vizdom dataGraph=dagre svgo=false +**Interactivity**: shadow out. Try 👇 hover nodes. + +```vizdom dataGraph=dagre svgo=false class=shadow digraph TD { cluster=true node [shape=box] @@ -58,7 +60,9 @@ digraph TD { ## Same example as Graphviz -```vizdom dataGraph=dagre +**Interactivity**: ants. Try 👇 hover or click nodes. + +```vizdom dataGraph=dagre class=ants digraph finite_state_machine { bgcolor="transparent"; fontname="Helvetica,Arial,sans-serif"; diff --git a/packages/demo/src/styles/custom.css b/packages/demo/src/styles/custom.css index cf2f39e..8f2c8d2 100644 --- a/packages/demo/src/styles/custom.css +++ b/packages/demo/src/styles/custom.css @@ -60,6 +60,10 @@ animation-timing-function: linear; } + .shadow { + opacity: 0.4; + } + .node { cursor: pointer; }