Files
video-gen/video-gen-admin/node_modules/@rc-component/table/es/VirtualTable/BodyGrid.js
T
2026-05-25 17:08:18 +08:00

261 lines
7.6 KiB
JavaScript

import { useContext } from '@rc-component/context';
import VirtualList from '@rc-component/virtual-list';
import * as React from 'react';
import TableContext, { responseImmutable } from "../context/TableContext";
import useFlattenRecords from "../hooks/useFlattenRecords";
import BodyLine from "./BodyLine";
import { GridContext, StaticContext } from "./context";
const Grid = /*#__PURE__*/React.forwardRef((props, ref) => {
const {
data,
onScroll
} = props;
const {
flattenColumns,
onColumnResize,
getRowKey,
expandedKeys,
prefixCls,
childrenColumnName,
scrollX,
direction
} = useContext(TableContext, ['flattenColumns', 'onColumnResize', 'getRowKey', 'prefixCls', 'expandedKeys', 'childrenColumnName', 'scrollX', 'direction']);
const {
sticky,
scrollY,
listItemHeight,
getComponent,
onScroll: onTablePropScroll
} = useContext(StaticContext);
// =========================== Ref ============================
const listRef = React.useRef(null);
// =========================== Data ===========================
const flattenData = useFlattenRecords(data, childrenColumnName, expandedKeys, getRowKey);
// ========================== Column ==========================
const columnsWidth = React.useMemo(() => {
let total = 0;
return flattenColumns.map(({
width,
minWidth,
key
}) => {
const finalWidth = Math.max(width || 0, minWidth || 0);
total += finalWidth;
return [key, finalWidth, total];
});
}, [flattenColumns]);
const columnsOffset = React.useMemo(() => columnsWidth.map(colWidth => colWidth[2]), [columnsWidth]);
React.useEffect(() => {
columnsWidth.forEach(([key, width]) => {
onColumnResize(key, width);
});
}, [columnsWidth]);
// =========================== Ref ============================
React.useImperativeHandle(ref, () => {
const obj = {
scrollTo: config => {
const {
offset,
...restConfig
} = config;
// If offset is provided, force align to 'top' for consistent behavior
if (offset) {
listRef.current?.scrollTo({
...restConfig,
offset,
align: 'top'
});
} else {
listRef.current?.scrollTo(config);
}
},
nativeElement: listRef.current?.nativeElement
};
Object.defineProperty(obj, 'scrollLeft', {
get: () => listRef.current?.getScrollInfo().x || 0,
set: value => {
listRef.current?.scrollTo({
left: value
});
}
});
// https://github.com/ant-design/ant-design/issues/54734
Object.defineProperty(obj, 'scrollTop', {
get: () => listRef.current?.getScrollInfo().y || 0,
set: value => {
listRef.current?.scrollTo({
top: value
});
}
});
return obj;
});
// ======================= Col/Row Span =======================
const getRowSpan = (column, index) => {
const record = flattenData[index]?.record;
const {
onCell
} = column;
if (onCell) {
const cellProps = onCell(record, index);
return cellProps?.rowSpan ?? 1;
}
return 1;
};
const extraRender = info => {
const {
start,
end,
getSize,
offsetY
} = info;
// Do nothing if no data
if (end < 0) {
return null;
}
// Find first rowSpan column
let firstRowSpanColumns = flattenColumns.filter(
// rowSpan is 0
column => getRowSpan(column, start) === 0);
let startIndex = start;
for (let i = start; i >= 0; i -= 1) {
firstRowSpanColumns = firstRowSpanColumns.filter(column => getRowSpan(column, i) === 0);
if (!firstRowSpanColumns.length) {
startIndex = i;
break;
}
}
// Find last rowSpan column
let lastRowSpanColumns = flattenColumns.filter(
// rowSpan is not 1
column => getRowSpan(column, end) !== 1);
let endIndex = end;
for (let i = end; i < flattenData.length; i += 1) {
lastRowSpanColumns = lastRowSpanColumns.filter(column => getRowSpan(column, i) !== 1);
if (!lastRowSpanColumns.length) {
endIndex = Math.max(i - 1, end);
break;
}
}
// Collect the line who has rowSpan
const spanLines = [];
for (let i = startIndex; i <= endIndex; i += 1) {
const item = flattenData[i];
// This code will never reach, just incase
if (!item) {
continue;
}
if (flattenColumns.some(column => getRowSpan(column, i) > 1)) {
spanLines.push(i);
}
}
// Patch extra line on the page
const nodes = spanLines.map(index => {
const item = flattenData[index];
const rowKey = getRowKey(item.record, index);
const getHeight = rowSpan => {
const endItemIndex = index + rowSpan - 1;
const endItem = flattenData[endItemIndex];
if (!endItem || !endItem.record) {
// clamp 到当前可用的最后一行,或退化为默认高度
const safeEndIndex = Math.min(endItemIndex, flattenData.length - 1);
const safeEndItem = flattenData[safeEndIndex];
const endItemKey = getRowKey(safeEndItem.record, safeEndIndex);
const sizeInfo = getSize(rowKey, endItemKey);
return sizeInfo.bottom - sizeInfo.top;
}
const endItemKey = getRowKey(endItem.record, endItemIndex);
const sizeInfo = getSize(rowKey, endItemKey);
return sizeInfo.bottom - sizeInfo.top;
};
const sizeInfo = getSize(rowKey);
return /*#__PURE__*/React.createElement(BodyLine, {
key: index,
data: item,
rowKey: rowKey,
index: index,
style: {
top: -offsetY + sizeInfo.top
},
extra: true,
getHeight: getHeight
});
});
return nodes;
};
// ========================= Context ==========================
const gridContext = React.useMemo(() => ({
columnsOffset
}), [columnsOffset]);
// ========================== Render ==========================
const tblPrefixCls = `${prefixCls}-tbody`;
// default 'div' in @rc-component/virtual-list
const wrapperComponent = getComponent(['body', 'wrapper']);
// ========================== Sticky Scroll Bar ==========================
const horizontalScrollBarStyle = {};
if (sticky) {
horizontalScrollBarStyle.position = 'sticky';
horizontalScrollBarStyle.bottom = 0;
if (typeof sticky === 'object' && sticky.offsetScroll) {
horizontalScrollBarStyle.bottom = sticky.offsetScroll;
}
}
return /*#__PURE__*/React.createElement(GridContext.Provider, {
value: gridContext
}, /*#__PURE__*/React.createElement(VirtualList, {
fullHeight: false,
ref: listRef,
prefixCls: `${tblPrefixCls}-virtual`,
styles: {
horizontalScrollBar: horizontalScrollBarStyle
},
className: tblPrefixCls,
height: scrollY,
itemHeight: listItemHeight || 24,
data: flattenData,
itemKey: item => getRowKey(item.record),
component: wrapperComponent,
scrollWidth: scrollX,
direction: direction,
onVirtualScroll: ({
x
}) => {
onScroll({
currentTarget: listRef.current?.nativeElement,
scrollLeft: x
});
},
onScroll: onTablePropScroll,
extraRender: extraRender
}, (item, index, itemProps) => {
const rowKey = getRowKey(item.record, index);
return /*#__PURE__*/React.createElement(BodyLine, {
data: item,
rowKey: rowKey,
index: index,
style: itemProps.style
});
}));
});
const ResponseGrid = responseImmutable(Grid);
if (process.env.NODE_ENV !== 'production') {
ResponseGrid.displayName = 'ResponseGrid';
}
export default ResponseGrid;