blob: 406f2e9dbb467b92dcb6cd95392d0c0652cc6d04 [file] [log] [blame]
import '@xyflow/react/dist/style.css';
import { ReactFlow, Background, Controls, Connection, BackgroundVariant, Edge, useReactFlow, Panel } from '@xyflow/react';
import { useStateStore, AppState, AppNode } from '@/lib/state';
import { useShallow } from "zustand/react/shallow";
import { useCallback, useMemo } from 'react';
import { NodeGatewayHttps } from "@/components/node-gateway-https";
import { NodeApp } from '@/components/node-app';
import { NodeVolume } from './node-volume';
import { NodePostgreSQL } from './node-postgresql';
import { NodeMongoDB } from './node-mongodb';
import { NodeGithub } from './node-github';
import { Actions } from './actions';
import { NodeGatewayTCP } from './node-gateway-tcp';
const selector = (state: AppState) => ({
nodes: state.nodes,
edges: state.edges,
onNodesChange: state.onNodesChange,
onEdgesChange: state.onEdgesChange,
onConnect: state.onConnect,
});
export function Canvas() {
const { nodes, edges, onNodesChange, onEdgesChange, onConnect } = useStateStore(
useShallow(selector),
);
const flow = useReactFlow();
const nodeTypes = useMemo(() => ({
"app": NodeApp,
"gateway-https": NodeGatewayHttps,
"gateway-tcp": NodeGatewayTCP,
"volume": NodeVolume,
"postgresql": NodePostgreSQL,
"mongodb": NodeMongoDB,
"github": NodeGithub,
}), []);
const isValidConnection = useCallback((c: Edge | Connection) => {
if (c.source === c.target) {
return false;
}
const sn = flow.getNode(c.source)! as AppNode;
const tn = flow.getNode(c.target)! as AppNode;
if (sn.type === "github") {
return c.targetHandle === "repository";
}
if (sn.type === "app") {
if (c.sourceHandle === "ports" && (!sn.data.ports || sn.data.ports.length === 0)) {
return false;
}
}
if (tn.type === "gateway-https") {
if (c.targetHandle === "https" && tn.data.https !== undefined) {
return false;
}
}
if (sn.type === "volume") {
if (c.targetHandle !== "volume") {
return false;
}
return true;
}
return true;
}, [flow]);
return (
<div style={{ width: '100%', height: '100%' }}>
<ReactFlow
nodeTypes={nodeTypes}
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
isValidConnection={isValidConnection}
fitView
proOptions={{ hideAttribution: true }}
>
<Controls />
<Background variant={BackgroundVariant.Dots} gap={12} size={1} />
<Panel position="bottom-right">
<Actions />
</Panel>
</ReactFlow>
</div>
);
}
export default Canvas;