UNPKG

@antv/s2

Version:

effective spreadsheet render core lib

504 lines 24 kB
import { Group, Rect } from '@antv/g'; import { last } from 'lodash'; import { FRONT_GROUND_GROUP_FROZEN_Z_INDEX, FrozenGroupArea, FrozenGroupAreaTypeMap, FrozenGroupType, KEY_GROUP_FROZEN_SPLIT_LINE, PANEL_GROUP_FROZEN_GROUP_Z_INDEX, S2Event, SPLIT_LINE_WIDTH, } from '../common/constant'; import { FrozenGroup } from '../group/frozen-group'; import { getValidFrozenOptions, renderLine, waitForCellMounted, } from '../utils'; import { getColsForGrid, getFrozenRowsForGrid, getRowsForGrid, } from '../utils/grid'; import { floor } from '../utils/math'; import { BaseFacet } from './base-facet'; import { getFrozenColOffset, getFrozenTrailingColOffset, getFrozenTrailingRowOffset, getScrollGroupClip, } from './header/util'; import { calculateFrozenCornerCells, calculateInViewIndexes, getFrozenGroupTypeByCell, splitInViewIndexesWithFrozen, translateGroup, } from './utils'; /** * Defines the row freeze abstract standard interface */ export class FrozenFacet extends BaseFacet { constructor() { super(...arguments); this.frozenGroupAreas = { [FrozenGroupArea.Col]: { width: 0, x: 0, range: [], }, [FrozenGroupArea.TrailingCol]: { width: 0, x: 0, range: [], }, [FrozenGroupArea.Row]: { height: 0, y: 0, range: [], }, [FrozenGroupArea.TrailingRow]: { height: 0, y: 0, range: [], }, }; this.panelScrollGroupIndexes = [0, 0, 0, 0]; this.addDataCell = (cell) => { const colLeafLength = this.getColLeafNodes().length; const cellRange = this.getCellRange(); const frozenGroupType = getFrozenGroupTypeByCell(cell.getMeta(), this.getFrozenOptions(), colLeafLength, cellRange); if (frozenGroupType === FrozenGroupType.Scroll) { this.panelScrollGroup.appendChild(cell); } else { this.frozenGroups[frozenGroupType].appendChild(cell); } waitForCellMounted(() => { this.spreadsheet.emit(S2Event.DATA_CELL_RENDER, cell); this.spreadsheet.emit(S2Event.LAYOUT_CELL_RENDER, cell); }); }; this.addFrozenCell = (colIndex, rowIndex, group) => { const viewMeta = this.getCellMeta(rowIndex, colIndex); if (viewMeta) { viewMeta['isFrozenCorner'] = true; const cell = this.createDataCell(viewMeta); group.appendChild(cell); } }; this.translateFrozenGroups = () => { const { scrollY, scrollX } = this.getScrollOffset(); const paginationScrollY = this.getPaginationScrollY(); const { x, y, viewportWidth, viewportHeight } = this.panelBBox; const colOffset = getFrozenColOffset(this, this.cornerBBox.width, scrollX); const trailingColOffset = getFrozenTrailingColOffset(this, viewportWidth); const trailingRowOffset = getFrozenTrailingRowOffset(this, viewportHeight, paginationScrollY); translateGroup(this.frozenGroups[FrozenGroupType.TopLeft], x - colOffset, y - paginationScrollY); translateGroup(this.frozenGroups[FrozenGroupType.TopRight], x - trailingColOffset, y - paginationScrollY); translateGroup(this.frozenGroups[FrozenGroupType.BottomLeft], x - colOffset, y - trailingRowOffset); translateGroup(this.frozenGroups[FrozenGroupType.BottomRight], x - trailingColOffset, y - trailingRowOffset); translateGroup(this.frozenGroups[FrozenGroupType.Row], x - scrollX, y - paginationScrollY); translateGroup(this.frozenGroups[FrozenGroupType.TrailingRow], x - scrollX, y - trailingRowOffset); translateGroup(this.frozenGroups[FrozenGroupType.Col], x - colOffset, y - scrollY - paginationScrollY); translateGroup(this.frozenGroups[FrozenGroupType.TrailingCol], x - trailingColOffset, y - scrollY - paginationScrollY); }; // eslint-disable-next-line max-lines-per-function this.renderFrozenGroupSplitLine = (scrollX, scrollY) => { var _a; // 在分页条件下需要额外处理 Y 轴滚动值 const relativeScrollY = Math.floor(scrollY - this.getPaginationScrollY()); // remove previous split line group (_a = this.foregroundGroup.getElementById(KEY_GROUP_FROZEN_SPLIT_LINE)) === null || _a === void 0 ? void 0 : _a.remove(); const { splitLine } = this.spreadsheet.theme; const splitLineGroup = this.foregroundGroup.appendChild(new Group({ id: KEY_GROUP_FROZEN_SPLIT_LINE, style: { zIndex: FRONT_GROUND_GROUP_FROZEN_Z_INDEX, }, })); const verticalBorderStyle = { lineWidth: SPLIT_LINE_WIDTH, stroke: splitLine === null || splitLine === void 0 ? void 0 : splitLine.verticalBorderColor, opacity: splitLine === null || splitLine === void 0 ? void 0 : splitLine.verticalBorderColorOpacity, }; const horizontalBorderStyle = { lineWidth: SPLIT_LINE_WIDTH, stroke: splitLine === null || splitLine === void 0 ? void 0 : splitLine.horizontalBorderColor, opacity: splitLine === null || splitLine === void 0 ? void 0 : splitLine.horizontalBorderColorOpacity, }; this.renderFrozenColSplitLine(splitLineGroup, splitLine, verticalBorderStyle, scrollX); this.renderFrozenTrailingColSplitLine(splitLineGroup, splitLine, verticalBorderStyle, scrollX); this.renderFrozenRowSplitLine(splitLineGroup, splitLine, horizontalBorderStyle, relativeScrollY); this.renderFrozenTrailingRowSplitLine(splitLineGroup, splitLine, horizontalBorderStyle, relativeScrollY); }; this.renderFrozenPanelCornerGroup = () => { const cellRange = this.getCellRange(); const result = calculateFrozenCornerCells(this.getFrozenOptions(), this.getColLeafNodes().length, cellRange); Object.keys(result || {}).forEach((key) => { const cells = result[key]; const group = this.frozenGroups[key]; if (group) { cells.forEach((cell) => { this.addFrozenCell(cell.x, cell.y, group); }); } }); }; this.getTotalHeightForRange = (start, end) => { if (start < 0 || end < 0) { return 0; } if (this.rowOffsets) { return this.rowOffsets[end + 1] - this.rowOffsets[start]; } let totalHeight = 0; for (let index = start; index < end + 1; index++) { const height = this.getDefaultCellHeight(); totalHeight += height; } return totalHeight; }; this.getShadowFill = (angle) => { var _a, _b; const { splitLine } = this.spreadsheet.theme; return `l (${angle}) 0:${(_a = splitLine === null || splitLine === void 0 ? void 0 : splitLine.shadowColors) === null || _a === void 0 ? void 0 : _a.left} 1:${(_b = splitLine === null || splitLine === void 0 ? void 0 : splitLine.shadowColors) === null || _b === void 0 ? void 0 : _b.right}`; }; } initPanelGroups() { super.initPanelGroups(); /* init frozen groups */ this.frozenGroups = [ FrozenGroupType.Row, FrozenGroupType.Col, FrozenGroupType.TrailingRow, FrozenGroupType.TrailingCol, FrozenGroupType.TopLeft, FrozenGroupType.TopRight, FrozenGroupType.BottomLeft, FrozenGroupType.BottomRight, ].reduce((acc, name) => { const frozenGroup = new FrozenGroup({ name, zIndex: PANEL_GROUP_FROZEN_GROUP_Z_INDEX, s2: this.spreadsheet, }); this.panelGroup.appendChild(frozenGroup); acc[name] = frozenGroup; return acc; }, {}); } /** * 获取冻结数量结果,主要是针对 col top level 的结果 */ getFrozenOptions() { if (!this.validFrozenOptions) { const colLength = this.getColLeafNodes().length; const cellRange = this.getCellRange(); this.validFrozenOptions = getValidFrozenOptions(this.spreadsheet.options.frozen, colLength, cellRange.end - cellRange.start + 1); } return this.validFrozenOptions; } calculateFrozenGroupInfo() { const { colCount, rowCount, trailingColCount, trailingRowCount } = this.getFrozenOptions(); const viewCellHeights = this.viewCellHeights; const cellRange = this.getCellRange(); const leafColNodes = this.getColLeafNodes(); if (colCount > 0) { this.frozenGroupAreas[FrozenGroupArea.Col].width = leafColNodes[colCount - 1].x + leafColNodes[colCount - 1].width; this.frozenGroupAreas[FrozenGroupArea.Col].x = 0; this.frozenGroupAreas[FrozenGroupArea.Col].range = [0, colCount - 1]; } if (rowCount > 0) { this.frozenGroupAreas[FrozenGroupArea.Row].height = viewCellHeights.getCellOffsetY(cellRange.start + rowCount) - viewCellHeights.getCellOffsetY(cellRange.start); this.frozenGroupAreas[FrozenGroupArea.Row].y = viewCellHeights.getCellOffsetY(cellRange.start); this.frozenGroupAreas[FrozenGroupArea.Row].range = [ cellRange.start, cellRange.start + rowCount - 1, ]; } if (trailingColCount > 0) { this.frozenGroupAreas[FrozenGroupArea.TrailingCol].width = leafColNodes[leafColNodes.length - 1].x - leafColNodes[leafColNodes.length - trailingColCount].x + leafColNodes[leafColNodes.length - 1].width; this.frozenGroupAreas[FrozenGroupArea.TrailingCol].x = leafColNodes[leafColNodes.length - trailingColCount].x; this.frozenGroupAreas[FrozenGroupArea.TrailingCol].range = [ leafColNodes.length - trailingColCount, leafColNodes.length - 1, ]; } if (trailingRowCount > 0) { this.frozenGroupAreas[FrozenGroupArea.TrailingRow].height = viewCellHeights.getCellOffsetY(cellRange.end + 1) - viewCellHeights.getCellOffsetY(cellRange.end + 1 - trailingRowCount); this.frozenGroupAreas[FrozenGroupArea.TrailingRow].y = viewCellHeights.getCellOffsetY(cellRange.end + 1 - trailingRowCount); this.frozenGroupAreas[FrozenGroupArea.TrailingRow].range = [ cellRange.end - trailingRowCount + 1, cellRange.end, ]; } } getFinalViewport() { const { viewportHeight: height, viewportWidth: width } = this.panelBBox; const { colCount, rowCount, trailingColCount, trailingRowCount } = this.getFrozenOptions(); const finalViewport = { width, height, x: 0, y: 0, }; if (colCount > 0 || trailingColCount > 0) { finalViewport.width -= this.frozenGroupAreas[FrozenGroupArea.Col].width + this.frozenGroupAreas[FrozenGroupArea.TrailingCol].width; finalViewport.x += this.frozenGroupAreas[FrozenGroupArea.Col].width; } if (rowCount > 0 || trailingRowCount > 0) { // canvas 高度小于 row height 和 trailingRow height 的时候 height 为 0 if (finalViewport.height < this.frozenGroupAreas[FrozenGroupArea.Row].height + this.frozenGroupAreas[FrozenGroupArea.TrailingRow].height) { finalViewport.height = 0; finalViewport.y = 0; } else { finalViewport.height -= this.frozenGroupAreas[FrozenGroupArea.Row].height + this.frozenGroupAreas[FrozenGroupArea.TrailingRow].height; finalViewport.y += this.frozenGroupAreas[FrozenGroupArea.Row].height; } } return finalViewport; } calculateXYIndexes(scrollX, scrollY) { var _a, _b; const finalViewport = this.getFinalViewport(); const indexes = this.spreadsheet.isTableMode() && ((_b = (_a = this.spreadsheet.dataSet) === null || _a === void 0 ? void 0 : _a.isEmpty) === null || _b === void 0 ? void 0 : _b.call(_a)) ? this.spreadsheet.dataSet.getEmptyViewIndexes() : calculateInViewIndexes({ scrollX, scrollY, widths: this.viewCellWidths, heights: this.viewCellHeights, viewport: finalViewport, rowRemainWidth: this.getRealScrollX(this.cornerBBox.width), }); this.panelScrollGroupIndexes = indexes; const colLength = this.getColLeafNodes().length; const cellRange = this.getCellRange(); return splitInViewIndexesWithFrozen(indexes, this.getFrozenOptions(), colLength, cellRange); } updateFrozenGroupGrid() { [ FrozenGroupArea.Col, FrozenGroupArea.Row, FrozenGroupArea.TrailingCol, FrozenGroupArea.TrailingRow, ].forEach((key) => { if (!this.frozenGroupAreas[key].range) { return; } let cols = []; let rows = []; if (key.toLowerCase().includes('row')) { const [rowMin, rowMax] = this.frozenGroupAreas[key].range || []; cols = this.gridInfo.cols; rows = getRowsForGrid(rowMin, rowMax, this.viewCellHeights); if (key === FrozenGroupArea.TrailingRow) { const top = this.frozenGroupAreas[FrozenGroupArea.TrailingRow].y; rows = getFrozenRowsForGrid(rowMin, rowMax, top, this.viewCellHeights); } } else { const [colMin, colMax] = this.frozenGroupAreas[key].range || []; const nodes = this.getColLeafNodes(); cols = getColsForGrid(colMin, colMax, nodes); rows = this.gridInfo.rows; } const frozenGroup = FrozenGroupAreaTypeMap[key]; this.frozenGroups[frozenGroup].updateGrid({ cols, rows, }, frozenGroup); }); } updatePanelScrollGroup() { super.updatePanelScrollGroup(); this.updateFrozenGroupGrid(); } translateRelatedGroups(scrollX, scrollY, hRowScroll) { super.translateRelatedGroups(scrollX, scrollY, hRowScroll); this.translateFrozenGroups(); this.renderRowResizeArea(); this.renderFrozenGroupSplitLine(scrollX, scrollY); } renderRowResizeArea() { } getFrozenColSplitLineSize() { const { viewportHeight, y: panelBBoxStartY } = this.panelBBox; const height = viewportHeight + panelBBoxStartY; return { y: 0, height, }; } renderFrozenColSplitLine(splitLineGroup, splitLine, verticalBorderStyle, scrollX) { const { colCount } = this.getFrozenOptions(); const { x: panelBBoxStartX } = this.panelBBox; if (colCount > 0) { const cornerWidth = this.cornerBBox.width; const colOffset = getFrozenColOffset(this, cornerWidth, scrollX); const x = panelBBoxStartX + this.frozenGroupAreas[FrozenGroupArea.Col].width - colOffset; const { y, height } = this.getFrozenColSplitLineSize(); renderLine(splitLineGroup, Object.assign(Object.assign({}, verticalBorderStyle), { x1: x, x2: x, y1: y, y2: y + height })); if ((splitLine === null || splitLine === void 0 ? void 0 : splitLine.showShadow) && scrollX > 0 && (this.spreadsheet.isFrozenRowHeader() || colOffset >= cornerWidth)) { splitLineGroup.appendChild(new Rect({ style: { x, y, width: splitLine === null || splitLine === void 0 ? void 0 : splitLine.shadowWidth, height, fill: this.getShadowFill(0), }, })); } } } renderFrozenTrailingColSplitLine(splitLineGroup, splitLine, verticalBorderStyle, scrollX) { const { trailingColCount } = this.getFrozenOptions(); const { viewportWidth, x: panelBBoxStartX } = this.panelBBox; if (trailingColCount > 0) { const x = viewportWidth - this.frozenGroupAreas[FrozenGroupArea.TrailingCol].width + panelBBoxStartX; const { y, height } = this.getFrozenColSplitLineSize(); const maxScrollX = Math.max(0, last(this.viewCellWidths) - viewportWidth); renderLine(splitLineGroup, Object.assign(Object.assign({}, verticalBorderStyle), { x1: x, x2: x, y1: y, y2: y + height })); if ((splitLine === null || splitLine === void 0 ? void 0 : splitLine.showShadow) && floor(scrollX) < floor(maxScrollX)) { splitLineGroup.appendChild(new Rect({ style: { x: x - splitLine.shadowWidth, y, width: splitLine.shadowWidth, height, fill: this.getShadowFill(180), }, })); } } } getFrozenRowSplitLineSize() { const { viewportWidth, x: panelBBoxStartX } = this.panelBBox; const width = panelBBoxStartX + viewportWidth; return { x: 0, width, }; } renderFrozenRowSplitLine(splitLineGroup, splitLine, horizontalBorderStyle, scrollY) { const { rowCount } = this.getFrozenOptions(); const { y: panelBBoxStartY } = this.panelBBox; if (rowCount > 0) { const y = panelBBoxStartY + this.frozenGroupAreas[FrozenGroupArea.Row].height; const { x, width } = this.getFrozenRowSplitLineSize(); renderLine(splitLineGroup, Object.assign(Object.assign({}, horizontalBorderStyle), { x1: x, x2: x + width, y1: y, y2: y })); if ((splitLine === null || splitLine === void 0 ? void 0 : splitLine.showShadow) && scrollY > 0) { splitLineGroup.appendChild(new Rect({ style: { x, y, width, height: splitLine === null || splitLine === void 0 ? void 0 : splitLine.shadowWidth, fill: this.getShadowFill(90), }, })); } } } renderFrozenTrailingRowSplitLine(splitLineGroup, splitLine, horizontalBorderStyle, scrollY) { const { trailingRowCount } = this.getFrozenOptions(); const { viewportHeight } = this.panelBBox; if (trailingRowCount > 0) { const y = this.panelBBox.maxY - this.frozenGroupAreas[FrozenGroupArea.TrailingRow].height; const { x, width } = this.getFrozenRowSplitLineSize(); const cellRange = this.getCellRange(); // scroll boundary const maxScrollY = Math.max(0, this.viewCellHeights.getCellOffsetY(cellRange.end + 1) - this.viewCellHeights.getCellOffsetY(cellRange.start) - viewportHeight); renderLine(splitLineGroup, Object.assign(Object.assign({}, horizontalBorderStyle), { x1: x, x2: x + width, y1: y, y2: y })); if ((splitLine === null || splitLine === void 0 ? void 0 : splitLine.showShadow) && scrollY < floor(maxScrollY)) { splitLineGroup.appendChild(new Rect({ style: { x, y: y - splitLine.shadowWidth, width, height: splitLine.shadowWidth, fill: this.getShadowFill(270), }, })); } } } render() { if (!this.shouldRender()) { return; } this.calculateFrozenGroupInfo(); this.renderFrozenPanelCornerGroup(); super.render(); } getCenterFrameScrollX(scrollX) { if (this.getFrozenOptions().colCount > 0) { return getFrozenColOffset(this, this.cornerBBox.width, scrollX); } return super.getCenterFrameScrollX(scrollX); } getDefaultCellHeight() { var _a; return (_a = this.getRowCellHeight(null)) !== null && _a !== void 0 ? _a : 0; } clip() { const { scrollX } = this.getScrollOffset(); const { x: panelScrollGroupClipX, width: panelScrollGroupClipWidth } = getScrollGroupClip(this, this.panelBBox); const frozenColGroupWidth = this.frozenGroupAreas[FrozenGroupArea.Col].width; const frozenTrailingColWidth = this.frozenGroupAreas[FrozenGroupArea.TrailingCol].width; const frozenRowGroupHeight = this.frozenGroupAreas[FrozenGroupArea.Row].height; const frozenTrailingRowHeight = this.frozenGroupAreas[FrozenGroupArea.TrailingRow].height; const panelScrollGroupClipY = this.panelBBox.y + frozenRowGroupHeight; const panelScrollGroupClipHeight = this.panelBBox.viewportHeight - frozenRowGroupHeight - frozenTrailingRowHeight; this.panelScrollGroup.style.clipPath = new Rect({ style: { x: panelScrollGroupClipX, y: panelScrollGroupClipY, width: panelScrollGroupClipWidth, height: panelScrollGroupClipHeight, }, }); /* frozen groups clip */ this.frozenGroups[FrozenGroupType.Col].style.clipPath = new Rect({ style: { x: this.panelBBox.x - getFrozenColOffset(this, this.cornerBBox.width, scrollX), y: panelScrollGroupClipY, width: frozenColGroupWidth, height: panelScrollGroupClipHeight, }, }); this.frozenGroups[FrozenGroupType.TrailingCol].style.clipPath = new Rect({ style: { x: this.panelBBox.x + this.panelBBox.viewportWidth - frozenTrailingColWidth, y: panelScrollGroupClipY, width: frozenTrailingColWidth, height: panelScrollGroupClipHeight, }, }); this.frozenGroups[FrozenGroupType.Row].style.clipPath = new Rect({ style: { x: panelScrollGroupClipX, y: this.panelBBox.y, width: panelScrollGroupClipWidth, height: frozenRowGroupHeight, }, }); this.frozenGroups[FrozenGroupType.TrailingRow].style.clipPath = new Rect({ style: { x: panelScrollGroupClipX, y: this.panelBBox.y + this.panelBBox.viewportHeight - frozenTrailingRowHeight, width: panelScrollGroupClipWidth, height: frozenTrailingRowHeight, }, }); } } //# sourceMappingURL=frozen-facet.js.map