This commit is contained in:
2026-05-25 17:08:18 +08:00
parent df501f6151
commit f1259b71b5
9178 changed files with 1626125 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
import * as React from 'react';
import type { AlignType, CellEllipsisType, ColumnType, CustomizeComponent, DataIndex, DefaultRecordType, ScopeType } from '../interface';
export interface CellProps<RecordType extends DefaultRecordType> {
prefixCls?: string;
className?: string;
style?: React.CSSProperties;
record?: RecordType;
/** `column` index is the real show rowIndex */
index?: number;
/** the index of the record. For the render(value, record, renderIndex) */
renderIndex?: number;
dataIndex?: DataIndex<RecordType>;
render?: ColumnType<RecordType>['render'];
component?: CustomizeComponent;
children?: React.ReactNode;
colSpan?: number;
rowSpan?: number;
scope?: ScopeType;
ellipsis?: CellEllipsisType;
align?: AlignType;
shouldCellUpdate?: (record: RecordType, prevRecord: RecordType) => boolean;
fixStart?: number | false;
fixEnd?: number | false;
fixedStartShadow?: boolean;
fixedEndShadow?: boolean;
offsetFixedStartShadow?: number;
offsetFixedEndShadow?: number;
zIndex?: number;
zIndexReverse?: number;
allColsFixedLeft?: boolean;
/** @private Used for `expandable` with nest tree */
appendNode?: React.ReactNode;
additionalProps?: React.TdHTMLAttributes<HTMLTableCellElement>;
rowType?: 'header' | 'body' | 'footer';
isSticky?: boolean;
}
declare const _default: <RecordType>(props: CellProps<RecordType>) => React.JSX.Element;
export default _default;
+200
View File
@@ -0,0 +1,200 @@
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import { useContext } from '@rc-component/context';
import { clsx } from 'clsx';
import * as React from 'react';
import TableContext from "../context/TableContext";
import devRenderTimes from "../hooks/useRenderTimes";
import useCellRender from "./useCellRender";
import useHoverState from "./useHoverState";
import { useEvent } from '@rc-component/util';
const getTitleFromCellRenderChildren = ({
ellipsis,
rowType,
children
}) => {
let title;
const ellipsisConfig = ellipsis === true ? {
showTitle: true
} : ellipsis;
if (ellipsisConfig && (ellipsisConfig.showTitle || rowType === 'header')) {
if (typeof children === 'string' || typeof children === 'number') {
title = children.toString();
} else if ( /*#__PURE__*/React.isValidElement(children) && typeof children.props?.children === 'string') {
title = children.props?.children;
}
}
return title;
};
const Cell = props => {
if (process.env.NODE_ENV !== 'production') {
devRenderTimes(props);
}
const {
component: Component,
children,
ellipsis,
scope,
// Style
prefixCls,
className,
style,
align,
// Value
record,
render,
dataIndex,
renderIndex,
shouldCellUpdate,
// Row
index,
rowType,
// Span
colSpan,
rowSpan,
// Fixed
fixStart,
fixEnd,
fixedStartShadow,
fixedEndShadow,
offsetFixedStartShadow,
offsetFixedEndShadow,
zIndex,
zIndexReverse,
// Private
appendNode,
additionalProps = {},
isSticky
} = props;
const cellPrefixCls = `${prefixCls}-cell`;
const {
allColumnsFixedLeft,
rowHoverable
} = useContext(TableContext, ['allColumnsFixedLeft', 'rowHoverable']);
// ====================== Value =======================
const [childNode, legacyCellProps] = useCellRender(record, dataIndex, renderIndex, children, render, shouldCellUpdate);
// ====================== Fixed =======================
const fixedStyle = {};
const isFixStart = typeof fixStart === 'number' && !allColumnsFixedLeft;
const isFixEnd = typeof fixEnd === 'number' && !allColumnsFixedLeft;
const [showFixStartShadow, showFixEndShadow] = useContext(TableContext, ({
scrollInfo
}) => {
if (!isFixStart && !isFixEnd) {
return [false, false];
}
const [absScroll, scrollWidth] = scrollInfo;
const showStartShadow = (isFixStart && fixedStartShadow && absScroll) -
// For precision, we not show shadow by default which has better user experience.
offsetFixedStartShadow >= 1;
const showEndShadow = (isFixEnd && fixedEndShadow && scrollWidth - absScroll) -
// Same as above
offsetFixedEndShadow > 1;
return [showStartShadow, showEndShadow];
});
if (isFixStart) {
fixedStyle.insetInlineStart = fixStart;
fixedStyle['--z-offset'] = zIndex;
fixedStyle['--z-offset-reverse'] = zIndexReverse;
}
if (isFixEnd) {
fixedStyle.insetInlineEnd = fixEnd;
fixedStyle['--z-offset'] = zIndex;
fixedStyle['--z-offset-reverse'] = zIndexReverse;
}
// ================ RowSpan & ColSpan =================
const mergedColSpan = legacyCellProps?.colSpan ?? additionalProps.colSpan ?? colSpan ?? 1;
const mergedRowSpan = legacyCellProps?.rowSpan ?? additionalProps.rowSpan ?? rowSpan ?? 1;
// ====================== Hover =======================
const [hovering, onHover] = useHoverState(index, mergedRowSpan);
const onMouseEnter = useEvent(event => {
if (record) {
onHover(index, index + mergedRowSpan - 1);
}
additionalProps?.onMouseEnter?.(event);
});
const onMouseLeave = useEvent(event => {
if (record) {
onHover(-1, -1);
}
additionalProps?.onMouseLeave?.(event);
});
// ====================== Render ======================
if (mergedColSpan === 0 || mergedRowSpan === 0) {
return null;
}
// >>>>> Title
const title = additionalProps.title ?? getTitleFromCellRenderChildren({
rowType,
ellipsis,
children: childNode
});
// >>>>> ClassName
const mergedClassName = clsx(cellPrefixCls, className, {
// Fixed
[`${cellPrefixCls}-fix`]: isFixStart || isFixEnd,
[`${cellPrefixCls}-fix-start`]: isFixStart,
[`${cellPrefixCls}-fix-end`]: isFixEnd,
// Fixed shadow
[`${cellPrefixCls}-fix-start-shadow`]: fixedStartShadow,
[`${cellPrefixCls}-fix-start-shadow-show`]: fixedStartShadow && showFixStartShadow,
[`${cellPrefixCls}-fix-end-shadow`]: fixedEndShadow,
[`${cellPrefixCls}-fix-end-shadow-show`]: fixedEndShadow && showFixEndShadow,
[`${cellPrefixCls}-ellipsis`]: ellipsis,
[`${cellPrefixCls}-with-append`]: appendNode,
[`${cellPrefixCls}-fix-sticky`]: (isFixStart || isFixEnd) && isSticky,
[`${cellPrefixCls}-row-hover`]: !legacyCellProps && hovering
}, additionalProps.className, legacyCellProps?.className);
// >>>>> Style
const alignStyle = {};
if (align) {
alignStyle.textAlign = align;
}
// The order is important since user can overwrite style.
// For example ant-design/ant-design#51763
const mergedStyle = {
...legacyCellProps?.style,
...fixedStyle,
...alignStyle,
...additionalProps.style,
...style
};
// >>>>> Children Node
let mergedChildNode = childNode;
// Not crash if final `childNode` is not validate ReactNode
if (typeof mergedChildNode === 'object' && !Array.isArray(mergedChildNode) && ! /*#__PURE__*/React.isValidElement(mergedChildNode)) {
mergedChildNode = null;
}
if (ellipsis && (fixedStartShadow || fixedEndShadow)) {
mergedChildNode = /*#__PURE__*/React.createElement("span", {
className: `${cellPrefixCls}-content`
}, mergedChildNode);
}
return /*#__PURE__*/React.createElement(Component, _extends({}, legacyCellProps, additionalProps, {
className: mergedClassName,
style: mergedStyle
// A11y
,
title: title,
scope: scope
// Hover
,
onMouseEnter: rowHoverable ? onMouseEnter : undefined,
onMouseLeave: rowHoverable ? onMouseLeave : undefined
//Span
,
colSpan: mergedColSpan !== 1 ? mergedColSpan : null,
rowSpan: mergedRowSpan !== 1 ? mergedRowSpan : null
}), appendNode, mergedChildNode);
};
export default /*#__PURE__*/React.memo(Cell);
@@ -0,0 +1,3 @@
import * as React from 'react';
import type { CellType, ColumnType, DataIndex } from '../interface';
export default function useCellRender<RecordType>(record: RecordType, dataIndex: DataIndex<RecordType>, renderIndex: number, children?: React.ReactNode, render?: ColumnType<RecordType>['render'], shouldCellUpdate?: ColumnType<RecordType>['shouldCellUpdate']): [React.ReactNode, CellType<RecordType>] | [React.ReactNode];
@@ -0,0 +1,60 @@
import useMemo from "@rc-component/util/es/hooks/useMemo";
import isEqual from "@rc-component/util/es/isEqual";
import getValue from "@rc-component/util/es/utils/get";
import warning from "@rc-component/util/es/warning";
import * as React from 'react';
import PerfContext from "../context/PerfContext";
import { validateValue } from "../utils/valueUtil";
import { useImmutableMark } from "../context/TableContext";
function isRenderCell(data) {
return data && typeof data === 'object' && !Array.isArray(data) && ! /*#__PURE__*/React.isValidElement(data);
}
export default function useCellRender(record, dataIndex, renderIndex, children, render, shouldCellUpdate) {
// TODO: Remove this after next major version
const perfRecord = React.useContext(PerfContext);
const mark = useImmutableMark();
// ======================== Render ========================
const retData = useMemo(() => {
if (validateValue(children)) {
return [children];
}
const path = dataIndex === null || dataIndex === undefined || dataIndex === '' ? [] : Array.isArray(dataIndex) ? dataIndex : [dataIndex];
const value = getValue(record, path);
// Customize render node
let returnChildNode = value;
let returnCellProps = undefined;
if (render) {
const renderData = render(value, record, renderIndex);
if (isRenderCell(renderData)) {
if (process.env.NODE_ENV !== 'production') {
warning(false, '`columns.render` return cell props is deprecated with perf issue, please use `onCell` instead.');
}
returnChildNode = renderData.children;
returnCellProps = renderData.props;
perfRecord.renderWithProps = true;
} else {
returnChildNode = renderData;
}
}
return [returnChildNode, returnCellProps];
}, [
// Force update deps
mark,
// Normal deps
record, children, dataIndex, render, renderIndex], (prev, next) => {
if (shouldCellUpdate) {
const [, prevRecord] = prev;
const [, nextRecord] = next;
return shouldCellUpdate(nextRecord, prevRecord);
}
// Legacy mode should always update
if (perfRecord.renderWithProps) {
return true;
}
return !isEqual(prev, next, true);
});
return retData;
}
@@ -0,0 +1,2 @@
import type { OnHover } from '../hooks/useHover';
export default function useHoverState(rowIndex: number, rowSpan: number): [hovering: boolean, onHover: OnHover];
@@ -0,0 +1,13 @@
import { useContext } from '@rc-component/context';
import TableContext from "../context/TableContext";
/** Check if cell is in hover range */
function inHoverRange(cellStartRow, cellRowSpan, startRow, endRow) {
const cellEndRow = cellStartRow + cellRowSpan - 1;
return cellStartRow <= endRow && cellEndRow >= startRow;
}
export default function useHoverState(rowIndex, rowSpan) {
return useContext(TableContext, ctx => {
const hovering = inHoverRange(rowIndex, rowSpan || 1, ctx.hoverStartRow, ctx.hoverEndRow);
return [hovering, ctx.onHover];
});
}