UNPKG

@antv/s2

Version:

effective spreadsheet render core lib

514 lines 24.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TableFacet = void 0; const tslib_1 = require("tslib"); const g_1 = require("@antv/g"); const lodash_1 = require("lodash"); const cell_1 = require("../cell"); const common_1 = require("../common"); const constant_1 = require("../common/constant"); const debug_1 = require("../common/debug"); const utils_1 = require("../utils"); const data_cell_1 = require("../utils/cell/data-cell"); const table_col_cell_1 = require("../utils/cell/table-col-cell"); 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 corner_bbox_1 = require("./bbox/corner-bbox"); const frozen_facet_1 = require("./frozen-facet"); const header_1 = require("./header"); const table_col_1 = require("./header/table-col"); const build_header_hierarchy_1 = require("./layout/build-header-hierarchy"); const hierarchy_1 = require("./layout/hierarchy"); const layout_hooks_1 = require("./layout/layout-hooks"); const node_1 = require("./layout/node"); class TableFacet extends frozen_facet_1.FrozenFacet { constructor(spreadsheet) { super(spreadsheet); this.onSortHandler = (sortParams) => tslib_1.__awaiter(this, void 0, void 0, function* () { const s2 = this.spreadsheet; let params = sortParams; // 兼容之前 sortParams 为对象的用法 if (!Array.isArray(sortParams)) { params = [sortParams]; } const currentParams = s2.dataCfg.sortParams || []; params = params.map((item) => { var _a, _b; const newItem = Object.assign(Object.assign({}, item), { // 兼容之前 sortKey 的用法 sortFieldId: (_a = item.sortKey) !== null && _a !== void 0 ? _a : item.sortFieldId }); const oldItem = (_b = currentParams.find((p) => p.sortFieldId === newItem.sortFieldId)) !== null && _b !== void 0 ? _b : {}; return Object.assign(Object.assign({}, oldItem), newItem); }); const oldConfigs = currentParams.filter((config) => { const newItem = params.find((p) => p.sortFieldId === config.sortFieldId); if (newItem) { return false; } return true; }); (0, lodash_1.set)(s2.dataCfg, 'sortParams', [...oldConfigs, ...params]); s2.setDataCfg(s2.dataCfg); yield s2.render(true); s2.emit(constant_1.S2Event.RANGE_SORTED, s2.dataSet.getDisplayDataSet()); }); this.onFilterHandler = (params) => tslib_1.__awaiter(this, void 0, void 0, function* () { const s2 = this.spreadsheet; const unFilter = !params.filteredValues || params.filteredValues.length === 0; const oldConfig = s2.dataCfg.filterParams || []; // check whether filter condition already exists on column, if so, modify it instead. const oldIndex = oldConfig.findIndex((item) => item.filterKey === params.filterKey); if (oldIndex !== -1) { if (unFilter) { // remove filter params on current key if passed an empty filterValues field oldConfig.splice(oldIndex, 1); } else { // if filter with same key already exists, replace it oldConfig[oldIndex] = params; } } else { oldConfig.push(params); } (0, lodash_1.set)(s2.dataCfg, 'filterParams', oldConfig); yield s2.render(true); s2.emit(constant_1.S2Event.RANGE_FILTERED, s2.dataSet.getDisplayDataSet()); }); this.spreadsheet.on(constant_1.S2Event.RANGE_SORT, this.onSortHandler); this.spreadsheet.on(constant_1.S2Event.RANGE_FILTER, this.onFilterHandler); } getCornerCellInstance() { return null; } getRowCellInstance(node) { const { dataCell } = this.spreadsheet.options; return ((dataCell === null || dataCell === void 0 ? void 0 : dataCell(node, this.spreadsheet)) || new cell_1.TableDataCell(node, this.spreadsheet)); } getColCellInstance(...args) { const { colCell } = this.spreadsheet.options; return (colCell === null || colCell === void 0 ? void 0 : colCell(...args)) || new cell_1.TableColCell(...args); } initGroups() { super.initGroups(); this.initEmptyPlaceholderGroup(); } shouldRender() { var _a; const { fields } = this.spreadsheet.dataSet; const isOnlyContainedSeriesNumber = (_a = fields === null || fields === void 0 ? void 0 : fields.columns) === null || _a === void 0 ? void 0 : _a.every((field) => field === constant_1.SERIES_NUMBER_FIELD); return super.shouldRender() && !isOnlyContainedSeriesNumber; } render() { if (!this.shouldRender()) { return; } super.render(); this.renderEmptyPlaceholder(); } clearAllGroup() { super.clearAllGroup(); this.emptyPlaceholderGroup.remove(); } initEmptyPlaceholderGroup() { this.emptyPlaceholderGroup = this.spreadsheet.container.appendChild(new g_1.Group({ name: constant_1.KEY_GROUP_EMPTY_PLACEHOLDER, style: { zIndex: constant_1.EMPTY_PLACEHOLDER_GROUP_CONTAINER_Z_INDEX }, })); } renderEmptyPlaceholder() { var _a, _b, _c; if (!((_a = this.spreadsheet.dataSet) === null || _a === void 0 ? void 0 : _a.isEmpty())) { return; } const { empty } = this.spreadsheet.options.placeholder; const { background } = this.spreadsheet.theme; const { icon, description } = this.spreadsheet.theme.empty; const { horizontalBorderWidth, horizontalBorderColor, horizontalBorderColorOpacity, } = this.spreadsheet.theme.dataCell.cell; const { maxY, viewportWidth, height } = this.panelBBox; const iconX = viewportWidth / 2 - icon.width / 2; const iconY = height / 2 + maxY - icon.height / 2 + icon.margin.top; const text = (_b = empty === null || empty === void 0 ? void 0 : empty.description) !== null && _b !== void 0 ? _b : (0, common_1.i18n)('暂无数据'); const descWidth = this.measureTextWidth(text, description); const descX = viewportWidth / 2 - descWidth / 2; const descY = iconY + icon.height + icon.margin.bottom; // 边框 const border = new g_1.Rect({ style: { x: 0, y: maxY, width: viewportWidth, height, stroke: horizontalBorderColor, strokeWidth: horizontalBorderWidth, strokeOpacity: horizontalBorderColorOpacity, fill: background === null || background === void 0 ? void 0 : background.color, fillOpacity: (_c = background === null || background === void 0 ? void 0 : background.opacity) !== null && _c !== void 0 ? _c : 1, }, }); this.emptyPlaceholderGroup.appendChild(border); // 空状态 Icon (0, utils_1.renderIcon)(this.emptyPlaceholderGroup, Object.assign(Object.assign({}, icon), { name: empty === null || empty === void 0 ? void 0 : empty.icon, x: iconX, y: iconY, width: icon.width, height: icon.height })); // 空状态描述文本 (0, utils_1.renderText)({ group: this.emptyPlaceholderGroup, style: Object.assign(Object.assign({}, description), { text, x: descX, y: descY }), }); this.emptyPlaceholderGroup.toFront(); } getDataCellAdaptiveHeight(viewMeta) { const node = { id: String(viewMeta === null || viewMeta === void 0 ? void 0 : viewMeta.rowIndex) }; const rowHeight = this.getRowCellHeight(node); if (this.isCustomRowCellHeight(node)) { // 标记当前行是否为自定义高度 this.customRowHeightStatusMap[viewMeta === null || viewMeta === void 0 ? void 0 : viewMeta.rowIndex] = true; return rowHeight || 0; } const defaultHeight = this.getCellHeightByRowIndex(viewMeta === null || viewMeta === void 0 ? void 0 : viewMeta.rowIndex); return this.getNodeAdaptiveHeight({ meta: viewMeta, cell: this.textWrapTempRowCell, defaultHeight, }); } getCellHeightByRowIndex(rowIndex) { var _a; if (this.rowOffsets) { return (_a = this.getRowCellHeight({ id: String(rowIndex) })) !== null && _a !== void 0 ? _a : 0; } return this.getDefaultCellHeight(); } /** * 开启换行后, 需要自适应调整高度, 明细表通过 rowCell.heightByField 调整, 同时还有一个 rowOffsets 控制行高, 所以要提前设置好, 保证渲染正确. */ presetRowCellHeightIfNeeded(rowIndex) { var _a; const { style } = this.spreadsheet.options; const colLeafNodes = this.getColLeafNodes(); // 不超过一行或者用户已经配置过当前行高则无需预设 if ((0, lodash_1.isEmpty)(colLeafNodes) || ((_a = style === null || style === void 0 ? void 0 : style.dataCell) === null || _a === void 0 ? void 0 : _a.maxLines) <= 1) { return; } // 当前行高取整行 dataCell 高度最大的值 const maxDataCellHeight = (0, lodash_1.max)(colLeafNodes.map((colNode) => { const viewMeta = this.getCellMeta(rowIndex, colNode.colIndex); return this.getDataCellAdaptiveHeight(viewMeta); })); // getCellHeightByRowIndex 会优先读取 heightByField, 保持逻辑统一 const height = maxDataCellHeight || this.getDefaultCellHeight(); (0, lodash_1.set)(this.spreadsheet.options, `style.rowCell.heightByField.${rowIndex}`, height); } calculateRowOffsets() { var _a, _b, _c; const { style } = this.spreadsheet.options; const heightByField = (_a = style === null || style === void 0 ? void 0 : style.rowCell) === null || _a === void 0 ? void 0 : _a.heightByField; const isEnableHeightAdaptive = ((_b = style === null || style === void 0 ? void 0 : style.dataCell) === null || _b === void 0 ? void 0 : _b.maxLines) > 1 && ((_c = style === null || style === void 0 ? void 0 : style.dataCell) === null || _c === void 0 ? void 0 : _c.wordWrap); if ((0, lodash_1.keys)(heightByField).length || isEnableHeightAdaptive) { const data = this.spreadsheet.dataSet.getDisplayDataSet(); this.textWrapNodeHeightCache.clear(false); this.customRowHeightStatusMap = {}; this.rowOffsets = [0]; this.lastRowOffset = 0; data.forEach((_, rowIndex) => { this.presetRowCellHeightIfNeeded(rowIndex); const currentHeight = this.getCellHeightByRowIndex(rowIndex); const currentOffset = this.lastRowOffset + currentHeight; this.rowOffsets.push(currentOffset); this.lastRowOffset = currentOffset; }); } } destroy() { super.destroy(); this.spreadsheet.off(constant_1.S2Event.RANGE_SORT, this.onSortHandler); this.spreadsheet.off(constant_1.S2Event.RANGE_FILTER, this.onFilterHandler); } calculateCornerBBox() { const { colsHierarchy } = this.getLayoutResult(); const height = (0, math_1.floor)(colsHierarchy.height); this.cornerBBox = new corner_bbox_1.CornerBBox(this); this.cornerBBox.height = height; this.cornerBBox.maxY = height; } doLayout() { const rowsHierarchy = new hierarchy_1.Hierarchy(); const { colLeafNodes, colsHierarchy } = this.buildColHeaderHierarchy(); this.calculateColNodesCoordinate(colLeafNodes, colsHierarchy); return { colNodes: colsHierarchy.getNodes(), colLeafNodes, colsHierarchy, rowNodes: [], rowsHierarchy, rowLeafNodes: [], cornerNodes: [], }; } buildColHeaderHierarchy() { const colHierarchy = (0, build_header_hierarchy_1.buildHeaderHierarchy)({ isRowHeader: false, spreadsheet: this.spreadsheet, }); return { colLeafNodes: colHierarchy.leafNodes, colsHierarchy: colHierarchy.hierarchy, }; } getCellMeta(rowIndex = 0, colIndex = 0) { var _a, _b, _c; const { options, dataSet } = this.spreadsheet; const colLeafNodes = this.getColLeafNodes(); const colNode = colLeafNodes[colIndex]; if (!colNode) { return null; } let data; const x = colNode.x; const y = this.viewCellHeights.getCellOffsetY(rowIndex); const cellHeight = this.getCellHeightByRowIndex(rowIndex); if (((_a = options.seriesNumber) === null || _a === void 0 ? void 0 : _a.enable) && colNode.field === constant_1.SERIES_NUMBER_FIELD) { data = rowIndex + 1; } else { data = dataSet.getCellData({ query: { field: colNode.field, rowIndex, }, }); } const valueField = colNode.field; const fieldValue = data; const rowQuery = { rowIndex }; const colQuery = { colIndex }; const cellMeta = { spreadsheet: this.spreadsheet, x, y, width: colNode.width, height: cellHeight, data: { [colNode.field]: data, }, rowIndex, colIndex, isTotals: false, colId: colNode.id, rowId: String(rowIndex), valueField, fieldValue, id: (0, data_cell_1.getDataCellId)(String(rowIndex), colNode.id), rowQuery, colQuery, query: Object.assign(Object.assign({}, rowQuery), colQuery), }; return (_c = (_b = options.layoutCellMeta) === null || _b === void 0 ? void 0 : _b.call(options, cellMeta)) !== null && _c !== void 0 ? _c : cellMeta; } getAdaptiveColWidth(colLeafNodes) { var _a; const { dataCell } = this.spreadsheet.options.style; const { seriesNumber } = this.spreadsheet.options; if (this.spreadsheet.getLayoutWidthType() !== constant_1.LayoutWidthType.Compact) { const seriesNumberWidth = this.getSeriesNumberWidth(); const colHeaderColSize = colLeafNodes.length - ((seriesNumber === null || seriesNumber === void 0 ? void 0 : seriesNumber.enable) ? 1 : 0); const canvasW = this.getCanvasSize().width - seriesNumberWidth - header_1.Frame.getVerticalBorderWidth(this.spreadsheet); return Math.max(dataCell === null || dataCell === void 0 ? void 0 : dataCell.width, (0, math_1.floor)(canvasW / Math.max(1, colHeaderColSize))); } return (0, math_1.round)((_a = dataCell === null || dataCell === void 0 ? void 0 : dataCell.width) !== null && _a !== void 0 ? _a : 0); } calculateColLeafNodesWidth(colLeafNodes, colsHierarchy) { let preLeafNode = node_1.Node.blankNode(); let currentCollIndex = 0; const adaptiveColWidth = this.getAdaptiveColWidth(colLeafNodes); colsHierarchy.getLeaves().forEach((currentNode) => { currentNode.colIndex = currentCollIndex; currentCollIndex += 1; currentNode.x = preLeafNode.x + preLeafNode.width; currentNode.width = this.getColLeafNodesWidth(currentNode, adaptiveColWidth); (0, layout_hooks_1.layoutCoordinate)(this.spreadsheet, null, currentNode); colsHierarchy.width += currentNode.width; preLeafNode = currentNode; }); } calculateColNodesHeight(colsHierarchy) { const colNodes = colsHierarchy.getNodes(); colNodes.forEach((colNode) => { var _a, _b; if (colNode.level === 0) { colNode.y = 0; } else { colNode.y = ((_a = colNode === null || colNode === void 0 ? void 0 : colNode.parent) === null || _a === void 0 ? void 0 : _a.y) + ((_b = colNode === null || colNode === void 0 ? void 0 : colNode.parent) === null || _b === void 0 ? void 0 : _b.height) || 0; } colNode.height = this.getColNodeHeight({ colNode, colsHierarchy, useCache: false, }); }); } calculateColNodesCoordinate(colLeafNodes, colsHierarchy) { // 先计算宽度, 再计算高度, 确保计算多行文本时能获取到正确的最大文本宽度 this.calculateColLeafNodesWidth(colLeafNodes, colsHierarchy); this.calculateColParentNodeWidthAndX(colLeafNodes); this.updateColsHierarchySampleMaxHeightNodes(colsHierarchy); this.calculateColNodesHeight(colsHierarchy); this.updateCustomFieldsSampleNodes(colsHierarchy); this.adjustCustomColLeafNodesHeight({ leafNodes: colLeafNodes, hierarchy: colsHierarchy, }); } getCompactColNodeWidth(colNode) { const { theme, dataSet } = this.spreadsheet; const { bolderText: colCellTextStyle } = theme.colCell; const { text: dataCellTextStyle, cell: cellStyle } = theme.dataCell; const data = dataSet.getDisplayDataSet(); const formatter = dataSet.getFieldFormatter(colNode.field); // 采样前 50,找出表身最长的数据 const maxLabel = (0, lodash_1.maxBy)(data === null || data === void 0 ? void 0 : data.slice(0, common_1.LAYOUT_SAMPLE_COUNT).map((data) => { var _a; return `${(_a = formatter === null || formatter === void 0 ? void 0 : formatter(data[colNode.field])) !== null && _a !== void 0 ? _a : data[colNode.field]}`; }), (label) => this.measureTextWidth(label, dataCellTextStyle)); debug_1.DebuggerUtil.getInstance().logger('Max Label In Col:', colNode.field, maxLabel); const maxLabelWidth = this.measureTextWidth(maxLabel, dataCellTextStyle) + cellStyle.padding.left + cellStyle.padding.right; // 计算表头 label+icon 占用的空间 const colHeaderNodeWidth = this.measureTextWidth(colNode.value, colCellTextStyle) + (0, table_col_cell_1.getOccupiedWidthForTableCol)(this.spreadsheet, colNode, theme.colCell); return (0, math_1.round)(Math.max(colHeaderNodeWidth, maxLabelWidth)); } getColLeafNodesWidth(colNode, adaptiveColWidth) { const { colCell } = this.spreadsheet.options.style; const layoutWidthType = this.spreadsheet.getLayoutWidthType(); 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 (colNode.field === constant_1.SERIES_NUMBER_FIELD) { return this.getSeriesNumberWidth(); } // 4. 紧凑模式 if (layoutWidthType === constant_1.LayoutWidthType.Compact) { return this.getCompactColNodeWidth(colNode); } // 5. 默认自适应列宽 return (0, math_1.round)(adaptiveColWidth); } getViewCellHeights() { const defaultCellHeight = this.getDefaultCellHeight(); return { getTotalHeight: () => { if (this.rowOffsets) { return (0, lodash_1.last)(this.rowOffsets) || 0; } return (defaultCellHeight * this.spreadsheet.dataSet.getDisplayDataSet().length); }, getCellOffsetY: (offset) => { if (offset <= 0) { return 0; } if (this.rowOffsets) { return this.rowOffsets[offset]; } return offset * defaultCellHeight; }, getTotalLength: () => this.spreadsheet.dataSet.getDisplayDataSet().length, getIndexRange: (minHeight, maxHeight) => { if (this.rowOffsets) { return (0, facet_1.getIndexRangeWithOffsets)(this.rowOffsets, minHeight, maxHeight); } const yMin = (0, math_1.floor)(minHeight / defaultCellHeight, 0); // 防止数组index溢出导致报错 const yMax = maxHeight % defaultCellHeight === 0 ? maxHeight / defaultCellHeight - 1 : (0, math_1.floor)(maxHeight / defaultCellHeight, 0); return { start: Math.max(0, yMin), end: Math.max(0, yMax), }; }, }; } renderRowResizeArea() { const { resize } = this.spreadsheet.options.interaction; const shouldDrawResize = (0, lodash_1.isBoolean)(resize) ? resize : resize === null || resize === void 0 ? void 0 : resize.rowCellVertical; if (!shouldDrawResize) { return; } const rowResizeGroup = this.foregroundGroup.getElementById(constant_1.KEY_GROUP_ROW_RESIZE_AREA); if (rowResizeGroup) { rowResizeGroup.removeChildren(); } const cells = (0, get_all_child_cells_1.getAllChildCells)(this.panelGroup.children, cell_1.TableDataCell); cells.forEach((cell) => { cell.drawResizeArea(); }); } getRowHeader() { return null; } getColHeader() { if (!this.columnHeader) { const { x, width, viewportHeight, viewportWidth } = this.panelBBox; return new table_col_1.TableColHeader({ width, height: this.cornerBBox.height, viewportWidth, viewportHeight, cornerWidth: this.cornerBBox.width, position: { x, y: 0 }, nodes: this.getColNodes(), sortParam: this.spreadsheet.store.get('sortParam'), spreadsheet: this.spreadsheet, }); } return this.columnHeader; } getSeriesNumberHeader() { return null; } getScrollbarPosition() { const { height } = this.getCanvasSize(); const position = super.getScrollbarPosition(); // 滚动条有两种模式, 一种是根据实际内容撑开, 一种是根据 Canvas 高度撑开, 现在有空数据占位符, 对于这种, 滚动条需要撑满 const maxY = this.spreadsheet.dataSet.isEmpty() ? height - this.scrollBarSize : position.maxY; return Object.assign(Object.assign({}, position), { maxY }); } /** * 获取序号单元格 * @description 明细表序号单元格是基于 DataCell 实现 */ getSeriesNumberCells() { return this.getDataCells().filter((cell) => { return cell.getMeta().valueField === constant_1.SERIES_NUMBER_FIELD; }); } getContentWidth() { const { colsHierarchy } = this.layoutResult; return (0, math_1.round)(colsHierarchy.width); } getContentHeight() { const { getTotalHeight } = this.getViewCellHeights(); const { colsHierarchy } = this.layoutResult; return (0, math_1.round)(getTotalHeight() + colsHierarchy.height + header_1.Frame.getHorizontalBorderWidth(this.spreadsheet)); } } exports.TableFacet = TableFacet; //# sourceMappingURL=table-facet.js.map