UNPKG

@antv/s2

Version:

effective spreadsheet render core lib

478 lines (477 loc) 21.8 kB
import { Group } from '@antv/g'; import { type Timer } from '@antv/vendor/d3-timer'; import { flruCache } from 'flru'; import { ColCell, CornerCell, DataCell, MergedCell, RowCell, SeriesNumberCell, TableColCell, TableSeriesNumberCell } from '../cell'; import { ScrollDirection } from '../common/constant'; import { type AdjustLeafNodesParams, type CellCustomSize, type GridInfo, type HiddenColumnsInfo, type LayoutResult, type S2CellType, type ScrollOffsetConfig, type SimpleData, type ViewMeta } from '../common/interface'; import type { CellScrollOffset, CellScrollPosition, ScrollOffset } from '../common/interface/scroll'; import { PanelScrollGroup } from '../group/panel-scroll-group'; import type { SpreadSheet } from '../sheet-type'; import { ScrollBar } from '../ui/scrollbar'; import type { SelectedIds } from '../utils'; import { type PanelIndexes } from '../utils/indexes'; import { CornerBBox } from './bbox/corner-bbox'; import { PanelBBox } from './bbox/panel-bbox'; import { ColHeader, CornerHeader, Frame, RowHeader, SeriesNumberHeader, type BaseHeaderConfig, type RowHeaderConfig } from './header'; import type { TableColHeader } from './header/table-col'; import type { Hierarchy } from './layout/hierarchy'; import type { ViewCellHeights } from './layout/interface'; import { Node } from './layout/node'; import { WheelEvent as MobileWheel } from './mobile/wheelEvent'; export declare abstract class BaseFacet { spreadsheet: SpreadSheet; cornerBBox: CornerBBox; panelBBox: PanelBBox; backgroundGroup: Group; panelGroup: Group; panelScrollGroup: PanelScrollGroup; foregroundGroup: Group; /** * 当前布局节点信息 * @private 内部消费, 外部调用请使用 facet.getLayoutResult() */ protected layoutResult: LayoutResult; viewCellWidths: number[]; viewCellHeights: ViewCellHeights; protected mobileWheel: MobileWheel; protected timer: Timer; hScrollBar: ScrollBar; hRowScrollBar: ScrollBar; vScrollBar: ScrollBar; rowHeader: RowHeader | null; columnHeader: ColHeader | TableColHeader; cornerHeader: CornerHeader; seriesNumberHeader: SeriesNumberHeader | null; centerFrame: Frame; gridInfo: GridInfo; protected textWrapNodeHeightCache: flruCache<number>; protected textWrapTempCornerCell: CornerCell | null; protected textWrapTempRowCell: RowCell | DataCell; protected textWrapTempColCell: ColCell | TableColCell; customRowHeightStatusMap: Record<string, boolean>; protected abstract getCornerCellInstance(node: Node, spreadsheet: SpreadSheet, config: Partial<BaseHeaderConfig>): CornerCell | null; protected abstract getRowCellInstance(node: Node | ViewMeta, spreadsheet: SpreadSheet, config: Partial<BaseHeaderConfig>): RowCell | DataCell; protected abstract getColCellInstance(node: Node, spreadsheet: SpreadSheet, config: Partial<BaseHeaderConfig>): ColCell; protected abstract doLayout(): LayoutResult; protected abstract clip(scrollX: number, scrollY: number): void; abstract calculateXYIndexes(scrollX: number, scrollY: number): PanelIndexes; abstract getViewCellHeights(layoutResult?: LayoutResult): ViewCellHeights; abstract getViewCellHeights(): ViewCellHeights; abstract addDataCell(cell: DataCell): void; abstract getCellMeta(rowIndex: number, colIndex: number): ViewMeta | null; abstract getContentWidth(): number; abstract getContentHeight(): number; protected scrollFrameId: ReturnType<typeof requestAnimationFrame> | null; protected scrollDirection: ScrollDirection; get scrollBarTheme(): import("../common/interface").DeepRequired<import("../common/interface").ScrollBarTheme>; get scrollBarSize(): number; protected preCellIndexes: PanelIndexes | null; constructor(spreadsheet: SpreadSheet); protected shouldRender(): boolean; getLayoutResult: () => LayoutResult; protected initTextWrapTemp(): void; protected initGroups(): void; protected initForegroundGroup(): void; protected initBackgroundGroup(): void; protected initPanelGroups(): void; getCellCustomSize(node: Node | null, customSize: CellCustomSize): number | null | undefined; protected getRowCellDraggedWidth(node: Node): number | undefined; protected getRowCellDraggedHeight(node: Node): number | undefined; protected isCustomRowCellHeight(node: Node): boolean; protected getCustomRowCellHeight(node: Node): number | null | undefined; protected getRowCellHeight(node: Node): number | undefined; protected getColCellDraggedWidth(node: Node): number | undefined; protected getColCellDraggedHeight(node: Node): number | undefined; protected getColNodeHeight(options: { colNode: Node; colsHierarchy: Hierarchy; useCache?: boolean; cornerNodes?: Node[]; }): number; protected getDefaultColNodeHeight(colNode: Node, colsHierarchy: Hierarchy): number; protected getNodeAdaptiveHeight(options: { meta: Node | ViewMeta; cell: S2CellType; defaultHeight?: number; useCache?: boolean; }): any; /** * 根据叶子节点宽度计算所有父级节点宽度和 x 坐标 */ protected calculateColParentNodeWidthAndX(colLeafNodes: Node[]): void; /** * 将每一层级的采样节点更新为高度最大的节点 (未隐藏, 非汇总节点) */ protected updateColsHierarchySampleMaxHeightNodes(colsHierarchy: Hierarchy, rowsHierarchy?: Hierarchy): void; hideScrollBar: () => void; delayHideScrollBar: import("lodash").DebouncedFunc<() => void>; delayHideScrollbarOnMobile: () => void; showVerticalScrollBar: () => void; showHorizontalScrollBar: () => void; onContainerWheelForMobileCompatibility: () => void; onContainerWheel: () => void; getMobileWheelDeltaY: (deltaY: number) => number; onContainerWheelForPc: () => void; onContainerWheelForMobile: () => void; bindEvents: () => void; render(): void; /** * 在每次render, 校验scroll offset是否在合法范围中 * 比如在滚动条已经滚动到100%的状态的前提下:( maxAvailableScrollOffsetX = colsHierarchy.width - viewportBBox.width ) * 此时changeSheetSize,sheet从 small width 变为 big width * 导致后者 viewport 区域更大,其结果就是后者的 maxAvailableScrollOffsetX 更小 * 此时就需要重置 scrollOffsetX,否则就会导致滚动过多,出现空白区域 */ protected adjustScrollOffset(): void; getSeriesNumberWidth(): number; getCanvasSize(): { width: number; height: number; }; /** * @alias s2.interaction.scrollTo(offsetConfig) */ updateScrollOffset(offsetConfig: ScrollOffsetConfig): void; getPaginationScrollY(): number; destroy(): void; setScrollOffset: (scrollOffset: ScrollOffset) => void; getScrollOffset: () => Required<ScrollOffset>; resetScrollX: () => void; resetRowScrollX: () => void; resetScrollY: () => void; resetScrollOffset: () => void; emitPaginationEvent: () => void; protected unbindEvents: () => void; calculateCellWidthHeight: () => void; /** * 提供给明细表做 rowOffsets 计算的 hook */ protected abstract calculateRowOffsets(): void; getRealScrollX: (scrollX: number, hRowScroll?: number) => number; protected calculateCornerBBox(): void; protected calculatePanelBBox(): void; getRealWidth: () => number; getCellRange(): { start: number; end: number; }; getRealHeight: () => number; clearAllGroup(): void; scrollWithAnimation: (offsetConfig?: ScrollOffsetConfig, duration?: number, cb?: () => void) => void; scrollImmediately: (offsetConfig?: ScrollOffsetConfig) => void; /** * @param skipScrollEvent 不触发 S2Event.GLOBAL_SCROLL */ startScroll: (skipScrollEvent?: boolean) => void; protected getRendererHeight: () => number; protected getAdjustedScrollOffset: ({ scrollX, scrollY, rowHeaderScrollX, }: ScrollOffset) => ScrollOffset; protected renderRowScrollBar(rowHeaderScrollX: number): void; getValidScrollBarOffset(offset: number, maxOffset: number): number; renderHScrollBar(width: number, realWidth: number, scrollX: number): void; protected getScrollbarPosition(): { maxX: number; maxY: number; }; renderVScrollBar(height: number, realHeight: number, scrollY: number): void; getScrollBarOffset: (offset: number, scrollbar: ScrollBar) => number; isScrollOverThePanelArea: ({ offsetX, offsetY }: CellScrollOffset) => boolean; isScrollOverTheCornerArea: ({ offsetX, offsetY }: CellScrollOffset) => boolean; updateHorizontalRowScrollOffset: ({ offset, offsetX, offsetY, }: CellScrollOffset) => void; updateHorizontalScrollOffset: ({ offset, offsetX, offsetY, }: CellScrollOffset) => void; isScrollToLeft: ({ deltaX, offsetX, offsetY }: CellScrollOffset) => boolean; isScrollToRight: ({ deltaX, offsetX, offsetY }: CellScrollOffset) => boolean; isScrollToTop: (deltaY: number) => boolean; isScrollToBottom: (deltaY: number) => boolean; isVerticalScrollOverTheViewport: (deltaY: number) => boolean; isHorizontalScrollOverTheViewport: (scrollOffset: CellScrollOffset) => boolean; /** * 在当前表格滚动分两种情况: * 1. 当前表格无滚动条: 无需阻止外部容器滚动 * 2. 当前表格有滚动条: * - 未滚动到顶部或底部: 当前表格滚动, 阻止外部容器滚动 * - 滚动到顶部或底部: 恢复外部容器滚动 */ isScrollOverTheViewport: (scrollOffset: CellScrollOffset) => boolean; cancelScrollFrame: () => boolean; clearScrollFrameIdOnMobile: () => void; /** * https://developer.mozilla.org/zh-CN/docs/Web/CSS/overscroll-behavior * 阻止外部容器滚动: 表格是虚拟滚动, 这里按照标准模拟浏览器的 [overscroll-behavior] 实现 * 1. auto => 只有在滚动到表格顶部或底部时才触发外部容器滚动 * 1. contain => 默认的滚动边界行为不变(“触底”效果或者刷新),但是临近的滚动区域不会被滚动链影响到 * 2. none => 临近滚动区域不受到滚动链影响,而且默认的滚动到边界的表现也被阻止 * 所以只要不为 `auto`, 或者表格内, 都需要阻止外部容器滚动 */ protected stopScrollChainingIfNeeded: (event: WheelEvent) => void; protected stopScrollChaining: (event: WheelEvent) => void; onWheel: (event: WheelEvent) => void; /** * Translate panelGroup, rowHeader, cornerHeader, columnHeader ect * according to new scroll offset * @param scrollX * @param scrollY * @param hRowScroll * @protected */ protected translateRelatedGroups(scrollX: number, scrollY: number, hRowScroll: number): void; protected getCenterFrameScrollX(scrollX: number): number; createDataCell(viewMeta: ViewMeta | null): DataCell | undefined; realDataCellRender: (scrollX: number, scrollY: number) => void; protected init(): void; protected renderBackground(): void; /** * Render all scrollbars, default horizontal scrollbar only control viewport * area(it means not contains row header) * 1. individual row scrollbar * 2. horizontal scroll bar(can control whether contains row header) * 3. vertical scroll bar */ protected renderScrollBars(): void; /** * Render all headers in {@link #foregroundGroup}, contains: * 1. row header * 2. col header * 3. center frame * 4. corner header * 5. series number header */ protected renderHeaders(): void; protected getRowHeaderCfg(): RowHeaderConfig; protected getRowHeader(): RowHeader | null; protected getColHeader(): ColHeader; protected getCornerHeader(): CornerHeader; protected getSeriesNumberHeader(): SeriesNumberHeader | null; protected getCenterFrame(): Frame; protected getGridInfo: () => { cols: number[]; rows: number[]; }; updatePanelScrollGroup(): void; /** * @param skipScrollEvent: 不触发 GLOBAL_SCROLL 事件 */ protected dynamicRenderCell(skipScrollEvent?: boolean): void; protected emitScrollEvent(position: CellScrollPosition): void; protected onAfterScroll: import("lodash").DebouncedFunc<() => void>; protected saveInitColumnLeafNodes(columnNodes?: Node[]): void; getHiddenColumnsInfo(columnNode: Node): HiddenColumnsInfo | undefined; updateCustomFieldsSampleNodes(colsHierarchy: Hierarchy): void; /** * @description 自定义行头时, 叶子节点层级不定, 需要自动对齐其宽度, 填充空白 * ------------------------------------------------- * | 自定义节点 a-1 | 自定义节点 a-1-1 | * |------------- |------------- |------------| * | 自定义节点 b-1 | 自定义节点 b-1-1 | 指标 1 | * ------------------------------------------------- */ adjustCustomRowLeafNodesWidth(params: AdjustLeafNodesParams): void; /** * @description 自定义列头时, 叶子节点层级不定, 需要自动对齐其高度, 填充空白 * ------------------------------------------------------------------------ * | 自定义节点 a-1 | * |----------------------------------------------------------------------| * | 自定义节点 a-1-1 | | * |-------------|-------------|------------------| 自定义节点 a-1-2 | * | 指标 1 | 自定义节点 a-1-1-1 | 指标 2 | | * ---------------------------------------------------------------------- */ adjustCustomColLeafNodesHeight(params: AdjustLeafNodesParams): void; adjustLeafNodesSize(type: 'width' | 'height'): ({ leafNodes, hierarchy }: AdjustLeafNodesParams) => void; /** * 获取表头节点 (角头,序号,行头,列头) (含可视区域) * @example 获取全部: facet.getHeaderNodes() * @example 获取一组 facet.getHeaderNodes(['root[&]浙江省[&]宁波市', 'root[&]浙江省[&]杭州市']) */ getHeaderNodes(nodeIds?: string[]): Node[]; /** * 获取角头节点 */ getCornerNodes(): Node[]; /** * 获取序号节点 */ getSeriesNumberNodes(): Node[]; /** * 获取列头节点 (含非可视区域) * @description 获取列头单元格 (可视区域内) facet.getColCells() * @example 获取全部: facet.getColNodes() * @example 指定层级: facet.getColNodes(level) */ getColNodes(level?: number): Node[]; getTopLevelColNodes(): Node[]; /** * 根据 id 获取指定列头节点 * @example facet.getColNodeById('root[&]节点1[&]数值') */ getColNodeById(nodeId: string): Node | undefined; /** * 根据列头索引获取指定列头节点 * @example facet.getColNodeByIndex(colIndex) */ getColNodeByIndex(colIndex: number): Node | undefined; /** * 获取在索引范围内的列头叶子节点 * @example facet.getColLeafNodesByRange(0,10) 获取索引范围在 0(包括 0) 到 10(包括 10)的列头叶子节点 */ getColLeafNodesByRange(minIndex: number, maxIndex: number): Node[]; /** * 根据列头索引获取指定列头叶子节点 * @example facet.getColLeafNodes(colIndex) */ getColLeafNodeByIndex(colIndex: number): Node | undefined; /** * 根据 field 获取指定列头节点 * @example facet.getColCellNodeByField('number') */ getColNodesByField(nodeField: string): Node[]; /** * 获取列头叶子节点节点 (含非可视区域) */ getColLeafNodes(): Node[]; /** * 获取列头小计/总计节点 (含非可视区域) * @example 获取全部: facet.getColTotalsNodes() * @example 指定层级: facet.getColTotalsNodes(level) */ getColTotalsNodes(level?: number): Node[]; /** * 获取列头小计节点 (含非可视区域) * @example 获取全部: facet.getColSubTotalsNodes() * @example 指定层级: facet.getColSubTotalsNodes(level) */ getColSubTotalsNodes(level?: number): Node[]; /** * 获取列头总计节点 (含非可视区域) * @example 获取全部: facet.getColGrandTotalsNodes() * @example 指定层级: facet.getColGrandTotalsNodes(level) */ getColGrandTotalsNodes(level?: number): Node[]; /** * 获取行头节点 (含非可视区域) * @description 获取行头单元格 (可视区域内) facet.getRowCells() * @example 获取全部: facet.getRowNodes() * @example 指定层级: facet.getRowNodes(level) */ getRowNodes(level?: number): Node[]; /** * 根据 id 获取单个行头节点 * @example facet.getRowNodeById('root[&]节点1[&]数值') */ getRowNodeById(nodeId: string): Node | undefined; /** * 根据行头索引获取指定列头节点 * @example facet.getRowNodeByIndex(rowIndex) */ getRowNodeByIndex(rowIndex: number): Node | undefined; /** * 根据行头索引获取指定列头叶子节点 * @example facet.getRowLeafNodeByIndex(rowIndex) */ getRowLeafNodeByIndex(rowIndex: number): Node | undefined; /** * 获取在索引范围内的行头叶子节点 * @example facet.getRowLeafNodesByRange(0,10) 获取索引范围在 0(包括 0) 到 10(包括 10)的行头叶子节点 */ getRowLeafNodesByRange(minIndex: number, maxIndex: number): Node[]; /** * 根据 field 获取行头节点 * @example facet.getRowNodeByField('number') */ getRowNodesByField(nodeField: string): Node[]; /** * 获取行头叶子节点节点 (含非可视区域) * @example 获取全部: facet.getRowLeafNodes() */ getRowLeafNodes(): Node[]; /** * 获取行头小计/总计节点 (含非可视区域) * @example 获取全部: facet.getRowTotalsNodes() * @example 指定层级: facet.getRowTotalsNodes(level) */ getRowTotalsNodes(level?: number): Node[]; /** * 获取行头小计节点 (含非可视区域) * @example 获取全部: facet.getRowSubTotalsNodes() * @example 指定层级: facet.getRowSubTotalsNodes(level) */ getRowSubTotalsNodes(level?: number): Node[]; /** * 获取行头总计节点 (含非可视区域) * @example 获取全部: facet.getRowGrandTotalsNodes() * @example 指定层级: facet.getRowGrandTotalsNodes(level) */ getRowGrandTotalsNodes(level?: number): Node[]; /** * 获取单元格的所有子节点 (含非可视区域) * @example * const rowCell = facet.getRowCells()[0] * facet.getCellChildrenNodes(rowCell) */ getCellChildrenNodes: (cell: S2CellType) => Node[]; /** * 获取数值单元格 (不含可视区域) * @description 由于虚拟滚动, 按需加载的特性, 非可视区域的单元格未被实例化 * @example 获取非可视区域的数值节点 facet.getCellMeta(rowIndex, colIndex) */ getDataCells(): DataCell[]; /** * 获取行头单元格 (不含可视区域) */ getRowCells(): RowCell[]; /** * 获取行头叶子节点单元格 (不含可视区域) */ getRowLeafCells(): RowCell[]; /** * 获取列头单元格 (不含可视区域) */ getColCells(): ColCell[]; /** * 获取列头叶子节点单元格 (不含可视区域) */ getColLeafCells(): ColCell[]; /** * 获取合并单元格 (不含可视区域) */ getMergedCells(): MergedCell[]; /** * 获取角头单元格 (不含可视区域) */ getCornerCells(): CornerCell[]; protected filterCells(cells: S2CellType[], filterIds?: string[] | SelectedIds): S2CellType[]; /** * 获取序号单元格 (不含可视区域) */ abstract getSeriesNumberCells(): SeriesNumberCell[] | TableSeriesNumberCell[]; /** * 获取表头单元格 (序号,角头,行头,列头) (不含可视区域) * @example 获取全部: facet.getHeaderCells() * @example 获取一组 facet.getHeaderCells(['root[&]浙江省[&]宁波市', 'root[&]浙江省[&]杭州市']) */ getHeaderCells(cellIds?: string[] | SelectedIds): S2CellType<ViewMeta>[]; /** * 根据单元格 id 获取指定单元格 (不含可视区域) * @example facet.getCellById('root[&]浙江省[&]宁波市') */ getCellById(cellId: string): S2CellType<ViewMeta> | undefined; /** * 根据单元格 field 获取指定单元格 (不含可视区域) * @example facet.getCellByField('city') */ getCellsByField(cellField: string): S2CellType<ViewMeta>[]; /** * 获取所有单元格 (角头,行头,列头,数值) (不含可视区域) * @example 获取全部: facet.getCells() * @example 获取一组 facet.getCells(['root[&]浙江省[&]宁波市', 'root[&]浙江省[&]杭州市']) */ getCells(cellIds?: string[]): S2CellType<ViewMeta>[]; getInitColIndexLeafNodes(): Node[]; getInitColLeafNodes(): Node[]; clearInitColLeafNodes(): void; /** * @tip 和 this.spreadsheet.measureTextWidth() 的区别在于: * 1. 额外添加一像素余量,防止 maxLabel 有多个同样长度情况下,一些 label 不能展示完全, 出现省略号 * 2. 测量时, 文本宽度取整, 避免子像素的不一致性 */ protected measureTextWidth(text: SimpleData, font: unknown): number; }