"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseFacet = void 0;
const tslib_1 = require("tslib");
const g_1 = require("@antv/g");
const d3_interpolate_1 = require("@antv/vendor/d3-interpolate");
const d3_timer_1 = require("@antv/vendor/d3-timer");
const flru_1 = tslib_1.__importDefault(require("flru"));
const lodash_1 = require("lodash");
const cell_1 = require("../cell");
const pool_1 = require("../cell/pool");
const constant_1 = require("../common/constant");
const pagination_1 = require("../common/constant/pagination");
const debug_1 = require("../common/debug");
const interface_1 = require("../common/interface");
const panel_scroll_group_1 = require("../group/panel-scroll-group");
const theme_1 = require("../theme");
const scrollbar_1 = require("../ui/scrollbar");
const facet_1 = require("../utils/facet");
const get_all_child_cells_1 = require("../utils/get-all-child-cells");
const grid_1 = require("../utils/grid");
const indexes_1 = require("../utils/indexes");
const is_mobile_1 = require("../utils/is-mobile");
const math_1 = require("../utils/math");
const corner_bbox_1 = require("./bbox/corner-bbox");
const panel_bbox_1 = require("./bbox/panel-bbox");
const header_1 = require("./header");
const node_1 = require("./layout/node");
const wheelEvent_1 = require("./mobile/wheelEvent");
const utils_1 = require("./utils");
class BaseFacet {
    get scrollBarTheme() {
        return this.spreadsheet.theme.scrollBar;
    }
    get scrollBarSize() {
        var _a;
        return (_a = this.scrollBarTheme) === null || _a === void 0 ? void 0 : _a.size;
    }
    constructor(spreadsheet) {
        this.scrollFrameId = null;
        this.getLayoutResult = () => {
            return Object.assign(Object.assign({}, this.layoutResult), { cornerNodes: this.getCornerNodes(), seriesNumberNodes: this.getSeriesNumberNodes() });
        };
        this.hideScrollBar = () => {
            var _a, _b, _c;
            (_a = this.hRowScrollBar) === null || _a === void 0 ? void 0 : _a.setAttribute('visibility', 'hidden');
            (_b = this.hScrollBar) === null || _b === void 0 ? void 0 : _b.setAttribute('visibility', 'hidden');
            (_c = this.vScrollBar) === null || _c === void 0 ? void 0 : _c.setAttribute('visibility', 'hidden');
        };
        this.delayHideScrollBar = (0, lodash_1.debounce)(this.hideScrollBar, 1000);
        this.delayHideScrollbarOnMobile = () => {
            if ((0, is_mobile_1.isMobile)()) {
                this.delayHideScrollBar();
            }
        };
        this.showVerticalScrollBar = () => {
            var _a;
            (_a = this.vScrollBar) === null || _a === void 0 ? void 0 : _a.setAttribute('visibility', 'visible');
        };
        this.showHorizontalScrollBar = () => {
            var _a, _b;
            (_a = this.hRowScrollBar) === null || _a === void 0 ? void 0 : _a.setAttribute('visibility', 'visible');
            (_b = this.hScrollBar) === null || _b === void 0 ? void 0 : _b.setAttribute('visibility', 'visible');
        };
        this.onContainerWheelForMobileCompatibility = () => {
            const canvas = this.spreadsheet.getCanvasElement();
            let startY;
            let endY;
            canvas.addEventListener('touchstart', (event) => {
                startY = event.touches[0].clientY;
            });
            canvas.addEventListener('touchend', (event) => {
                endY = event.changedTouches[0].clientY;
                if (endY < startY) {
                    this.scrollDirection = constant_1.ScrollDirection.SCROLL_UP;
                }
                else if (endY > startY) {
                    this.scrollDirection = constant_1.ScrollDirection.SCROLL_DOWN;
                }
            });
        };
        this.onContainerWheel = () => {
            if ((0, is_mobile_1.isMobile)()) {
                this.onContainerWheelForMobile();
            }
            else {
                this.onContainerWheelForPc();
            }
        };
        // g-gesture@1.0.1 手指快速往上滚动时, deltaY 有时会为负数, 导致向下滚动时然后回弹, 看起来就像表格在抖动, 需要判断滚动方向, 修正一下.
        this.getMobileWheelDeltaY = (deltaY) => {
            if (this.scrollDirection === constant_1.ScrollDirection.SCROLL_UP) {
                return Math.max(0, deltaY);
            }
            if (this.scrollDirection === constant_1.ScrollDirection.SCROLL_DOWN) {
                return Math.min(0, deltaY);
            }
            return deltaY;
        };
        this.onContainerWheelForPc = () => {
            const canvas = this.spreadsheet.getCanvasElement();
            canvas === null || canvas === void 0 ? void 0 : canvas.addEventListener('wheel', this.onWheel);
        };
        this.onContainerWheelForMobile = () => {
            this.mobileWheel = new wheelEvent_1.WheelEvent(this.spreadsheet.container);
            this.mobileWheel.on('wheel', (ev) => {
                this.spreadsheet.hideTooltip();
                const originEvent = ev.originalEvent;
                const { deltaX, deltaY: defaultDeltaY, x, y } = ev;
                const deltaY = this.getMobileWheelDeltaY(defaultDeltaY);
                this.onWheel(Object.assign(Object.assign({}, originEvent), { deltaX,
                    deltaY, offsetX: x, offsetY: y }));
            });
            this.onContainerWheelForMobileCompatibility();
        };
        this.bindEvents = () => {
            this.onContainerWheel();
            this.emitPaginationEvent();
        };
        this.setScrollOffset = (scrollOffset) => {
            Object.keys(scrollOffset || {}).forEach((key) => {
                const offset = (0, lodash_1.get)(scrollOffset, key);
                if (!(0, lodash_1.isUndefined)(offset)) {
                    this.spreadsheet.store.set(key, (0, math_1.floor)(offset));
                }
            });
        };
        this.getScrollOffset = () => {
            const { store } = this.spreadsheet;
            return {
                scrollX: store.get('scrollX', 0),
                scrollY: store.get('scrollY', 0),
                rowHeaderScrollX: store.get('rowHeaderScrollX', 0),
            };
        };
        this.resetScrollX = () => {
            this.setScrollOffset({ scrollX: 0 });
        };
        this.resetRowScrollX = () => {
            this.setScrollOffset({ rowHeaderScrollX: 0 });
        };
        this.resetScrollY = () => {
            this.setScrollOffset({ scrollY: 0 });
        };
        this.resetScrollOffset = () => {
            this.setScrollOffset({ scrollX: 0, scrollY: 0, rowHeaderScrollX: 0 });
        };
        this.emitPaginationEvent = () => {
            const { pagination } = this.spreadsheet.options;
            if (pagination) {
                const { current = pagination_1.DEFAULT_PAGE_INDEX, pageSize } = pagination;
                const total = this.viewCellHeights.getTotalLength();
                const pageCount = (0, math_1.floor)((total - 1) / pageSize) + 1;
                this.spreadsheet.emit(constant_1.S2Event.LAYOUT_PAGINATION, {
                    pageSize,
                    pageCount,
                    total,
                    current,
                });
            }
        };
        this.unbindEvents = () => {
            var _a;
            const canvas = this.spreadsheet.getCanvasElement();
            canvas === null || canvas === void 0 ? void 0 : canvas.removeEventListener('wheel', this.onWheel);
            (_a = this.mobileWheel) === null || _a === void 0 ? void 0 : _a.destroy();
        };
        this.calculateCellWidthHeight = () => {
            const { colLeafNodes } = this.layoutResult;
            const widths = (0, lodash_1.reduce)(colLeafNodes, (result, node) => {
                const width = (0, lodash_1.last)(result) || 0;
                result.push(width + node.width);
                return result;
            }, [0]);
            this.viewCellWidths = widths;
            this.viewCellHeights = this.getViewCellHeights();
        };
        this.getRealScrollX = (scrollX, hRowScroll = 0) => this.spreadsheet.isFrozenRowHeader() ? hRowScroll : scrollX;
        this.getRealWidth = () => (0, lodash_1.last)(this.viewCellWidths) || 0;
        this.getRealHeight = () => {
            const { pagination } = this.spreadsheet.options;
            const heights = this.viewCellHeights;
            if (pagination) {
                const { start, end } = this.getCellRange();
                return heights.getCellOffsetY(end + 1) - heights.getCellOffsetY(start);
            }
            return heights.getTotalHeight();
        };
        this.scrollWithAnimation = (offsetConfig = {}, duration = 200, cb) => {
            var _a, _b, _c, _d;
            const { scrollX: adjustedScrollX, scrollY: adjustedScrollY, rowHeaderScrollX: adjustedRowScrollX, } = this.getAdjustedScrollOffset({
                scrollX: ((_a = offsetConfig.offsetX) === null || _a === void 0 ? void 0 : _a.value) || 0,
                scrollY: ((_b = offsetConfig.offsetY) === null || _b === void 0 ? void 0 : _b.value) || 0,
                rowHeaderScrollX: ((_c = offsetConfig.rowHeaderOffsetX) === null || _c === void 0 ? void 0 : _c.value) || 0,
            });
            (_d = this.timer) === null || _d === void 0 ? void 0 : _d.stop();
            const scrollOffset = this.getScrollOffset();
            const newOffset = [
                adjustedScrollX !== null && adjustedScrollX !== void 0 ? adjustedScrollX : scrollOffset.scrollX,
                adjustedScrollY !== null && adjustedScrollY !== void 0 ? adjustedScrollY : scrollOffset.scrollY,
                adjustedRowScrollX !== null && adjustedRowScrollX !== void 0 ? adjustedRowScrollX : scrollOffset.rowHeaderScrollX,
            ];
            const interpolate = (0, d3_interpolate_1.interpolateArray)(Object.values(scrollOffset), newOffset);
            this.timer = (0, d3_timer_1.timer)((elapsed) => {
                try {
                    const ratio = Math.min(elapsed / duration, 1);
                    const [scrollX, scrollY, rowHeaderScrollX] = interpolate(ratio);
                    this.setScrollOffset({
                        rowHeaderScrollX,
                        scrollX,
                        scrollY,
                    });
                    this.startScroll(offsetConfig === null || offsetConfig === void 0 ? void 0 : offsetConfig.skipScrollEvent);
                    if (elapsed > duration) {
                        this.timer.stop();
                        cb === null || cb === void 0 ? void 0 : cb();
                    }
                }
                catch (e) {
                    // eslint-disable-next-line no-console
                    console.error(e);
                    this.timer.stop();
                }
            });
        };
        this.scrollImmediately = (offsetConfig = {}) => {
            var _a, _b, _c;
            const { scrollX, scrollY, rowHeaderScrollX } = this.getAdjustedScrollOffset({
                scrollX: ((_a = offsetConfig.offsetX) === null || _a === void 0 ? void 0 : _a.value) || 0,
                scrollY: ((_b = offsetConfig.offsetY) === null || _b === void 0 ? void 0 : _b.value) || 0,
                rowHeaderScrollX: ((_c = offsetConfig.rowHeaderOffsetX) === null || _c === void 0 ? void 0 : _c.value) || 0,
            });
            this.setScrollOffset({ scrollX, scrollY, rowHeaderScrollX });
            this.startScroll(offsetConfig === null || offsetConfig === void 0 ? void 0 : offsetConfig.skipScrollEvent);
        };
        /**
         * @param skipScrollEvent 不触发 S2Event.GLOBAL_SCROLL
         */
        this.startScroll = (skipScrollEvent = false) => {
            var _a, _b, _c;
            const { rowHeaderScrollX, scrollX, scrollY } = this.getScrollOffset();
            (_a = this.hRowScrollBar) === null || _a === void 0 ? void 0 : _a.onlyUpdateThumbOffset(this.getScrollBarOffset(rowHeaderScrollX, this.hRowScrollBar));
            (_b = this.hScrollBar) === null || _b === void 0 ? void 0 : _b.onlyUpdateThumbOffset(this.getScrollBarOffset(scrollX, this.hScrollBar));
            (_c = this.vScrollBar) === null || _c === void 0 ? void 0 : _c.onlyUpdateThumbOffset(this.getScrollBarOffset(scrollY, this.vScrollBar));
            this.dynamicRenderCell(skipScrollEvent);
        };
        this.getRendererHeight = () => {
            const { start, end } = this.getCellRange();
            return (this.viewCellHeights.getCellOffsetY(end + 1) -
                this.viewCellHeights.getCellOffsetY(start));
        };
        this.getAdjustedScrollOffset = ({ scrollX, scrollY, rowHeaderScrollX, }) => {
            return {
                scrollX: (0, facet_1.getAdjustedScrollOffset)(scrollX, this.layoutResult.colsHierarchy.width, this.panelBBox.width),
                scrollY: (0, facet_1.getAdjustedScrollOffset)(scrollY, this.getRendererHeight(), this.panelBBox.height),
                rowHeaderScrollX: (0, facet_1.getAdjustedRowScrollX)(rowHeaderScrollX, this.cornerBBox),
            };
        };
        // (滑动 offset / 最大 offset（滚动对象真正长度 - 轨道长）) = (滑块 offset / 最大滑动距离（轨道长 - 滑块长）)
        this.getScrollBarOffset = (offset, scrollbar) => {
            const { trackLen, thumbLen, scrollTargetMaxOffset } = scrollbar;
            return (offset * (trackLen - thumbLen)) / scrollTargetMaxOffset;
        };
        this.isScrollOverThePanelArea = ({ offsetX, offsetY }) => offsetX > this.panelBBox.minX &&
            offsetX < this.panelBBox.maxX &&
            offsetY > this.panelBBox.minY &&
            offsetY < this.panelBBox.maxY;
        this.isScrollOverTheCornerArea = ({ offsetX, offsetY }) => offsetX > this.cornerBBox.minX &&
            offsetX < this.cornerBBox.maxX &&
            offsetY > this.cornerBBox.minY &&
            offsetY < this.cornerBBox.maxY + this.panelBBox.height;
        this.updateHorizontalRowScrollOffset = ({ offset, offsetX, offsetY, }) => {
            var _a;
            // 在行头区域滚动时 才更新行头水平滚动条
            if (this.isScrollOverTheCornerArea({ offsetX, offsetY })) {
                (_a = this.hRowScrollBar) === null || _a === void 0 ? void 0 : _a.emitScrollChange(offset);
            }
        };
        this.updateHorizontalScrollOffset = ({ offset, offsetX, offsetY, }) => {
            var _a;
            // 1.行头没有滚动条 2.在数值区域滚动时 才更新数值区域水平滚动条
            if (!this.hRowScrollBar ||
                this.isScrollOverThePanelArea({ offsetX, offsetY })) {
                (_a = this.hScrollBar) === null || _a === void 0 ? void 0 : _a.emitScrollChange(offset);
            }
        };
        this.isScrollToLeft = ({ deltaX, offsetX, offsetY }) => {
            if (!this.hScrollBar && !this.hRowScrollBar) {
                return true;
            }
            const isScrollRowHeaderToLeft = !this.hRowScrollBar ||
                this.isScrollOverThePanelArea({ offsetY, offsetX }) ||
                this.hRowScrollBar.thumbOffset <= 0;
            const isScrollPanelToLeft = !this.hScrollBar || this.hScrollBar.thumbOffset <= 0;
            return deltaX <= 0 && isScrollPanelToLeft && isScrollRowHeaderToLeft;
        };
        this.isScrollToRight = ({ deltaX, offsetX, offsetY }) => {
            var _a, _b, _c, _d, _e, _f;
            if (!this.hScrollBar && !this.hRowScrollBar) {
                return true;
            }
            const viewportWidth = this.spreadsheet.isFrozenRowHeader()
                ? (_a = this.panelBBox) === null || _a === void 0 ? void 0 : _a.width
                : (_b = this.panelBBox) === null || _b === void 0 ? void 0 : _b.maxX;
            const isScrollRowHeaderToRight = !this.hRowScrollBar ||
                this.isScrollOverThePanelArea({ offsetY, offsetX }) ||
                ((_c = this.hRowScrollBar) === null || _c === void 0 ? void 0 : _c.thumbOffset) + ((_d = this.hRowScrollBar) === null || _d === void 0 ? void 0 : _d.thumbLen) >=
                    this.cornerBBox.width;
            const isScrollPanelToRight = (this.hRowScrollBar &&
                this.isScrollOverTheCornerArea({ offsetX, offsetY })) ||
                ((_e = this.hScrollBar) === null || _e === void 0 ? void 0 : _e.thumbOffset) + ((_f = this.hScrollBar) === null || _f === void 0 ? void 0 : _f.thumbLen) >= viewportWidth;
            return deltaX >= 0 && isScrollPanelToRight && isScrollRowHeaderToRight;
        };
        this.isScrollToTop = (deltaY) => {
            var _a;
            if (!this.vScrollBar) {
                return true;
            }
            return deltaY <= 0 && ((_a = this.vScrollBar) === null || _a === void 0 ? void 0 : _a.thumbOffset) <= 0;
        };
        this.isScrollToBottom = (deltaY) => {
            var _a, _b, _c;
            if (!this.vScrollBar) {
                return true;
            }
            return (deltaY >= 0 &&
                ((_a = this.vScrollBar) === null || _a === void 0 ? void 0 : _a.thumbOffset) + ((_b = this.vScrollBar) === null || _b === void 0 ? void 0 : _b.thumbLen) >=
                    ((_c = this.panelBBox) === null || _c === void 0 ? void 0 : _c.height));
        };
        this.isVerticalScrollOverTheViewport = (deltaY) => !this.isScrollToTop(deltaY) && !this.isScrollToBottom(deltaY);
        this.isHorizontalScrollOverTheViewport = (scrollOffset) => !this.isScrollToLeft(scrollOffset) && !this.isScrollToRight(scrollOffset);
        /**
         * 在当前表格滚动分两种情况:
         *  1. 当前表格无滚动条: 无需阻止外部容器滚动
         *  2. 当前表格有滚动条:
         *    - 未滚动到顶部或底部: 当前表格滚动, 阻止外部容器滚动
         *    - 滚动到顶部或底部: 恢复外部容器滚动
         */
        this.isScrollOverTheViewport = (scrollOffset) => {
            const { deltaY, deltaX, offsetY } = scrollOffset;
            const isScrollOverTheHeader = offsetY <= this.cornerBBox.maxY;
            // 光标在角头或列头时, 不触发表格自身滚动
            if (isScrollOverTheHeader) {
                return false;
            }
            if (deltaY !== 0) {
                return this.isVerticalScrollOverTheViewport(deltaY);
            }
            if (deltaX !== 0) {
                return this.isHorizontalScrollOverTheViewport(scrollOffset);
            }
            return false;
        };
        this.cancelScrollFrame = () => {
            if ((0, is_mobile_1.isMobile)() && this.scrollFrameId) {
                return false;
            }
            cancelAnimationFrame(this.scrollFrameId);
            return true;
        };
        this.clearScrollFrameIdOnMobile = () => {
            if ((0, is_mobile_1.isMobile)()) {
                this.scrollFrameId = null;
            }
        };
        /**
         * https://developer.mozilla.org/zh-CN/docs/Web/CSS/overscroll-behavior
         * 阻止外部容器滚动: 表格是虚拟滚动, 这里按照标准模拟浏览器的 [overscroll-behavior] 实现
         * 1. auto => 只有在滚动到表格顶部或底部时才触发外部容器滚动
         * 1. contain => 默认的滚动边界行为不变（“触底”效果或者刷新），但是临近的滚动区域不会被滚动链影响到
         * 2. none => 临近滚动区域不受到滚动链影响，而且默认的滚动到边界的表现也被阻止
         * 所以只要不为 `auto`, 或者表格内, 都需要阻止外部容器滚动
         */
        this.stopScrollChainingIfNeeded = (event) => {
            const { interaction } = this.spreadsheet.options;
            if ((interaction === null || interaction === void 0 ? void 0 : interaction.overscrollBehavior) !== 'auto') {
                this.cancelScrollFrame();
                this.stopScrollChaining(event);
            }
        };
        this.stopScrollChaining = (event) => {
            var _a, _b;
            if (event === null || event === void 0 ? void 0 : event.cancelable) {
                (_a = event === null || event === void 0 ? void 0 : event.preventDefault) === null || _a === void 0 ? void 0 : _a.call(event);
            }
            // 使用 G 对应的原生 TouchEvent，以达到移动端禁用外部容器滚动的效果
            const mobileEvent = (event === null || event === void 0 ? void 0 : event.nativeEvent) || event;
            if (mobileEvent === null || mobileEvent === void 0 ? void 0 : mobileEvent.cancelable) {
                (_b = mobileEvent === null || mobileEvent === void 0 ? void 0 : mobileEvent.preventDefault) === null || _b === void 0 ? void 0 : _b.call(mobileEvent);
            }
        };
        this.onWheel = (event) => {
            const { interaction } = this.spreadsheet.options;
            let { deltaX, deltaY, offsetX, offsetY } = event;
            const { scrollX: currentScrollX, rowHeaderScrollX } = this.getScrollOffset();
            const { shiftKey } = event;
            // Windows 环境，按住 shift 时，固定为水平方向滚动，macOS 环境默认有该行为
            // see https://github.com/antvis/S2/issues/2198
            if (shiftKey && (0, is_mobile_1.isWindows)()) {
                offsetX = offsetX - deltaX + deltaY;
                deltaX = deltaY;
                offsetY -= deltaY;
                deltaY = 0;
            }
            const [optimizedDeltaX, optimizedDeltaY] = (0, utils_1.optimizeScrollXY)(deltaX, deltaY, interaction === null || interaction === void 0 ? void 0 : interaction.scrollSpeedRatio);
            this.spreadsheet.hideTooltip();
            this.spreadsheet.interaction.clearHoverTimer();
            if (!this.isScrollOverTheViewport({
                deltaX: optimizedDeltaX,
                deltaY: optimizedDeltaY,
                offsetX,
                offsetY,
            })) {
                this.stopScrollChainingIfNeeded(event);
                return;
            }
            this.stopScrollChaining(event);
            this.spreadsheet.interaction.addIntercepts([constant_1.InterceptType.HOVER]);
            if (!this.cancelScrollFrame()) {
                return;
            }
            if (this.scrollDirection !== undefined &&
                this.scrollDirection !==
                    (optimizedDeltaX > 0
                        ? constant_1.ScrollDirection.SCROLL_LEFT
                        : constant_1.ScrollDirection.SCROLL_RIGHT)) {
                this.scrollDirection =
                    optimizedDeltaX > 0
                        ? constant_1.ScrollDirection.SCROLL_LEFT
                        : constant_1.ScrollDirection.SCROLL_RIGHT;
                this.updateHorizontalRowScrollOffset({
                    offsetX,
                    offsetY,
                    offset: rowHeaderScrollX,
                });
                this.updateHorizontalScrollOffset({
                    offsetX,
                    offsetY,
                    offset: currentScrollX,
                });
                return;
            }
            this.scrollDirection =
                deltaX > 0 ? constant_1.ScrollDirection.SCROLL_LEFT : constant_1.ScrollDirection.SCROLL_RIGHT;
            this.scrollFrameId = requestAnimationFrame(() => {
                var _a;
                const { scrollX: currentScrollX, scrollY: currentScrollY, rowHeaderScrollX, } = this.getScrollOffset();
                if (optimizedDeltaX !== 0) {
                    this.showHorizontalScrollBar();
                    this.updateHorizontalRowScrollOffset({
                        offsetX,
                        offsetY,
                        offset: optimizedDeltaX + rowHeaderScrollX,
                    });
                    this.updateHorizontalScrollOffset({
                        offsetX,
                        offsetY,
                        offset: optimizedDeltaX + currentScrollX,
                    });
                }
                if (optimizedDeltaY !== 0) {
                    this.showVerticalScrollBar();
                    (_a = this.vScrollBar) === null || _a === void 0 ? void 0 : _a.emitScrollChange(optimizedDeltaY + currentScrollY);
                }
                this.delayHideScrollbarOnMobile();
                this.clearScrollFrameIdOnMobile();
            });
        };
        this.realDataCellRender = (scrollX, scrollY) => {
            const indexes = this.calculateXYIndexes(scrollX, scrollY);
            debug_1.DebuggerUtil.getInstance().logger('realDataCellRender:', this.preCellIndexes, indexes);
            const { add: willAddDataCells, remove: willRemoveDataCells } = (0, indexes_1.diffPanelIndexes)(this.preCellIndexes, indexes);
            debug_1.DebuggerUtil.getInstance().debugCallback(debug_1.DEBUG_VIEW_RENDER, () => {
                var _a;
                if ((_a = this.spreadsheet.options.future) === null || _a === void 0 ? void 0 : _a.experimentalReuseCell) {
                    const allDataCells = this.getDataCells();
                    const maxLength = Math.max(willRemoveDataCells.length, willAddDataCells.length);
                    // 交替执行删除和添加操作
                    for (let i = 0; i < maxLength; i++) {
                        // 删除单元格
                        if (i < willRemoveDataCells.length) {
                            const [colIndex, rowIndex] = willRemoveDataCells[i];
                            const mountedDataCell = (0, lodash_1.find)(allDataCells, (cell) => cell.name === `${rowIndex}-${colIndex}`);
                            if (mountedDataCell) {
                                this.dataCellPool.release(mountedDataCell);
                            }
                        }
                        // 添加单元格
                        if (i < willAddDataCells.length) {
                            const [colIndex, rowIndex] = willAddDataCells[i];
                            const viewMeta = this.getCellMeta(rowIndex, colIndex);
                            const cell = this.createDataCell(viewMeta);
                            if (cell) {
                                this.addDataCell(cell);
                            }
                        }
                    }
                    debug_1.DebuggerUtil.getInstance().logger(`Render Cell Panel: ${allDataCells === null || allDataCells === void 0 ? void 0 : allDataCells.length}, Add: ${willAddDataCells === null || willAddDataCells === void 0 ? void 0 : willAddDataCells.length}, Remove: ${willRemoveDataCells === null || willRemoveDataCells === void 0 ? void 0 : willRemoveDataCells.length}`);
                }
                else {
                    // add new cell in panelCell
                    (0, lodash_1.each)(willAddDataCells, ([colIndex, rowIndex]) => {
                        const viewMeta = this.getCellMeta(rowIndex, colIndex);
                        const cell = this.createDataCell(viewMeta);
                        if (!cell) {
                            return;
                        }
                        this.addDataCell(cell);
                    });
                    const allDataCells = this.getDataCells();
                    // remove cell from panelCell
                    (0, lodash_1.each)(willRemoveDataCells, ([colIndex, rowIndex]) => {
                        const mountedDataCell = (0, lodash_1.find)(allDataCells, (cell) => cell.name === `${rowIndex}-${colIndex}`);
                        mountedDataCell === null || mountedDataCell === void 0 ? void 0 : mountedDataCell.destroy();
                    });
                    debug_1.DebuggerUtil.getInstance().logger(`Render Cell Panel: ${allDataCells === null || allDataCells === void 0 ? void 0 : allDataCells.length}, Add: ${willAddDataCells === null || willAddDataCells === void 0 ? void 0 : willAddDataCells.length}, Remove: ${willRemoveDataCells === null || willRemoveDataCells === void 0 ? void 0 : willRemoveDataCells.length}`);
                }
            });
            this.preCellIndexes = indexes;
            this.spreadsheet.emit(constant_1.S2Event.LAYOUT_AFTER_REAL_DATA_CELL_RENDER, {
                add: willAddDataCells,
                remove: willRemoveDataCells,
                spreadsheet: this.spreadsheet,
            });
        };
        this.getGridInfo = () => {
            const [colMin, colMax, rowMin, rowMax] = this.preCellIndexes.center;
            const cols = (0, grid_1.getColsForGrid)(colMin, colMax, this.layoutResult.colLeafNodes);
            const rows = (0, grid_1.getRowsForGrid)(rowMin, rowMax, this.viewCellHeights);
            return {
                cols,
                rows,
            };
        };
        this.onAfterScroll = (0, lodash_1.debounce)(() => {
            const { interaction, container } = this.spreadsheet;
            // 如果是选中单元格状态, 则继续保留 hover 拦截, 避免滚动后 hover 清空已选单元格
            if (!interaction.isSelectedState()) {
                interaction.removeIntercepts([constant_1.InterceptType.HOVER]);
                if (interaction.getHoverAfterScroll()) {
                    // https://github.com/antvis/S2/issues/2222
                    const canvasMousemoveEvent = interaction.eventController.canvasMousemoveEvent;
                    if (canvasMousemoveEvent) {
                        const { x, y } = canvasMousemoveEvent;
                        const shape = container.document.elementFromPointSync(x, y);
                        if (shape) {
                            container.emit(constant_1.OriginEventType.POINTER_MOVE, Object.assign(Object.assign({}, canvasMousemoveEvent), { shape, target: shape, timestamp: performance.now() }));
                        }
                    }
                }
            }
        }, 300);
        /**
         * 获取单元格的所有子节点 (含非可视区域)
         * @example
         * const rowCell = facet.getRowCells()[0]
         * facet.getCellChildrenNodes(rowCell)
         */
        this.getCellChildrenNodes = (cell) => {
            var _a;
            const selectNode = (_a = cell === null || cell === void 0 ? void 0 : cell.getMeta) === null || _a === void 0 ? void 0 : _a.call(cell);
            const isRowCell = (cell === null || cell === void 0 ? void 0 : cell.cellType) === constant_1.CellType.ROW_CELL;
            const isHierarchyTree = this.spreadsheet.isHierarchyTreeType();
            // 树状模式的行头点击不需要遍历当前行头的所有子节点，因为只会有一级
            if (isHierarchyTree && isRowCell) {
                return node_1.Node.getAllLeaveNodes(selectNode).filter((node) => node.rowIndex === selectNode.rowIndex);
            }
            // 平铺模式 或 树状模式的列头点击遍历所有子节点
            return node_1.Node.getAllChildrenNodes(selectNode);
        };
        this.spreadsheet = spreadsheet;
        this.init();
    }
    shouldRender() {
        return !(0, utils_1.areAllFieldsEmpty)(this.spreadsheet.dataCfg.fields);
    }
    initTextWrapTemp() {
        var _a;
        const node = {};
        const args = [
            node,
            this.spreadsheet,
            { shallowRender: true },
        ];
        this.textWrapTempRowCell = this.getRowCellInstance(...args);
        this.textWrapTempColCell = this.getColCellInstance(...args);
        this.textWrapTempCornerCell = (_a = this.getCornerCellInstance) === null || _a === void 0 ? void 0 : _a.call(this, ...args);
        this.textWrapNodeHeightCache = (0, flru_1.default)(500);
        this.customRowHeightStatusMap = {};
    }
    initGroups() {
        this.initBackgroundGroup();
        this.initPanelGroups();
        this.initForegroundGroup();
    }
    initForegroundGroup() {
        this.foregroundGroup = this.spreadsheet.container.appendChild(new g_1.Group({
            name: constant_1.KEY_GROUP_FORE_GROUND,
            style: { zIndex: constant_1.FRONT_GROUND_GROUP_CONTAINER_Z_INDEX },
        }));
    }
    initBackgroundGroup() {
        this.backgroundGroup = this.spreadsheet.container.appendChild(new g_1.Group({
            name: constant_1.KEY_GROUP_BACK_GROUND,
            style: { zIndex: constant_1.BACK_GROUND_GROUP_CONTAINER_Z_INDEX },
        }));
    }
    initPanelGroups() {
        this.panelGroup = this.spreadsheet.container.appendChild(new g_1.Group({
            name: constant_1.KEY_GROUP_PANEL_GROUND,
            style: { zIndex: constant_1.PANEL_GROUP_GROUP_CONTAINER_Z_INDEX },
        }));
        this.panelScrollGroup = new panel_scroll_group_1.PanelScrollGroup({
            name: constant_1.KEY_GROUP_PANEL_SCROLL,
            zIndex: constant_1.PANEL_GROUP_SCROLL_GROUP_Z_INDEX,
            s2: this.spreadsheet,
        });
        this.panelGroup.appendChild(this.panelScrollGroup);
    }
    getCellCustomSize(node, customSize) {
        return (0, lodash_1.isFunction)(customSize) ? customSize(node) : customSize;
    }
    getRowCellDraggedWidth(node) {
        var _a, _b, _c;
        const { rowCell } = this.spreadsheet.options.style;
        return ((_b = (_a = rowCell === null || rowCell === void 0 ? void 0 : rowCell.widthByField) === null || _a === void 0 ? void 0 : _a[node === null || node === void 0 ? void 0 : node.id]) !== null && _b !== void 0 ? _b : (_c = rowCell === null || rowCell === void 0 ? void 0 : rowCell.widthByField) === null || _c === void 0 ? void 0 : _c[node === null || node === void 0 ? void 0 : node.field]);
    }
    getRowCellDraggedHeight(node) {
        var _a, _b, _c;
        const { rowCell } = this.spreadsheet.options.style;
        return ((_b = (_a = rowCell === null || rowCell === void 0 ? void 0 : rowCell.heightByField) === null || _a === void 0 ? void 0 : _a[node === null || node === void 0 ? void 0 : node.id]) !== null && _b !== void 0 ? _b : (_c = rowCell === null || rowCell === void 0 ? void 0 : rowCell.heightByField) === null || _c === void 0 ? void 0 : _c[node === null || node === void 0 ? void 0 : node.field]);
    }
    isCustomRowCellHeight(node) {
        var _a;
        const { dataCell } = this.spreadsheet.options.style;
        const defaultDataCellHeight = (_a = constant_1.DEFAULT_STYLE.dataCell) === null || _a === void 0 ? void 0 : _a.height;
        return ((0, lodash_1.isNumber)(this.getCustomRowCellHeight(node)) ||
            (dataCell === null || dataCell === void 0 ? void 0 : dataCell.height) !== defaultDataCellHeight);
    }
    getCustomRowCellHeight(node) {
        var _a;
        const { rowCell } = this.spreadsheet.options.style;
        return ((_a = this.getRowCellDraggedHeight(node)) !== null && _a !== void 0 ? _a : this.getCellCustomSize(node, rowCell === null || rowCell === void 0 ? void 0 : rowCell.height));
    }
    getRowCellHeight(node) {
        var _a;
        const { dataCell } = this.spreadsheet.options.style;
        // 优先级: 行头拖拽 > 行头自定义高度 > 通用单元格高度
        return (_a = this.getCustomRowCellHeight(node)) !== null && _a !== void 0 ? _a : dataCell === null || dataCell === void 0 ? void 0 : dataCell.height;
    }
    getColCellDraggedWidth(node) {
        var _a, _b, _c, _d, _e, _f;
        const { colCell } = this.spreadsheet.options.style;
        // 指标的 field 是 $$extra$$, 对用户来说其实是 s2DataConfig.fields.values 里面的 field
        // 此时应该按 $$extra$$ 对应的 value field 匹配
        return ((_d = (_b = (_a = colCell === null || colCell === void 0 ? void 0 : colCell.widthByField) === null || _a === void 0 ? void 0 : _a[node === null || node === void 0 ? void 0 : node.id]) !== null && _b !== void 0 ? _b : (_c = colCell === null || colCell === void 0 ? void 0 : colCell.widthByField) === null || _c === void 0 ? void 0 : _c[node === null || node === void 0 ? void 0 : node.field]) !== null && _d !== void 0 ? _d : (_e = colCell === null || colCell === void 0 ? void 0 : colCell.widthByField) === null || _e === void 0 ? void 0 : _e[(_f = node === null || node === void 0 ? void 0 : node.query) === null || _f === void 0 ? void 0 : _f[constant_1.EXTRA_FIELD]]);
    }
    getColCellDraggedHeight(node) {
        var _a, _b, _c, _d, _e, _f;
        const { colCell } = this.spreadsheet.options.style;
        // 高度同理
        return ((_d = (_b = (_a = colCell === null || colCell === void 0 ? void 0 : colCell.heightByField) === null || _a === void 0 ? void 0 : _a[node === null || node === void 0 ? void 0 : node.id]) !== null && _b !== void 0 ? _b : (_c = colCell === null || colCell === void 0 ? void 0 : colCell.heightByField) === null || _c === void 0 ? void 0 : _c[node === null || node === void 0 ? void 0 : node.field]) !== null && _d !== void 0 ? _d : (_e = colCell === null || colCell === void 0 ? void 0 : colCell.heightByField) === null || _e === void 0 ? void 0 : _e[(_f = node === null || node === void 0 ? void 0 : node.query) === null || _f === void 0 ? void 0 : _f[constant_1.EXTRA_FIELD]]);
    }
    getColNodeHeight(options) {
        var _a, _b;
        const { colNode, colsHierarchy, useCache = true, cornerNodes = [], } = options;
        if (!colNode) {
            return 0;
        }
        const { colCell: colCellStyle, cornerCell: cornerCellStyle } = this.spreadsheet.options.style;
        // 优先级: 列头拖拽 > 列头自定义高度 > 多行文本自适应高度 > 通用单元格高度
        const height = (_a = this.getColCellDraggedHeight(colNode)) !== null && _a !== void 0 ? _a : this.getCellCustomSize(colNode, colCellStyle === null || colCellStyle === void 0 ? void 0 : colCellStyle.height);
        if ((0, lodash_1.isNumber)(height) && height !== ((_b = constant_1.DEFAULT_STYLE.colCell) === null || _b === void 0 ? void 0 : _b.height)) {
            // 标记为自定义高度, 方便计算文本 maxLines
            colNode.extra.isCustomHeight = true;
            return height;
        }
        const isEnableColNodeHeightAdaptive = ((colCellStyle === null || colCellStyle === void 0 ? void 0 : colCellStyle.maxLines) > 1 && (colCellStyle === null || colCellStyle === void 0 ? void 0 : colCellStyle.wordWrap)) ||
            this.spreadsheet.theme.colCell.text.fontSize > theme_1.DEFAULT_FONTSIZE ||
            this.spreadsheet.theme.colCell.bolderText.fontSize > theme_1.DEFAULT_FONTSIZE;
        const isEnableCornerNodeHeightAdaptive = ((cornerCellStyle === null || cornerCellStyle === void 0 ? void 0 : cornerCellStyle.maxLines) > 1 && (cornerCellStyle === null || cornerCellStyle === void 0 ? void 0 : cornerCellStyle.wordWrap)) ||
            this.spreadsheet.theme.cornerCell.text.fontSize > theme_1.DEFAULT_FONTSIZE ||
            this.spreadsheet.theme.cornerCell.bolderText.fontSize > theme_1.DEFAULT_FONTSIZE;
        const defaultHeight = this.getDefaultColNodeHeight(colNode, colsHierarchy);
        let colAdaptiveHeight = defaultHeight;
        let cornerAdaptiveHeight = defaultHeight;
        // 1. 列头开启自动换行, 计算列头自适应高度
        if (isEnableColNodeHeightAdaptive) {
            colAdaptiveHeight = this.getNodeAdaptiveHeight({
                meta: colNode,
                cell: this.textWrapTempColCell,
                defaultHeight,
                useCache,
            });
        }
        /**
         * 2. 角头开启自动换行, 列头的高度除了自身以外, 还需要考虑当前整行对应的角头
         * 存在角头/列头同时换行, 只有角头换行, 只有列头换行等多种场景
         */
        if (isEnableCornerNodeHeightAdaptive) {
            const currentCornerNodes = cornerNodes.filter((node) => {
                // 兼容数值置于行/列的不同场景
                if (colNode.isLeaf) {
                    return node.cornerType === interface_1.CornerNodeType.Row;
                }
                return node.field === colNode.field;
            });
            if (!(0, lodash_1.isEmpty)(currentCornerNodes)) {
                cornerAdaptiveHeight = (0, lodash_1.max)(currentCornerNodes.map((cornerNode) => this.getNodeAdaptiveHeight({
                    meta: cornerNode,
                    cell: this.textWrapTempCornerCell,
                    defaultHeight,
                    useCache: false,
                })));
            }
        }
        // 两者要取最大, 保证高度自动撑高的合理性
        return (0, math_1.round)(Math.max(cornerAdaptiveHeight, colAdaptiveHeight, defaultHeight));
    }
    getDefaultColNodeHeight(colNode, colsHierarchy) {
        var _a, _b, _c, _d;
        if (!colNode) {
            return 0;
        }
        const { colCell } = this.spreadsheet.options.style;
        // 当前层级高度最大的单元格
        const sampleMaxHeight = ((_b = (_a = colsHierarchy === null || colsHierarchy === void 0 ? void 0 : colsHierarchy.sampleNodesForAllLevels) === null || _a === void 0 ? void 0 : _a.find((node) => node.level === colNode.level)) === null || _b === void 0 ? void 0 : _b.height) || 0;
        // 优先级: 列头拖拽 > 列头自定义高度 > 通用单元格高度
        const defaultHeight = (_d = (_c = this.getColCellDraggedHeight(colNode)) !== null && _c !== void 0 ? _c : this.getCellCustomSize(colNode, colCell === null || colCell === void 0 ? void 0 : colCell.height)) !== null && _d !== void 0 ? _d : 0;
        return (0, math_1.round)(Math.max(defaultHeight, sampleMaxHeight));
    }
    getNodeAdaptiveHeight(options) {
        var _a, _b;
        const { meta, cell, defaultHeight = 0, useCache = true } = options;
        if (!meta || !cell) {
            return defaultHeight;
        }
        // 共用一个单元格用于测量, 通过动态更新 meta 的方式, 避免数据量大时频繁实例化触发 GC
        cell.setMeta(Object.assign(Object.assign({}, meta), { shallowRender: true }));
        const fieldValue = String(cell.getFieldValue());
        if (!fieldValue) {
            return defaultHeight;
        }
        const maxTextWidth = Math.ceil(cell.getMaxTextWidth());
        if (maxTextWidth <= 0 && cell.cellType === constant_1.CellType.COL_CELL) {
            return defaultHeight;
        }
        /**
         * [Bug Fix] 使用完整的 fieldValue 作为缓存键，确保准确性
         * 之前的 `size(fieldValue)` (即 fieldValue.length) 是不准确的
         * 相同长度的字符串，其渲染后的实际宽度可能完全不同
         * * */
        const cacheKey = `${fieldValue}${constant_1.NODE_ID_SEPARATOR}${maxTextWidth}`;
        const cacheHeight = this.textWrapNodeHeightCache.get(cacheKey);
        if (useCache && (0, lodash_1.isNumber)(cacheHeight)) {
            return cacheHeight || defaultHeight;
        }
        // 预生成 icon 配置, 用于计算文本正确的最大可用宽度
        (_b = (_a = cell).generateIconConfig) === null || _b === void 0 ? void 0 : _b.call(_a);
        cell.drawTextShape();
        const { padding } = cell.getStyle().cell;
        const textHeight = cell.getActualTextHeight();
        const adaptiveHeight = textHeight + padding.top + padding.bottom;
        const height = textHeight >= defaultHeight ? adaptiveHeight : defaultHeight;
        this.textWrapNodeHeightCache.set(cacheKey, height);
        return height;
    }
    /**
     * 根据叶子节点宽度计算所有父级节点宽度和 x 坐标
     */
    calculateColParentNodeWidthAndX(colLeafNodes) {
        var _a;
        let prevColParent = null;
        let i = 0;
        const leafNodes = colLeafNodes.slice(0);
        while (i < leafNodes.length) {
            const node = leafNodes[i++];
            const parentNode = node === null || node === void 0 ? void 0 : node.parent;
            if (prevColParent !== parentNode && parentNode) {
                leafNodes.push(parentNode);
                const firstVisibleChildNode = (_a = parentNode.children) === null || _a === void 0 ? void 0 : _a.find((childNode) => childNode.width);
                // 父节点 x 坐标 = 第一个未隐藏的子节点的 x 坐标
                const parentNodeX = (firstVisibleChildNode === null || firstVisibleChildNode === void 0 ? void 0 : firstVisibleChildNode.x) || 0;
                // 父节点宽度 = 所有子节点宽度之和
                const parentNodeWidth = (0, lodash_1.sumBy)(parentNode.children, 'width');
                parentNode.x = parentNodeX;
                parentNode.width = parentNodeWidth;
                prevColParent = parentNode;
            }
        }
    }
    /**
     * 将每一层级的采样节点更新为高度最大的节点 (未隐藏, 非汇总节点)
     */
    updateColsHierarchySampleMaxHeightNodes(colsHierarchy, rowsHierarchy) {
        var _a;
        const hasNotSample = (0, lodash_1.isEmpty)(colsHierarchy.sampleNodesForAllLevels);
        const sampleNodes = hasNotSample
            ? colsHierarchy.allNodesWithoutRoot
            : colsHierarchy.sampleNodesForAllLevels;
        const sampleMaxHeightNodesForAllLevels = sampleNodes.map((sampleNode) => {
            const maxHeightNode = (0, lodash_1.maxBy)(colsHierarchy
                .getNodes(sampleNode.level)
                .filter((node) => !node.isTotals || hasNotSample), (levelSampleNode) => {
                return this.getColNodeHeight({
                    colNode: levelSampleNode,
                    colsHierarchy,
                });
            });
            return maxHeightNode;
        });
        if (hasNotSample) {
            colsHierarchy.sampleNodeForLastLevel =
                (_a = sampleMaxHeightNodesForAllLevels[0]) !== null && _a !== void 0 ? _a : null;
            colsHierarchy.maxLevel = 0;
        }
        colsHierarchy.sampleNodesForAllLevels = (0, lodash_1.compact)(sampleMaxHeightNodesForAllLevels);
        const cornerNodes = rowsHierarchy
            ? header_1.CornerHeader.getCornerNodes({
                position: { x: 0, y: 0 },
                width: rowsHierarchy.width,
                height: colsHierarchy.height,
                layoutResult: {
                    rowsHierarchy,
                    colsHierarchy,
                },
                seriesNumberWidth: this.getSeriesNumberWidth(),
                spreadsheet: this.spreadsheet,
            })
            : [];
        colsHierarchy.sampleNodesForAllLevels.forEach((levelSampleNode) => {
            var _a;
            levelSampleNode.height = this.getColNodeHeight({
                colNode: levelSampleNode,
                colsHierarchy,
                cornerNodes,
            });
            if (levelSampleNode.level === 0) {
                levelSampleNode.y = 0;
            }
            else {
                const preLevelSample = (_a = colsHierarchy.sampleNodesForAllLevels[levelSampleNode.level - 1]) !== null && _a !== void 0 ? _a : {
                    y: 0,
                    height: 0,
                };
                levelSampleNode.y = preLevelSample.y + preLevelSample.height;
            }
            colsHierarchy.height += levelSampleNode.height;
        });
        colsHierarchy.rootNode.height = colsHierarchy.height;
    }
    render() {
        if (!this.shouldRender()) {
            return;
        }
        this.adjustScrollOffset();
        this.renderHeaders();
        this.renderScrollBars();
        this.renderBackground();
        this.dynamicRenderCell(true);
    }
    /**
     * 在每次render, 校验scroll offset是否在合法范围中
     * 比如在滚动条已经滚动到100%的状态的前提下：（ maxAvailableScrollOffsetX = colsHierarchy.width - viewportBBox.width ）
     *     此时changeSheetSize，sheet从 small width 变为 big width
     *     导致后者 viewport 区域更大，其结果就是后者的 maxAvailableScrollOffsetX 更小
     *     此时就需要重置 scrollOffsetX，否则就会导致滚动过多，出现空白区域
     */
    adjustScrollOffset() {
        const offset = this.getAdjustedScrollOffset(this.getScrollOffset());
        this.setScrollOffset(offset);
    }
    getSeriesNumberWidth() {
        var _a, _b;
        const { seriesNumber } = this.spreadsheet.options;
        return (0, math_1.round)((seriesNumber === null || seriesNumber === void 0 ? void 0 : seriesNumber.enable)
            ? (_b = (_a = this.spreadsheet.theme.rowCell) === null || _a === void 0 ? void 0 : _a.seriesNumberWidth) !== null && _b !== void 0 ? _b : 0
            : 0);
    }
    getCanvasSize() {
        const { width = 0, height = 0 } = this.spreadsheet.options;
        return {
            width,
            height,
        };
    }
    /**
     * @alias s2.interaction.scrollTo(offsetConfig)
     */
    updateScrollOffset(offsetConfig) {
        var _a, _b, _c;
        if (((_a = offsetConfig.rowHeaderOffsetX) === null || _a === void 0 ? void 0 : _a.animate) ||
            ((_b = offsetConfig.offsetX) === null || _b === void 0 ? void 0 : _b.animate) ||
            ((_c = offsetConfig.offsetY) === null || _c === void 0 ? void 0 : _c.animate)) {
            this.scrollWithAnimation(offsetConfig);
        }
        else {
            this.scrollImmediately(offsetConfig);
        }
    }
    getPaginationScrollY() {
        const { pagination } = this.spreadsheet.options;
        if (pagination) {
            const { current = pagination_1.DEFAULT_PAGE_INDEX, pageSize } = pagination;
            const heights = this.viewCellHeights;
            const offset = Math.max((current - 1) * pageSize, 0);
            return heights.getCellOffsetY(offset);
        }
        return 0;
    }
    destroy() {
        this.unbindEvents();
        this.clearAllGroup();
        this.preCellIndexes = null;
        this.customRowHeightStatusMap = {};
        this.textWrapNodeHeightCache.clear(false);
        cancelAnimationFrame(this.scrollFrameId);
    }
    calculateCornerBBox() {
        this.cornerBBox = new corner_bbox_1.CornerBBox(this, true);
    }
    calculatePanelBBox() {
        this.panelBBox = new panel_bbox_1.PanelBBox(this, true);
    }
    getCellRange() {
        const { pagination } = this.spreadsheet.options;
        return (0, utils_1.getCellRange)(this.viewCellHeights, pagination);
    }
    clearAllGroup() {
        this.panelGroup.remove();
        this.foregroundGroup.remove();
        this.backgroundGroup.remove();
    }
    renderRowScrollBar(rowHeaderScrollX) {
        if (this.spreadsheet.isFrozenRowHeader() &&
            this.cornerBBox.width < this.cornerBBox.originalWidth) {
            const maxOffset = this.cornerBBox.originalWidth - this.cornerBBox.width;
            const { maxY } = this.getScrollbarPosition();
            const { splitLine, scrollBar } = this.spreadsheet.theme;
            const thumbSize = Math.max((this.cornerBBox.width * this.cornerBBox.width) /
                this.cornerBBox.originalWidth, scrollBar === null || scrollBar === void 0 ? void 0 : scrollBar.thumbHorizontalMinSize);
            // 行头有分割线, 滚动条应该预留分割线的宽度
            const displayThumbSize = thumbSize - (splitLine === null || splitLine === void 0 ? void 0 : splitLine.verticalBorderWidth);
            this.hRowScrollBar = new scrollbar_1.ScrollBar({
                isHorizontal: true,
                trackLen: this.cornerBBox.width,
                thumbLen: displayThumbSize,
                position: {
                    x: this.cornerBBox.minX + this.scrollBarSize / 2,
                    y: maxY,
                },
                thumbOffset: (rowHeaderScrollX * (this.cornerBBox.width - thumbSize)) / maxOffset,
                theme: this.scrollBarTheme,
                scrollTargetMaxOffset: maxOffset,
            });
            this.hRowScrollBar.addEventListener(scrollbar_1.ScrollType.ScrollChange, ({ offset }) => {
                var _a, _b;
                const newOffset = this.getValidScrollBarOffset(offset, maxOffset);
                const newRowHeaderScrollX = (0, math_1.floor)(newOffset);
                this.setScrollOffset({ rowHeaderScrollX: newRowHeaderScrollX });
                (_a = this.rowHeader) === null || _a === void 0 ? void 0 : _a.onRowScrollX(newRowHeaderScrollX, constant_1.KEY_GROUP_ROW_RESIZE_AREA);
                (_b = this.seriesNumberHeader) === null || _b === void 0 ? void 0 : _b.onRowScrollX(newRowHeaderScrollX, constant_1.KEY_GROUP_ROW_INDEX_RESIZE_AREA);
                this.cornerHeader.onRowScrollX(newRowHeaderScrollX, constant_1.KEY_GROUP_CORNER_RESIZE_AREA);
                const scrollBarOffsetX = this.getScrollBarOffset(newRowHeaderScrollX, this.hRowScrollBar);
                const { scrollX, scrollY } = this.getScrollOffset();
                const position = {
                    scrollX,
                    scrollY,
                    rowHeaderScrollX: scrollBarOffsetX,
                };
                this.hRowScrollBar.updateThumbOffset(scrollBarOffsetX, false);
                this.spreadsheet.emit(constant_1.S2Event.ROW_CELL_SCROLL, position);
                this.spreadsheet.emit(constant_1.S2Event.GLOBAL_SCROLL, position);
            });
            this.foregroundGroup.appendChild(this.hRowScrollBar);
        }
    }
    getValidScrollBarOffset(offset, maxOffset) {
        return (0, lodash_1.clamp)(offset, 0, maxOffset);
    }
    renderHScrollBar(width, realWidth, scrollX) {
        if ((0, math_1.floor)(width) < (0, math_1.floor)(realWidth)) {
            const halfScrollSize = this.scrollBarSize / 2;
            const { maxY } = this.getScrollbarPosition();
            const isScrollContainsRowHeader = !this.spreadsheet.isFrozenRowHeader();
            const finalWidth = width +
                (isScrollContainsRowHeader
                    ? Math.min(this.cornerBBox.width, this.getCanvasSize().width)
                    : 0);
            const finalPosition = {
                x: this.panelBBox.minX +
                    (isScrollContainsRowHeader
                        ? -this.cornerBBox.width + halfScrollSize
                        : halfScrollSize),
                y: maxY,
            };
            const finaleRealWidth = realWidth + (isScrollContainsRowHeader ? this.cornerBBox.width : 0);
            const { scrollBar } = this.spreadsheet.theme;
            const maxOffset = finaleRealWidth - finalWidth;
            const thumbLen = Math.max((finalWidth / finaleRealWidth) * finalWidth, scrollBar === null || scrollBar === void 0 ? void 0 : scrollBar.thumbHorizontalMinSize);
            this.hScrollBar = new scrollbar_1.ScrollBar({
                isHorizontal: true,
                trackLen: finalWidth,
                thumbLen,
                position: finalPosition,
                thumbOffset: (scrollX * (finalWidth - thumbLen)) / maxOffset,
                theme: this.scrollBarTheme,
                scrollTargetMaxOffset: maxOffset,
            });
            this.hScrollBar.addEventListener(scrollbar_1.ScrollType.ScrollChange, ({ offset, updateThumbOffset }) => {
                const newScrollX = this.getValidScrollBarOffset(offset, maxOffset);
                if (updateThumbOffset) {
                    this.hScrollBar.updateThumbOffset(this.getScrollBarOffset(newScrollX, this.hScrollBar), false);
                }
                this.setScrollOffset({
                    scrollX: newScrollX,
                });
                this.dynamicRenderCell();
            });
            this.foregroundGroup.appendChild(this.hScrollBar);
        }
    }
    getScrollbarPosition() {
        const { maxX, maxY } = this.panelBBox;
        const { width, height } = this.getCanvasSize();
        const isContentMode = this.spreadsheet.options.interaction.scrollbarPosition ===
            constant_1.ScrollbarPositionType.CONTENT;
        return {
            maxX: (isContentMode ? maxX : width) - this.scrollBarSize,
            maxY: (isContentMode ? maxY : height) - this.scrollBarSize,
        };
    }
    renderVScrollBar(height, realHeight, scrollY) {
        if (height < realHeight) {
            const { scrollBar } = this.spreadsheet.theme;
            const thumbLen = Math.max((height / realHeight) * height, scrollBar === null || scrollBar === void 0 ? void 0 : scrollBar.thumbVerticalMinSize);
            const maxOffset = realHeight - height;
            const { maxX } = this.getScrollbarPosition();
            this.vScrollBar = new scrollbar_1.ScrollBar({
                isHorizontal: false,
                trackLen: height,
                thumbLen,
                thumbOffset: (scrollY * (height - thumbLen)) / maxOffset,
                position: {
                    x: maxX,
                    y: this.panelBBox.minY,
                },
                theme: this.scrollBarTheme,
                scrollTargetMaxOffset: maxOffset,
            });
            this.vScrollBar.addEventListener(scrollbar_1.ScrollType.ScrollChange, ({ offset, updateThumbOffset }) => {
                const newScrollY = this.getValidScrollBarOffset(offset, maxOffset);
                if (updateThumbOffset) {
                    this.vScrollBar.updateThumbOffset(this.getScrollBarOffset(newScrollY, this.vScrollBar), false);
                }
                this.setScrollOffset({ scrollY: newScrollY });
                this.dynamicRenderCell();
            });
            this.foregroundGroup.appendChild(this.vScrollBar);
        }
    }
    /**
     * Translate panelGroup, rowHeader, cornerHeader, columnHeader ect
     * according to new scroll offset
     * @param scrollX
     * @param scrollY
     * @param hRowScroll
     * @protected
     */
    translateRelatedGroups(scrollX, scrollY, hRowScroll) {
        var _a, _b, _c, _d, _e, _f;
        (0, utils_1.translateGroup)(this.panelScrollGroup, this.panelBBox.x - scrollX, this.panelBBox.y - scrollY);
        (_a = this.rowHeader) === null || _a === void 0 ? void 0 : _a.onScrollXY(this.getRealScrollX(scrollX, hRowScroll), scrollY, constant_1.KEY_GROUP_ROW_RESIZE_AREA);
        (_b = this.seriesNumberHeader) === null || _b === void 0 ? void 0 : _b.onScrollXY(this.getRealScrollX(scrollX, hRowScroll), scrollY, constant_1.KEY_GROUP_ROW_INDEX_RESIZE_AREA);
        (_c = this.cornerHeader) === null || _c === void 0 ? void 0 : _c.onCorScroll(this.getRealScrollX(scrollX, hRowScroll), constant_1.KEY_GROUP_CORNER_RESIZE_AREA);
        (_d = this.centerFrame) === null || _d === void 0 ? void 0 : _d.onChangeShadowVisibility(scrollX, this.getRealWidth() - this.panelBBox.width);
        (_e = this.centerFrame) === null || _e === void 0 ? void 0 : _e.onBorderScroll(this.getCenterFrameScrollX(scrollX));
        (_f = this.columnHeader) === null || _f === void 0 ? void 0 : _f.onColScroll(scrollX, constant_1.KEY_GROUP_COL_RESIZE_AREA);
    }
    getCenterFrameScrollX(scrollX) {
        return this.getRealScrollX(scrollX);
    }
    createDataCell(viewMeta) {
        var _a, _b, _c;
        if (!viewMeta) {
            return;
        }
        let cell;
        if (this.dataCellPool.pool.length > 0 &&
            ((_a = this.spreadsheet.options.future) === null || _a === void 0 ? void 0 : _a.experimentalReuseCell)) {
            cell = this.dataCellPool.acquire();
            cell.setMeta(viewMeta);
        }
        else {
            cell = (_c = (_b = this.spreadsheet.options).dataCell) === null || _c === void 0 ? void 0 : _c.call(_b, viewMeta, this.spreadsheet);
        }
        if (!cell) {
            return;
        }
        const { rowIndex, colIndex } = viewMeta;
        cell.position = [rowIndex, colIndex];
        cell.name = `${rowIndex}-${colIndex}`;
        return cell;
    }
    init() {
        this.initCellPool();
        this.initTextWrapTemp();
        this.initGroups();
        // layout
        debug_1.DebuggerUtil.getInstance().debugCallback(debug_1.DEBUG_HEADER_LAYOUT, () => {
            this.layoutResult = this.doLayout();
            this.saveInitColumnLeafNodes(this.layoutResult.colLeafNodes);
            this.spreadsheet.emit(constant_1.S2Event.LAYOUT_AFTER_HEADER_LAYOUT, this.layoutResult);
        });
        // all cell's width&height
        this.calculateCellWidthHeight();
        this.calculateRowOffsets();
        this.calculateCornerBBox();
        this.calculatePanelBBox();
        this.bindEvents();
    }
    renderBackground() {
        const { width, height } = this.getCanvasSize();
        const { color, opacity } = this.spreadsheet.theme.background;
        this.backgroundGroup.appendChild(new g_1.Rect({
            style: {
                fill: color,
                opacity,
                x: 0,
                y: 0,
                width,
                height,
            },
        }));
    }
    /**
     * 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
     */
    renderScrollBars() {
        const { scrollX, scrollY, rowHeaderScrollX } = this.getScrollOffset();
        const { width, height } = this.panelBBox;
        const realWidth = this.layoutResult.colsHierarchy.width;
        const realHeight = this.getRealHeight();
        // scroll row header separate from the whole canvas
        this.renderRowScrollBar(rowHeaderScrollX);
        // render horizontal scroll bar(default not contains row header)
        this.renderHScrollBar(width, realWidth, scrollX);
        // render vertical scroll bar
        this.renderVScrollBar(height, realHeight, scrollY);
    }
    /**
     * Render all headers in {@link #foregroundGroup}, contains:
     * 1. row header
     * 2. col header
     * 3. center frame
     * 4. corner header
     * 5. series number header
     */
    renderHeaders() {
        const seriesNumberWidth = this.getSeriesNumberWidth();
        this.rowHeader = this.getRowHeader();
        this.columnHeader = this.getColHeader();
        if (seriesNumberWidth > 0 && !this.seriesNumberHeader) {
            this.seriesNumberHeader = this.getSeriesNumberHeader();
        }
        this.cornerHeader = this.getCornerHeader();
        this.centerFrame = this.getCenterFrame();
        if (this.seriesNumberHeader) {
            this.foregroundGroup.appendChild(this.seriesNumberHeader);
        }
        if (this.rowHeader) {
            this.foregroundGroup.appendChild(this.rowHeader);
        }
        this.foregroundGroup.appendChild(this.columnHeader);
        this.foregroundGroup.appendChild(this.cornerHeader);
        this.foregroundGroup.appendChild(this.centerFrame);
    }
    getRowHeaderCfg() {
        const { y, viewportHeight, viewportWidth, height } = this.panelBBox;
        const seriesNumberWidth = this.getSeriesNumberWidth();
        return {
            width: this.cornerBBox.width,
            height,
            viewportWidth,
            viewportHeight,
            position: { x: seriesNumberWidth, y },
            nodes: this.layoutResult.rowNodes,
            spreadsheet: this.spreadsheet,
        };
    }
    getRowHeader() {
        if (!this.rowHeader) {
            return new header_1.RowHeader(this.getRowHeaderCfg());
        }
        return this.rowHeader;
    }
    getColHeader() {
        if (!this.columnHeader) {
            const { x, width, viewportHeight, viewportWidth } = this.panelBBox;
            return new header_1.ColHeader({
                width,
                cornerWidth: this.cornerBBox.width,
                height: this.cornerBBox.height,
                viewportWidth,
                viewportHeight,
                position: { x, y: 0 },
                nodes: this.layoutResult.colNodes,
                sortParam: this.spreadsheet.store.get('sortParam'),
                spreadsheet: this.spreadsheet,
            });
        }
        return this.columnHeader;
    }
    getCornerHeader() {
        return (this.cornerHeader ||
            header_1.CornerHeader.getCornerHeader({
                panelBBox: this.panelBBox,
                cornerBBox: this.cornerBBox,
                seriesNumberWidth: this.getSeriesNumberWidth(),
                layoutResult: this.layoutResult,
                spreadsheet: this.spreadsheet,
            }));
    }
    getSeriesNumberHeader() {
        return (this.seriesNumberHeader ||
            header_1.SeriesNumberHeader.getSeriesNumberHeader({
                spreadsheet: this.spreadsheet,
                panelBBox: this.panelBBox,
                cornerWidth: this.cornerBBox.width,
                seriesNumberWidth: this.getSeriesNumberWidth(),
                rowsHierarchy: this.layoutResult.rowsHierarchy,
            }));
    }
    getCenterFrame() {
        var _a;
        if (!this.centerFrame) {
            const { viewportWidth, viewportHeight } = this.panelBBox;
            const cornerWidth = this.cornerBBox.width;
            const cornerHeight = this.cornerBBox.height;
            const frame = (_a = this.spreadsheet.options) === null || _a === void 0 ? void 0 : _a.frame;
            const frameCfg = {
                position: {
                    x: this.cornerBBox.x,
                    y: this.cornerBBox.y,
                },
                cornerWidth,
                cornerHeight,
                viewportWidth,
                viewportHeight,
                showViewportLeftShadow: false,
                showViewportRightShadow: false,
                spreadsheet: this.spreadsheet,
            };
            return frame ? frame(frameCfg) : new header_1.Frame(frameCfg);
        }
        return this.centerFrame;
    }
    updatePanelScrollGroup() {
        this.gridInfo = this.getGridInfo();
        this.panelScrollGroup.update(this.gridInfo);
    }
    /**
     * @param skipScrollEvent: 不触发 GLOBAL_SCROLL 事件
     */
    dynamicRenderCell(skipScrollEvent) {
        const { scrollX, scrollY: originalScrollY, rowHeaderScrollX, } = this.getScrollOffset();
        const scrollY = originalScrollY + this.getPaginationScrollY();
        this.spreadsheet.hideTooltip();
        this.spreadsheet.interaction.clearHoverTimer();
        this.realDataCellRender(scrollX, scrollY);
        this.updatePanelScrollGroup();
        this.translateRelatedGroups(scrollX, scrollY, rowHeaderScrollX);
        this.clip(scrollX, scrollY);
        if (!skipScrollEvent) {
            this.emitScrollEvent({ scrollX, scrollY, rowHeaderScrollX });
        }
        this.onAfterScroll();
    }
    emitScrollEvent(position) {
        this.spreadsheet.emit(constant_1.S2Event.GLOBAL_SCROLL, position);
    }
    saveInitColumnLeafNodes(columnNodes = []) {
        const { store, options } = this.spreadsheet;
        const { hiddenColumnFields = [] } = options.interaction;
        // 当前显示的 + 被隐藏的
        const originalColumnsLength = columnNodes.length + hiddenColumnFields.length;
        const initColLeafNodes = this.getInitColLeafNodes();
        if (originalColumnsLength !== (initColLeafNodes === null || initColLeafNodes === void 0 ? void 0 : initColLeafNodes.length)) {
            store.set('initColLeafNodes', columnNodes);
        }
    }
    getHiddenColumnsInfo(columnNode) {
        const hiddenColumnsDetail = this.spreadsheet.store.get('hiddenColumnsDetail', []);
        if ((0, lodash_1.isEmpty)(hiddenColumnsDetail)) {
            return;
        }
        return hiddenColumnsDetail.find((detail) => { var _a; return (_a = detail === null || detail === void 0 ? void 0 : detail.hideColumnNodes) === null || _a === void 0 ? void 0 : _a.some((node) => node.id === columnNode.id); });
    }
    updateCustomFieldsSampleNodes(colsHierarchy) {
        var _a;
        if (!this.spreadsheet.isCustomColumnFields()) {
            return;
        }
        const levelIds = ((_a = colsHierarchy.sampleNodeForLastLevel) === null || _a === void 0 ? void 0 : _a.id.split(constant_1.NODE_ID_SEPARATOR)) || [];
        // 每一列层级不定, 用层级最深的那一列采样高度
        const nodes = colsHierarchy.getNodes().filter((node) => {
            var _a;
            return (((_a = colsHierarchy.sampleNodeForLastLevel) === null || _a === void 0 ? void 0 : _a.id.includes(node.id)) &&
                levelIds.includes(node.field));
        });
        colsHierarchy.sampleNodesForAllLevels = nodes;
        colsHierarchy.height = (0, lodash_1.sumBy)(nodes, 'height');
    }
    /**
     * @description 自定义行头时, 叶子节点层级不定, 需要自动对齐其宽度, 填充空白
     * -------------------------------------------------
     * |  自定义节点 a-1  |  自定义节点 a-1-1              |
     * |-------------   |-------------     |------------|
     * |  自定义节点 b-1  |  自定义节点 b-1-1 |  指标 1    |
     * -------------------------------------------------
     */
    adjustCustomRowLeafNodesWidth(params) {
        if (!this.spreadsheet.isCustomRowFields()) {
            return;
        }
        this.adjustLeafNodesSize('width')(params);
    }
    /**
     * @description 自定义列头时, 叶子节点层级不定, 需要自动对齐其高度, 填充空白
     * ------------------------------------------------------------------------
     * |                       自定义节点 a-1                                   |
     * |----------------------------------------------------------------------|
     * |                 自定义节点 a-1-1               |                       |
     * |-------------|-------------|------------------|   自定义节点 a-1-2      |
     * |   指标 1    |  自定义节点 a-1-1-1    | 指标 2  |                        |
     * ----------------------------------------------------------------------
     */
    adjustCustomColLeafNodesHeight(params) {
        if (!this.spreadsheet.isCustomColumnFields()) {
            return;
        }
        this.adjustLeafNodesSize('height')(params);
    }
    adjustLeafNodesSize(type) {
        return ({ leafNodes, hierarchy }) => {
            const { sampleNodeForLastLevel, sampleNodesForAllLevels } = hierarchy;
            leafNodes.forEach((node) => {
                if (node.level > (sampleNodeForLastLevel === null || sampleNodeForLastLevel === void 0 ? void 0 : sampleNodeForLastLevel.level)) {
                    return;
                }
                const leafNodeSize = (0, lodash_1.sumBy)(sampleNodesForAllLevels, (sampleNode) => {
                    if (sampleNode.level < node.level) {
                        return 0;
                    }
                    return sampleNode[type];
                });
                node[type] = leafNodeSize;
            });
        };
    }
    /**
     * 获取表头节点 (角头,序号,行头,列头) (含可视区域)
     * @example 获取全部: facet.getHeaderNodes()
     * @example 获取一组 facet.getHeaderNodes(['root[&]浙江省[&]宁波市', 'root[&]浙江省[&]杭州市'])
     */
    getHeaderNodes(nodeIds) {
        const headerNodes = (0, lodash_1.concat)(this.getCornerNodes(), this.getSeriesNumberNodes(), this.getRowNodes(), this.getColNodes());
        if (!nodeIds) {
            return headerNodes;
        }
        return headerNodes.filter((node) => nodeIds.includes(node.id));
    }
    /**
     * 获取角头节点
     */
    getCornerNodes() {
        var _a;
        return ((_a = this.cornerHeader) === null || _a === void 0 ? void 0 : _a.getNodes()) || [];
    }
    /**
     * 获取序号节点
     */
    getSeriesNumberNodes() {
        var _a;
        return ((_a = this.seriesNumberHeader) === null || _a === void 0 ? void 0 : _a.getNodes()) || [];
    }
    /**
     * 获取列头节点 (含非可视区域)
     * @description 获取列头单元格 (可视区域内) facet.getColCells()
     * @example 获取全部: facet.getColNodes()
     * @example 指定层级: facet.getColNodes(level)
     */
    getColNodes(level) {
        const { colNodes = [] } = this.layoutResult;
        if ((0, lodash_1.isNil)(level)) {
            return colNodes;
        }
        return colNodes.filter((node) => node.level === level);
    }
    getTopLevelColNodes() {
        return this.getColNodes(0);
    }
    /**
     * 根据 id 获取指定列头节点
     * @example facet.getColNodeById('root[&]节点1[&]数值')
     */
    getColNodeById(nodeId) {
        return this.getColNodes().find((node) => node.id === nodeId);
    }
    /**
     * 根据列头索引获取指定列头节点
     * @example facet.getColNodeByIndex(colIndex)
     */
    getColNodeByIndex(colIndex) {
        return this.getColNodes().find((node) => node.colIndex === colIndex);
    }
    /**
     * 获取在索引范围内的列头叶子节点
     * @example facet.getColLeafNodesByRange(0,10) 获取索引范围在 0（包括 0） 到 10（包括 10）的列头叶子节点
     */
    getColLeafNodesByRange(minIndex, maxIndex) {
        return this.getColLeafNodes().filter((node) => node.colIndex >= minIndex && node.colIndex <= maxIndex);
    }
    /**
     * 根据列头索引获取指定列头叶子节点
     * @example facet.getColLeafNodes(colIndex)
     */
    getColLeafNodeByIndex(colIndex) {
        return this.getColLeafNodes().find((node) => node.colIndex === colIndex);
    }
    /**
     * 根据 field 获取指定列头节点
     * @example facet.getColCellNodeByField('number')
     */
    getColNodesByField(nodeField) {
        return this.getColNodes().filter((node) => node.field === nodeField);
    }
    /**
     * 获取列头叶子节点节点 (含非可视区域)
     */
    getColLeafNodes() {
        var _a;
        return ((_a = this.layoutResult) === null || _a === void 0 ? void 0 : _a.colLeafNodes) || [];
    }
    /**
     * 获取列头小计/总计节点 (含非可视区域)
     * @example 获取全部: facet.getColTotalsNodes()
     * @example 指定层级: facet.getColTotalsNodes(level)
     */
    getColTotalsNodes(level) {
        return this.getColNodes(level).filter((node) => node.isTotals);
    }
    /**
     * 获取列头小计节点 (含非可视区域)
     * @example 获取全部: facet.getColSubTotalsNodes()
     * @example 指定层级: facet.getColSubTotalsNodes(level)
     */
    getColSubTotalsNodes(level) {
        return this.getColTotalsNodes(level).filter((node) => node.isSubTotals);
    }
    /**
     * 获取列头总计节点 (含非可视区域)
     * @example 获取全部: facet.getColGrandTotalsNodes()
     * @example 指定层级: facet.getColGrandTotalsNodes(level)
     */
    getColGrandTotalsNodes(level) {
        return this.getColTotalsNodes(level).filter((node) => node.isGrandTotals);
    }
    /**
     * 获取行头节点 (含非可视区域)
     * @description 获取行头单元格 (可视区域内) facet.getRowCells()
     * @example 获取全部: facet.getRowNodes()
     * @example 指定层级: facet.getRowNodes(level)
     */
    getRowNodes(level) {
        const { rowNodes = [] } = this.layoutResult;
        if ((0, lodash_1.isNil)(level)) {
            return rowNodes;
        }
        return rowNodes.filter((node) => node.level === level);
    }
    /**
     * 根据 id 获取单个行头节点
     * @example facet.getRowNodeById('root[&]节点1[&]数值')
     */
    getRowNodeById(nodeId) {
        return this.getRowNodes().find((node) => node.id === nodeId);
    }
    /**
     * 根据行头索引获取指定列头节点
     * @example facet.getRowNodeByIndex(rowIndex)
     */
    getRowNodeByIndex(rowIndex) {
        return this.getRowNodes().find((node) => node.rowIndex === rowIndex);
    }
    /**
     * 根据行头索引获取指定列头叶子节点
     * @example facet.getRowLeafNodeByIndex(rowIndex)
     */
    getRowLeafNodeByIndex(rowIndex) {
        return this.getRowLeafNodes().find((node) => node.rowIndex === rowIndex);
    }
    /**
     * 获取在索引范围内的行头叶子节点
     * @example facet.getRowLeafNodesByRange(0,10) 获取索引范围在 0（包括 0） 到 10（包括 10）的行头叶子节点
     */
    getRowLeafNodesByRange(minIndex, maxIndex) {
        return this.getRowLeafNodes().filter((node) => node.rowIndex >= minIndex && node.rowIndex <= maxIndex);
    }
    /**
     * 根据 field 获取行头节点
     * @example facet.getRowNodeByField('number')
     */
    getRowNodesByField(nodeField) {
        return this.getRowNodes().filter((node) => node.field === nodeField);
    }
    /**
     * 获取行头叶子节点节点 (含非可视区域)
     * @example 获取全部: facet.getRowLeafNodes()
     */
    getRowLeafNodes() {
        var _a;
        return ((_a = this.layoutResult) === null || _a === void 0 ? void 0 : _a.rowLeafNodes) || [];
    }
    /**
     * 获取行头小计/总计节点 (含非可视区域)
     * @example 获取全部: facet.getRowTotalsNodes()
     * @example 指定层级: facet.getRowTotalsNodes(level)
     */
    getRowTotalsNodes(level) {
        return this.getRowNodes(level).filter((node) => node.isTotals);
    }
    /**
     * 获取行头小计节点 (含非可视区域)
     * @example 获取全部: facet.getRowSubTotalsNodes()
     * @example 指定层级: facet.getRowSubTotalsNodes(level)
     */
    getRowSubTotalsNodes(level) {
        return this.getRowTotalsNodes(level).filter((node) => node.isSubTotals);
    }
    /**
     * 获取行头总计节点 (含非可视区域)
     * @example 获取全部: facet.getRowGrandTotalsNodes()
     * @example 指定层级: facet.getRowGrandTotalsNodes(level)
     */
    getRowGrandTotalsNodes(level) {
        return this.getRowTotalsNodes(level).filter((node) => node.isGrandTotals);
    }
    /**
     * 获取数值单元格 (不含可视区域)
     * @description 由于虚拟滚动, 按需加载的特性, 非可视区域的单元格未被实例化
     * @example 获取非可视区域的数值节点 facet.getCellMeta(rowIndex, colIndex)
     */
    getDataCells() {
        var _a;
        return (0, get_all_child_cells_1.getAllChildCells)((_a = this.panelGroup) === null || _a === void 0 ? void 0 : _a.children, cell_1.DataCell);
    }
    /**
     * 获取行头单元格 (不含可视区域)
     */
    getRowCells() {
        var _a;
        const headerChildren = (((_a = this.getRowHeader()) === null || _a === void 0 ? void 0 : _a.children) || []);
        return (0, get_all_child_cells_1.getAllChildCells)(headerChildren, cell_1.RowCell).filter((cell) => cell.cellType === constant_1.CellType.ROW_CELL);
    }
    /**
     * 获取行头叶子节点单元格 (不含可视区域)
     */
    getRowLeafCells() {
        return this.getRowCells().filter((cell) => cell.getMeta().isLeaf);
    }
    /**
     * 获取列头单元格 (不含可视区域)
     */
    getColCells() {
        var _a;
        const headerChildren = (((_a = this.getColHeader()) === null || _a === void 0 ? void 0 : _a.children) || []);
        return (0, get_all_child_cells_1.getAllChildCells)(headerChildren, cell_1.ColCell).filter((cell) => cell.cellType === constant_1.CellType.COL_CELL);
    }
    /**
     * 获取列头叶子节点单元格 (不含可视区域)
     */
    getColLeafCells() {
        return this.getColCells().filter((cell) => cell.getMeta().isLeaf);
    }
    /**
     * 获取合并单元格 (不含可视区域)
     */
    getMergedCells() {
        return (0, lodash_1.filter)(this.panelScrollGroup.getMergedCellsGroup().children, (element) => element instanceof cell_1.MergedCell);
    }
    /**
     * 获取角头单元格 (不含可视区域)
     */
    getCornerCells() {
        var _a;
        const headerChildren = (((_a = this.getCornerHeader()) === null || _a === void 0 ? void 0 : _a.children) ||
            []);
        return (0, get_all_child_cells_1.getAllChildCells)(headerChildren, cell_1.CornerCell).filter((cell) => cell.cellType === constant_1.CellType.CORNER_CELL);
    }
    filterCells(cells, filterIds) {
        if ((0, lodash_1.isEmpty)(filterIds)) {
            return cells;
        }
        if ((0, lodash_1.isArray)(filterIds)) {
            return cells.filter((cell) => {
                return (0, lodash_1.includes)(filterIds, cell.getMeta().id);
            });
        }
        return cells.filter((cell) => {
            const ids = filterIds[cell.cellType];
            if (!ids) {
                return false;
            }
            return ids.includes(cell.getMeta().id);
        });
    }
    /**
     * 获取表头单元格 (序号,角头,行头,列头) (不含可视区域)
     * @example 获取全部: facet.getHeaderCells()
     * @example 获取一组 facet.getHeaderCells(['root[&]浙江省[&]宁波市', 'root[&]浙江省[&]杭州市'])
     */
    getHeaderCells(cellIds) {
        const headerCells = (0, lodash_1.concat)(this.getCornerCells(), this.getSeriesNumberCells(), this.getRowCells(), this.getColCells());
        return this.filterCells(headerCells, cellIds);
    }
    /**
     * 根据单元格 id 获取指定单元格 (不含可视区域)
     * @example facet.getCellById('root[&]浙江省[&]宁波市')
     */
    getCellById(cellId) {
        return this.getCells().find((cell) => cell.getMeta().id === cellId);
    }
    /**
     * 根据单元格 field 获取指定单元格 (不含可视区域)
     * @example facet.getCellByField('city')
     */
    getCellsByField(cellField) {
        return this.getCells().filter((cell) => cell.getMeta().field === cellField);
    }
    /**
     * 获取所有单元格 (角头,行头,列头,数值) (不含可视区域)
     * @example 获取全部: facet.getCells()
     * @example 获取一组 facet.getCells(['root[&]浙江省[&]宁波市', 'root[&]浙江省[&]杭州市'])
     */
    getCells(cellIds) {
        const cells = (0, lodash_1.concat)(this.getHeaderCells(), this.getDataCells());
        if (!cellIds) {
            return cells;
        }
        return cells.filter((cell) => cellIds.includes(cell.getMeta().id));
    }
    getInitColIndexLeafNodes() {
        return this.layoutResult.colsHierarchy.getIndexNodes() || [];
    }
    getInitColLeafNodes() {
        return this.spreadsheet.store.get('initColLeafNodes', []);
    }
    clearInitColLeafNodes() {
        this.spreadsheet.store.set('initColLeafNodes', undefined);
    }
    /**
     * @tip 和 this.spreadsheet.measureTextWidth() 的区别在于:
     * 1. 额外添加一像素余量，防止 maxLabel 有多个同样长度情况下，一些 label 不能展示完全, 出现省略号
     * 2. 测量时, 文本宽度取整, 避免子像素的不一致性
     */
    measureTextWidth(text, font) {
        const EXTRA_PIXEL = 1;
        return (Math.ceil(this.spreadsheet.measureTextWidth(text, font)) + EXTRA_PIXEL);
    }
    initCellPool() {
        this.dataCellPool = new pool_1.DataCellPool();
    }
}
exports.BaseFacet = BaseFacet;
//# sourceMappingURL=base-facet.js.map