diff --git a/frontend/src/components/Sidebar.locate-toolbar.test.tsx b/frontend/src/components/Sidebar.locate-toolbar.test.tsx index 58790f4..0a630e7 100644 --- a/frontend/src/components/Sidebar.locate-toolbar.test.tsx +++ b/frontend/src/components/Sidebar.locate-toolbar.test.tsx @@ -659,12 +659,25 @@ describe('Sidebar locate toolbar', () => { expect(source).toContain("overflow: 'hidden'"); expect(source).not.toContain("overflowX: isV2Ui ? 'auto' : 'hidden'"); expect(source).toContain('scrollWidth={isV2Ui ? v2TreeHorizontalScrollWidth : undefined}'); - expect(css).toMatch(/\.gn-v2-explorer-tree-shell \{[^}]*overflow: hidden !important;/s); - expect(css).toMatch(/\.gn-v2-explorer-tree-shell \.ant-tree \{[^}]*width: 100%;[^}]*min-width: 0;/s); + expect(source).toContain('const V2_TREE_HORIZONTAL_SCROLL_BOTTOM_RESERVE = 32;'); + expect(source).toContain('const effectiveTreeHeight = isV2Ui && v2TreeHorizontalScrollWidth'); + expect(source).toContain('treeHeight - V2_TREE_HORIZONTAL_SCROLL_BOTTOM_RESERVE'); + expect(source).toContain('height={effectiveTreeHeight}'); + expect(source).toContain('treeData={isV2Ui ? v2VisibleTreeData : displayTreeData}'); + expect(source).not.toContain('__v2-tree-horizontal-scroll-spacer__'); + expect(source).not.toContain('v2TreeDataWithScrollSpacer'); + expect(css).toMatch(/\.gn-v2-explorer-tree-shell \{[^}]*--gn-v2-tree-horizontal-scroll-reserve: 32px;[^}]*overflow: hidden !important;/s); + expect(css).toMatch(/\.gn-v2-explorer-tree-shell \.sidebar-tree-scroll-content \{[^}]*display: flex;[^}]*height: 100%;[^}]*padding: 4px 0 0;/s); + expect(css).toMatch(/\.gn-v2-explorer-tree-shell \.ant-tree \{[^}]*flex: 1 1 auto;[^}]*width: 100%;[^}]*min-width: 0;[^}]*height: 100%;/s); + expect(css).toMatch(/\.gn-v2-explorer-tree-shell \.ant-tree-list \{[^}]*height: calc\(100% - var\(--gn-v2-tree-horizontal-scroll-reserve\)\);[^}]*min-height: 0;[^}]*box-sizing: border-box;/s); + expect(css).not.toMatch(/\.gn-v2-explorer-tree-shell \.ant-tree-list \{[^}]*height: 100%;/s); expect(css).toMatch(/\.gn-v2-explorer-tree-shell \.ant-tree-list-holder-inner \{[^}]*width: 100%;[^}]*min-width: 100%;/s); expect(css).not.toMatch(/\.gn-v2-explorer-tree-shell \.ant-tree-list-holder-inner \{[^}]*width: max-content;/s); + expect(css).not.toMatch(/\.gn-v2-explorer-tree-shell \.ant-tree-list \{[^}]*position: static !important;/s); + expect(css).not.toMatch(/\.gn-v2-explorer-tree-shell \.ant-tree-list-holder \{[^}]*calc\(100% - var\(--gn-v2-tree-horizontal-scroll-reserve\)\)/s); expect(css).not.toMatch(/\.gn-v2-explorer-tree-shell \.ant-tree-list-holder \{[^}]*overflow-x: auto !important;/s); - expect(css).toMatch(/\.gn-v2-explorer-tree-shell \.ant-tree-list-scrollbar-horizontal \{[^}]*height: 12px !important;/s); + expect(css).toMatch(/\.gn-v2-explorer-tree-shell \.ant-tree-list-scrollbar-horizontal \{[^}]*height: 12px !important;[^}]*bottom: calc\(\(var\(--gn-v2-tree-horizontal-scroll-reserve\) - 12px\) \* -1\) !important;/s); + expect(css).not.toContain('.gn-v2-tree-horizontal-scroll-spacer'); expect(css).toMatch(/\.gn-v2-explorer-tree-shell \.ant-tree-list-scrollbar-horizontal \.ant-tree-list-scrollbar-thumb \{[^}]*height: 8px !important;/s); expect(css).toMatch(/\.gn-v2-explorer-tree-shell \.ant-tree-node-content-wrapper \{[^}]*display: flex !important;/s); expect(css).toMatch(/\.gn-v2-tree-title\.is-connection \{[^}]*align-items:\s*center;/s); diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 7a36736..6d64db6 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -427,6 +427,7 @@ const V2_TREE_HORIZONTAL_SCROLL_BASE_WIDTH = 88; const V2_TREE_HORIZONTAL_SCROLL_INDENT_WIDTH = 24; const V2_TREE_HORIZONTAL_SCROLL_AVG_CHAR_WIDTH = 8; const V2_TREE_HORIZONTAL_SCROLL_VIEWPORT_BUFFER = 48; +const V2_TREE_HORIZONTAL_SCROLL_BOTTOM_RESERVE = 32; export const estimateV2TreeHorizontalScrollWidth = ( nodes: TreeNode[], @@ -6159,6 +6160,9 @@ const Sidebar: React.FC<{ () => estimateV2TreeHorizontalScrollWidth(v2VisibleTreeData, treeViewportWidth), [treeViewportWidth, v2VisibleTreeData], ); + const effectiveTreeHeight = isV2Ui && v2TreeHorizontalScrollWidth + ? Math.max(1, treeHeight - V2_TREE_HORIZONTAL_SCROLL_BOTTOM_RESERVE) + : treeHeight; const v2TreeMetrics = useMemo(() => { const databaseTableCounts = new Map(); const objectGroupCounts = new Map(); @@ -8850,7 +8854,7 @@ const Sidebar: React.FC<{ autoExpandParent={autoExpandParent} selectedKeys={selectedKeys} blockNode - height={treeHeight} + height={effectiveTreeHeight} scrollWidth={isV2Ui ? v2TreeHorizontalScrollWidth : undefined} onRightClick={onRightClick} /> diff --git a/frontend/src/v2-theme.css b/frontend/src/v2-theme.css index aab8d91..c8d8c83 100644 --- a/frontend/src/v2-theme.css +++ b/frontend/src/v2-theme.css @@ -2222,6 +2222,7 @@ body[data-ui-version="v2"] .gn-v2-explorer-toolbar { } body[data-ui-version="v2"] .gn-v2-explorer-tree-shell { + --gn-v2-tree-horizontal-scroll-reserve: 32px; margin: 0; border: 0; border-radius: 0; @@ -2230,23 +2231,39 @@ body[data-ui-version="v2"] .gn-v2-explorer-tree-shell { } body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .sidebar-tree-scroll-content { - padding: 4px 0 12px; + display: flex; + flex-direction: column; + min-height: 0; + height: 100%; + box-sizing: border-box; + padding: 4px 0 0; } body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree { + flex: 1 1 auto; width: 100%; min-width: 0; + min-height: 0; + height: 100%; font-size: var(--gn-sidebar-tree-font-size, var(--gn-font-size-sm, 12px)); line-height: 1.2; } +body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-list { + height: calc(100% - var(--gn-v2-tree-horizontal-scroll-reserve)); + min-height: 0; + box-sizing: border-box; +} + body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-list-holder { scrollbar-width: thin; } body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-list-scrollbar-horizontal { height: 12px !important; - bottom: 1px !important; + left: 8px !important; + right: 8px !important; + bottom: calc((var(--gn-v2-tree-horizontal-scroll-reserve) - 12px) * -1) !important; } body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-list-scrollbar-horizontal .ant-tree-list-scrollbar-thumb { @@ -2263,6 +2280,7 @@ body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-list-scrollbar-h body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-list-holder-inner { width: 100%; min-width: 100%; + box-sizing: border-box; } body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-treenode {