diff --git a/space_flow/package-lock.json b/space_flow/package-lock.json index 737d5c7de..f850d0ec8 100644 --- a/space_flow/package-lock.json +++ b/space_flow/package-lock.json @@ -23,11 +23,13 @@ "@types/react-dom": "^18.0.10", "axios": "^1.3.2", "react": "^18.2.0", + "react-cookie": "^4.1.1", "react-dom": "^18.2.0", "react-icons": "^4.7.1", "react-laag": "^2.0.5", "react-router-dom": "^6.8.1", "react-scripts": "5.0.1", + "react-tabs": "^6.0.0", "reactflow": "^11.5.5", "tailwindcss": "^3.2.6", "typescript": "^4.9.5", @@ -4211,6 +4213,11 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz", + "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==" + }, "node_modules/@types/d3": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz", @@ -4490,6 +4497,15 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -14801,6 +14817,19 @@ "node": ">=14" } }, + "node_modules/react-cookie": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.1.1.tgz", + "integrity": "sha512-ffn7Y7G4bXiFbnE+dKhHhbP+b8I34mH9jqnm8Llhj89zF4nPxPutxHT1suUqMeCEhLDBI7InYwf1tpaSoK5w8A==", + "dependencies": { + "@types/hoist-non-react-statics": "^3.0.1", + "hoist-non-react-statics": "^3.0.0", + "universal-cookie": "^4.0.0" + }, + "peerDependencies": { + "react": ">= 16.3.0" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -15070,6 +15099,18 @@ } } }, + "node_modules/react-tabs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-6.0.0.tgz", + "integrity": "sha512-8jKLKrlwxmn5/+xsa76yi27ZdB8E/WhlhQZw739O5UlOeUGtVoVeWnpqIewv/KbjTw7gQf/uA51zWUNt4IVygQ==", + "dependencies": { + "clsx": "^1.1.0", + "prop-types": "^15.5.0" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -16819,6 +16860,23 @@ "node": ">=8" } }, + "node_modules/universal-cookie": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz", + "integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==", + "dependencies": { + "@types/cookie": "^0.3.3", + "cookie": "^0.4.0" + } + }, + "node_modules/universal-cookie/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", diff --git a/space_flow/package.json b/space_flow/package.json index 2009ca52d..4837d084b 100644 --- a/space_flow/package.json +++ b/space_flow/package.json @@ -18,11 +18,13 @@ "@types/react-dom": "^18.0.10", "axios": "^1.3.2", "react": "^18.2.0", + "react-cookie": "^4.1.1", "react-dom": "^18.2.0", "react-icons": "^4.7.1", "react-laag": "^2.0.5", "react-router-dom": "^6.8.1", "react-scripts": "5.0.1", + "react-tabs": "^6.0.0", "reactflow": "^11.5.5", "tailwindcss": "^3.2.6", "typescript": "^4.9.5", diff --git a/space_flow/src/App.tsx b/space_flow/src/App.tsx index 9b1a39a0d..32b87e72f 100644 --- a/space_flow/src/App.tsx +++ b/space_flow/src/App.tsx @@ -12,6 +12,8 @@ import { locationContext } from "./contexts/locationContext"; import FlowPage from "./pages/FlowPage"; import Sidebar from "./components/SidebarComponent"; import Header from "./components/HeaderComponent"; +import { TabsProvider } from "./contexts/tabsContext"; +import { TabsManager } from "./pages/FlowPage/flowManager"; export default function App() { var _ = require("lodash"); @@ -96,7 +98,7 @@ export default function App() { return ( //need parent component with width and height
- +
@@ -108,7 +110,7 @@ export default function App() {
{/* Primary column */}
- +
@@ -142,7 +144,6 @@ export default function App() { ))} - ); } diff --git a/space_flow/src/CustomNodes/BooleanNode/index.tsx b/space_flow/src/CustomNodes/BooleanNode/index.tsx index 60b161c30..733d657f6 100644 --- a/space_flow/src/CustomNodes/BooleanNode/index.tsx +++ b/space_flow/src/CustomNodes/BooleanNode/index.tsx @@ -8,7 +8,7 @@ import { typesContext } from "../../contexts/typesContext"; export default function BooleanNode({ data }) { const [enabled, setEnabled] = useState(false); - const {types} = useContext(typesContext); + const {types, deleteNode} = useContext(typesContext); return (
@@ -21,7 +21,7 @@ export default function BooleanNode({ data }) {
diff --git a/space_flow/src/CustomNodes/InputNode/index.tsx b/space_flow/src/CustomNodes/InputNode/index.tsx index 195ac29d3..a6aa084bd 100644 --- a/space_flow/src/CustomNodes/InputNode/index.tsx +++ b/space_flow/src/CustomNodes/InputNode/index.tsx @@ -13,7 +13,7 @@ import { typesContext } from "../../contexts/typesContext"; import TextAreaComponent from "../../components/textAreaComponent"; export default function InputNode({ data }) { - const { types } = useContext(typesContext); + const {types, deleteNode} = useContext(typesContext); return (
@@ -39,7 +39,7 @@ export default function InputNode({ data }) {
+ + ) : ( +
+ {isRename ? ( + { + setIsRename(false); + if (value !== "") { + let newFlow = _.cloneDeep(flow); + newFlow.name = value; + updateFlow(newFlow); + } + }} + value={value} + onChange={(e) => { + setValue(e.target.value); + }} + /> + ) : ( + { + setIsRename(true); + setValue(flow.name); + }} + > + {flow.name} + + )} + +
+ ) + ) : ( +
+ +
+ )} + + ); +} diff --git a/space_flow/src/pages/FlowPage/index.tsx b/space_flow/src/pages/FlowPage/index.tsx index 10009d942..cbab1f045 100644 --- a/space_flow/src/pages/FlowPage/index.tsx +++ b/space_flow/src/pages/FlowPage/index.tsx @@ -5,6 +5,8 @@ import ReactFlow, { addEdge, useEdgesState, useNodesState, + ReactFlowProvider, + useReactFlow, } from "reactflow"; import { locationContext } from "../../contexts/locationContext"; import ExtraSidebar from "./components/extraSidebarComponent"; @@ -16,6 +18,8 @@ import ChatOutputNode from "../../CustomNodes/ChatOutputNode"; import InputNode from "../../CustomNodes/InputNode"; import BooleanNode from "../../CustomNodes/BooleanNode"; import { alertContext } from "../../contexts/alertContext"; +import { TabsContext } from "../../contexts/tabsContext"; +import { typesContext } from "../../contexts/typesContext"; const nodeTypes = { genericNode: GenericNode, @@ -27,17 +31,44 @@ const nodeTypes = { var _ = require("lodash"); -export default function FlowPage() { +export default function FlowPage({ flow }) { + let { updateFlow, nodeId } = useContext(TabsContext); + const { types, reactFlowInstance, setReactFlowInstance } = + useContext(typesContext); const reactFlowWrapper = useRef(null); - const getId = () => `dndnode_${_.uniqueId()}`; + function getId(){ + console.log(nodeId); + nodeId = nodeId+1 + return `dndnode_}`+nodeId; + }; const { setExtraComponent, setExtraNavigation } = useContext(locationContext); const { setErrorData } = useContext(alertContext); - const [nodes, setNodes, onNodesChange] = useNodesState([]); - const [edges, setEdges, onEdgesChange] = useEdgesState([]); - const [reactFlowInstance, setReactFlowInstance] = useState(null); + const [nodes, setNodes, onNodesChange] = useNodesState( + flow.data?.nodes ?? [] + ); + const [edges, setEdges, onEdgesChange] = useEdgesState( + flow.data?.edges ?? [] + ); + + useEffect(() => { + if (reactFlowInstance && flow) { + flow.data = reactFlowInstance.toObject(); + updateFlow(flow); + } + }, [nodes, edges]); + + useEffect(() => { + setNodes(flow?.data?.nodes ?? []); + setEdges(flow?.data?.edges ?? []); + if (reactFlowInstance) { + reactFlowInstance.setViewport( + flow?.data?.viewport ?? { x: 1, y: 0, zoom: 1 } + ); + } + }, [flow, reactFlowInstance, setEdges, setNodes]); useEffect(() => { setExtraComponent(); @@ -89,30 +120,24 @@ export default function FlowPage() { y: event.clientY - reactflowBounds.top, }); let newId = getId(); - + const newNode = { id: newId, type: - (data.type === "str" + data.type === "str" ? "inputNode" - : (data.type === "chatInput" + : data.type === "chatInput" ? "chatInputNode" - : (data.type === "chatOutput" + : data.type === "chatOutput" ? "chatOutputNode" - : (data.type === "bool" + : data.type === "bool" ? "booleanNode" - : "genericNode")))), + : "genericNode", position, data: { ...data, id: newId, value: null, - reactFlowInstance, - onDelete: () => { - setNodes( - reactFlowInstance.getNodes().filter((n) => n.id !== newId) - ); - }, }, }; setNodes((nds) => nds.concat(newNode)); @@ -128,22 +153,32 @@ export default function FlowPage() { return (
- - - - - + {Object.keys(types).length > 0 ? ( + <> + + updateFlow({ ...flow, data: reactFlowInstance.toObject() }) + } + edges={edges} + onNodesChange={onNodesChange} + onEdgesChange={onEdgesChangeMod} + onConnect={onConnect} + onLoad={setReactFlowInstance} + onInit={setReactFlowInstance} + nodeTypes={nodeTypes} + connectionLineComponent={connection} + onDragOver={onDragOver} + onDrop={onDrop} + > + + + + + + ) : ( + <> + )}
); } diff --git a/space_flow/src/utils.ts b/space_flow/src/utils.ts index d9c17191d..093742370 100644 --- a/space_flow/src/utils.ts +++ b/space_flow/src/utils.ts @@ -335,18 +335,18 @@ export function getConnectedNodes(edge: Edge, nodes: Array): Array { } export function isValidConnection( - data, - { source, target, sourceHandle, targetHandle } + { source, target, sourceHandle, targetHandle }, + reactFlowInstance ) { if ( sourceHandle.split('|')[0] === targetHandle.split("|")[0] || sourceHandle.split('|').slice(2).some((t) => t === targetHandle.split("|")[0]) || targetHandle.split("|")[0] === "str" ) { - let targetNode = data.reactFlowInstance.getNode(target).data.node; + let targetNode = reactFlowInstance.getNode(target).data.node; if (!targetNode) { if ( - !data.reactFlowInstance + !reactFlowInstance .getEdges() .find((e) => e.targetHandle === targetHandle) ) { @@ -354,7 +354,7 @@ export function isValidConnection( } } else if ( (!targetNode.template[targetHandle.split("|")[1]].list && - !data.reactFlowInstance + !reactFlowInstance .getEdges() .find((e) => e.targetHandle === targetHandle)) || targetNode.template[targetHandle.split("|")[1]].list