Skip to main content
Glama

MCPDemo - Visual SQL Chat Platform

by Ayi456
ResizablePanel.tsx5.32 kB
import React, { useState, useRef, useCallback, useEffect } from 'react'; interface ResizablePanelProps { topContent: React.ReactNode; bottomContent: React.ReactNode; defaultTopHeight?: number; // 默认上面板高度百分比 (0-100) minTopHeight?: number; // 最小上面板高度百分比 maxTopHeight?: number; // 最大上面板高度百分比 storageKey?: string; // localStorage中的键名,用于保存用户偏好 } const ResizablePanel: React.FC<ResizablePanelProps> = ({ topContent, bottomContent, defaultTopHeight = 50, minTopHeight = 20, maxTopHeight = 80, storageKey, }) => { // 从 localStorage获取保存的高度,如果没有则使用默认值 const getInitialHeight = () => { if (typeof window !== 'undefined' && storageKey) { const saved = localStorage.getItem(storageKey); if (saved) { const height = parseFloat(saved); if (!isNaN(height) && height >= minTopHeight && height <= maxTopHeight) { return height; } } } return defaultTopHeight; }; const [topHeight, setTopHeight] = useState(getInitialHeight); const [isDragging, setIsDragging] = useState(false); const containerRef = useRef<HTMLDivElement>(null); const handleMouseDown = useCallback((e: React.MouseEvent) => { e.preventDefault(); setIsDragging(true); }, []); const handleMouseMove = useCallback( (e: MouseEvent) => { if (!isDragging || !containerRef.current) return; const container = containerRef.current; const containerRect = container.getBoundingClientRect(); const containerHeight = containerRect.height; const relativeY = e.clientY - containerRect.top; const percentageHeight = (relativeY / containerHeight) * 100; // 限制在最小和最大高度之间 const newHeight = Math.min( Math.max(percentageHeight, minTopHeight), maxTopHeight ); setTopHeight(newHeight); // 保存到localStorage if (storageKey) { localStorage.setItem(storageKey, newHeight.toString()); } }, [isDragging, minTopHeight, maxTopHeight] ); const handleMouseUp = useCallback(() => { setIsDragging(false); }, []); useEffect(() => { if (isDragging) { document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); // 防止拖拽时选中文本 document.body.style.userSelect = 'none'; document.body.style.cursor = 'ns-resize'; } else { document.body.style.userSelect = ''; document.body.style.cursor = ''; } return () => { document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); document.body.style.userSelect = ''; document.body.style.cursor = ''; }; }, [isDragging, handleMouseMove, handleMouseUp]); return ( <div ref={containerRef} className="flex flex-col h-full w-full relative"> {/* 上面板 */} <div className="overflow-hidden" style={{ height: `${topHeight}%` }} > {topContent} </div> {/* 可拖拽的分隔条 */} <div className="relative group"> {/* 拖拽热区 - 更大的可点击区域 */} <div className="absolute inset-x-0 -top-2 -bottom-2 cursor-ns-resize z-10" onMouseDown={handleMouseDown} /> {/* 分隔条视觉元素 */} <div className={` relative h-1 bg-gray-300 dark:bg-gray-600 group-hover:bg-blue-400 dark:group-hover:bg-blue-500 transition-all ${isDragging ? 'bg-blue-500 dark:bg-blue-500 h-1.5' : ''} `} > {/* 拖拽手柄图标 */} <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 pointer-events-none"> <div className="flex items-center gap-2"> <div className="flex flex-col gap-0.5"> <div className="w-0.5 h-0.5 bg-gray-500 dark:bg-gray-400 rounded-full"></div> <div className="w-0.5 h-0.5 bg-gray-500 dark:bg-gray-400 rounded-full"></div> <div className="w-0.5 h-0.5 bg-gray-500 dark:bg-gray-400 rounded-full"></div> </div> <div className="flex flex-col gap-0.5"> <div className="w-0.5 h-0.5 bg-gray-500 dark:bg-gray-400 rounded-full"></div> <div className="w-0.5 h-0.5 bg-gray-500 dark:bg-gray-400 rounded-full"></div> <div className="w-0.5 h-0.5 bg-gray-500 dark:bg-gray-400 rounded-full"></div> </div> <div className="flex flex-col gap-0.5"> <div className="w-0.5 h-0.5 bg-gray-500 dark:bg-gray-400 rounded-full"></div> <div className="w-0.5 h-0.5 bg-gray-500 dark:bg-gray-400 rounded-full"></div> <div className="w-0.5 h-0.5 bg-gray-500 dark:bg-gray-400 rounded-full"></div> </div> </div> </div> </div> </div> {/* 下面板 */} <div className="flex-1 overflow-hidden min-h-0" style={{ height: `${100 - topHeight}%` }} > {bottomContent} </div> </div> ); }; export default ResizablePanel;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ayi456/visual-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server