UNPKG

@antv/s2

Version:

effective spreadsheet render core lib

419 lines 18.8 kB
import { isEmpty } from 'lodash'; import { CellType, FrozenGroupArea, HORIZONTAL_RESIZE_AREA_KEY_PRE, KEY_GROUP_COL_RESIZE_AREA, ResizeAreaEffect, ResizeDirectionType, S2Event, SPLIT_LINE_WIDTH, } from '../common/constant'; import { CellBorderPosition, CellClipBox, } from '../common/interface'; import { CustomRect } from '../engine'; import { Frame } from '../facet/header/frame'; import { getHorizontalTextIconPosition, getVerticalIconPosition, getVerticalTextPosition, } from '../utils/cell/cell'; import { adjustTextIconPositionWhileScrolling } from '../utils/cell/text-scrolling'; import { renderIcon, renderLine } from '../utils/g-renders'; import { getHiddenColumnContinuousSiblingNodes, isEqualDisplaySiblingNodeId, isLastColumnAfterHidden, } from '../utils/hide-columns'; import { getOrCreateResizeAreaGroupById, getResizeAreaAttrs, shouldAddResizeArea, } from '../utils/interaction/resize'; import { normalizeTextAlign } from '../utils/normalize'; import { HeaderCell } from './header-cell'; export class ColCell extends HeaderCell { get cellType() { return CellType.COL_CELL; } getBorderPositions() { return [CellBorderPosition.TOP, CellBorderPosition.RIGHT]; } initCell() { super.initCell(); // 1、draw rect background this.drawBackgroundShape(); // interactive background shape this.drawInteractiveBgShape(); // interactive cell border shape this.drawInteractiveBorderShape(); // draw text this.drawTextOrCustomRenderer(); } afterDrawText() { // 绘制字段标记 -- icon this.drawActionAndConditionIcons(); // draw borders this.drawBorders(); // draw resize ares this.drawResizeArea(); this.addExpandColumnIconShapes(); this.update(); } getFormattedFieldValue() { var _a; const { extra, value, field } = this.meta; const { fields } = this.spreadsheet.dataSet; // 列头对应的数值标题不应该格式化 const isCustomValueFieldNode = (extra === null || extra === void 0 ? void 0 : extra.isCustomNode) && ((_a = fields === null || fields === void 0 ? void 0 : fields.values) === null || _a === void 0 ? void 0 : _a.includes(field)); if (isCustomValueFieldNode) { return { formattedValue: value, value, }; } return super.getFormattedFieldValue(); } getMaxTextWidth() { const { width } = this.getBBoxByType(CellClipBox.CONTENT_BOX); return width - this.getActionAndConditionIconWidth(); } isBolderText() { // 非叶子节点、小计总计,均为粗体 const { isLeaf, isTotals } = this.meta; if (isTotals || !isLeaf) { return true; } return false; } /** * 计算文本位置时候需要,留给后代根据情况(固定列)覆盖 * @param viewport * @returns viewport */ handleViewport() { if (this.meta.isFrozen) { return { start: 0, size: Number.POSITIVE_INFINITY, }; } const { viewportWidth, cornerWidth = 0, scrollX = 0, } = this.getHeaderConfig(); const frozenGroupAreas = this.spreadsheet.facet .frozenGroupAreas; const frozenColGroupWidth = frozenGroupAreas[FrozenGroupArea.Col].width; const frozenTrailingColGroupWidth = frozenGroupAreas[FrozenGroupArea.TrailingCol].width; if (this.spreadsheet.isFrozenRowHeader()) { return { start: scrollX + frozenColGroupWidth, size: viewportWidth - frozenColGroupWidth - frozenTrailingColGroupWidth, }; } return { start: frozenColGroupWidth + Math.max(0, scrollX - cornerWidth), size: viewportWidth - frozenColGroupWidth - frozenTrailingColGroupWidth + Math.min(scrollX, cornerWidth), }; } getContentPosition({ contentWidth = this.getActualTextWidth(), } = {}) { var _a, _b, _c; const { isLeaf } = this.meta; const textStyle = this.getTextStyle(); const contentBox = this.getBBoxByType(CellClipBox.CONTENT_BOX); const iconStyle = this.getIconStyle(); const textY = getVerticalTextPosition(contentBox, textStyle.textBaseline); const iconY = getVerticalIconPosition(iconStyle.size, textY, textStyle.fontSize, textStyle.textBaseline); if (isLeaf) { const { textX, leftIconX, rightIconX } = getHorizontalTextIconPosition({ bbox: contentBox, textWidth: contentWidth, textAlign: textStyle.textAlign, groupedIcons: this.groupedIcons, iconStyle, isCustomRenderer: !!this.getRenderer(), }); this.leftIconPosition = { x: leftIconX, y: iconY, }; this.rightIconPosition = { x: rightIconX, y: iconY, }; return { x: textX, y: textY }; } const viewport = this.handleViewport(); const { cell, icon } = this.getStyle(); const { textAlign, textBaseline } = this.getTextStyle(); const { textStart, iconStart, iconEnd } = adjustTextIconPositionWhileScrolling(viewport, { start: contentBox.x, size: contentBox.width }, { align: normalizeTextAlign(textAlign), size: { textSize: contentWidth, iconStartSize: this.getActionAndConditionIconWidth('left'), iconEndSize: this.getActionAndConditionIconWidth('right'), }, padding: { start: (_a = cell === null || cell === void 0 ? void 0 : cell.padding) === null || _a === void 0 ? void 0 : _a.left, end: (_b = cell === null || cell === void 0 ? void 0 : cell.padding) === null || _b === void 0 ? void 0 : _b.right, betweenTextAndEndIcon: (_c = icon === null || icon === void 0 ? void 0 : icon.margin) === null || _c === void 0 ? void 0 : _c.left, }, }, { isCustomRenderer: !!this.getRenderer(), }); const y = getVerticalTextPosition(contentBox, textBaseline); this.leftIconPosition = { x: iconStart, y: iconY, }; this.rightIconPosition = { x: iconEnd, y: iconY, }; return { x: textStart, y }; } getTextPosition() { return this.getContentPosition(); } getColResizeArea() { return getOrCreateResizeAreaGroupById(this.spreadsheet, KEY_GROUP_COL_RESIZE_AREA); } getHorizontalResizeAreaName() { return `${HORIZONTAL_RESIZE_AREA_KEY_PRE}${this.meta.field}`; } /** * @description 叶子节点, 但层级不同于其他节点 (如下图 a-1-1), 说明是任意不规则自定义节点, 此时不需要绘制热区 * -------------------------------------------------- * | 自定义节点 a-1 | | * |------------- |-----------| 自定义节点 a-1-1 | * | a-1-1 | a-1-2 | a-1-3 | | * ------------------------------------------------- */ isCrossColumnLeafNode() { var _a; const { colsHierarchy } = this.spreadsheet.facet.getLayoutResult(); const { level, isLeaf } = this.meta; return ((_a = colsHierarchy === null || colsHierarchy === void 0 ? void 0 : colsHierarchy.sampleNodeForLastLevel) === null || _a === void 0 ? void 0 : _a.level) !== level && isLeaf; } drawHorizontalResizeArea() { var _a, _b; // 隐藏列头时不绘制水平热区 https://github.com/antvis/S2/issues/1603 const isHiddenCol = ((_b = (_a = this.spreadsheet.options.style) === null || _a === void 0 ? void 0 : _a.colCell) === null || _b === void 0 ? void 0 : _b.height) === 0; if (isHiddenCol || !this.shouldDrawResizeAreaByType('colCellVertical', this)) { return; } const { y, height } = this.meta; const { position } = this.getHeaderConfig(); const resizeStyle = this.getResizeAreaStyle(); const resizeArea = this.getColResizeArea(); if (!resizeArea || this.isCrossColumnLeafNode()) { return; } const resizeAreaName = this.getHorizontalResizeAreaName(); const existedHorizontalResizeArea = resizeArea === null || resizeArea === void 0 ? void 0 : resizeArea.find((element) => element.name === resizeAreaName); // 如果已经绘制当前列高调整热区热区,则不再绘制 if (existedHorizontalResizeArea) { return; } const offsetY = position.y + y; const resizeAreaWidth = this.getResizeAreaWidth(); // 列高调整热区 const attrs = getResizeAreaAttrs({ theme: resizeStyle, type: ResizeDirectionType.Vertical, effect: ResizeAreaEffect.Field, offsetX: 0, offsetY, width: resizeAreaWidth, height, meta: this.meta, cell: this, }); resizeArea.appendChild(new CustomRect({ name: resizeAreaName, style: Object.assign(Object.assign({}, attrs.style), { x: 0, y: offsetY + height - resizeStyle.size, width: resizeAreaWidth }), }, attrs.appendInfo)); } getResizeAreaWidth() { const { cornerWidth = 0, viewportWidth: headerWidth } = this.getHeaderConfig(); return (Frame.getVerticalBorderWidth(this.spreadsheet) + cornerWidth + headerWidth); } shouldAddVerticalResizeArea() { if (this.getMeta().isFrozen) { return true; } const { x, y, width, height } = this.meta; const { scrollX = 0, scrollY, cornerWidth = 0, height: headerHeight, width: headerWidth, } = this.getHeaderConfig(); const resizeStyle = this.getResizeAreaStyle(); const resizeAreaBBox = { x: x + width - resizeStyle.size, y, width: resizeStyle.size, height, }; const frozenGroupAreas = this.spreadsheet.facet .frozenGroupAreas; const colWidth = frozenGroupAreas[FrozenGroupArea.Col].width; const trailingColWidth = frozenGroupAreas[FrozenGroupArea.TrailingCol].width; let resizeClipAreaBBox; if (this.spreadsheet.isFrozenRowHeader()) { resizeClipAreaBBox = { x: colWidth, y: 0, width: headerWidth - colWidth - trailingColWidth, height: headerHeight, }; } else { resizeClipAreaBBox = { x: colWidth - cornerWidth, y: 0, width: headerWidth - colWidth - trailingColWidth + cornerWidth, height: headerHeight, }; } return shouldAddResizeArea(resizeAreaBBox, resizeClipAreaBBox, { scrollX, scrollY, }); } getVerticalResizeAreaOffset() { const { x, y } = this.meta; const { scrollX = 0, position, cornerWidth = 0, viewportWidth, } = this.getHeaderConfig(); const isFrozenRowHeader = this.spreadsheet.isFrozenRowHeader(); const frozenGroupAreas = this.spreadsheet.facet .frozenGroupAreas; const frozenColGroup = frozenGroupAreas[FrozenGroupArea.Col]; const frozenTrailingColGroup = frozenGroupAreas[FrozenGroupArea.TrailingCol]; let offsetX = position === null || position === void 0 ? void 0 : position.x; if (this.getMeta().isFrozenHead) { offsetX += x - frozenColGroup.x - (isFrozenRowHeader ? 0 : Math.min(scrollX, cornerWidth)); } else if (this.getMeta().isFrozenTrailing) { offsetX += x - frozenTrailingColGroup.x + viewportWidth - frozenTrailingColGroup.width; } else { offsetX += x - scrollX; } return { x: offsetX, y: (position === null || position === void 0 ? void 0 : position.y) + y, }; } drawVerticalResizeArea() { if (!this.meta.isLeaf || this.meta.hideColCellHorizontalResize || !this.shouldDrawResizeAreaByType('colCellHorizontal', this)) { return; } const { width, height } = this.meta; const resizeStyle = this.getResizeAreaStyle(); const resizeArea = this.getColResizeArea(); if (!resizeArea || !this.shouldAddVerticalResizeArea()) { return; } const { x: offsetX, y: offsetY } = this.getVerticalResizeAreaOffset(); /* * 列宽调整热区 * 基准线是根据 container 坐标来的,因此把热区画在 container */ const attrs = getResizeAreaAttrs({ theme: resizeStyle, type: ResizeDirectionType.Horizontal, effect: ResizeAreaEffect.Cell, offsetX, offsetY, width, height, meta: this.meta, cell: this, }); resizeArea.appendChild(new CustomRect({ style: Object.assign(Object.assign({}, attrs.style), { x: offsetX + width - resizeStyle.size, y: offsetY, height }), }, attrs.appendInfo)); } // 绘制热区 drawResizeArea() { this.drawHorizontalResizeArea(); this.drawVerticalResizeArea(); } hasHiddenColumnCell() { const { interaction } = this.spreadsheet.options; const hiddenColumnsDetail = this.spreadsheet.store.get('hiddenColumnsDetail', []); if (isEmpty(hiddenColumnsDetail) || isEmpty(interaction === null || interaction === void 0 ? void 0 : interaction.hiddenColumnFields)) { return false; } return !!hiddenColumnsDetail.find((column) => isEqualDisplaySiblingNodeId(column === null || column === void 0 ? void 0 : column.displaySiblingNode, this.meta.id, this.isLastColumn() ? 'prev' : 'next')); } getExpandIconTheme() { const themeCfg = this.getStyle(); return themeCfg.icon; } addExpandColumnSplitLine() { const { x, y, width, height } = this.getBBoxByType(); const { horizontalBorderColor, horizontalBorderColorOpacity } = this.theme.splitLine; const lineX = this.isLastColumn() ? x + width : x; renderLine(this, { x1: lineX, y1: y, x2: lineX, y2: y + height, stroke: horizontalBorderColor, lineWidth: SPLIT_LINE_WIDTH, strokeOpacity: horizontalBorderColorOpacity, }); } addExpandColumnIconShapes() { if (!this.hasHiddenColumnCell()) { return; } this.addExpandColumnSplitLine(); this.addExpandColumnIcons(); } addExpandColumnIcons() { const isLastColumn = this.isLastColumn(); this.addExpandColumnIcon(isLastColumn); // 如果当前节点的兄弟节点 (前/后) 都被隐藏了, 隐藏后当前节点变为最后一个节点, 需要渲染两个展开按钮, 一个展开[前], 一个展开[后] if (this.isAllDisplaySiblingNodeHidden() && isLastColumn) { this.addExpandColumnIcon(false); } } addExpandColumnIcon(isLastColumn) { const iconConfig = this.getExpandColumnIconConfig(isLastColumn); const icon = renderIcon(this, Object.assign(Object.assign({}, iconConfig), { name: 'ExpandColIcon', cursor: 'pointer' })); icon.addEventListener('click', () => { this.spreadsheet.emit(S2Event.COL_CELL_EXPANDED, this.meta, isLastColumn ? 'prev' : 'next'); }); icon.addEventListener('mouseenter', (event) => { this.spreadsheet.emit(S2Event.COL_CELL_EXPAND_ICON_HOVER, { event, meta: this.meta, hiddenColumns: getHiddenColumnContinuousSiblingNodes(this.spreadsheet, this.meta.id, isLastColumn ? 'prev' : 'next'), }); }); } // 在隐藏的下一个兄弟节点的起始坐标显示隐藏提示线和展开按钮, 如果是尾元素, 则显示在前一个兄弟节点的结束坐标 getExpandColumnIconConfig(isLastColumn) { const { size = 0 } = this.getExpandIconTheme(); const { x, y, width, height } = this.getBBoxByType(); const baseIconX = x - size; const iconX = isLastColumn ? baseIconX + width : baseIconX; const iconY = y + height / 2 - size / 2; return { x: iconX, y: iconY, width: size * 2, height: size, }; } isLastColumn() { return isLastColumnAfterHidden(this.spreadsheet, this.meta.id); } isAllDisplaySiblingNodeHidden() { const { id } = this.meta; const lastHiddenColumnDetail = this.spreadsheet.store.get('hiddenColumnsDetail', []); const isPrevSiblingNodeHidden = lastHiddenColumnDetail.find(({ displaySiblingNode }) => { var _a; return ((_a = displaySiblingNode === null || displaySiblingNode === void 0 ? void 0 : displaySiblingNode.next) === null || _a === void 0 ? void 0 : _a.id) === id; }); const isNextSiblingNodeHidden = lastHiddenColumnDetail.find(({ displaySiblingNode }) => { var _a; return ((_a = displaySiblingNode === null || displaySiblingNode === void 0 ? void 0 : displaySiblingNode.prev) === null || _a === void 0 ? void 0 : _a.id) === id; }); return isNextSiblingNodeHidden && isPrevSiblingNodeHidden; } /** * 以下场景根据当前高度动态计算 maxLines, 保证文本展示合理性 * 1.手动拖拽 2.预设高度 */ getResizedTextMaxLines() { var _a, _b, _c, _d, _e; const { colCell } = this.spreadsheet.options.style; return ((_d = (_b = (_a = colCell === null || colCell === void 0 ? void 0 : colCell.maxLinesByField) === null || _a === void 0 ? void 0 : _a[this.meta.id]) !== null && _b !== void 0 ? _b : (_c = colCell === null || colCell === void 0 ? void 0 : colCell.maxLinesByField) === null || _c === void 0 ? void 0 : _c[this.meta.field]) !== null && _d !== void 0 ? _d : this.getMaxLinesByCustomHeight({ isCustomHeight: (_e = this.meta.extra) === null || _e === void 0 ? void 0 : _e.isCustomHeight, })); } } //# sourceMappingURL=col-cell.js.map