hierarchical-estimates-visu.../packages/webpage-refactor/components/graph/graph.js

228 lines
6.0 KiB
JavaScript
Raw Normal View History

2022-06-18 03:20:44 +00:00
import React, { useEffect, useState, useRef } from "react";
2022-06-18 18:18:15 +00:00
import colormap from "colormap";
2022-06-18 03:20:44 +00:00
import cytoscape from "cytoscape";
2022-06-19 23:25:55 +00:00
2022-06-18 03:20:44 +00:00
import {
resolveToNumIfPossible,
getSquiggleSparkline,
} from "../../lib/squiggle.js";
import { truncateValueForDisplay } from "../../lib/truncateNums.js";
2022-06-19 23:25:55 +00:00
import { cutOffLongNames } from "../../lib/stringManipulations.js";
2022-06-18 03:20:44 +00:00
2022-06-20 00:50:53 +00:00
// import spread from "cytoscape-spread";
2022-06-18 03:20:44 +00:00
// import dagre from "cytoscape-dagre";
// import cola from "cytoscape-cola";
// import fcose from "cytoscape-fcose";
2022-06-18 18:18:15 +00:00
// import avsdf from "cytoscape-avsdf";
2022-06-18 03:20:44 +00:00
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 =
2022-06-18 18:18:15 +00:00
sparklineConcat + " →" + sparkline.sparkline.replace("▁▁▁▁▁▁▁▁▁▁▁▁▁", "");
2022-06-18 03:20:44 +00:00
//alert("▁▁▁▁▁▁▁▁▁▁▁");
}
if (num.asNum) {
sparklineConcat =
sparklineConcat + " ⇾ " + truncateValueForDisplay(num.num);
//alert("▁▁▁▁▁▁▁▁▁▁▁");
}
2022-06-20 00:50:53 +00:00
return squiggleString + sparklineConcat;
2022-06-18 03:20:44 +00:00
};
2022-06-18 18:18:15 +00:00
const getColors = (n) => {
2022-06-19 23:25:55 +00:00
let colors;
if (n >= 9) {
colors = colormap({
colormap: "viridis",
nshades: n,
format: "hex",
alpha: 1,
});
2022-06-18 18:18:15 +00:00
} else {
2022-06-19 23:25:55 +00:00
colors = colormap({
2022-06-20 00:50:53 +00:00
colormap: "greys", // other themes: hot, winter, etc.
2022-06-19 23:25:55 +00:00
nshades: n,
format: "hex",
alpha: 1,
});
2022-06-18 18:18:15 +00:00
}
2022-06-19 23:25:55 +00:00
return colors;
2022-06-18 18:18:15 +00:00
};
export function Graph({
listOfElements,
links,
isListOrdered,
2022-06-19 23:25:55 +00:00
listAfterMergeSort,
2022-06-18 18:18:15 +00:00
}) {
2022-06-18 23:10:27 +00:00
const containerRef = useRef("hello-world");
2022-06-18 18:18:15 +00:00
const [visibility, setVisibility] = useState(""); /// useState("invisible");
const callEffect = async ({
listOfElements,
links,
isListOrdered,
2022-06-19 23:25:55 +00:00
listAfterMergeSort,
2022-06-18 18:18:15 +00:00
}) => {
//setVisibility("invisible");
let layoutName = "circle"; //
2022-06-20 00:50:53 +00:00
// cytoscape.use(spread); // necessary for non-default themes,
2022-06-18 18:18:15 +00:00
let listOfElementsForGraph = isListOrdered
2022-06-19 23:25:55 +00:00
? listAfterMergeSort
2022-06-18 18:18:15 +00:00
: listOfElements;
2022-06-18 03:20:44 +00:00
2022-06-18 18:18:15 +00:00
let colors = new Array(listOfElements.length);
if (isListOrdered) {
colors = getColors(listOfElements.length);
}
2022-06-18 03:20:44 +00:00
2022-06-18 18:18:15 +00:00
let nodeElements = listOfElements.map((element, i) => {
return {
data: {
id: cutOffLongNames(element.name),
color: colors[i] || "darkgreen",
2022-06-19 23:25:55 +00:00
labelColor: isListOrdered
? i >= listOfElementsForGraph.length - 2
2022-06-18 18:56:37 +00:00
? "black"
2022-06-19 23:25:55 +00:00
: "white"
: "white",
2022-06-18 18:18:15 +00:00
},
};
2022-06-18 03:20:44 +00:00
});
let linkElements = await Promise.all(
links.map(async (link, i) => {
return {
data: {
id: `link-${i}`,
2022-06-18 18:18:15 +00:00
source: cutOffLongNames(link.source),
target: cutOffLongNames(link.target),
2022-06-18 03:20:44 +00:00
label: await getEdgeLabel(link.squiggleString),
},
};
})
);
2022-06-18 18:18:15 +00:00
const cytoscapeStylesheet = [
{
selector: "node",
style: {
2022-06-18 18:56:37 +00:00
padding: "30px",
2022-06-18 18:18:15 +00:00
shape: "round-rectangle",
content: "data(id)",
"background-color": "data(color)",
"text-wrap": "wrap",
"text-max-width": 70,
"z-index": 1,
2022-06-18 03:20:44 +00:00
},
2022-06-18 18:18:15 +00:00
},
{
selector: "node[id]",
style: {
label: "data(id)",
2022-06-18 18:56:37 +00:00
"font-size": "13",
color: "data(labelColor)",
2022-06-18 18:18:15 +00:00
"text-halign": "center",
"text-valign": "center",
"z-index": 1,
2022-06-18 03:20:44 +00:00
},
2022-06-18 18:18:15 +00:00
},
{
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,
2022-06-20 00:50:53 +00:00
elements: [...nodeElements, ...linkElements],
2022-06-18 03:20:44 +00:00
layout: {
2022-06-18 18:18:15 +00:00
name: layoutName, // circle, grid, dagre
2022-06-18 03:20:44 +00:00
minDist: 10,
//prelayout: false,
2022-06-18 03:38:06 +00:00
// animate: false, // whether to transition the node positions
// animationDuration: 250, // duration of animation in ms if enabled
2022-06-20 00:50:53 +00:00
// the cytoscape documentation is pretty good here.
2022-06-18 03:20:44 +00:00
},
userZoomingEnabled: false,
userPanningEnabled: false,
};
cytoscape(config);
2022-06-20 00:50:53 +00:00
// setTimeout(() => setVisibility(""), 700);
// necessary for themes like spread, which have
// a confusing animation at the beginning
2022-06-18 03:20:44 +00:00
};
useEffect(async () => {
2022-06-19 23:25:55 +00:00
await callEffect({
listOfElements,
links,
isListOrdered,
listAfterMergeSort,
});
}, [listOfElements, links, isListOrdered, listAfterMergeSort]);
2022-06-18 03:20:44 +00:00
return (
2022-06-19 23:25:55 +00:00
<div className="">
<div className={visibility + "grid grid-cols-1 place-items-center "}>
<div
ref={containerRef}
style={{
height: "900px", // isListOrdered ? "900px" : "500px",
width: "900px", // isListOrdered ? "900px" : "500px",
}}
/>
2022-06-18 03:38:06 +00:00
</div>
2022-06-18 03:20:44 +00:00
<button
className={effectButtonStyle}
2022-06-18 18:18:15 +00:00
onClick={() =>
2022-06-19 23:25:55 +00:00
callEffect({
listOfElements,
links,
isListOrdered,
listAfterMergeSort,
})
2022-06-18 18:18:15 +00:00
}
2022-06-18 03:20:44 +00:00
>
{"Redraw graph"}
</button>
</div>
);
2022-06-17 22:48:11 +00:00
}