123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- 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<N,L> {
- graphData: D3GraphData<N,L>;
- help: JSX.Element;
- graphvizHelp?: JSX.Element;
- mouseUpHandler: (event, mouse:{x:number, y:number}, node?: D3NodeData<N>) => void;
- defaultRenderer?: Renderer;
- children?: any;
- }
- export function GraphView<N,L>(props: GraphViewProps<N,L>) {
- const {graphData, help, mouseUpHandler, defaultRenderer} = props;
- const [renderer, setRenderer] = React.useState<Renderer>(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)}` : `<L${o.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 = <Mantine.ScrollArea style={{backgroundColor:"#eee"}}>
- <GraphvizComponent dot={dot2}
- />
- </Mantine.ScrollArea>;
- return <Mantine.Stack>
- <Mantine.Group position="center">
- <Mantine.Tooltip withArrow openDelay={300} label={<Mantine.Text><b>D3</b>: Fast, poor layout<br/><b>Graphviz</b>: Slow, nice layout</Mantine.Text>}>
- <Mantine.SegmentedControl
- data={[
- { label: 'D3', value: 'd3' },
- { label: 'Graphviz', value: 'graphviz' },
- ]}
- value={renderer}
- onChange={setRenderer}
- />
- </Mantine.Tooltip>
- {props.children}
- <Mantine.Button onClick={()=>alert(dot2)}>Get Graphviz dot</Mantine.Button>
- </Mantine.Group>
- {renderer==="d3"?
- <InfoHoverCardOverlay contents={help}>
- <D3Graph graph={graphData} forces={defaultGraphForces} mouseUpHandler={mouseUpHandler} />
- </InfoHoverCardOverlay>
- :
- props.graphvizHelp ?
- <InfoHoverCardOverlay contents={props.graphvizHelp}>{graphviz}</InfoHoverCardOverlay>
- : graphviz
- }
- </Mantine.Stack>;
- }
|