mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-22 08:53:46 +08:00
🐛 fix(datagrid): 优化列宽拖拽交互
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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),否则横向滚动时会穿透到勾选/行号上方
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user