mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-07 23:23:52 +08:00
⚡️ perf(data-grid): 优化虚拟表格滚动与列宽拖拽
- 合并横向滚动同步并减少固定列样式级联重算 - 去重虚拟滚动事件并稳定复用未变化的数据行 - 移除列宽拖拽辅助线并补充滚动回归测试
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
diff --git a/node_modules/rc-table/es/VirtualTable/BodyGrid.js b/node_modules/rc-table/es/VirtualTable/BodyGrid.js
|
||||
index 42dcde8..2cca743 100644
|
||||
index 42dcde8..2fc28a7 100644
|
||||
--- a/node_modules/rc-table/es/VirtualTable/BodyGrid.js
|
||||
+++ b/node_modules/rc-table/es/VirtualTable/BodyGrid.js
|
||||
@@ -23,6 +23,8 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
@@ -23,11 +23,14 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
sticky = _useContext2.sticky,
|
||||
scrollY = _useContext2.scrollY,
|
||||
listItemHeight = _useContext2.listItemHeight,
|
||||
@@ -11,7 +11,15 @@ index 42dcde8..2cca743 100644
|
||||
getComponent = _useContext2.getComponent,
|
||||
onTablePropScroll = _useContext2.onScroll;
|
||||
|
||||
@@ -113,8 +115,14 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
// =========================== Ref ============================
|
||||
var listRef = React.useRef();
|
||||
+ var lastForwardedXRef = React.useRef(0);
|
||||
|
||||
// =========================== Data ===========================
|
||||
var flattenData = useFlattenRecords(data, childrenColumnName, expandedKeys, getRowKey);
|
||||
@@ -110,10 +113,16 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
return 1;
|
||||
};
|
||||
var extraRender = function extraRender(info) {
|
||||
+ // The column-window fast path explicitly requires rows without spans.
|
||||
+ // Avoid scanning every column on each vertical window update.
|
||||
@@ -27,7 +35,7 @@ index 42dcde8..2cca743 100644
|
||||
|
||||
// Do nothing if no data
|
||||
if (end < 0) {
|
||||
@@ -198,6 +200,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
@@ -198,6 +207,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
style: {
|
||||
top: -offsetY + sizeInfo.top
|
||||
},
|
||||
@@ -35,7 +43,7 @@ index 42dcde8..2cca743 100644
|
||||
extra: true,
|
||||
getHeight: getHeight
|
||||
});
|
||||
@@ -239,6 +242,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
@@ -239,6 +249,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
className: tblPrefixCls,
|
||||
height: scrollY,
|
||||
itemHeight: listItemHeight || 24,
|
||||
@@ -43,7 +51,16 @@ index 42dcde8..2cca743 100644
|
||||
data: flattenData,
|
||||
itemKey: function itemKey(item) {
|
||||
return getRowKey(item.record);
|
||||
@@ -262,6 +266,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
@@ -249,6 +260,8 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
onVirtualScroll: function onVirtualScroll(_ref4) {
|
||||
var _listRef$current7;
|
||||
var x = _ref4.x;
|
||||
+ if (lastForwardedXRef.current === x) return;
|
||||
+ lastForwardedXRef.current = x;
|
||||
onScroll({
|
||||
currentTarget: (_listRef$current7 = listRef.current) === null || _listRef$current7 === void 0 ? void 0 : _listRef$current7.nativeElement,
|
||||
scrollLeft: x
|
||||
@@ -262,6 +275,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
data: item,
|
||||
rowKey: rowKey,
|
||||
index: index,
|
||||
@@ -52,7 +69,7 @@ index 42dcde8..2cca743 100644
|
||||
});
|
||||
}));
|
||||
diff --git a/node_modules/rc-table/es/VirtualTable/BodyLine.js b/node_modules/rc-table/es/VirtualTable/BodyLine.js
|
||||
index c760819..1a6c396 100644
|
||||
index c760819..c7a7c29 100644
|
||||
--- a/node_modules/rc-table/es/VirtualTable/BodyLine.js
|
||||
+++ b/node_modules/rc-table/es/VirtualTable/BodyLine.js
|
||||
@@ -2,7 +2,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
|
||||
@@ -179,6 +196,34 @@ index c760819..1a6c396 100644
|
||||
if (rowSupportExpand) {
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
ref: ref
|
||||
@@ -98,7 +151,26 @@ var BodyLine = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
}
|
||||
return rowNode;
|
||||
});
|
||||
-var ResponseBodyLine = responseImmutable(BodyLine);
|
||||
+var bodyLinePropsAreEqual = function bodyLinePropsAreEqual(prevProps, nextProps) {
|
||||
+ var prevKeys = Object.keys(prevProps);
|
||||
+ var nextKeys = Object.keys(nextProps);
|
||||
+ if (prevKeys.length !== nextKeys.length) return false;
|
||||
+ for (var i = 0; i < prevKeys.length; i += 1) {
|
||||
+ var key = prevKeys[i];
|
||||
+ if (key !== 'style' && prevProps[key] !== nextProps[key]) return false;
|
||||
+ }
|
||||
+ var prevStyle = prevProps.style || {};
|
||||
+ var nextStyle = nextProps.style || {};
|
||||
+ var prevStyleKeys = Object.keys(prevStyle);
|
||||
+ var nextStyleKeys = Object.keys(nextStyle);
|
||||
+ if (prevStyleKeys.length !== nextStyleKeys.length) return false;
|
||||
+ for (var _i2 = 0; _i2 < prevStyleKeys.length; _i2 += 1) {
|
||||
+ var styleKey = prevStyleKeys[_i2];
|
||||
+ if (prevStyle[styleKey] !== nextStyle[styleKey]) return false;
|
||||
+ }
|
||||
+ return true;
|
||||
+};
|
||||
+var ResponseBodyLine = responseImmutable(BodyLine, bodyLinePropsAreEqual);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
ResponseBodyLine.displayName = 'BodyLine';
|
||||
}
|
||||
diff --git a/node_modules/rc-table/es/VirtualTable/context.d.ts b/node_modules/rc-table/es/VirtualTable/context.d.ts
|
||||
index e558165..ccd74fc 100644
|
||||
--- a/node_modules/rc-table/es/VirtualTable/context.d.ts
|
||||
@@ -234,10 +279,10 @@ index fb00e9b..a778aa6 100644
|
||||
// ========================== Render ==========================
|
||||
return /*#__PURE__*/React.createElement(StaticContext.Provider, {
|
||||
diff --git a/node_modules/rc-table/lib/VirtualTable/BodyGrid.js b/node_modules/rc-table/lib/VirtualTable/BodyGrid.js
|
||||
index dee2539..b077789 100644
|
||||
index dee2539..85f90af 100644
|
||||
--- a/node_modules/rc-table/lib/VirtualTable/BodyGrid.js
|
||||
+++ b/node_modules/rc-table/lib/VirtualTable/BodyGrid.js
|
||||
@@ -33,6 +33,8 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
@@ -33,11 +33,14 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
sticky = _useContext2.sticky,
|
||||
scrollY = _useContext2.scrollY,
|
||||
listItemHeight = _useContext2.listItemHeight,
|
||||
@@ -246,7 +291,15 @@ index dee2539..b077789 100644
|
||||
getComponent = _useContext2.getComponent,
|
||||
onTablePropScroll = _useContext2.onScroll;
|
||||
|
||||
@@ -123,8 +125,14 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
// =========================== Ref ============================
|
||||
var listRef = React.useRef();
|
||||
+ var lastForwardedXRef = React.useRef(0);
|
||||
|
||||
// =========================== Data ===========================
|
||||
var flattenData = (0, _useFlattenRecords.default)(data, childrenColumnName, expandedKeys, getRowKey);
|
||||
@@ -120,10 +123,16 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
return 1;
|
||||
};
|
||||
var extraRender = function extraRender(info) {
|
||||
+ // The column-window fast path explicitly requires rows without spans.
|
||||
+ // Avoid scanning every column on each vertical window update.
|
||||
@@ -262,7 +315,7 @@ index dee2539..b077789 100644
|
||||
|
||||
// Do nothing if no data
|
||||
if (end < 0) {
|
||||
@@ -208,6 +210,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
@@ -208,6 +217,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
style: {
|
||||
top: -offsetY + sizeInfo.top
|
||||
},
|
||||
@@ -270,7 +323,7 @@ index dee2539..b077789 100644
|
||||
extra: true,
|
||||
getHeight: getHeight
|
||||
});
|
||||
@@ -249,6 +252,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
@@ -249,6 +259,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
className: tblPrefixCls,
|
||||
height: scrollY,
|
||||
itemHeight: listItemHeight || 24,
|
||||
@@ -278,7 +331,16 @@ index dee2539..b077789 100644
|
||||
data: flattenData,
|
||||
itemKey: function itemKey(item) {
|
||||
return getRowKey(item.record);
|
||||
@@ -272,6 +276,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
@@ -259,6 +270,8 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
onVirtualScroll: function onVirtualScroll(_ref4) {
|
||||
var _listRef$current7;
|
||||
var x = _ref4.x;
|
||||
+ if (lastForwardedXRef.current === x) return;
|
||||
+ lastForwardedXRef.current = x;
|
||||
onScroll({
|
||||
currentTarget: (_listRef$current7 = listRef.current) === null || _listRef$current7 === void 0 ? void 0 : _listRef$current7.nativeElement,
|
||||
scrollLeft: x
|
||||
@@ -272,6 +285,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
data: item,
|
||||
rowKey: rowKey,
|
||||
index: index,
|
||||
@@ -287,7 +349,7 @@ index dee2539..b077789 100644
|
||||
});
|
||||
}));
|
||||
diff --git a/node_modules/rc-table/lib/VirtualTable/BodyLine.js b/node_modules/rc-table/lib/VirtualTable/BodyLine.js
|
||||
index e1275fb..d962380 100644
|
||||
index e1275fb..374781d 100644
|
||||
--- a/node_modules/rc-table/lib/VirtualTable/BodyLine.js
|
||||
+++ b/node_modules/rc-table/lib/VirtualTable/BodyLine.js
|
||||
@@ -19,7 +19,7 @@ var _useRowInfo = _interopRequireDefault(require("../hooks/useRowInfo"));
|
||||
@@ -405,6 +467,34 @@ index e1275fb..d962380 100644
|
||||
if (rowSupportExpand) {
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
ref: ref
|
||||
@@ -108,7 +161,26 @@ var BodyLine = /*#__PURE__*/React.forwardRef(function (props, ref) {
|
||||
}
|
||||
return rowNode;
|
||||
});
|
||||
-var ResponseBodyLine = (0, _TableContext.responseImmutable)(BodyLine);
|
||||
+var bodyLinePropsAreEqual = function bodyLinePropsAreEqual(prevProps, nextProps) {
|
||||
+ var prevKeys = Object.keys(prevProps);
|
||||
+ var nextKeys = Object.keys(nextProps);
|
||||
+ if (prevKeys.length !== nextKeys.length) return false;
|
||||
+ for (var i = 0; i < prevKeys.length; i += 1) {
|
||||
+ var key = prevKeys[i];
|
||||
+ if (key !== 'style' && prevProps[key] !== nextProps[key]) return false;
|
||||
+ }
|
||||
+ var prevStyle = prevProps.style || {};
|
||||
+ var nextStyle = nextProps.style || {};
|
||||
+ var prevStyleKeys = Object.keys(prevStyle);
|
||||
+ var nextStyleKeys = Object.keys(nextStyle);
|
||||
+ if (prevStyleKeys.length !== nextStyleKeys.length) return false;
|
||||
+ for (var _i2 = 0; _i2 < prevStyleKeys.length; _i2 += 1) {
|
||||
+ var styleKey = prevStyleKeys[_i2];
|
||||
+ if (prevStyle[styleKey] !== nextStyle[styleKey]) return false;
|
||||
+ }
|
||||
+ return true;
|
||||
+};
|
||||
+var ResponseBodyLine = (0, _TableContext.responseImmutable)(BodyLine, bodyLinePropsAreEqual);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
ResponseBodyLine.displayName = 'BodyLine';
|
||||
}
|
||||
diff --git a/node_modules/rc-table/lib/VirtualTable/context.d.ts b/node_modules/rc-table/lib/VirtualTable/context.d.ts
|
||||
index e558165..ccd74fc 100644
|
||||
--- a/node_modules/rc-table/lib/VirtualTable/context.d.ts
|
||||
|
||||
@@ -471,6 +471,16 @@ describe('DataGrid layout', () => {
|
||||
expect(source).not.toContain('ghostRef');
|
||||
});
|
||||
|
||||
it('keeps the V2 column resize hit target visually transparent', () => {
|
||||
const css = readV2ThemeCss();
|
||||
|
||||
expect(css).toMatch(
|
||||
/\.gn-v2-data-grid \.react-resizable-handle\s*\{[^}]*background-image:\s*none\s*!important;/s,
|
||||
);
|
||||
expect(css).not.toContain('.react-resizable-handle::after');
|
||||
expect(css).not.toContain('.react-resizable-handle:hover::after');
|
||||
});
|
||||
|
||||
it('avoids duplicating legacy pagination page text beside the pager', () => {
|
||||
const markup = renderToStaticMarkup(
|
||||
<DataGridPaginationBar
|
||||
|
||||
@@ -55,6 +55,8 @@ import {
|
||||
resolveExternalHorizontalScrollMetrics,
|
||||
} from './dataGridLayout';
|
||||
import {
|
||||
applyDataGridFixedCellPreviewOffset,
|
||||
commitDataGridFixedCellOffset,
|
||||
createDataGridIdleCommitScheduler,
|
||||
createDataGridVisualFrameGuard,
|
||||
type DataGridIdleCommitScheduler,
|
||||
@@ -4124,8 +4126,8 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
|
||||
/**
|
||||
* 虚拟表横滚视觉同步:
|
||||
* - 表体:filler marginLeft = -offset;固定列用 --gn-datagrid-h-scroll 做 translateX 补偿
|
||||
* (filler 有 transform:translateY,sticky 在表体不可靠)
|
||||
* - 表体:filler marginLeft = -offset;预览时只移动当前固定单元格,避免修改
|
||||
* 继承变量导致整棵虚拟表体重新计算样式
|
||||
* - 表头:真实 scrollLeft + sticky 固定全选/行号(不要对 header table 做 transform,
|
||||
* 否则会把全选 checkbox / # 裁没或钉飞)
|
||||
*/
|
||||
@@ -4141,18 +4143,13 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
const clampedOffset = Math.max(0, Math.min(maxScroll, nextOffset));
|
||||
const currentOffset = Math.max(0, Math.abs(parseFloat(innerEl.style.marginLeft) || 0));
|
||||
const nextMarginLeft = `${-clampedOffset}px`;
|
||||
const scrollVar = `${clampedOffset}px`;
|
||||
virtualHorizontalPostCommitGuardRef.current?.update(clampedOffset);
|
||||
|
||||
if (innerEl.style.marginLeft !== nextMarginLeft) {
|
||||
innerEl.style.marginLeft = nextMarginLeft;
|
||||
}
|
||||
|
||||
// 只在固定列的最近公共祖先写一次,避免同一继承变量使整棵表体
|
||||
// 连续发生三次 style invalidation。
|
||||
if (innerEl.style.getPropertyValue('--gn-datagrid-h-scroll') !== scrollVar) {
|
||||
innerEl.style.setProperty('--gn-datagrid-h-scroll', scrollVar);
|
||||
}
|
||||
applyDataGridFixedCellPreviewOffset(tableContainer, clampedOffset);
|
||||
if (tableContainer.style.getPropertyValue('--gn-datagrid-h-scroll')) {
|
||||
tableContainer.style.removeProperty('--gn-datagrid-h-scroll');
|
||||
}
|
||||
@@ -4167,7 +4164,7 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
return { holderEl, clampedOffset, currentOffset };
|
||||
return { holderEl, innerEl, clampedOffset, currentOffset };
|
||||
}, [resolveVirtualHorizontalElements, tableScrollX]);
|
||||
|
||||
virtualHorizontalPostCommitFrameHandlerRef.current = (offset) => {
|
||||
@@ -4216,7 +4213,7 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
return false;
|
||||
}
|
||||
|
||||
const { holderEl, clampedOffset, currentOffset } = synced;
|
||||
const { holderEl, innerEl, clampedOffset, currentOffset } = synced;
|
||||
const deltaX = clampedOffset - currentOffset;
|
||||
if (Math.abs(deltaX) < 0.5 && !options?.forceInternalScroll) {
|
||||
scheduleVirtualHorizontalPostCommit(tableContainer, clampedOffset);
|
||||
@@ -4227,6 +4224,7 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
if (tableInstance && typeof tableInstance.scrollTo === 'function') {
|
||||
// 更新 rc-virtual-list 内部 offsetLeft
|
||||
tableInstance.scrollTo({ left: clampedOffset });
|
||||
commitDataGridFixedCellOffset(tableContainer, innerEl, clampedOffset);
|
||||
lastCommittedVirtualHorizontalOffsetRef.current = clampedOffset;
|
||||
scheduleVirtualHorizontalPostCommit(tableContainer, clampedOffset);
|
||||
return true;
|
||||
@@ -4239,6 +4237,8 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}));
|
||||
commitDataGridFixedCellOffset(tableContainer, innerEl, clampedOffset);
|
||||
lastCommittedVirtualHorizontalOffsetRef.current = clampedOffset;
|
||||
scheduleVirtualHorizontalPostCommit(tableContainer, clampedOffset);
|
||||
return true;
|
||||
}, [scheduleVirtualHorizontalPostCommit, syncVirtualHorizontalVisualOffset]);
|
||||
@@ -4582,6 +4582,12 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
// rc-virtual-list 内部 offsetLeft,避免大结果集每帧触发行渲染。
|
||||
const alreadyCommitted = virtualListItemColumnVirtual
|
||||
&& Math.abs(lastCommittedVirtualHorizontalOffsetRef.current - resolvedScrollLeft) < 0.5;
|
||||
if (alreadyCommitted) {
|
||||
const { innerEl } = resolveVirtualHorizontalElements(tableContainer);
|
||||
if (innerEl instanceof HTMLElement) {
|
||||
commitDataGridFixedCellOffset(tableContainer, innerEl, resolvedScrollLeft);
|
||||
}
|
||||
}
|
||||
const applied = alreadyCommitted
|
||||
|| applyVirtualHorizontalOffset(tableContainer, resolvedScrollLeft, { forceInternalScroll: true });
|
||||
if (applied) {
|
||||
@@ -4604,7 +4610,7 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
}
|
||||
horizontalSyncSourceRef.current = '';
|
||||
});
|
||||
}, [applyVirtualHorizontalOffset, enableVirtual, isExternalScrollbarInteractionActive, readVirtualHorizontalOffset, syncVirtualHorizontalVisualOffset, virtualListItemColumnVirtual]);
|
||||
}, [applyVirtualHorizontalOffset, enableVirtual, isExternalScrollbarInteractionActive, readVirtualHorizontalOffset, resolveVirtualHorizontalElements, syncVirtualHorizontalVisualOffset, virtualListItemColumnVirtual]);
|
||||
|
||||
externalIdleCommitHandlerRef.current = (syncSequence) => {
|
||||
externalScrollInteractionUntilRef.current = 0;
|
||||
@@ -4653,7 +4659,7 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
const requestedExternalScrollLeft = pendingExternalScrollLeftRef.current ?? latestExternalScroll.scrollLeft;
|
||||
pendingExternalScrollLeftRef.current = null;
|
||||
const tableContainer = tableContainerRef.current;
|
||||
// 用户连续拖动/滚动时,只写 marginLeft、header.scrollLeft 和固定列 CSS 变量。
|
||||
// 用户连续拖动/滚动时,只写 marginLeft、header.scrollLeft 和当前固定单元格。
|
||||
// 不在每一帧调用 Table.scrollTo,否则 rc-virtual-list 会随数据量放大渲染开销。
|
||||
if (enableVirtual && tableContainer instanceof HTMLElement) {
|
||||
if (isExternalScrollbarInteractionActive()) {
|
||||
|
||||
@@ -2,12 +2,66 @@ import { describe, expect, it, vi } from 'vitest';
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
import {
|
||||
applyDataGridFixedCellPreviewOffset,
|
||||
calculateFixedVirtualRange,
|
||||
commitDataGridFixedCellOffset,
|
||||
createDataGridIdleCommitScheduler,
|
||||
createDataGridVisualFrameGuard,
|
||||
type DataGridVisualFrameGuard,
|
||||
} from './dataGridVirtualScroll';
|
||||
|
||||
const createStyleStub = () => {
|
||||
const values = new Map<string, { value: string; priority: string }>();
|
||||
return {
|
||||
getPropertyPriority: (name: string) => values.get(name)?.priority ?? '',
|
||||
getPropertyValue: (name: string) => values.get(name)?.value ?? '',
|
||||
removeProperty: vi.fn((name: string) => {
|
||||
const previous = values.get(name)?.value ?? '';
|
||||
values.delete(name);
|
||||
return previous;
|
||||
}),
|
||||
setProperty: vi.fn((name: string, value: string, priority = '') => {
|
||||
values.set(name, { value, priority });
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
describe('fixed cell horizontal preview', () => {
|
||||
it('updates visible fixed cells directly without invalidating their ancestor', () => {
|
||||
const first = { style: createStyleStub() };
|
||||
const second = { style: createStyleStub() };
|
||||
const root = { querySelectorAll: vi.fn(() => [first, second]) };
|
||||
|
||||
expect(applyDataGridFixedCellPreviewOffset(root as unknown as ParentNode, 640)).toBe(2);
|
||||
expect(first.style.setProperty).toHaveBeenCalledWith(
|
||||
'transform',
|
||||
'translate3d(640px, 0, 0)',
|
||||
'important',
|
||||
);
|
||||
expect(second.style.setProperty).toHaveBeenCalledTimes(1);
|
||||
|
||||
applyDataGridFixedCellPreviewOffset(root as unknown as ParentNode, 640);
|
||||
expect(first.style.setProperty).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('persists the settled offset once and releases per-cell preview styles', () => {
|
||||
const first = { style: createStyleStub() };
|
||||
const second = { style: createStyleStub() };
|
||||
const root = { querySelectorAll: vi.fn(() => [first, second]) };
|
||||
const inner = { style: createStyleStub() };
|
||||
applyDataGridFixedCellPreviewOffset(root as unknown as ParentNode, 480);
|
||||
|
||||
expect(commitDataGridFixedCellOffset(
|
||||
root as unknown as ParentNode,
|
||||
inner as unknown as HTMLElement,
|
||||
480,
|
||||
)).toBe(2);
|
||||
expect(inner.style.setProperty).toHaveBeenCalledWith('--gn-datagrid-h-scroll', '480px');
|
||||
expect(first.style.removeProperty).toHaveBeenCalledWith('transform');
|
||||
expect(second.style.removeProperty).toHaveBeenCalledWith('transform');
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateFixedVirtualRange', () => {
|
||||
const calculateLinearReference = ({
|
||||
itemCount,
|
||||
@@ -128,6 +182,9 @@ describe('calculateFixedVirtualRange', () => {
|
||||
expect(virtualListPatch).toContain('fixedStartIndex');
|
||||
expect(tablePatch).toContain('listItemHeightFixed');
|
||||
expect(tablePatch).toContain('itemHeightFixed: listItemHeightFixed');
|
||||
expect(tablePatch).toContain('bodyLinePropsAreEqual');
|
||||
expect(tablePatch).toContain('responseImmutable(BodyLine, bodyLinePropsAreEqual)');
|
||||
expect(tablePatch).toContain('lastForwardedXRef');
|
||||
expect(tablePatch).toContain('listItemColumnVirtual');
|
||||
expect(tablePatch).toContain('cell-virtual-spacer');
|
||||
expect(tablePatch).toContain('if (listItemColumnVirtual)');
|
||||
|
||||
@@ -12,6 +12,68 @@ export interface FixedVirtualRange {
|
||||
offset: number;
|
||||
}
|
||||
|
||||
const DATA_GRID_FIXED_CELL_SELECTOR = [
|
||||
'.ant-table-tbody-virtual-holder-inner .ant-table-cell-fix-left',
|
||||
'.ant-table-tbody-virtual-holder-inner .ant-table-cell-fix-left-first',
|
||||
'.ant-table-tbody-virtual-holder-inner .ant-table-cell-fix-left-last',
|
||||
'.ant-table-tbody-virtual-holder-inner .ant-table-cell-fix-right',
|
||||
'.ant-table-tbody-virtual-holder-inner .ant-table-cell-fix-right-first',
|
||||
'.ant-table-tbody-virtual-holder-inner .ant-table-cell-fix-right-last',
|
||||
'.ant-table-tbody-virtual-holder-inner .ant-table-selection-column',
|
||||
].join(',');
|
||||
|
||||
const normalizeHorizontalOffset = (offset: number): number => (
|
||||
Number.isFinite(offset) ? Math.max(0, offset) : 0
|
||||
);
|
||||
|
||||
const queryDataGridFixedCells = (root: ParentNode): NodeListOf<HTMLElement> => (
|
||||
root.querySelectorAll<HTMLElement>(DATA_GRID_FIXED_CELL_SELECTOR)
|
||||
);
|
||||
|
||||
/**
|
||||
* Keeps fixed cells visually pinned during a continuous horizontal preview.
|
||||
* Writing the inherited ancestor variable here would invalidate every cell in
|
||||
* the virtual body, so only the currently mounted fixed cells are touched.
|
||||
*/
|
||||
export const applyDataGridFixedCellPreviewOffset = (
|
||||
root: ParentNode,
|
||||
offset: number,
|
||||
): number => {
|
||||
const transform = `translate3d(${normalizeHorizontalOffset(offset)}px, 0, 0)`;
|
||||
const cells = queryDataGridFixedCells(root);
|
||||
cells.forEach((cell) => {
|
||||
if (
|
||||
cell.style.getPropertyValue('transform') !== transform
|
||||
|| cell.style.getPropertyPriority('transform') !== 'important'
|
||||
) {
|
||||
cell.style.setProperty('transform', transform, 'important');
|
||||
}
|
||||
});
|
||||
return cells.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Persists the settled offset for rows mounted by later vertical scrolling,
|
||||
* then releases the per-cell preview overrides.
|
||||
*/
|
||||
export const commitDataGridFixedCellOffset = (
|
||||
root: ParentNode,
|
||||
inner: HTMLElement,
|
||||
offset: number,
|
||||
): number => {
|
||||
const scrollVar = `${normalizeHorizontalOffset(offset)}px`;
|
||||
if (inner.style.getPropertyValue('--gn-datagrid-h-scroll') !== scrollVar) {
|
||||
inner.style.setProperty('--gn-datagrid-h-scroll', scrollVar);
|
||||
}
|
||||
const cells = queryDataGridFixedCells(root);
|
||||
cells.forEach((cell) => {
|
||||
if (cell.style.getPropertyValue('transform')) {
|
||||
cell.style.removeProperty('transform');
|
||||
}
|
||||
});
|
||||
return cells.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mirrors rc-virtual-list's visible range semantics for a fixed-height list,
|
||||
* but calculates the range arithmetically instead of scanning every item.
|
||||
|
||||
@@ -4549,19 +4549,8 @@ body[data-ui-version="v2"] .gn-v2-column-title-comment {
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid .react-resizable-handle::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
bottom: 8px;
|
||||
left: 4px;
|
||||
width: 2px;
|
||||
border-radius: 1px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid .react-resizable-handle:hover::after {
|
||||
background: color-mix(in srgb, var(--gn-info) 42%, transparent);
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid .react-resizable-handle {
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-tbody > tr > td,
|
||||
|
||||
Reference in New Issue
Block a user