import { useState, useCallback, ReactNode } from 'react'; import { ReactFlow, applyNodeChanges, applyEdgeChanges, addEdge, Background, Controls, ReactFlowProvider, NodeTypes, NodeChange, EdgeChange, MarkerType, NodeMouseHandler, OnConnect, MiniMap, IsValidConnection, EdgeMouseHandler } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; import { AppNode, AppEdge, NodeData, EdgeData, Settings } from './types/graph'; import CustomNode from './components/CustomNode'; import NodeEditor from './components/NodeEditor'; import EdgeEditor from './components/EdgeEditor' import Toggle from "./components/Toggle" import { generateWireGuardPrivateKey } from './utils/wireguardConfig'; import './App.css'; const initialNodes: AppNode[] = []; const initialEdges: AppEdge[] = []; const initialSettings : Settings = { listenPort: 38894, mtu: 1420, }; const nodeTypes : NodeTypes = { custom: CustomNode, }; function generateNodeData(count: number) : NodeData | null { const privateKey = generateWireGuardPrivateKey(); const node : NodeData = { id: `n-${crypto.randomUUID()}`, label: `Node-${count + 1}`, privateKey: privateKey } return node } function FlowContent(): ReactNode { const [nodes, setNodes] = useState(initialNodes); const [edges, setEdges] = useState(initialEdges); const [settings, setSettings] = useState(initialSettings); const [editingNode, setEditingNode] = useState(undefined); const [editingEdge, setEditingEdge] = useState(undefined); const [enableTwoWay, setEnableTwoWay] = useState(false); const onNodesChange = useCallback( (changes: NodeChange[]) => setNodes((nds) => applyNodeChanges(changes, nds)), []); const onEdgesChange = useCallback( (changes : EdgeChange[]) => setEdges((eds) => applyEdgeChanges(changes, eds)), []); const onConnect = useCallback( (params) => { const id = `e-${crypto.randomUUID()}` const newEdge : AppEdge = { ...params, id: id, animated: !enableTwoWay, markerEnd: enableTwoWay ? undefined : { type: MarkerType.ArrowClosed }, data : { id: id, isTwoWayEdge: enableTwoWay } } return setEdges((eds) => addEdge(newEdge, eds)); }, [enableTwoWay]); const onNodeClick = useCallback>( (_event, node) => setEditingNode(node.data), []); const onEdgeClick = useCallback>( (_event, edge) => setEditingEdge(edge.data), [] ) const validateConnection = useCallback( (connection) => { if (connection.source === connection.target) { return false; } const isDuplicate = edges.some( (edge) => ( (edge.source === connection.source && edge.target === connection.target) || (edge.source === connection.target && edge.target === connection.source) ) ); return !isDuplicate; }, [edges]); const handleAddNode = (): void => { const result = generateNodeData(nodes.length); if(result == null) return; const newNode: AppNode = { id: result.id, position: { x: 0, y: 0 }, data: result, type: 'custom', selected: true }; setNodes((prev) => [...prev.map(node => ({ ...node, selected: false })), newNode]); }; const handleUpdateNode = (updatedData: NodeData): void => { setNodes((prev) => prev.map((node) => { if (node.data.id === editingNode?.id) { return { ...node, data: updatedData }; } return node; }) ); setEditingNode(undefined); }; const handleUpdateEdge = (updatedData: EdgeData): void => { setEdges((prev) => prev.map((edge) => { if (edge.data && edge.data.id === editingEdge?.id) { return { ...edge, data: updatedData }; } return edge; }) ); setEditingNode(undefined); }; return (
{ if (n.type === 'input') return 'blue'; return '#eee'; }} maskColor="rgba(0, 0, 0, 0.1)" position="bottom-right" // 也可以是 top-right 等 />
setEnableTwoWay(checked)} >双向连接
{editingNode && ( setEditingNode(undefined)} settings={settings} /> )} {editingEdge && ( setEditingEdge(undefined)} /> )}
); } export default function App(): ReactNode { return ( ); }