UNPKG

vexip-ui

Version:

A Vue 3 UI library, Highly customizability, full TypeScript, performance pretty good

1 lines 91.9 kB
{"version":3,"file":"store.cjs","sources":["../../../components/table/store.ts"],"sourcesContent":["import { useCascadedChecked } from '@/components/tree/hooks'\n\nimport { computed, reactive, watchEffect } from 'vue'\n\nimport {\n boundRange,\n createBITree,\n debounceMinor,\n deepClone,\n getLast,\n isNull,\n mapTree,\n sortByProps,\n toFalse,\n toFixed,\n toNumber,\n walkTree,\n} from '@vexip-ui/utils'\nimport { DEFAULT_KEY_FIELD, TABLE_FOOT_PREFIX, TABLE_HEAD_PREFIX, columnTypes } from './symbol'\n\nimport type { ClassType, LocaleConfig, StyleType } from '@vexip-ui/config'\nimport type { TooltipTheme } from '@/components/tooltip'\nimport type {\n CellSpanResult,\n ColumnCellSpanFn,\n ColumnGroupWithKey,\n ColumnRawWithKey,\n ColumnWithKey,\n Data,\n ExpandRenderFn,\n Key,\n ParsedFilterOptions,\n ParsedTableSorterOptions,\n StoreOptions,\n StoreState,\n SummaryCellSpanFn,\n SummaryWithKey,\n TableCellPropFn,\n TableCellSpanFn,\n TableColResizeType,\n TableColumnOptions,\n TableColumnRawOptions,\n TableColumnType,\n TableDragColumn,\n TableExpandColumn,\n TableFilterOptions,\n TableFootPropFn,\n TableHeadPropFn,\n TableKeyConfig,\n TableRowPropFn,\n TableRowState,\n TableSelectionColumn,\n TableSorterOptions,\n TableSummaryData,\n TableSummaryOptions,\n} from './symbol'\n\nconst defaultSummaryData = Object.freeze<TableSummaryData>({\n sum: NaN,\n min: NaN,\n max: NaN,\n})\n\nlet indexId = 1\n\nfunction getIndexId() {\n return `__vxp-table-key-${indexId++}`\n}\n\nfunction defaultIndexLabel(index: number) {\n return index + 1\n}\n\nconst COLUMN_DEFAULT_WIDTH = 100\nconst COLUMN_DEFAULT_MIN_WIDTH = 10\n\nexport function useStore(options: StoreOptions) {\n const state = reactive({\n ...options,\n columns: [],\n normalColumns: [],\n allColumns: [],\n summaries: [],\n data: [],\n dataKey: options.dataKey ?? DEFAULT_KEY_FIELD,\n rowData: [],\n treeRowData: [],\n width: 0,\n rightFixedColumns: [],\n leftFixedColumns: [],\n aboveSummaries: [],\n belowSummaries: [],\n rowMinHeight: options.rowMinHeight || 36,\n rowDraggable: !!options.rowDraggable,\n columnMap: new Map(),\n rowMap: new Map(),\n summaryMap: new Map(),\n idMaps: new WeakMap(),\n checkedAll: false,\n partial: false,\n widths: new Map(),\n sorters: new Map(),\n filters: new Map(),\n resized: new Set(),\n bodyYScroll: 0,\n bodyXScroll: 0,\n padTop: 0,\n startRow: 0,\n endRow: 0,\n dragging: false,\n heightBITree: null!,\n virtualData: [],\n totalHeight: options.rowMinHeight * options.data.length,\n colResizing: false,\n resizeLeft: 0,\n cellSpanMap: new Map(),\n collapseMap: new Map(),\n sidePadding: options.sidePadding || [0, 0],\n locked: false,\n barScrolling: false,\n heightTrigger: 0,\n hoveredRowKey: null as Key | null,\n }) as StoreState\n\n setColumns(options.columns)\n setSummaries(options.summaries)\n setData(options.data)\n\n const userData = computed(() => {\n return typeof state.dataFilter === 'function'\n ? state.rowData.filter(row => state.dataFilter(row.data))\n : state.rowData\n })\n const filteredData = computed(() => {\n return state.customFilter\n ? userData.value\n : filterData(state.filters, userData.value, state.singleFilter)\n })\n const sortedData = computed(() => {\n const data = state.customSorter\n ? filteredData.value\n : sortData(state.sorters, filteredData.value, state.columns, state.singleSorter)\n\n return data\n })\n const processedData = computed(() => {\n const data = pageData(state.currentPage, state.pageSize, sortedData.value)\n\n for (let i = 0, len = data.length; i < len; ++i) {\n data[i].listIndex = i\n data[i].last = i === len - 1\n }\n\n return data\n })\n const visibleKeys = computed(() => new Set(processedData.value.map(row => row.key)))\n const disableCheckRows = computed(() => {\n const rowData = processedData.value\n const selectionColumn = state.columns.find(\n item => (item as TableSelectionColumn).type === 'selection',\n ) as TableSelectionColumn | undefined\n const disableCheckRows = new Set<Key>()\n\n if (selectionColumn && typeof selectionColumn.disableRow === 'function') {\n const isDisabled = selectionColumn.disableRow\n\n for (let i = 0, len = rowData.length; i < len; ++i) {\n const row = rowData[i]\n\n if (isDisabled(row.data)) {\n disableCheckRows.add(row.key)\n }\n }\n }\n\n return disableCheckRows\n })\n const disableExpandRows = computed(() => {\n const rowData = processedData.value\n const expandColumn = state.columns.find(\n item => (item as TableExpandColumn).type === 'expand',\n ) as TableExpandColumn | undefined\n const disableExpandRows = new Set<Key>()\n\n if (expandColumn && typeof expandColumn.disableRow === 'function') {\n const isDisabled = expandColumn.disableRow\n\n for (let i = 0, len = rowData.length; i < len; ++i) {\n const row = rowData[i]\n\n if (isDisabled(row.data)) {\n disableExpandRows.add(row.key)\n }\n }\n }\n\n return disableExpandRows\n })\n const disableDragRows = computed(() => {\n const rowData = processedData.value\n const dragColumn = state.columns.find(item => (item as TableDragColumn).type === 'drag') as\n | TableDragColumn\n | undefined\n const disableDragRows = new Set<Key>()\n\n if (dragColumn && typeof dragColumn.disableRow === 'function') {\n const isDisabled = dragColumn.disableRow\n\n for (let i = 0, len = rowData.length; i < len; ++i) {\n const row = rowData[i]\n\n if (isDisabled(row.data)) {\n disableDragRows.add(row.key)\n }\n }\n }\n\n return disableDragRows\n })\n const usingTree = computed(() => {\n return !state.disabledTree && state.rowData.some(row => row.children?.length)\n })\n const hasDragColumn = computed(() => {\n return !!state.columns.find(column => 'type' in column && column.type === 'drag')\n })\n const rowDragging = computed(() => !!processedData.value.find(row => row.dragging))\n const totalWidths = computed(() => getColumnsWidths())\n const normalWidths = computed(() => getColumnsWidths(state.normalColumns))\n const leftFixedWidths = computed(() => getColumnsWidths(state.leftFixedColumns))\n const rightFixedWidths = computed(() => getColumnsWidths(state.rightFixedColumns))\n const expandColumn = computed(() => {\n return state.columns.find(column => (column as TableExpandColumn).type === 'expand') as\n | TableExpandColumn\n | undefined\n })\n const summaryData = computed(() => {\n const { columns, summaries, data } = state\n const map = new Map<Key, TableSummaryData>()\n\n if (!summaries.length) return map\n\n for (const column of columns) {\n const key = column.key\n\n if (column.type || column.noSummary) {\n map.set(key, defaultSummaryData)\n continue\n }\n\n const accessor =\n typeof column.accessor === 'function' ? column.accessor : (data: Data) => data[key]\n\n let sum = 0\n let min = Infinity\n let max = -Infinity\n let valid = false\n\n for (let i = 0, len = data.length; i < len; ++i) {\n const value = accessor(data[i], i)\n const number = parseFloat(value as string)\n\n if (Number.isNaN(number)) continue\n\n sum += number\n min = Math.min(min, number)\n max = Math.max(max, number)\n valid = true\n }\n\n valid ? map.set(key, { sum, min, max }) : map.set(key, defaultSummaryData)\n }\n\n return map\n })\n const topFixedHeights = computed(() => getSummariesHeights(state.aboveSummaries))\n const bottomFixedHeights = computed(() => getSummariesHeights())\n const indentedColumn = computed(() => {\n return state.columns.find(column => !column.type && column.indented)\n })\n const hasFixedColumn = computed(() => {\n return !!(state.leftFixedColumns.length || state.rightFixedColumns.length)\n })\n\n const getters = reactive({\n filteredData,\n sortedData,\n processedData,\n visibleKeys,\n disableCheckRows,\n disableExpandRows,\n disableDragRows,\n usingTree,\n hasDragColumn,\n rowDragging,\n totalWidths,\n normalWidths,\n leftFixedWidths,\n rightFixedWidths,\n expandColumn,\n summaryData,\n topFixedHeights,\n bottomFixedHeights,\n indentedColumn,\n hasFixedColumn,\n })\n\n const mutations = {\n // 这几个个方法被 deep watch 回调\n // 需要防止在一个微任务内被多次调用\n setColumns: debounceMinor(setColumns),\n // setColumns,\n setSummaries: debounceMinor(setSummaries),\n setData: debounceMinor(setData),\n\n // 这个方法被大量的 watch 回调,需要防抖\n updateTotalHeight: debounceMinor(updateTotalHeight),\n\n isGroupColumn,\n buildSummaryKey,\n setColumnProp,\n setSummaryProp,\n setDataKey,\n setCurrentPage,\n setPageSize,\n setRowClass,\n setRowStyle,\n setRowAttrs,\n setCellClass,\n setCellStyle,\n setCellAttrs,\n setHeadClass,\n setHeadStyle,\n setHeadAttrs,\n setFootClass,\n setFootStyle,\n setFootAttrs,\n setTableWidth,\n setRowHeight,\n setRowMinHeight,\n setCellHeight,\n setVirtual,\n setRowDraggable,\n setBodyYScroll,\n setBodyXScroll,\n setBorder,\n setStripe,\n setHighlight,\n setRowProp,\n setLocale,\n setTooltipTheme,\n setTooltipWidth,\n setSingleSorter,\n setSingleFilter,\n setDragging,\n setKeyConfig,\n setDisabledTree,\n setNoCascaded,\n setColResizable,\n setCustomSorter,\n setCustomFilter,\n setColumnResizing,\n setResizeLeft,\n setExpandRenderer,\n setCellSpan,\n setSidePadding,\n setBorderWidth,\n setDataFilter,\n setEllipsis,\n setLocked,\n setBarScrolling,\n setHoveredRowKey,\n\n handleSort,\n clearSort,\n handleFilter,\n clearFilter,\n toggleFilterItemActive,\n refreshRowIndex,\n handleCheck,\n handleCheckAll,\n clearCheckAll,\n setRenderRows,\n handleExpand,\n handleDrag,\n collectUnderRows,\n setTreeExpanded,\n getParentRow,\n handleColumnResize,\n getCurrentData,\n createMinRowState,\n flatTreeRows,\n refreshRowDepth,\n triggerHeightChange,\n queryRow,\n }\n\n watchEffect(() => {\n state.heightBITree = createBITree(\n processedData.value.length,\n state.rowHeight || state.rowMinHeight,\n )\n\n state.totalHeight = -1\n updateTotalHeight()\n })\n watchEffect(computeCellSpan)\n\n function triggerHeightChange() {\n ++state.heightTrigger\n\n if (state.heightTrigger >= Number.MAX_SAFE_INTEGER) {\n state.heightTrigger = 0\n }\n }\n\n function getColumnsWidths(columns = state.columns) {\n const widths = state.widths\n const combinedWidths: number[] = [0]\n\n let width = 0\n\n for (let i = 0, len = columns.length; i < len; ++i) {\n const column = columns[i]\n const key = column.key\n const columnWidth = widths.get(key) || 0\n\n width += columnWidth\n combinedWidths.push(width)\n }\n\n return combinedWidths\n }\n\n function getSummariesHeights(summaries = state.belowSummaries) {\n const rowMap = state.rowMap\n const heights: number[] = [0]\n\n let height = 0\n\n for (let i = 0, len = summaries.length; i < len; ++i) {\n const summary = summaries[i]\n const key = buildSummaryKey(summary.key)\n const row = rowMap.get(key)\n\n if (row) {\n height += row.height || 0\n }\n\n heights.push(height)\n }\n\n return heights\n }\n\n function createMinRowState(key: Key) {\n return { key, cellHeights: {}, height: state.rowHeight ?? state.rowMinHeight } as TableRowState\n }\n\n function isGroupColumn(column: any): column is ColumnGroupWithKey {\n return !!column.children?.length\n }\n\n function buildColumns(columns: TableColumnRawOptions[]) {\n const allColumns: ColumnRawWithKey[][] = []\n const baseColumns: ColumnWithKey[] = []\n const columnMap = new Map<Key, ColumnRawWithKey>()\n const existedTypes = new Set<TableColumnType>()\n\n const getFixedOrder = (fixed?: boolean | 'left' | 'right') => {\n return fixed === true || fixed === 'left' ? -1 : fixed === 'right' ? 1 : 0\n }\n const build = (\n _columns: TableColumnRawOptions[],\n fixed?: boolean | 'left' | 'right',\n row = 0,\n result: ColumnRawWithKey[][] = [],\n ) => {\n _columns = _columns\n .filter(column => !('children' in column) || isGroupColumn(column))\n .sort((prev, next) => (prev.order || 0) - (next.order || 0))\n .sort((prev, next) => getFixedOrder(prev.fixed) - getFixedOrder(next.fixed))\n fixed = fixed === true ? 'left' : fixed\n\n const columns = _columns as ColumnRawWithKey[]\n const rowColumns = result[row] ?? (result[row] = [])\n\n let index = row > 0 ? result[row - 1].length - 1 : 0\n\n for (const { ...column } of columns) {\n if (!isNull(fixed)) {\n column.fixed = fixed\n }\n\n rowColumns[index] = column\n\n if (isGroupColumn(column)) {\n const endIndex = build(column.children, column.fixed, row + 1, result)\n\n column.key = Symbol('TableColumnGroup')\n column.headSpan = endIndex - index\n index = endIndex\n } else {\n const validType = column.type && columnTypes.includes(column.type)\n\n if (validType) {\n if (existedTypes.has(column.type)) {\n console.warn(`[vexip-ui:Table] Table has duplicate column with type '${column.type}'`)\n }\n\n existedTypes.add(column.type)\n }\n\n let key = column.key\n\n if (isNull(key)) {\n if (validType) {\n key = `__vxp_${column.type}`\n } else {\n console.warn('[vexip-ui:Table] Table column requires key prop, but missing')\n\n key = getIndexId()\n }\n }\n\n column.key = key\n baseColumns.push(column)\n index += 1\n }\n\n columnMap.set(column.key, column)\n }\n\n return index\n }\n\n build(columns, undefined, 0, allColumns)\n\n let length = 0\n\n for (const rowColumns of allColumns) {\n length = Math.max(rowColumns.length, length)\n }\n\n for (const rowColumns of allColumns) {\n if (rowColumns.length) {\n getLast(rowColumns)!.last = true\n }\n\n rowColumns.length = length\n }\n\n for (let i = 0, rowCount = allColumns.length; i < length; ++i) {\n let span = 1\n\n for (let j = rowCount - 1; j >= 0; --j) {\n const column = allColumns[j][i]\n\n if (column) {\n column.colIndex = i\n column.rowSpan = span\n span = 1\n } else {\n ++span\n }\n }\n }\n\n return { allColumns, baseColumns, columnMap }\n }\n\n function setColumns(columns: TableColumnRawOptions[]) {\n const { widths, sorters, filters } = state\n const { allColumns, baseColumns, columnMap } = buildColumns(columns)\n\n const normalColumns: ColumnWithKey[] = []\n const rightFixedColumns: ColumnWithKey[] = []\n const leftFixedColumns: ColumnWithKey[] = []\n\n for (let i = 0, len = baseColumns.length; i < len; ++i) {\n const column = baseColumns[i]\n\n column.first = false\n column.last = false\n column.index = i\n\n if (column.type && columnTypes.includes(column.type)) {\n switch (column.type) {\n case 'order': {\n column.truthIndex = !!column.truthIndex\n\n if (typeof column.orderLabel !== 'function') {\n column.orderLabel = defaultIndexLabel\n }\n\n if (isNull(column.width)) {\n column.width = 60\n column.minWidth = 60\n }\n\n break\n }\n case 'selection': {\n column.selectionSize = column.selectionSize || 'default'\n\n if (typeof column.disableRow !== 'function') {\n column.disableRow = toFalse\n }\n\n if (isNull(column.width)) {\n column.width = 40\n column.minWidth = 40\n }\n\n break\n }\n case 'expand': {\n if (typeof column.disableRow !== 'function') {\n column.disableRow = toFalse\n }\n\n if (isNull(column.width)) {\n column.width = 40\n column.minWidth = 40\n }\n\n break\n }\n case 'drag': {\n if (typeof column.disableRow !== 'function') {\n column.disableRow = toFalse\n }\n\n if (isNull(column.width)) {\n column.width = 40\n column.minWidth = 40\n }\n\n break\n }\n }\n\n if (!column.key) {\n column.key = `__vxp_${column.type}-${i}`\n }\n } else {\n column.type = undefined\n }\n\n // 独立属性解析时注意隔断同对象引用\n widths.set(\n column.key,\n typeof column.width === 'string'\n ? COLUMN_DEFAULT_MIN_WIDTH\n : Math.round(\n boundRange(\n column.width || COLUMN_DEFAULT_WIDTH,\n column.minWidth || COLUMN_DEFAULT_MIN_WIDTH,\n column.maxWidth || Infinity,\n ),\n ),\n )\n sorters.set(column.key, parseSorter(column.sorter))\n filters.set(column.key, parseFilter(column.filter))\n\n const fixed = column.fixed\n\n if (fixed === true || fixed === 'left') {\n leftFixedColumns.push(column)\n } else if (fixed === 'right') {\n rightFixedColumns.push(column)\n } else {\n normalColumns.push(column)\n }\n }\n\n if (state.allColumns.length > allColumns.length) {\n for (let i = allColumns.length - 1, len = state.allColumns.length; i < len; ++i) {\n state.rowMap.delete(`${TABLE_HEAD_PREFIX}${i}`)\n }\n }\n\n for (let i = 0, len = allColumns.length; i < len; ++i) {\n const rowKey = `${TABLE_HEAD_PREFIX}${i}`\n\n state.rowMap.set(rowKey, createMinRowState(rowKey))\n }\n\n state.columnMap = columnMap\n state.columns = Array.from(leftFixedColumns).concat(normalColumns, rightFixedColumns)\n state.normalColumns = normalColumns\n state.allColumns = allColumns\n\n if (state.columns.length) {\n for (const column of state.columns) {\n if (!column.type) {\n column.first = true\n break\n }\n }\n\n getLast(state.columns)!.last = true\n }\n\n if (leftFixedColumns.length) {\n state.leftFixedColumns = leftFixedColumns\n }\n\n if (rightFixedColumns.length) {\n state.rightFixedColumns = rightFixedColumns\n }\n }\n\n function setColumnProp(key: Key, prop: string, value: any) {\n if (state.columnMap.has(key)) {\n ;(state.columnMap.get(key) as any)[prop] = value\n }\n }\n\n function buildSummaryKey(key: Key) {\n return typeof key === 'symbol' ? key : `${TABLE_FOOT_PREFIX}${key}`\n }\n\n function setSummaries(summaries: TableSummaryOptions[]) {\n summaries = Array.from(summaries).sort((prev, next) => {\n return (prev.order || 0) - (next.order || 0)\n })\n\n const prevKeys = new Set(state.summaries.map(summary => summary.key))\n const aboveSummaries: SummaryWithKey[] = []\n const belowSummaries: SummaryWithKey[] = []\n const summaryMap = new Map<Key, SummaryWithKey>()\n\n for (let i = 0, len = summaries.length; i < len; ++i) {\n const summary = { ...summaries[i] } as SummaryWithKey\n\n let key = summary.key\n\n if (isNull(key)) {\n console.error('[vexip-ui:Table] Table summary requires key prop, but missing')\n\n key = getIndexId()\n }\n\n summary.key = key\n ;(summary.above ? aboveSummaries : belowSummaries).push(summary)\n\n if (!prevKeys.has(summary.key)) {\n const rowKey = buildSummaryKey(summary.key)\n\n state.rowMap.set(rowKey, createMinRowState(rowKey))\n }\n\n prevKeys.delete(summary.key)\n summaryMap.set(summary.key, summary)\n }\n\n state.summaries = Array.from(aboveSummaries).concat(belowSummaries)\n state.summaryMap = summaryMap\n\n if (aboveSummaries.length) {\n state.aboveSummaries = aboveSummaries\n }\n\n if (belowSummaries.length) {\n state.belowSummaries = belowSummaries\n }\n\n if (prevKeys.size) {\n for (const key of prevKeys) {\n state.rowMap.delete(buildSummaryKey(key))\n }\n }\n }\n\n function setSummaryProp(key: Key, prop: string, value: any) {\n if (state.summaryMap.has(key)) {\n ;(state.summaryMap.get(key) as any)[prop] = value\n }\n }\n\n function setDataKey(field: string) {\n const oldDataKey = state.dataKey\n\n if (!isNull(field) && oldDataKey !== field) {\n const { rowData, idMaps } = state\n\n state.dataKey = field\n\n rowData.forEach(row => {\n let key = row.data[field] as Key\n\n if (isNull(key)) {\n key = getIndexId()\n }\n\n row.key = key\n idMaps.set(row.data, key)\n })\n }\n }\n\n function collectUnderRows(row: TableRowState, result: TableRowState[] = []) {\n if (row.treeExpanded && row.children?.length) {\n for (const childRow of row.children) {\n result.push(childRow)\n collectUnderRows(childRow, result)\n }\n }\n\n return result\n }\n\n function setData(data: Data[]) {\n const clonedData: TableRowState[] = []\n const rowMap = new Map<Key, TableRowState>()\n const { allColumns, dataKey, keyConfig, idMaps, disabledTree } = state\n const oldDataMap = state.rowMap\n const hidden = !!state.virtual\n\n const {\n children: childrenKey,\n checked: checkedKey,\n height: heightKey,\n expanded: expandedKey,\n treeExpanded: treeExpandedKey,\n } = keyConfig\n\n for (let i = 0, len = allColumns.length; i < len; ++i) {\n const key = `${TABLE_HEAD_PREFIX}${i}`\n\n rowMap.set(key, oldDataMap.get(key) || createMinRowState(key))\n }\n\n for (const summary of state.summaries) {\n const key = buildSummaryKey(summary.key)\n\n rowMap.set(key, oldDataMap.get(key) || createMinRowState(key))\n }\n\n const parseRow = (origin: Data[], result: TableRowState[], parent?: TableRowState) => {\n for (let i = 0, len = origin.length; i < len; ++i) {\n const item = origin[i]\n\n let key = item[dataKey] as Key\n\n if (isNull(key)) {\n key = idMaps.get(item)!\n\n if (isNull(key)) {\n key = getIndexId()\n }\n }\n\n let row: TableRowState\n\n if (oldDataMap.has(key)) {\n row = oldDataMap.get(key)!\n\n const {\n [checkedKey]: checked,\n [heightKey]: height,\n [expandedKey]: expanded,\n [treeExpandedKey]: treeExpanded,\n } = row.data !== item ? Object.assign(row.data, item) : row.data\n\n row.checked = !isNull(checked) ? !!checked : row.checked\n row.height = !isNull(height) ? toNumber(height) : row.height\n row.expanded = !isNull(expanded) ? !!expanded : row.expanded\n row.treeExpanded = !isNull(treeExpanded) ? !!treeExpanded : row.treeExpanded\n } else {\n const {\n [checkedKey]: checked,\n [heightKey]: height,\n [expandedKey]: expanded,\n [treeExpandedKey]: treeExpanded,\n } = item\n\n row = {\n key,\n hidden,\n checked: !!checked,\n height: toNumber(height),\n expanded: !!expanded,\n hover: false,\n expandHeight: 0,\n index: -1,\n children: [],\n depth: 0,\n treeExpanded: !!treeExpanded,\n partial: false,\n dragging: false,\n listIndex: 0,\n cellHeights: reactive({}),\n last: false,\n expandAnimate: false,\n data: item,\n }\n\n idMaps.set(item, key)\n }\n\n if (parent) {\n row.parent = parent.key\n row.depth = parent.depth + 1\n }\n\n row.children = []\n\n const children = row.data[childrenKey]\n children?.length && parseRow(children, row.children, row)\n\n result.push(row)\n rowMap.set(key, row)\n }\n }\n\n parseRow(data, clonedData)\n\n state.rowMap = rowMap\n state.treeRowData = clonedData\n\n if (!disabledTree) {\n flatTreeRows()\n } else {\n state.rowData = clonedData\n }\n\n state.data = data\n\n refreshRowIndex()\n computePartial()\n }\n\n function flatTreeRows() {\n if (state.disabledTree) return\n\n const rowData: TableRowState[] = []\n\n for (const row of state.treeRowData) {\n rowData.push(row)\n collectUnderRows(row, rowData)\n }\n\n state.rowData = rowData\n }\n\n function refreshRowDepth() {\n walkTree(state.treeRowData, (row, depth) => {\n row.depth = depth\n })\n }\n\n function setCurrentPage(currentPage: number) {\n state.currentPage = currentPage ?? 1\n }\n\n function setPageSize(pageSize: number) {\n state.pageSize = pageSize || 0\n }\n\n function setRowClass(rowClass: ClassType | TableRowPropFn<ClassType>) {\n state.rowClass = rowClass ?? ''\n }\n\n function setRowStyle(rowStyle: StyleType | TableRowPropFn<StyleType>) {\n state.rowStyle = rowStyle ?? ''\n }\n\n function setRowAttrs(rowAttrs: Record<string, any> | TableRowPropFn<Record<string, any>>) {\n state.rowAttrs = rowAttrs ?? null!\n }\n\n function setCellClass(cellClass: ClassType | TableCellPropFn<ClassType>) {\n state.cellClass = cellClass ?? ''\n }\n\n function setCellStyle(cellStyle: StyleType | TableCellPropFn<StyleType>) {\n state.cellStyle = cellStyle ?? ''\n }\n\n function setCellAttrs(cellAttrs: Record<string, any> | TableCellPropFn<Record<string, any>>) {\n state.cellAttrs = cellAttrs ?? null!\n }\n\n function setHeadClass(headClass: ClassType | TableHeadPropFn<ClassType>) {\n state.headClass = headClass ?? ''\n }\n\n function setHeadStyle(headStyle: StyleType | TableHeadPropFn<StyleType>) {\n state.headStyle = headStyle ?? ''\n }\n\n function setHeadAttrs(headAttrs: Record<string, any> | TableHeadPropFn<Record<string, any>>) {\n state.headAttrs = headAttrs ?? null!\n }\n\n function setFootClass(footClass: ClassType | TableFootPropFn<ClassType>) {\n state.footClass = footClass ?? ''\n }\n\n function setFootStyle(footStyle: StyleType | TableFootPropFn<StyleType>) {\n state.footStyle = footStyle ?? ''\n }\n\n function setFootAttrs(footAttrs: Record<string, any> | TableFootPropFn<Record<string, any>>) {\n state.footAttrs = footAttrs ?? null!\n }\n\n function setTableWidth(width: number) {\n width = toNumber(width)\n\n const { columns, widths, resized } = state\n\n const hasWidthColumns: ColumnWithKey[] = []\n const flexColumns: ColumnWithKey[] = []\n\n let flexWidth = width\n\n for (let i = 0, len = columns.length; i < len; ++i) {\n const column = columns[i]\n const { minWidth, maxWidth } = column\n\n if (resized.has(column.key)) {\n flexWidth -= widths.get(column.key)!\n hasWidthColumns.push(column)\n } else if (column.width) {\n if (typeof column.width === 'string') {\n const percent = boundRange(toNumber(column.width), 0, 100)\n\n if (percent) {\n const fixedWidth = Math.round(\n boundRange(\n (width * percent) / 100,\n minWidth || COLUMN_DEFAULT_MIN_WIDTH,\n maxWidth || Infinity,\n ),\n )\n\n flexWidth -= fixedWidth\n widths.set(column.key, fixedWidth)\n hasWidthColumns.push(column)\n } else {\n flexColumns.push(column)\n }\n } else {\n const width = Math.round(\n boundRange(\n column.width || COLUMN_DEFAULT_WIDTH,\n minWidth || COLUMN_DEFAULT_MIN_WIDTH,\n maxWidth || Infinity,\n ),\n )\n\n flexWidth -= width\n widths.set(column.key, width)\n hasWidthColumns.push(column)\n }\n } else {\n flexColumns.push(column)\n }\n }\n\n const flexColumnCount = flexColumns.length\n const flexWidths = distributeWidths(flexColumns, flexWidth)\n\n let usedWidth = 0\n\n for (let i = 0; i < flexColumnCount; ++i) {\n const column = flexColumns[i]\n const width = Math[i % 2 ? 'ceil' : 'floor'](flexWidths[i])\n\n if (i < flexColumnCount - 1) {\n usedWidth += width\n }\n\n widths.set(column.key, width)\n }\n\n if (flexColumnCount && flexWidth >= usedWidth + getLast(flexWidths)!) {\n widths.set(getLast(flexColumns)!.key, flexWidth - usedWidth)\n }\n\n state.width = width\n }\n\n function distributeWidths(columns: ColumnWithKey[], totalWidth: number): number[] {\n const count = columns.length\n const baseWidth = Math.max(totalWidth / count, COLUMN_DEFAULT_WIDTH)\n\n const widths = columns.map(col => {\n let w = baseWidth\n if (col.minWidth != null) w = Math.max(w, col.minWidth)\n if (col.maxWidth != null) w = Math.min(w, col.maxWidth)\n return w\n })\n\n const currentTotal = widths.reduce((a, b) => a + b, 0)\n let delta = totalWidth - currentTotal\n\n const canGrow = (i: number) => columns[i].maxWidth == null || widths[i] < columns[i].maxWidth!\n const canShrink = (i: number) => columns[i].minWidth == null || widths[i] > columns[i].minWidth!\n\n const epsilon = 0.1\n let adjusted = false\n\n while (Math.abs(delta) > epsilon) {\n const adjustableIndices = widths\n .map((_, i) => {\n if (delta > 0 && canGrow(i)) return i\n if (delta < 0 && canShrink(i)) return i\n return -1\n })\n .filter(i => i !== -1)\n\n if (adjustableIndices.length === 0) {\n adjusted = false\n break\n }\n\n const adjustment = delta / adjustableIndices.length\n for (const i of adjustableIndices) {\n const old = widths[i]\n let next = old + adjustment\n\n if (columns[i].minWidth != undefined) next = Math.max(next, columns[i].minWidth!)\n if (columns[i].maxWidth != undefined) next = Math.min(next, columns[i].maxWidth!)\n\n delta -= next - old\n widths[i] = next\n }\n\n adjusted = true\n }\n\n // delta > 0 且无法再调整时,强行补给最后一列\n if (!adjusted && delta > epsilon) {\n widths[count - 1] += delta\n }\n\n return widths\n }\n\n function setRowHeight(height: number) {\n state.rowHeight = height\n }\n\n function setRowMinHeight(height: number) {\n state.rowMinHeight = height\n }\n\n function setCellHeight(rowKey: Key, columnKey: Key, height: number) {\n if (!isNull(height) && state.rowMap.has(rowKey)) {\n state.rowMap.get(rowKey)!.cellHeights[columnKey] = height\n }\n }\n\n function setRowDraggable(draggable: boolean) {\n state.rowDraggable = !!draggable\n }\n\n function setBodyYScroll(scroll: number) {\n state.bodyYScroll = scroll\n }\n\n function setBodyXScroll(scroll: number) {\n state.bodyXScroll = scroll\n }\n\n function setBorder(able: boolean) {\n state.border = !!able\n }\n\n function setStripe(able: boolean) {\n state.stripe = !!able\n }\n\n function setHighlight(able: boolean) {\n state.highlight = !!able\n }\n\n function setVirtual(virtual: boolean) {\n state.virtual = !!virtual\n }\n\n function setRowProp(key: Key, prop: Exclude<keyof TableRowState, 'key'>, value: any) {\n const row = state.rowMap.get(key)\n\n if (row && row[prop] !== value) {\n ;(row as any)[prop] = value\n }\n }\n\n function setLocale(locale: LocaleConfig['table']) {\n state.locale = locale\n }\n\n function setTooltipTheme(theme: TooltipTheme) {\n state.tooltipTheme = theme\n }\n\n function setTooltipWidth(theme: number | string) {\n state.tooltipWidth = theme\n }\n\n function setSingleSorter(able: boolean) {\n state.singleSorter = !!able\n }\n\n function setSingleFilter(able: boolean) {\n state.singleFilter = !!able\n }\n\n function setDragging(dragging: boolean) {\n state.dragging = !!dragging\n }\n\n function setKeyConfig(keyConfig: Required<TableKeyConfig>) {\n state.keyConfig = keyConfig\n }\n\n function setDisabledTree(disabled: boolean) {\n state.disabledTree = !!disabled\n }\n\n function setNoCascaded(noCascaded: boolean) {\n state.noCascaded = !!noCascaded\n }\n\n function setColResizable(resizable: boolean | TableColResizeType) {\n state.colResizable = resizable === true ? 'lazy' : resizable\n }\n\n function setCustomSorter(able: boolean) {\n state.customSorter = !!able\n }\n\n function setCustomFilter(able: boolean) {\n state.customFilter = !!able\n }\n\n function setColumnResizing(resizing: boolean) {\n state.colResizing = !!resizing\n }\n\n function setResizeLeft(left: number) {\n state.resizeLeft = left\n }\n\n function setExpandRenderer(renderer: ExpandRenderFn | null) {\n state.expandRenderer = renderer\n }\n\n function setCellSpan(spanFn: TableCellSpanFn | null) {\n state.cellSpan = spanFn\n }\n\n function setSidePadding(padding: number | number[]) {\n state.sidePadding = Array.isArray(padding) ? padding : [padding, padding]\n }\n\n function setBorderWidth(width: number) {\n state.borderWidth = Math.max(width, 0)\n }\n\n function setDataFilter(filter: (data: Data) => boolean) {\n state.dataFilter = filter\n }\n\n function setEllipsis(ellipsis: boolean) {\n state.ellipsis = ellipsis\n }\n\n function setLocked(locked: boolean) {\n state.locked = locked\n }\n\n function setBarScrolling(scrolling: boolean) {\n state.barScrolling = scrolling\n }\n\n function setHoveredRowKey(key: Key | null) {\n if (state.hoveredRowKey !== key) {\n const prevHoveredRow = state.hoveredRowKey && state.rowMap.get(state.hoveredRowKey)\n const newHoveredRow = key && state.rowMap.get(key)\n\n if (prevHoveredRow) {\n prevHoveredRow.hover = false\n }\n\n if (newHoveredRow) {\n newHoveredRow.hover = true\n }\n }\n\n state.hoveredRowKey = key\n }\n\n function handleSort(key: Key, type: ParsedTableSorterOptions['type']) {\n if (state.sorters.has(key)) {\n if (state.singleSorter && type) {\n clearSort()\n }\n\n state.sorters.get(key)!.type = type\n }\n }\n\n function clearSort() {\n const sorters = state.sorters\n\n for (const sorter of sorters.values()) {\n sorter.type = null\n }\n }\n\n function handleFilter(key: Key, active: ParsedFilterOptions['active']) {\n if (state.filters.has(key)) {\n if (state.singleFilter && (Array.isArray(active) ? active.length : active)) {\n clearFilter()\n }\n\n state.filters.get(key)!.active = Array.isArray(active) ? Array.from(active) : active\n }\n }\n\n function clearFilter() {\n const filters = state.filters\n\n for (const filter of filters.values()) {\n filter.active = null\n\n for (const option of filter.options) {\n option.active = false\n }\n }\n }\n\n const { updateCheckedUpward, updateCheckedDown } = useCascadedChecked({\n getNode: key => state.rowMap.get(key),\n disableNode: row => disableCheckRows.value.has(row.key),\n })\n\n function computeChecked(key: Key) {\n const { rowMap, rowData } = state\n const { disableCheckRows } = getters\n\n if (!rowMap.has(key)) return\n\n const rowList = [rowMap.get(key)!].concat(\n // 需要包含被禁用且被勾选的节点\n rowData.filter(row => disableCheckRows.has(row.key) && row.checked),\n )\n\n for (let i = 0, len = rowList.length; i < len; ++i) {\n updateCheckedUpward(rowList[i].key)\n updateCheckedDown(rowList[i].key)\n }\n }\n\n function handleCheck(key: Key, checked: boolean, single = false) {\n const { rowMap, noCascaded } = state\n const { disableCheckRows } = getters\n const row = rowMap.get(key)\n\n if (!row) return\n\n if (single) {\n clearCheckAll(true)\n row.checked = !!checked\n }\n\n if (!disableCheckRows.has(key)) {\n row.checked = !!checked\n row.partial = false\n }\n\n !noCascaded && computeChecked(key)\n computePartial()\n }\n\n function handleCheckAll() {\n const { rowData, checkedAll } = state\n const { disableCheckRows } = getters\n\n let checked = !checkedAll\n\n // 阻断 disabled 元素对全选的影响\n if (disableCheckRows.size) {\n // 由于被禁用的元素不可被操作,如果存在被禁用的元素且该状态为未被选中,则全选时仍然是 partial 状态\n // 假设除了禁用的元素,其余元素均为选中状态(此时对于用户来说属于已经全选,点击的期望是取消全选)\n let partialCheckedAll = true\n\n for (const row of rowData) {\n // 检查是否存在非禁用的且未被选中的元素(如有则证明现在不是全选,用户点击的期望是进行全选)\n if (!disableCheckRows.has(row.key) && !row.checked) {\n partialCheckedAll = false\n\n break\n }\n }\n\n checked = !partialCheckedAll\n }\n\n for (const row of rowData) {\n if (!disableCheckRows.has(row.key)) {\n row.checked = checked\n }\n }\n\n state.checkedAll = checked\n state.partial = false\n\n computePartial()\n }\n\n function clearCheckAll(includeDisabled = false) {\n const { rowData } = state\n const { disableCheckRows } = getters\n\n for (const row of rowData) {\n if (includeDisabled || !disableCheckRows.has(row.key)) {\n row.checked = false\n }\n\n if (includeDisabled) {\n row.partial = false\n }\n }\n\n state.checkedAll = false\n state.partial = false\n\n !includeDisabled && computePartial()\n }\n\n function computePartial() {\n const data = state.rowData\n\n let hasChecked = false\n let hasNotChecked = false\n let partial = false\n\n for (let i = 0, len = data.length; i < len; ++i) {\n const row = data[i]\n\n if (row.checked) {\n hasChecked = true\n } else {\n hasNotChecked = true\n }\n\n if (hasChecked && hasNotChecked) {\n partial = true\n\n break\n }\n }\n\n if (hasChecked && !partial) {\n state.checkedAll = true\n } else {\n state.checkedAll = false\n }\n\n state.partial = partial\n }\n\n function setRenderRows(start: number, end: number, force = false) {\n const { startRow, endRow, heightBITree, virtualData } = state\n\n if (!force && start === startRow && end === endRow) return\n\n const { processedData } = getters\n\n if (!processedData.length) {\n virtualData.length = 0\n return\n }\n\n const prevData = new Set([...virtualData])\n const added: TableRowState[] = []\n const removed: TableRowState[] = []\n\n for (let i = 0, len = processedData.length; i < len; ++i) {\n const data = processedData[i]\n\n data.hidden = !(i >= start && i < end)\n\n if (data.hidden) {\n data.hover = false\n\n if (prevData.has(data)) {\n removed.push(data)\n }\n } else if (!prevData.has(data)) {\n added.push(data)\n }\n\n prevData.delete(data)\n }\n\n removed.push(...prevData)\n\n const length = Math.min(added.length, removed.length)\n\n for (let i = 0; i < length; ++i) {\n virtualData[virtualData.indexOf(removed[i])] = added[i]\n }\n\n if (added.length > removed.length) {\n virtualData.push(...added.slice(length))\n } else if (added.length < removed.length) {\n state.virtualData = virtualData.filter(data => !removed.includes(data))\n }\n\n state.padTop = heightBITree?.sum(start) ?? 0\n state.startRow = start\n state.endRow = end\n }\n\n function handleExpand(key: Key, expanded: boolean) {\n const { rowMap } = state\n const { disableExpandRows } = getters\n\n if (rowMap.has(key) && !disableExpandRows.has(key)) {\n rowMap.get(key)!.expanded = !!expanded\n }\n }\n\n function handleDrag(key: Key, dragging: boolean) {\n const { rowMap } = state\n const { disableDragRows } = getters\n\n if (rowMap.has(key) && !disableDragRows.has(key)) {\n rowMap.get(key)!.dragging = !!dragging\n }\n }\n\n function setTreeExpanded(key: Key, expanded: boolean) {\n if (!usingTree.value) return\n\n const { rowMap, rowData, virtual } = state\n const row = rowMap.get(key)\n\n if (!row?.children?.length) return\n\n const underRows = collectUnderRows({ ...row, treeExpanded: true })\n\n if (expanded) {\n rowData.splice(row.index + 1, 0, ...underRows)\n } else {\n rowData.splice(row.index + 1, underRows.length)\n }\n\n row.treeExpanded = !!expanded\n\n refreshRowIndex()\n virtual && setRenderRows(state.startRow, state.endRow, true)\n }\n\n function toggleFilterItemActive(options: {\n key: Key,\n value: number | string | null,\n active?: boolean,\n disableOthers?: boolean,\n }) {\n const { key, value, active = false, disableOthers = false } = options\n\n if (state.filters.has(key)) {\n const filterOptions = state.filters.get(key)!.options\n\n if (disableOthers) {\n for (let i = 0, len = filterOptions.length; i < len; ++i) {\n filterOptions[i].active = false\n }\n }\n\n const item = filterOptions.find(item => item.value === value)\n\n if (item) {\n item.active = active\n }\n }\n }\n\n function refreshRowIndex() {\n const data = state.rowData\n\n for (let i = 0, len = data.length; i < len; ++i) {\n data[i].index = i\n }\n }\n\n function updateTotalHeight() {\n const { heightBITree } = state\n\n if (heightBITree) {\n state.totalHeight = heightBITree.sum() ?? 0\n } else {\n state.totalHeight = 0\n }\n }\n\n function parseSorter(sorter: boolean | TableSorterOptions = false): ParsedTableSorterOptions {\n const raw = typeof sorter === 'boolean' ? { able: sorter } : sorter\n const { able = true, type = null, order = 0, method = null } = raw\n\n return { able, type, order, method }\n }\n\n function parseFilter(filter?: TableFilterOptions | null): ParsedFilterOptions {\n filter = filter || { able: false, options: [] }\n\n const {\n able = true,\n custom = false,\n multiple = false,\n active = null,\n method = null,\n meta,\n } = filter\n // 防止内部变化触发 deep watch\n const options = deepClone(filter.options ?? [])\n const formattedOptions = []\n\n for (let i = 0, len = options.length; i < len; ++i) {\n const item = options[i]\n const option = typeof item === 'string' ? { value: item } : { ...item }\n\n option.label = option.label ?? option.value.toString()\n\n let isActive = false\n\n if (multiple && Array.isArray(active)) {\n isActive = active.includes(option.value)\n } else if (!isNull(active)) {\n isActive = Object.is(option.value, active)\n }\n\n option.active = isActive\n\n formattedOptions.push(option as { value: string | number, label: string, active: boolean })\n }\n\n return { able, custom, meta, options: formattedOptions, multiple, active, method }\n }\n\n function filterData(\n filters: Map<Key, ParsedFilterOptions>,\n data: TableRowState[],\n isSingle: boolean,\n ) {\n const usedFilter: ParsedFilterOptions[] = []\n const usedData: TableRowState[] = []\n\n for (const filter of filters.values()) {\n const { able, active, method } = filter\n\n if (able && active && typeof method === 'function') {\n usedFilter.push(filter)\n\n if (isSingle) break\n }\n }\n\n const usedFilterCount = usedFilter.length\n\n for (let i = 0, len = data.length; i < len; ++i) {\n const row = data[i]\n\n let isFilter = true\n\n for (let j = 0; j < usedFilterCount; j++) {\n const { active, method } = usedFilter[j]\n\n isFilter = method!(active! as any, row.data)\n\n if (!isFilter) {\n break\n }\n }\n\n if (isFilter) {\n usedData.push(row)\n }\n }\n\n return usedData\n }\n\n function sortData(\n sorters: Map<Key, ParsedTableSorterOptions>,\n data: TableRowState[],\n columns: TableColumnOptions[],\n isSingle: boolean,\n ) {\n const usedSorter = []\n\n for (const [_key, sorter] of sorters) {\n const key = _key as keyof TableRowState\n const { able, type, order, method } = sorter\n\n if (able && type) {\n const column = columns.find(item => item.key === key)\n const accessor = column?.accessor\n\n usedSorter.push({\n able,\n key,\n order,\n type,\n method: method ?? undefined,\n accessor(row: TableRowState) {\n if (typeof accessor === 'function') {\n return accessor(row.data, row.index)\n }\n\n return row.data[key]\n },\n })\n\n if (isSingle) break\n }\n }\n\n // 多列排序优先级\n usedSorter.sort((prev, next) => prev.order - next.order)\n\n return sortByProps(data, usedSorter)\n }\n\n function pageData(currentPage: number, pageSize: number, data: TableRowState[]) {\n return pageSize > 0 ? data.slice((currentPage - 1) * pageSize, currentPage * pageSize) : data\n }\n\n function getParentRow(key: Key) {\n const { rowMap } = state\n const row = rowMap.get(key)\n\n if (!isNull(row?.parent)) {\n return rowMap.get(row!.parent) ?? null\n }\n\n return null\n }\n\n let lastColumnWidth: number | undefined\n\n function handleColumnResize(keys: Key[], newWidth: number) {\n const { resized, widths, columns, columnMap, width: tableWidth } = state\n const length = keys.length\n\n if (!columns.length || !length) return\n\n const deltaWidth = newWidth / length\n const lastKey = getLast(columns)!.key\n\n for (let i = 0; i < length; ++i) {\n const key = keys[i]\n const column = columnMap.get(key) as ColumnWithKey\n\n if (!column) continue\n\n const width =\n length === 1 ? Math.round(deltaWidth) : Math[i % 2 ? 'ceil' : 'floor'](deltaWidth)\n const { minWidth, maxWidth } = column\n\n resized.add(key)\n widths.set(key, boundRange(width, minWidth || COLUMN_DEFAULT_MIN_WIDTH, maxWidth || Infinity))\n }\n\n let totalWidth = 0\n\n for (const width of widths.values()) {\n totalWidth += width\n }\n\n totalWidth = toFixed(totalWidth, 1)\n\n if (\n totalWidth - widths.get(lastKey)! <\n tableWidth - (lastColumnWidth ?? widths.get(lastKey)!)\n ) {\n if (!lastColumnWidth) {\n lastColumnWidth = widths.get(lastKey)\n }\n\n widths.set(lastKey, widths.get(lastKey)! + tableWidth - totalWidth)\n } else if (lastColumnWidth) {\n widths.set(lastKey, lastColumnWidth!)\n lastColumnWidth = undefined\n }\n }\n\n function computeCellSpan() {\n const {\n normalColumns,\n leftFixedColumns,\n rightFixedColumns,\n aboveSummaries,\n belowSummaries,\n rowData,\n cellSpanMap,\n collapseMap,\n } = state\n const { processedData } = getters\n\n for (const type of ['left', 'default', 'right'] as const) {\n cellSpanMap.set(type, new Map())\n collapseMap.set(type, new Map())\n }\n\n function setCellSpan(\n rowIndex: number,\n columnIndex: number,\n fixed: 'left' | 'default' | 'right',\n getSpan: () => Required<CellSpanResult>,\n prefix = '',\n ) {\n const masterKey = `${prefix}${rowIndex},${columnIndex}`\n const collapsed = collapseMap.get(fixed)!\n\n if (collapsed.has(masterKey)) {\n cellSpanMap.get(fixed)!.set(masterKey, { colSpan: 0, rowSpan: 0 })\n return\n }\n\n const span = getSpan()\n const { colSpan, rowSpan } = span\n\n for (let i = 0; i < colSpan; ++i) {\n for (let j = 0; j < rowSpan; ++j) {\n if (!i && !j) continue\n\n const key = `${prefix}${rowIndex + j},${columnIndex + i}`\n\n let masterSet = collapsed.get(key)\n\n if (!masterSet) {\n masterSet = new Set()\n collapsed.set(key, masterSet)\n }\n\n masterSet.add(masterKey)\n }\n }\n\n cellSpanMap.get(fixed)!.set(masterKey, span)\n }\n\n for (const columns of [normalColumns, leftFixedColumns, rightFixedColumns]) {\n if (!columns.length) continue\n\n const fixed = columns[0].fixed === true ? 'left' : columns[0].fixed || 'default'\n const columnFixed = columns[0].fixed === true ? 'left' : columns[0].fixed || undefined\n\n const left = state.leftFixedColumns.length\n const right = state.allColumns[0].length - state.rightFixedColumns.length\n\n let allColumns: ColumnRawWithKey[][]\n\n if (fixed === 'left') {\n allColumns = state.allColumns.map(columns => columns.slice(0, left))\n } else if (fixed === 'right') {\n allColumns = state.allColumns.map(columns =>\n columns.slice(right, state.allColumns[0].length),\n )\n } else {\n allColumns = state.allColumns.map(columns => columns.slice(left, right))\n }\n\n for (let i = 0, len1 = allColumns.length; i < len1; ++i) {\n const rowColumns = allColumns[i]\n\n for (let j = 0, len2 = rowColum