UNPKG

@antv/s2

Version:

effective spreadsheet render core lib

461 lines 21.7 kB
import { EXTRA_FIELD, Node, ORIGIN_FIELD, PivotFacet, ScrollType, getAllChildCells, getCellWidth, getDataCellId, getHeaderTotalStatus, round, } from '@antv/s2'; import { concat, floor, get, isEmpty, isNumber, last, merge, sum, } from 'lodash'; import { KEY_GROUP_COL_AXIS_RESIZE_AREA, KEY_GROUP_ROW_AXIS_RESIZE_AREA, X_FIELD_FORMATTER, } from '../constant'; import { AxisColHeader } from '../header/axis-col'; import { AxisCornerHeader } from '../header/axis-corner'; import { AxisRowHeader } from '../header/axis-row'; import { CornerHeader } from '../header/corner'; import { AxisColCell } from '../cell/axis-col-cell'; import { AxisCornerCell } from '../cell/axis-corner-cell'; import { AxisRowCell } from '../cell/axis-row-cell'; import { AxisCellType } from '../cell/cell-type'; import { separateRowColLeafNodes } from '../utils/separate-axis'; import { CornerBBox } from './corner-bbox'; import { Frame } from './frame'; import { PanelBBox } from './panel-bbox'; export class PivotChartFacet extends PivotFacet { constructor() { super(...arguments); /** * 获取单元格的所有子节点 (含非可视区域) * @example * const rowCell = facet.getRowCells()[0] * facet.getCellChildrenNodes(rowCell) */ this.getCellChildrenNodes = (cell) => { var _a; const selectNode = (_a = cell === null || cell === void 0 ? void 0 : cell.getMeta) === null || _a === void 0 ? void 0 : _a.call(cell); return Node.getAllChildrenNodes(selectNode, (node) => { // 行列头区域,也把对应的 axis 区域 node 返回 return node.relatedNode ? [node, node.relatedNode] : [node]; }); }; } doLayout() { let layoutResult = this.buildAllHeaderHierarchy(); layoutResult = separateRowColLeafNodes(layoutResult, this.spreadsheet); this.calculateHeaderNodesCoordinate(layoutResult); this.calculateAxisHierarchyCoordinate(layoutResult); const { rowsHierarchy, colsHierarchy, axisRowsHierarchy, axisColsHierarchy, } = layoutResult; return { axisRowsHierarchy, axisColsHierarchy, rowsHierarchy, rowNodes: rowsHierarchy.getNodes(), rowLeafNodes: rowsHierarchy.getLeaves(), colsHierarchy, colNodes: colsHierarchy.getNodes(), colLeafNodes: colsHierarchy.getLeaves(), }; } getColLeafNodeRelatedCount(colNode) { const isValueInCols = this.spreadsheet.isValueInCols(); const isPolar = this.spreadsheet.isPolarCoordinate(); const size = !isValueInCols && !isPolar ? get(colNode.relatedNode, 'children', []).length : 1; return size; } getRowLeafNodeRelatedCount(rowNode) { const isValueInCols = this.spreadsheet.isValueInCols(); const isPolar = this.spreadsheet.isPolarCoordinate(); const size = isValueInCols && !isPolar ? get(rowNode.relatedNode, 'children', []).length : 1; return size; } getRowAxisWidth() { var _a, _b; const { rowCell } = this.spreadsheet.options.style; const { rows = [] } = this.spreadsheet.dataSet.fields; const lastRow = last(rows); return round((_b = (_a = rowCell === null || rowCell === void 0 ? void 0 : rowCell.widthByField) === null || _a === void 0 ? void 0 : _a[lastRow]) !== null && _b !== void 0 ? _b : 0); } getColAxisHeight() { var _a, _b; const { colCell } = this.spreadsheet.options.style; const { columns = [] } = this.spreadsheet.dataSet.fields; const lastCol = last(columns); return round((_b = (_a = colCell === null || colCell === void 0 ? void 0 : colCell.heightByField) === null || _a === void 0 ? void 0 : _a[lastCol]) !== null && _b !== void 0 ? _b : 0); } getCompactGridColNodeWidth(colNode) { const { dataCell, compactExtraWidth = 0, compactMinWidth, } = this.spreadsheet.options.style; const dataCellWidth = getCellWidth(dataCell, this.getColLeafNodeRelatedCount(colNode)); const calculatedWidth = round(dataCellWidth + compactExtraWidth); return isNumber(compactMinWidth) ? Math.max(calculatedWidth, compactMinWidth) : calculatedWidth; } getAdaptGridColWidth(colLeafNodes, colNode, rowHeaderWidth) { const { rows = [] } = this.spreadsheet.dataSet.fields; const { dataCell } = this.spreadsheet.options.style; const rowHeaderColSize = Math.max(0, rows.length - 1); const colHeaderColSize = sum(colLeafNodes.map((node) => this.getColLeafNodeRelatedCount(node))); const { width } = this.getCanvasSize(); const availableWidth = width - this.getSeriesNumberWidth() - this.getRowAxisWidth() - Frame.getVerticalBorderWidth(this.spreadsheet); const colSize = Math.max(1, rowHeaderColSize + colHeaderColSize); const currentSize = colNode ? this.getColLeafNodeRelatedCount(colNode) : 1; if (!rowHeaderWidth) { return round(currentSize * Math.max(getCellWidth(dataCell), floor(availableWidth / colSize))); } return round(currentSize * Math.max(getCellWidth(dataCell), floor((availableWidth - rowHeaderWidth) / colHeaderColSize))); } getRowLeafNodeHeight(rowLeafNode) { var _a; const customHeight = this.getCustomRowCellHeight(rowLeafNode); // 1. 拖拽后的宽度优先级最高 if (isNumber(customHeight)) { return round(customHeight); } const { dataCell } = this.spreadsheet.options.style; const dataCellHeight = round((_a = dataCell === null || dataCell === void 0 ? void 0 : dataCell.height) !== null && _a !== void 0 ? _a : 0); return this.getRowLeafNodeRelatedCount(rowLeafNode) * dataCellHeight; } calculateAxisHierarchyCoordinate(layoutResult) { this.adjustTotalNodesCoordinateAfterSeparateAxisHierarchy(layoutResult); this.calculateAxisRowsHierarchyCoordinate(layoutResult); this.calculateAxisColsHierarchyCoordinate(layoutResult); } adjustTotalNodesCoordinateAfterSeparateAxisHierarchy(layoutResult) { // 最后一个维度分离出去后,再存在总计、小计分组时,会存在总计、小计格子出现空缺,因为 pivot-facet 层是按照未拆分的逻辑做的补全。 // 拆分后需要再处理一下,而且只需要针对维度拆分的部分做处理即可,指标拆分正常显示 var _a, _b; const { rowsHierarchy, colsHierarchy } = layoutResult; if (!isEmpty((_a = this.spreadsheet.options.totals) === null || _a === void 0 ? void 0 : _a.row) && this.spreadsheet.isValueInCols()) { const sampleNodeForLastLevel = rowsHierarchy.sampleNodeForLastLevel; const maxX = sampleNodeForLastLevel.x + sampleNodeForLastLevel.width; rowsHierarchy.getLeaves().forEach((leaf) => { const rightX = leaf.x + leaf.width; if (maxX > rightX) { leaf.width += maxX - rightX; } }); } if (!isEmpty((_b = this.spreadsheet.options.totals) === null || _b === void 0 ? void 0 : _b.col) && !this.spreadsheet.isValueInCols()) { const sampleNodeForLastLevel = colsHierarchy.sampleNodeForLastLevel; const maxY = sampleNodeForLastLevel.y + sampleNodeForLastLevel.height; colsHierarchy.getLeaves().forEach((leaf) => { const bottomY = leaf.y + leaf.height; if (maxY > bottomY) { leaf.height += maxY - bottomY; } }); } } calculateAxisRowsHierarchyCoordinate(layoutResult) { const { rowsHierarchy, axisRowsHierarchy } = layoutResult; if (!axisRowsHierarchy) { return; } const isValueInCols = this.spreadsheet.isValueInCols(); const isPolar = this.spreadsheet.isPolarCoordinate(); rowsHierarchy.width = rowsHierarchy.isPlaceholder && isValueInCols && !isPolar ? 0 : rowsHierarchy.width; const rowAxisWidth = this.getRowAxisWidth(); rowsHierarchy.getLeaves().forEach((leaf) => { const relatedNode = leaf.relatedNode; if (!relatedNode) { return; } relatedNode.y = leaf.y; relatedNode.width = rowAxisWidth; relatedNode.height = leaf.height; }); if (isValueInCols && isPolar) { axisRowsHierarchy.width = 0; axisRowsHierarchy.getNodes().forEach((node) => { node.width = 0; }); } else { axisRowsHierarchy.width = rowAxisWidth; } axisRowsHierarchy.height = rowsHierarchy.height; } calculateAxisColsHierarchyCoordinate(layoutResult) { const { colsHierarchy, axisColsHierarchy } = layoutResult; if (!axisColsHierarchy) { return; } const isValueInCols = this.spreadsheet.isValueInCols(); const isPolar = this.spreadsheet.isPolarCoordinate(); const colAxisHeight = this.getColAxisHeight(); colsHierarchy.getLeaves().forEach((leaf) => { const relatedNode = leaf.relatedNode; if (!relatedNode) { return; } relatedNode.x = leaf.x; relatedNode.width = leaf.width; relatedNode.height = colAxisHeight; }); axisColsHierarchy.width = colsHierarchy.width; if (!isValueInCols && isPolar) { axisColsHierarchy.height = 0; axisColsHierarchy.getNodes().forEach((node) => { node.height = 0; }); } else { axisColsHierarchy.height = colAxisHeight; } } calculateCornerBBox() { this.cornerBBox = new CornerBBox(this, true); } calculatePanelBBox() { this.panelBBox = new PanelBBox(this, true); } getCenterFrame() { var _a; if (!this.centerFrame) { const { viewportWidth, viewportHeight } = this.panelBBox; const cornerWidth = this.cornerBBox.width; const cornerHeight = this.cornerBBox.height; const frame = (_a = this.spreadsheet.options) === null || _a === void 0 ? void 0 : _a.frame; const frameCfg = { position: { x: this.cornerBBox.x, y: this.cornerBBox.y, }, cornerWidth, cornerHeight, viewportWidth, viewportHeight, showViewportLeftShadow: false, showViewportRightShadow: false, spreadsheet: this.spreadsheet, }; return frame ? frame(frameCfg) : new Frame(frameCfg); } return this.centerFrame; } renderHeaders() { super.renderHeaders(); this.axisRowHeader = this.getAxisRowHeader(); if (this.axisRowHeader) { this.foregroundGroup.appendChild(this.axisRowHeader); } this.axisColumnHeader = this.getAxisColHeader(); if (this.axisColumnHeader) { this.foregroundGroup.appendChild(this.axisColumnHeader); } this.axisCornerHeader = this.getAxisCornerHeader(); if (this.axisCornerHeader) { this.foregroundGroup.appendChild(this.axisCornerHeader); } } getCornerHeader() { return (this.cornerHeader || CornerHeader.getCornerHeader({ panelBBox: this.panelBBox, cornerBBox: this.cornerBBox, seriesNumberWidth: this.getSeriesNumberWidth(), layoutResult: this.layoutResult, spreadsheet: this.spreadsheet, })); } getAxisRowHeader() { var _a; if (this.axisRowHeader) { return this.axisRowHeader; } const { y, viewportHeight, viewportWidth, height } = this.panelBBox; const { rowsHierarchy, axisRowsHierarchy } = this.layoutResult; const seriesNumberWidth = this.getSeriesNumberWidth(); return new AxisRowHeader({ width: this.cornerBBox.width, height, viewportWidth, viewportHeight, position: { x: seriesNumberWidth + rowsHierarchy.width, y }, nodes: (_a = axisRowsHierarchy === null || axisRowsHierarchy === void 0 ? void 0 : axisRowsHierarchy.getNodes()) !== null && _a !== void 0 ? _a : [], spreadsheet: this.spreadsheet, }); } getAxisColHeader() { var _a, _b; if (this.axisColumnHeader) { return this.axisColumnHeader; } const { x, width, viewportWidth, y, viewportHeight } = this.panelBBox; const { axisColsHierarchy } = this.layoutResult; return new AxisColHeader({ width, cornerWidth: this.cornerBBox.width, height: (_a = axisColsHierarchy === null || axisColsHierarchy === void 0 ? void 0 : axisColsHierarchy.height) !== null && _a !== void 0 ? _a : 0, viewportWidth, viewportHeight, position: { x, y: y + viewportHeight }, nodes: (_b = axisColsHierarchy === null || axisColsHierarchy === void 0 ? void 0 : axisColsHierarchy.getNodes()) !== null && _b !== void 0 ? _b : [], spreadsheet: this.spreadsheet, }); } getAxisCornerHeader() { return (this.axisCornerHeader || AxisCornerHeader.getCornerHeader({ panelBBox: this.panelBBox, cornerBBox: this.cornerBBox, seriesNumberWidth: this.getSeriesNumberWidth(), layoutResult: this.layoutResult, spreadsheet: this.spreadsheet, })); } translateRelatedGroups(scrollX, scrollY, hRowScroll) { var _a, _b, _c; super.translateRelatedGroups(scrollX, scrollY, hRowScroll); (_a = this.axisRowHeader) === null || _a === void 0 ? void 0 : _a.onScrollXY(this.getRealScrollX(scrollX, hRowScroll), scrollY, KEY_GROUP_ROW_AXIS_RESIZE_AREA); (_b = this.axisColumnHeader) === null || _b === void 0 ? void 0 : _b.onColScroll(scrollX, KEY_GROUP_COL_AXIS_RESIZE_AREA); (_c = this.axisCornerHeader) === null || _c === void 0 ? void 0 : _c.onCorScroll(this.getRealScrollX(scrollX, hRowScroll)); } renderRowScrollBar(rowHeaderScrollX) { super.renderRowScrollBar(rowHeaderScrollX); if (this.hRowScrollBar) { const maxOffset = this.cornerBBox.originalWidth - this.cornerBBox.width; this.hRowScrollBar.addEventListener(ScrollType.ScrollChange, ({ offset }) => { var _a, _b; const newOffset = this.getValidScrollBarOffset(offset, maxOffset); const newRowHeaderScrollX = floor(newOffset); this.setScrollOffset({ rowHeaderScrollX: newRowHeaderScrollX }); (_a = this.axisRowHeader) === null || _a === void 0 ? void 0 : _a.onRowScrollX(newRowHeaderScrollX, KEY_GROUP_ROW_AXIS_RESIZE_AREA); (_b = this.axisCornerHeader) === null || _b === void 0 ? void 0 : _b.onRowScrollX(newRowHeaderScrollX); }); } } /** * 根据行列索引获取单元格元数据 */ getCellMeta(rowIndex = 0, colIndex = 0) { var _a, _b, _c, _d; const { options, dataSet } = this.spreadsheet; const { axisRowsHierarchy, axisColsHierarchy } = this.getLayoutResult(); const rowAxisLeafNodes = (_a = axisRowsHierarchy === null || axisRowsHierarchy === void 0 ? void 0 : axisRowsHierarchy.getLeaves()) !== null && _a !== void 0 ? _a : []; const colAxisLeafNodes = (_b = axisColsHierarchy === null || axisColsHierarchy === void 0 ? void 0 : axisColsHierarchy.getLeaves()) !== null && _b !== void 0 ? _b : []; const rowAxis = rowAxisLeafNodes[rowIndex]; const colAxis = colAxisLeafNodes[colIndex]; if (!rowAxis || !colAxis) { return null; } const data = []; const xField = rowAxis.field === EXTRA_FIELD ? colAxis.field : rowAxis.field; const yField = rowAxis.field === EXTRA_FIELD ? rowAxis.value : colAxis.value; for (const rowChild of rowAxis.children) { for (const colChild of colAxis.children) { const rowQuery = rowChild.query; const colQuery = colChild.query; const isTotals = rowChild.isTotals || rowChild.isTotalMeasure || colChild.isTotals || colChild.isTotalMeasure; const totalStatus = getHeaderTotalStatus(rowChild, colChild); const dataQuery = merge({}, rowQuery, colQuery); const current = dataSet.getCellData({ query: dataQuery, isTotals, totalStatus, }); let xValue; let xValueShouldFormatter = true; if (rowChild.field === EXTRA_FIELD) { xValue = colChild.value; xValueShouldFormatter = !colChild.isTotalRoot; } else { xValue = rowChild.value; xValueShouldFormatter = !rowChild.isTotalRoot; } const origin = Object.assign({ [xField]: xValue, [X_FIELD_FORMATTER]: xValueShouldFormatter }, current === null || current === void 0 ? void 0 : current[ORIGIN_FIELD]); data.push(origin); } } const cellMeta = { spreadsheet: this.spreadsheet, x: colAxis.x, y: rowAxis.y, width: colAxis.width, height: rowAxis.height, data, rowIndex, colIndex, rowId: rowAxis.id, colId: colAxis.id, fieldValue: data, valueField: yField, xField, yField, id: getDataCellId(rowAxis.id, colAxis.id), }; return (_d = (_c = options.layoutCellMeta) === null || _c === void 0 ? void 0 : _c.call(options, cellMeta)) !== null && _d !== void 0 ? _d : cellMeta; } getFrozenColSplitLineSize() { var _a; const { viewportHeight, y: panelBBoxStartY } = this.panelBBox; const { axisColsHierarchy } = this.layoutResult; const height = viewportHeight + panelBBoxStartY + ((_a = axisColsHierarchy === null || axisColsHierarchy === void 0 ? void 0 : axisColsHierarchy.height) !== null && _a !== void 0 ? _a : 0); return { y: 0, height, }; } getAxisCornerCells() { var _a; const headerChildren = (((_a = this.getAxisCornerHeader()) === null || _a === void 0 ? void 0 : _a.children) || []); return getAllChildCells(headerChildren, AxisCornerCell).filter((cell) => cell.cellType === AxisCellType.AXIS_CORNER_CELL); } getAxisRowCells() { var _a; const headerChildren = (((_a = this.getAxisRowHeader()) === null || _a === void 0 ? void 0 : _a.children) || []); return getAllChildCells(headerChildren, AxisRowCell).filter((cell) => cell.cellType === AxisCellType.AXIS_ROW_CELL); } getAxisColCells() { var _a; const headerChildren = (((_a = this.getAxisColHeader()) === null || _a === void 0 ? void 0 : _a.children) || []); return getAllChildCells(headerChildren, AxisColCell).filter((cell) => cell.cellType === AxisCellType.AXIS_COL_CELL); } /** * 获取表头单元格 (序号,角头,行头,列头) (不含可视区域) * @example 获取全部: facet.getHeaderCells() * @example 获取一组 facet.getHeaderCells(['root[&]浙江省[&]宁波市', 'root[&]浙江省[&]杭州市']) */ getHeaderCells(cellIds) { const headerCells = concat(this.getCornerCells(), this.getSeriesNumberCells(), this.getRowCells(), this.getColCells(), this.getAxisCornerCells(), this.getAxisRowCells(), this.getAxisColCells()); return this.filterCells(headerCells, cellIds); } getAxisCornerNodes() { var _a; return ((_a = this.axisCornerHeader) === null || _a === void 0 ? void 0 : _a.getNodes()) || []; } getAxisRowNodes() { var _a; return ((_a = this.axisRowHeader) === null || _a === void 0 ? void 0 : _a.getNodes()) || []; } getAxisColNodes() { var _a; return ((_a = this.axisColumnHeader) === null || _a === void 0 ? void 0 : _a.getNodes()) || []; } /** * 获取表头节点 (角头,序号,行头,列头) (含可视区域) * @example 获取全部: facet.getHeaderNodes() * @example 获取一组 facet.getHeaderNodes(['root[&]浙江省[&]宁波市', 'root[&]浙江省[&]杭州市']) */ getHeaderNodes(nodeIds) { const headerNodes = concat(this.getCornerNodes(), this.getSeriesNumberNodes(), this.getRowNodes(), this.getColNodes(), this.getAxisCornerNodes(), this.getAxisRowNodes(), this.getAxisColNodes()); if (!nodeIds) { return headerNodes; } return headerNodes.filter((node) => nodeIds.includes(node.id)); } } //# sourceMappingURL=pivot-chart-facet.js.map