linkmore-design
Version:
π πlmη»δ»ΆεΊγπ
666 lines (641 loc) β’ 24.6 kB
JavaScript
;
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.INTERNAL_HOOKS = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _classnames = _interopRequireDefault(require("classnames"));
var _rcResizeObserver = _interopRequireDefault(require("rc-resize-observer"));
var _isVisible = _interopRequireDefault(require("rc-util/lib/Dom/isVisible"));
var _styleChecker = require("rc-util/lib/Dom/styleChecker");
var _getScrollBarSize = require("rc-util/lib/getScrollBarSize");
var _pickAttrs = _interopRequireDefault(require("rc-util/lib/pickAttrs"));
var _warning = _interopRequireDefault(require("rc-util/lib/warning"));
var React = _interopRequireWildcard(require("react"));
var _shallowequal = _interopRequireDefault(require("shallowequal"));
var _Body = _interopRequireDefault(require("./Body"));
var _ColGroup = _interopRequireDefault(require("./ColGroup"));
var _constant = require("./constant");
var _BodyContext = _interopRequireDefault(require("./context/BodyContext"));
var _ExpandedRowContext = _interopRequireDefault(require("./context/ExpandedRowContext"));
var _ResizeContext = _interopRequireDefault(require("./context/ResizeContext"));
var _StickyContext = _interopRequireDefault(require("./context/StickyContext"));
var _TableContext = _interopRequireDefault(require("./context/TableContext"));
var _FixedHolder = _interopRequireDefault(require("./FixedHolder"));
var _Footer = _interopRequireWildcard(require("./Footer"));
var _Summary = _interopRequireDefault(require("./Footer/Summary"));
var _Header = _interopRequireDefault(require("./Header/Header"));
var _useColumns = _interopRequireDefault(require("./hooks/useColumns"));
var _useFrame = require("./hooks/useFrame");
var _useSticky = _interopRequireDefault(require("./hooks/useSticky"));
var _useStickyOffsets = _interopRequireDefault(require("./hooks/useStickyOffsets"));
var _Panel = _interopRequireDefault(require("./Panel"));
var _stickyScrollBar = _interopRequireDefault(require("./stickyScrollBar"));
var _Column = _interopRequireDefault(require("./sugar/Column"));
var _ColumnGroup = _interopRequireDefault(require("./sugar/ColumnGroup"));
var _expandUtil = require("./utils/expandUtil");
var _fixUtil = require("./utils/fixUtil");
var _legacyUtil = require("./utils/legacyUtil");
var _valueUtil = require("./utils/valueUtil");
/**
* Feature:
* - fixed not need to set width
* - support `rowExpandable` to config row expand logic
* - add `summary` to support `() => ReactNode`
*
* Update:
* - `dataIndex` is `array[]` now
* - `expandable` wrap all the expand related props
*
* Removed:
* - expandIconAsCell
* - useFixedHeader
* - rowRef
* - columns[number].onCellClick
* - onRowClick
* - onRowDoubleClick
* - onRowMouseEnter
* - onRowMouseLeave
* - getBodyWrapper
* - bodyStyle
*
* Deprecated:
* - All expanded props, move into expandable
*/
// Used for conditions cache
const EMPTY_DATA = [];
// Used for customize scroll
const EMPTY_SCROLL_TARGET = {};
const INTERNAL_HOOKS = 'rc-table-internal-hook';
exports.INTERNAL_HOOKS = INTERNAL_HOOKS;
const MemoTableContent = /*#__PURE__*/React.memo(({
children
}) => children, (prev, next) => {
if (!(0, _shallowequal.default)(prev.props, next.props)) {
return false;
}
// No additional render when pinged status change.
// This is not a bug.
return prev.pingLeft !== next.pingLeft || prev.pingRight !== next.pingRight;
});
function Table(props) {
const {
prefixCls,
className,
rowClassName,
contentClassName,
style,
data,
rowKey,
scroll,
tableLayout,
direction,
// Additional Part
title,
footer,
summary,
caption,
// Customize
id,
showHeader,
components,
emptyText,
onRow,
onHeaderRow,
// Internal
internalHooks,
transformColumns,
internalRefs,
sticky
} = props;
const mergedData = data || EMPTY_DATA;
const hasData = !!mergedData.length;
// ===================== Warning ======================
if (process.env.NODE_ENV !== 'production') {
;
['onRowClick', 'onRowDoubleClick', 'onRowContextMenu', 'onRowMouseEnter', 'onRowMouseLeave'].forEach(name => {
(0, _warning.default)(props[name] === undefined, `\`${name}\` is removed, please use \`onRow\` instead.`);
});
(0, _warning.default)(!('getBodyWrapper' in props), '`getBodyWrapper` is deprecated, please use custom `components` instead.');
}
// ==================== Customize =====================
const getComponent = React.useCallback((path, defaultComponent) => (0, _valueUtil.getPathValue)(components || {}, path) || defaultComponent, [components]);
const getRowKey = React.useMemo(() => {
if (typeof rowKey === 'function') {
return rowKey;
}
return record => {
const key = record && record[rowKey];
if (process.env.NODE_ENV !== 'production') {
(0, _warning.default)(key !== undefined, 'Each record in table should have a unique `key` prop, or set `rowKey` to an unique primary key.');
}
return key;
};
}, [rowKey]);
// ====================== Expand ======================
const expandableConfig = (0, _legacyUtil.getExpandableProps)(props);
const {
expandIcon,
expandedRowKeys,
defaultExpandedRowKeys,
defaultExpandAllRows,
expandedRowRender,
columnTitle,
onExpand,
onExpandedRowsChange,
expandRowByClick,
rowExpandable,
expandIconColumnIndex,
expandedRowClassName,
childrenColumnName,
indentSize
} = expandableConfig;
const mergedExpandIcon = expandIcon || _expandUtil.renderExpandIcon;
const mergedChildrenColumnName = childrenColumnName || 'children';
const expandableType = React.useMemo(() => {
if (expandedRowRender) {
return 'row';
}
/* eslint-disable no-underscore-dangle */
/**
* Fix https://github.com/ant-design/ant-design/issues/21154
* This is a workaround to not to break current behavior.
* We can remove follow code after final release.
*
* To other developer:
* Do not use `__PARENT_RENDER_ICON__` in prod since we will remove this when refactor
*/
if (props.expandable && internalHooks === INTERNAL_HOOKS && props.expandable.__PARENT_RENDER_ICON__ || mergedData.some(record => record && typeof record === 'object' && record[mergedChildrenColumnName])) {
return 'nest';
}
/* eslint-enable */
return false;
}, [!!expandedRowRender, mergedData]);
const [innerExpandedKeys, setInnerExpandedKeys] = React.useState(() => {
if (defaultExpandedRowKeys) {
return defaultExpandedRowKeys;
}
if (defaultExpandAllRows) {
return (0, _expandUtil.findAllChildrenKeys)(mergedData, getRowKey, mergedChildrenColumnName);
}
return [];
});
const mergedExpandedKeys = React.useMemo(() => new Set(expandedRowKeys || innerExpandedKeys || []), [expandedRowKeys, innerExpandedKeys]);
const onTriggerExpand = React.useCallback(record => {
const key = getRowKey(record, mergedData.indexOf(record));
let newExpandedKeys;
const hasKey = mergedExpandedKeys.has(key);
if (hasKey) {
mergedExpandedKeys.delete(key);
newExpandedKeys = [...mergedExpandedKeys];
} else {
newExpandedKeys = [...mergedExpandedKeys, key];
}
setInnerExpandedKeys(newExpandedKeys);
if (onExpand) {
onExpand(!hasKey, record);
}
if (onExpandedRowsChange) {
onExpandedRowsChange(newExpandedKeys);
}
}, [getRowKey, mergedExpandedKeys, mergedData, onExpand, onExpandedRowsChange]);
// Warning if use `expandedRowRender` and nest children in the same time
if (process.env.NODE_ENV !== 'production' && expandedRowRender && mergedData.some(record => {
return Array.isArray(record?.[mergedChildrenColumnName]);
})) {
(0, _warning.default)(false, '`expandedRowRender` should not use with nested Table');
}
// ====================== Column ======================
const [componentWidth, setComponentWidth] = React.useState(0);
const [columns, flattenColumns] = (0, _useColumns.default)({
...props,
...expandableConfig,
expandable: !!expandedRowRender,
columnTitle: columnTitle,
expandedKeys: mergedExpandedKeys,
getRowKey,
// https://github.com/ant-design/ant-design/issues/23894
onTriggerExpand,
expandIcon: mergedExpandIcon,
expandIconColumnIndex,
direction
}, internalHooks === INTERNAL_HOOKS ? transformColumns : null);
const columnContext = React.useMemo(() => ({
columns,
flattenColumns
}), [columns, flattenColumns]);
// ====================== Scroll ======================
const fullTableRef = React.useRef();
const scrollHeaderRef = React.useRef();
const scrollBodyRef = React.useRef();
const scrollBodyContainerRef = React.useRef();
const scrollSummaryRef = React.useRef();
const [pingedLeft, setPingedLeft] = React.useState(false);
const [pingedRight, setPingedRight] = React.useState(false);
const [colsWidths, updateColsWidths] = (0, _useFrame.useLayoutState)(new Map());
// Convert map to number width
const colsKeys = (0, _valueUtil.getColumnsKey)(flattenColumns);
const pureColWidths = colsKeys.map(columnKey => colsWidths.get(columnKey));
const colWidths = React.useMemo(() => pureColWidths, [pureColWidths.join('_')]);
const stickyOffsets = (0, _useStickyOffsets.default)(colWidths, flattenColumns.length, direction);
const fixHeader = scroll && (0, _valueUtil.validateValue)(scroll.y);
const horizonScroll = scroll && (0, _valueUtil.validateValue)(scroll.x) || Boolean(expandableConfig.fixed);
const fixColumn = horizonScroll && flattenColumns.some(({
fixed
}) => fixed);
// Sticky
const stickyRef = React.useRef();
const {
isSticky,
offsetHeader,
offsetSummary,
offsetScroll,
stickyClassName,
container
} = (0, _useSticky.default)(sticky, prefixCls);
// Footer (Fix footer must fixed header)
const summaryNode = summary?.(mergedData);
const fixFooter = (fixHeader || isSticky) && /*#__PURE__*/React.isValidElement(summaryNode) && summaryNode.type === _Summary.default && summaryNode.props.fixed;
// Scroll
let scrollXStyle;
let scrollYStyle;
let scrollTableStyle;
if (fixHeader) {
scrollYStyle = {
overflowY: 'scroll',
maxHeight: scroll.y
};
}
if (horizonScroll) {
scrollXStyle = {
overflowX: 'auto'
};
// When no vertical scrollbar, should hide it
// https://github.com/ant-design/ant-design/pull/20705
// https://github.com/ant-design/ant-design/issues/21879
if (!fixHeader) {
scrollYStyle = {
overflowY: 'hidden'
};
}
scrollTableStyle = {
width: scroll?.x === true ? 'auto' : scroll?.x,
minWidth: '100%'
};
}
const onColumnResize = React.useCallback((columnKey, width) => {
if ((0, _isVisible.default)(fullTableRef.current)) {
updateColsWidths(widths => {
if (widths.get(columnKey) !== width) {
const newWidths = new Map(widths);
newWidths.set(columnKey, width);
return newWidths;
}
return widths;
});
}
}, []);
const [setScrollTarget, getScrollTarget] = (0, _useFrame.useTimeoutLock)(null);
function forceScroll(scrollLeft, target) {
if (!target) {
return;
}
if (typeof target === 'function') {
target(scrollLeft);
} else if (target.scrollLeft !== scrollLeft) {
target.scrollLeft = scrollLeft;
// Delay to force scroll position if not sync
// ref: https://github.com/ant-design/ant-design/issues/37179
if (target.scrollLeft !== scrollLeft) {
setTimeout(() => {
target.scrollLeft = scrollLeft;
}, 0);
}
}
}
const onScroll = ({
currentTarget,
scrollLeft
}) => {
const isRTL = direction === 'rtl';
const mergedScrollLeft = typeof scrollLeft === 'number' ? scrollLeft : currentTarget.scrollLeft;
const compareTarget = currentTarget || EMPTY_SCROLL_TARGET;
if (!getScrollTarget() || getScrollTarget() === compareTarget) {
setScrollTarget(compareTarget);
forceScroll(mergedScrollLeft, scrollHeaderRef.current);
forceScroll(mergedScrollLeft, scrollBodyRef.current);
forceScroll(mergedScrollLeft, scrollSummaryRef.current);
forceScroll(mergedScrollLeft, stickyRef.current?.setScrollLeft);
}
if (currentTarget) {
const {
scrollWidth,
clientWidth
} = currentTarget;
// There is no space to scroll
if (scrollWidth === clientWidth) {
setPingedLeft(false);
setPingedRight(false);
return;
}
if (isRTL) {
setPingedLeft(-mergedScrollLeft < scrollWidth - clientWidth);
setPingedRight(-mergedScrollLeft > 0);
} else {
setPingedLeft(mergedScrollLeft > 0);
setPingedRight(mergedScrollLeft < scrollWidth - clientWidth);
}
}
};
const triggerOnScroll = () => {
if (horizonScroll && scrollBodyRef.current) {
onScroll({
currentTarget: scrollBodyRef.current
});
} else {
setPingedLeft(false);
setPingedRight(false);
}
};
const onFullTableResize = ({
width
}) => {
if (width !== componentWidth) {
triggerOnScroll();
setComponentWidth(fullTableRef.current ? fullTableRef.current.offsetWidth : width);
}
};
// Sync scroll bar when init or `horizonScroll`, `data` and `columns.length` changed
const mounted = React.useRef(false);
React.useEffect(() => {
// onFullTableResize will be trigger once when ResizeObserver is mounted
// This will reduce one duplicated triggerOnScroll time
if (mounted.current) {
triggerOnScroll();
}
}, [horizonScroll, data, columns.length]);
React.useEffect(() => {
mounted.current = true;
}, []);
// ===================== Effects ======================
const [scrollbarSize, setScrollbarSize] = React.useState(0);
const [supportSticky, setSupportSticky] = React.useState(true); // Only IE not support, we mark as support first
React.useEffect(() => {
if (scrollBodyRef.current instanceof Element) {
setScrollbarSize((0, _getScrollBarSize.getTargetScrollBarSize)(scrollBodyRef.current).width);
} else {
setScrollbarSize((0, _getScrollBarSize.getTargetScrollBarSize)(scrollBodyContainerRef.current).width);
}
setSupportSticky((0, _styleChecker.isStyleSupport)('position', 'sticky'));
}, []);
// ================== INTERNAL HOOKS ==================
React.useEffect(() => {
if (internalHooks === INTERNAL_HOOKS && internalRefs) {
internalRefs.body.current = scrollBodyRef.current;
}
});
// ====================== Render ======================
const TableComponent = getComponent(['table'], 'table');
// Table layout
const mergedTableLayout = React.useMemo(() => {
if (tableLayout) {
return tableLayout;
}
// https://github.com/ant-design/ant-design/issues/25227
// When scroll.x is max-content, no need to fix table layout
// it's width should stretch out to fit content
if (fixColumn) {
return scroll?.x === 'max-content' ? 'auto' : 'fixed';
}
if (fixHeader || isSticky || flattenColumns.some(({
ellipsis
}) => ellipsis)) {
return 'fixed';
}
return 'auto';
}, [fixHeader, fixColumn, flattenColumns, tableLayout, isSticky]);
let groupTableNode;
// Header props
const headerProps = {
colWidths,
columCount: flattenColumns.length,
stickyOffsets,
onHeaderRow,
fixHeader,
scroll
};
// Empty
const emptyNode = React.useMemo(() => {
if (hasData) {
return null;
}
if (typeof emptyText === 'function') {
return emptyText();
}
return emptyText;
}, [hasData, emptyText]);
// Body
const bodyTable = /*#__PURE__*/React.createElement(_Body.default, {
data: mergedData,
measureColumnWidth: fixHeader || horizonScroll || isSticky,
expandedKeys: mergedExpandedKeys,
rowExpandable: rowExpandable,
getRowKey: getRowKey,
onRow: onRow,
emptyNode: emptyNode,
childrenColumnName: mergedChildrenColumnName
});
const bodyColGroup = /*#__PURE__*/React.createElement(_ColGroup.default, {
colWidths: flattenColumns.map(({
width
}) => width),
columns: flattenColumns
});
const captionElement = caption !== null && caption !== undefined ? /*#__PURE__*/React.createElement("caption", {
className: `${prefixCls}-caption`
}, caption) : undefined;
const customizeScrollBody = getComponent(['body']);
if (process.env.NODE_ENV !== 'production' && typeof customizeScrollBody === 'function' && hasData && !fixHeader) {
(0, _warning.default)(false, '`components.body` with render props is only work on `scroll.y`.');
}
if (fixHeader || isSticky) {
// >>>>>> Fixed Header
let bodyContent;
if (typeof customizeScrollBody === 'function') {
bodyContent = customizeScrollBody(mergedData, {
scrollbarSize,
ref: scrollBodyRef,
onScroll
});
headerProps.colWidths = flattenColumns.map(({
width
}, index) => {
const colWidth = index === columns.length - 1 ? width - scrollbarSize : width;
if (typeof colWidth === 'number' && !Number.isNaN(colWidth)) {
return colWidth;
}
(0, _warning.default)(false, 'When use `components.body` with render props. Each column should have a fixed `width` value.');
return 0;
});
} else {
bodyContent = /*#__PURE__*/React.createElement("div", {
style: {
...scrollXStyle,
...scrollYStyle
},
onScroll: onScroll,
ref: scrollBodyRef,
className: (0, _classnames.default)(`${prefixCls}-body`)
}, /*#__PURE__*/React.createElement(TableComponent, {
style: {
...scrollTableStyle,
tableLayout: mergedTableLayout
},
className: contentClassName
}, captionElement, bodyColGroup, bodyTable, !fixFooter && summaryNode && /*#__PURE__*/React.createElement(_Footer.default, {
stickyOffsets: stickyOffsets,
flattenColumns: flattenColumns
}, summaryNode)));
}
// Fixed holder share the props
const fixedHolderProps = {
noData: !mergedData.length,
maxContentScroll: horizonScroll && scroll.x === 'max-content',
...headerProps,
...columnContext,
direction,
stickyClassName,
onScroll
};
groupTableNode = /*#__PURE__*/React.createElement(React.Fragment, null, showHeader !== false && /*#__PURE__*/React.createElement(_FixedHolder.default, (0, _extends2.default)({}, fixedHolderProps, {
stickyTopOffset: offsetHeader,
className: `${prefixCls}-header`,
ref: scrollHeaderRef
}), fixedHolderPassProps => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(_Header.default, fixedHolderPassProps), fixFooter === 'top' && /*#__PURE__*/React.createElement(_Footer.default, fixedHolderPassProps, summaryNode))), bodyContent, fixFooter && fixFooter !== 'top' && /*#__PURE__*/React.createElement(_FixedHolder.default, (0, _extends2.default)({}, fixedHolderProps, {
stickyBottomOffset: offsetSummary,
className: `${prefixCls}-summary`,
ref: scrollSummaryRef
}), fixedHolderPassProps => /*#__PURE__*/React.createElement(_Footer.default, fixedHolderPassProps, summaryNode)), isSticky && /*#__PURE__*/React.createElement(_stickyScrollBar.default, {
ref: stickyRef,
offsetScroll: offsetScroll,
scrollBodyRef: scrollBodyRef,
onScroll: onScroll,
container: container
}));
} else {
// >>>>>> Unique table
groupTableNode = /*#__PURE__*/React.createElement("div", {
style: {
...scrollXStyle,
...scrollYStyle
},
className: (0, _classnames.default)(`${prefixCls}-content`),
onScroll: onScroll,
ref: scrollBodyRef
}, /*#__PURE__*/React.createElement(TableComponent, {
style: {
...scrollTableStyle,
tableLayout: mergedTableLayout
}
}, captionElement, bodyColGroup, showHeader !== false && /*#__PURE__*/React.createElement(_Header.default, (0, _extends2.default)({}, headerProps, columnContext)), bodyTable, summaryNode && /*#__PURE__*/React.createElement(_Footer.default, {
stickyOffsets: stickyOffsets,
flattenColumns: flattenColumns
}, summaryNode)));
}
const ariaProps = (0, _pickAttrs.default)(props, {
aria: true,
data: true
});
let fullTable = /*#__PURE__*/React.createElement("div", (0, _extends2.default)({
className: (0, _classnames.default)(prefixCls, className, {
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-ping-left`]: pingedLeft,
[`${prefixCls}-ping-right`]: pingedRight,
[`${prefixCls}-layout-fixed`]: tableLayout === 'fixed',
[`${prefixCls}-fixed-header`]: fixHeader,
/** No used but for compatible */
[`${prefixCls}-fixed-column`]: fixColumn,
[`${prefixCls}-scroll-horizontal`]: horizonScroll,
[`${prefixCls}-has-fix-left`]: flattenColumns[0] && flattenColumns[0].fixed,
[`${prefixCls}-has-fix-right`]: flattenColumns[flattenColumns.length - 1] && flattenColumns[flattenColumns.length - 1].fixed === 'right'
}),
style: style,
id: id,
ref: fullTableRef
}, ariaProps), /*#__PURE__*/React.createElement(MemoTableContent, {
pingLeft: pingedLeft,
pingRight: pingedRight,
props: {
...props,
stickyOffsets,
mergedExpandedKeys
}
}, title && /*#__PURE__*/React.createElement(_Panel.default, {
className: `${prefixCls}-title`
}, title(mergedData)), /*#__PURE__*/React.createElement("div", {
ref: scrollBodyContainerRef,
className: `${prefixCls}-container`
}, groupTableNode), footer && /*#__PURE__*/React.createElement(_Panel.default, {
className: `${prefixCls}-footer`
}, footer(mergedData))));
if (horizonScroll) {
fullTable = /*#__PURE__*/React.createElement(_rcResizeObserver.default, {
onResize: onFullTableResize
}, fullTable);
}
const TableContextValue = React.useMemo(() => ({
prefixCls,
getComponent,
scrollbarSize,
direction,
fixedInfoList: flattenColumns.map((_, colIndex) => (0, _fixUtil.getCellFixedInfo)(colIndex, colIndex, flattenColumns, stickyOffsets, direction)),
isSticky
}), [prefixCls, getComponent, scrollbarSize, direction, flattenColumns, stickyOffsets, isSticky]);
const BodyContextValue = React.useMemo(() => ({
...columnContext,
tableLayout: mergedTableLayout,
rowClassName,
expandedRowClassName,
expandIcon: mergedExpandIcon,
expandableType,
expandRowByClick,
expandedRowRender,
onTriggerExpand,
expandIconColumnIndex,
indentSize,
allColumnsFixedLeft: columnContext.flattenColumns.every(col => col.fixed === 'left')
}), [columnContext, mergedTableLayout, rowClassName, expandedRowClassName, mergedExpandIcon, expandableType, expandRowByClick, expandedRowRender, onTriggerExpand, expandIconColumnIndex, indentSize]);
const ExpandedRowContextValue = React.useMemo(() => ({
componentWidth,
fixHeader,
fixColumn,
horizonScroll
}), [componentWidth, fixHeader, fixColumn, horizonScroll]);
const ResizeContextValue = React.useMemo(() => ({
onColumnResize
}), [onColumnResize]);
return /*#__PURE__*/React.createElement(_StickyContext.default.Provider, {
value: supportSticky
}, /*#__PURE__*/React.createElement(_TableContext.default.Provider, {
value: TableContextValue
}, /*#__PURE__*/React.createElement(_BodyContext.default.Provider, {
value: BodyContextValue
}, /*#__PURE__*/React.createElement(_ExpandedRowContext.default.Provider, {
value: ExpandedRowContextValue
}, /*#__PURE__*/React.createElement(_ResizeContext.default.Provider, {
value: ResizeContextValue
}, fullTable)))));
}
Table.EXPAND_COLUMN = _constant.EXPAND_COLUMN;
Table.Column = _Column.default;
Table.ColumnGroup = _ColumnGroup.default;
Table.Summary = _Footer.FooterComponents;
Table.defaultProps = {
rowKey: 'key',
prefixCls: 'rc-table',
emptyText: () => 'No Data'
};
var _default = Table;
exports.default = _default;