♻️ refactor(sidebar): 清理 V2 rail 残留死代码

- 删除未被调用的 rail connection button/group 渲染闭包

- 移除对应的折叠、拖拽状态和 badge/host 辅助函数

- 保留当前可达的 active connection 状态展示和树拖拽排序逻辑
This commit is contained in:
Syngnat
2026-06-19 15:16:31 +08:00
parent 540dbc2a28
commit f6ebfc2e44

View File

@@ -46,7 +46,6 @@ import { Tree, message, Dropdown, MenuProps, Input, Button, Form, Badge, Checkbo
FolderAddOutlined,
SaveOutlined,
EditOutlined,
DownOutlined,
SearchOutlined,
KeyOutlined,
ThunderboltOutlined,
@@ -1190,18 +1189,6 @@ const Sidebar: React.FC<{
const allSavedQueriesNode = useMemo<TreeNode | null>(() => {
return buildAllSavedQueriesTreeNode(savedQueries, connections);
}, [connections, savedQueries]);
const v2RailConnectionGroups = useMemo(
() => buildV2RailConnectionGroups(connections, connectionTags, sidebarRootOrder),
[connections, connectionTags, sidebarRootOrder],
);
const [collapsedV2RailGroupIds, setCollapsedV2RailGroupIds] = useState<string[]>([]);
const collapsedV2RailGroupIdSet = useMemo(
() => new Set(collapsedV2RailGroupIds),
[collapsedV2RailGroupIds],
);
const hasV2RailConnectionGroups = v2RailConnectionGroups.some((group) => !group.isUngrouped);
const [draggingV2RailRootToken, setDraggingV2RailRootToken] = useState('');
const snapshotTreeSelectionBeforeDrag = useCallback(() => {
treeDragSelectionSnapshotRef.current = {
selectedKeys: [...selectedKeys],
@@ -6828,101 +6815,6 @@ const Sidebar: React.FC<{
return connectionStatusMap.get(connectionId) || 'idle';
}, [connectionStatusMap]);
const toggleV2RailConnectionGroup = useCallback((groupId: string) => {
setCollapsedV2RailGroupIds((prev) => (
prev.includes(groupId)
? prev.filter((id) => id !== groupId)
: [...prev, groupId]
));
}, []);
const handleV2RailRootDrop = useCallback((
sourceToken: string,
targetToken: string,
insertBefore: boolean,
) => {
if (!sourceToken || !targetToken || sourceToken === targetToken) {
return;
}
reorderSidebarRoot(sourceToken, targetToken, insertBefore);
}, [reorderSidebarRoot]);
const getRailConnectionTypeLabel = (conn: SavedConnection): string => {
const iconType = resolveConnectionIconType(conn);
if (iconType === 'mysql' || iconType === 'mariadb' || iconType === 'oceanbase') return 'MY';
if (iconType === 'postgres') return 'PG';
if (iconType === 'gaussdb') return 'GS';
if (iconType === 'redis') return 'R';
if (iconType === 'mongodb') return 'MO';
if (iconType === 'oracle') return 'OR';
if (iconType === 'sqlserver') return 'SS';
if (iconType === 'starrocks') return 'SR';
if (iconType === 'sqlite') return 'SQ';
if (iconType === 'jvm') return 'JV';
return iconType.slice(0, 2).toUpperCase() || 'DB';
};
const getRailConnectionHostLabel = (conn: SavedConnection): string => {
const hostTokens = resolveConnectionHostTokens(conn.config);
const primaryHost = String(hostTokens[0] || '').trim().replace(/^\[|\]$/g, '');
if (primaryHost) {
if (/^(localhost|127(?:\.\d{1,3}){3}|0\.0\.0\.0)$/i.test(primaryHost)) {
return 'LO';
}
if (/^\d{1,3}(?:\.\d{1,3}){3}$/.test(primaryHost)) {
const lastSegment = primaryHost.split('.').pop() || '';
return lastSegment.slice(-3).toUpperCase() || 'IP';
}
if (primaryHost.includes(':') && /^[a-f0-9:]+$/i.test(primaryHost)) {
const lastSegment = primaryHost.split(':').filter(Boolean).pop() || '';
return lastSegment.slice(-3).toUpperCase() || 'IP';
}
const hostFragments = primaryHost
.split(/[^a-z0-9\u4e00-\u9fa5]+/i)
.map((entry) => entry.trim())
.filter(Boolean);
if (hostFragments.length >= 2) {
return `${hostFragments[0][0] || ''}${hostFragments[1][0] || ''}`.toUpperCase();
}
const hostToken = hostFragments[0] || primaryHost.split('.')[0] || '';
if (hostToken) {
return hostToken.replace(/[^a-z0-9\u4e00-\u9fa5]/gi, '').slice(0, 3).toUpperCase() || 'DB';
}
}
return getRailConnectionTypeLabel(conn);
};
const getRailConnectionBadgeLabel = (conn: SavedConnection): string => {
const connectionName = String(conn.name || '').trim();
const cjkParts = connectionName.match(/[\u4e00-\u9fa5]/g);
if (cjkParts && cjkParts.length > 0) {
return cjkParts.slice(0, 2).join('');
}
const latinTokens = connectionName.match(/[a-z0-9]+/gi) || [];
if (latinTokens.length >= 2) {
const firstToken = latinTokens[0] || '';
const secondToken = latinTokens[1] || '';
return `${firstToken[0] || ''}${secondToken[0] || ''}`.toUpperCase();
}
if (latinTokens.length === 1) {
const token = latinTokens[0];
const alphaPrefix = token.match(/^[a-z]+/i)?.[0] || '';
if (alphaPrefix) {
return alphaPrefix.slice(0, 3).toUpperCase();
}
const trailingDigits = token.match(/(\d{2,})$/)?.[1];
if (trailingDigits) {
return trailingDigits.slice(-3).toUpperCase();
}
return token.slice(0, 3).toUpperCase();
}
return getRailConnectionTypeLabel(conn);
};
const openV2ConnectionContextMenu = (
event: React.MouseEvent,
connOrNode: SavedConnection | TreeNode,
@@ -8893,165 +8785,6 @@ const Sidebar: React.FC<{
}
};
const renderV2RailConnectionButton = (conn: SavedConnection) => {
const accent = resolveConnectionAccentColor(conn);
const status = buildRailConnectionStatus(conn.id);
const badgeLabel = getRailConnectionBadgeLabel(conn);
const hostLabel = getRailConnectionHostLabel(conn);
const title = `${conn.name} · ${resolveConnectionHostSummary(conn.config) || conn.config.type}`;
const rootToken = buildSidebarRootConnectionToken(conn.id);
return (
<Tooltip key={conn.id} title={title} placement="right">
<button
type="button"
className={`gn-v2-rail-item${conn.id === activeConnectionId ? ' is-active' : ''}`}
draggable
onDragStart={(event) => {
snapshotTreeSelectionBeforeDrag();
treeDragSelectSuppressUntilRef.current = Date.now() + 600;
setDraggingV2RailRootToken(rootToken);
setIsTreeDragging(true);
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', rootToken);
}}
onDragEnd={() => {
restoreTreeSelectionAfterDrag();
setDraggingV2RailRootToken('');
setIsTreeDragging(false);
}}
onDragOver={(event) => {
if (!draggingV2RailRootToken || draggingV2RailRootToken === rootToken) {
return;
}
event.preventDefault();
event.stopPropagation();
event.dataTransfer.dropEffect = 'move';
}}
onDrop={(event) => {
if (!draggingV2RailRootToken || draggingV2RailRootToken === rootToken) {
return;
}
event.preventDefault();
event.stopPropagation();
const rect = event.currentTarget.getBoundingClientRect();
const insertBefore = event.clientY < rect.top + rect.height / 2;
handleV2RailRootDrop(draggingV2RailRootToken, rootToken, insertBefore);
restoreTreeSelectionAfterDrag();
setDraggingV2RailRootToken('');
setIsTreeDragging(false);
}}
onClick={() => selectConnectionFromRail(conn)}
onContextMenu={(event) => openV2ConnectionContextMenu(event, conn)}
aria-label={`切换到连接 ${conn.name}`}
title={title}
data-v2-rail-host-context-menu-trigger="true"
>
<span className="gn-v2-rail-active-bar" />
<span className="gn-v2-rail-badge-wrap">
<span className="gn-v2-rail-badge" style={{ background: accent }}>
{badgeLabel}
</span>
<span className={`gn-v2-rail-status is-${status}`} />
</span>
<span className="gn-v2-rail-fallback">{hostLabel}</span>
</button>
</Tooltip>
);
};
const renderV2RailConnectionGroup = (group: V2RailConnectionGroup) => {
const collapsed = collapsedV2RailGroupIdSet.has(group.id);
const groupTitle = group.name || t('connection.sidebar.group.untitled');
const rootToken = group.rootToken;
return (
<div
key={group.id}
className={`gn-v2-rail-group${group.isUngrouped ? ' is-ungrouped' : ''}${collapsed ? ' is-collapsed' : ''}`}
data-v2-rail-connection-group="true"
draggable
onDragStart={(event) => {
snapshotTreeSelectionBeforeDrag();
treeDragSelectSuppressUntilRef.current = Date.now() + 600;
setDraggingV2RailRootToken(rootToken);
setIsTreeDragging(true);
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', rootToken);
}}
onDragEnd={() => {
restoreTreeSelectionAfterDrag();
setDraggingV2RailRootToken('');
setIsTreeDragging(false);
}}
onDragOver={(event) => {
if (!draggingV2RailRootToken || draggingV2RailRootToken === rootToken) {
return;
}
event.preventDefault();
event.stopPropagation();
event.dataTransfer.dropEffect = 'move';
}}
onDrop={(event) => {
if (!draggingV2RailRootToken || draggingV2RailRootToken === rootToken) {
return;
}
event.preventDefault();
event.stopPropagation();
const rect = event.currentTarget.getBoundingClientRect();
const insertBefore = event.clientY < rect.top + rect.height / 2;
handleV2RailRootDrop(draggingV2RailRootToken, rootToken, insertBefore);
restoreTreeSelectionAfterDrag();
setDraggingV2RailRootToken('');
setIsTreeDragging(false);
}}
>
{hasV2RailConnectionGroups && (
<Tooltip title={t('connection.sidebar.group.meta', { count: group.connections.length.toLocaleString() })} placement="right">
<button
type="button"
className={`gn-v2-rail-group-header${group.isUngrouped ? ' is-ungrouped' : ''}`}
onClick={() => toggleV2RailConnectionGroup(group.id)}
onContextMenu={(event) => {
if (group.isUngrouped) return;
event.preventDefault();
event.stopPropagation();
const position = resolveSidebarContextMenuPosition(event.clientX, event.clientY);
setContextMenu({
x: position.x,
y: position.y,
sourceX: event.clientX,
sourceY: event.clientY,
items: [],
kind: 'v2-connection-group',
node: group,
rootClassName: 'gn-v2-table-context-menu-popup',
overlayStyle: { width: 264, maxWidth: 'calc(100vw - 24px)' },
maxHeight: position.maxHeight,
});
}}
aria-label={t(collapsed ? 'connection.sidebar.group.expandAria' : 'connection.sidebar.group.collapseAria', { name: groupTitle })}
aria-expanded={!collapsed}
title={t('connection.sidebar.group.meta', { count: group.connections.length.toLocaleString() })}
data-v2-rail-connection-group-header="true"
>
<span className="gn-v2-rail-group-chevron">
<DownOutlined />
</span>
<span className="gn-v2-rail-group-title">{groupTitle}</span>
<span className="gn-v2-rail-group-count">{group.connections.length}</span>
</button>
</Tooltip>
)}
{!collapsed && (
<div className="gn-v2-rail-group-items">
{group.connections.map(renderV2RailConnectionButton)}
</div>
)}
</div>
);
};
const v2RailObjectActionsLabel = t('sidebar.rail.object_actions');
const v2RailSystemActionsLabel = t('sidebar.rail.system_actions');
const v2NewGroupLabel = t('sidebar.action.new_group');