@antv/s2
Version:
effective spreadsheet render core lib
481 lines (480 loc) • 21.9 kB
TypeScript
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 { DataCellPool } from '../cell/pool';
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;
protected dataCellPool: DataCellPool;
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;
}): number;
/**
* 根据叶子节点宽度计算所有父级节点宽度和 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;
protected initCellPool(): void;
}