import * as React from "react"; import * as Mantine from "@mantine/core"; import { GraphvizComponent } from '../graphviz'; import {D3Graph, D3GraphData, D3NodeData, defaultGraphForces} from "../d3graph/d3graph"; import {InfoHoverCardOverlay} from "../info_hover_card"; function esc(str) { return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); } function esc2(str) { return encodeURIComponent(str); } // I know global variables are bad, but a GraphViz URL can only call a global function: const graphvizMap = new Map(); (window as any).graphvizClicked = function(nodeId, button) { const callback = graphvizMap.get(nodeId); if (callback) callback(button); } export type Renderer = 'd3' | 'graphviz'; interface GraphViewProps { graphData: D3GraphData; help: JSX.Element; graphvizHelp?: JSX.Element; mouseUpHandler: (event, mouse:{x:number, y:number}, node?: D3NodeData) => void; defaultRenderer?: Renderer; children?: any; } export function GraphView(props: GraphViewProps) { const {graphData, help, mouseUpHandler, defaultRenderer} = props; const [renderer, setRenderer] = React.useState(defaultRenderer || "d3"); const id = React.useId(); if (mouseUpHandler) { for (const node of graphData.nodes) { graphvizMap.set(id+node.id, (button) => mouseUpHandler({button}, {x:0, y:0}, node)); } } const dot1 = `digraph { bgcolor="transparent"; ${graphData.nodes.map(node => `"${esc(node.id)}" [${node.label===""?`shape=circle, width=0.3, height=0.3`:`shape=box`}, label="${esc(node.label)}", fillcolor="${node.color}", style="filled,rounded", ${node.bold?`penwidth=4.0,`:``} URL="javascript:${esc2(`graphvizClicked('${id+node.id}', 2)`)}"]`).join('\n')} ${graphData.links.map(link => `"${esc(link.source.id || link.source)}" -> "${esc(link.target.id || link.target)}" [label="${esc(link.label)}", fontcolor="${link.color}", color="${link.color}"${link.bidirectional?`, dir=none`:``}]`).join('\n')} }`; const dot2 = `digraph { bgcolor="transparent"; node[shape=record]; ${graphData.nodes.filter(node => !node.id.startsWith("V")).map(node => { const outgoing = graphData.links.filter(link => (link.source.id || link.source) === node.id); return `"${esc(node.id)}" [label="{ ${["", ...outgoing.map(o => `${o.label}`)].join('|')} } | { ${[esc(node.label), ...outgoing.map(o => (o.target.id || o.target).startsWith("V") ? `${esc(o.target.label)}` : ``)].join('|')} }", fillcolor="${node.color}", style="filled,rounded", ${node.bold?`penwidth=4.0,`:``} URL="javascript:${esc2(`graphvizClicked('${id+node.id}', 2)`)}"]`; }).join('\n')} ${graphData.links.filter(link => !link.target.id.startsWith("V")).map(link => `"${esc(link.source.id || link.source)}":"L${link.label}" -> "${esc(link.target.id || link.target)}" [label="${esc(link.label)}", fontcolor="${link.color}", color="${link.color}"${link.bidirectional?`, dir=none`:``}]`).join('\n')} }`; // @ts-ignore: const graphviz = ; return D3: Fast, poor layout
Graphviz: Slow, nice layout}>
{props.children} alert(dot2)}>Get Graphviz dot
{renderer==="d3"? : props.graphvizHelp ? {graphviz} : graphviz }
; }