261 lines
7.1 KiB
JavaScript
261 lines
7.1 KiB
JavaScript
import React, { useEffect, useState, useRef } from "react";
|
|
import colormap from "colormap";
|
|
import cytoscape from "cytoscape";
|
|
|
|
import { DynamicSquiggleChart } from "../dynamicSquiggleChart.js";
|
|
import {
|
|
resolveToNumIfPossible,
|
|
getSquiggleSparkline,
|
|
} from "../../lib/squiggleCalculations.js";
|
|
import { truncateValueForDisplay } from "../../lib/truncateNums.js";
|
|
import { cutOffLongNames } from "../../lib/stringManipulations.js";
|
|
|
|
// import spread from "cytoscape-spread";
|
|
// import dagre from "cytoscape-dagre";
|
|
// import cola from "cytoscape-cola";
|
|
// import fcose from "cytoscape-fcose";
|
|
// import avsdf from "cytoscape-avsdf";
|
|
|
|
const effectButtonStyle =
|
|
"bg-transparent m-2 hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded mt-5";
|
|
|
|
const getEdgeLabel = async (squiggleString) => {
|
|
let sparkline = await getSquiggleSparkline(squiggleString);
|
|
let num = await resolveToNumIfPossible(squiggleString);
|
|
|
|
let sparklineConcat = "";
|
|
if (false && sparkline.success) {
|
|
console.log(sparkline);
|
|
|
|
sparklineConcat =
|
|
sparklineConcat + " →" + sparkline.sparkline.replace("▁▁▁▁▁▁▁▁▁▁▁▁▁", "");
|
|
//alert("▁▁▁▁▁▁▁▁▁▁▁");
|
|
}
|
|
if (num.asNum) {
|
|
sparklineConcat =
|
|
sparklineConcat + " ⇾ " + truncateValueForDisplay(num.num);
|
|
//alert("▁▁▁▁▁▁▁▁▁▁▁");
|
|
}
|
|
|
|
return squiggleString + sparklineConcat;
|
|
};
|
|
|
|
const getColors = (n) => {
|
|
let colors;
|
|
if (n >= 9) {
|
|
colors = colormap({
|
|
colormap: "viridis",
|
|
nshades: n,
|
|
format: "hex",
|
|
alpha: 1,
|
|
});
|
|
} else {
|
|
colors = colormap({
|
|
colormap: "greys", // other themes: hot, winter, etc.
|
|
nshades: n,
|
|
format: "hex",
|
|
alpha: 1,
|
|
});
|
|
}
|
|
return colors;
|
|
};
|
|
|
|
export function Graph({
|
|
listOfElements,
|
|
links,
|
|
isListOrdered,
|
|
listAfterMergeSort,
|
|
}) {
|
|
const containerRef = useRef("hello-world");
|
|
const [visibility, setVisibility] = useState(""); /// useState("invisible");
|
|
const [cs, setCs] = useState(null); /// useState("invisible");
|
|
const [selectedLink, setSelectedLink] = useState(null);
|
|
const [selectedLinkTimeout, setSelectedLinkTimeout] = useState(null);
|
|
|
|
const callEffect = async ({
|
|
listOfElements,
|
|
links,
|
|
isListOrdered,
|
|
listAfterMergeSort,
|
|
}) => {
|
|
//setVisibility("invisible");
|
|
let layoutName = "circle"; //
|
|
|
|
// cytoscape.use(spread); // necessary for non-default themes,
|
|
let listOfElementsForGraph = isListOrdered
|
|
? listAfterMergeSort
|
|
: listOfElements;
|
|
|
|
let colors = new Array(listOfElements.length);
|
|
if (isListOrdered) {
|
|
colors = getColors(listOfElements.length);
|
|
}
|
|
|
|
let nodeElements = listOfElements.map((element, i) => {
|
|
return {
|
|
data: {
|
|
id: cutOffLongNames(element.name),
|
|
color: colors[i] || "darkgreen",
|
|
labelColor: isListOrdered
|
|
? i >= listOfElementsForGraph.length - 2
|
|
? "black"
|
|
: "white"
|
|
: "white",
|
|
},
|
|
};
|
|
});
|
|
let linkElements = await Promise.all(
|
|
links.map(async (link, i) => {
|
|
return {
|
|
data: {
|
|
id: `link-${i}`,
|
|
source: cutOffLongNames(link.source),
|
|
target: cutOffLongNames(link.target),
|
|
label: await getEdgeLabel(link.squiggleString),
|
|
squiggleString: link.squiggleString,
|
|
},
|
|
};
|
|
})
|
|
);
|
|
|
|
const cytoscapeStylesheet = [
|
|
{
|
|
selector: "node",
|
|
style: {
|
|
padding: "30px",
|
|
shape: "round-rectangle",
|
|
content: "data(id)",
|
|
"background-color": "data(color)",
|
|
"text-wrap": "wrap",
|
|
"text-max-width": 70,
|
|
"z-index": 1,
|
|
},
|
|
},
|
|
{
|
|
selector: "node[id]",
|
|
style: {
|
|
label: "data(id)",
|
|
"font-size": "13",
|
|
color: "data(labelColor)",
|
|
"text-halign": "center",
|
|
"text-valign": "center",
|
|
"z-index": 1,
|
|
},
|
|
},
|
|
{
|
|
selector: "edge",
|
|
style: {
|
|
"curve-style": "unbundled-bezier",
|
|
"target-arrow-shape": "vee",
|
|
width: 1.5,
|
|
"target-arrow-color": "green",
|
|
"arrow-scale": 3,
|
|
"target-arrow-fill": "filled",
|
|
"text-rotation": "autorotate",
|
|
"z-index": 0,
|
|
},
|
|
},
|
|
{
|
|
selector: "edge[label]",
|
|
style: {
|
|
label: "data(label)",
|
|
"font-size": "12",
|
|
|
|
"text-background-color": "#f9f9f9",
|
|
"text-background-opacity": 1,
|
|
"text-background-padding": "4px",
|
|
|
|
"text-border-color": "black",
|
|
"text-border-style": "solid",
|
|
"text-border-width": 0.5,
|
|
"text-border-opacity": 1,
|
|
"z-index": 3,
|
|
},
|
|
},
|
|
];
|
|
|
|
const config = {
|
|
container: containerRef.current,
|
|
style: cytoscapeStylesheet,
|
|
elements: [...nodeElements, ...linkElements],
|
|
layout: {
|
|
name: layoutName, // circle, grid, dagre
|
|
minDist: 10,
|
|
//prelayout: false,
|
|
// animate: false, // whether to transition the node positions
|
|
// animationDuration: 250, // duration of animation in ms if enabled
|
|
// the cytoscape documentation is pretty good here.
|
|
},
|
|
userZoomingEnabled: false,
|
|
userPanningEnabled: false,
|
|
};
|
|
let newCs = cytoscape(config);
|
|
setCs(newCs);
|
|
// setTimeout(() => setVisibility(""), 700);
|
|
// necessary for themes like spread, which have
|
|
// a confusing animation at the beginning
|
|
};
|
|
useEffect(() => {
|
|
callEffect({
|
|
listOfElements,
|
|
links,
|
|
isListOrdered,
|
|
listAfterMergeSort,
|
|
});
|
|
}, [listOfElements, links, isListOrdered, listAfterMergeSort, selectedLink]);
|
|
|
|
useEffect(() => {
|
|
if (cs != null) {
|
|
clearTimeout(selectedLinkTimeout);
|
|
let newTimeout = setTimeout(() => {
|
|
cs.edges().on("mouseover", (event) => {
|
|
// on("click",
|
|
let edge = event.target;
|
|
// alert(JSON.stringify(edge.json()));
|
|
console.log(JSON.stringify(edge.json()));
|
|
setSelectedLink(JSON.parse(JSON.stringify(edge.json())).data);
|
|
});
|
|
}, 100);
|
|
setSelectedLinkTimeout(newTimeout);
|
|
}
|
|
}, [cs]);
|
|
return (
|
|
<div className="grid place-items-center">
|
|
<div
|
|
className={
|
|
visibility +
|
|
`grid grid-cols-${
|
|
selectedLink == null ? "1 " : "2"
|
|
} place-items-center place-self-center space-x-0 w-10/12 `
|
|
}
|
|
>
|
|
<div
|
|
ref={containerRef}
|
|
style={{
|
|
height: "900px", // isListOrdered ? "900px" : "500px",
|
|
width: "900px", // isListOrdered ? "900px" : "500px",
|
|
}}
|
|
className=""
|
|
/>
|
|
<DynamicSquiggleChart
|
|
link={selectedLink}
|
|
stopShowing={() => setSelectedLink(null)}
|
|
/>
|
|
</div>
|
|
<button
|
|
className={effectButtonStyle}
|
|
onClick={() =>
|
|
callEffect({
|
|
listOfElements,
|
|
links,
|
|
isListOrdered,
|
|
listAfterMergeSort,
|
|
})
|
|
}
|
|
>
|
|
{"Redraw graph"}
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|