UNPKG

@are-visual/virtual-table

Version:
231 lines (227 loc) 7.58 kB
import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { useTableRowManager, useContainerSize, createMiddleware, useShallowMemo, useStableFn, getRowKey, isValidFixed } from '@are-visual/virtual-table'; import { useControllableValue } from '@are-visual/virtual-table/middleware/utils/useControllableValue'; import clsx from 'clsx'; import { useMemo, useRef, useCallback } from 'react'; const ExpandRowHeightKey = 'ExpandRow'; const ExpandRow = props => { const { className, style, rowKey, isExpanded, colSpan, children, fixed } = props; const { setRowHeightByRowKey } = useTableRowManager(); const { tableWidth } = useContainerSize(); return jsx("tr", { className: clsx('virtual-table-expanded-row', className), style: { ...style, display: isExpanded ? undefined : 'none' }, ref: node => { if (node == null) return; setRowHeightByRowKey(rowKey, ExpandRowHeightKey, node.offsetHeight); }, children: jsx("td", { colSpan: colSpan, children: jsx("div", { className: clsx('virtual-table-cell', fixed && 'virtual-table-expanded-row-fixed'), style: { width: tableWidth <= 0 ? undefined : tableWidth }, children: children }) }) }); }; const EXPANSION_COLUMN_KEY = 'VirtualTable.EXPANSION_COLUMN'; function isExpansionColumn(column) { return column.key === EXPANSION_COLUMN_KEY; } function useTableExpandable(ctx, options) { const disablePlugin = options == null; const { rowKey, columns: rawColumns, dataSource } = ctx; const { // expandedRowKeys, // onExpandedRowsChange, defaultExpandedRowKeys, expandedRowRender, columnTitle, expandRowByClick = false, expandIcon, onExpand, defaultExpandAllRows = false, // TODO: 未实现 // indentSize: _indentSize, showExpandColumn = true, expandedRowClassName, rowExpandable, columnWidth = 50, fixed, extraColumnProps } = options ?? {}; const _rowExpandableValue = useMemo(() => { return dataSource.map(row => { if (!rowExpandable) return false; return rowExpandable(row); }); }, [dataSource, rowExpandable]); // useShallowMemo 每一次渲染时候都会重新求值,这对于某些开销较大的计算不太友好, // 所以使用 useMemo 求值再通过 useShallowMemo 浅比较 // useMemo 标记 deps 后,若 deps 不变也就不需要重新求值 // 使用 useShallowMemo 主要是为了防止重新求值后结果不变但地址指针变化,导致不必要的渲染 const rowExpandableRecord = useShallowMemo(() => _rowExpandableValue); // 即使 defaultExpandAllRows 变更之后,此插件也不要响应变化,只使用初始值,所以存储一下 const defaultExpandAll = useRef( // options 中有 expandedRowKeys 则表示受控模式,那么 defaultExpandAllRows 不生效 'expandedRowKeys' in (options ?? {}) || 'defaultExpandedRowKeys' in (options ?? {}) ? false : defaultExpandAllRows); const defaultExpandKey = useShallowMemo(() => { if (defaultExpandAll.current) { const expandKeys = dataSource.map((record, index) => { if (rowExpandableRecord[index]) { const key = record[rowKey]; return key; } return null; }); return expandKeys.filter(x => x != null); } return []; }); const expansionKeys = useRef(new Set(defaultExpandedRowKeys ?? defaultExpandKey)); const [expansion, setExpansion] = useControllableValue(options ?? {}, { trigger: 'onExpandedRowsChange', valuePropName: 'expandedRowKeys', defaultValue: defaultExpandedRowKeys ?? defaultExpandKey }); const onUpdateExpansion = useStableFn((rowData, shouldExpand) => { const key = getRowKey(rowData, rowKey); expansionKeys.current.add(key); if (shouldExpand == null) { if (expansion.includes(key)) { setExpansion(expansion.filter(x => x !== key)); onExpand?.(false, rowData); } else { setExpansion([...expansion, key]); onExpand?.(true, rowData); } } else if (shouldExpand) { setExpansion([...expansion, key]); onExpand?.(true, rowData); } else { setExpansion(expansion.filter(x => x !== key)); onExpand?.(false, rowData); } }); const isFixed = isValidFixed(fixed); const renderRow = useCallback((children, args) => { const { rowData, rowIndex, rowKey: key, columnDescriptor } = args; const isExpandable = rowExpandableRecord[rowIndex]; if (isExpandable) { const isExpanded = expansion.includes(key); const isRendered = expansionKeys.current.has(key); let className = ''; if (typeof expandedRowClassName === 'string') { className = expandedRowClassName; } else if (typeof expandedRowClassName === 'function') { className = expandedRowClassName(rowData, rowIndex, 0); } return jsxs(Fragment, { children: [children, isRendered && jsx(ExpandRow, { className: className, rowKey: key, isExpanded: isExpanded, colSpan: columnDescriptor.length, fixed: isFixed, children: expandedRowRender?.(rowData, rowIndex, 0, isExpanded) })] }); } return children; }, [isFixed, rowExpandableRecord, expansion, expandedRowClassName, expandedRowRender]); const columns = useMemo(() => { if (!showExpandColumn) { return rawColumns; } return [{ ...extraColumnProps, key: EXPANSION_COLUMN_KEY, title: columnTitle, width: columnWidth, fixed, render(_value, record, index) { const key = getRowKey(record, rowKey); const expandable = rowExpandableRecord[index] ?? false; const expanded = expansion.includes(key); if (typeof expandIcon === 'function') { return expandIcon({ expandable, expanded, record, prefixCls: 'virtual-table', onExpand: (rowData, _e) => { onUpdateExpansion(rowData); } }); } if (!expandable) { return null; } return jsx("button", { className: clsx('virtual-table-row-expand-icon', expanded ? 'virtual-table-row-expand-icon-expanded' : 'virtual-table-row-expand-icon-collapsed'), type: "button", "aria-label": expanded ? '关闭行' : '展开行', "aria-expanded": expanded, onClick: () => { onUpdateExpansion(record, !expanded); } }); }, onHeaderCell() { return { className: 'virtual-table-expand-column' }; } }, ...rawColumns]; }, [showExpandColumn, columnTitle, columnWidth, fixed, rawColumns, rowKey, rowExpandableRecord, expansion, expandIcon, onUpdateExpansion, extraColumnProps]); const onRow = useCallback((record, index) => { if (rowExpandableRecord[index]) { return { onClick: e => { e.stopPropagation(); onUpdateExpansion(record); } }; } return {}; }, [rowExpandableRecord, onUpdateExpansion]); if (disablePlugin) { return ctx; } return { ...ctx, columns, renderRow, onRow: !expandRowByClick ? undefined : onRow }; } const tableExpandable = createMiddleware(useTableExpandable); export { EXPANSION_COLUMN_KEY, ExpandRowHeightKey, isExpansionColumn, tableExpandable }; //# sourceMappingURL=index.js.map