import type { ArtColumn } from '../interfaces' import { collectNodes, isLeafNode } from '../utils' import type { HorizontalRenderRange, RenderInfo, ResolvedUseVirtual, VirtualEnum, VisibleColumnDescriptor, } from './interfaces' import { BaseTable } from './table' import { AUTO_VIRTUAL_THRESHOLD, OVERSCAN_SIZE, sum } from './utils' function resolveVirtualEnabled(virtualEnum: VirtualEnum, defaultValue: boolean) { if (virtualEnum == null || virtualEnum === 'auto') { return defaultValue } return virtualEnum } let lockColumnNeedSpecifiedWidthWarned = false function warnLockColumnNeedSpecifiedWidth(column: ArtColumn) { if (!lockColumnNeedSpecifiedWidthWarned) { lockColumnNeedSpecifiedWidthWarned = true console.warn('[ali-react-table] lock=true 的列需要指定宽度', column) } } let columnHiddenDeprecatedWarned = false function warnColumnHiddenDeprecated(column: ArtColumn) { if (!columnHiddenDeprecatedWarned) { columnHiddenDeprecatedWarned = true console.warn('[ali-react-table] column.hidden 已经过时,如果需要隐藏该列,请将其从 columns 数组中移除', column) } } /** 检查列配置 & 设置默认宽度 & 剔除隐藏的列 */ function processColumns(columns: ArtColumn[], defaultColumnWidth: number) { if (columns == null || !Array.isArray(columns)) { console.warn('[ali-react-table] props.columns 需要传入一个数组', columns) columns = [] } function dfs(columns: ArtColumn[]): ArtColumn[] { const result: ArtColumn[] = [] for (let column of columns) { if (column.width == null) { if (defaultColumnWidth != null) { column = { ...column, width: defaultColumnWidth } } else if (process.env.NODE_ENV !== 'production' && isLeafNode(column) && column.lock) { warnLockColumnNeedSpecifiedWidth(column) } } if (isLeafNode(column)) { if (column.hidden) { // 被隐藏的列 会在这里被剔除 warnColumnHiddenDeprecated(column) } else if (column.visible === false) { // 隐藏的列 会在这里被剔除 } else { result.push(column) } } else { const nextChildren = dfs(column.children) // 如果 nextChildren 为空,说明所有的子节点均被隐藏了,在这里隐藏父节点 if (nextChildren.length > 0) { result.push({ ...column, children: nextChildren }) } } } return result } return dfs(columns) } export function getLeftNestedLockCount(columns: ArtColumn[]) { let nestedCount = 0 for (const col of columns) { if (isLock(col)) { nestedCount += 1 } else { break } } return nestedCount function isLock(col: ArtColumn): boolean { if (isLeafNode(col)) { return col.lock } else { return col.lock || col.children.some(isLock) } } } function getHorizontalRenderRange({ offsetX, maxRenderWidth, flat, useVirtual, }: { offsetX: number maxRenderWidth: number flat: RenderInfo['flat'] useVirtual: ResolvedUseVirtual }): HorizontalRenderRange { if (!useVirtual.horizontal) { return { leftIndex: 0, leftBlank: 0, rightIndex: flat.full.length, rightBlank: 0 } } let leftIndex = 0 let centerCount = 0 let leftBlank = 0 let centerRenderWidth = 0 const overscannedOffsetX = Math.max(0, offsetX - OVERSCAN_SIZE) while (leftIndex < flat.center.length) { const col = flat.center[leftIndex] if (col.width + leftBlank < overscannedOffsetX) { leftIndex += 1 leftBlank += col.width } else { break } } // 考虑 over scan 之后,中间部分的列至少需要渲染的宽度 const minCenterRenderWidth = maxRenderWidth + (overscannedOffsetX - leftBlank) + 2 * OVERSCAN_SIZE while (leftIndex + centerCount < flat.center.length) { const col = flat.center[leftIndex + centerCount] if (col.width + centerRenderWidth < minCenterRenderWidth) { centerRenderWidth += col.width centerCount += 1 } else { break } } const rightBlankCount = flat.center.length - leftIndex - centerCount const rightBlank = sum(flat.center.slice(flat.center.length - rightBlankCount).map((col) => col.width)) return { leftIndex: leftIndex, leftBlank, rightIndex: leftIndex + centerCount, rightBlank, } } // 一顿计算,将表格本次渲染所需要的数据都给算出来(代码写得有点乱,有较大优化空间) // todo 可以考虑下将 header 部分的计算逻辑也放到这个文件中,目前应该有一些重复的计算逻辑 export function calculateRenderInfo(table: BaseTable): RenderInfo { const { offsetX, maxRenderWidth } = table.state const { useVirtual: useVirtualProp, columns: columnsProp, dataSource: dataSourceProp, defaultColumnWidth, overflowVerticalNumber = 0, dragType } = table.props // 过滤掉隐藏的列 // const _columns = columnsProp.filter((col) => col.visible !== false) /**处理列*/ const columns = processColumns(columnsProp, defaultColumnWidth) // 获取锁定列 const leftNestedLockCount = getLeftNestedLockCount(columns) /***/ const fullFlat = collectNodes(columns, 'leaf-only') /**分组数据*/ let groupColumns = [] let otherColumns = [...columnsProp] if (dragType === 'columnGroup') { groupColumns = columnsProp.filter((col) => typeof col.groupIndex === 'number').sort((a, b) => a.groupIndex - b.groupIndex) otherColumns = columnsProp.filter((col) => typeof col.groupIndex !== 'number') } let flat: RenderInfo['flat'] let nested: RenderInfo['nested'] let useVirtual: RenderInfo['useVirtual'] if (leftNestedLockCount === columns.length) { flat = { left: [], right: [], full: fullFlat, center: fullFlat } nested = { left: [], right: [], full: columns, center: columns } useVirtual = { horizontal: false, vertical: false, header: false } } else { const leftNested = columns.slice(0, leftNestedLockCount) const rightNestedLockCount = getLeftNestedLockCount(columns.slice().reverse()) const centerNested = columns.slice(leftNestedLockCount, columns.length - rightNestedLockCount) const rightNested = columns.slice(columns.length - rightNestedLockCount) const shouldEnableHozVirtual = fullFlat.length >= AUTO_VIRTUAL_THRESHOLD && fullFlat.every((col) => col.width != null) const shouldEnableVerVirtual = dataSourceProp.length >= AUTO_VIRTUAL_THRESHOLD useVirtual = { horizontal: resolveVirtualEnabled( typeof useVirtualProp === 'object' ? useVirtualProp.horizontal : useVirtualProp, shouldEnableHozVirtual, ), vertical: resolveVirtualEnabled( typeof useVirtualProp === 'object' ? useVirtualProp.vertical : useVirtualProp, shouldEnableVerVirtual, ), header: resolveVirtualEnabled(typeof useVirtualProp === 'object' ? useVirtualProp.header : useVirtualProp, false), } flat = { left: collectNodes(leftNested, 'leaf-only'), full: fullFlat, right: collectNodes(rightNested, 'leaf-only'), center: collectNodes(centerNested, 'leaf-only'), } nested = { left: leftNested, full: columns, right: rightNested, center: centerNested, } } const horizontalRenderRange = getHorizontalRenderRange({ maxRenderWidth, offsetX, useVirtual, flat }) const verticalRenderRange = table.getVerticalRenderRange(useVirtual) const { leftBlank, leftIndex, rightBlank, rightIndex } = horizontalRenderRange const unfilteredVisibleColumnDescriptors: VisibleColumnDescriptor[] = [ ...flat.left.map((col, i) => ({ type: 'normal', col, colIndex: i } as const)), leftBlank > 0 && { type: 'blank', blankSide: 'left', width: leftBlank }, ...flat.center .slice(leftIndex, rightIndex) .map((col, i) => ({ type: 'normal', col, colIndex: flat.left.length + leftIndex + i } as const)), rightBlank > 0 && { type: 'blank', blankSide: 'right', width: rightBlank }, ...flat.right.map( (col, i) => ({ type: 'normal', col, colIndex: flat.full.length - flat.right.length + i } as const), ), ] const visibleColumnDescriptors = unfilteredVisibleColumnDescriptors.filter(Boolean) const fullFlatCount = flat.full.length const leftFlatCount = flat.left.length const rightFlatCount = flat.right.length const stickyLeftMap = new Map() let stickyLeft = 0 for (let i = 0; i < leftFlatCount; i++) { stickyLeftMap.set(i, stickyLeft) stickyLeft += flat.full[i].width } const stickyRightMap = new Map() let stickyRight = 0 for (let i = 0; i < rightFlatCount; i++) { stickyRightMap.set(fullFlatCount - 1 - i, stickyRight) stickyRight += flat.full[fullFlatCount - 1 - i].width } const leftLockTotalWidth = sum(flat.left.map((col) => col.width)) const rightLockTotalWidth = sum(flat.right.map((col) => col.width)) if (overflowVerticalNumber) { const { topIndex, bottomIndex } = verticalRenderRange const length = dataSourceProp.length; const startTopIndex = topIndex - overflowVerticalNumber <= 0 ? 0 : topIndex - overflowVerticalNumber; const endBottomIndex = bottomIndex + overflowVerticalNumber >= length ? length : bottomIndex + overflowVerticalNumber; verticalRenderRange.startTopIndex = startTopIndex verticalRenderRange.endBottomIndex = endBottomIndex } return { groupColumns, otherColumns, columns: columnsProp, horizontalRenderRange, verticalRenderRange, visible: visibleColumnDescriptors, flat, nested, useVirtual, stickyLeftMap, stickyRightMap, leftLockTotalWidth, rightLockTotalWidth, hasLockColumn: nested.left.length > 0 || nested.right.length > 0, } }