Implemented tabs storage, only working if current tab is empty

This commit is contained in:
Lucas Oliveira 2023-02-23 01:28:24 -03:00
commit 45d6f8e569
8 changed files with 119 additions and 48 deletions

View file

@ -23,6 +23,7 @@
"@types/react-dom": "^18.0.10", "@types/react-dom": "^18.0.10",
"axios": "^1.3.2", "axios": "^1.3.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-cookie": "^4.1.1",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-icons": "^4.7.1", "react-icons": "^4.7.1",
"react-laag": "^2.0.5", "react-laag": "^2.0.5",
@ -4212,6 +4213,11 @@
"@types/node": "*" "@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": { "node_modules/@types/d3": {
"version": "7.4.0", "version": "7.4.0",
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz", "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz",
@ -4491,6 +4497,15 @@
"@types/node": "*" "@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": { "node_modules/@types/html-minifier-terser": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
@ -14802,6 +14817,19 @@
"node": ">=14" "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": { "node_modules/react-dev-utils": {
"version": "12.0.1", "version": "12.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
@ -16832,6 +16860,23 @@
"node": ">=8" "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": { "node_modules/universalify": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",

View file

@ -18,6 +18,7 @@
"@types/react-dom": "^18.0.10", "@types/react-dom": "^18.0.10",
"axios": "^1.3.2", "axios": "^1.3.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-cookie": "^4.1.1",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-icons": "^4.7.1", "react-icons": "^4.7.1",
"react-laag": "^2.0.5", "react-laag": "^2.0.5",

View file

@ -5,10 +5,11 @@ import {
nodeColors, nodeColors,
snakeToNormalCase, snakeToNormalCase,
} from "../../../../utils"; } from "../../../../utils";
import { useEffect, useRef, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import InputComponent from "../../../../components/inputComponent"; import InputComponent from "../../../../components/inputComponent";
import ToggleComponent from "../../../../components/toggleComponent"; import ToggleComponent from "../../../../components/toggleComponent";
import InputListComponent from "../../../../components/inputListComponent"; import InputListComponent from "../../../../components/inputListComponent";
import { typesContext } from "../../../../contexts/typesContext";
export default function ParameterComponent({ export default function ParameterComponent({
left, left,
@ -37,7 +38,8 @@ export default function ParameterComponent({
}, [data.id, position, updateNodeInternals]); }, [data.id, position, updateNodeInternals]);
const [enabled, setEnabled] = useState(data.node.template[name]?.value ?? false); const [enabled, setEnabled] = useState(data.node.template[name]?.value ?? false);
let disabled = data.reactFlowInstance.getEdges().some((e) => (e.sourceHandle === id)); const {reactFlowInstance} = useContext(typesContext);
let disabled = reactFlowInstance?.getEdges().some((e) => (e.sourceHandle === id)) ?? false;
return ( return (
<div ref={ref} className="w-full flex flex-wrap justify-between items-center bg-gray-50 mt-1 px-5 py-2"> <div ref={ref} className="w-full flex flex-wrap justify-between items-center bg-gray-50 mt-1 px-5 py-2">

View file

@ -1,3 +1,4 @@
import { CookiesProvider } from "react-cookie";
import { AlertProvider } from "./alertContext"; import { AlertProvider } from "./alertContext";
import { LocationProvider } from "./locationContext"; import { LocationProvider } from "./locationContext";
import PopUpProvider from "./popUpContext"; import PopUpProvider from "./popUpContext";
@ -7,15 +8,17 @@ import { TypesProvider } from "./typesContext";
export default function ContextWrapper({ children }) { export default function ContextWrapper({ children }) {
return ( return (
<> <>
<LocationProvider> <CookiesProvider>
<PopUpProvider> <LocationProvider>
<TypesProvider> <PopUpProvider>
<TabsProvider> <TypesProvider>
<AlertProvider>{children}</AlertProvider> <TabsProvider>
</TabsProvider> <AlertProvider>{children}</AlertProvider>
</TypesProvider> </TabsProvider>
</PopUpProvider> </TypesProvider>
</LocationProvider> </PopUpProvider>
</LocationProvider>
</CookiesProvider>
</> </>
); );
} }

View file

@ -1,4 +1,4 @@
import { createContext, useState } from "react"; import { createContext, useEffect, useState } from "react";
type flow={name:string,id:string,data:any} type flow={name:string,id:string,data:any}
@ -7,8 +7,8 @@ type TabsContextType={
setTabIndex:(index:number)=>void; setTabIndex:(index:number)=>void;
flows:Array<flow> flows:Array<flow>
removeFlow:(id:string)=>void; removeFlow:(id:string)=>void;
addFlow:(newFlow:flow)=>void; addFlow:()=>void;
updateFlow:(newFLow:flow)=>void; updateFlow:(newFlow:flow)=>void;
} }
const TabsContextInitialValue = { const TabsContextInitialValue = {
@ -16,8 +16,8 @@ const TabsContextInitialValue = {
setTabIndex:(index:number)=>{}, setTabIndex:(index:number)=>{},
flows:[], flows:[],
removeFlow:(id:string)=>{}, removeFlow:(id:string)=>{},
addFlow:(newFlow:flow)=>{}, addFlow:()=>{},
updateFlow:(newFLow:flow)=>{} updateFlow:(newFlow:flow)=>{}
} }
@ -27,6 +27,22 @@ export const TabsContext = createContext<TabsContextType>(TabsContextInitialValu
export function TabsProvider({children}){ export function TabsProvider({children}){
const [tabIndex,setTabIndex] = useState(0) const [tabIndex,setTabIndex] = useState(0)
const [flows,setFlows] = useState<Array<flow>>([]) const [flows,setFlows] = useState<Array<flow>>([])
const [id, setId] = useState(0);
useEffect(() => {
if(flows.length !== 0)
window.localStorage.setItem('tabsData', JSON.stringify({tabIndex, flows, id}));
}, [flows, id, tabIndex]);
useEffect(() => {
let cookie = window.localStorage.getItem('tabsData');
if(cookie){
let cookieObject = JSON.parse(cookie);
setTabIndex(cookieObject.tabIndex);
setFlows(cookieObject.flows)
setId(cookieObject.id)
}
}, [])
function removeFlow(id:string){ function removeFlow(id:string){
setFlows(prevState=>{ setFlows(prevState=>{
const newFlows = [...prevState]; const newFlows = [...prevState];
@ -42,14 +58,14 @@ export function TabsProvider({children}){
} }
} }
window.sessionStorage.setItem('tabs', JSON.stringify(newFlows));
return newFlows; return newFlows;
}) });
} }
function addFlow(newFlow: flow) { function addFlow() {
let newFlow: flow = {name: "flow"+id, id: id.toString(), data:null}
setId((old) => old+1);
setFlows(prevState => { setFlows(prevState => {
const newFlows = [...prevState, newFlow]; const newFlows = [...prevState, newFlow];
window.sessionStorage.setItem('tabs', JSON.stringify(newFlows));
return newFlows; return newFlows;
}); });
setTabIndex(flows.length); setTabIndex(flows.length);
@ -62,7 +78,6 @@ export function TabsProvider({children}){
newFlows[index].data = newFlow.data newFlows[index].data = newFlow.data
newFlows[index].name = newFlow.name newFlows[index].name = newFlow.name
} }
window.sessionStorage.setItem('tabs', JSON.stringify(newFlows));
return newFlows; return newFlows;
}); });
} }

View file

@ -1,12 +1,17 @@
import { createContext, useState } from "react"; import { ReactEventHandler, createContext, useState } from "react";
import { ReactFlowInstance } from "reactflow";
type typesContextType= type typesContextType=
{ {
reactFlowInstance: ReactFlowInstance;
setReactFlowInstance: any;
types: {}; types: {};
setTypes:(newState:{})=>void; setTypes:(newState:{})=>void;
} }
const initialValue= { const initialValue= {
reactFlowInstance: null,
setReactFlowInstance: ()=>{},
types: {}, types: {},
setTypes:()=>{}, setTypes:()=>{},
} }
@ -15,8 +20,9 @@ export const typesContext = createContext<typesContextType>(initialValue);
export function TypesProvider({children}){ export function TypesProvider({children}){
const [types, setTypes] = useState({}); const [types, setTypes] = useState({});
const [reactFlowInstance, setReactFlowInstance] = useState(null);
return ( return (
<typesContext.Provider value={{ types, setTypes}}> <typesContext.Provider value={{ types, setTypes, reactFlowInstance, setReactFlowInstance}}>
{children} {children}
</typesContext.Provider> </typesContext.Provider>
) )

View file

@ -9,14 +9,13 @@ export function TabsManager() {
const { flows, addFlow, tabIndex, setTabIndex } = useContext(TabsContext); const { flows, addFlow, tabIndex, setTabIndex } = useContext(TabsContext);
useEffect(() => { useEffect(() => {
if (flows.length === 0) { if (flows.length === 0) {
const id = _.uniqueId(); addFlow();
addFlow({ name: "flow " + id, data: null, id });
} }
}, []); }, [addFlow, flows.length]);
return ( return (
<div className="h-full w-full flex flex-col"> <div className="h-full w-full flex flex-col">
<div className="w-full flex pr-2 flex-row text-center items-center bg-gray-100"> <div className="w-full flex pr-2 flex-row text-center items-center bg-gray-100 px-2">
{flows.map((flow, index) => { {flows.map((flow, index) => {
return ( return (
<TabComponent <TabComponent
@ -29,17 +28,19 @@ export function TabsManager() {
})} })}
<TabComponent <TabComponent
onClick={() => { onClick={() => {
const id = _.uniqueId(); addFlow();
addFlow({ name: "flow" + id, data: null, id });
}} }}
selected={false} selected={false}
key={0}
flow={null} flow={null}
/> />
</div> </div>
<div className="w-full h-full"> <div className="w-full h-full">
<ReactFlowProvider> <ReactFlowProvider>
<FlowPage flow={flows[tabIndex]}></FlowPage> {flows[tabIndex] ? (
<FlowPage flow={flows[tabIndex]}></FlowPage>
) : (
<></>
)}
</ReactFlowProvider> </ReactFlowProvider>
</div> </div>
</div> </div>

View file

@ -19,6 +19,7 @@ import InputNode from "../../CustomNodes/InputNode";
import BooleanNode from "../../CustomNodes/BooleanNode"; import BooleanNode from "../../CustomNodes/BooleanNode";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { typesContext } from "../../contexts/typesContext";
const nodeTypes = { const nodeTypes = {
genericNode: GenericNode, genericNode: GenericNode,
@ -31,9 +32,9 @@ const nodeTypes = {
var _ = require("lodash"); var _ = require("lodash");
export default function FlowPage({flow}) { export default function FlowPage({flow}) {
console.log(flow?.data)
const {updateFlow} = useContext(TabsContext) const {updateFlow} = useContext(TabsContext)
const {reactFlowInstance, setReactFlowInstance} = useContext(typesContext);
const reactFlowWrapper = useRef(null); const reactFlowWrapper = useRef(null);
const getId = () => `dndnode_${_.uniqueId()}`; const getId = () => `dndnode_${_.uniqueId()}`;
@ -41,30 +42,27 @@ export default function FlowPage({flow}) {
const { setExtraComponent, setExtraNavigation } = useContext(locationContext); const { setExtraComponent, setExtraNavigation } = useContext(locationContext);
const { setErrorData } = useContext(alertContext); const { setErrorData } = useContext(alertContext);
const [nodes, setNodes, onNodesChange] = useNodesState(flow?.data?.nodes || []); const [nodes, setNodes, onNodesChange] = useNodesState(flow.data?.nodes ?? []);
const [edges, setEdges, onEdgesChange] = useEdgesState(flow?.data?.edges || []); const [edges, setEdges, onEdgesChange] = useEdgesState(flow.data?.edges ?? []);
const [reactFlowInstance, setReactFlowInstance] = useState(null);
const { setViewport} = useReactFlow();
useEffect(()=>{
if(flow?.data && reactFlowInstance){
setViewport(flow.data.viewport)
}
},[nodes, edges, flow, reactFlowInstance, setViewport])
useEffect(()=>{ useEffect(()=>{
if(reactFlowInstance && flow){ if(reactFlowInstance && flow){
flow.data =reactFlowInstance.toObject() flow.data = reactFlowInstance.toObject();
updateFlow(flow) updateFlow(flow);
} }
},[nodes,edges]) },[nodes,edges])
useEffect(()=>{ useEffect(()=>{
setNodes(flow?.data?.nodes || []) console.log(flow);
setEdges(flow?.data?.edges || []) setNodes(flow?.data?.nodes ?? [])
setEdges(flow?.data?.edges ?? [])
},[flow]) },[flow])
useEffect(() => {
console.log(nodes);
console.log(edges);
}, [edges, nodes])
useEffect(() => { useEffect(() => {
setExtraComponent(<ExtraSidebar />); setExtraComponent(<ExtraSidebar />);
setExtraNavigation({ title: "Nodes" }); setExtraNavigation({ title: "Nodes" });
@ -133,7 +131,6 @@ export default function FlowPage({flow}) {
...data, ...data,
id: newId, id: newId,
value: null, value: null,
reactFlowInstance,
onDelete: () => { onDelete: () => {
setNodes( setNodes(
reactFlowInstance.getNodes().filter((n) => n.id !== newId) reactFlowInstance.getNodes().filter((n) => n.id !== newId)
@ -156,6 +153,7 @@ export default function FlowPage({flow}) {
<div className="w-full h-full" ref={reactFlowWrapper}> <div className="w-full h-full" ref={reactFlowWrapper}>
<ReactFlow <ReactFlow
nodes={nodes} nodes={nodes}
defaultViewport={flow?.data?.viewport ?? {x: 1, y: 0, zoom: 1}}
onMove={()=>updateFlow({...flow,data:reactFlowInstance.toObject()})} onMove={()=>updateFlow({...flow,data:reactFlowInstance.toObject()})}
edges={edges} edges={edges}
onNodesChange={onNodesChange} onNodesChange={onNodesChange}