From 1dea343aa230b6aa29c36eaf74dfefb373ed37df Mon Sep 17 00:00:00 2001 From: Syngnat Date: Fri, 19 Jun 2026 18:03:28 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(sidebar):=20?= =?UTF-8?q?=E6=8A=BD=E5=87=BA=20V2=20=E6=A0=91=E6=A0=87=E9=A2=98=E6=B8=B2?= =?UTF-8?q?=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sidebar.locate-toolbar.test.tsx | 14 +- frontend/src/components/Sidebar.tsx | 139 ++-------------- .../components/sidebar/SidebarTreeTitle.tsx | 152 ++++++++++++++++++ 3 files changed, 173 insertions(+), 132 deletions(-) create mode 100644 frontend/src/components/sidebar/SidebarTreeTitle.tsx diff --git a/frontend/src/components/Sidebar.locate-toolbar.test.tsx b/frontend/src/components/Sidebar.locate-toolbar.test.tsx index 3c2a9000..786cf0f8 100644 --- a/frontend/src/components/Sidebar.locate-toolbar.test.tsx +++ b/frontend/src/components/Sidebar.locate-toolbar.test.tsx @@ -70,6 +70,7 @@ const readSidebarSource = () => [ readSourceFile('./sidebar/SidebarExternalSqlWorkflow.tsx'), readSourceFile('./sidebar/useSidebarTreeLoaders.tsx'), readSourceFile('./sidebar/SidebarEntityModals.tsx'), + readSourceFile('./sidebar/SidebarTreeTitle.tsx'), readSourceFile('./sidebarV2Utils.ts'), ].join('\n'); const readLegacyNodeMenuSource = () => readSourceFile('./sidebar/sidebarLegacyNodeMenu.tsx'); @@ -2340,9 +2341,9 @@ describe('Sidebar locate toolbar', () => { const externalSqlFlowStart = source.indexOf('const handleAddExternalSQLDirectory = async (node: any) => {'); const externalSqlFlowEnd = source.indexOf('const cancelSQLFileExecution = () => {', externalSqlFlowStart); const externalSqlFlowSource = source.slice(externalSqlFlowStart, externalSqlFlowEnd); - const treeTitleStart = source.indexOf('const renderV2TreeTitle = (node: any, hoverTitle: string, statusBadge: React.ReactNode) => {'); - const treeTitleEnd = source.indexOf('const selectConnectionFromRail', treeTitleStart); - const treeTitleSource = source.slice(treeTitleStart, treeTitleEnd); + const treeTitleSource = readSourceFile('./sidebar/SidebarTreeTitle.tsx'); + const treeTitleStart = 0; + const treeTitleEnd = treeTitleSource.length; const externalSqlMenuStart = legacyMenuSource.indexOf("if (node.type === 'external-sql-root') {", legacyMenuSource.indexOf('// 已存查询节点的右键菜单')); const externalSqlMenuEnd = legacyMenuSource.indexOf("if (node.type === 'external-sql-directory') {", externalSqlMenuStart); const externalSqlMenuSource = legacyMenuSource.slice(externalSqlMenuStart, externalSqlMenuEnd); @@ -2593,11 +2594,12 @@ describe('Sidebar locate toolbar', () => { expect(tableGroupCallSource).not.toContain(rawSnippet); }); - const treeTitleStart = sidebarSource.indexOf('const renderV2TreeTitle'); - const treeTitleEnd = sidebarSource.indexOf('const selectConnectionFromRail', treeTitleStart); + const treeTitleModuleSource = readSourceFile('./sidebar/SidebarTreeTitle.tsx'); + const treeTitleStart = treeTitleModuleSource.indexOf('export const renderSidebarV2TreeTitle'); + const treeTitleEnd = treeTitleModuleSource.length; expect(treeTitleStart).toBeGreaterThanOrEqual(0); expect(treeTitleEnd).toBeGreaterThan(treeTitleStart); - const treeTitleSource = sidebarSource.slice(treeTitleStart, treeTitleEnd); + const treeTitleSource = treeTitleModuleSource.slice(treeTitleStart, treeTitleEnd); expect(treeTitleSource).toContain('const objectGroupTitle = resolveV2ObjectGroupTitle(node);'); expect(treeTitleSource).toContain('if (objectGroupTitle) return objectGroupTitle;'); diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 41176a82..e87f1931 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -19,6 +19,7 @@ import { } from './sidebar/useSidebarBatchExport'; import { SidebarBatchExportModals } from './sidebar/SidebarBatchExportModals'; import { SidebarEntityModals } from './sidebar/SidebarEntityModals'; +import { renderSidebarV2TreeTitle } from './sidebar/SidebarTreeTitle'; import { normalizeDriverType, useSidebarTreeLoaders, @@ -108,9 +109,7 @@ import { Tree, message, Dropdown, MenuProps, Input, Button, Form, Badge, Checkbo MoreOutlined, ToolOutlined, SettingOutlined, - BarsOutlined, - StarFilled, - StarOutlined + BarsOutlined } from '@ant-design/icons'; import { buildSidebarRootConnectionToken, @@ -4151,129 +4150,17 @@ const Sidebar: React.FC<{ void fetchV2TableContextMenuStats(node); }; - const renderV2TreeTitle = (node: any, hoverTitle: string, statusBadge: React.ReactNode) => { - const rawTitle = String(node.title ?? ''); - const groupKey = String(node?.dataRef?.groupKey || ''); - const dragText = resolveSidebarObjectDragText(node); - if (node.type === 'v2-table-section') { - return ( - - {rawTitle} - - ); - } - const displayTitle = (() => { - if (node.type === 'queries-folder') return t('sidebar.tree.saved_queries'); - if (node.type === 'external-sql-root') return t('sidebar.external_sql.root'); - if (node.type === 'object-group') { - const objectGroupTitle = resolveV2ObjectGroupTitle(node); - if (objectGroupTitle) return objectGroupTitle; - } - return rawTitle; - })(); - const metaText = getV2TreeMetaText(node); - const isMono = node.type === 'table' - || node.type === 'view' - || node.type === 'materialized-view' - || node.type === 'db-trigger' - || node.type === 'db-event' - || node.type === 'routine' - || node.type === 'saved-query' - || node.type === 'external-sql-file'; - const titleClassName = [ - 'gn-v2-tree-title', - isMono ? 'is-mono' : '', - node.type === 'object-group' ? 'is-group' : '', - node.type === 'table' && node?.dataRef?.pinnedSidebarTable ? 'is-pinned-table' : '', - ].filter(Boolean).join(' '); - const tablePinAction = node.type === 'table' ? ( - - ) : null; - if (node.type === 'connection') { - return ( - - {statusBadge} - - {displayTitle} - - - ); - } - return ( - <> - { - snapshotTreeSelectionBeforeDrag(); - treeDragSelectSuppressUntilRef.current = Date.now() + 600; - setIsTreeDragging(true); - event.stopPropagation(); - event.dataTransfer.effectAllowed = 'copy'; - event.dataTransfer.setData('text/plain', dragText); - event.dataTransfer.setData( - SIDEBAR_SQL_EDITOR_DRAG_MIME, - encodeSidebarSqlEditorDragPayload({ - text: dragText, - nodeType: node.type, - connectionId: String(node?.dataRef?.id || ''), - dbName: String(node?.dataRef?.dbName || ''), - }), - ); - } : undefined} - onDragEnd={dragText ? () => { - restoreTreeSelectionAfterDrag(); - setIsTreeDragging(false); - } : undefined} - > - {statusBadge} - {displayTitle} - {metaText && {metaText}} - - {tablePinAction} - - ); - }; + const renderV2TreeTitle = (node: any, hoverTitle: string, statusBadge: React.ReactNode) => renderSidebarV2TreeTitle({ + node, + hoverTitle, + statusBadge, + getV2TreeMetaText, + toggleSidebarTablePinned, + snapshotTreeSelectionBeforeDrag, + restoreTreeSelectionAfterDrag, + treeDragSelectSuppressUntilRef, + setIsTreeDragging, + }); const selectConnectionFromRail = useCallback((conn: SavedConnection) => { const key = conn.id; diff --git a/frontend/src/components/sidebar/SidebarTreeTitle.tsx b/frontend/src/components/sidebar/SidebarTreeTitle.tsx new file mode 100644 index 00000000..3db6508c --- /dev/null +++ b/frontend/src/components/sidebar/SidebarTreeTitle.tsx @@ -0,0 +1,152 @@ +import React from 'react'; +import { StarFilled, StarOutlined } from '@ant-design/icons'; +import { t } from '../../i18n'; +import { SIDEBAR_SQL_EDITOR_DRAG_MIME, encodeSidebarSqlEditorDragPayload } from '../../utils/sidebarSqlDrag'; +import { resolveSidebarObjectDragText } from '../sidebarCoreUtils'; +import { resolveV2ObjectGroupTitle } from './sidebarHelpers'; + +type SidebarV2TreeTitleOptions = { + node: any; + hoverTitle: string; + statusBadge: React.ReactNode; + getV2TreeMetaText: (node: any) => string; + toggleSidebarTablePinned: (node: any) => void; + snapshotTreeSelectionBeforeDrag: () => void; + restoreTreeSelectionAfterDrag: () => void; + treeDragSelectSuppressUntilRef: React.MutableRefObject; + setIsTreeDragging: (dragging: boolean) => void; +}; + +export const renderSidebarV2TreeTitle = ({ + node, + hoverTitle, + statusBadge, + getV2TreeMetaText, + toggleSidebarTablePinned, + snapshotTreeSelectionBeforeDrag, + restoreTreeSelectionAfterDrag, + treeDragSelectSuppressUntilRef, + setIsTreeDragging, +}: SidebarV2TreeTitleOptions): React.ReactNode => { + const rawTitle = String(node.title ?? ''); + const groupKey = String(node?.dataRef?.groupKey || ''); + const dragText = resolveSidebarObjectDragText(node); + if (node.type === 'v2-table-section') { + return ( + + {rawTitle} + + ); + } + const displayTitle = (() => { + if (node.type === 'queries-folder') return t('sidebar.tree.saved_queries'); + if (node.type === 'external-sql-root') return t('sidebar.external_sql.root'); + if (node.type === 'object-group') { + const objectGroupTitle = resolveV2ObjectGroupTitle(node); + if (objectGroupTitle) return objectGroupTitle; + } + return rawTitle; + })(); + const metaText = getV2TreeMetaText(node); + const isMono = node.type === 'table' + || node.type === 'view' + || node.type === 'materialized-view' + || node.type === 'db-trigger' + || node.type === 'db-event' + || node.type === 'routine' + || node.type === 'saved-query' + || node.type === 'external-sql-file'; + const titleClassName = [ + 'gn-v2-tree-title', + isMono ? 'is-mono' : '', + node.type === 'object-group' ? 'is-group' : '', + node.type === 'table' && node?.dataRef?.pinnedSidebarTable ? 'is-pinned-table' : '', + ].filter(Boolean).join(' '); + const tablePinAction = node.type === 'table' ? ( + + ) : null; + if (node.type === 'connection') { + return ( + + {statusBadge} + + {displayTitle} + + + ); + } + return ( + <> + { + snapshotTreeSelectionBeforeDrag(); + treeDragSelectSuppressUntilRef.current = Date.now() + 600; + setIsTreeDragging(true); + event.stopPropagation(); + event.dataTransfer.effectAllowed = 'copy'; + event.dataTransfer.setData('text/plain', dragText); + event.dataTransfer.setData( + SIDEBAR_SQL_EDITOR_DRAG_MIME, + encodeSidebarSqlEditorDragPayload({ + text: dragText, + nodeType: node.type, + connectionId: String(node?.dataRef?.id || ''), + dbName: String(node?.dataRef?.dbName || ''), + }), + ); + } : undefined} + onDragEnd={dragText ? () => { + restoreTreeSelectionAfterDrag(); + setIsTreeDragging(false); + } : undefined} + > + {statusBadge} + {displayTitle} + {metaText && {metaText}} + + {tablePinAction} + + ); +};