UNPKG

@antv/s2

Version:

effective spreadsheet render core lib

351 lines 14.3 kB
import { find, get, merge } from 'lodash'; import { CellType, FrozenGroupArea, KEY_GROUP_ROW_RESIZE_AREA, ResizeAreaEffect, ResizeDirectionType, S2Event, } from '../common/constant'; import { CellBorderPosition, CellClipBox, } from '../common/interface'; import { CustomRect } from '../engine'; import { getHorizontalTextIconPosition, getVerticalIconPosition, } from '../utils/cell/cell'; import { renderCircle, renderTreeIcon } from '../utils/g-renders'; import { getAllChildrenNodeHeight } from '../utils/get-all-children-node-height'; import { getOrCreateResizeAreaGroupById, getResizeAreaAttrs, shouldAddResizeArea, } from '../utils/interaction/resize'; import { isMobile } from '../utils/is-mobile'; import { adjustTextIconPositionWhileScrolling } from './../utils/cell/text-scrolling'; import { normalizeTextAlign } from './../utils/normalize'; import { HeaderCell } from './header-cell'; export class RowCell extends HeaderCell { get cellType() { return CellType.ROW_CELL; } getBorderPositions() { return [CellBorderPosition.BOTTOM, CellBorderPosition.LEFT]; } initCell() { super.initCell(); // 绘制单元格背景 this.drawBackgroundShape(); // 绘制交互背景 this.drawInteractiveBgShape(); // 绘制交互边框 this.drawInteractiveBorderShape(); // 绘制单元格文本 or 图片 this.drawTextOrCustomRenderer(); } afterDrawText() { // 绘制字段和 action标记 -- icon 和 action this.drawActionAndConditionIcons(); // 绘制树状模式收起展开的 icon this.drawTreeIcon(); // 绘制树状模式下子节点层级占位圆点 this.drawTreeLeafNodeAlignDot(); // 绘制单元格边框 this.drawBorders(); // 绘制 resize 热区 this.drawResizeAreaInLeaf(); this.update(); } getBackgroundColor() { const { backgroundColor, backgroundColorOpacity } = this.getCrossBackgroundColor(this.meta.rowIndex); return merge({ backgroundColor, backgroundColorOpacity }, this.getBackgroundConditionFill()); } showTreeIcon() { return this.spreadsheet.isHierarchyTreeType() && !this.meta.isLeaf; } showTreeLeafNodeAlignDot() { var _a, _b; return (((_b = (_a = this.spreadsheet.options.style) === null || _a === void 0 ? void 0 : _a.rowCell) === null || _b === void 0 ? void 0 : _b.showTreeLeafNodeAlignDot) && this.spreadsheet.isHierarchyTreeType()); } // 获取树状模式下叶子节点的父节点收起展开 icon 图形属性 getParentTreeIconCfg() { var _a, _b; if (!this.showTreeLeafNodeAlignDot() || !this.spreadsheet.isHierarchyTreeType() || !this.meta.isLeaf) { return; } const treeIcon = (_b = (_a = this.meta.parent) === null || _a === void 0 ? void 0 : _a.belongsCell) === null || _b === void 0 ? void 0 : _b.getTreeIcon(); return treeIcon === null || treeIcon === void 0 ? void 0 : treeIcon.style; } onTreeIconClick() { const { isCollapsed, hierarchy } = this.meta; if (isMobile()) { return; } // 折叠行头时因 scrollY 没变,导致底层出现空白 if (!isCollapsed) { const { scrollY: oldScrollY } = this.spreadsheet.facet.getScrollOffset(); // 可视窗口高度 const viewportHeight = this.spreadsheet.facet.panelBBox.viewportHeight || 0; // 被折叠项的高度 const deleteHeight = getAllChildrenNodeHeight(this.meta); // 折叠后真实高度 const realHeight = hierarchy.height - deleteHeight; if (oldScrollY > 0 && oldScrollY + viewportHeight > realHeight) { const currentScrollY = realHeight - viewportHeight; this.spreadsheet.facet.setScrollOffset({ scrollY: currentScrollY > 0 ? currentScrollY : 0, }); } } this.emitCollapseEvent(); } emitCollapseEvent() { this.spreadsheet.emit(S2Event.ROW_CELL_COLLAPSED__PRIVATE, { isCollapsed: !this.meta.isCollapsed, node: this.meta, }); } drawTreeIcon() { if (!this.showTreeIcon()) { return; } const { isCollapsed } = this.meta; const { x } = this.getBBoxByType(CellClipBox.CONTENT_BOX); const { fill } = this.getTextStyle(); const { size } = this.getStyle().icon; const contentIndent = this.getContentIndent(); const iconX = x + contentIndent; const iconY = this.getIconPosition().y; this.treeIcon = renderTreeIcon({ group: this, iconCfg: { x: iconX, y: iconY, width: size, height: size, fill, }, isCollapsed, onClick: () => { this.onTreeIconClick(); }, }); // 移动端, 点击热区为整个单元格 if (isMobile()) { this.addEventListener('touchend', () => { this.emitCollapseEvent(); }); } } drawTreeLeafNodeAlignDot() { const parentTreeIconCfg = this.getParentTreeIconCfg(); if (!parentTreeIconCfg) { return; } const { size, margin } = this.getStyle().icon; const x = parentTreeIconCfg.x + size + margin.right; const textY = this.getTextPosition().y; const { fill, fontSize } = this.getTextStyle(); // 半径,暂时先写死,后面看是否有这个点点的定制需求 const r = size / 5; this.treeLeafNodeAlignDot = renderCircle(this, { // 和收起展开 icon 保持居中对齐 cx: x + size / 2, cy: textY + (fontSize - r) / 2, r, fill, // 暂时先写死,后面看是否有这个点点的定制需求 fillOpacity: 0.3, }); } isBolderText() { // 非叶子节点、小计总计,均为粗体 const { isLeaf, isTotals, level } = this.meta; return (!isLeaf && level === 0) || isTotals; } getResizesArea() { return getOrCreateResizeAreaGroupById(this.spreadsheet, KEY_GROUP_ROW_RESIZE_AREA); } drawResizeAreaInLeaf() { if (!this.meta.isLeaf || this.meta.hideRowCellVerticalResize || !this.shouldDrawResizeAreaByType('rowCellVertical', this)) { return; } const { x, y, width, height } = this.getBBoxByType(); const resizeStyle = this.getResizeAreaStyle(); const resizeArea = this.getResizesArea(); if (!resizeArea) { return; } const { position, width: headerWidth, viewportHeight: headerHeight, scrollX = 0, scrollY = 0, } = this.getHeaderConfig(); const resizeAreaBBox = { x, y: y + height - resizeStyle.size, width, height: resizeStyle.size, }; const isFrozen = this.meta.isFrozen; const frozenGroupAreas = this.spreadsheet.facet .frozenGroupAreas; const frozenRowGroup = frozenGroupAreas[FrozenGroupArea.Row]; const frozenTrailingRowGroup = frozenGroupAreas[FrozenGroupArea.TrailingRow]; const resizeClipAreaBBox = { x: 0, y: isFrozen ? 0 : frozenRowGroup.height, width: headerWidth, height: isFrozen ? Number.POSITIVE_INFINITY : headerHeight - frozenRowGroup.height - frozenTrailingRowGroup.height, }; if (!shouldAddResizeArea(resizeAreaBBox, resizeClipAreaBBox, { scrollX, scrollY: isFrozen ? 0 : scrollY, })) { return; } const { offsetX, offsetY } = this.getHorizontalResizeAreaOffset(); const resizeAreaWidth = this.spreadsheet.isFrozenRowHeader() ? headerWidth - position.x - (x - scrollX) : width; const attrs = getResizeAreaAttrs({ theme: resizeStyle, type: ResizeDirectionType.Vertical, 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, y: offsetY + height - resizeStyle.size, width: resizeAreaWidth }), }, attrs.appendInfo)); } getHorizontalResizeAreaOffset() { const { position, viewportHeight: headerHeight, scrollX = 0, scrollY = 0, } = this.getHeaderConfig(); const { x, y } = this.getBBoxByType(); const frozenGroupAreas = this.spreadsheet.facet .frozenGroupAreas; const frozenRowGroup = frozenGroupAreas[FrozenGroupArea.Row]; const frozenTrailingRowGroup = frozenGroupAreas[FrozenGroupArea.TrailingRow]; const offsetX = position.x + x - scrollX; let offsetY = position.y; if (this.meta.isFrozenHead) { offsetY += y - frozenRowGroup.y; } else if (this.meta.isFrozenTrailing) { offsetY += headerHeight - frozenTrailingRowGroup.height + y - frozenTrailingRowGroup.y; } else { offsetY += y - scrollY; } return { offsetX, offsetY, }; } getContentIndent() { if (!this.spreadsheet.isHierarchyTreeType()) { return 0; } const { icon, cell } = this.getStyle(); const iconWidth = icon.size + icon.margin.right; let parent = this.meta.parent; let sum = 0; while (parent) { if (parent.height !== 0) { sum += iconWidth; } parent = parent.parent; } if (this.showTreeLeafNodeAlignDot()) { sum += this.isTreeLevel() ? 0 : cell.padding.right + icon.margin.right; } return sum; } getTextIndent() { const { size, margin } = this.getStyle().icon; const contentIndent = this.getContentIndent(); const treeIconWidth = this.showTreeIcon() || (this.isTreeLevel() && this.showTreeLeafNodeAlignDot()) ? size + margin.right : 0; return contentIndent + treeIconWidth; } // 判断当前节点的兄弟节点是否叶子节点 isTreeLevel() { return find(get(this.meta, 'parent.children'), (cell) => !cell.isLeaf); } getMaxTextWidth() { const { width } = this.getBBoxByType(CellClipBox.CONTENT_BOX); return width - this.getTextIndent() - this.getActionAndConditionIconWidth(); } getTextArea() { const content = this.getBBoxByType(CellClipBox.CONTENT_BOX); const textIndent = this.getTextIndent(); return Object.assign(Object.assign({}, content), { x: content.x + textIndent, width: content.width - textIndent }); } handleViewport() { if (this.meta.isFrozen) { return { start: 0, size: Number.POSITIVE_INFINITY, }; } const { scrollY, viewportHeight } = this.getHeaderConfig(); const frozenGroupAreas = this.spreadsheet.facet .frozenGroupAreas; const frozenRowGroupHeight = frozenGroupAreas[FrozenGroupArea.Row].height; const frozenTrailingRowGroupHeight = frozenGroupAreas[FrozenGroupArea.TrailingRow].height; const viewport = { start: scrollY + frozenRowGroupHeight, size: viewportHeight - frozenRowGroupHeight - frozenTrailingRowGroupHeight, }; return viewport; } getContentPosition({ contentWidth = this.getActualTextWidth(), } = {}) { const textArea = this.getTextArea(); const textStyle = this.getTextStyle(); const { cell, icon: iconStyle } = this.getStyle(); const viewport = this.handleViewport(); const textHeight = this.getActualTextHeight(); const { textStart } = adjustTextIconPositionWhileScrolling(viewport, { start: textArea.y, size: textArea.height, }, { align: normalizeTextAlign(textStyle.textBaseline), size: { textSize: textHeight, }, padding: { start: cell.padding.top, end: cell.padding.bottom, }, }); const { textX, leftIconX, rightIconX } = getHorizontalTextIconPosition({ bbox: textArea, textWidth: contentWidth, textAlign: textStyle.textAlign, groupedIcons: this.groupedIcons, iconStyle, isCustomRenderer: !!this.getRenderer(), }); const iconY = getVerticalIconPosition(iconStyle === null || iconStyle === void 0 ? void 0 : iconStyle.size, textStart, textHeight, textStyle.textBaseline); this.leftIconPosition = { x: leftIconX, y: iconY, }; this.rightIconPosition = { x: rightIconX, y: iconY, }; return { x: textX, y: textStart }; } getTextPosition() { return this.getContentPosition(); } getResizedTextMaxLines() { var _a, _b, _c, _d, _e; const { rowCell } = this.spreadsheet.options.style; return ((_d = (_b = (_a = rowCell === null || rowCell === void 0 ? void 0 : rowCell.maxLinesByField) === null || _a === void 0 ? void 0 : _a[this.meta.id]) !== null && _b !== void 0 ? _b : (_c = rowCell === null || rowCell === void 0 ? void 0 : rowCell.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=row-cell.js.map