import React, { useCallback, useRef, useState } from 'react'; import { Layout } from 'antd'; import { TopNav } from '../Nav/TopNav'; import { ConnectionTree } from '../Sidebar/ConnectionTree'; import { ConnectionProperties } from '../Sidebar/ConnectionProperties'; import { SessionTabs } from '../Tabs/SessionTabs'; const { Content } = Layout; const MIN_SIDEBAR = 180; const MAX_SIDEBAR = 600; const DEFAULT_SIDEBAR = 260; const MIN_PROPERTIES_HEIGHT = 150; const DEFAULT_TREE_FRACTION = 0.6; export const MainLayout: React.FC = () => { const [sidebarWidth, setSidebarWidth] = useState(DEFAULT_SIDEBAR); const [treeFraction, setTreeFraction] = useState(DEFAULT_TREE_FRACTION); const isResizing = useRef(false); const isVResizing = useRef(false); const sidebarRef = useRef(null); // Horizontal sidebar resize const onMouseDown = useCallback((e: React.MouseEvent) => { e.preventDefault(); isResizing.current = true; document.body.style.cursor = 'col-resize'; document.body.style.userSelect = 'none'; const onMouseMove = (me: MouseEvent) => { if (!isResizing.current) return; const newWidth = Math.min(MAX_SIDEBAR, Math.max(MIN_SIDEBAR, me.clientX)); setSidebarWidth(newWidth); }; const onMouseUp = () => { isResizing.current = false; document.body.style.cursor = ''; document.body.style.userSelect = ''; document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); }; document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); }, []); // Vertical splitter between tree and properties const onVMouseDown = useCallback((e: React.MouseEvent) => { e.preventDefault(); isVResizing.current = true; document.body.style.cursor = 'row-resize'; document.body.style.userSelect = 'none'; const onMouseMove = (me: MouseEvent) => { if (!isVResizing.current || !sidebarRef.current) return; const rect = sidebarRef.current.getBoundingClientRect(); const totalHeight = rect.height; const relativeY = me.clientY - rect.top; const propertiesHeight = totalHeight - relativeY; // Enforce min heights if (propertiesHeight < MIN_PROPERTIES_HEIGHT || relativeY < MIN_PROPERTIES_HEIGHT) return; setTreeFraction(relativeY / totalHeight); }; const onMouseUp = () => { isVResizing.current = false; document.body.style.cursor = ''; document.body.style.userSelect = ''; document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); }; document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); }, []); return (
{/* Resizable sidebar */}
{/* Connection tree (top) */}
{/* Vertical splitter */}
{ (e.currentTarget as HTMLDivElement).style.background = '#1677ff40'; }} onMouseLeave={(e) => { if (!isVResizing.current) (e.currentTarget as HTMLDivElement).style.background = 'transparent'; }} /> {/* Connection properties (bottom) */}
{/* Resize handle */}
{ (e.currentTarget as HTMLDivElement).style.background = '#1677ff40'; }} onMouseLeave={(e) => { if (!isResizing.current) (e.currentTarget as HTMLDivElement).style.background = 'transparent'; }} /> {/* Main content area */}
); };