From a0d9a063408f371e3c5295153888e434e21669ff Mon Sep 17 00:00:00 2001
From: Joris <joris.l@hotmail.com>
Date: Wed, 9 Feb 2022 15:14:33 +0100
Subject: [PATCH] fix(schema): library for schema works

---
 .husky/pre-commit                             |   4 +
 .../src/components/schema/schema.stories.tsx  |  45 +++++
 .../src/components/schema/schema.tsx          |  37 ++++
 libs/schema/schema-usecases/.babelrc          |   3 +
 libs/schema/schema-usecases/.eslintrc.json    |  18 ++
 libs/schema/schema-usecases/README.md         |   7 +
 libs/schema/schema-usecases/jest.config.js    |  14 ++
 libs/schema/schema-usecases/project.json      |  23 +++
 libs/schema/schema-usecases/src/index.ts      |   1 +
 .../src/lib/schema-schema-usecases.spec.ts    |   7 +
 .../src/lib/schema-schema-usecases.ts         | 178 ++++++++++++++++++
 libs/schema/schema-usecases/tsconfig.json     |  19 ++
 libs/schema/schema-usecases/tsconfig.lib.json |  10 +
 .../schema/schema-usecases/tsconfig.spec.json |  19 ++
 libs/shared/data-access/store/src/index.ts    |   2 +
 .../data-access/store/src/lib/schemaSlice.ts  |  38 +++-
 package.json                                  |   5 +-
 tsconfig.base.json                            |   3 +
 workspace.json                                |   1 +
 yarn.lock                                     | 121 +++++++++++-
 20 files changed, 546 insertions(+), 9 deletions(-)
 create mode 100644 .husky/pre-commit
 create mode 100644 apps/web-graphpolaris/src/components/schema/schema.stories.tsx
 create mode 100644 apps/web-graphpolaris/src/components/schema/schema.tsx
 create mode 100644 libs/schema/schema-usecases/.babelrc
 create mode 100644 libs/schema/schema-usecases/.eslintrc.json
 create mode 100644 libs/schema/schema-usecases/README.md
 create mode 100644 libs/schema/schema-usecases/jest.config.js
 create mode 100644 libs/schema/schema-usecases/project.json
 create mode 100644 libs/schema/schema-usecases/src/index.ts
 create mode 100644 libs/schema/schema-usecases/src/lib/schema-schema-usecases.spec.ts
 create mode 100644 libs/schema/schema-usecases/src/lib/schema-schema-usecases.ts
 create mode 100644 libs/schema/schema-usecases/tsconfig.json
 create mode 100644 libs/schema/schema-usecases/tsconfig.lib.json
 create mode 100644 libs/schema/schema-usecases/tsconfig.spec.json

diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 000000000..449fcdee1
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,4 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+npm test
diff --git a/apps/web-graphpolaris/src/components/schema/schema.stories.tsx b/apps/web-graphpolaris/src/components/schema/schema.stories.tsx
new file mode 100644
index 000000000..490388470
--- /dev/null
+++ b/apps/web-graphpolaris/src/components/schema/schema.stories.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import Schema from './schema';
+import { ComponentStory, ComponentMeta } from '@storybook/react';
+import { Provider } from 'react-redux';
+import { configureStore } from '@reduxjs/toolkit';
+import schemaSlice, {
+  setSchema,
+} from 'libs/shared/data-access/store/src/lib/schemaSlice';
+
+export default {
+  /* 👇 The title prop is optional.
+   * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
+   * to learn how to generate automatic titles
+   */
+  title: 'Schema',
+  component: Schema,
+  decorators: [
+    (story) => (
+      <div style={{ padding: '3rem' }}>
+        <Provider store={Mockstore}>{story()}</Provider>
+      </div>
+    ),
+  ],
+} as ComponentMeta<typeof Schema>;
+
+const Template: ComponentStory<typeof Schema> = (args) => <Schema {...args} />;
+
+// A super-simple mock of a redux store
+const Mockstore = configureStore({
+  reducer: {
+    schema: schemaSlice,
+  },
+});
+
+export const TestWithSchema = Template.bind({});
+
+TestWithSchema.play = async () => {
+  const dispatch = Mockstore.dispatch;
+  dispatch(
+    setSchema({
+      id: 1,
+      // mock schema hiero
+    })
+  );
+};
diff --git a/apps/web-graphpolaris/src/components/schema/schema.tsx b/apps/web-graphpolaris/src/components/schema/schema.tsx
new file mode 100644
index 000000000..fd4b20c91
--- /dev/null
+++ b/apps/web-graphpolaris/src/components/schema/schema.tsx
@@ -0,0 +1,37 @@
+import { useSchema } from '@graphpolaris/shared/data-access/store';
+//import { useEffect } from 'react';
+import ReactFlow from 'react-flow-renderer';
+import styled from 'styled-components';
+import { createReactFlowElements } from '@graphpolaris/schema/schema-usecases';
+
+interface Props {
+  content: string;
+}
+
+const Div = styled.div`
+  background-color: red;
+  font: 'Arial';
+`;
+
+const Schema = (props: Props) => {
+  const dbschema = useSchema();
+
+  // In case the schema is updated
+  // useEffect(() => {
+  //   console.log('update schema useEffect');
+  // }, [dbschema]);
+
+  return (
+    <Div>
+      <ReactFlow elements={createReactFlowElements(dbschema)} />
+    </Div>
+  );
+};
+
+export default Schema;
+
+// Fix layout of the schema
+// create reactflow elements on xy coords
+// connect reactflow elements together
+
+// maybe ook gelijk instellingen knoppie fixen op alle panels
diff --git a/libs/schema/schema-usecases/.babelrc b/libs/schema/schema-usecases/.babelrc
new file mode 100644
index 000000000..cf7ddd99c
--- /dev/null
+++ b/libs/schema/schema-usecases/.babelrc
@@ -0,0 +1,3 @@
+{
+  "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]]
+}
diff --git a/libs/schema/schema-usecases/.eslintrc.json b/libs/schema/schema-usecases/.eslintrc.json
new file mode 100644
index 000000000..3456be9b9
--- /dev/null
+++ b/libs/schema/schema-usecases/.eslintrc.json
@@ -0,0 +1,18 @@
+{
+  "extends": ["../../../.eslintrc.json"],
+  "ignorePatterns": ["!**/*"],
+  "overrides": [
+    {
+      "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
+      "rules": {}
+    },
+    {
+      "files": ["*.ts", "*.tsx"],
+      "rules": {}
+    },
+    {
+      "files": ["*.js", "*.jsx"],
+      "rules": {}
+    }
+  ]
+}
diff --git a/libs/schema/schema-usecases/README.md b/libs/schema/schema-usecases/README.md
new file mode 100644
index 000000000..52ab1f83a
--- /dev/null
+++ b/libs/schema/schema-usecases/README.md
@@ -0,0 +1,7 @@
+# schema-schema-usecases
+
+This library was generated with [Nx](https://nx.dev).
+
+## Running unit tests
+
+Run `nx test schema-schema-usecases` to execute the unit tests via [Jest](https://jestjs.io).
diff --git a/libs/schema/schema-usecases/jest.config.js b/libs/schema/schema-usecases/jest.config.js
new file mode 100644
index 000000000..3ce30ca8d
--- /dev/null
+++ b/libs/schema/schema-usecases/jest.config.js
@@ -0,0 +1,14 @@
+module.exports = {
+  displayName: 'schema-schema-usecases',
+  preset: '../../../jest.preset.js',
+  globals: {
+    'ts-jest': {
+      tsconfig: '<rootDir>/tsconfig.spec.json',
+    },
+  },
+  transform: {
+    '^.+\\.[tj]sx?$': 'ts-jest',
+  },
+  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
+  coverageDirectory: '../../../coverage/libs/schema/schema-usecases',
+};
diff --git a/libs/schema/schema-usecases/project.json b/libs/schema/schema-usecases/project.json
new file mode 100644
index 000000000..39846a002
--- /dev/null
+++ b/libs/schema/schema-usecases/project.json
@@ -0,0 +1,23 @@
+{
+  "root": "libs/schema/schema-usecases",
+  "sourceRoot": "libs/schema/schema-usecases/src",
+  "projectType": "library",
+  "targets": {
+    "lint": {
+      "executor": "@nrwl/linter:eslint",
+      "outputs": ["{options.outputFile}"],
+      "options": {
+        "lintFilePatterns": ["libs/schema/schema-usecases/**/*.ts"]
+      }
+    },
+    "test": {
+      "executor": "@nrwl/jest:jest",
+      "outputs": ["coverage/libs/schema/schema-usecases"],
+      "options": {
+        "jestConfig": "libs/schema/schema-usecases/jest.config.js",
+        "passWithNoTests": true
+      }
+    }
+  },
+  "tags": []
+}
diff --git a/libs/schema/schema-usecases/src/index.ts b/libs/schema/schema-usecases/src/index.ts
new file mode 100644
index 000000000..5d9846859
--- /dev/null
+++ b/libs/schema/schema-usecases/src/index.ts
@@ -0,0 +1 @@
+export * from './lib/schema-schema-usecases';
diff --git a/libs/schema/schema-usecases/src/lib/schema-schema-usecases.spec.ts b/libs/schema/schema-usecases/src/lib/schema-schema-usecases.spec.ts
new file mode 100644
index 000000000..f2130094e
--- /dev/null
+++ b/libs/schema/schema-usecases/src/lib/schema-schema-usecases.spec.ts
@@ -0,0 +1,7 @@
+import { schemaSchemaUsecases } from './schema-schema-usecases';
+
+describe('schemaSchemaUsecases', () => {
+  it('should work', () => {
+    expect(schemaSchemaUsecases()).toEqual('schema-schema-usecases');
+  });
+});
diff --git a/libs/schema/schema-usecases/src/lib/schema-schema-usecases.ts b/libs/schema/schema-usecases/src/lib/schema-schema-usecases.ts
new file mode 100644
index 000000000..0eee56b95
--- /dev/null
+++ b/libs/schema/schema-usecases/src/lib/schema-schema-usecases.ts
@@ -0,0 +1,178 @@
+import Graph from 'graphology';
+import * as cs from 'cytoscape';
+import { setSchema, store } from '@graphpolaris/shared/data-access/store';
+import { Elements, Node, Edge } from 'react-flow-renderer';
+
+type CytoNode = {
+  data: {
+    id: string;
+    type: string;
+    source?: string;
+    target?: string;
+    position?: {
+      x: number;
+      y: number;
+    };
+  };
+};
+
+// Layouts a given schema
+export function handleSchemaLayout(graph: Graph): void {
+  const layout = createSchemaLayout(graph);
+
+  layout.then((cy) => {
+    cy.cy.elements().forEach((elem) => {
+      const position = elem.position();
+
+      graph.setNodeAttribute(elem.id(), 'x', position.x);
+      graph.setNodeAttribute(elem.id(), 'y', position.y);
+    });
+
+    store.dispatch(setSchema(graph));
+  });
+}
+
+// Creates a schema layout (async)
+function createSchemaLayout(graph: Graph): Promise<cytoscape.EventObject> {
+  const cytonodes: CytoNode[] = trimSchema(graph);
+
+  const cy = cs({
+    elements: cytonodes,
+  });
+
+  const options = {
+    name: 'cose',
+
+    // Whether to animate while running the layout
+    // true : Animate continuously as the layout is running
+    // false : Just show the end result
+    // 'end' : Animate with the end result, from the initial positions to the end positions
+    animate: true,
+
+    // Easing of the animation for animate:'end'
+    animationEasing: undefined,
+
+    // The duration of the animation for animate:'end'
+    animationDuration: undefined,
+
+    // A function that determines whether the node should be animated
+    // All nodes animated by default on animate enabled
+    // Non-animated nodes are positioned immediately when the layout starts
+    // animateFilter: function (node: any, i: any) {
+    //   return true;
+    // },
+
+    // The layout animates only after this many milliseconds for animate:true
+    // (prevents flashing on fast runs)
+    animationThreshold: 250,
+
+    // Number of iterations between consecutive screen positions update
+    refresh: 20,
+
+    // Whether to fit the network view after when done
+    fit: true,
+
+    // Padding on fit
+    padding: 60, //30
+
+    // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
+    boundingBox: undefined,
+
+    // Excludes the label when calculating node bounding boxes for the layout algorithm
+    nodeDimensionsIncludeLabels: false,
+
+    // Randomize the initial positions of the nodes (true) or use existing positions (false)
+    randomize: false,
+
+    // Extra spacing between components in non-compound graphs
+    componentSpacing: 200, //  40
+
+    // Node repulsion (non overlapping) multiplier
+    nodeRepulsion: function (node: any) {
+      return 2048;
+    },
+
+    // Node repulsion (overlapping) multiplier
+    nodeOverlap: 8, //4
+
+    // Ideal edge (non nested) length
+    idealEdgeLength: function (edge: any) {
+      return 32; //32
+    },
+
+    // Divisor to compute edge forces
+    edgeElasticity: function (edge: any) {
+      return 32;
+    },
+
+    // Nesting factor (multiplier) to compute ideal edge length for nested edges
+    nestingFactor: 1.2,
+
+    // Gravity force (constant)
+    gravity: 1,
+
+    // Maximum number of iterations to perform
+    numIter: 1000,
+
+    // Initial temperature (maximum node displacement)
+    initialTemp: 1000,
+
+    // Cooling factor (how the temperature is reduced between consecutive iterations
+    coolingFactor: 0.99,
+
+    // Lower temperature threshold (below this point the layout will end)
+    minTemp: 1.0,
+  };
+
+  const layout = cy.layout(options);
+
+  layout.run();
+
+  return layout.pon('layoutstop');
+}
+
+// Takes the schema as input and creates a list of nodes and edges in a format that the layouting algorithm can use.
+function trimSchema(graph: Graph): CytoNode[] {
+  const cytonodes: CytoNode[] = [];
+
+  graph.forEachNode((node) => {
+    cytonodes.push({
+      data: { id: node, type: 'node' },
+    });
+  });
+
+  graph.forEachEdge((edge, _attributes, source, target) => {
+    cytonodes.push({
+      data: { id: edge, type: 'edge', source: source, target: target },
+    });
+  });
+
+  return cytonodes;
+}
+
+// Takes the schema as an imput and creates basic react flow elements for them.
+export function createReactFlowElements(graph: Graph): Elements<Node | Edge> {
+  const initialElements: Elements<Node | Edge> = [];
+
+  graph.forEachNode((node, attributes) => {
+    const newNode: Node = {
+      id: node,
+      data: {
+        label: attributes.name,
+      },
+      position: { x: attributes.x, y: attributes.y },
+    };
+    initialElements.push(newNode);
+  });
+
+  graph.forEachEdge((edge, _attributes, source, target) => {
+    const newEdge: Edge = {
+      id: edge,
+      source: source,
+      target: target,
+    };
+    initialElements.push(newEdge);
+  });
+
+  return initialElements;
+}
diff --git a/libs/schema/schema-usecases/tsconfig.json b/libs/schema/schema-usecases/tsconfig.json
new file mode 100644
index 000000000..355f7fda9
--- /dev/null
+++ b/libs/schema/schema-usecases/tsconfig.json
@@ -0,0 +1,19 @@
+{
+  "extends": "../../../tsconfig.base.json",
+  "files": [],
+  "include": [],
+  "references": [
+    {
+      "path": "./tsconfig.lib.json"
+    },
+    {
+      "path": "./tsconfig.spec.json"
+    }
+  ],
+  "compilerOptions": {
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "noImplicitReturns": true,
+    "noFallthroughCasesInSwitch": true
+  }
+}
diff --git a/libs/schema/schema-usecases/tsconfig.lib.json b/libs/schema/schema-usecases/tsconfig.lib.json
new file mode 100644
index 000000000..efdd77fbf
--- /dev/null
+++ b/libs/schema/schema-usecases/tsconfig.lib.json
@@ -0,0 +1,10 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../../../dist/out-tsc",
+    "declaration": true,
+    "types": []
+  },
+  "include": ["**/*.ts"],
+  "exclude": ["**/*.spec.ts"]
+}
diff --git a/libs/schema/schema-usecases/tsconfig.spec.json b/libs/schema/schema-usecases/tsconfig.spec.json
new file mode 100644
index 000000000..d8716fecf
--- /dev/null
+++ b/libs/schema/schema-usecases/tsconfig.spec.json
@@ -0,0 +1,19 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../../../dist/out-tsc",
+    "module": "commonjs",
+    "types": ["jest", "node"]
+  },
+  "include": [
+    "**/*.test.ts",
+    "**/*.spec.ts",
+    "**/*.test.tsx",
+    "**/*.spec.tsx",
+    "**/*.test.js",
+    "**/*.spec.js",
+    "**/*.test.jsx",
+    "**/*.spec.jsx",
+    "**/*.d.ts"
+  ]
+}
diff --git a/libs/shared/data-access/store/src/index.ts b/libs/shared/data-access/store/src/index.ts
index 8251e55ee..d950f6edc 100644
--- a/libs/shared/data-access/store/src/index.ts
+++ b/libs/shared/data-access/store/src/index.ts
@@ -1,5 +1,7 @@
 export * from './lib/store';
 export * from './lib/hooks';
+
+export { setSchema, readInSchemaFromBackend } from './lib/schemaSlice';
 export {
   selectGraphQueryResult,
   selectGraphQueryResultLinks,
diff --git a/libs/shared/data-access/store/src/lib/schemaSlice.ts b/libs/shared/data-access/store/src/lib/schemaSlice.ts
index a8a22e05b..4bd77364d 100644
--- a/libs/shared/data-access/store/src/lib/schemaSlice.ts
+++ b/libs/shared/data-access/store/src/lib/schemaSlice.ts
@@ -42,6 +42,10 @@ export const schemaSlice = createSlice({
   // `createSlice` will infer the state type from the `initialState` argument
   initialState,
   reducers: {
+    setSchema: (state, action: PayloadAction<DirectedGraph>) => {
+      state.graphologySerialized = action.payload.export();
+    },
+
     readInSchemaFromBackend: (
       state,
       action: PayloadAction<SchemaFromBackend>
@@ -49,22 +53,44 @@ export const schemaSlice = createSlice({
       const { nodes, edges } = action.payload;
       // Instantiate a directed graph that allows self loops and parallel edges
       const schema = new DirectedGraph({ allowSelfLoops: true, multi: true });
+
+      // The graph schema needs a node for each node AND edge. These need then be connected
+
       nodes.forEach((node) => {
-        schema.addNode(node.name, { attributes: node.attributes });
+        schema.addNode(node.name, {
+          name: node.name,
+          attributes: node.attributes,
+          x: 0,
+          y: 0,
+        });
       });
 
-      edges.forEach((edge) =>
-        schema.addDirectedEdgeWithKey(edge.name, edge.from, edge.to, {
+      // The name of the edge will be name + from + to, since edge names are not unique
+      edges.forEach((edge) => {
+        const edgeID = edge.name + edge.from + edge.to;
+
+        // This node is the actual edge
+        schema.addNode(edgeID, {
+          name: edge.name,
+          attributes: edge.attributes,
+          from: edge.from,
+          to: edge.to,
           collection: edge.collection,
-        })
-      );
+          x: 0,
+          y: 0,
+        });
+
+        // These lines are simply for keeping the schema together
+        schema.addDirectedEdgeWithKey(edgeID + 'f', edge.from, edgeID);
+        schema.addDirectedEdgeWithKey(edgeID + 't', edgeID, edge.to);
+      });
 
       state.graphologySerialized = schema.export();
     },
   },
 });
 
-export const { readInSchemaFromBackend } = schemaSlice.actions;
+export const { readInSchemaFromBackend, setSchema } = schemaSlice.actions;
 
 // Select the schema and convert it to a graphology object
 export const selectSchema = (state: RootState) =>
diff --git a/package.json b/package.json
index 64fe528c6..c6242a66f 100644
--- a/package.json
+++ b/package.json
@@ -12,13 +12,16 @@
   "dependencies": {
     "@commitlint/config-conventional": "^16.0.0",
     "@reduxjs/toolkit": "^1.7.1",
+    "@types/cytoscape": "^3.19.4",
     "@types/react-grid-layout": "^1.3.0",
     "@types/styled-components": "^5.1.21",
     "core-js": "^3.6.5",
+    "cytoscape": "^3.21.0",
     "graphology": "^0.23.2",
     "graphology-types": "^0.23.0",
     "react": "17.0.2",
     "react-dom": "17.0.2",
+    "react-flow-renderer": "^9.7.4",
     "react-grid-layout": "^1.3.3",
     "react-json-view": "^1.21.3",
     "react-redux": "^7.2.6",
@@ -65,7 +68,7 @@
     "eslint-plugin-jsx-a11y": "6.5.1",
     "eslint-plugin-react": "7.27.0",
     "eslint-plugin-react-hooks": "4.3.0",
-    "husky": "^7.0.4",
+    "husky": "^7.0.0",
     "jest": "27.2.3",
     "prettier": "^2.5.1",
     "react-test-renderer": "17.0.2",
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 5a2caf532..ceb128bb7 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -15,6 +15,9 @@
     "skipDefaultLibCheck": true,
     "baseUrl": ".",
     "paths": {
+      "@graphpolaris/schema/schema-usecases": [
+        "libs/schema/schema-usecases/src/index.ts"
+      ],
       "@graphpolaris/shared/data-access/store": [
         "libs/shared/data-access/store/src/index.ts"
       ]
diff --git a/workspace.json b/workspace.json
index 1a59e05a3..812b87061 100644
--- a/workspace.json
+++ b/workspace.json
@@ -1,6 +1,7 @@
 {
   "version": 2,
   "projects": {
+    "schema-schema-usecases": "libs/schema/schema-usecases",
     "shared-data-access-store": "libs/shared/data-access/store",
     "web-graphpolaris": "apps/web-graphpolaris",
     "web-graphpolaris-e2e": "apps/web-graphpolaris-e2e"
diff --git a/yarn.lock b/yarn.lock
index 948c5e109..43c74ec78 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3973,6 +3973,11 @@
   dependencies:
     "@types/node" "*"
 
+"@types/cytoscape@^3.19.4":
+  version "3.19.4"
+  resolved "https://registry.yarnpkg.com/@types/cytoscape/-/cytoscape-3.19.4.tgz#f41214103b80ff3d7d8741bacc32265ed90e45b5"
+  integrity sha512-0IozTg1vdZrA3nuAK5o9Pa8nl2INUnTaXwcGwoiALDcsD8/TiVnp0Zi+R1IiPRG6edoy0Ya61/3osFLtfkhhmw==
+
 "@types/eslint-scope@^3.7.0":
   version "3.7.3"
   resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224"
@@ -6265,6 +6270,11 @@ class-utils@^0.3.5:
     isobject "^3.0.0"
     static-extend "^0.1.1"
 
+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@^4.2.3:
   version "4.2.4"
   resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178"
@@ -7305,6 +7315,79 @@ cypress@^9.1.0:
     untildify "^4.0.0"
     yauzl "^2.10.0"
 
+cytoscape@^3.21.0:
+  version "3.21.0"
+  resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.21.0.tgz#8a73f6f0f3a44f948e266ac7df9b2eff65e8bd3c"
+  integrity sha512-xPINMzQNGN4WIog93gYRScT4y/CyZMmqunnhU59/8LynhWwzepJtUydMfvIPuz5TmJ9rSCMdw6rmxhfbb1eofw==
+  dependencies:
+    heap "^0.2.6"
+    lodash.debounce "^4.0.8"
+    lodash.get "^4.4.2"
+    lodash.set "^4.3.2"
+    lodash.topath "^4.5.2"
+
+"d3-color@1 - 3":
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.0.1.tgz#03316e595955d1fcd39d9f3610ad41bb90194d0a"
+  integrity sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw==
+
+"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":
+  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"
+
 damerau-levenshtein@^1.0.7:
   version "1.0.8"
   resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
@@ -9533,6 +9616,11 @@ he@^1.2.0:
   resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
   integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
 
+heap@^0.2.6:
+  version "0.2.7"
+  resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc"
+  integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==
+
 highlight.js@^10.1.1, highlight.js@~10.7.0:
   version "10.7.3"
   resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
@@ -9797,7 +9885,7 @@ human-signals@^2.1.0:
   resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
   integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
 
-husky@^7.0.4:
+husky@^7.0.0:
   version "7.0.4"
   resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535"
   integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==
@@ -11515,6 +11603,11 @@ lodash.flow@^3.3.0:
   resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a"
   integrity sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=
 
+lodash.get@^4.4.2:
+  version "4.4.2"
+  resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+  integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
+
 lodash.isequal@^4.0.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
@@ -11540,6 +11633,16 @@ lodash.once@^4.1.1:
   resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
   integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
 
+lodash.set@^4.3.2:
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
+  integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=
+
+lodash.topath@^4.5.2:
+  version "4.5.2"
+  resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009"
+  integrity sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=
+
 lodash.uniq@4.5.0, lodash.uniq@^4.5.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
@@ -13685,7 +13788,7 @@ react-dom@17.0.2:
     object-assign "^4.1.1"
     scheduler "^0.20.2"
 
-react-draggable@^4.0.0, react-draggable@^4.0.3, react-draggable@^4.4.3:
+react-draggable@^4.0.0, react-draggable@^4.0.3, react-draggable@^4.4.3, react-draggable@^4.4.4:
   version "4.4.4"
   resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.4.tgz#5b26d9996be63d32d285a426f41055de87e59b2f"
   integrity sha512-6e0WdcNLwpBx/YIDpoyd2Xb04PB0elrDrulKUgdrIlwuYvxh5Ok9M+F8cljm8kPXXs43PmMzek9RrB1b7mLMqA==
@@ -13714,6 +13817,20 @@ react-fast-compare@^3.0.1, react-fast-compare@^3.2.0:
   resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
   integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
 
+react-flow-renderer@^9.7.4:
+  version "9.7.4"
+  resolved "https://registry.yarnpkg.com/react-flow-renderer/-/react-flow-renderer-9.7.4.tgz#11394c05ca953b650e2017d056c075fd3df9075c"
+  integrity sha512-GxHBXzkn8Y+TEG8pul7h6Fjo4cKrT0kW9UQ34OAGZqAnSBLbBsx9W++TF8GiULBbTn3O8o7HtHxux685Op10mQ==
+  dependencies:
+    "@babel/runtime" "^7.16.7"
+    classcat "^5.0.3"
+    d3-selection "^3.0.0"
+    d3-zoom "^3.0.0"
+    fast-deep-equal "^3.1.3"
+    react-draggable "^4.4.4"
+    react-redux "^7.2.6"
+    redux "^4.1.2"
+
 react-grid-layout@^1.3.3:
   version "1.3.3"
   resolved "https://registry.yarnpkg.com/react-grid-layout/-/react-grid-layout-1.3.3.tgz#6d255ae036bb2371a46effd494ca4dd6f7224311"
-- 
GitLab