ali-react-table
Version:
705 lines (688 loc) • 29.9 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var pipeline = require('./chunks/ali-react-table-pipeline-d381df96.js');
var React = require('react');
var rxjs = require('rxjs');
var styled = require('styled-components');
require('classnames');
require('rxjs/operators');
require('resize-observer-polyfill');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
var styled__default = /*#__PURE__*/_interopDefaultLegacy(styled);
function simpleEncode(path) {
if (path.length === 0) {
return 'key:@total@';
}
return `key:${path.join(' ')}`;
}
/** 根据指定的 code 序列计算下钻树 */
function buildDrillTree(data, codes, { encode = simpleEncode, totalValue = '总计', includeTopWrapper = false, isExpand = pipeline.always(true), enforceExpandTotalNode = true, } = {}) {
const emptyPath = [];
const totalKey = encode(emptyPath);
let array;
let hasChild = false;
if (codes.length === 0) {
array = [];
}
else if (!enforceExpandTotalNode && !isExpand(totalKey)) {
array = [];
hasChild = data.length > 0;
}
else {
array = dfs(data, []);
}
if (includeTopWrapper) {
const rootNode = {
key: totalKey,
value: totalValue,
path: emptyPath,
children: array,
};
if (hasChild) {
rootNode.hasChild = hasChild;
}
return [rootNode];
}
if (includeTopWrapper) {
return [
{
key: totalKey,
value: totalValue,
path: emptyPath,
children: array,
},
];
}
return array;
function dfs(slice, path) {
const depth = path.length;
const array = [];
const code = codes[depth];
const groups = pipeline.groupBy2(slice, (row) => row[code]);
for (const groupKey of groups.keys()) {
path.push(groupKey);
const node = {
key: encode(path),
value: groupKey,
path: path.slice(),
};
array.push(node);
const group = groups.get(groupKey);
if (group.length > 0 && depth < codes.length - 1) {
if (isExpand(node.key)) {
node.children = dfs(group, path);
}
else {
node.hasChild = true;
}
}
path.pop();
}
return array;
}
}
function fallbackAggregate(slice) {
return slice.length === 1 ? slice[0] : {};
}
/** 根据表格左侧与上方的下钻树,从全量明细数据中计算对应的数据立方 */
function buildRecordMatrix({ data, leftCodes, topCodes, aggregate = fallbackAggregate, encode = simpleEncode, isLeftExpand = pipeline.always(true), isTopExpand = pipeline.always(true), prebuiltLeftTree, prebuiltTopTree, }) {
const ctx = {
peculiarity: new Set(),
};
const [leftRootDrillNode] = prebuiltLeftTree !== null && prebuiltLeftTree !== void 0 ? prebuiltLeftTree : buildDrillTree(data, leftCodes, {
encode,
includeTopWrapper: true,
isExpand: isLeftExpand,
});
const [topRootDrillNode] = prebuiltTopTree !== null && prebuiltTopTree !== void 0 ? prebuiltTopTree : buildDrillTree(data, topCodes, {
encode,
includeTopWrapper: true,
isExpand: isTopExpand,
});
const transientMatrixRow = buildByLeft(ctx, data, leftRootDrillNode, 0);
return makeMatrix(transientMatrixRow);
function buildByLeft(ctx, slice, drillNode, depth) {
let children = null;
let col;
if (pipeline.isLeafNode(drillNode)) {
col = buildByTop(ctx, slice, topRootDrillNode, 0);
}
else {
children = [];
const code = leftCodes[depth];
const groups = pipeline.groupBy2(slice, (dwdRow) => dwdRow[code]);
ctx.peculiarity.add(code);
for (const child of drillNode.children) {
const group = groups.get(child.value);
if (group) {
children.push(buildByLeft(ctx, group, child, depth + 1));
}
}
ctx.peculiarity.delete(code);
col = mergeColsByTopTree(ctx, children.map((child) => child.col));
}
return {
leftKey: drillNode.key,
children,
col,
};
}
function buildByTop(ctx, slice, drillNode, depth) {
let children = null;
let record;
if (pipeline.isLeafNode(drillNode)) {
record = aggregate(slice, ctx);
}
else {
children = [];
const code = topCodes[depth];
const groups = pipeline.groupBy2(slice, (dwdRow) => dwdRow[code]);
ctx.peculiarity.add(code);
for (const child of drillNode.children) {
const group = groups.get(child.value);
if (group) {
children.push(buildByTop(ctx, group, child, depth + 1));
}
}
ctx.peculiarity.delete(code);
record = aggregate(children.map((child) => child.record), ctx);
}
return {
topKey: drillNode.key,
topValue: drillNode.value,
children,
record,
};
}
function mergeColsByTopTree(ctx, colsToMerge) {
return dfs(ctx, colsToMerge, topRootDrillNode, 0);
function dfs(ctx, cols, topDrillNode, depth) {
let children = null;
const record = aggregate(cols.map((c) => c.record), ctx);
if (!pipeline.isLeafNode(topDrillNode)) {
const topCode = topCodes[depth];
ctx.peculiarity.add(topCode);
const drillChildDict = pipeline.fromEntries(topDrillNode.children.map((child) => [child.value, child]));
const colChildDictArray = cols.map((col) => pipeline.fromEntries(col.children.map((child) => [child.topValue, child])));
children = topDrillNode.children.map((item) => {
const childCols = colChildDictArray.map((colChildDict) => colChildDict[item.value]).filter(Boolean);
return dfs(ctx, childCols, drillChildDict[item.value], depth + 1);
});
ctx.peculiarity.delete(topCode);
}
return {
topKey: topDrillNode.key,
topValue: topDrillNode.value,
record,
children,
};
}
}
function makeMatrix(rootRow) {
const result = new Map();
dfsRow(result, rootRow);
return result;
function dfsRow(matrix, row) {
const subMap = new Map();
matrix.set(row.leftKey, subMap);
dfsCol(subMap, row.col);
if (!pipeline.isLeafNode(row)) {
row.children.forEach((childRow) => {
dfsRow(matrix, childRow);
});
}
}
function dfsCol(subMap, col) {
subMap.set(col.topKey, col.record);
if (!pipeline.isLeafNode(col)) {
col.children.forEach((childCol) => {
dfsCol(subMap, childCol);
});
}
}
}
}
/** buildRecordMatrix 的简化版本,只能处理一个维度序列,返回一个 Map。
* 相当于只处理 matrix 的第一行(汇总行) */
function buildRecordMap({ codes, encode = simpleEncode, data, aggregate, isExpand, }) {
const matrix = buildRecordMatrix({
data,
leftCodes: [],
topCodes: codes,
isTopExpand: isExpand,
aggregate,
encode,
});
const totalKey = encode([]);
return matrix.get(totalKey);
}
const ExpandSpan = styled__default['default'].span `
display: inline-flex;
align-items: center;
padding: 2px 8px 2px 0;
cursor: pointer;
.icon {
fill: #999;
margin-right: 4px;
&.expanded {
transform-origin: center center;
transform: rotate(90deg);
}
}
`;
function convertDrillTreeToCrossTree(drillTree, { indicators, encode = simpleEncode, generateSubtotalNode, enforceExpandTotalNode = true, expandKeys, onChangeExpandKeys = rxjs.noop, supportsExpand, } = {}) {
const totalKey = encode([]);
if (supportsExpand && expandKeys == null) {
throw new Error('[ali-react-table] convertDrillTreeToCrossTree(...) 设置 supportsExpand=true 时,expandKeys 不能为 null/undefined.');
}
const expandKeySet = new Set(expandKeys);
return dfs(drillTree);
/** 在 indicators 非空的情况下获取指标对应的 CrossTreeNode */
function getIndicators(node, nodeData) {
return indicators.map((indicator) => (Object.assign({ key: encode(node.path.concat([indicator.code])), value: indicator.name, data: Object.assign(Object.assign({}, nodeData), { indicator }) }, indicator)));
}
function drillNodeToTreeNode(node, nodeData) {
if (indicators != null) {
return {
key: node.key,
value: node.value,
data: nodeData,
children: getIndicators(node, nodeData),
};
}
else {
return {
key: node.key,
value: node.value,
data: nodeData,
};
}
}
function dfs(drillNodes, depth) {
const result = [];
for (const node of drillNodes) {
const nodeData = { dataKey: node.key, dataPath: node.path };
if (pipeline.isLeafNode(node) && !node.hasChild) {
result.push(drillNodeToTreeNode(node, nodeData));
}
else {
let needProcessChildren = true;
let crossTreeNode = {
key: node.key,
value: node.value,
data: nodeData,
};
if (!supportsExpand || (enforceExpandTotalNode && node.key === totalKey)) {
// 不支持展开功能 或是强制展开
crossTreeNode.children = dfs(node.children);
}
else if (expandKeySet.has(node.key)) {
// 展开的父节点
// @ts-ignore
crossTreeNode.title = (React__default['default'].createElement(ExpandSpan, { onClick: () => {
onChangeExpandKeys(expandKeys.filter((k) => k !== node.key), node, 'collapse');
} },
React__default['default'].createElement(pipeline.icons.CaretRight, { className: "icon expanded" }),
node.value));
crossTreeNode.children = dfs(node.children);
}
else {
// 收拢的父节点
needProcessChildren = false;
crossTreeNode.title = (React__default['default'].createElement(ExpandSpan, { onClick: () => {
onChangeExpandKeys(expandKeys.concat([node.key]), node, 'expand');
} },
React__default['default'].createElement(pipeline.icons.CaretRight, { className: "icon collapsed" }),
node.value));
if (indicators != null) {
crossTreeNode.children = getIndicators(node, nodeData);
}
}
if (needProcessChildren) {
const subtotalNodeData = generateSubtotalNode === null || generateSubtotalNode === void 0 ? void 0 : generateSubtotalNode(node);
if (subtotalNodeData) {
const { position = 'start', value } = subtotalNodeData;
const subtotalPath = node.path.concat([value]);
const subtotalDrillNode = {
key: encode(subtotalPath),
path: subtotalPath,
value,
};
const subtotalTreeNode = drillNodeToTreeNode(subtotalDrillNode, nodeData);
if (position === 'start') {
crossTreeNode.children.unshift(subtotalTreeNode);
}
else {
crossTreeNode.children.push(subtotalTreeNode);
}
}
}
result.push(crossTreeNode);
}
}
return result;
}
}
const ROW_KEY = 'rowKey';
function buildCrossTable(options) {
var _a, _b, _c, _d, _e, _f;
const { leftTotalNode, topTotalNode } = options;
const columnOffset = (_a = options.columnOffset) !== null && _a !== void 0 ? _a : 0;
const rowOffset = (_b = options.rowOffset) !== null && _b !== void 0 ? _b : 0;
const hasOffset = columnOffset !== 0 || rowOffset !== 0;
// 有的时候 leftTree/topTree 是通过 node.children 传入的
// 此时 leftTree/topTree 等于 null 和等于空数组是等价的
// 故在这里兼容 leftTree/topTree 为空的情况
const leftTree = (_c = options.leftTree) !== null && _c !== void 0 ? _c : [];
const topTree = (_d = options.topTree) !== null && _d !== void 0 ? _d : [];
const getValue = (_e = options.getValue) !== null && _e !== void 0 ? _e : pipeline.always(null);
const leftMetaColumns = (_f = options.leftMetaColumns) !== null && _f !== void 0 ? _f : [];
const leftHeaderWidth = Math.max(leftMetaColumns.length, pipeline.getTreeDepth(leftTree) + 1);
return {
columns: getColumns(),
dataSource: getDataSource(),
};
/** 获取表格的列配置 */
function getColumns() {
return [...getLeftPartColumns(), ...getDataPartColumns()];
function getLeftPartColumns() {
var _a;
const leftPartColumns = [];
for (let index = 0; index < leftHeaderWidth; index++) {
const metaCol = (_a = leftMetaColumns[index]) !== null && _a !== void 0 ? _a : {};
const staticMetaColConfig = pipeline.__rest(metaCol, ["getCellProps", "render"]);
leftPartColumns.push(Object.assign(Object.assign({ columnType: 'left', lock: true }, staticMetaColConfig), { getCellProps: leftHeaderGetCellPropsFactory(metaCol, index), getSpanRect: leftHeaderGetSpanRectFactory(metaCol, index), getValue: leftHeaderGetValueFactory(metaCol, index), render: leftHeaderRenderFactory(metaCol, index) }));
}
return leftPartColumns;
function leftHeaderGetCellPropsFactory(metaCol, colIndex) {
return (_value, row, rowIndex) => {
var _a;
const node = row.nodes[colIndex];
return (_a = metaCol.getCellProps) === null || _a === void 0 ? void 0 : _a.call(metaCol, node, colIndex);
};
}
function leftHeaderGetSpanRectFactory(metaCol, colIndex) {
return (_value, row) => {
const rect = row.rects[colIndex];
if (hasOffset) {
return {
left: rect.left + columnOffset,
right: rect.right + columnOffset,
top: rect.top + rowOffset,
bottom: rect.bottom + rowOffset,
};
}
return rect;
};
}
function leftHeaderGetValueFactory(metaCol, colIndex) {
return (row, rowIndex) => {
const node = row.nodes[colIndex];
return node.value;
};
}
function leftHeaderRenderFactory(metaCol, colIndex) {
return (v, row, rowIndex) => {
var _a;
const node = row.nodes[colIndex];
if (metaCol.render) {
return metaCol.render(node, colIndex);
}
return (_a = node.title) !== null && _a !== void 0 ? _a : node.value;
};
}
}
/** 获取表格数据部分的列配置 */
function getDataPartColumns() {
if (topTree.length > 0) {
return dfs(topTree, { valuePath: [], depth: 0 });
}
else if (topTotalNode) {
return dfs([topTotalNode], { valuePath: [], depth: 0 });
}
else {
return [];
}
function dfs(nodes, ctx) {
const result = [];
for (const node of nodes) {
ctx.valuePath.push(node.value);
if (pipeline.isLeafNode(node)) {
// 叶子节点
result.push(getDataColumn(node, ctx.depth));
}
else {
const { key, value, children } = node, others = pipeline.__rest(node
// 强制展开的节点
, ["key", "value", "children"]);
// 强制展开的节点
result.push(Object.assign(Object.assign({ columnType: 'data-parent' }, others), { name: value, children: dfs(children, { valuePath: ctx.valuePath, depth: ctx.depth + 1 }) }));
}
ctx.valuePath.pop();
}
return result;
}
}
function getDataColumn(topNode, topDepth) {
const columnGetValue = (row) => {
const leftDepth = row.nodes.length - 1;
const leftNode = row.nodes[leftDepth];
return getValue(leftNode, topNode, leftDepth, topDepth);
};
const { key, value, children } = topNode, others = pipeline.__rest(topNode, ["key", "value", "children"]);
return Object.assign(Object.assign({ columnType: 'data' }, others), { getValue: columnGetValue, name: value, children: null, render(value, row) {
if (options.render) {
const leftDepth = row.nodes.length - 1;
const leftNode = row.nodes[leftDepth];
return options.render(value, leftNode, topNode, leftDepth, topDepth);
}
return value;
},
getCellProps(value, row) {
if (options.getCellProps) {
const leftDepth = row.nodes.length - 1;
const leftNode = row.nodes[leftDepth];
return options.getCellProps(value, leftNode, topNode, leftDepth, topDepth);
}
} });
}
}
function getDataSource() {
const flatRows = [];
const ctx = { depth: 0, nodes: [], rects: [], rowIndex: 0 };
if (leftTree.length > 0) {
dfs(leftTree, ctx);
}
else if (leftTotalNode) {
dfs([leftTotalNode], ctx);
} // else 表格没有行,展示空表格
return flatRows;
function dfs(nodes, ctx) {
let count = 0;
for (const node of nodes) {
if (node.hidden) {
// 跳过被隐藏的节点
continue;
}
const rect = {
top: ctx.rowIndex + count,
bottom: -1,
left: ctx.depth,
right: -1, // 会在 dfs 之后算出结果
};
const row = {
[ROW_KEY]: node.key,
rects: [...ctx.rects, rect],
nodes: [...ctx.nodes, node],
};
if (pipeline.isLeafNode(node)) {
rect.right = leftHeaderWidth;
rect.bottom = rect.top + 1;
flatRows.push(row);
count += 1;
}
else {
ctx.rects.push(rect);
ctx.nodes.push(node);
const ret = dfs(node.children, {
nodes: ctx.nodes,
rects: ctx.rects,
depth: ctx.depth + 1,
rowIndex: ctx.rowIndex + count,
});
ctx.rects.pop();
ctx.nodes.pop();
count += ret.count;
rect.right = rect.left + 1;
rect.bottom = rect.top + ret.count;
}
}
return { count };
}
}
}
function CrossTable(_a) {
var { BaseTableComponent = pipeline.BaseTable, leftTree, leftTotalNode, topTree, topTotalNode, getValue, getCellProps, leftMetaColumns, render, baseTableRef } = _a, others = pipeline.__rest(_a, ["BaseTableComponent", "leftTree", "leftTotalNode", "topTree", "topTotalNode", "getValue", "getCellProps", "leftMetaColumns", "render", "baseTableRef"]);
const { dataSource, columns } = buildCrossTable({
leftTree,
topTree,
leftTotalNode,
topTotalNode,
getValue,
getCellProps,
render,
leftMetaColumns,
});
return (React__default['default'].createElement(BaseTableComponent, Object.assign({ ref: baseTableRef }, others, { primaryKey: ROW_KEY, dataSource: dataSource, columns: columns })));
}
function buildCrossTreeTable(options) {
var _a, _b;
const { primaryColumn = { name: '' }, openKeys, onChangeOpenKeys, indentSize, isLeafNode: isLeafNodeOpt = pipeline.isLeafNode, } = options;
// 有的时候 leftTree/topTree 是通过 node.children 传入的
// 此时 leftTree/topTree 等于 null 和等于空数组是等价的
// 故在这里兼容 leftTree/topTree 为空的情况
const leftTree = (_a = options.leftTree) !== null && _a !== void 0 ? _a : [];
const topTree = (_b = options.topTree) !== null && _b !== void 0 ? _b : [];
const pipeline$1 = new pipeline.TablePipeline({
state: {},
setState: rxjs.noop,
ctx: { primaryKey: ROW_KEY },
});
pipeline$1.input({ dataSource: getDataSource(), columns: getColumns() });
pipeline$1.use(pipeline.treeMode({
openKeys,
onChangeOpenKeys,
indentSize,
isLeafNode(row, nodeMeta) {
// 调用上层 isLeafNodeOpt 时,会从 row.node 中读取该表格行对应的 leftTreeNode
return isLeafNodeOpt(row.node, nodeMeta);
},
}));
return {
dataSource: pipeline$1.getDataSource(),
columns: pipeline$1.getColumns(),
};
/** 获取表格的列配置 */
function getColumns() {
return [
Object.assign(Object.assign({}, primaryColumn), { getValue(row) {
return row.node.value;
},
getCellProps(value, row) {
if (primaryColumn.getCellProps) {
return primaryColumn.getCellProps(row.node, row.nodes.length - 1);
}
},
render(value, row) {
if (primaryColumn.render) {
return primaryColumn.render(row.node, row.nodes.length - 1);
}
return value;
} }),
...getDataPartColumns(),
];
/** 获取表格数据部分的列配置 */
function getDataPartColumns() {
return dfs(topTree, { depth: 0 });
function dfs(nodes, ctx) {
const result = [];
for (const node of nodes) {
if (pipeline.isLeafNode(node)) {
result.push(getDataColumn(node, ctx.depth));
}
else {
const { key, value, children } = node, others = pipeline.__rest(node, ["key", "value", "children"]);
result.push(Object.assign(Object.assign({}, others), { name: value, children: dfs(children, { depth: ctx.depth + 1 }) }));
}
}
return result;
}
}
function getDataColumn(topNode, topDepth) {
const columnGetValue = (row) => {
const leftDepth = row.nodes.length - 1;
const leftNode = row.node;
if (options.getValue) {
return options.getValue(leftNode, topNode, leftDepth, topDepth);
}
return null;
};
const { key, value, children } = topNode, others = pipeline.__rest(topNode, ["key", "value", "children"]);
return Object.assign(Object.assign({}, others), { getValue: columnGetValue, name: value, children: null, render(value, row) {
if (options.render) {
const leftDepth = row.nodes.length - 1;
const leftNode = row.node;
return options.render(value, leftNode, topNode, leftDepth, topDepth);
}
return value;
},
getCellProps(value, row) {
if (options.getCellProps) {
const leftDepth = row.nodes.length - 1;
const leftNode = row.node;
return options.getCellProps(value, leftNode, topNode, leftDepth, topDepth);
}
} });
}
}
function getDataSource() {
return dfs(leftTree, { nodes: [] });
function dfs(nodes, ctx) {
const result = [];
for (const node of nodes) {
if (node.hidden) {
// 跳过被隐藏的节点
continue;
}
if (pipeline.isLeafNode(node)) {
result.push({
[ROW_KEY]: node.key,
node,
nodes: [...ctx.nodes, node],
});
}
else {
const nodes = [...ctx.nodes, node];
ctx.nodes.push(node);
const children = dfs(node.children, ctx);
result.push({ [ROW_KEY]: node.key, node, nodes, children });
ctx.nodes.pop();
}
}
return result;
}
}
}
class CrossTreeTable extends React__default['default'].Component {
constructor(props) {
super(props);
this.onChangeOpenKeys = (nextOpenKeys) => {
this.props.onChangeOpenKeys(nextOpenKeys);
if (!('openKeys' in this.props)) {
this.setState({ openKeys: nextOpenKeys });
}
};
this.state = {
openKeys: props.defaultOpenKeys,
};
}
static getDerivedStateFromProps(nextProps) {
if ('openKeys' in nextProps) {
return { openKeys: nextProps.openKeys };
}
return null;
}
render() {
const _a = this.props, { BaseTableComponent = pipeline.BaseTable, leftTree, topTree, getValue, getCellProps, primaryColumn, render, openKeys: openKeysProp, defaultOpenKeys, onChangeOpenKeys, indentSize, isLeafNode, baseTableRef } = _a, others = pipeline.__rest(_a, ["BaseTableComponent", "leftTree", "topTree", "getValue", "getCellProps", "primaryColumn", "render", "openKeys", "defaultOpenKeys", "onChangeOpenKeys", "indentSize", "isLeafNode", "baseTableRef"]) // 透传其他 BaseTable 的 props
;
const openKeys = openKeysProp !== null && openKeysProp !== void 0 ? openKeysProp : this.state.openKeys;
const { dataSource, columns } = buildCrossTreeTable({
leftTree,
topTree,
getValue,
getCellProps,
render,
primaryColumn,
openKeys,
onChangeOpenKeys: this.onChangeOpenKeys,
indentSize,
isLeafNode,
});
return (React__default['default'].createElement(BaseTableComponent, Object.assign({ ref: baseTableRef }, others, { primaryKey: ROW_KEY, dataSource: dataSource, columns: columns })));
}
}
CrossTreeTable.defaultProps = {
defaultOpenKeys: [],
onChangeOpenKeys: rxjs.noop,
};
exports.CrossTable = CrossTable;
exports.CrossTreeTable = CrossTreeTable;
exports.ROW_KEY = ROW_KEY;
exports.buildCrossTable = buildCrossTable;
exports.buildCrossTreeTable = buildCrossTreeTable;
exports.buildDrillTree = buildDrillTree;
exports.buildRecordMap = buildRecordMap;
exports.buildRecordMatrix = buildRecordMatrix;
exports.convertDrillTreeToCrossTree = convertDrillTreeToCrossTree;
exports.simpleEncode = simpleEncode;