Skip to content

Commit

Permalink
fix(comp:table): refactor table column width measure logic (#1899)
Browse files Browse the repository at this point in the history
1. measured width should be used in table header fix holder
2. table content colgroup width is set using width config in columns
  • Loading branch information
sallerli1 committed Apr 24, 2024
1 parent 8debc9d commit 425def0
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 159 deletions.
28 changes: 22 additions & 6 deletions packages/components/table/src/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@
*/

import type { VirtualScrollEnabled } from '@idux/cdk/scroll'
import type { VKey } from '@idux/cdk/utils'

import { type VNode, computed, defineComponent, normalizeClass, provide, watch } from 'vue'

import { isBoolean } from 'lodash-es'

import { type VKey, useState } from '@idux/cdk/utils'
import { ɵHeader } from '@idux/components/_private/header'
import { useGlobalConfig } from '@idux/components/config'
import { IxSpin, type SpinProps } from '@idux/components/spin'
import { useThemeToken } from '@idux/components/theme'
import { useGetKey } from '@idux/components/utils'

import { useColumnOffsets } from './composables/useColumnOffsets'
import { useColumnWidthMeasure } from './composables/useColumnWidthMeasure'
import { type TableColumnMerged, useColumns } from './composables/useColumns'
import { useDataSource } from './composables/useDataSource'
import { useExpandable } from './composables/useExpandable'
Expand Down Expand Up @@ -51,6 +53,8 @@ export default defineComponent({
const locale = useGlobalConfig('locale')
const config = useGlobalConfig('table')

const [clientWidth, setClientWidth] = useState(0)

const mergedAutoHeight = computed(() => props.autoHeight ?? config.autoHeight)
const mergedChildrenKey = computed(() => props.childrenKey ?? config.childrenKey)
const mergedGetKey = useGetKey(props, config, 'components/table')
Expand All @@ -70,9 +74,18 @@ export default defineComponent({
const stickyContext = useSticky(props)
const scrollContext = useScroll(props, stickyContext)
const columnsContext = useColumns(props, slots, config, scrollContext.scrollBarSizeOnFixedHolder)
const sortableContext = useSortable(columnsContext.flattedColumns)
const filterableContext = useFilterable(columnsContext.flattedColumns)
const expandableContext = useExpandable(props, columnsContext.flattedColumns)

const { flattedColumns, flattedColumnsWithScrollBar, fixedColumns } = columnsContext

const columnMeasureContext = useColumnWidthMeasure(flattedColumns)
const { measuredColumnWidthMap } = columnMeasureContext

const columnCount = computed(() => flattedColumnsWithScrollBar.value.length)

const columnOffsetsContext = useColumnOffsets(fixedColumns, measuredColumnWidthMap, columnCount)
const sortableContext = useSortable(flattedColumns)
const filterableContext = useFilterable(flattedColumns)
const expandableContext = useExpandable(props, flattedColumns)
const tableLayout = useTableLayout(
props,
columnsContext,
Expand All @@ -82,7 +95,6 @@ export default defineComponent({
mergedAutoHeight,
)

const { columnWidthMap, flattedColumns } = columnsContext
const { activeSorters } = sortableContext
const { activeFilters } = filterableContext

Expand Down Expand Up @@ -117,14 +129,16 @@ export default defineComponent({
useScrollOnChange(props, config, mergedPagination, activeSorters, activeFilters, scrollContext.scrollTo)

const getVirtualColWidth = (rowKey: VKey, colKey: VKey) => {
return columnWidthMap.value[colKey] ?? columnMap.get(colKey)?.width
return measuredColumnWidthMap.value[colKey] ?? columnMap.get(colKey)?.width
}

const context = {
props,
slots,
config,
locale,
clientWidth,
setClientWidth,
mergedPrefixCls,
mergedEmptyCell,
mergedInsetShadow,
Expand All @@ -134,6 +148,8 @@ export default defineComponent({
mergedAutoHeight,
getVirtualColWidth,
...columnsContext,
...columnMeasureContext,
...columnOffsetsContext,
...scrollContext,
...sortableContext,
...filterableContext,
Expand Down
90 changes: 90 additions & 0 deletions packages/components/table/src/composables/useColumnOffsets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import type { VKey } from '@idux/cdk/utils'

import { type ComputedRef, type Ref, computed } from 'vue'

import { TableColumnMerged, TableColumnScrollBar } from './useColumns'

export interface ColumnOffsetsContext {
columnOffsets: ComputedRef<{
starts: Record<VKey, { index: number; offset: number }>
ends: Record<VKey, { index: number; offset: number }>
}>
columnOffsetsWithScrollBar: ComputedRef<{
starts: Record<VKey, { index: number; offset: number }>
ends: Record<VKey, { index: number; offset: number }>
}>
}

export function useColumnOffsets(
fixedColumns: ComputedRef<{
fixedStartColumns: (TableColumnMerged | TableColumnScrollBar)[]
fixedEndColumns: (TableColumnMerged | TableColumnScrollBar)[]
fixedColumnIndexMap: Record<VKey, number>
}>,
columnWidthsMap: Ref<Record<VKey, number>>,
columnCount: Ref<number>,
): ColumnOffsetsContext {
const columnOffsets = computed(() => {
const { fixedStartColumns, fixedEndColumns, fixedColumnIndexMap } = fixedColumns.value
return calculateOffsets(
fixedStartColumns,
fixedEndColumns.filter(column => column.type !== 'scroll-bar'),
fixedColumnIndexMap,
columnWidthsMap.value,
columnCount.value - 1,
)
})
const columnOffsetsWithScrollBar = computed(() => {
const { fixedStartColumns, fixedEndColumns, fixedColumnIndexMap } = fixedColumns.value
return calculateOffsets(
fixedStartColumns,
fixedEndColumns,
fixedColumnIndexMap,
columnWidthsMap.value,
columnCount.value,
)
})
return { columnOffsets, columnOffsetsWithScrollBar }
}

function calculateOffsets(
startColumns: (TableColumnMerged | TableColumnScrollBar)[],
endColumns: (TableColumnMerged | TableColumnScrollBar)[],
columnIndexMap: Record<VKey, number>,
columnWidthsMap: Record<VKey, number>,
columnCount: number,
) {
const startOffsets: Record<VKey, { index: number; offset: number }> = {}
const endOffsets: Record<VKey, { index: number; offset: number }> = {}

let startOffset = 0
let endOffset = 0

for (let index = 0; index < startColumns.length; index++) {
const column = startColumns[index]
const width = columnWidthsMap[column.key] ?? column.width ?? 0

startOffsets[column.key] = { index: columnIndexMap[column.key] ?? index, offset: startOffset }
startOffset += width
}

for (let index = 0; index < endColumns.length; index++) {
const column = endColumns[endColumns.length - index - 1]
const width = columnWidthsMap[column.key] ?? column.width ?? 0

endOffsets[column.key] = { index: columnIndexMap[column.key] ?? columnCount - index - 1, offset: endOffset }
endOffset += width
}

return {
starts: startOffsets,
ends: endOffsets,
}
}
42 changes: 42 additions & 0 deletions packages/components/table/src/composables/useColumnWidthMeasure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import type { TableColumnMerged } from './useColumns'
import type { VKey } from '@idux/cdk/utils'

import { type ComputedRef, type Ref, ref, watch } from 'vue'

export interface ColumnWidthMeasureContext {
measuredColumnWidthMap: Ref<Record<VKey, number>>
changeColumnWidth: (key: VKey, width: number | false) => void
}

export function useColumnWidthMeasure(flattedColumns: ComputedRef<TableColumnMerged[]>): ColumnWidthMeasureContext {
const measuredColumnWidthMap = ref<Record<VKey, number>>({})

const changeColumnWidth = (key: VKey, width: number | false) => {
if (width === false) {
delete measuredColumnWidthMap.value[key]
} else {
measuredColumnWidthMap.value[key] = width
}
}

watch(flattedColumns, columns => {
const columnKeySet = new Set(columns.map(column => column.key))
Object.keys(measuredColumnWidthMap).forEach(key => {
if (!columnKeySet.has(key)) {
delete measuredColumnWidthMap.value[key]
}
})
})

return {
measuredColumnWidthMap,
changeColumnWidth,
}
}
133 changes: 4 additions & 129 deletions packages/components/table/src/composables/useColumns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,9 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import {
type ComputedRef,
type Ref,
type Slots,
type VNode,
type VNodeChild,
computed,
ref,
watch,
watchEffect,
} from 'vue'

import { debounce, isNil, isString } from 'lodash-es'
import { type ComputedRef, type Slots, type VNode, type VNodeChild, computed } from 'vue'

import { isNil, isString } from 'lodash-es'

import { type VKey, flattenNode } from '@idux/cdk/utils'
import { type TableConfig } from '@idux/components/config'
Expand Down Expand Up @@ -54,11 +44,6 @@ export function useColumns(
)
const hasFixed = computed(() => flattedColumns.value.some(column => column.fixed))

const columnCount = computed(() => flattedColumnsWithScrollBar.value.length)

const { columnWidthMap, columnWidths, changeColumnWidth } = useColumnWidths(flattedColumns)
const { columnOffsets, columnOffsetsWithScrollBar } = useColumnOffsets(fixedColumns, columnWidthMap, columnCount)

const mergedRows = computed(() => mergeRows(mergedColumns.value, scrollBarColumn.value))

return {
Expand All @@ -69,11 +54,6 @@ export function useColumns(
fixedColumnKeys,
hasEllipsis,
hasFixed,
columnWidthMap,
columnWidths,
changeColumnWidth,
columnOffsets,
columnOffsetsWithScrollBar,
mergedRows,
}
}
Expand All @@ -85,24 +65,14 @@ export interface ColumnsContext {
fixedColumns: ComputedRef<{
fixedStartColumns: (TableColumnMerged | TableColumnScrollBar)[]
fixedEndColumns: (TableColumnMerged | TableColumnScrollBar)[]
fixedColumnIndexMap: Record<VKey, number>
}>
fixedColumnKeys: ComputedRef<{
lastStartKey: VKey | undefined
firstEndKey: VKey | undefined
}>
hasEllipsis: ComputedRef<boolean>
hasFixed: ComputedRef<boolean>
columnWidthMap: Ref<Record<VKey, number>>
columnWidths: Ref<number[]>
changeColumnWidth: (key: VKey, width: number | false) => void
columnOffsets: ComputedRef<{
starts: Record<VKey, { index: number; offset: number }>
ends: Record<VKey, { index: number; offset: number }>
}>
columnOffsetsWithScrollBar: ComputedRef<{
starts: Record<VKey, { index: number; offset: number }>
ends: Record<VKey, { index: number; offset: number }>
}>
mergedRows: ComputedRef<{
rows: TableColumnMergedExtra[][]
offsetIndexMap: Record<VKey, { colStart: number; colEnd: number }>
Expand Down Expand Up @@ -315,101 +285,6 @@ function useFixedColumns(flattedColumnsWithScrollBar: ComputedRef<(TableColumnMe
return { fixedColumns, fixedColumnKeys }
}

function useColumnWidths(flattedColumns: ComputedRef<TableColumnMerged[]>) {
const widthMap = ref<Record<VKey, number>>({})
const widthString = ref<string>()
const columnWidths = ref<number[]>([])
watch(
widthString,
// resizable: 列宽设置百分比的情况下,拖拽会改变多列的宽度,用 debounce 来减少重复渲染次数。
debounce(widths => {
columnWidths.value = widths ? widths.split('-').filter(Boolean).map(Number) : []
}, 16),
)

watchEffect(() => {
const columns = flattedColumns.value
widthString.value = columns.map(column => widthMap.value[column.key]).join('-')
})

const changeColumnWidth = (key: VKey, width: number | false) => {
if (width === false) {
delete widthMap.value[key]
} else {
widthMap.value[key] = width
}
}

return { columnWidthMap: widthMap, columnWidths, changeColumnWidth }
}

function useColumnOffsets(
fixedColumns: ComputedRef<{
fixedStartColumns: (TableColumnMerged | TableColumnScrollBar)[]
fixedEndColumns: (TableColumnMerged | TableColumnScrollBar)[]
fixedColumnIndexMap: Record<VKey, number>
}>,
columnWidthsMap: Ref<Record<VKey, number>>,
columnCount: Ref<number>,
) {
const columnOffsets = computed(() => {
const { fixedStartColumns, fixedEndColumns, fixedColumnIndexMap } = fixedColumns.value
return calculateOffsets(
fixedStartColumns,
fixedEndColumns.filter(column => column.type !== 'scroll-bar'),
fixedColumnIndexMap,
columnWidthsMap.value,
columnCount.value - 1,
)
})
const columnOffsetsWithScrollBar = computed(() => {
const { fixedStartColumns, fixedEndColumns, fixedColumnIndexMap } = fixedColumns.value
return calculateOffsets(
fixedStartColumns,
fixedEndColumns,
fixedColumnIndexMap,
columnWidthsMap.value,
columnCount.value,
)
})
return { columnOffsets, columnOffsetsWithScrollBar }
}

function calculateOffsets(
startColumns: (TableColumnMerged | TableColumnScrollBar)[],
endColumns: (TableColumnMerged | TableColumnScrollBar)[],
columnIndexMap: Record<VKey, number>,
columnWidthsMap: Record<VKey, number>,
columnCount: number,
) {
const startOffsets: Record<VKey, { index: number; offset: number }> = {}
const endOffsets: Record<VKey, { index: number; offset: number }> = {}

let startOffset = 0
let endOffset = 0

for (let index = 0; index < startColumns.length; index++) {
const column = startColumns[index]
const width = columnWidthsMap[column.key] ?? column.width ?? 0

startOffsets[column.key] = { index: columnIndexMap[column.key] ?? index, offset: startOffset }
startOffset += width
}

for (let index = 0; index < endColumns.length; index++) {
const column = endColumns[endColumns.length - index - 1]
const width = columnWidthsMap[column.key] ?? column.width ?? 0

endOffsets[column.key] = { index: columnIndexMap[column.key] ?? columnCount - index - 1, offset: endOffset }
endOffset += width
}

return {
starts: startOffsets,
ends: endOffsets,
}
}

function mergeRows(mergedColumns: TableColumnMerged[], scrollBarColumn: TableColumnScrollBar | undefined) {
const rows: TableColumnMergedExtra[][] = []
const offsetIndexMap: Record<VKey, { colStart: number; colEnd: number }> = {}
Expand Down
Loading

0 comments on commit 425def0

Please sign in to comment.