🐛 fix(datagrid): 修复最小列宽下筛选与排序控件隐藏

This commit is contained in:
home
2026-07-30 23:44:13 +08:00
parent 457a77d13a
commit 4e96660eab
7 changed files with 37 additions and 13 deletions

View File

@@ -90,6 +90,7 @@ describe('DataGridColumnTitle', () => {
expect(markup).toContain('主键 ID');
expect(markup).toContain('flex-direction:column');
expect(markup).toContain('align-items:flex-start');
expect(markup).toContain('display:inline-flex;width:100%;max-width:100%;min-width:0;overflow:hidden');
});
it('keeps column metadata tooltip readable in light theme', () => {
@@ -187,6 +188,8 @@ describe('DataGridColumnTitle', () => {
expect(markup).toContain('data-grid-column-filter-active="true"');
expect(markup).toContain('data-grid-column-filter-popover="true"');
expect(markup).toContain('flex:1 1 auto');
expect(markup).toContain('display:inline-flex;flex:1 1 auto;max-width:100%;min-width:0;overflow:hidden');
expect(markup).toContain('width:100%');
expect(markup).toContain('Filter status');
expect(markup).toContain('value="active"');
});

View File

@@ -258,7 +258,9 @@ const DataGridColumnTitle: React.FC<DataGridColumnTitleProps> = ({
styles={{ root: { maxWidth: 640 } }}
{...(!darkMode ? { color: 'rgba(0, 0, 0, 0.82)' } : {})}
>
<span style={{ display: 'inline-flex', maxWidth: '100%' }}>{titleNode}</span>
<span style={{ display: 'inline-flex', width: '100%', maxWidth: '100%', minWidth: 0, overflow: 'hidden' }}>
{titleNode}
</span>
</Tooltip>
);
})();
@@ -483,15 +485,16 @@ const DataGridColumnTitle: React.FC<DataGridColumnTitleProps> = ({
<span
className="gn-v2-column-title-shell"
style={{
display: 'inline-flex',
display: 'flex',
// 顶对齐有无注释时标题块高度不同center 会让筛选图标上下错位
alignItems: 'flex-start',
gap: 4,
width: '100%',
maxWidth: '100%',
minWidth: 0,
}}
>
<span style={{ display: 'inline-flex', flex: '1 1 auto', minWidth: 0 }}>
<span style={{ display: 'inline-flex', flex: '1 1 auto', maxWidth: '100%', minWidth: 0, overflow: 'hidden' }}>
{titleWithOptionalTooltip}
</span>
<Popover

View File

@@ -176,12 +176,12 @@ describe('useDataGridColumnResize interaction cleanup', () => {
expect(fakeWindow.listenerCount('blur')).toBe(0);
});
it('clamps manual data column resizing to the shared 80px minimum', () => {
it('clamps manual data column resizing to the shared 120px minimum', () => {
beginResize();
act(() => fakeDocument.dispatch('mouseup', { clientX: 0 }));
expectLastWidthUpdate(80);
expectLastWidthUpdate(120);
});
it('cancels pending RAF and gate work without committing when unmounted mid-resize', () => {

View File

@@ -1,12 +1,14 @@
import React, { useCallback, useEffect, useRef } from 'react';
import { resolveDataTableColumnWidth } from '../utils/dataGridDisplay';
import {
MIN_DATA_TABLE_COLUMN_WIDTH,
resolveDataTableColumnWidth,
} from '../utils/dataGridDisplay';
import { calculateAutoFitColumnWidth } from './dataGridAutoWidth';
import { DEFAULT_GRID_MONO_FONT_FAMILY, GONAVI_ROW_NUMBER_COLUMN_KEY } from './DataGridCore';
const ROW_NUMBER_DEFAULT_WIDTH = 36;
const ROW_NUMBER_MIN_WIDTH = 28;
const ROW_NUMBER_MAX_WIDTH = 120;
const DATA_COLUMN_MIN_WIDTH = 80;
type UseDataGridColumnResizeContext = Record<string, any>;
type ColumnResizeListeners = {
@@ -110,7 +112,7 @@ export const useDataGridColumnResize = (ctx: UseDataGridColumnResizeContext) =>
const finalClientX = Number.isFinite(clientX) ? clientX as number : latestClientX ?? dragState.startX;
const deltaX = finalClientX - dragState.startX;
const isRowNumberColumn = dragState.key === GONAVI_ROW_NUMBER_COLUMN_KEY;
const minWidth = isRowNumberColumn ? ROW_NUMBER_MIN_WIDTH : DATA_COLUMN_MIN_WIDTH;
const minWidth = isRowNumberColumn ? ROW_NUMBER_MIN_WIDTH : MIN_DATA_TABLE_COLUMN_WIDTH;
const maxWidth = isRowNumberColumn ? ROW_NUMBER_MAX_WIDTH : Number.POSITIVE_INFINITY;
const newWidth = Math.min(maxWidth, Math.max(minWidth, dragState.startWidth + deltaX));
setColumnWidthsRef.current((prev: Record<string, number>) => ({ ...prev, [dragState.key]: newWidth }));
@@ -216,7 +218,7 @@ export const useDataGridColumnResize = (ctx: UseDataGridColumnResizeContext) =>
valueTexts: displayData.slice(0, 200).map((row: any) => row?.[key]),
measureHeaderText: (text) => measureTextWidth(text, `600 ${font}`),
measureCellText: (text) => measureTextWidth(text, `400 ${font}`),
minWidth: 40,
minWidth: MIN_DATA_TABLE_COLUMN_WIDTH,
maxWidth: 600,
defaultWidth: densityParams.defaultColumnWidth,
});
@@ -249,7 +251,7 @@ export const useDataGridColumnResize = (ctx: UseDataGridColumnResizeContext) =>
measureHeaderText: buildAutoFitMeasurer(headerEl ?? null, `600 ${densityParams.dataFontSize}px ${DEFAULT_GRID_MONO_FONT_FAMILY}`),
measureCellText: buildAutoFitMeasurer(sampleCell ?? null, `400 ${densityParams.dataFontSize}px ${DEFAULT_GRID_MONO_FONT_FAMILY}`),
defaultWidth,
minWidth: DATA_COLUMN_MIN_WIDTH,
minWidth: MIN_DATA_TABLE_COLUMN_WIDTH,
maxWidth: Math.max(720, Math.floor(containerWidth * 0.85)),
});

View File

@@ -47,7 +47,8 @@ describe('dataGridDisplay helpers', () => {
it('keeps manual column widths ahead of density defaults', () => {
expect(resolveDataTableColumnWidth({ manualWidth: 320, density: 'compact' })).toBe(320);
expect(resolveDataTableColumnWidth({ manualWidth: undefined, density: 'compact' })).toBe(100);
expect(resolveDataTableColumnWidth({ manualWidth: 80, density: 'comfortable' })).toBe(120);
expect(resolveDataTableColumnWidth({ manualWidth: undefined, density: 'compact' })).toBe(120);
});
it('uses subtle themed vertical border colors and transparent when disabled', () => {

View File

@@ -26,6 +26,9 @@ export const MIN_DATA_TABLE_FONT_SIZE = 10;
export const MAX_DATA_TABLE_FONT_SIZE = 18;
export const MIN_SIDEBAR_TREE_FONT_SIZE = 10;
export const MAX_SIDEBAR_TREE_FONT_SIZE = 18;
// Keep enough room for the column title, filter trigger, sorter and resize handle.
// Default, manual and auto-fit widths must all preserve this invariant.
export const MIN_DATA_TABLE_COLUMN_WIDTH = 120;
type DensityOptionTranslator = (key: string) => string;
@@ -148,10 +151,10 @@ export const resolveDataTableColumnWidth = ({
density: DataTableDensity | null | undefined;
}): number => {
if (typeof manualWidth === 'number' && Number.isFinite(manualWidth) && manualWidth > 0) {
return manualWidth;
return Math.max(MIN_DATA_TABLE_COLUMN_WIDTH, manualWidth);
}
return resolveDataTableDefaultColumnWidth(density);
return Math.max(MIN_DATA_TABLE_COLUMN_WIDTH, resolveDataTableDefaultColumnWidth(density));
};
export const resolveDataTableVerticalBorderColor = ({

View File

@@ -4331,15 +4331,25 @@ body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-table-designer.is-embedded .a
}
body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-thead .ant-table-column-title {
min-width: 0;
overflow: hidden;
font-size: var(--gn-data-table-font-size, var(--gn-font-size-mono, 12px));
font-weight: 400 !important;
}
body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-thead .ant-table-column-sorters {
width: 100%;
max-width: 100%;
min-width: 0;
overflow: hidden;
min-height: 0 !important;
align-items: flex-start !important;
}
body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-thead .ant-table-column-sorter {
flex: 0 0 auto;
}
body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-thead > tr > th.is-single-line-title {
vertical-align: middle !important;
}
@@ -4350,9 +4360,11 @@ body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-thead > tr > th.is-single
}
body[data-ui-version="v2"] .gn-v2-data-grid .sortable-header-cell-drag-handle {
box-sizing: border-box;
min-height: 48px !important;
align-items: flex-start !important;
padding: 3px 8px !important;
padding-right: 12px !important;
}
body[data-ui-version="v2"] .gn-v2-data-grid .sortable-header-cell-drag-handle > div {