import cx from 'classnames' import React from 'react' import type { CSSProperties, ReactNode } from 'react' import type { ArtColumn } from '../interfaces' import { internals } from '../internals' import { Colgroup } from './colgroup' import SpanManager from './helpers/SpanManager' import type { RenderInfo } from './interfaces' import { Classes } from './styles' import type { BaseTableProps } from './table' export interface HtmlTableProps extends Required> { tbodyHtmlTag: 'tbody' | 'tfoot' data: any[] horizontalRenderInfo: Pick< RenderInfo, 'flat' | 'visible' | 'horizontalRenderRange' | 'stickyLeftMap' | 'stickyRightMap' > verticalRenderInfo: { offset: number first: number last: number limit: number } } export function HtmlTable({ tbodyHtmlTag, getRowProps, primaryKey, data, verticalRenderInfo: verInfo, horizontalRenderInfo: hozInfo, components: { Row, Cell, TableBody }, }: HtmlTableProps) { const { flat, horizontalRenderRange: hoz } = hozInfo const spanManager = new SpanManager() const fullFlatCount = flat.full.length const leftFlatCount = flat.left.length const rightFlatCount = flat.right.length const tbody = TableBody != null && tbodyHtmlTag === 'tbody' ? ( ) : ( React.createElement(tbodyHtmlTag, null, data.map(renderRow)) ) return ( {tbody}
) function renderRow(row: any, i: number) { const rowIndex = verInfo.offset + i spanManager.stripUpwards(rowIndex) const rowProps = getRowProps(row, rowIndex) const rowClass = cx( Classes.tableRow, { first: rowIndex === verInfo.first, last: rowIndex === verInfo.last, even: rowIndex % 2 === 0, odd: rowIndex % 2 === 1, }, rowProps?.className, ) const trProps = { ...rowProps, className: rowClass, 'data-rowindex': rowIndex, children: hozInfo.visible.map((descriptor) => { if (descriptor.type === 'blank') { return } return renderBodyCell(row, rowIndex, descriptor.col, descriptor.colIndex) }), } const key = internals.safeGetRowKey(primaryKey, row, rowIndex) if (Row != null && tbodyHtmlTag === 'tbody') { return React.createElement(Row, { key, row, rowIndex, trProps }) } else { return } } function renderBodyCell(row: any, rowIndex: number, column: ArtColumn, colIndex: number) { if (spanManager.testSkip(rowIndex, colIndex)) { return null } const value = internals.safeGetValue(column, row, rowIndex) const cellProps = column.getCellProps?.(value, row, rowIndex) ?? {} let cellContent: ReactNode = value if (column.render) { cellContent = column.render(value, row, rowIndex) } else if (column.enumData) { cellContent = column.enumData[`${value}`] || value } let colSpan = 1 let rowSpan = 1 if (column.getSpanRect) { const spanRect = column.getSpanRect(value, row, rowIndex) colSpan = spanRect == null ? 1 : spanRect.right - colIndex rowSpan = spanRect == null ? 1 : spanRect.bottom - rowIndex } else { if (cellProps.colSpan != null) { colSpan = cellProps.colSpan } if (cellProps.rowSpan != null) { rowSpan = cellProps.rowSpan } } // rowSpan/colSpan 不能过大,避免 rowSpan/colSpan 影响因虚拟滚动而未渲染的单元格 rowSpan = Math.min(rowSpan, verInfo.limit - rowIndex) colSpan = Math.min(colSpan, leftFlatCount + hoz.rightIndex - colIndex) const hasSpan = colSpan > 1 || rowSpan > 1 if (hasSpan) { spanManager.add(rowIndex, colIndex, colSpan, rowSpan) } const positionStyle: CSSProperties = {} if (colIndex < leftFlatCount) { positionStyle.position = 'sticky' positionStyle.left = hozInfo.stickyLeftMap.get(colIndex) } else if (colIndex >= fullFlatCount - rightFlatCount) { positionStyle.position = 'sticky' positionStyle.right = hozInfo.stickyRightMap.get(colIndex) } const tdProps = { ...cellProps, className: cx(Classes.tableCell, cellProps.className, { first: colIndex === 0, last: colIndex + colSpan === fullFlatCount, 'lock-left': colIndex < leftFlatCount, 'lock-left-last': colIndex === leftFlatCount - 1, 'lock-right': colIndex >= fullFlatCount - rightFlatCount, "lock-right-first": colIndex === fullFlatCount - rightFlatCount, }), ...(hasSpan ? { colSpan, rowSpan } : null), style: { textAlign: column.align, ...cellProps.style, ...positionStyle, }, children: cellContent, } if (Cell != null && tbodyHtmlTag === 'tbody') { return } else { return } } }