1
This commit is contained in:
+38
@@ -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
@@ -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);
|
||||
+3
@@ -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];
|
||||
+60
@@ -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;
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
import type { OnHover } from '../hooks/useHover';
|
||||
export default function useHoverState(rowIndex: number, rowSpan: number): [hovering: boolean, onHover: OnHover];
|
||||
+13
@@ -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];
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user