/// <reference types="node" />
import Paint from "./Paint";
import Body from "./Body";
import Header from "./Header";
import Footer from "./Footer";
import Editor from "./Editor";
import Scroller from "./Scroller";
import Events from "./Events";
import Tooltip from "./Tooltip";
import Clipboard from "./Clipboard";
import Histories from "./History";
import { StyleManager, type TableTheme } from "./StyleManager";
import EdgeScroller from "./EdgeScroller";
import { type HeaderConfig } from "./util";
import { type FooterCellClickInfo, type FooterData, type FooterCellData, type GroupHeaderCellClickInfo, type FooterStatRefreshInfo, type GroupHeaderStatRefreshInfo } from "./types";
import { type PerformanceMonitor } from "./PerformanceMonitor";
interface Column extends HeaderConfig {
    children?: Column[];
    [key: string]: any;
}
interface RowData {
    [key: string]: any;
}
interface CellInfo {
    colIndex: number;
    rowIndex: number;
    x: number;
    y: number;
    width: number;
    height: number;
    value: any;
    fixed?: 'left' | 'right' | '';
    dataType?: string;
    options?: any[];
    readonly?: boolean;
    [key: string]: any;
}
interface SelectorState {
    show: boolean;
    isSelected: boolean;
    xArr: [number, number];
    yArr: [number, number];
}
interface AutofillState {
    enable: boolean;
    xIndex: number;
    yIndex: number;
    xArr: [number, number];
    yArr: [number, number];
}
interface RowDragState {
    isDragging: boolean;
    dragStartX: number;
    dragStartY: number;
    draggedRowIndices: number[];
    dropIndicatorType: 'line' | 'overlay' | null;
    dropIndicatorY: number;
    dropTargetRowIndex: number;
    justFinishedDrag: boolean;
    hasStartedDragSelection: boolean;
    isCtrlPressed: boolean;
    isShiftPressed: boolean;
    skipNextClick: boolean;
    pendingCheckedRowDrag: boolean;
    /** 是否在 checkbox 区域触发的 pending 拖拽（用于 mouseUp 时区分行号点击和 checkbox 点击） */
    pendingIsCheckboxArea: boolean;
    /** DOM ghost element that follows the mouse during row drag */
    ghostEl: HTMLDivElement | null;
}
interface ColDragState {
    isDragging: boolean;
    dragStartX: number;
    draggedColIds: string[];
    dropIndicatorType: 'line' | null;
    dropIndicatorX: number;
    dropTargetColId: string;
    dropInsertBefore: boolean;
    justFinishedDrag: boolean;
    skipNextClick: boolean;
    pendingColId: string;
    pendingStartX: number;
}
interface ViewerDragState {
    isDragging: boolean;
    cell: any | null;
    startX: number;
    startY: number;
    startValue: any;
    currentValue: any;
}
interface RangeState {
    minX: number;
    minY: number;
    maxY: number;
    maxX?: number;
}
interface ColumnContextMenuInfo {
    colId: string;
    colKey: string;
    colIndex: number;
    colType?: string;
    colTitle: string;
    fixed: string;
    nestedChildOffset?: number;
    parentKey?: string;
    clientX: number;
    clientY: number;
    /**
     * 表头单元格在屏幕坐标系下的矩形（left/top/width/height）。
     * 供外部把 popover（如右键菜单→筛选）锚点对齐到"该列表头下方左对齐"。
     * 对 nested-child 列，矩形为子表头本身；对 fixed 左右冻结列，已按可视屏幕坐标换算。
     */
    headerBounds: {
        left: number;
        top: number;
        width: number;
        height: number;
    };
    /** 当前选中的列 colId 集合（用于区分「表头右键菜单」和「选中列右键菜单」） */
    selectedColIds: string[];
    /** 当前选中的列原始 key 集合（colKey），方便业务层直接匹配 */
    selectedColKeys: string[];
}
/** 行右键菜单回调参数 */
interface RowContextMenuInfo {
    /** 行数据 */
    rowData: RowData;
    /** 行索引 */
    rowIndex: number;
    /** 右键点击位置对应的列 key */
    colKey: string;
    /** 右键点击位置对应的列索引 */
    colIndex: number;
    /** 是否为分组行 */
    isGroupRow: boolean;
    /** 鼠标屏幕 X 坐标 */
    clientX: number;
    /** 鼠标屏幕 Y 坐标 */
    clientY: number;
    /** Canvas 行索引（供外部通过 isRowCheckedByIndex 查询选中状态） */
    _canvasRowIndex?: number;
    /** 右键行是否处于选中（checked）状态 */
    _isRowChecked?: boolean;
    /** 当前选中行总数 */
    _selectedRowCount?: number;
    /** Row 对象内部的 _rowId，和 selectedRowIds 中的 key 格式一致 */
    _internalRowId?: string;
}
export interface DataGridOptions {
    width?: number | string;
    height?: number | string;
    data?: RowData[];
    columns?: Column[];
    color?: string;
    borderColor?: string;
    fillColor?: string;
    borderWidth?: number;
    showCheckbox?: boolean;
    showTableIndex?: boolean;
    /**
     * 是否在表头左上角显示全选 checkbox
     * 默认 false（不显示），设为 true 时表头左上角显示全选/半选 checkbox
     */
    showHeaderCheckbox?: boolean;
    /**
     * 索引列模式：
     * - 'auto'：自动模式，行号 = rowIndex + 1（默认）
     * - 'data'：数据驱动模式，行号从行数据的 _tableIndex 字段读取，
     *   分组行/分隔行/添加按钮行不显示序号
     */
    tableIndexMode?: 'auto' | 'data';
    headerHeight?: number;
    rowKey?: string;
    striped?: boolean;
    stripedOddColor?: string;
    stripedEvenColor?: string;
    rowHeight?: number;
    theme?: string | TableTheme;
    /** 自定义 StyleManager Token 覆盖（如字体族），由宿主应用传入 */
    customTokens?: Record<string, any>;
    beforeSelectCell?: (cell: CellInfo) => void;
    afterSelectCell?: (cell: CellInfo | null) => void;
    beforeMultiSelectCell?: (cells: CellInfo[]) => void;
    afterMultiSelectCell?: (cells: CellInfo[]) => void;
    beforeEditCell?: (cell: CellInfo) => void;
    afterEditCell?: (cell: RowData) => void;
    beforeSelectRow?: (row: RowData) => void;
    afterSelectRow?: (row: RowData) => void;
    beforeResizeColumn?: () => void;
    afterResizeColumn?: () => void;
    beforeResizeRow?: () => void;
    afterResizeRow?: () => void;
    beforeAutofill?: () => void;
    afterAutofill?: (rows: RowData[]) => void;
    customAutofill?: (params: {
        sourceData: {
            label: string;
            value: any;
            dataType: string;
        }[][];
        targetRange: {
            xArr: [number, number];
            yArr: [number, number];
        };
        selectionRange: {
            xArr: [number, number];
            yArr: [number, number];
        };
        setCell: (rowIndex: number, colIndex: number, value: any) => void;
        getCellDataType: (colIndex: number) => string;
    }) => void;
    /** autofill 拖拽开始回调：mousedown on corner handle 时触发 */
    onAutofillStart?: (params: {
        selectionRange: {
            xArr: [number, number];
            yArr: [number, number];
        };
        shiftKey: boolean;
    }) => void;
    /** autofill 拖拽过程回调：mousemove 期间实时触发 */
    onAutofillDragging?: (params: {
        autofillRange: {
            xArr: [number, number];
            yArr: [number, number];
        };
        selectionRange: {
            xArr: [number, number];
            yArr: [number, number];
        };
    }) => void;
    /** 嵌套表格 autofill 拖拽开始回调 */
    onNestedAutofillStart?: (params: {
        parentRowData: any;
        parentColKey: string;
        parentRowIndex: number;
        parentColIndex: number;
        selectionRange: {
            startRow: number;
            startCol: number;
            endRow: number;
            endCol: number;
        };
        shiftKey: boolean;
    }) => void;
    /** 嵌套表格 autofill 拖拽过程回调 */
    onNestedAutofillDragging?: (params: {
        parentRowData: any;
        parentColKey: string;
        parentRowIndex: number;
        parentColIndex: number;
        autofillRange: {
            startRow: number;
            startCol: number;
            endRow: number;
            endCol: number;
        };
        selectionRange: {
            startRow: number;
            startCol: number;
            endRow: number;
            endCol: number;
        };
    }) => void;
    /** 嵌套表格 autofill 拖拽结束回调 */
    onNestedAutofillEnd?: (params: {
        parentRowData: any;
        parentColKey: string;
        parentRowIndex: number;
        parentColIndex: number;
        autofillRange: {
            startRow: number;
            startCol: number;
            endRow: number;
            endCol: number;
        };
        selectionRange: {
            startRow: number;
            startCol: number;
            endRow: number;
            endCol: number;
        };
        /** NestedGrid 实际渲染的数据数组（行索引与 autofillRange 完全一致） */
        nestedGridData: any[];
    }) => void;
    /** 嵌套子表内子行拖动排序回调（Alt + 拖拽触发，不跨外层行） */
    onNestedRowDragReorder?: (params: {
        parentRowData: any;
        parentColKey: string;
        parentRowIndex: number;
        parentColIndex: number;
        /** 被拖拽的子行索引（相对 nestedGridData） */
        fromRowIndex: number;
        /** 目标插入位置索引（相对 nestedGridData） */
        toRowIndex: number;
        /** 插入位置：front 插到目标行之前，tail 插到目标行之后 */
        position: 'front' | 'tail';
        /** 被拖拽子行数据 */
        fromRowData: any;
        /** 目标参考子行数据 */
        toRowData: any;
        /** NestedGrid 实际渲染的数据数组 */
        nestedGridData: any[];
    }) => void;
    /** 嵌套子表框选变化回调：选区跨子表或单子表变化时触发 */
    onNestedSelectionChange?: (params: {
        parentColKey: string;
        selections: Array<{
            parentRowData: any;
            parentRowIndex: number;
            parentColIndex: number;
            selectionRange: {
                startRow: number;
                startCol: number;
                endRow: number;
                endCol: number;
            };
        }>;
    }) => void;
    /** 嵌套子表头点击回调：通知外部进行选中逻辑处理 */
    onNestedSubHeadClick?: (params: {
        parentKey: string;
        nestedKey: string;
        nestedChildOffset: number;
        shiftKey: boolean;
        ctrlKey: boolean;
    }) => void;
    /** 嵌套子表跨子表 autofill 结束回调 */
    onNestedCrossAutofillEnd?: (params: {
        parentColKey: string;
        sourceSelections: Array<{
            parentRowData: any;
            parentRowIndex: number;
            selectionRange: {
                startRow: number;
                startCol: number;
                endRow: number;
                endCol: number;
            };
        }>;
        targetSelections: Array<{
            parentRowData: any;
            parentRowIndex: number;
            autofillRange: {
                startRow: number;
                startCol: number;
                endRow: number;
                endCol: number;
            };
            /** NestedGrid 实际渲染的数据数组（行索引与 autofillRange 完全一致） */
            nestedGridData: any[];
        }>;
    }) => void;
    beforeCopy?: () => void;
    afterCopy?: () => void;
    beforePaste?: () => void;
    afterPaste?: (rows: RowData[]) => void;
    afterClear?: (rows: RowData[]) => void;
    onLoad?: () => void;
    onRowDragStart?: (draggedRows: RowData[]) => void;
    onRowDragEnd?: () => void;
    onRowDragOutside?: (draggedRows: RowData[], mouseEvent: MouseEvent) => void;
    onRowDragReorder?: (draggedRows: RowData[], targetIndex: number) => void;
    onRowDragMove?: (draggedRows: RowData[], targetRow: RowData) => void;
    onGroupToggle?: (groupRow: RowData, expanded: boolean) => void;
    /**
     * 分组折叠模式：
     * - 'controlled'：受控模式，DataGrid 不自行修改数据，由外部通过 onGroupToggle 回调 + loadData 驱动
     * - 'uncontrolled'：非受控模式，DataGrid 内部根据 children / expanded 自行过滤行并重绘
     * 默认 'controlled'
     */
    groupToggleMode?: 'controlled' | 'uncontrolled';
    /**
     * 分组维度数（由外部传入，优先级高于行数据遍历推断）。
     * - 0 = 无分组
     * - 1 = 一维分组
     * - 2 = 二维分组
     * - 3 = 三维分组
     * 未传入时退化为遍历行数据推断（旧逻辑）。
     */
    groupDimension?: number;
    /** 是否启用内置键盘快捷键（默认 false） */
    enableKeyboardShortcuts?: boolean;
    /**
     * Whether to disable DataGrid's built-in "click outside to close editing" behavior.
     *
     * When true, handleDocumentMouseDown will NOT execute:
     * - doneEdit() (close internal editor)
     * - clearSelectedCols() (clear column selection)
     * - clearCellSelection() (clear cell selection)
     * - afterSelectCell(null) (notify external to close editor panel)
     *
     * Use this when the host application (e.g. AssetMatrix) manages "click outside"
     * via its own mechanism (e.g. v-click-outside) with finer-grained exclusion logic.
     *
     * Default: false (preserves existing behavior for backward compatibility).
     */
    disableClickOutside?: boolean;
    /**
     * 是否隐藏表头业务图标（TAPD / IOMC / fieldHint / link）。
     * 对齐 DOM 表格的 hideIcon 能力：用户在 "显示表头图标" 开关关闭时，
     * 表头区域的四种业务图标均不再绘制，但图标右侧的排序/筛选/设置图标不受影响。
     * 默认 false（显示图标）。
     */
    hideHeaderIcons?: boolean;
    onTreeExpand?: (rowData: RowData, expanded: boolean) => void;
    onCellTextClick?: (rowData: RowData, colId: string) => void;
    onCellTotalNumClick?: (rowData: RowData, colId: string) => void;
    /** 富文本单元格编辑笔点击回调：点击右侧编辑图标时触发，用于打开编辑弹窗 */
    onRichTextEditClick?: (info: {
        rowData: RowData;
        colId: string;
        clientX: number;
        clientY: number;
    }) => void;
    /** 富文本单元格文字区域点击回调：点击中间文本区域时触发，用于打开预览弹窗 */
    onRichTextContentClick?: (info: {
        rowData: RowData;
        colId: string;
        clientX: number;
        clientY: number;
    }) => void;
    onColumnDragReorder?: (draggedColIds: string[], targetColId: string, insertBefore: boolean) => void;
    onColumnContextMenu?: (info: ColumnContextMenuInfo) => void;
    /** 冻结列总宽度超过表格展示宽度时触发的回调 */
    onFrozenColumnOverflow?: (info: {
        fixedLeftWidth: number;
        fixedRightWidth: number;
        tableWidth: number;
    }) => void;
    /** 行右键菜单回调：行区域右键时触发 */
    onRowContextMenu?: (info: RowContextMenuInfo) => void;
    onModuleOverflowClick?: (modules: any[], rowData: any, rowIndex: number, colIndex: number) => void;
    onAddRow?: (groupInfo?: any) => void;
    /** 表头排序图标点击回调 */
    onHeaderSortClick?: (info: {
        colId: string;
        colIndex: number;
        colTitle: string;
        colKey: string;
        colType?: string;
        parentKey?: string;
        clientX: number;
        clientY: number;
    }) => void;
    /** 表头筛选图标点击回调 */
    onHeaderFilterClick?: (info: {
        colId: string;
        colIndex: number;
        colTitle: string;
        colKey: string;
        colType?: string;
        parentKey?: string;
        clientX: number;
        clientY: number;
        headerBounds: {
            left: number;
            top: number;
            width: number;
            height: number;
        };
    }) => void;
    /** 嵌套列设置图标点击回调 */
    onNestedSettingsClick?: (info: {
        colId: string;
        colIndex: number;
        colTitle: string;
        clientX: number;
        clientY: number;
    }) => void;
    /** 滚动回调：每次滚动位置变化时触发 */
    onScroll?: (scrollX: number, scrollY: number) => void;
    /** 滚动开始回调 */
    onScrollStart?: () => void;
    /** 滚动结束回调（滚动停止 300ms 后触发） */
    onScrollEnd?: () => void;
    /** Enable debug mode with detailed performance monitoring and console output */
    debug?: boolean;
    /** WfState Viewer 显示样式：'icon'=图标+文字（默认），'tag'=色块标签 */
    wfStateStyle?: 'icon' | 'tag';
    /** 列总宽小于表格可视区域宽度时，是否将剩余空间均分到每列（默认 false） */
    autoFillTableWidth?: boolean;
    /** 是否为商业化库环境（影响统计选项，如"商业化单子数"） */
    isBusiness?: boolean;
    /** 是否显示底部统计行 */
    showFooter?: boolean;
    /** 底部统计行高度（默认 FOOTER_HEIGHT = 36） */
    footerHeight?: number;
    /** Footer 单元格点击回调 */
    onFooterCellClick?: (info: FooterCellClickInfo) => void;
    /** 组头单元格点击回调 */
    onGroupHeaderCellClick?: (info: GroupHeaderCellClickInfo) => void;
    /** 人员工时图标点击回调 */
    onUserWorkingHoursClick?: (info: {
        groupId: string;
        clientX: number;
        clientY: number;
        isAllGroup: boolean;
        canvasElement: HTMLCanvasElement;
        iconBounds?: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
    }) => void;
    /** Footer 嵌套列统计刷新回调 */
    onFooterStatRefresh?: (info: FooterStatRefreshInfo) => void;
    /** 组头嵌套列统计刷新回调 */
    onGroupHeaderStatRefresh?: (info: GroupHeaderStatRefreshInfo) => void;
    /** Viewer 编辑图标点击回调（拦截内置编辑器，由外部处理编辑） */
    onEditIconClick?: (info: {
        rowData: RowData;
        colKey: string;
        colIndex: number;
        rowIndex: number;
        bounds: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
    }) => void;
    /** 嵌套子表格右键菜单回调：子表格区域右键时触发 */
    onNestedGridContextMenu?: (info: {
        parentRowData: any;
        parentColKey: string;
        parentRowIndex: number;
        parentColIndex: number;
        nestedRowData: any | null;
        nestedRowIndex: number;
        nestedColKey: string;
        nestedColIndex: number;
        selectionRange: {
            startRow: number;
            startCol: number;
            endRow: number;
            endCol: number;
        } | null;
        clientX: number;
        clientY: number;
    }) => void;
    /** 嵌套子表格编辑笔点击回调：子表格编辑笔被点击时触发，拦截内置编辑器 */
    onNestedEditIconClick?: (info: {
        parentRowData: any;
        parentColKey: string;
        parentRowIndex: number;
        parentColIndex: number;
        nestedRowData: any;
        nestedRowIndex: number;
        nestedColKey: string;
        nestedColIndex: number;
        column: any;
        value: any;
        bounds: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
    }) => void;
    /** 嵌套表格展开/收起回调：展开全部或收起按钮点击时触发，用于通知业务层更新数据 */
    onNestedExpandToggle?: (info: {
        parentRowData: any;
        parentColKey: string;
        parentRowIndex: number;
        expanded: boolean;
    }) => void;
    onCollapseAll?: () => void;
    /** 展开所有任务回调 */
    onExpandAll?: () => void;
    /** 展开前 N 层级回调 */
    onExpandToLevel?: (level: number) => void;
    /** 表头列宽拖拽开始回调 */
    onResizeHeaderStart?: (width: number, info: {
        headerIndex: number;
    }) => void;
    /** 表头列宽拖拽结束回调（携带最终宽度） */
    onResizeHeader?: (finalWidth: number, info: {
        headerIndex: number;
        viewer: string;
    }) => void;
    /** 嵌套子表头列宽拖拽结束回调（携带最终宽度、subIndex、subSize） */
    onResizeSubHeader?: (finalWidth: number, info: {
        headerIndex: number;
        viewer: string;
        colKey: string;
    }, subIndex: number, subSize: number) => void;
    /** 表头列宽拖拽完全结束回调（nextTick 后调用） */
    onResizeHeaderEnd?: () => void;
    /** 表头双击重置列宽回调（双击列宽拖拽热区触发，在 min/max 之间切换宽度） */
    onDblClickResizeHeader?: (info: {
        headerIndex: number;
        colKey: string;
        isNestedChild: boolean;
        nestedChildOffset: number;
    }) => void;
    /** Cell hover 变化回调：当 hover 的单元格变化时通知外部（用于时间型字段 tooltip / 历史变更等） */
    onCellHoverChange?: (info: {
        show: boolean;
        rowIndex: number;
        colIndex: number;
        colKey: string;
        rowData: Record<string, any>;
        position: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
        canvasElement: HTMLCanvasElement;
        displayText: string;
        mouseX: number;
        mouseY: number;
    }) => void;
    /** Error icon hover 回调：当鼠标悬停在 warning icon 上时触发（用于外部 AlarmPopper） */
    onErrorIconHover?: (info: {
        show: boolean;
        position: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
        errorMessage: string;
        errorProperty?: string;
        rowData?: Record<string, any>;
    }) => void;
    /** Viewer icon hover 回调：鼠标悬停在 viewer 内部 icon（如任务图标、IOMC 提单图标）上时触发（用于外部 AlarmPopper） */
    onViewerIconHover?: (info: {
        show: boolean;
        position: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
        tooltipText: string;
    }) => void;
    /** Reminder 蓝色三角点击回调：当用户点击单元格右上角蓝色提醒三角时触发 */
    onReminderClick?: (info: {
        position: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
        rowId: any;
        key: string;
        remindInfo: {
            field_name: string;
            is_filed_admin?: boolean;
            fcr_open_time?: string;
            color?: string;
            [key: string]: any;
        };
        rowData: Record<string, any>;
    }) => void;
    /** 表头 Link icon hover 回调：鼠标悬停/离开引用字段表头 link icon 时触发 */
    onHeaderLinkIconHover?: (info: {
        show: boolean;
        colKey: string;
        colIndex: number;
        colTitle: string;
        linkKey?: string;
        linkTask?: string;
        position: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
    }) => void;
    /** 表头 fieldHint info icon hover 回调：鼠标悬停/离开字段提示图标时触发 */
    onHeaderFieldHintIconHover?: (info: {
        show: boolean;
        colKey: string;
        colIndex: number;
        colTitle: string;
        fieldHint: string;
        position: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
    }) => void;
    /** 表体 Link icon 点击回调：点击引用字段单元格的 link icon 时触发 */
    onLinkIconClick?: (info: {
        rowData: Record<string, any>;
        colKey: string;
        colIndex: number;
        rowIndex: number;
        linkKey?: string;
        bounds: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
    }) => void;
    /** 行渲染前回调：每个可见行绘制前触发，用于渐进式数据加载等场景 */
    onBeforeRowRender?: (rowIndex: number, rowData: RowData) => void;
    /** 嵌套表格渲染前回调：每个嵌套表格格子绘制前触发，用于分组表格管线列渐进式加载 */
    onBeforeNestedGridRender?: (rowIndex: number, rowData: RowData, colKey: string) => void;
    /**
     * 嵌套子表数据比对 key 列表（性能优化）。
     * 在 loadData/refreshDataGrid 时，通过比较新旧嵌套数据中这些 key 的值来判断数据是否变化。
     * 如果未变化，则复用旧的 NestedGrid 实例，跳过重建。
     * 默认 ['id']——只比较子行 id 列表。
     *
     * 对应 DOM 版本的 REFRESH_TABLE_COMPARE_KEYS。
     */
    nestedDataCompareKeys?: string[];
    /**
     * 嵌套子表强制刷新 key 列表（性能优化）。
     * 当新嵌套数据中任意一行包含这些 key 且有值时，跳过数据比对，强制重建 NestedGrid。
     * 默认 []（不强制刷新）。
     *
     * 对应 DOM 版本的 FORCE_REFRESH_KEYS。
     */
    nestedForceRefreshKeys?: string[];
    /** 普通行 checkbox 变更回调（受控模式下通知外部更新选中行列表） */
    onRowCheckChange?: (info: {
        rowId: string;
        checked: boolean;
        rowData: any;
        selectedRowIds: string[];
        event?: MouseEvent;
    }) => void;
    /** 组头 checkbox 变更回调（受控模式下通知外部更新组头选中状态） */
    onGroupCheckboxChange?: (info: {
        groupId: string | number;
        checked: boolean;
        data: any;
        selectedRowIds: string[];
    }) => void;
    /** 最大可选行数（超过此数量时阻止勾选并触发 onSelectionLimitExceeded 回调），不设置则不限制 */
    maxSelectedRows?: number;
    /** 勾选超限回调（当勾选操作将导致选中数量超过 maxSelectedRows 时触发） */
    onSelectionLimitExceeded?: (info: {
        currentCount: number;
        attemptCount: number;
        limit: number;
    }) => void;
    /**
    /** Cell value transformer callback.
     * Called by Cell.drawWithViewer() before passing data to viewer.draw().
     * Transforms raw cell values to display values (e.g. treeDropdown ID → display name + color).
     * Return undefined to keep the original value.
     *
     * TODO: Migrate other viewer types (person, module, wf-state, task-node, dropdown, etc.)
     *       from transformCellValues to this callback for full decoupling.
     */
    cellValueTransformer?: import('./viewers/types').CellValueTransformer;
    /**
     * Render error callback: called when draw() throws an exception.
     * Receives the error object and the count of consecutive errors.
     * When consecutive errors exceed the threshold (default 5), the render loop
     * will be paused to prevent the browser from freezing.
     */
    onRenderError?: (error: unknown, consecutiveErrorCount: number) => void;
    /**
     * 外部 OS 文件拖入 cell 时的 4 个回调（仅响应 dataTransfer.types includes 'Files'）。
     * - DragEnter / DragOver / DragLeave 仅做选中态/视觉提示；选中已由 DataGrid 内部 selectCell 完成。
     * - Drop 真正触发上传流程（业务侧 controller 接管：插占位 + 调 graph.addFile / addFileOnNonFileNode）。
     */
    onExternalFileDragEnter?: (info: {
        rowIndex: number;
        colIndex: number;
        columnKey: string;
    }) => void;
    onExternalFileDragOver?: (info: {
        rowIndex: number;
        colIndex: number;
        columnKey: string;
    } | null) => void;
    onExternalFileDragLeave?: (info: {
        rowIndex: number;
        colIndex: number;
        columnKey: string;
    } | null) => void;
    onExternalFileDrop?: (info: {
        rowIndex: number;
        colIndex: number;
        columnKey: string;
        files: File[];
        e?: DragEvent;
    }) => void;
    /**
     * 外部 OS 文件粘贴（Ctrl/Cmd+V 把剪贴板里的文件追加到当前选中 cell）回调。
     * 与 onExternalFileDrop 形参对称，但语义为"追加上传"——业务侧不应清掉 cell 现有文件。
     */
    onExternalFilePaste?: (info: {
        rowIndex: number;
        colIndex: number;
        columnKey: string;
        files: File[];
        e?: ClipboardEvent;
    }) => void;
}
declare class DataGrid {
    target: HTMLCanvasElement;
    scrollY: number;
    scrollX: number;
    checkboxWidth: number;
    horizontalScrollerSize: number;
    verticalScrollerSize: number;
    tempValue: any;
    focusCell: any;
    enterShift: boolean;
    _multiSelectStartX: number;
    _multiSelectStartY: number;
    _multiSelectThresholdMet: boolean;
    hashChange: {
        [key: string]: boolean;
    };
    selector: SelectorState;
    editor: Editor;
    autofill: AutofillState;
    rowDrag: RowDragState;
    colDrag: ColDragState;
    selectedColIds: Set<string>;
    lastSelectedColId: string;
    nestedSubHeadCheckedKeys: string[];
    viewerDrag: ViewerDragState;
    painter: Paint;
    clipboard: Clipboard;
    header: Header;
    footer: Footer | null;
    body: Body;
    scroller: Scroller;
    tooltip: Tooltip;
    events: Events;
    history: Histories;
    styleManager: StyleManager;
    /**
     * 宿主应用传入的自定义 Token 覆盖（如字体族）。
     * 主题切换后会自动重新应用，确保自定义 Token 不被主题默认值覆盖。
     */
    private _customTokens;
    edgeScroller: EdgeScroller;
    rowIdMap: Map<string, number>;
    selectedRowIds: Set<string>;
    selectedGroupIds: Set<string>;
    lastSelectedRowIndex: number;
    /** Footer 行高度（showFooter 为 false 时返回 0） */
    get footerHeight(): number;
    data: RowData[];
    color: string;
    borderColor: string;
    fillColor: string;
    borderWidth: number;
    showCheckbox: boolean;
    showTableIndex: boolean;
    /** 是否在表头左上角显示全选 checkbox，默认 false */
    showHeaderCheckbox: boolean;
    /** 索引列模式：'auto' = rowIndex+1，'data' = 从行数据 _tableIndex 读取 */
    tableIndexMode: 'auto' | 'data';
    headerHeight: number;
    rowKey: string;
    striped: boolean;
    stripedOddColor: string;
    stripedEvenColor: string;
    rowHeight: number;
    hoverRowIndex: number;
    hoverColIndex: number;
    isScrolling: boolean;
    scrollTimer: NodeJS.Timeout | null;
    beforeSelectCell: (cell: CellInfo) => void;
    afterSelectCell: (cell: CellInfo | null) => void;
    beforeMultiSelectCell: (cells: CellInfo[]) => void;
    afterMultiSelectCell: (cells: CellInfo[]) => void;
    beforeEditCell: (cell: CellInfo) => void;
    afterEditCell: (cell: RowData) => void;
    beforeSelectRow: (row: RowData) => void;
    afterSelectRow: (row: RowData) => void;
    beforeResizeColumn: () => void;
    afterResizeColumn: () => void;
    beforeResizeRow: () => void;
    afterResizeRow: () => void;
    beforeAutofill: () => void;
    afterAutofill: (rows: RowData[]) => void;
    customAutofill?: (params: {
        sourceData: {
            label: string;
            value: any;
            dataType: string;
        }[][];
        targetRange: {
            xArr: [number, number];
            yArr: [number, number];
        };
        selectionRange: {
            xArr: [number, number];
            yArr: [number, number];
        };
        setCell: (rowIndex: number, colIndex: number, value: any) => void;
        getCellDataType: (colIndex: number) => string;
    }) => void;
    onAutofillStart?: DataGridOptions['onAutofillStart'];
    onAutofillDragging?: DataGridOptions['onAutofillDragging'];
    onNestedAutofillStart?: DataGridOptions['onNestedAutofillStart'];
    onNestedAutofillDragging?: DataGridOptions['onNestedAutofillDragging'];
    onNestedAutofillEnd?: DataGridOptions['onNestedAutofillEnd'];
    onNestedRowDragReorder?: DataGridOptions['onNestedRowDragReorder'];
    onNestedSelectionChange?: DataGridOptions['onNestedSelectionChange'];
    onNestedSubHeadClick?: DataGridOptions['onNestedSubHeadClick'];
    onNestedCrossAutofillEnd?: DataGridOptions['onNestedCrossAutofillEnd'];
    beforeCopy: () => void;
    afterCopy: () => void;
    beforePaste: () => void;
    afterPaste: (rows: RowData[]) => void;
    afterClear: (rows: RowData[]) => void;
    onLoad: () => void;
    onRowDragStart?: (draggedRows: RowData[]) => void;
    onRowDragEnd?: () => void;
    onRowDragOutside?: (draggedRows: RowData[], mouseEvent: MouseEvent) => void;
    onRowDragReorder?: (draggedRows: RowData[], targetIndex: number) => void;
    onRowDragMove?: (draggedRows: RowData[], targetRow: RowData) => void;
    onGroupToggle?: (groupRow: RowData, expanded: boolean) => void;
    /** 分组折叠模式：'controlled'（受控）| 'uncontrolled'（非受控） */
    groupToggleMode: 'controlled' | 'uncontrolled';
    /** 是否启用内置键盘快捷键 */
    enableKeyboardShortcuts: boolean;
    /** Whether to disable built-in "click outside to close editing" behavior */
    disableClickOutside: boolean;
    /** 是否隐藏表头业务图标（TAPD / IOMC / fieldHint / link），对齐 DOM 表格 hideIcon */
    hideHeaderIcons: boolean;
    /** 非受控模式下的完整数据（包含所有分组的子行） */
    private _fullData;
    /** 双缓冲：离屏 canvas（避免每帧创建，懒初始化 + 复用） */
    private _offscreenCanvas;
    private _offscreenCtx;
    onTreeExpand?: (rowData: RowData, expanded: boolean) => void;
    onCellTextClick?: (rowData: RowData, colId: string) => void;
    onCellTotalNumClick?: (rowData: RowData, colId: string) => void;
    onRichTextEditClick?: (info: {
        rowData: RowData;
        colId: string;
        clientX: number;
        clientY: number;
    }) => void;
    onRichTextContentClick?: (info: {
        rowData: RowData;
        colId: string;
        clientX: number;
        clientY: number;
    }) => void;
    onColumnDragReorder?: (draggedColIds: string[], targetColId: string, insertBefore: boolean) => void;
    onColumnContextMenu?: (info: ColumnContextMenuInfo) => void;
    /** 冻结列总宽度超过表格展示宽度时触发的回调 */
    onFrozenColumnOverflow?: (info: {
        fixedLeftWidth: number;
        fixedRightWidth: number;
        tableWidth: number;
    }) => void;
    /** 行右键菜单回调 */
    onRowContextMenu?: (info: RowContextMenuInfo) => void;
    onModuleOverflowClick?: (modules: any[], rowData: any, rowIndex: number, colIndex: number) => void;
    onAddRow?: (groupInfo?: any) => void;
    onScroll?: (scrollX: number, scrollY: number) => void;
    onScrollStart?: () => void;
    onScrollEnd?: () => void;
    private scrollEndTimer;
    private wasScrolling;
    /** 列总宽小于表格可视区域宽度时，是否将剩余空间均分到每列（默认 false） */
    autoFillTableWidth: boolean;
    isBusiness: boolean;
    showFooter: boolean;
    /** 用户自定义的 footer 行高度（默认 FOOTER_HEIGHT） */
    _footerHeight: number;
    onFooterCellClick?: (info: FooterCellClickInfo) => void;
    onGroupHeaderCellClick?: (info: GroupHeaderCellClickInfo) => void;
    onUserWorkingHoursClick?: (info: {
        groupId: string;
        clientX: number;
        clientY: number;
        isAllGroup: boolean;
        canvasElement: HTMLCanvasElement;
        iconBounds?: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
    }) => void;
    onFooterStatRefresh?: (info: FooterStatRefreshInfo) => void;
    onGroupHeaderStatRefresh?: (info: GroupHeaderStatRefreshInfo) => void;
    /** Footer 弹窗激活的列标识 key（弹窗打开时设置，关闭时清除），保持该列的下拉箭头和刷新图标始终显示 */
    footerPopoverActiveKey: string | null;
    /** 组头弹窗激活状态（弹窗打开时设置，关闭时清除），包含列标识和分组 ID，确保只有对应分组的列显示激活样式 */
    groupHeaderPopoverActiveState: {
        key: string;
        groupId: string | number;
    } | null;
    /**
     * 右键菜单保护锁：contextmenu 回调期间设置，阻止外部异步 watch 通过 setSelectedRows 覆盖多选状态。
     * 场景：用户多选行后右键 → onRowContextMenu 回调 → 外部 watch 异步 setSelectedRows([仅右键行])
     * 保护锁在设置后 300ms 自动释放。
     */
    private _contextMenuSelectionLock;
    private _contextMenuSelectionSnapshot;
    /** 普通行 checkbox 变更回调（受控模式） */
    onRowCheckChange?: (info: {
        rowId: string;
        checked: boolean;
        rowData: any;
        selectedRowIds: string[];
        event?: MouseEvent;
    }) => void;
    /** 组头 checkbox 变更回调（校验勾选） */
    onGroupCheckboxChange?: (info: {
        groupId: string | number;
        checked: boolean;
        data: any;
        selectedRowIds: string[];
    }) => void;
    /** 最大可选行数（超过此数量时阻止勾选） */
    maxSelectedRows?: number;
    /** 勾选超限回调 */
    onSelectionLimitExceeded?: (info: {
        currentCount: number;
        attemptCount: number;
        limit: number;
    }) => void;
    /** 折叠所有任务回调 */
    onCollapseAll?: () => void;
    /** 展开所有任务回调 */
    onExpandAll?: () => void;
    /** 展开前 N 层级回调 */
    onExpandToLevel?: (level: number) => void;
    /** 表头列宽拖拽开始回调 */
    onResizeHeaderStart?: (width: number, info: {
        headerIndex: number;
    }) => void;
    /** 表头列宽拖拽结束回调（携带最终宽度） */
    onResizeHeader?: (finalWidth: number, info: {
        headerIndex: number;
        viewer: string;
        colKey: string;
    }) => void;
    /** 嵌套子表头列宽拖拽结束回调（携带最终宽度、subIndex、subSize） */
    onResizeSubHeader?: (finalWidth: number, info: {
        headerIndex: number;
        viewer: string;
        colKey: string;
    }, subIndex: number, subSize: number) => void;
    /** 表头列宽拖拽完全结束回调（nextTick 后调用） */
    onResizeHeaderEnd?: () => void;
    /** 表头双击重置列宽回调（双击列宽拖拽热区触发，在 min/max 之间切换宽度） */
    onDblClickResizeHeader?: (info: {
        headerIndex: number;
        colKey: string;
        isNestedChild: boolean;
        nestedChildOffset: number;
    }) => void;
    /** 行渲染前回调：每个可见行绘制前触发，用于渐进式数据加载 */
    onBeforeRowRender?: (rowIndex: number, rowData: RowData) => void;
    /** 嵌套表格渲染前回调：每个嵌套表格格子绘制前触发，用于分组表格管线列渐进式加载 */
    onBeforeNestedGridRender?: (rowIndex: number, rowData: RowData, colKey: string) => void;
    /** Cell value transformer (see DataGridOptions.cellValueTransformer) */
    cellValueTransformer?: import('./viewers/types').CellValueTransformer;
    /** Render error callback (see DataGridOptions.onRenderError) */
    onRenderError?: (error: unknown, consecutiveErrorCount: number) => void;
    /** 外部 OS 文件拖入 cell 时的 4 个回调（详见 DataGridOptions.onExternalFileDrag*） */
    onExternalFileDragEnter?: DataGridOptions['onExternalFileDragEnter'];
    onExternalFileDragOver?: DataGridOptions['onExternalFileDragOver'];
    onExternalFileDragLeave?: DataGridOptions['onExternalFileDragLeave'];
    onExternalFileDrop?: DataGridOptions['onExternalFileDrop'];
    onExternalFilePaste?: DataGridOptions['onExternalFilePaste'];
    /**
     * 外部 OS 文件拖拽状态机。
     * - activeCell：当前 hover 的 image/file cell（已被 selectCell 选中）
     * - previousFocusCell：拖拽开始前的原 focusCell，用于拖拽离开 / drop 完成后还原选区
     */
    externalFileDrag: {
        activeCell: {
            rowIndex: number;
            colIndex: number;
            columnKey: string;
        } | null;
        previousFocusCell: {
            rowIndex: number;
            colIndex: number;
        } | null;
    };
    nestedDataCompareKeys: string[];
    nestedForceRefreshKeys: string[];
    range: RangeState;
    columns: HeaderConfig[];
    headers: HeaderConfig[];
    columnsLength: number;
    originFixedWidth: number;
    /** Maximum group level in current data (-1=no groups, 0=1D, 1=2D, 2+=3D) */
    maxGroupLevel: number;
    /** Whether groupDimension was explicitly set from outside (disables auto-detection) */
    private _groupDimensionFromOption;
    containerOriginX: number;
    containerOriginY: number;
    width: number;
    height: number;
    _configWidth?: number | string;
    _configHeight?: number | string;
    private _baseColumnWidths;
    private _lastRawColumns;
    private _columnsChangedSinceLastLoad;
    fixedLeftWidth: number;
    fixedRightWidth: number;
    tableWidth: number;
    tableHeight: number;
    tableHeaderHeight: number;
    nestedColumnColors: Map<number, string>;
    private isDirty;
    private lastScrollX;
    private lastScrollY;
    /** [FLICKER-DEBUG] draw 日志节流时间戳 */
    _lastDrawLogTime: number;
    private _afterRenderListeners;
    private _renderErrorCount;
    private _renderPaused;
    private static readonly MAX_CONSECUTIVE_RENDER_ERRORS;
    private _frozenOverflowWarned;
    perfMonitor: PerformanceMonitor;
    private _imageLoadedHandler;
    private _gifAnimationHandler;
    wfStateStyle: 'icon' | 'tag';
    /** Minimum time (ms) a loading indicator must be visible before it can be hidden */
    private static readonly MIN_LOADING_DISPLAY_MS;
    private loadingCells;
    private loadingRotation;
    private loadingImage;
    private loadingAnimationId;
    /** Footer/GroupRow 嵌套列刷新图标（预加载 SVG） */
    private refreshIcon;
    /** Footer/GroupRow loading 计数器（用于控制 loading 动画的启停） */
    private footerLoadingCount;
    colResizeIndicatorX: number;
    private columnHighlightMap;
    private headerTextColorMap;
    private sortOrderMap;
    private sortOrderCount;
    private moduleSortOrderMap;
    private moduleSortCountMap;
    private _searchMatchColors;
    hoveredHeaderColId: string | null;
    onHeaderSortClick?: (info: {
        colId: string;
        colIndex: number;
        colTitle: string;
        colKey: string;
        colType?: string;
        parentKey?: string;
        clientX: number;
        clientY: number;
    }) => void;
    onHeaderFilterClick?: (info: {
        colId: string;
        colIndex: number;
        colTitle: string;
        colKey: string;
        colType?: string;
        parentKey?: string;
        clientX: number;
        clientY: number;
        headerBounds: {
            left: number;
            top: number;
            width: number;
            height: number;
        };
    }) => void;
    onNestedSettingsClick?: (info: {
        colId: string;
        colIndex: number;
        colTitle: string;
        clientX: number;
        clientY: number;
    }) => void;
    onCellHoverChange?: (info: {
        show: boolean;
        rowIndex: number;
        colIndex: number;
        colKey: string;
        rowData: Record<string, any>;
        position: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
        canvasElement: HTMLCanvasElement;
        displayText: string;
        mouseX: number;
        mouseY: number;
    }) => void;
    onErrorIconHover?: (info: {
        show: boolean;
        position: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
        errorMessage: string;
        errorProperty?: string;
        rowData?: Record<string, any>;
    }) => void;
    /** 当前是否正在 hover error icon（用于避免重复触发） */
    _errorIconHoverState: {
        rowIndex: number;
        colIndex: number;
    } | null;
    onViewerIconHover?: (info: {
        show: boolean;
        position: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
        tooltipText: string;
    }) => void;
    /** 当前是否正在 hover viewer icon（用于避免重复触发） */
    _viewerIconHoverState: {
        rowIndex: number;
        colIndex: number;
        tooltipText: string;
    } | null;
    onReminderClick?: (info: {
        position: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
        rowId: any;
        key: string;
        remindInfo: {
            field_name: string;
            is_filed_admin?: boolean;
            fcr_open_time?: string;
            color?: string;
            [key: string]: any;
        };
        rowData: Record<string, any>;
    }) => void;
    onHeaderLinkIconHover?: (info: {
        show: boolean;
        colKey: string;
        colIndex: number;
        colTitle: string;
        linkKey?: string;
        linkTask?: string;
        position: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
    }) => void;
    onHeaderFieldHintIconHover?: (info: {
        show: boolean;
        colKey: string;
        colIndex: number;
        colTitle: string;
        fieldHint: string;
        position: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
    }) => void;
    onLinkIconClick?: (info: {
        rowData: Record<string, any>;
        colKey: string;
        colIndex: number;
        rowIndex: number;
        linkKey?: string;
        bounds: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
    }) => void;
    onNestedGridContextMenu?: (info: {
        parentRowData: any;
        parentColKey: string;
        parentRowIndex: number;
        parentColIndex: number;
        nestedRowData: any | null;
        nestedRowIndex: number;
        nestedColKey: string;
        nestedColIndex: number;
        selectionRange: {
            startRow: number;
            startCol: number;
            endRow: number;
            endCol: number;
        } | null;
        clientX: number;
        clientY: number;
    }) => void;
    onNestedEditIconClick?: (info: {
        parentRowData: any;
        parentColKey: string;
        parentRowIndex: number;
        parentColIndex: number;
        nestedRowData: any;
        nestedRowIndex: number;
        nestedColKey: string;
        nestedColIndex: number;
        column: any;
        value: any;
        bounds: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
    }) => void;
    /** 嵌套表格展开/收起回调：展开全部或收起按钮点击时触发，用于通知业务层更新数据 */
    onNestedExpandToggle?: (info: {
        parentRowData: any;
        parentColKey: string;
        parentRowIndex: number;
        expanded: boolean;
    }) => void;
    constructor(target: HTMLCanvasElement, options: DataGridOptions);
    /**
     * 容器初始化相关
     */
    initConfig(options: DataGridOptions): void;
    /**
     * 获取容器可视区域宽、高，设置canvas容器样式尺寸、画布尺寸
     */
    setLayoutSize(options?: DataGridOptions): void;
    /**
     * 获取未被 autoFill 改写的表格原始宽度。
     */
    getTableOriginalWidth(): number;
    /**
     * 获取表格左、右冻结列宽，表格实际的宽、高
     */
    getTableSize(): void;
    initTableSize(): void;
    /**
     * 列总宽小于可视区域宽度时，需要补齐
     */
    fillTableWidth(): void;
    /**
     * Clamp scrollX/scrollY to valid bounds after layout changes (column add/remove, resize, etc.).
     * Prevents white gaps when total content size shrinks below the current scroll offset.
     * Also updates scrollbar thumb positions.
     */
    clampScrollPosition(): void;
    resize(): void;
    /**
     * Restore all column widths (leaf and nested children) to their saved base values.
     * Since this.columns and this.headers share nestedColumns references (via shallow copy),
     * restoring nestedColumns[j].width here also restores header config values.
     */
    private restoreBaseColumnWidths;
    createContainer(): void;
    /**
     * 选择、编辑相关---------------------------------------------------->
     */
    getCell(x: number, y: number): any;
    /**
     * Select a range of rows by index, skipping group and separate rows.
     * Shared by row-header drag-select and edge-scroll callback.
     */
    selectRowRange(startIndex: number, endIndex: number): void;
    /**
     * Select rows exclusively within the given range.
     * Rows inside [startIndex, endIndex] are selected;
     * rows outside this range are deselected.
     * Used by row-header drag-select so that dragging back
     * deselects rows that fall outside the current range.
     */
    selectRowRangeExclusive(startIndex: number, endIndex: number): void;
    private _nestedDebugLogged;
    selectCell({ colIndex, rowIndex }: {
        colIndex: number;
        rowIndex: number;
    }, options?: {
        skipBoundaryAdjust?: boolean;
    }): void;
    resetCellPosition(): void;
    /**
     * 将画布单元格中的数据传递到编辑器中
     */
    putCell(): void;
    multiSelectCell(x: number, y: number, mouseX: number, mouseY: number): void;
    endMultiSelect(): void;
    clearMultiSelect(): void;
    clearAllNestedGridsSelection(): void;
    selectCols(col: any, e?: {
        ctrlKey?: boolean;
        metaKey?: boolean;
        shiftKey?: boolean;
    }): void;
    /**
     * 获取嵌套父列下所有子列的 uniqueColKey
     */
    getNestedChildUniqueKeys(parentKey: string): string[];
    /**
     * 获取所有选中列的基础index集合（去重），通过 uniqueColKey 反查当前 ColumnHeader 的 index
     */
    getSelectedColBaseIndices(): number[];
    /**
     * 开始列拖拽排序
     */
    startColDrag(colId: string, mouseX: number): void;
    /**
     * 更新列拖拽状态
     */
    updateColDrag(mouseX: number, mouseY: number): void;
    /**
     * 结束列拖拽
     */
    endColDrag(): void;
    /**
     * 取消列拖拽
     */
    cancelColDrag(): void;
    /**
     * 清除列选中状态
     */
    clearSelectedCols(): void;
    /**
     * 外部设置嵌套子表头选中的子列 key 列表（受控模式）。
     * 将 key 列表转换为 NestedGrid 的 selectionRange，复用已有的选区渲染、
     * 选区边框和键盘快捷键（复制/粘贴/删除）机制。
     */
    setNestedSubHeadCheckedKeys(keys: string[]): void;
    /**
     * 判断某个嵌套子列 key 是否在 nestedSubHeadCheckedKeys 选中列表中
     */
    isNestedSubHeadChecked(nestedKey: string): boolean;
    /**
     * Clear all row selection (checkbox) state.
     * Clears selectedRowIds, unchecks all rows, syncs group headers, and updates header indeterminate.
     */
    clearRowSelection(): void;
    /**
     * Clear cell/multi-cell selection state (selector highlight and focus cell).
     */
    clearCellSelection(): void;
    /**
     * 判断列是否被选中（通过uniqueColKey判断，不随列排序变化）
     */
    isColSelected(uniqueColKey: string): boolean;
    /**
     * 判断嵌套父列的所有子列是否都被选中
     */
    isNestedParentAllChildrenSelected(parentIndex: number): boolean;
    /**
     * 将 selectedColIds（uniqueColKey 集合）转换为 colId 数组（用于外部回调）
     */
    getSelectedColIds(): string[];
    selectRows({ rowIndex }: {
        rowIndex: number;
    }): void;
    startAutofill(shiftKey?: boolean): void;
    clearAuaofill(): void;
    startEdit(): void;
    doneEdit(): void;
    /**
     * 单个写入数据
     * @param {Number} colIndex 需要写入数据的单元格X轴坐标，表格最左上角为<0,0>
     * @param {Number} rowIndex 需要写入数据的单元格Y轴坐标
     * @param {*} value 需要写入的数据: 简单数据类型
     */
    setData(value: any, { colIndex, rowIndex }?: {
        colIndex: number;
        rowIndex: number;
    }): void;
    /**
     * 批量写入数据
     * @param {Number} colIndex 需要写入数据的单元格范围起始X轴坐标，表格最左上角为<0,0>
     * @param {Number} rowIndex 需要写入数据的单元格范围起始Y轴坐标
     * @param {Array} value 需要写入的数据: 二维数组[<Array><Array>]
     */
    batchSetData({ colIndex, rowIndex, value }: {
        colIndex: number;
        rowIndex: number;
        value: any[][];
    }): void;
    /**
     * 将用户通过编辑器输入的值存储为一个临时变量，执行doneEdit()后再去setData()
     */
    setTempData(value: any): void;
    pasteData(arr: any[][]): void;
    clearSelectedData(): void;
    focusCellByCoord(minX: number, minY: number, maxX?: number, maxY?: number): void;
    /**
     * 调整列宽、行高
     */
    resizeColumn(colIndex: number, newWidth: number): void;
    resizeRow(rowIndex: number, height: number): void;
    handleCheckRow(y?: number): void;
    handleCheckHeader(): void;
    moveFocus(dir: "LEFT" | "TOP" | "RIGHT" | "BOTTOM"): void;
    /**
     * @deprecated Use edgeScroller.update() instead. Kept for backward compatibility.
     */
    adjustPosition(_x: number, _y: number, mouseX: number, mouseY: number): void;
    adjustBoundaryPosition(): void;
    /**
     * 画布绘制相关------------------------------------------------------->
     */
    initPaint(): void;
    /**
     * Mark the canvas as needing a repaint.
     * Called when data changes, interaction state changes, etc.
     */
    markDirty(): void;
    /**
     * [PERF] 反注册本实例向 ImageManager 单例注册过的回调。
     * Vue 组件 onUnmounted 时调用，避免每次组件卸载/重建累计泄漏 callback
     * （ImageManager 是全局单例，不会被 GC，闭包持有 this 会拖住整棵 DataGrid 对象图）。
     * 调用后再次调用是 idempotent 的（offXxx 对不存在的 callback 是 no-op）。
     */
    destroyImageManagerHooks(): void;
    /**
     * 注册一次性的"下一帧渲染完成后"回调。
     * 触发时机：initPaint 的 finally 块（draw 完成或抛异常后）。
     * 回调执行后队列会被清空；每次调用仅添加一个监听器，监听器执行一次后自动移除。
     * fail-safe：回调内部抛错只会被捕获并 console.error，不影响后续渲染循环。
     */
    onceAfterRender(cb: () => void): void;
    /**
     * 计算指定列头在屏幕坐标系下的矩形区域，用于外部将 popover / 弹窗锚点
     * 对齐到"该列表头单元格下方左对齐"。
     *
     * 返回值格式为 DOMRect-like 的 { left, top, width, height }，其中：
     * - left / top：该列表头左上角相对于视口（viewport）的坐标；
     * - width / height：该列表头的宽高（对 nested-child 即为子表头本身的尺寸）。
     *
     * 兼容 3 种 fixed 场景（left / right / 非冻结）以及水平滚动 scrollX。
     * 对嵌套子列（colType === 'nested-child'），col.y 已是子表头行的 y，
     * 因此无需额外处理，直接用 col.y 即可得到子表头自身的矩形。
     */
    computeHeaderScreenBounds(col: any): {
        left: number;
        top: number;
        width: number;
        height: number;
    };
    /**
     * Resume the render loop after it was paused due to consecutive render errors.
     * Also resets the error counter. Call this after the root cause has been fixed
     * (e.g. after loadData with corrected data).
     */
    resumeRenderLoop(): void;
    /**
     * 清除 Body 层的所有内部缓存（不影响接口数据缓存）。
     *
     * 典型场景：displayTaskNum 变化时，需要彻底清除 canvas 表格实例的缓存，
     * 确保后续 loadData 时不会从旧的 body.data 迁移任何状态（如 _nestedExpanded），
     * 同时所有 Row/Cell 对象会被重新创建。
     *
     * 清除范围：
     * - body.data（设为空数组，阻止 Body.paint() 迁移旧状态）
     * - body.rowCache / rowAccessTime（Row 对象缓存池和 LRU 访问时间）
     * - body.visibleRowIndexes / rows（可见行索引和 Row 对象数组）
     * - body._nestedDirtyRows / _nestedRenderedRows（嵌套表格渲染追踪）
     * - body.rowHeights / rowOffsets（行高和偏移量数组）
     */
    clearBodyCache(): void;
    drawContainer(): void;
    draw(): void;
    /**
     * 绘制冻结列分隔线。
     * 完全对齐 DOM 表格的 shadow-line 样式：
     *   width: 2px; box-shadow: 2px 0 2px 0 rgba(0,0,0,0.4);
     *   无独立 background-color（与表格背景色一致）。
     * 在左冻结列右边界和右冻结列左边界各绘制一条 2px 宽、与背景同色的矩形，
     * 并附带阴影，从表头顶部到表格底部（不含水平滚动条区域），始终可见。
     */
    drawFrozenColumnDivider(): void;
    /**
     * 绘制嵌套列的彩色垂直边框。
     * 在数据行（Row）、组头行（GroupRow）、加号行（AddTaskRow）和分隔行（SeparateRow）的 Y 范围内绘制。
     * 相邻行的范围会合并，确保竖线连续不断裂。
     * clip 到非冻结列区域，避免穿透到冻结列。
     */
    drawNestedColumnBorders(): void;
    /**
     * 绘制冻结列区域的嵌套列彩色竖边框（在固定列 cells 之后调用，确保不会被穿透）
     */
    drawFixedNestedColumnBorders(rowY: number, rowHeight: number): void;
    /**
     * 绘制行拖拽指示器
     */
    drawColResizeIndicator(): void;
    drawRowDragIndicator(): void;
    /**
     * Create a DOM ghost element that follows the mouse during row drag.
     * Styled to look like a table cell with task name and selected count badge.
     */
    private createDragGhostEl;
    /**
     * Remove the DOM ghost element.
     */
    private removeDragGhostEl;
    /**
     * 绘制列拖拽指示器
     */
    drawColDragIndicator(): void;
    /**
     * 事件相关------------------------------------------------------->
     */
    updateColumns(columns: Column[]): void;
    loadColumns(columns: Column[]): void;
    /**
     * 根据 selectedColIds（uniqueColKey）刷新 selector.xArr（列重排后调用）
     */
    refreshSelectorFromSelectedCols(): void;
    loadData(data: RowData[], options?: {
        preserveSelection?: boolean;
        groupDimension?: number;
    }): void;
    /**
     * 动态更新分组维度并触发完整重绘。
     *
     * 当外部分组方式变化（如从二维切到三维）时调用此方法，
     * 会更新 maxGroupLevel、originFixedWidth，清空行缓存，
     * 重建表头和表格尺寸，确保所有行（组头、数据行、+号行、分割行）
     * 都使用新的分组维度样式重新绘制。
     *
     * @param dimension - 分组维度数（0=无分组, 1=一维, 2=二维, 3=三维）
     */
    setGroupDimension(dimension: number, skipRepaint?: boolean): void;
    /**
     * 增量刷新指定列的数据（不重建整张表）
     *
     * 适用于异步数据回填场景（如 upstream、thumbnail 等字段异步加载后更新单列）。
     * 相比 loadData() 全量重建，此方法只更新目标列的 Cell 值并触发重绘，
     * 不清空 Row 缓存、不重算布局，性能开销极低。
     *
     * @param colKeys - 需要刷新的列 key 数组（如 ['upstream_reminder_state']）
     * @param newDataByKey - 可选，新的列数据 { [colKey]: value }[]，与 data 按索引对齐。
     *                       如果不传，则假设外部已直接修改了 data 数组，仅同步缓存。
     */
    refreshColumns(colKeys: string[], newDataByKey?: Record<string, any>[]): void;
    /** 获取当前可视区域的行索引范围（含缓冲区，end 为开区间） */
    getVisibleRowRange(): {
        first: number;
        end: number;
    };
    getData(): RowData[];
    getRowData(y: number): RowData;
    getCheckedRows(): RowData[];
    setSelectedRows(rowIds: string[]): void;
    getSelectedRowIds(): string[];
    /**
     * 通过 rowIndex 判断行是否被选中（checked 状态）。
     * 用于 Canvas 模式下右键菜单判断，避免 _rowId 格式不一致导致匹配失败。
     */
    isRowCheckedByIndex(rowIndex: number): boolean | undefined;
    /**
     * 获取全量数据（非受控模式下包含折叠隐藏的行）
     * 用于需要遍历所有行（包括折叠不可见的）的场景
     */
    getFullData(): RowData[];
    getChangedRows(): RowData[];
    validate(callback?: (valid: boolean) => void): void;
    validateChanged(callback?: (valid: boolean) => void): void;
    validateField(ci?: number, ri?: number): void;
    getValidations(): any[][];
    setValidations(errors?: any[]): void;
    clearValidations(): void;
    updateData(data?: any[]): void;
    /**
     * Incrementally update a single row by its index.
     * Delegates to Body.updateRowByIndex for data replacement and Cell cache sync.
     *
     * @param rowIndex - The zero-based index of the row to update
     * @param newRowData - The complete new row data object
     */
    updateRowByIndex(rowIndex: number, newRowData: any): void;
    /**
     * 暂停所有 GIF 动画
     */
    pauseGif(): void;
    /**
     * 恢复所有 GIF 动画
     */
    resumeGif(): void;
    /**
     * 切换 GIF 动画播放状态
     * @returns 当前播放状态（true: 播放中, false: 已暂停）
     */
    toggleGif(): boolean;
    /**
     * 获取 GIF 动画播放状态
     * @returns 当前播放状态（true: 播放中, false: 已暂停）
     */
    isGifEnabled(): boolean;
    /**
     * 设置斑马纹开关
     * @param enabled 是否启用斑马纹
     */
    setStriped(enabled: boolean): void;
    /**
     * 获取斑马纹状态
     * @returns 当前斑马纹状态
     */
    getStriped(): boolean;
    /**
     * 切换斑马纹状态
     * @returns 当前斑马纹状态
     */
    toggleStriped(): boolean;
    /**
     * 设置主题
     * @param theme 主题名称或主题对象
     */
    setTheme(theme: string | TableTheme): void;
    /**
     * 设置自定义 Token 覆盖（如字体族）。
     * 传入后会持久保存，主题切换时自动重新应用。
     * @param tokens 要覆盖的 Token 键值对
     */
    setCustomTokens(tokens: Record<string, any>): void;
    /**
     * 获取当前主题
     * @returns 当前主题对象
     */
    getTheme(): TableTheme;
    /**
     * 注册自定义样式类
     * @param className 样式类名
     * @param style 样式对象
     */
    registerStyle(className: string, style: any): void;
    /**
     * 批量注册样式类
     * @param styles 样式对象集合
     */
    registerStyles(styles: Record<string, any>): void;
    /**
     * 设置行高
     * @param height 行高值（像素）
     */
    setRowHeight(height: number): void;
    /**
     * 获取当前行高
     * @returns 当前行高值
     */
    getRowHeight(): number;
    /**
     * 设置日期显示类型（绝对时间/相对时间）
     * @param dateDisplayType 日期显示类型值
     */
    setDateDisplayType(dateDisplayType: string): void;
    /**
     * 开始行拖拽
     */
    startRowDrag(rowIndex: number, mouseY: number): void;
    /**
     * 更新行拖拽状态
     */
    updateRowDrag(mouseX: number, mouseY: number, clientX?: number, clientY?: number): void;
    /**
     * 结束行拖拽
     * @param mouseEvent 可选的鼠标事件，用于检测鼠标是否在 canvas 外部（拖拽到外部组件如 TreeView）
     */
    endRowDrag(mouseEvent?: MouseEvent): void;
    /**
     * 取消行拖拽
     */
    cancelRowDrag(): void;
    /**
     * 设置hover行索引
     * @param rowIndex 行索引，-1表示无hover
     */
    setHoverRow(rowIndex: number): void;
    /**
     * 设置hover单元格索引
     * @param colIndex 列索引，-1表示无hover
     * @param rowIndex 行索引，-1表示无hover
     */
    setHoverCell(colIndex: number, rowIndex: number): void;
    /**
     * 设置滚动状态
     * @param isScrolling 是否正在滚动
     */
    setScrolling(isScrolling: boolean): void;
    /**
     * 程序化滚动到指定位置
     * @param x 水平滚动位置（0 或负值）。传 undefined/null 则保持当前值。
     * @param y 垂直滚动位置（0 或负值）。传 undefined/null 则保持当前值。
     */
    scrollTo(x?: number | null, y?: number | null): void;
    /**
     * 获取当前滚动位置
     */
    getScrollPosition(): {
        scrollX: number;
        scrollY: number;
        maxScrollX: number;
        maxScrollY: number;
    };
    /**
     * 滚动到最右边（水平方向）
     */
    scrollToRight(): void;
    /**
     * 滚动到指定水平位置（使 left 位置出现在视口右边缘）
     * 与 DOM 版本 fattable.scrollLeftTo(left) 语义一致：scrollLeft = left - clientWidth
     * @param left 目标位置（正值，单位 px）
     */
    scrollLeftTo(left: number): void;
    /**
     * 判断指定 rowIndex 的行是否完全在可见视口内
     * @param rowIndex - data 数组中的行索引
     * @returns 行是否完全可见
     */
    isRowIndexFullyVisible(rowIndex: number): boolean;
    /**
     * 判断指定 rowId 的行是否完全在可见视口内
     * @param rowId - 行的唯一标识（基于 rowKey 配置对应的字段值）
     * @returns 行是否完全可见
     */
    isRowFullyVisibleById(rowId: string | number): boolean;
    /**
     * 将指定 rowIndex 的行滚动到可见区域内（如果已在可见区域则不做任何操作）
     * @param rowIndex - data 数组中的行索引
     */
    scrollRowIntoView(rowIndex: number): void;
    /**
     * 将指定 rowId 的行滚动到可见区域内（如果已在可见区域则不做任何操作）
     * @param rowId - 行的唯一标识（基于 rowKey 配置对应的字段值）
     */
    scrollRowIntoViewById(rowId: string | number): void;
    /**
     * 获取指定行的布局信息（精确的 y 偏移和高度）
     * @param rowIndex - data 数组中的行索引
     * @returns 行的布局信息 { y, height }，如果行不存在则返回 null
     */
    getRowLayout(rowIndex: number): {
        y: number;
        height: number;
    } | null;
    /**
     * 根据列 key 获取列的布局信息（精确的 x 偏移、宽度、冻结状态）
     * @param key - 列配置中的 key 属性
     * @returns 列的布局信息 { x, width, fixed }，如果列不存在则返回 null
     *
     * task=89c1ec62/iter#3：原实现走 `this.columns.findIndex(key)` →
     *   `topLevelHeaders[colIndex]`，但 `this.columns = toLeaf(columns)` 会把
     *   `children` 复合表头展平成多个叶子，而 `topLevelHeaders = allColumnHeaders.filter(!level)`
     *   只保留 level=0（每个复合表头只算一个）；二者长度/索引不一致，导致：
     *   - 复合表头之后的简单列 colIndex 比实际位置大 → 取到错误 colHeader（甚至 undefined）；
     *   - 真实事故：iter#2 verify case-3 freeze `name` 后 isFieldInFixLeftArea=true（控制器
     *     已写 fixedLeft），但 isColumnFullyVisibleByKey('name') 仍返回 false → 拿到的
     *     colHeader.fixed 不是 'left'，即取到了错误的列。
     *   修复：优先按 `ColumnHeader.colKey` 直接查 topLevelHeaders；查不到再回退原索引法。
     */
    getColumnLayoutByKey(key: string): {
        x: number;
        width: number;
        fixed: 'left' | 'right' | '' | undefined;
    } | null;
    /**
     * 判断指定 key 的列是否完全位于「非冻结视口」内（task=89c1ec62）。
     *
     * 视口左边界 = fixedLeftWidth；视口右边界 = width - fixedRightWidth。
     * 列在屏幕上的左/右 = colX + scrollX / colX + scrollX + colWidth。
     *
     * - 冻结列（fixed === 'left' | 'right'）恒返回 true（永远可见）
     * - 列不存在 / layout 为空 → 返回 false
     *
     * @param key 列配置中的 key 属性
     */
    isColumnFullyVisibleByKey(key: string): boolean;
    /**
     * 横向滚动表格，使指定 key 的列出现在「非冻结视口」内（task=89c1ec62）。
     *
     * - 列在视口左侧外 → newScrollX = fixedLeftWidth - layout.x（让列左边贴齐视口左边界）
     * - 列在视口右侧外 → newScrollX = (width - fixedRightWidth) - (layout.x + layout.width)
     *                      （让列右边贴齐视口右边界）
     * - 已完全可见 / 冻结列 / 列不存在 → no-op
     *
     * scrollTo 内部会 clamp 到 [maxScrollX, 0]，无需在此手动 clamp。
     *
     * @param key 列配置中的 key 属性
     */
    scrollColumnIntoViewByKey(key: string): void;
    /**
     * 获取指定单元格相对于 canvas 的调整后 bounds（与 Row.ts 中 onEditIconClick 传递的 bounds 计算方式一致）
     * @param colIndex - canvas 中的列索引
     * @param rowIndex - canvas 中的行索引
     * @returns 调整后的 bounds（考虑了固定列和滚动偏移），或 null
     */
    getCellBounds(colIndex: number, rowIndex: number): {
        x: number;
        y: number;
        width: number;
        height: number;
    } | null;
    /**
     * 根据 canvas 局部坐标命中可见 cell（外部文件拖拽 hit-test 入口）。
     *
     * 复用既有 `Body.rows[].cells[].isInsideHorizontalBodyBoundary` /
     * `isInsideFixedHorizontalBodyBoundary` / 行级 `isInsideVerticaBodyBoundary`，
     * 不重写坐标转换公式，保证与 mousedown/mousemove/click 路径完全一致
     * （遵守 canvas-table-event-management SKILL §"Coordinate Systems"）。
     *
     * 嵌套子表（NestedGrid）：当命中的主表 cell 持有 nestedGrid 实例且 NestedGrid
     * 也实现了 getCellAtPoint 时，递归命中子格子；否则返回主表 cell。
     * 若嵌套表 hit-test 当前未实现（NestedGrid 没有 getCellAtPoint），fallback
     * 到主表 cell（保证主表场景永不退化），后续 iter 在 NestedGrid 上补该方法即可。
     *
     * @param x canvas 局部 X（不含 dpr 缩放）
     * @param y canvas 局部 Y
     * @returns 命中的 cell 信息，或 null
     */
    getCellAtPoint(x: number, y: number): {
        rowIndex: number;
        colIndex: number;
        columnKey: string;
        column: any;
        cell: any;
        bounds: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
    } | null;
    /**
     * 判断列是否接受外部 OS 文件 drop。
     * 规则（按优先级）：
     * 1. column.acceptExternalFileDrop === false → 显式禁用，永远 false
     * 2. column.readonly === true → 禁用
     * 3. column.acceptExternalFileDrop === true → 显式启用
     * 4. column.type === 'image' | 'file'（或 viewerType 等价）→ 默认启用
     * 5. 其它 → false
     */
    isColumnAcceptingExternalFileDrop(column: any): boolean;
    /**
     * 外部文件拖拽：命中 cell 后的核心状态机。
     * - 命中 image/file（且 acceptExternalFileDrop 不为 false）：
     *   1) 首次进入：暂存 previousFocusCell；
     *   2) 切换 cell：调 selectCell 把高亮迁移；
     *   3) 同 cell：no-op，避免每帧重复 selectCell。
     * - 命中非 image/file 列 or 命中失败：调 clearExternalFileDragHover 清状态。
     */
    handleExternalFileHit(cellInfo: {
        rowIndex: number;
        colIndex: number;
        columnKey: string;
        column: any;
        cell: any;
        bounds: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
    } | null): void;
    /**
     * 清掉外部文件拖拽的视觉状态：
     * - 还原拖拽前的 focusCell（若存在），否则 clearMultiSelect 清空选区；
     * - 触发 onExternalFileDragLeave 回调（带最后一个 active cell 信息）；
     * - 重置内部 externalFileDrag 状态机。
     *
     * 2026-05-25 修复（task=27c2d1ef e2e iter9 后真实使用反馈）：
     *   drop 之后 cell 形成"从 dragenter cell 一直延展到鼠标当前位置"的 rectangle 框选，
     *   必须用户再 mousedown 一次才恢复。根因：dragenter 期间调 `selectCell` 把
     *   `selector.isSelected = true`（L1721 进入"多选 latch"状态），后续 mousemove
     *   会扩展 selector.xArr/yArr。但 drop 是 native drag 事件，不会触发 mouseup，
     *   `endMultiSelect()` 永不调用 → `isSelected` 永远 latched 为 true。
     *   修法：在拖拽视觉清理末尾显式把 `selector.isSelected = false`（等价于一次
     *   合成的 mouseup），同时 `edgeScroller.stop()` 防止边缘自动滚动状态残留
     *   （endMultiSelect 也会调）。
     *
     * 2026-05-26 修复：drop 命中目标 cell 后，UX 预期是\"被上传的那个 cell 保持选中\"
     *   而不是回退到拖拽前的 focusCell（直觉上正在动作的对象该有焦点）。
     *   增加可选参数 `dropTarget`：drop 命中且 callback 调通时，由 Events 层传入
     *   目标 cell；本方法优先 selectCell(dropTarget)，并跳过 previousFocusCell 还原。
     *   - dragleave 路径不传 dropTarget → 走原 prev 还原逻辑（保留旧行为）
     *   - drop 未命中（空白处 / 列不接受）路径 Events 层也不传 dropTarget → 同样还原 prev
     */
    clearExternalFileDragHover(dropTarget?: {
        rowIndex: number;
        colIndex: number;
    }): void;
    /**
     * 将指定单元格滚动到可见区域内（同时处理垂直和水平方向）
     * - 垂直方向：确保行在视口内
     * - 水平方向：确保列不被固定列遮挡（仅对非固定列生效）
     * @param rowIndex - 行索引
     * @param colIndex - 列索引
     */
    scrollCellIntoView(rowIndex: number, colIndex: number): void;
    /**
     * 将指定单元格滚动到可见区域内（通过 rowId 和 colIndex）
     * @param rowId - 行的唯一标识
     * @param colIndex - 列索引；传入负数（如 -1）表示仅做垂直滚动、保持水平滚动位置
     *                  （适用于分组组头等不关联具体列的行定位场景）
     */
    scrollCellIntoViewById(rowId: string | number, colIndex: number): void;
    /**
     * 预加载 loading 图片和刷新图标
     */
    private preloadLoadingImage;
    /**
     * 预加载刷新 SVG 图标（icon_backtodefault），转换为可绘制的 Image 对象
     */
    private preloadRefreshIcon;
    /**
     * 显示指定单元格的 loading 状态
     * @param rowIds 行的唯一标识数组（基于 rowKey 配置对应的行数据字段值）
     * @param columnKeys 列的 key 字段数组
     * @param timeout 超时时间（毫秒），默认 60000（1 分钟）
     */
    showLoading(rowIds: string[], columnKeys: string[], timeout?: number): void;
    /**
     * 手动停止指定单元格的 loading
     * @param rowIds 行的唯一标识数组
     * @param columnKeys 列的 key 字段数组
     */
    hideLoading(rowIds: string[], columnKeys: string[]): void;
    /**
     * 清理所有 loading 状态
     */
    clearAllLoading(): void;
    /**
     * 清除图片加载失败记录，允许重新加载。
     * 当图片签名 URL 过期后业务层获取到新 URL 时调用，
     * 清除旧 URL 的失败标记使 ImageViewer 能重新尝试加载。
     */
    clearFailedImages(): void;
    /**
     * 内部辅助：移除单个 loading 状态
     */
    private removeSingleLoading;
    /**
     * Check if there are any active loading cells.
     */
    hasLoadingCells(): boolean;
    /**
     * 查询指定 cellKey 是否处于 loading 状态
     */
    isLoadingCell(cellKey: string): boolean;
    /**
     * 获取当前 loading 旋转角度
     */
    getLoadingRotation(): number;
    /**
     * 获取预加载的 loading 图片
     */
    getLoadingImage(): HTMLImageElement | null;
    /**
     * 获取预加载的刷新图标
     */
    getRefreshIcon(): HTMLImageElement | null;
    /**
     * 增加 footer/groupRow loading 引用计数，确保动画持续运行
     */
    addFooterLoading(): void;
    /**
     * 减少 footer/groupRow loading 引用计数
     */
    removeFooterLoading(): void;
    /**
     * 启动 loading 动画循环（轻量级：仅更新角度 + markDirty）
     */
    private startLoadingAnimation;
    /**
     * 停止 loading 动画循环
     */
    private stopLoadingAnimation;
    /**
     * 设置列高亮（列着色）
     * @param columnKey 列的 key 标识
     * @param color 高亮背景色，传空字符串或 falsy 值表示清除该列的高亮
     */
    setColumnHighlight(columnKey: string, color: string): void;
    /**
     * 批量设置列高亮（列着色）
     * @param highlights 列着色映射对象：{ columnKey: color }，color 为空字符串表示清除
     */
    setColumnHighlights(highlights: Record<string, string>): void;
    /**
     * 清除所有列高亮
     */
    clearColumnHighlights(): void;
    /**
     * 获取指定列的高亮色
     * @param columnKey 列的 key 标识
     * @returns 高亮色字符串，未设置时返回空字符串
     */
    getColumnHighlightColor(columnKey: string): string;
    /**
     * 设置是否隐藏表头业务图标（TAPD / IOMC / fieldHint / link）。
     * 对齐 DOM 表格的 "显示表头图标" 开关：关闭时四种图标均不绘制，
     * 但排序/筛选/设置图标不受影响，同时图标 hover 热区（fieldHintIconBounds /
     * linkIconBounds）也会在下次 draw 时被置空，避免隐藏后仍触发 hover。
     */
    setHideHeaderIcons(hide: boolean): void;
    /**
     * 批量设置表头文字颜色（用于新加入字段紫色高亮等场景）
     * @param colors 表头文字颜色映射对象：{ columnKey: color }，color 为空字符串表示清除
     */
    setHeaderTextColors(colors: Record<string, string>): void;
    /**
     * 获取指定列的表头文字颜色
     * @param columnKey 列的 key 标识
     * @returns 文字颜色字符串，未设置时返回空字符串
     */
    getHeaderTextColor(columnKey: string): string;
    /**
     * 批量设置排序状态（替换所有排序）
     * @param orders 排序数组：[{ meta: string, type: 'asc'|'desc' }]
     *   meta 对应列的 convertMeta，type 为排序方向
     */
    setSortOrders(orders: Array<{
        meta: string;
        type: 'asc' | 'desc';
    }>): void;
    /**
     * 批量设置模块排序状态（各模块独立管理排序，与全局排序互不干扰）
     * @param moduleOrders 模块排序数组：[{ moduleId: string, orders: [{ meta, type }] }]
     */
    setModuleSortOrders(moduleOrders: Array<{
        moduleId: string;
        orders: Array<{
            meta: string;
            type: 'asc' | 'desc';
        }>;
    }>): void;
    /**
     * 获取全局排序状态
     * @param convertMeta 列的 convertMeta 标识
     * @returns 排序状态对象，未排序时返回 null
     */
    getSortOrder(convertMeta: string): {
        type: 'asc' | 'desc';
        index: number;
    } | null;
    /**
     * 获取模块排序状态（嵌套子列使用）
     * @param moduleId 模块 ID（即父列的 key）
     * @param convertMeta 列的 convertMeta 标识
     * @returns 排序状态对象，未排序时返回 null
     */
    getModuleSortOrder(moduleId: string, convertMeta: string): {
        type: 'asc' | 'desc';
        index: number;
    } | null;
    /**
     * 获取全局排序列总数
     */
    getSortOrderCount(): number;
    /**
     * 获取指定模块的排序列总数
     */
    getModuleSortOrderCount(moduleId: string): number;
    /**
     * 设置搜索匹配颜色
     */
    setSearchMatchColors(colors: {
        backgroundColor: string;
        borderColor: string;
    }): void;
    /**
     * 获取搜索匹配颜色
     */
    getSearchMatchColors(): {
        backgroundColor: string;
        borderColor: string;
    };
    /**
     * Get layout information for a specific cell identified by rowId and columnKey.
     * Returns a deep copy to prevent accidental mutation of internal state.
     * @param rowId - Row identifier (number for normal/viewer, string for grouped)
     * @param columnKey - Column key string
     * @returns CellLayout object or null if cell not found
     */
    testGetCellLayout(rowId: string | number, columnKey: string): {
        x: number;
        y: number;
        width: number;
        height: number;
        visible: boolean;
        columnKey: string;
        rowId: string | number;
        colIndex: number;
        rowIndex: number;
        fixed: string;
    } | null;
    /**
     * Get all cells currently visible in the viewport.
     * Only returns cells rendered by the virtual scroll engine.
     * @returns Array of visible cell layout objects
     */
    testGetAllVisibleCells(): Array<{
        x: number;
        y: number;
        width: number;
        height: number;
        visible: boolean;
        columnKey: string;
        rowId: string | number;
        colIndex: number;
        rowIndex: number;
        fixed: string;
        value: any;
        label: string;
    }>;
    /**
     * Get configuration for a specific column by its key.
     * Supports both top-level columns and nested child columns.
     * @param columnKey - Column key string
     * @returns Column configuration object or null if not found
     */
    testGetColumnConfig(columnKey: string): {
        key: string;
        title: string;
        width: number;
        fixed: string;
        visible: boolean;
        index: number;
        level: number;
        type: string;
        children: any[] | null;
        colspan: number;
        rowspan: number;
        parentKey: string | null;
    } | null;
    /**
     * Get data for a specific row identified by rowId.
     * @param rowId - Row identifier
     * @returns Row data object (deep copy) or null if not found
     */
    testGetRowData(rowId: string | number): Record<string, any> | null;
    /**
     * 获取组头行搜索命中高亮状态（用于 E2E / 单元测试验证）。
     *
     * @param rowId - 组头行 id（分组模式下通常是 string，如 "group-project-0"）
     * @returns { matched, currentHighlight } 或 null（行不存在 / 非组头行）
     */
    testGetGroupHeaderSearchMatchState(rowId: string | number): {
        matched: boolean;
        currentHighlight: boolean;
    } | null;
    /**
     * 获取组头行搜索命中的"首列左边框" x 坐标（用于 E2E / 单元测试验证多层分组的绘制位置）。
     *
     * @param rowId - 组头行 id
     * @returns x 坐标（canvas 相对坐标）或 null（行不存在 / 非组头行 / 未命中 GroupRow 实例）
     */
    testGetSearchHighlightFirstLeftBorderX(rowId: string | number): number | null;
    /**
     * Get current scroll position and scrollbar state.
     * @returns ScrollPosition object
     */
    testGetScrollPosition(): {
        scrollX: number;
        scrollY: number;
        maxScrollX: number;
        maxScrollY: number;
        hasHorizontalScrollbar: boolean;
        hasVerticalScrollbar: boolean;
    };
    /**
     * Get overall table dimensions and metrics.
     * @returns TableDimensions object
     */
    testGetTableDimensions(): {
        containerWidth: number;
        containerHeight: number;
        tableWidth: number;
        tableHeight: number;
        headerHeight: number;
        rowHeight: number;
        fixedLeftWidth: number;
        fixedRightWidth: number;
        totalRows: number;
        totalColumns: number;
    };
    /**
     * Test hook: Compare header column widths with body cell widths.
     * Returns per-column comparison to detect header/body width mismatches,
     * especially for nested (multi-sub-column) columns.
     */
    testGetHeaderBodyWidthComparison(): Array<{
        colIndex: number;
        columnKey: string;
        headerWidth: number;
        bodyCellWidth: number;
        isNested: boolean;
        match: boolean;
    }>;
    /**
     * Get info for all nested grids in the current viewport rows.
     * Used by E2E tests to discover and interact with nested grids.
     *
     * ⚠️ 实现细节（避免 e2e 探测假阴性）：
     *   1. `body.rows` 由虚拟滚动只缓存视口内的行，所以"完全滚出视口的行"
     *      上的嵌套表无法被报告 —— 这是必然损耗，e2e 端需要先 scroll 到目标行附近。
     *   2. `cell.nestedGrid` 是 **延迟创建** 的（见 Cell.ts:createNestedGridLazy()），
     *      只有 cell 被 `draw()` 至少一次才会实例化。横向滚动到刚露出的 cell
     *      在第一次 draw 之前 nestedGrid 仍是 null，但 `cell.hasNestedData=true`
     *      已经设好。这里用 `hasNestedData` 当判据，未实例化的也报告出来，
     *      让 e2e 端能正确识别"该列存在嵌套表，只是当前还没 draw"。
     *      对于这种 cell，`dataRowCount/columnCount` 等字段从 `nestedConfig`
     *      或 `column.nestedColumns` 推导，bounds 仍由 cell 几何 + scroll 计算。
     *   3. 触发实际 draw：调用方拿到这个 list 后若需要交互，可用 `triggerNestedGridDraw`
     *      或者先 scrollToCell 让目标 cell 进入绘制流程，nestedGrid 就会被创建。
     */
    testGetNestedGridInfoList(): Array<{
        parentRowId: string | number;
        parentRowIndex: number;
        parentColKey: string;
        parentColIndex: number;
        /** Parent column display name（取自 column.name / title / label，便于 spec 按"配置/美宣"等业务名匹配） */
        parentColName: string;
        bounds: {
            x: number;
            y: number;
            width: number;
            height: number;
        };
        dataRowCount: number;
        columnCount: number;
        columnKeys: string[];
        selectionRange: {
            startRow: number;
            startCol: number;
            endRow: number;
            endCol: number;
        } | null;
        selectedCell: {
            row: number;
            col: number;
        } | null;
        additionalSelectedCells: string[];
        expanded: boolean;
        editingCell: {
            row: number;
            col: number;
        } | null;
        hoveredCell: {
            row: number;
            col: number;
        } | null;
        rowHeight: number;
        /** NestedGrid 实例是否已 lazy-create（false=配置存在但还没 draw 过） */
        rendered: boolean;
    }>;
    /**
     * Get nested grid info for a specific parent cell.
     */
    testGetNestedGridInfo(parentRowId: string | number, parentColKey: string): any | null;
    /**
     * Get the canvas-relative bounding box of a nested cell.
     * Used for dispatching mouse events precisely on a nested cell.
     */
    testGetNestedCellBounds(parentRowId: string | number, parentColKey: string, nestedRow: number, nestedCol: number): {
        x: number;
        y: number;
        width: number;
        height: number;
        canvasX: number;
        canvasY: number;
    } | null;
    /**
     * Find row index by row ID, searching through body data.
     * Supports both numeric and string row IDs.
     * @param rowId - Row identifier to search for
     * @returns Row index or -1 if not found
     */
    private findRowIndexById;
    /**
     * 根据数据中最大 groupLevel 动态计算 originFixedWidth（index 列宽度）
     * 规则：
     * - 非分组/一维分组(maxLevel=0): 52px
     * - 二维分组(maxLevel=1): 62px
     * - 三维分组(maxLevel>=2): 72px
     * @param data 可选，如果不传则使用当前 this.data
     */
    recalcOriginFixedWidth(data?: RowData[]): void;
    /** 动态切换 footer 显示状态 */
    setShowTableIndex(show: boolean): void;
    setShowFooter(show: boolean): void;
    /** 动态设置 footer 行高度 */
    setFooterHeight(height: number): void;
    /** 一次性设置所有列的 footer 统计数据 */
    setFooterData(data: FooterData): void;
    /** 设置单列的 footer 统计数据 */
    setFooterCellData(columnKey: string, data: FooterCellData): void;
    /** 一次性设置指定分组组头各列的统计数据 */
    setGroupHeaderStatData(groupId: string | number, data: FooterData): void;
    /** 设置指定分组指定列的组头统计数据（不立即重绘，使用 markDirty 批量合并） */
    setGroupHeaderStatCellData(groupId: string | number, columnKey: string, cellData: FooterCellData): void;
    /**
     * 设置所有组头行中指定列的 loading 状态
     * @param columnKey - 列标识 key（嵌套列格式为 "moduleColKey::childKey"）
     * @param loading - 是否设为 loading 状态
     */
    setGroupHeaderColumnLoading(columnKey: string, loading: boolean): void;
    /**
     * 设置 Footer 弹窗激活的列标识（弹窗打开时调用），
     * 使该列的下拉箭头和刷新图标在鼠标移开后仍保持显示。
     * 传 null 表示关闭弹窗、清除激活状态。
     */
    setFooterPopoverActive(key: string | null): void;
    /**
     * 设置组头弹窗激活状态（弹窗打开时调用），
     * 使该分组该列的下拉箭头和刷新图标在鼠标移开后仍保持显示。
     * 传 null 表示关闭弹窗、清除激活状态。
     * @param key - 列标识 key，传 null 表示清除
     * @param groupId - 分组 ID，仅 key 非 null 时需要传入；
     *   若省略 groupId 但当前已有激活状态，则保留已有的 groupId（兼容外部只传 key 的调用）
     */
    setGroupHeaderPopoverActive(key: string | null, groupId?: string | number): void;
    /** 从 body.data 中查找指定 groupId 的 GroupRowData */
    private findGroupRowData;
    /**
     * 非受控模式下：切换分组展开/折叠并重绘
     * 根据 _fullData 中 isGroup 行的 expanded 和 children 属性，
     * 递归过滤出可见行（展开的分组显示子行，折叠的隐藏子行），
     * 然后调用 body.paint() 重新渲染。
     *
     * @param _groupRow - 被点击的分组行数据
     */
    toggleGroup(_groupRow: RowData): void;
    /**
     * 同步所有叶子组头的 checked 状态：
     * 遍历全量数据，检查每个叶子组下的子行是否全部在 selectedRowIds 中，
     * 如果全选则 checked = true，否则 checked = false
     */
    /**
     * 给行数据分配稳定的 _rowId（基于 rowKey 配置字段，如 "id"），
     * 确保 Row 构造函数使用稳定 ID（而非 row_${index}），
     * 使 selectedRowIds 中的值在展开/收起后仍然有效
     */
    private ensureRowIds;
    syncGroupCheckedState(): void;
    /**
     * 清除所有组头的勾选状态、selectedGroupIds 集合及 sessionStorage 缓存。
     * 在任务勾选被清除时调用，确保组头状态和外部缓存保持一致。
     */
    clearGroupCheckedState(): void;
    /**
     * 折叠所有任务（受控模式下触发 onCollapseAll 回调，由外部操作 tableTaskList 后刷新）
     */
    collapseAll(): void;
    /**
     * 展开所有任务（受控模式下触发 onExpandAll 回调，由外部操作 tableTaskList 后刷新）
     */
    expandAll(): void;
    /**
     * 展开前 N 层级（受控模式下触发 onExpandToLevel 回调，由外部操作 tableTaskList 后刷新）
     * @param level - 目标展开层级
     */
    expandToLevel(level: number): void;
    /**
     * 根据分组展开状态过滤可见行（非受控模式）
     *
     * 数据结构约定：
     * - isGroup 行有 children 属性（GroupRowData[]）和 expanded 属性
     * - _fullData 是扁平数组，分组行和其子行按顺序排列
     * - 折叠分组时，需要跳过其后续的所有子行（直到下一个同级或更高级分组/分隔行）
     *
     * 算法：遍历 _fullData，使用分组栈跟踪折叠状态
     */
    filterVisibleRows(fullData: RowData[]): RowData[];
    /**
     * Get interactive areas within a column header.
     * Returns sort/filter icon, resize handle, and settings icon bounds.
     */
    testGetHeaderInteractiveAreas(colIndex: number): {
        header: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        };
        sortIcon: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        } | null;
        filterIcon: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        } | null;
        resizeHandle: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        };
        settingsIcon: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        } | null;
        meta: {
            colId: string;
            columnKey: string;
            colType: string;
            index: number;
            sortDisabled: boolean;
            canDrag: boolean;
            isNested: boolean;
        };
    } | null;
    /**
     * Get all interactive (clickable) areas within a specific cell.
     * Returns canvas-relative coordinates for edit icon, link icon, error icon, etc.
     */
    testGetCellInteractiveAreas(rowId: string | number, columnKey: string): {
        cell: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        };
        editIcon: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        } | null;
        linkIcon: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        } | null;
        errorIcon: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        } | null;
        reminderTriangle: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        } | null;
        viewerAreas: Array<{
            type: string;
            bounds: {
                canvasX: number;
                canvasY: number;
                width: number;
                height: number;
            };
            value?: any;
        }>;
        meta: {
            viewerType: string;
            readonly: boolean;
            isLink: boolean;
            hasError: boolean;
            hasReminder: boolean;
            hasNestedGrid: boolean;
        };
    } | null;
    /**
     * Get interactive areas within a group row header.
     */
    testGetGroupRowAreas(rowId: string): {
        expandArrow: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        };
        checkbox: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        } | null;
        row: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        };
        meta: {
            expanded: boolean;
            groupLevel: number;
            groupName: string;
            isLeafGroup: boolean;
            rowId: string;
        };
    } | null;
    /**
     * Get the row checkbox area for a normal (non-group) data row, in canvas coordinates.
     *
     * 这是 LRN-20260512-001 约定的"长期稳定 e2e 锚点 API"：批量下载 / 多选等
     * spec 在 canvas 模式下需要点击行 checkbox。原本依赖 e2e 端按"index 列宽度的
     * 一半"近似估算 click point，在分组模式 / 不同 originFixedWidth 下会偏移到
     * 行号区，触发 onRowClick 而不是 checkbox toggle，导致 selectFirst5Tasks 失败。
     *
     * 这里直接复用 RowHeader 内部已有的 checkbox 偏移规则（与
     * `RowHeader.isInsideCheckboxBoundary` 完全对齐），保证 e2e 点中的位置就是
     * 业务运行时判定 checkbox 命中的位置。
     *
     * @param rowId - Row identifier
     * @returns 16×16px checkbox 矩形 + 行内 y 中心坐标 + 元数据；行不存在 / 关闭了
     *          showTableIndex / showCheckbox / 是组头行 → 返回 null。
     */
    testGetRowCheckboxArea(rowId: string | number): {
        canvasX: number;
        canvasY: number;
        width: number;
        height: number;
        meta: {
            rowIndex: number;
            checked: boolean;
            maxGroupLevel: number;
            parentGroupLevel: number | null;
        };
    } | null;
    /**
     * Find a row's stable user-facing id (`row[rowKey]`) by a name-like field value.
     *
     * 这是 LRN-20260512-001 约定的"长期稳定 e2e 锚点 API"，专门给 AmE2ETest 在 canvas
     * 模式下做"按任务名找 row"用——之前 helper 端 `findRowIdByTaskName` 自己在
     * `getAllVisibleCells` / `grid.body.data` / `controller.tableTaskList` 三层 fallback
     * 各 evaluate 一次，每次都序列化深拷贝整张表，刚 setExtraTableData 后还有 hooks
     * 短暂返回 null 的窗口期；这里把"name → rowId"映射收敛进表本体的 `body.data`，
     * 不依赖任何视口状态、也不需要 row 在 rowCache 中实例化。
     *
     * 匹配规则（默认）：
     *   - 候选字段：`name`, `task_name`, `taskName`, `title`
     *     · 优先精确相等；找不到才退到 includes 匹配。
     *     · 字段值是对象时（自定义 viewer，例如交付物列返回 `{ files: [...] }`），
     *       展开 `value.name / value.task_name / value.taskName / value.title /
     *             value.value / value.label / value.text` 再匹配字符串。
     *   - 跳过组头行 / 分隔行 / 加任务行（这些行的 rowKey 通常不是真实任务 id）。
     *
     * 搜索范围（iter#3 加固，覆盖 batch-download spec 在上传/分组场景的命中失败）：
     *   1) 先扫 `this.body.data`（视口内 + 当前 filter 后的数据，最常见路径）
     *   2) 没命中再扫 `this._fullData`（包含被折叠组隐藏的子行 / filter 过滤掉的行）
     *   - 这样上传文件触发的"行临时移出 body.data"窗口期、以及 grouped 模式下查
     *     找折叠组子任务时不再返回 null，spec 端 retry 次数显著下降。
     *   - 返回的 id 仍然是 `row[rowKey]`，与 body.data 路径完全一致；不会因为
     *     fallback 出现"id 形态不一致 → checkbox 命中比较失败"的二次问题。
     *
     * 返回的是 `row[rowKey]`（与 `getAllVisibleCells().rowId` / `getCheckedRowIds()`
     * 严格对齐），不是内部的 `_rowId` string——这样 spec 端 `===` 比较不会因
     * `'string' === 1234` 失败。
     *
     * @param name - 任务名 / 显示名（不区分精确匹配 vs 包含匹配，由内部判定）
     * @param options.fields - 自定义候选字段数组（默认 `['name','task_name','taskName','title']`）
     * @param options.exact - true 时仅做精确相等匹配；默认 false（fallback 到 includes）
     * @returns row[rowKey] 或 null（找不到 / data 为空）
     */
    testFindRowIdByName(name: string, options?: {
        fields?: string[];
        exact?: boolean;
    }): string | number | null;
    /**
     * E2E 诊断 hook：返回当前 DataGrid 的关键状态摘要（不含完整 row dump，避免序列化爆炸）。
     *
     * iter#4 新增（task=cdc1b0da）：spec 在 canvas 模式下连续 3 轮出现 0/10 task 命中，
     * 但既有 hook 全是"找不到就返回 null"——观察侧拿不到任何中间状态信息。本 hook 给
     * spec / observation 一个轻量的"实地探查"入口，可以快速判断：
     *   - 当前 grid 是不是空的（data.length === 0）
     *   - rowKey 是不是预期值（业务侧默认 'id'，task 池常用 'task_id'）
     *   - 任务名是否真的存在于 body.data / _fullData 但被规则筛掉了（broadScan 命中数）
     *   - 前 3 行的字段集合是什么样（用于人工核对 task name 落在哪个字段）
     *
     * task=b1464bc3/iter#4 增强：返回新增 `columnsSummary`——top-level 列的
     * `{index, key, name, fixed, hasChildren}` 摘要（最多 50 条），用于 freeze-column
     * spec 在 `waitForFieldFrozenState` 超时时精准区分"controller 未生效"vs"grid 未刷新"。
     *
     * 返回结构刻意保持小（前 3 行 sample, 字段名列表）—— 便于 console.log 整段直接 dump。
     *
     * @param name - 可选；提供时会做一次 case-insensitive includes 扫描，返回命中数和首批样本
     * @returns 诊断对象，永不抛错
     */
    testGetDiagnostics(name?: string): {
        rowKey: string;
        bodyDataLength: number;
        fullDataLength: number;
        columnsCount: number;
        sampleRowFieldNames: string[];
        sampleRows: Array<Record<string, any>>;
        nameProbe: {
            query: string;
            bodyMatchCount: number;
            fullMatchCount: number;
            bodyHits: Array<{
                rowIndex: number;
                matchedField: string;
                matchedValue: string;
                rowKeyValue: any;
            }>;
        } | null;
        columnsSummary: Array<{
            index: number;
            key: string;
            name: string;
            fixed: string;
            hasChildren: boolean;
        }>;
    };
    /**
     * Look up a column's business `key` by header text / column title.
     *
     * 与 `testFindRowIdByName` 互补：解决 LRN-20260512-002 描述的
     * "column.key vs header.colId（数字字符串）混淆"——AmE2ETest helper
     * `findCanvasDeliverableColumnKey('交付物')` 在 iter-1 主要走 host
     * controller 的 `e2eFindCanvasColumnKey`，但 host 没就绪 / 业务列正在重建时
     * 会 fallback 到 `grid.columns[i].title.includes(...)`，多次 evaluate 命中率低。
     *
     * 现在表格本体直接给一个稳定 entry：从 `this.columns[i].title / name / label`
     * 里反查到第一个匹配项的 `key`。
     *
     * @param displayName - 列的显示名（如 "交付物"）
     * @param options.exact - 默认 false：includes 匹配；true 时仅做精确相等
     * @returns column.key（业务字段名）或 null
     */
    testGetColumnKeyByName(displayName: string, options?: {
        exact?: boolean;
    }): string | null;
    /**
     * Atomic"按任务名 + 列显示名一次性拿到 cell interactive areas"hook。
     *
     * iter#2 加固（task=3f7204c0/iter#2）：解决 batch-download spec 在 canvas 模式下
     * 的"多次 page.evaluate 之间存在重渲染窗口期"问题——
     *   旧路径 = ① findRowIdByName → ② getColumnKeyByName → ③ scrollToRow
     *           → ④ getCellInteractiveAreas，每一步都跨一次浏览器 tick；上传完文件后
     *           setExtraTableData 会触发部分 row 临时重排，第 ① 步拿到的 rowId 在第
     *           ④ 步前可能已经从 body.data 里移走，导致 getCellInteractiveAreas 返回
     *           null，spec 端判定"找不到 cell"。
     *
     * 这里把 4 步压成单次同步调用：name → rowId（含 _fullData fallback） →
     * displayName → columnKey → 自动 scrollIntoView → 直接复用
     * `testGetCellInteractiveAreas` 拿区域信息。spec / helper 只需要一次 evaluate。
     *
     * 不能命中的情况：
     *   - taskName 在 body.data 与 _fullData 都查不到 → 返回 { ok: false, reason: 'row-not-found' }
     *   - column displayName 找不到对应 column.key → 返回 { ok: false, reason: 'column-not-found', rowId }
     *   - rowId 找到了但 row 不在 body.data（被分组折叠）+ scrollIntoView 后仍未进入
     *     视口 → 返回 { ok: false, reason: 'row-not-visible', rowId, columnKey }
     *
     * 完全命中时返回 testGetCellInteractiveAreas 的全部输出 + 解析出的 rowId/columnKey。
     *
     * @param taskName - 任务名（与 testFindRowIdByName 的入参语义一致）
     * @param columnDisplayName - 列显示名（与 testGetColumnKeyByName 的入参语义一致）
     * @param options.scrollIfNeeded - 默认 true：如果 row 不在视口内，先调
     *        scrollRowIntoViewById 让它进入视口再取 areas；false 时跳过
     *        scrollIntoView（保持当前 scroll 状态，直接用上一次绘制的 cell 几何）
     * @param options.exact - 透传给 findRowIdByName / getColumnKeyByName 的 exact 标志
     */
    testFindCellAreasByName(taskName: string, columnDisplayName: string, options?: {
        scrollIfNeeded?: boolean;
        exact?: boolean;
    }): {
        ok: boolean;
        reason?: 'row-not-found' | 'column-not-found' | 'row-not-visible' | 'cell-not-found';
        rowId?: string | number;
        columnKey?: string;
        areas?: ReturnType<DataGrid['testGetCellInteractiveAreas']>;
    };
    /**
     * Get all visible group rows.
     */
    testGetVisibleGroupRows(): Array<{
        rowId: string;
        rowIndex: number;
        expanded: boolean;
        groupName: string;
        groupLevel: number;
        isLeafGroup: boolean;
    }>;
    /**
     * 列出当前可见的所有 ⊕ AddTaskRow 行的 canvas 几何（task=be29ce06）。
     *
     * 仅返回 `body.rows` 中 `instanceof AddTaskRow` 的行——AddTaskRow 是数据驱动
     * 的特殊 canvas 行（`Body.ts` L905 `if (isAddTaskRow(rowData)) new AddTaskRow(...)`），
     * 不走 cell renderer 路径，所以 `testGetAllVisibleCells` 不会返回它的 cell。
     * 这里专门给 e2e 暴露 ⊕ 圆心的精确 canvas 坐标，规避 spec 端按
     * `originFixedWidth/2` 估算在分组下偏到行号区。
     *
     * 几何**严格复刻** `AddTaskRow.isInsideIconArea` 的 `bw + iconX + hitPadding=4`
     * 计算（一字不差）：保证"e2e 鼠标命中点 = AddTaskRow 运行时判定命中点"。
     *
     * 返回的 `iconArea` 已经包含 hitPadding：spec 用 `(canvasX + width/2,
     * canvasY + height/2)` → `canvasToViewport` → `page.mouse.click` 即可命中。
     *
     * 这是 LRN-20260512-001 约定的"长期稳定 e2e 锚点 API"，不要随手隐藏。
     */
    testGetAddTaskRowAreas(): Array<{
        rowId: string;
        rowIndex: number;
        groupId: string | number | null;
        parentGroupLevel: number | null;
        rowArea: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        };
        iconArea: {
            canvasX: number;
            canvasY: number;
            width: number;
            height: number;
        };
    }>;
    /**
     * Get current selection, editing, and checked-row state.
     */
    testGetSelectionState(): {
        range: {
            startRow: number;
            startCol: number;
            endRow: number;
            endCol: number;
        } | null;
        activeCell: {
            rowIndex: number;
            colIndex: number;
        } | null;
        checkedRowIds: (string | number)[];
        editor: {
            show: boolean;
            rowIndex: number;
            colIndex: number;
        } | null;
    };
}
export default DataGrid;
