feat: Enhance GraphView component with connection creation delegation and fullscreen mode handling, and update ServerManager to support new connection logic
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m25s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m25s
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useMemo, useCallback, useEffect, useRef } from 'react';
|
||||
import React, { useMemo, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
ReactFlow,
|
||||
Background,
|
||||
@@ -7,14 +7,17 @@ import {
|
||||
useNodesState,
|
||||
addEdge,
|
||||
MarkerType,
|
||||
Handle,
|
||||
Position,
|
||||
} from '@xyflow/react';
|
||||
import '@xyflow/react/dist/style.css';
|
||||
import { IconZoomIn, IconZoomOut, IconMaximize, IconMinimize } from '@tabler/icons-react';
|
||||
|
||||
function GraphView({ servers, connections }) {
|
||||
function GraphView({ servers, connections, onCreateConnection }) {
|
||||
const flowRef = useRef(null);
|
||||
const instanceRef = useRef(null);
|
||||
const containerRef = useRef(null);
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
|
||||
const getTunnelStyle = useCallback((tunnelType) => {
|
||||
const map = {
|
||||
@@ -83,7 +86,16 @@ function GraphView({ servers, connections }) {
|
||||
setEdges(initialEdgesData);
|
||||
}, [initialEdgesData, setEdges]);
|
||||
|
||||
const onConnect = useCallback((params) => setEdges((eds) => addEdge({ ...params, type: 'default' }, eds)), [setEdges]);
|
||||
const onConnect = useCallback(
|
||||
(params) => {
|
||||
// Делегируем создание связи в родителя, чтобы сохранить единую логику хранения
|
||||
if (!params?.source || !params?.target || params.source === params.target) return;
|
||||
if (onCreateConnection) {
|
||||
onCreateConnection({ from: params.source, to: params.target });
|
||||
}
|
||||
},
|
||||
[onCreateConnection]
|
||||
);
|
||||
|
||||
const nodeTypes = useMemo(
|
||||
() => ({
|
||||
@@ -110,6 +122,8 @@ function GraphView({ servers, connections }) {
|
||||
background: '#fff',
|
||||
}}
|
||||
>
|
||||
{/* Точка входа соединений */}
|
||||
<Handle type="target" position={Position.Left} style={{ background: countryColor }} />
|
||||
<div className="px-2 py-1 d-flex align-items-center" style={{ background: '#f8fafc', borderBottom: '1px solid #eef2f7' }}>
|
||||
<div className="d-flex align-items-center" style={{ gap: 6 }}>
|
||||
<span style={{ fontSize: 16, lineHeight: '1' }}>{flag}</span>
|
||||
@@ -132,6 +146,8 @@ function GraphView({ servers, connections }) {
|
||||
<div style={{ fontSize: 12, color: '#334155' }}>{s.ip}</div>
|
||||
<div style={{ fontSize: 10, color: '#6b7280' }}>{s.provider}</div>
|
||||
</div>
|
||||
{/* Точка исхода соединений */}
|
||||
<Handle type="source" position={Position.Right} style={{ background: countryColor }} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
@@ -161,6 +177,23 @@ function GraphView({ servers, connections }) {
|
||||
return () => clearTimeout(t);
|
||||
}, [servers, connections]);
|
||||
|
||||
// Трек полноэкранного режима и авто-fit после входа/выхода
|
||||
useEffect(() => {
|
||||
const handler = () => {
|
||||
const fs = Boolean(document.fullscreenElement);
|
||||
setIsFullscreen(fs);
|
||||
if (instanceRef.current) {
|
||||
setTimeout(() => {
|
||||
try {
|
||||
instanceRef.current.fitView({ padding: 0.2 });
|
||||
} catch {}
|
||||
}, 60);
|
||||
}
|
||||
};
|
||||
document.addEventListener('fullscreenchange', handler);
|
||||
return () => document.removeEventListener('fullscreenchange', handler);
|
||||
}, []);
|
||||
|
||||
if (!servers || servers.length === 0) {
|
||||
return (
|
||||
<div className="text-center py-5">
|
||||
@@ -173,7 +206,18 @@ function GraphView({ servers, connections }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={containerRef} style={{ width: '100%', height: 500, border: '1px solid #e5e7eb', borderRadius: 8, overflow: 'hidden', position: 'relative' }}>
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: isFullscreen ? '100vh' : 500,
|
||||
border: '1px solid #e5e7eb',
|
||||
borderRadius: 8,
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
background: '#f8fafc',
|
||||
}}
|
||||
>
|
||||
<ReactFlow
|
||||
ref={flowRef}
|
||||
nodes={nodes}
|
||||
@@ -188,6 +232,7 @@ function GraphView({ servers, connections }) {
|
||||
markerEnd: { type: MarkerType.ArrowClosed },
|
||||
}}
|
||||
onInit={onInit}
|
||||
style={{ width: '100%', height: '100%', background: '#f8fafc' }}
|
||||
>
|
||||
<Background variant="dots" gap={20} size={1} color="#e5e7eb" />
|
||||
<MiniMap
|
||||
@@ -237,7 +282,7 @@ function GraphView({ servers, connections }) {
|
||||
}}
|
||||
title="Во весь экран"
|
||||
>
|
||||
{document.fullscreenElement ? <IconMinimize size={16} /> : <IconMaximize size={16} />}
|
||||
{isFullscreen ? <IconMinimize size={16} /> : <IconMaximize size={16} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -765,7 +765,13 @@ function ServerManager() {
|
||||
</div>
|
||||
<GraphView
|
||||
servers={servers.filter(s => (!graphCountryFilter || s.country === graphCountryFilter) && (!graphProviderFilter || s.provider === graphProviderFilter))}
|
||||
connections={connections.filter(c => graphTunnelFilters[c.tunnelType])}
|
||||
connections={connections.filter(c => graphTunnelFilters[c.tunnelType])}
|
||||
onCreateConnection={({ from, to }) => {
|
||||
// Открываем модал добавления связи и проставляем выбранные узлы
|
||||
setNewConnection(prev => ({ ...prev, from, to }));
|
||||
const el = document.querySelector('#add-connection-anchor');
|
||||
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}}
|
||||
/>
|
||||
<div className="mt-3">
|
||||
<div className="d-flex align-items-center gap-3 flex-wrap">
|
||||
@@ -777,7 +783,7 @@ function ServerManager() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card w-100 mb-4">
|
||||
<div id="add-connection-anchor" className="card w-100 mb-4">
|
||||
<div className="card-header">
|
||||
<h3 className="card-title mb-0">Добавить связь между серверами</h3>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user