🐛 fix(datagrid): 优化列宽拖拽交互

This commit is contained in:
Kunghim
2026-07-31 23:28:44 +08:00
parent 98d07e49cc
commit 9feb1bc882
5 changed files with 3 additions and 48 deletions

View File

@@ -2196,7 +2196,6 @@ const DataGrid: React.FC<DataGridProps> = ({
const {
autoFitColumnWidth,
ghostRef,
handleResizeAutoFit,
handleResizeStart,
isResizingRef,
@@ -5422,7 +5421,6 @@ const DataGrid: React.FC<DataGridProps> = ({
formatTextViewValue,
getTargets,
getTemporalPickerType,
ghostRef,
gridCssText,
gridFieldSelectOptions,
gridId,

View File

@@ -742,6 +742,7 @@ const ResizableTitle = React.forwardRef<HTMLTableCellElement, any>((props, ref)
right: 0, // Align to right edge
bottom: 0,
top: 0,
height: 'auto',
width: 10,
cursor: 'col-resize',
// 必须低于固定列表头 z-index(30),否则横向滚动时会穿透到勾选/行号上方

View File

@@ -147,7 +147,6 @@ const DataGridShell: React.FC<DataGridShellProps> = (props) => {
formatTextViewValue,
getTargets,
getTemporalPickerType,
ghostRef,
gridCssText,
gridFieldSelectOptions,
gridId,
@@ -1053,23 +1052,6 @@ const renderDataTableView = () => (
/>
<style>{gridCssText}</style>
{/* Ghost Resize Line for Columns */}
<div
ref={ghostRef}
style={{
position: 'absolute',
top: 0,
bottom: 0, // Fits container height
left: 0,
width: '2px',
background: selectionAccentHex,
zIndex: 9999,
display: 'none',
pointerEvents: 'none',
willChange: 'transform'
}}
/>
{/* Preview SQL Modal */}
<Modal

View File

@@ -40,7 +40,6 @@ describe('useDataGridColumnResize interaction cleanup', () => {
let resize: ReturnType<typeof useDataGridColumnResize> | null = null;
let fakeWindow: FakeEventTarget;
let fakeDocument: FakeEventTarget & { body: { style: { cursor: string; userSelect: string } } };
let ghost: { style: { display: string; transform: string } };
let scheduledFrames: Map<number, FrameRequestCallback>;
let nextFrameId: number;
let setColumnWidths: ReturnType<typeof vi.fn>;
@@ -103,8 +102,6 @@ describe('useDataGridColumnResize interaction cleanup', () => {
fakeDocument = Object.assign(new FakeEventTarget(), {
body: { style: { cursor: 'crosshair', userSelect: 'text' } },
});
ghost = { style: { display: 'none', transform: '' } };
Object.defineProperty(globalThis, 'window', { configurable: true, value: fakeWindow });
Object.defineProperty(globalThis, 'document', { configurable: true, value: fakeDocument });
Object.defineProperty(globalThis, 'requestAnimationFrame', {
@@ -123,7 +120,6 @@ describe('useDataGridColumnResize interaction cleanup', () => {
act(() => {
renderer = create(<Harness />);
});
(resize!.ghostRef as React.MutableRefObject<any>).current = ghost;
});
afterEach(() => {
@@ -146,19 +142,17 @@ describe('useDataGridColumnResize interaction cleanup', () => {
}
});
it('restores body styles, hides the ghost, and commits on window blur', () => {
it('restores body styles and commits on window blur', () => {
beginResize();
act(() => fakeDocument.dispatch('mousemove', { buttons: 1, clientX: 230 }));
expect(fakeDocument.body.style).toEqual({ cursor: 'col-resize', userSelect: 'none' });
expect(ghost.style.display).toBe('block');
expect(fakeWindow.listenerCount('blur')).toBe(1);
act(() => fakeWindow.dispatch('blur'));
expectLastWidthUpdate(150);
expect(scheduledFrames.size).toBe(0);
expect(ghost.style.display).toBe('none');
expect(fakeDocument.body.style).toEqual({ cursor: 'crosshair', userSelect: 'text' });
expect(fakeDocument.listenerCount('mousemove')).toBe(0);
expect(fakeDocument.listenerCount('mouseup')).toBe(0);
@@ -178,7 +172,6 @@ describe('useDataGridColumnResize interaction cleanup', () => {
act(() => flushAnimationFrames());
expectLastWidthUpdate(150);
expect(ghost.style.transform).toBe('translateX(190px)');
});
it('self-heals when movement reports no pressed button', () => {
@@ -187,7 +180,6 @@ describe('useDataGridColumnResize interaction cleanup', () => {
act(() => fakeDocument.dispatch('mousemove', { buttons: 0, clientX: 260 }));
expectLastWidthUpdate(180);
expect(ghost.style.display).toBe('none');
expect(fakeDocument.body.style).toEqual({ cursor: 'crosshair', userSelect: 'text' });
expect(fakeDocument.listenerCount('mousemove')).toBe(0);
expect(fakeWindow.listenerCount('blur')).toBe(0);
@@ -212,7 +204,6 @@ describe('useDataGridColumnResize interaction cleanup', () => {
expect(scheduledFrames.size).toBe(0);
expect(cancelAnimationFrame).toHaveBeenCalledTimes(1);
expect(resize?.isResizingRef.current).toBe(false);
expect(ghost.style.display).toBe('none');
expect(fakeDocument.body.style).toEqual({ cursor: 'crosshair', userSelect: 'text' });
expect(fakeDocument.listenerCount('mousemove')).toBe(0);
expect(fakeDocument.listenerCount('mouseup')).toBe(0);

View File

@@ -15,7 +15,6 @@ type ColumnResizeDragState = {
startX: number;
startWidth: number;
key: string;
containerLeft: number;
};
type ColumnResizeListeners = {
blur: () => void;
@@ -48,7 +47,6 @@ export const useDataGridColumnResize = (ctx: UseDataGridColumnResizeContext) =>
} = ctx;
const draggingRef = useRef<ColumnResizeDragState | null>(null);
const ghostRef = useRef<HTMLDivElement>(null);
const resizeRafRef = useRef<number | null>(null);
const latestClientXRef = useRef<number | null>(null);
const isResizingRef = useRef(false);
@@ -77,10 +75,6 @@ export const useDataGridColumnResize = (ctx: UseDataGridColumnResizeContext) =>
if (latestClientXRef.current === null) return;
const dragState = draggingRef.current;
const clientX = latestClientXRef.current;
if (ghostRef.current) {
const relativeLeft = clientX - dragState.containerLeft;
ghostRef.current.style.transform = `translateX(${relativeLeft}px)`;
}
applyResizeWidth(dragState, clientX);
}, [applyResizeWidth]);
@@ -115,9 +109,6 @@ export const useDataGridColumnResize = (ctx: UseDataGridColumnResizeContext) =>
resizeRafRef.current = null;
}
latestClientXRef.current = null;
if (ghostRef.current) {
ghostRef.current.style.display = 'none';
}
detachResizeListeners();
restoreResizeBodyStyles();
@@ -157,17 +148,10 @@ export const useDataGridColumnResize = (ctx: UseDataGridColumnResizeContext) =>
manualWidth: columnWidths[key],
density: dataTableDensity,
});
const containerLeft = containerRef.current?.getBoundingClientRect().left ?? 0;
draggingRef.current = { startX, startWidth: currentWidth, key, containerLeft };
draggingRef.current = { startX, startWidth: currentWidth, key };
lastAppliedResizeWidthRef.current = currentWidth;
latestClientXRef.current = startX;
if (ghostRef.current && containerRef.current) {
const relativeLeft = startX - containerLeft;
ghostRef.current.style.transform = `translateX(${relativeLeft}px)`;
ghostRef.current.style.display = 'block';
}
const handleMove = (event: MouseEvent) => {
if (!draggingRef.current) return;
latestClientXRef.current = event.clientX;
@@ -306,7 +290,6 @@ export const useDataGridColumnResize = (ctx: UseDataGridColumnResizeContext) =>
return {
autoFitColumnWidth,
ghostRef,
handleResizeAutoFit,
handleResizeStart,
isResizingRef,