blob: 406f2e9dbb467b92dcb6cd95392d0c0652cc6d04 [file] [log] [blame]
gio5f2f1002025-03-20 18:38:48 +04001import '@xyflow/react/dist/style.css';
2import { ReactFlow, Background, Controls, Connection, BackgroundVariant, Edge, useReactFlow, Panel } from '@xyflow/react';
3import { useStateStore, AppState, AppNode } from '@/lib/state';
4import { useShallow } from "zustand/react/shallow";
5import { useCallback, useMemo } from 'react';
6import { NodeGatewayHttps } from "@/components/node-gateway-https";
7import { NodeApp } from '@/components/node-app';
8import { NodeVolume } from './node-volume';
9import { NodePostgreSQL } from './node-postgresql';
10import { NodeMongoDB } from './node-mongodb';
11import { NodeGithub } from './node-github';
12import { Actions } from './actions';
13import { NodeGatewayTCP } from './node-gateway-tcp';
14
15const selector = (state: AppState) => ({
16 nodes: state.nodes,
17 edges: state.edges,
18 onNodesChange: state.onNodesChange,
19 onEdgesChange: state.onEdgesChange,
20 onConnect: state.onConnect,
21});
22
23export function Canvas() {
24 const { nodes, edges, onNodesChange, onEdgesChange, onConnect } = useStateStore(
25 useShallow(selector),
26 );
27 const flow = useReactFlow();
28 const nodeTypes = useMemo(() => ({
29 "app": NodeApp,
30 "gateway-https": NodeGatewayHttps,
31 "gateway-tcp": NodeGatewayTCP,
32 "volume": NodeVolume,
33 "postgresql": NodePostgreSQL,
34 "mongodb": NodeMongoDB,
35 "github": NodeGithub,
36 }), []);
37 const isValidConnection = useCallback((c: Edge | Connection) => {
38 if (c.source === c.target) {
39 return false;
40 }
41 const sn = flow.getNode(c.source)! as AppNode;
42 const tn = flow.getNode(c.target)! as AppNode;
43 if (sn.type === "github") {
44 return c.targetHandle === "repository";
45 }
46 if (sn.type === "app") {
47 if (c.sourceHandle === "ports" && (!sn.data.ports || sn.data.ports.length === 0)) {
48 return false;
49 }
50 }
51 if (tn.type === "gateway-https") {
52 if (c.targetHandle === "https" && tn.data.https !== undefined) {
53 return false;
54 }
55 }
56 if (sn.type === "volume") {
57 if (c.targetHandle !== "volume") {
58 return false;
59 }
60 return true;
61 }
62 return true;
63 }, [flow]);
64 return (
65 <div style={{ width: '100%', height: '100%' }}>
66 <ReactFlow
67 nodeTypes={nodeTypes}
68 nodes={nodes}
69 edges={edges}
70 onNodesChange={onNodesChange}
71 onEdgesChange={onEdgesChange}
72 onConnect={onConnect}
73 isValidConnection={isValidConnection}
74 fitView
75 proOptions={{ hideAttribution: true }}
76 >
77 <Controls />
78 <Background variant={BackgroundVariant.Dots} gap={12} size={1} />
79 <Panel position="bottom-right">
80 <Actions />
81 </Panel>
82 </ReactFlow>
83 </div>
84 );
85}
86
87export default Canvas;