UNPKG

linkmore-design

Version:

🌈 πŸš€lmη»„δ»ΆεΊ“γ€‚πŸš€

666 lines (641 loc) β€’ 24.6 kB
"use strict"; 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;