@are-visual/virtual-table
Version:
### VirtualTable
231 lines (227 loc) • 7.58 kB
JavaScript
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