diff --git a/package.json b/package.json
index 8c97eea..615a8bc 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"proskomma-react-hooks": "^2.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-flow-renderer": "^10.3.12",
"react-json-view": "^1.21.3",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.0",
diff --git a/src/App.css b/src/App.css
index 7b38ffa..d97fca5 100644
--- a/src/App.css
+++ b/src/App.css
@@ -44,6 +44,22 @@ header {
}
+.graph-pane, .graph-pane-hidden {}
+
+.graph-pane-hidden {
+ display: none;
+}
+
+.react-flow {
+ width: 100%;
+ min-height: 40vh;
+ height: 40vh;
+}
+
+.react-flow__attribution.bottom.right {
+ display: none;
+}
+
.result-pane {
min-width: 69vw;
max-width: 69vw;
@@ -325,4 +341,21 @@ select {
button.editor-tab:disabled {
border-bottom: 2px solid lime;
color: #56c156;
-}
\ No newline at end of file
+}
+
+.graph-button {
+ float: left;
+ background-color: transparent;
+ color: #AAA;
+ font-weight: bold;
+ font-size: large
+}
+
+.opened-graph-button {
+ float: right;
+ background-color: transparent;
+ color: #AAA;
+ font-weight: bold;
+ font-size: large
+}
+
diff --git a/src/App.js b/src/App.js
index 084a366..68638b4 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,4 +1,4 @@
-import React, { useState} from 'react';
+import React, {useState,useCallback} from 'react';
import {useProskomma} from 'proskomma-react-hooks';
import deepCopy from 'deep-copy-all';
import StepSpec from "./components/StepSpec";
@@ -7,19 +7,116 @@ import runCallback from "./lib/runCallback";
// import DisplayResult from "./components/DisplayResult";
import DisplayIssues from "./components/DisplayIssues";
import LoadSteps from "./components/LoadSteps";
+import ReactFlow, {
+ addEdge,
+ Background,
+ useNodesState,
+ useEdgesState,
+} from 'react-flow-renderer';
import './App.css';
import EditorWrapper from './components/EditorWrapper';
+const graphStyles = { width: "100%", height: "500px" };
+
function App() {
const [specSteps, setSpecSteps] = useState([]);
const [nextStepId, setNextStepId] = useState(1);
const [results, setResults] = useState([]);
const [runIssues, setRunIssues] = useState([]);
const [expandSpecs, setExpandSpecs] = useState(true);
+ const [showGraph, setShowGraph] = useState(false)
+ const [flowInstance,setFlowInstance] = useState({})
const { proskomma } = useProskomma({ verbose: false });
+ const [nodes, setNodes, onNodesChange] = useNodesState([]);
+ const [edges, setEdges, onEdgesChange] = useEdgesState([]);
+ const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), [setEdges]);
+ const onInit = (reactFlowInstance) => {
+ // console.log('flow loaded:', reactFlowInstance);
+ setFlowInstance(reactFlowInstance)
+ }
+
+ const setNodeAndSpecStep = (specs) => {
+ setSpecSteps(specs)
+ const stepY = 60;
+ let sourceY = 0;
+ let transformY = 0;
+ let outputY = 0;
+ let position = {};
+ const mappedNodes = specs.map( step => {
+ let sourcePosition = undefined;
+ let targetPosition = undefined;
+ let type = undefined;
+ let draggable = true;
+ let colorStr = "";
+ if (step.type==='Source') {
+ sourcePosition = 'right';
+ type = 'input';
+ draggable = false;
+ position = { x: 0, y: (sourceY * stepY) };
+ sourceY++;
+ colorStr = 'green';
+ } else if (step.type==='Display') {
+ targetPosition = 'left';
+ type = 'output';
+ position = { x: 500, y: (outputY * stepY) };
+ outputY++;
+ colorStr = 'blue';
+ } else {
+ sourcePosition = 'right';
+ targetPosition = 'left';
+ position = { x: 250, y: (transformY * stepY) };
+ transformY++;
+ colorStr = 'red';
+ }
+ return {
+ ...step,
+ sourcePosition,
+ targetPosition,
+ data: { label: step.title },
+ id: step.id.toString(),
+ draggable,
+ position,
+ type,
+ style: { border: `2px solid ${colorStr}`, padding: 10 },
+ }
+ })
+ setNodes(mappedNodes)
+ const mappedEdges = []
+ specs.forEach( step => {
+ const target = step.id.toString();
+ step.inputs && step.inputs.forEach( i => {
+ const srcIdStr = i.source.match(/\d+/)
+ const findNode = mappedNodes.filter(n => n.id === srcIdStr[0])
+ if (findNode[0]) {
+ const source = findNode[0].id.toString()
+ mappedEdges.push({
+ id: `e${target}-${source}`,
+ source,
+ target
+ })
+ }
+ })
+ if (step.inputSource) {
+ const srcIdStr = step.inputSource.match(/\d+/)
+ const findNode = mappedNodes.filter(n => n.id === srcIdStr[0])
+ if (findNode[0]) {
+ const source = findNode[0].id.toString()
+ mappedEdges.push({
+ id: `e${target}-${source}`,
+ source,
+ target
+ })
+ }
+ }
+ })
+ setEdges(mappedEdges)
+ flowInstance.zoomIn()
+ flowInstance.fitView()
+ }
+
const cleanSteps = steps => {
const ret = deepCopy(steps);
for (const step of ret) {
@@ -43,7 +140,7 @@ function App() {
}
const addStepCallback = stepType => {
- setSpecSteps(
+ setNodeAndSpecStep(
[
...specSteps,
{
@@ -75,7 +172,8 @@ function App() {
newSpec[key] = newTemplate[key];
}
}
- setSpecSteps(specSteps.map(v => v.id === newSpec.id ? newSpec : v));
+
+ setNodeAndSpecStep(specSteps.map(v => v.id === newSpec.id ? newSpec : v));
}
const moveCallback = (specPosition, direction) => {
@@ -86,17 +184,18 @@ function App() {
} else {
specs.splice(specPosition + 1, 0, specSteps[specPosition]);
}
- setSpecSteps(specs);
+
+ setNodeAndSpecStep(specs);
}
- const deleteCallback = deleteId => setSpecSteps(specSteps.filter(v => v.id !== deleteId));
+ const deleteCallback = deleteId => setNodeAndSpecStep(specSteps.filter(v => v.id !== deleteId));
const clearResultsCallback = () => {
setResults([]);
setRunIssues([]);
}
- return (
+ return (
@@ -113,17 +212,51 @@ function App() {
className="tooltiptext rtooltiptext">It's called Perfidy because... oh never mind
- an IDE for PERF
+ {showGraph &&
+ setShowGraph(false)
+ }
+ >
+ X
+
+ }
-
-
-
+
+ {
+ // To do: might add interactivity features here
+ // console.log("click", element);
+ }}
+ fitView
+ >
+
+
+
+ {!showGraph &&
+ <>
+
+
+
Build your Pipeline Here
{"Spec "}
-
+
Add a Display Step
-
+
Add a Transform Step
-
+
Add a Source Step
-
+
Load Steps from File
-
+
Save Steps to File
{
@@ -185,7 +318,7 @@ function App() {
{"P>"}
-
+
Expand All Steps
<" : "<>"}
-
- {
- specSteps.map(
- (ss, n) =>
-
- )
- }
+ {(nodes && nodes.length > 0) && (
+
+ Graph view
+ setShowGraph(!showGraph)
+ }
+ >
+ {showGraph ? "[L]" : "[G]"}
+
+
+ )}
+
+ {
+ specSteps.map(
+ (ss, n) =>
+
+ )
+ }
+
-
-
-
-
+
+
+
Run the steps
>"}
-
+
See the Results of your Pipeline Here
- {"Result "}
+ {"Result "}
-
+
Delete the results
-
- {
- runIssues.length > 0 &&
-
- }
- {
- runIssues.length === 0 &&
-
- }
-
+
+ {
+ runIssues.length > 0 &&
+
+ }
+ {
+ runIssues.length === 0 &&
+
+ }
+
+
-
+ >
+ }
);
diff --git a/yarn.lock b/yarn.lock
index 6718ddf..f8b6e36 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1356,6 +1356,13 @@
dependencies:
regenerator-runtime "^0.13.4"
+"@babel/runtime@^7.18.9":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
+ integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
version "7.9.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.0.tgz#337eda67401f5b066a6f205a3113d4ac18ba495b"
@@ -3555,6 +3562,11 @@ cjs-module-lexer@^1.0.0:
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40"
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
+classcat@^5.0.3:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/classcat/-/classcat-5.0.3.tgz#38eaa0ec6eb1b10faf101bbcef2afb319c23c17b"
+ integrity sha512-6dK2ke4VEJZOFx2ZfdDAl5OhEL8lvkl6EHF92IfRePfHxQTqir5NlcNVUv+2idjDqCX2NDc8m8YSAI5NI975ZQ==
+
clean-css@^5.2.2:
version "5.3.1"
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.1.tgz#d0610b0b90d125196a2894d35366f734e5d7aa32"
@@ -4009,6 +4021,68 @@ csstype@^3.0.2:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2"
integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==
+"d3-color@1 - 3":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2"
+ integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
+
+"d3-dispatch@1 - 3":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
+ integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
+
+"d3-drag@2 - 3", d3-drag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
+ integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
+ dependencies:
+ d3-dispatch "1 - 3"
+ d3-selection "3"
+
+"d3-ease@1 - 3":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
+ integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
+
+"d3-interpolate@1 - 3":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
+ integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
+ dependencies:
+ d3-color "1 - 3"
+
+"d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
+ integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
+
+"d3-timer@1 - 3":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
+ integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
+
+"d3-transition@2 - 3":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f"
+ integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==
+ dependencies:
+ d3-color "1 - 3"
+ d3-dispatch "1 - 3"
+ d3-ease "1 - 3"
+ d3-interpolate "1 - 3"
+ d3-timer "1 - 3"
+
+d3-zoom@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3"
+ integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
+ dependencies:
+ d3-dispatch "1 - 3"
+ d3-drag "2 - 3"
+ d3-interpolate "1 - 3"
+ d3-selection "2 - 3"
+ d3-transition "2 - 3"
+
d64@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/d64/-/d64-1.0.0.tgz#4002a87e850cbfc9f9d9706b60fca613a3336e90"
@@ -8501,6 +8575,18 @@ react-error-overlay@^6.0.11:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb"
integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==
+react-flow-renderer@^10.3.12:
+ version "10.3.12"
+ resolved "https://registry.yarnpkg.com/react-flow-renderer/-/react-flow-renderer-10.3.12.tgz#ed603e7cab298aee7003e325cebc08372a4e2ca0"
+ integrity sha512-DTaz4HV0rA/qtvY80fjdb/QwIvtZEhqCQ2iAqfzFH08RjWCrLmESX4Nc400EB3CGcCK8/pDn/ta4cOS3udunTw==
+ dependencies:
+ "@babel/runtime" "^7.18.9"
+ classcat "^5.0.3"
+ d3-drag "^3.0.0"
+ d3-selection "^3.0.0"
+ d3-zoom "^3.0.0"
+ zustand "^3.7.2"
+
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -10599,3 +10685,8 @@ zen-observable@0.8.15:
version "0.8.15"
resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==
+
+zustand@^3.7.2:
+ version "3.7.2"
+ resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.7.2.tgz#7b44c4f4a5bfd7a8296a3957b13e1c346f42514d"
+ integrity sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==