@antv/s2
Version:
effective spreadsheet render core lib
755 lines • 39 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PivotFacet = void 0;
const lodash_1 = require("lodash");
const cell_1 = require("../cell");
const common_1 = require("../common");
const constant_1 = require("../common/constant");
const interaction_1 = require("../common/constant/interaction");
const debug_1 = require("../common/debug");
const theme_1 = require("../theme");
const utils_1 = require("../utils");
const data_cell_1 = require("../utils/cell/data-cell");
const header_cell_1 = require("../utils/cell/header-cell");
const condition_1 = require("../utils/condition/condition");
const pivot_data_set_1 = require("../utils/dataset/pivot-data-set");
const facet_1 = require("../utils/facet");
const get_all_child_cells_1 = require("../utils/get-all-child-cells");
const math_1 = require("../utils/math");
const text_1 = require("../utils/text");
const frozen_facet_1 = require("./frozen-facet");
const header_1 = require("./header");
const build_header_hierarchy_1 = require("./layout/build-header-hierarchy");
const layout_hooks_1 = require("./layout/layout-hooks");
const node_1 = require("./layout/node");
class PivotFacet extends frozen_facet_1.FrozenFacet {
get rowCellTheme() {
return this.spreadsheet.theme.rowCell.cell;
}
getCornerCellInstance(...args) {
var _a, _b;
return (((_b = (_a = this.spreadsheet.options).cornerCell) === null || _b === void 0 ? void 0 : _b.call(_a, ...args)) || new cell_1.CornerCell(...args));
}
getRowCellInstance(...args) {
var _a, _b;
return ((_b = (_a = this.spreadsheet.options).rowCell) === null || _b === void 0 ? void 0 : _b.call(_a, ...args)) || new cell_1.RowCell(...args);
}
getColCellInstance(...args) {
var _a, _b;
return ((_b = (_a = this.spreadsheet.options).colCell) === null || _b === void 0 ? void 0 : _b.call(_a, ...args)) || new cell_1.ColCell(...args);
}
doLayout() {
const { rowLeafNodes, colLeafNodes, rowsHierarchy, colsHierarchy } = this.buildAllHeaderHierarchy();
this.calculateHeaderNodesCoordinate({
rowLeafNodes,
rowsHierarchy,
colLeafNodes,
colsHierarchy,
});
return {
colNodes: colsHierarchy.getNodes(),
colsHierarchy,
rowNodes: rowsHierarchy.getNodes(),
rowsHierarchy,
rowLeafNodes,
colLeafNodes,
};
}
buildAllHeaderHierarchy() {
const { leafNodes: rowLeafNodes, hierarchy: rowsHierarchy } = (0, build_header_hierarchy_1.buildHeaderHierarchy)({
isRowHeader: true,
spreadsheet: this.spreadsheet,
});
const { leafNodes: colLeafNodes, hierarchy: colsHierarchy } = (0, build_header_hierarchy_1.buildHeaderHierarchy)({
isRowHeader: false,
spreadsheet: this.spreadsheet,
});
return {
rowLeafNodes,
colLeafNodes,
rowsHierarchy,
colsHierarchy,
};
}
getDataQueryInfo(rowQuery, colQuery) {
var _a, _b, _c, _d;
const { options, dataSet } = this.spreadsheet;
const hideMeasure = (_c = (_b = (_a = options.style) === null || _a === void 0 ? void 0 : _a.colCell) === null || _b === void 0 ? void 0 : _b.hideValue) !== null && _c !== void 0 ? _c : false;
/**
* 如果在非自定义目录情况下hide measure query中是没有度量信息的,所以需要自动补上
* 存在一个场景的冲突,如果是多个度量,定位数据数据是无法知道哪一列代表什么
* 因此默认只会去 第一个度量拼接query
*/
const measureInfo = hideMeasure
? {
[constant_1.EXTRA_FIELD]: (_d = dataSet.fields.values) === null || _d === void 0 ? void 0 : _d[0],
}
: {};
const dataQuery = (0, lodash_1.merge)({}, rowQuery, colQuery, measureInfo);
const valueField = dataQuery[constant_1.EXTRA_FIELD];
return {
dataQuery,
valueField,
};
}
/**
* 根据行列索引获取单元格元数据
*/
getCellMeta(rowIndex = 0, colIndex = 0) {
var _a, _b;
const { options, dataSet } = this.spreadsheet;
const { rowLeafNodes, colLeafNodes } = this.getLayoutResult();
const row = rowLeafNodes[rowIndex];
const col = colLeafNodes[colIndex];
if (!row || !col) {
return null;
}
const rowQuery = row.query;
const colQuery = col.query;
const isTotals = row.isTotals || row.isTotalMeasure || col.isTotals || col.isTotalMeasure;
const totalStatus = (0, pivot_data_set_1.getHeaderTotalStatus)(row, col);
const { dataQuery, valueField } = this.getDataQueryInfo(rowQuery, colQuery);
const data = dataSet.getCellData({
query: dataQuery,
rowNode: row,
isTotals,
totalStatus,
});
const fieldValue = (0, lodash_1.get)(data, constant_1.VALUE_FIELD, null);
const cellMeta = {
spreadsheet: this.spreadsheet,
x: col.x,
y: row.y,
width: col.width,
height: row.height,
data,
rowIndex,
colIndex,
isTotals,
valueField,
fieldValue,
rowQuery,
colQuery,
query: Object.assign(Object.assign({}, rowQuery), colQuery),
rowId: row.id,
colId: col.id,
id: (0, data_cell_1.getDataCellId)(row.id, col.id),
};
return (_b = (_a = options.layoutCellMeta) === null || _a === void 0 ? void 0 : _a.call(options, cellMeta)) !== null && _b !== void 0 ? _b : cellMeta;
}
getPreLevelSampleNode(colNode, colsHierarchy) {
// 之前是采样每一级第一个节点, 现在 sampleNodesForAllLevels 是采样每一级高度最大的节点
// 但是初始化布局时只有第一个节点有值, 所以这里需要适配下
return colsHierarchy
.getNodes(colNode.level - 1)
.find((node) => !node.isTotals);
}
calculateHeaderNodesCoordinate(layoutResult) {
this.calculateRowNodesCoordinate(layoutResult);
this.calculateColNodesCoordinate(layoutResult);
}
calculateColNodesCoordinate(layoutResult) {
const { colLeafNodes, colsHierarchy, rowsHierarchy } = layoutResult;
// 1. 计算叶子节点宽度
this.calculateColLeafNodesWidth(layoutResult);
// 2. 根据叶子节点宽度计算所有父级节点宽度和 x 坐标, 便于计算自动换行后节点的真实高度
this.calculateColParentNodeWidthAndX(colLeafNodes);
// 3. 计算每一层级的采样节点
this.updateColsHierarchySampleMaxHeightNodes(colsHierarchy, rowsHierarchy);
// 4. 计算所有节点的高度
this.calculateColNodesHeight(colsHierarchy);
// 5. 如果存在自定义多级列头, 还需要更新某一层级的采样
this.updateCustomFieldsSampleNodes(colsHierarchy);
// 6. 补齐自定义列头节点缺失的高度
this.adjustCustomColLeafNodesHeight({
leafNodes: colLeafNodes,
hierarchy: colsHierarchy,
});
// 7. 更新汇总节点坐标
this.adjustColTotalNodesCoordinate(colsHierarchy);
}
calculateRowOffsets() { }
adjustColTotalNodesCoordinate(colsHierarchy) {
var _a;
if (!(0, lodash_1.isEmpty)((_a = this.spreadsheet.options.totals) === null || _a === void 0 ? void 0 : _a.col)) {
this.adjustTotalNodesCoordinate({
hierarchy: colsHierarchy,
isRowHeader: false,
isSubTotal: true,
});
this.adjustTotalNodesCoordinate({
hierarchy: colsHierarchy,
isRowHeader: false,
isSubTotal: false,
});
}
}
calculateColLeafNodesWidth(layoutResult) {
const { rowLeafNodes, colLeafNodes, rowsHierarchy, colsHierarchy } = layoutResult;
let preLeafNode = node_1.Node.blankNode();
let currentColIndex = 0;
colsHierarchy.getLeaves().forEach((currentNode) => {
currentNode.colIndex = currentColIndex;
currentColIndex++;
currentNode.x = preLeafNode.x + preLeafNode.width;
currentNode.width = this.getColLeafNodesWidth(currentNode, colLeafNodes, rowLeafNodes, rowsHierarchy.width);
colsHierarchy.width += currentNode.width;
preLeafNode = currentNode;
});
}
calculateColNodesHeight(colsHierarchy) {
const colNodes = colsHierarchy.getNodes();
colNodes.forEach((currentNode) => {
if (currentNode.level === 0) {
currentNode.y = 0;
}
else {
const preLevelSample = this.getPreLevelSampleNode(currentNode, colsHierarchy);
currentNode.y = (preLevelSample === null || preLevelSample === void 0 ? void 0 : preLevelSample.y) + (preLevelSample === null || preLevelSample === void 0 ? void 0 : preLevelSample.height) || 0;
}
// 数值置于行头时, 列头的总计即叶子节点, 此时应该用列高: https://github.com/antvis/S2/issues/1715
const colNodeHeight = this.getColNodeHeight({
colNode: currentNode,
colsHierarchy,
useCache: false,
});
currentNode.height =
currentNode.isGrandTotals &&
!currentNode.isTotalMeasure &&
currentNode.isLeaf
? colsHierarchy.height
: colNodeHeight;
(0, layout_hooks_1.layoutCoordinate)(this.spreadsheet, null, currentNode);
});
}
// please read README-adjustTotalNodesCoordinate.md to understand this function
getMultipleMap(hierarchy, isRowHeader, isSubTotal) {
const { maxLevel } = hierarchy;
const dataSet = this.spreadsheet.dataSet;
const { totals } = this.spreadsheet.options;
const moreThanOneValue = dataSet.moreThanOneValue();
const { rows, columns } = dataSet.fields;
const fields = isRowHeader ? rows : columns;
const totalConfig = isRowHeader ? totals.row : totals.col;
const defaultDimensionGroup = isSubTotal
? (totalConfig === null || totalConfig === void 0 ? void 0 : totalConfig.subTotalsGroupDimensions) || []
: (totalConfig === null || totalConfig === void 0 ? void 0 : totalConfig.grandTotalsGroupDimensions) || [];
const dimensionGroup = !dataSet.isEmpty() ? defaultDimensionGroup : [];
const multipleMap = Array.from({ length: maxLevel + 1 }, () => 1);
for (let level = maxLevel; level > 0; level--) {
const currentField = fields[level];
// 若不符合【分组维度包含此维度】或者【指标维度下多指标维度】,此表头单元格为空,将宽高合并到上级单元格
const existValueField = currentField === constant_1.EXTRA_FIELD && moreThanOneValue;
if (!(dimensionGroup.includes(currentField) || existValueField)) {
multipleMap[level - 1] += multipleMap[level];
multipleMap[level] = 0;
}
}
return multipleMap;
}
// please read README-adjustTotalNodesCoordinate.md to understand this function
adjustTotalNodesCoordinate(params) {
const { hierarchy, isRowHeader, isSubTotal } = params;
const multipleMap = this.getMultipleMap(hierarchy, isRowHeader, isSubTotal);
const totalNodes = (0, lodash_1.filter)(hierarchy.getNodes(), (node) => isSubTotal ? node.isSubTotals : node.isGrandTotals);
const key = isRowHeader ? 'width' : 'height';
(0, lodash_1.forEach)(totalNodes, (node) => {
var _a;
let multiple = multipleMap[node.level];
// 小计根节点若为 0,则改为最近上级倍数 - level 差
if (!multiple && isSubTotal) {
let lowerLevelIndex = 1;
while (multiple < 1) {
multiple =
multipleMap[node.level - lowerLevelIndex] - lowerLevelIndex;
lowerLevelIndex++;
}
}
let res = 0;
for (let i = 0; i < multiple; i++) {
res += (0, lodash_1.get)((_a = hierarchy.sampleNodesForAllLevels) === null || _a === void 0 ? void 0 : _a.find((sampleNode) => sampleNode.level === node.level + i), [key], 0);
}
node[key] = res;
});
}
getColLeafNodesWidth(colNode, colLeafNodes, rowLeafNodes, rowHeaderWidth) {
const { colCell } = this.spreadsheet.options.style;
const cellDraggedWidth = this.getColCellDraggedWidth(colNode);
// 1. 拖拽后的宽度优先级最高
if ((0, lodash_1.isNumber)(cellDraggedWidth)) {
return (0, math_1.round)(cellDraggedWidth);
}
// 2. 其次是自定义, 返回 null 则使用默认宽度
const cellCustomWidth = this.getCellCustomSize(colNode, colCell === null || colCell === void 0 ? void 0 : colCell.width);
if ((0, lodash_1.isNumber)(cellCustomWidth)) {
return (0, math_1.round)(cellCustomWidth);
}
// 3. 紧凑布局
if (this.spreadsheet.getLayoutWidthType() === constant_1.LayoutWidthType.Compact) {
return this.getCompactGridColNodeWidth(colNode, rowLeafNodes);
}
/**
* 4. 自适应 adaptive
* 4.1 树状自定义
*/
if (this.spreadsheet.isHierarchyTreeType()) {
return this.getAdaptTreeColWidth(colNode, colLeafNodes, rowLeafNodes);
}
// 4.2 网格自定义
return this.getAdaptGridColWidth(colLeafNodes, colNode, rowHeaderWidth);
}
getRowNodeHeight(rowNode) {
if (!rowNode) {
return 0;
}
const { rowCell: rowCellStyle } = this.spreadsheet.options.style;
const defaultHeight = this.getRowCellHeight(rowNode);
// 文本超过 1 行时再自适应单元格高度, 不然会频繁触发 GC, 导致性能降低: https://github.com/antvis/S2/issues/2693
const isEnableHeightAdaptive = ((rowCellStyle === null || rowCellStyle === void 0 ? void 0 : rowCellStyle.maxLines) > 1 && (rowCellStyle === null || rowCellStyle === void 0 ? void 0 : rowCellStyle.wordWrap)) ||
this.spreadsheet.theme.rowCell.text.fontSize > theme_1.DEFAULT_FONTSIZE ||
this.spreadsheet.theme.rowCell.measureText.fontSize > theme_1.DEFAULT_FONTSIZE ||
this.spreadsheet.theme.rowCell.seriesText.fontSize > theme_1.DEFAULT_FONTSIZE ||
this.spreadsheet.theme.rowCell.bolderText.fontSize > theme_1.DEFAULT_FONTSIZE;
if (this.isCustomRowCellHeight(rowNode) || !isEnableHeightAdaptive) {
rowNode.extra.isCustomHeight = true;
return defaultHeight || 0;
}
return this.getNodeAdaptiveHeight({
meta: rowNode,
cell: this.textWrapTempRowCell,
defaultHeight,
});
}
/**
* 获得图标区域预估宽度
* 不考虑用户自定义的 displayCondition 条件
* @param iconStyle 图标样式
* @returns 宽度
*/
getExpectedCellIconWidth(cellType, useDefaultIcon, iconStyle) {
var _a, _b;
// count icons
let iconCount = 0;
if (useDefaultIcon) {
iconCount = 1;
}
else {
const actionIcons = (0, lodash_1.map)(this.spreadsheet.options.headerActionIcons, (iconCfg) => {
return Object.assign(Object.assign({}, iconCfg), {
// ignore condition func when layout calc
displayCondition: () => true });
});
const customIcons = (0, header_cell_1.getActionIconConfig)(actionIcons, null, cellType);
iconCount = (customIcons === null || customIcons === void 0 ? void 0 : customIcons.icons.length) || 0;
}
// calc width
return iconCount
? iconCount * (iconStyle.size + ((_a = iconStyle.margin) === null || _a === void 0 ? void 0 : _a.left)) +
((_b = iconStyle.margin) === null || _b === void 0 ? void 0 : _b.right)
: 0;
}
calculateRowNodesAllLevelSampleNodes(layoutResult) {
const { rowsHierarchy, colLeafNodes } = layoutResult;
const isTree = this.spreadsheet.isHierarchyTreeType();
const sampleNodeByLevel = rowsHierarchy.sampleNodesForAllLevels || [];
if (isTree) {
rowsHierarchy.width = this.getTreeRowHeaderWidth();
}
else {
sampleNodeByLevel.forEach((levelSample) => {
var _a;
levelSample.width = this.getGridRowNodesWidth(levelSample, colLeafNodes);
rowsHierarchy.width += levelSample.width;
const preLevelSample = (_a = sampleNodeByLevel[levelSample.level - 1]) !== null && _a !== void 0 ? _a : {
x: 0,
width: 0,
};
levelSample.x = (preLevelSample === null || preLevelSample === void 0 ? void 0 : preLevelSample.x) + (preLevelSample === null || preLevelSample === void 0 ? void 0 : preLevelSample.width);
});
}
rowsHierarchy.rootNode.width = rowsHierarchy.width;
}
getRowLeafNodeHeight(rowLeafNode) {
var _a;
const { rowCell: rowCellStyle } = this.spreadsheet.options.style;
const isEnableHeightAdaptive = (rowCellStyle === null || rowCellStyle === void 0 ? void 0 : rowCellStyle.maxLines) > 1 && (rowCellStyle === null || rowCellStyle === void 0 ? void 0 : rowCellStyle.wordWrap);
const currentBranchNodeHeights = isEnableHeightAdaptive
? node_1.Node.getBranchNodes(rowLeafNode).map((rowNode) => this.getRowNodeHeight(rowNode))
: [];
const defaultHeight = this.getRowNodeHeight(rowLeafNode);
// 父节点的高度是叶子节点的高度之和, 由于存在多行文本, 叶子节点的高度以当前路径下节点高度最大的为准: https://github.com/antvis/S2/issues/2678
// 自定义高度除外: https://github.com/antvis/S2/issues/2594
const nodeHeight = this.isCustomRowCellHeight(rowLeafNode)
? defaultHeight
: (_a = (0, lodash_1.max)(currentBranchNodeHeights)) !== null && _a !== void 0 ? _a : defaultHeight;
return (0, math_1.round)(nodeHeight);
}
calculateRowNodesBBox(rowsHierarchy) {
const isTree = this.spreadsheet.isHierarchyTreeType();
const sampleNodeByLevel = rowsHierarchy.sampleNodesForAllLevels || [];
let preLeafNode = node_1.Node.blankNode();
const rowNodes = rowsHierarchy.getNodes();
rowNodes.forEach((currentNode) => {
var _a, _b;
// 树状模式都按叶子处理节点
const isLeaf = isTree || (!isTree && currentNode.isLeaf);
// 1. 获取宽度, 便于多行文本计算高度
if (isTree) {
currentNode.width = this.getTreeRowHeaderWidth();
}
else {
const levelSampleNode = sampleNodeByLevel[currentNode.level];
currentNode.width = levelSampleNode === null || levelSampleNode === void 0 ? void 0 : levelSampleNode.width;
}
// 2. 计算叶子节点的高度和坐标
if (isLeaf) {
// 2.1. 普通树状结构, 叶子节点各占一行, 2.2. 自定义树状结构 (平铺模式)
const rowIndex = ((_a = preLeafNode === null || preLeafNode === void 0 ? void 0 : preLeafNode.rowIndex) !== null && _a !== void 0 ? _a : -1) + 1;
// 文本超过 1 行时再自适应单元格高度, 不然会频繁触发 GC, 导致性能降低: https://github.com/antvis/S2/issues/2693
(_b = currentNode.rowIndex) !== null && _b !== void 0 ? _b : (currentNode.rowIndex = rowIndex);
currentNode.y = preLeafNode.y + preLeafNode.height;
currentNode.height = this.getRowLeafNodeHeight(currentNode);
preLeafNode = currentNode;
// mark row hierarchy's height
rowsHierarchy.height += currentNode.height;
}
if (isTree || currentNode.level === 0) {
currentNode.x = 0;
}
else {
const preLevelSample = sampleNodeByLevel[currentNode.level - 1];
currentNode.x = (preLevelSample === null || preLevelSample === void 0 ? void 0 : preLevelSample.x) + (preLevelSample === null || preLevelSample === void 0 ? void 0 : preLevelSample.width);
}
(0, layout_hooks_1.layoutCoordinate)(this.spreadsheet, currentNode, null);
});
}
calculateRowNodesCoordinate(layoutResult) {
const { rowsHierarchy, rowLeafNodes } = layoutResult;
const isTree = this.spreadsheet.isHierarchyTreeType();
// 1、计算每一层级的采样节点
this.calculateRowNodesAllLevelSampleNodes(layoutResult);
// 2、计算节点的高度和 y (叶子节点),x坐标和宽度 (所有节点)
this.calculateRowNodesBBox(rowsHierarchy);
if (!isTree) {
// 3. 补齐自定义行头节点缺失的宽度;
this.adjustCustomRowLeafNodesWidth({
leafNodes: rowLeafNodes,
hierarchy: rowsHierarchy,
});
// 4.根据叶子节点高度计算所有父级节点高度和 Y 坐标, 便于计算自动换行后节点的真实高度
this.calculateRowNodeHeightAndY(rowLeafNodes);
// 5. 更新汇总节点坐标
this.adjustRowTotalNodesCoordinate(rowsHierarchy);
}
}
adjustRowTotalNodesCoordinate(rowsHierarchy) {
var _a;
if (!(0, lodash_1.isEmpty)((_a = this.spreadsheet.options.totals) === null || _a === void 0 ? void 0 : _a.row)) {
this.adjustTotalNodesCoordinate({
hierarchy: rowsHierarchy,
isRowHeader: true,
isSubTotal: false,
});
this.adjustTotalNodesCoordinate({
hierarchy: rowsHierarchy,
isRowHeader: true,
isSubTotal: true,
});
}
}
/**
* @description Auto calculate row no-leaf node's height and y coordinate
* @param rowLeafNodes
*/
calculateRowNodeHeightAndY(rowLeafNodes) {
// 3、in grid type, all no-leaf node's height, y are auto calculated
let prevRowParent = null;
let i = 0;
const leafNodes = rowLeafNodes.slice(0);
while (i < leafNodes.length) {
const node = leafNodes[i++];
const parent = node === null || node === void 0 ? void 0 : node.parent;
if (prevRowParent !== parent && parent) {
leafNodes.push(parent);
// 父节点 y 坐标 = 第一个未隐藏的子节点的 y 坐标
parent.y = parent.children[0].y;
// 父节点高度 = 所有子节点高度之和
parent.height = (0, lodash_1.sumBy)(parent.children, 'height');
prevRowParent = parent;
}
}
}
/**
* 计算 grid 模式下 node 宽度
*/
getGridRowNodesWidth(node, colLeafNodes) {
const { rowCell } = this.spreadsheet.options.style;
const cellDraggedWidth = this.getRowCellDraggedWidth(node);
if ((0, lodash_1.isNumber)(cellDraggedWidth)) {
return (0, math_1.round)(cellDraggedWidth);
}
const cellCustomWidth = this.getCellCustomSize(node, rowCell === null || rowCell === void 0 ? void 0 : rowCell.width);
if ((0, lodash_1.isNumber)(cellCustomWidth)) {
return (0, math_1.round)(cellCustomWidth);
}
if (this.spreadsheet.getLayoutWidthType() !== constant_1.LayoutWidthType.Adaptive) {
// compact or colAdaptive
return this.getCompactGridRowNodeWidth(node);
}
// adaptive
return this.getAdaptGridColWidth(colLeafNodes);
}
/**
* 计算树状模式等宽条件下的列宽
*/
getAdaptTreeColWidth(col, colLeafNodes, rowLeafNodes) {
// tree row width = [config width, canvas / 2]
const canvasW = this.getCanvasSize().width;
const availableWidth = canvasW -
this.getSeriesNumberWidth() -
header_1.Frame.getVerticalBorderWidth(this.spreadsheet);
const rowHeaderWidth = Math.min(availableWidth / 2, this.getTreeRowHeaderWidth());
// calculate col width
const colSize = Math.max(1, colLeafNodes.length);
const { dataCell } = this.spreadsheet.options.style;
return (0, math_1.round)(Math.max((0, text_1.getCellWidth)(dataCell, this.getColLabelLength(col, rowLeafNodes)), (0, math_1.floor)((availableWidth - rowHeaderWidth) / colSize)));
}
getColLabelLength(col, rowLeafNodes) {
// 如果 label 字段形如 "["xx","xxx"]",直接获取其长度
const labels = (0, utils_1.safeJsonParse)(col === null || col === void 0 ? void 0 : col.value);
if ((0, lodash_1.isArray)(labels)) {
return labels.length;
}
// 采样前 50,根据指标个数获取单元格列宽
let maxLength = 1;
for (let index = 0; index < common_1.LAYOUT_SAMPLE_COUNT; index++) {
const rowNode = rowLeafNodes[index];
if (!rowNode) {
// 抽样个数大于叶子节点个数
return maxLength;
}
const cellData = this.spreadsheet.dataSet.getCellData({
query: Object.assign(Object.assign({}, col.query), rowNode.query),
rowNode,
isTotals: col.isTotals ||
col.isTotalMeasure ||
rowNode.isTotals ||
rowNode.isTotalMeasure,
});
const cellDataKeys = (0, lodash_1.keys)(cellData);
for (let j = 0; j < cellDataKeys.length; j++) {
const dataValue = (0, lodash_1.get)(cellData, cellDataKeys[j]);
const valueSize = (0, lodash_1.size)((0, lodash_1.get)(dataValue === null || dataValue === void 0 ? void 0 : dataValue.values, '0'));
if (valueSize > maxLength) {
// greater length
maxLength = valueSize;
}
}
}
return maxLength;
}
/**
* 计算平铺模式等宽条件下的列宽
*/
getAdaptGridColWidth(colLeafNodes,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
colNode, rowHeaderWidth) {
const { rows = [] } = this.spreadsheet.dataSet.fields;
const { dataCell } = this.spreadsheet.options.style;
const rowHeaderColSize = rows.length;
const colHeaderColSize = colLeafNodes.length;
const { width } = this.getCanvasSize();
const availableWidth = width -
this.getSeriesNumberWidth() -
header_1.Frame.getVerticalBorderWidth(this.spreadsheet);
const colSize = Math.max(1, rowHeaderColSize + colHeaderColSize);
if (!rowHeaderWidth) {
return (0, math_1.round)(Math.max((0, text_1.getCellWidth)(dataCell), (0, math_1.floor)(availableWidth / colSize)));
}
return (0, math_1.round)(Math.max((0, text_1.getCellWidth)(dataCell), (0, math_1.floor)((availableWidth - rowHeaderWidth) / colHeaderColSize)));
}
/**
* 计算树状结构行头宽度
*/
getTreeRowHeaderWidth() {
var _a, _b, _c, _d;
const { rowCell } = this.spreadsheet.options.style;
// 1. 用户拖拽或手动指定的行头宽度优先级最高
if ((0, lodash_1.isNumber)(rowCell === null || rowCell === void 0 ? void 0 : rowCell.treeWidth)) {
return (0, math_1.round)(rowCell.treeWidth);
}
const customRowCellWidth = this.getCellCustomSize(null, rowCell === null || rowCell === void 0 ? void 0 : rowCell.width);
if ((0, lodash_1.isNumber)(customRowCellWidth)) {
return (0, math_1.round)(customRowCellWidth);
}
// 2. 然后是计算 (+ icon province/city/level)
const treeHeaderLabel = header_1.CornerHeader.getTreeCornerText(this.spreadsheet);
const { bolderText: cornerCellTextStyle, icon: cornerIconStyle } = this.spreadsheet.theme.cornerCell;
/**
* 初始化角头时,保证其在树形模式下不换行, 给与两个icon的宽度空余(tree icon 和 action icon),减少复杂的 action icon 判断
* 额外增加 1,当内容和容器宽度恰好相等时会出现换行
*/
const maxLabelWidth = this.measureTextWidth(treeHeaderLabel, cornerCellTextStyle) +
cornerIconStyle.size * 2 +
((_a = cornerIconStyle.margin) === null || _a === void 0 ? void 0 : _a.left) +
((_b = cornerIconStyle.margin) === null || _b === void 0 ? void 0 : _b.right) +
((_c = this.rowCellTheme.padding) === null || _c === void 0 ? void 0 : _c.left) +
((_d = this.rowCellTheme.padding) === null || _d === void 0 ? void 0 : _d.right);
const width = Math.max(customRowCellWidth !== null && customRowCellWidth !== void 0 ? customRowCellWidth : common_1.DEFAULT_ROW_CELL_TREE_WIDTH, maxLabelWidth);
return Number.isNaN(width) ? common_1.DEFAULT_ROW_CELL_TREE_WIDTH : (0, math_1.round)(width);
}
/**
* 计算 compact 模式下 node 宽度
*
* | fieldName |
* _______________
* | label - icon | <- node
* | label - icon |
* | label - icon |
*/
getCompactGridRowNodeWidth(node) {
var _a;
const { bolderText: rowTextStyle, icon: rowIconStyle, cell: rowCellStyle, } = this.spreadsheet.theme.rowCell;
const { bolderText: cornerTextStyle, icon: cornerIconStyle, cell: cornerCellStyle, } = this.spreadsheet.theme.cornerCell;
const { field, isLeaf } = node;
// calc rowNode width
const rowIconWidth = this.getExpectedCellIconWidth(interaction_1.CellType.ROW_CELL, !this.spreadsheet.isValueInCols() &&
isLeaf &&
this.spreadsheet.options.showDefaultHeaderActionIcon, rowIconStyle);
const allLabels = (_a = this.spreadsheet.dataSet
.getDimensionValues(field)) === null || _a === void 0 ? void 0 : _a.slice(0, common_1.LAYOUT_SAMPLE_COUNT).map((dimValue) => {
var _a, _b;
return (_b = (_a = this.spreadsheet.dataSet.getFieldFormatter(field)) === null || _a === void 0 ? void 0 : _a(dimValue, undefined, node)) !== null && _b !== void 0 ? _b : dimValue;
});
const maxLabel = (0, lodash_1.maxBy)(allLabels, (label) => `${label}`.length);
const rowNodeWidth = this.measureTextWidth(maxLabel, rowTextStyle) +
rowIconWidth +
rowCellStyle.padding.left +
rowCellStyle.padding.right +
rowCellStyle.verticalBorderWidth;
// calc corner fieldNameNodeWidth
const fieldName = this.spreadsheet.dataSet.getFieldName(field);
const cornerIconWidth = this.getExpectedCellIconWidth(interaction_1.CellType.CORNER_CELL, false, cornerIconStyle);
const fieldNameNodeWidth = this.measureTextWidth(fieldName, cornerTextStyle) +
cornerIconWidth +
cornerCellStyle.padding.left +
cornerCellStyle.padding.right;
debug_1.DebuggerUtil.getInstance().logger('Max Label In Row:', field, rowNodeWidth > fieldNameNodeWidth ? maxLabel : fieldName);
const { compactExtraWidth = 0, compactMinWidth } = this.spreadsheet.options.style || {};
const calculatedWidth = (0, math_1.round)(Math.max(rowNodeWidth, fieldNameNodeWidth) + compactExtraWidth);
return (0, lodash_1.isNumber)(compactMinWidth)
? Math.max(calculatedWidth, compactMinWidth)
: calculatedWidth;
}
getCompactGridColNodeWidth(colNode, rowLeafNodes) {
var _a, _b, _c, _d, _e, _f;
const { bolderText: colCellTextStyle, cell: colCellStyle, icon: colIconStyle, } = this.spreadsheet.theme.colCell;
const { text: dataCellTextStyle, icon: dataCellIconStyle } = this.spreadsheet.theme.dataCell;
// leaf node width
const cellFormatter = this.spreadsheet.dataSet.getFieldFormatter(colNode.field);
const leafNodeLabel = (_a = cellFormatter === null || cellFormatter === void 0 ? void 0 : cellFormatter(colNode.value, undefined, colNode)) !== null && _a !== void 0 ? _a : colNode.value;
const colIconWidth = this.getExpectedCellIconWidth(interaction_1.CellType.COL_CELL, this.spreadsheet.isValueInCols() &&
this.spreadsheet.options.showDefaultHeaderActionIcon, colIconStyle);
const leafNodeWidth = this.measureTextWidth(leafNodeLabel, colCellTextStyle) + colIconWidth;
// 采样 50 个 label,逐个计算找出最长的 label
let maxDataLabel = '';
let maxDataLabelWidth = 0;
let iconWidthOfMaxDataLabel = 0;
for (let index = 0; index < common_1.LAYOUT_SAMPLE_COUNT; index++) {
const rowNode = rowLeafNodes[index];
if (rowNode) {
const { valueField, dataQuery } = this.getDataQueryInfo(rowNode.query, colNode.query);
const cellData = this.spreadsheet.dataSet.getCellData({
query: dataQuery,
rowNode,
isTotals: colNode.isTotals ||
colNode.isTotalMeasure ||
rowNode.isTotals ||
rowNode.isTotalMeasure,
});
if (cellData) {
// 总小计格子不一定有数据
const valueData = cellData === null || cellData === void 0 ? void 0 : cellData[constant_1.VALUE_FIELD];
const cellLabel = (_c = (_b = this.spreadsheet.dataSet.getFieldFormatter(cellData[constant_1.EXTRA_FIELD])) === null || _b === void 0 ? void 0 : _b(valueData, cellData, colNode)) !== null && _c !== void 0 ? _c : valueData;
// 考虑字段标记 icon 的宽度: https://github.com/antvis/S2/pull/2673
const hasIcon = (0, condition_1.findFieldCondition)((_d = this.spreadsheet.options.conditions) === null || _d === void 0 ? void 0 : _d.icon, valueField);
const dataCellIconWidth = hasIcon
? (dataCellIconStyle === null || dataCellIconStyle === void 0 ? void 0 : dataCellIconStyle.size) +
((_e = dataCellIconStyle === null || dataCellIconStyle === void 0 ? void 0 : dataCellIconStyle.margin) === null || _e === void 0 ? void 0 : _e.left) +
((_f = dataCellIconStyle === null || dataCellIconStyle === void 0 ? void 0 : dataCellIconStyle.margin) === null || _f === void 0 ? void 0 : _f.right)
: 0;
const cellLabelWidth = this.measureTextWidth(cellLabel, dataCellTextStyle) +
dataCellIconWidth;
if (cellLabelWidth > maxDataLabelWidth) {
maxDataLabel = cellLabel;
maxDataLabelWidth = cellLabelWidth;
iconWidthOfMaxDataLabel = dataCellIconWidth;
}
}
}
}
const isLeafNodeWidthLonger = leafNodeWidth > maxDataLabelWidth;
const maxLabel = isLeafNodeWidthLonger ? leafNodeLabel : maxDataLabel;
const appendedWidth = isLeafNodeWidthLonger
? colIconWidth
: iconWidthOfMaxDataLabel;
debug_1.DebuggerUtil.getInstance().logger('Max Label In Col:', colNode.field, maxLabel, maxDataLabelWidth, iconWidthOfMaxDataLabel);
// 取列头/数值字体最大的文本宽度: https://github.com/antvis/S2/issues/2385
const maxTextWidth = this.measureTextWidth(maxLabel, Object.assign(Object.assign({}, colCellTextStyle), { fontSize: Math.max(dataCellTextStyle.fontSize, colCellTextStyle.fontSize) }));
const { compactExtraWidth = 0, compactMinWidth } = this.spreadsheet.options.style || {};
const calculatedWidth = (0, math_1.round)(maxTextWidth +
colCellStyle.padding.left +
colCellStyle.padding.right +
colCellStyle.verticalBorderWidth * 2 +
appendedWidth +
compactExtraWidth);
return (0, lodash_1.isNumber)(compactMinWidth)
? Math.max(calculatedWidth, compactMinWidth)
: calculatedWidth;
}
getViewCellHeights() {
const rowLeafNodes = this.getRowLeafNodes();
const heights = (0, lodash_1.reduce)(rowLeafNodes, (result, node) => {
const currentNodeHeight = (0, lodash_1.last)(result) || 0;
result.push(currentNodeHeight + node.height);
return result;
}, [0]);
return {
getTotalHeight: () => (0, lodash_1.last)(heights) || 0,
getCellOffsetY: (index) => heights[index] || 0,
// 多了一个数据 [0]
getTotalLength: () => heights.length - 1,
getIndexRange: (minHeight, maxHeight) => (0, facet_1.getIndexRangeWithOffsets)(heights, minHeight, maxHeight),
};
}
/**
* 获取序号单元格
* @description 对于透视表, 序号属于 RowCell
*/
getSeriesNumberCells() {
var _a;
const headerChildren = (((_a = this.getSeriesNumberHeader()) === null || _a === void 0 ? void 0 : _a.children) ||
[]);
return (0, get_all_child_cells_1.getAllChildCells)(headerChildren, cell_1.SeriesNumberCell);
}
getFrozenOptions() {
if (!this.validFrozenOptions) {
this.validFrozenOptions = (0, utils_1.getValidFrozenOptionsForPivot)(super.getFrozenOptions(), this.spreadsheet.options);
}
return this.validFrozenOptions;
}
getContentWidth() {
const { rowsHierarchy, colsHierarchy } = this.layoutResult;
return (0, math_1.round)(rowsHierarchy.width +
colsHierarchy.width +
header_1.Frame.getVerticalBorderWidth(this.spreadsheet));
}
getContentHeight() {
const { rowsHierarchy, colsHierarchy } = this.layoutResult;
return (0, math_1.round)(rowsHierarchy.height +
colsHierarchy.height +
header_1.Frame.getHorizontalBorderWidth(this.spreadsheet));
}
}
exports.PivotFacet = PivotFacet;
//# sourceMappingURL=pivot-facet.js.map