Переглянути джерело

setup react-flow visualizer

AykutSarac 3 роки тому
батько
коміт
fcf8dc253a
1 змінених файлів з 106 додано та 27 видалено
  1. 106 27
      src/pages/editor/LiveEditor/index.tsx

+ 106 - 27
src/pages/editor/LiveEditor/index.tsx

@@ -1,41 +1,120 @@
 import React from "react";
-import styled from "styled-components";
-import ReactFlow from "react-flow-renderer";
+import styled, { css } from "styled-components";
+import ReactFlow, {
+  Controls,
+  Elements,
+  isNode,
+  NodeExtent,
+  Position,
+  ReactFlowProvider,
+} from "react-flow-renderer";
+import { defaultValue } from "../JsonEditor";
+import { parser } from "src/utils/json-editor-parser";
+import { useLocalStorage } from "usehooks-ts";
+import dagre from "dagre";
+import { CustomNodeComponent } from "./Node";
+
+const dagreGraph = new dagre.graphlib.Graph();
+dagreGraph.setDefaultEdgeLabel(() => ({}));
+
+const nodeExtent: NodeExtent = [
+  [0, 0],
+  [1000, 1000],
+];
 
 const StyledLiveEditor = styled.div`
   width: 100%;
   height: 100%;
 `;
 
-const elements = [
-  {
-    id: "1",
-    type: "input", // input node
-    data: { label: "Input Node" },
-    position: { x: 250, y: 25 },
-  },
-  // default node
-  {
-    id: "2",
-    // you can also pass a React component as a label
-    data: { label: <div>Default Node</div> },
-    position: { x: 100, y: 125 },
-  },
-  {
-    id: "3",
-    type: "output", // output node
-    data: { label: "Output Node" },
-    position: { x: 250, y: 250 },
-  },
-  // animated edge
-  { id: "e1-2", source: "1", target: "2", animated: true },
-  { id: "e2-3", source: "2", target: "3" },
-];
+const onLayout = (
+  direction: string,
+  elements: Elements,
+  setElements: React.Dispatch<Elements>
+) => {
+  const isHorizontal = direction === "LR";
+  dagreGraph.setGraph({ rankdir: direction });
+
+  elements.forEach((el) => {
+    if (isNode(el)) {
+      dagreGraph.setNode(el.id, { width: 175, height: 50 });
+    } else {
+      dagreGraph.setEdge(el.source, el.target);
+    }
+  });
+
+  dagre.layout(dagreGraph);
+
+  const layoutedElements = elements.map((el) => {
+    if (isNode(el)) {
+      const nodeWithPosition = dagreGraph.node(el.id);
+      el.targetPosition = isHorizontal ? Position.Left : Position.Top;
+      el.sourcePosition = isHorizontal ? Position.Right : Position.Bottom;
+      el.position = {
+        x: nodeWithPosition.x + Math.random() / 1000,
+        y: nodeWithPosition.y,
+      };
+    }
+
+    return el;
+  });
+
+  setElements(layoutedElements);
+};
+
+const nodeTypes = {
+  special: CustomNodeComponent,
+};
 
 const LiveEditor: React.FC = () => {
+  const [json] = useLocalStorage("json", JSON.stringify(defaultValue));
+  const [rfInstance, setRfInstance] = React.useState<any>(null);
+  const [valid, setValid] = React.useState(true);
+  const [elements, setElements] = React.useState<Elements>(
+    parser(JSON.parse(json))
+  );
+
+  React.useEffect(() => {
+    try {
+      JSON.parse(json);
+      setElements(parser(JSON.parse(json)));
+      setValid(true);
+    } catch (error) {
+      setValid(false);
+    }
+  }, [json]);
+
+  React.useEffect(() => {
+    if (rfInstance) {
+      rfInstance.fitView();
+    }
+  }, [elements, rfInstance]);
+
+  if (!valid) return null;
+
   return (
     <StyledLiveEditor>
-      <ReactFlow elements={elements} />
+      <ReactFlowProvider>
+        <ReactFlow
+          nodeExtent={nodeExtent}
+          elements={elements}
+          nodeTypes={nodeTypes}
+          onLoad={(rf: any) => {
+            setRfInstance(rf);
+            onLayout("RL", elements, setElements);
+          }}
+        >
+          <Controls>
+            <button
+              type="button"
+              className="react-flow__controls-button react-flow__controls-zoomin"
+              onClick={() => onLayout("RL", elements, setElements)}
+            >
+              Style
+            </button>
+          </Controls>
+        </ReactFlow>
+      </ReactFlowProvider>
     </StyledLiveEditor>
   );
 };