Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(components): [table] fixed column supported in grouped header #10096

Merged
merged 10 commits into from Oct 23, 2022
10 changes: 10 additions & 0 deletions docs/en-US/component/table.md
Expand Up @@ -95,6 +95,16 @@ table/grouping-header

:::

## Table with fixed group header

fixed group head is supported

:::demo The attribute `fixed` of the group header is determined by the outermost `el-table-column`

table/fixed-column-and-group-header

:::

## Single select

Single row selection is supported.
Expand Down
73 changes: 73 additions & 0 deletions docs/examples/table/fixed-column-and-group-header.vue
@@ -0,0 +1,73 @@
<template>
<el-table :data="tableData" style="width: 100%" height="250">
<el-table-column prop="date" label="Date" width="150" />
<el-table-column prop="name" label="Name" width="150" />
<el-table-column prop="zip" label="Zip" width="150" />
<el-table-column label="Address Info" fixed="right">
<el-table-column prop="state" label="State" width="100" />
<el-table-column prop="city" label="City" width="120" />
<el-table-column prop="address" label="Address" width="250" />
</el-table-column>
</el-table>
</template>

<script lang="ts" setup>
const tableData = [
{
date: '2016-05-03',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-02',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-04',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-08',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-06',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-07',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
]
</script>
65 changes: 65 additions & 0 deletions packages/components/table/__tests__/table-column.test.ts
Expand Up @@ -701,6 +701,71 @@ describe('table column', () => {
wrapper.unmount()
})

it('should work with fixed', async () => {
const wrapper = mount({
components: {
ElTable,
ElTableColumn,
},
template: `
<el-table :data="testData">
<el-table-column prop="name" />
<el-table-column label="group" fixed="left">
<el-table-column label="group's group">
<el-table-column prop="runtime" width="100" fixed="right"/>
<el-table-column prop="director" width="100" fixed="right"/>
</el-table-column>
<el-table-column prop="director"/>
tinyfind marked this conversation as resolved.
Show resolved Hide resolved
</el-table-column>
<el-table-column prop="director"/>
<el-table-column prop="runtime"/>
<el-table-column label="group2" fixed="right">
<el-table-column prop="runtime" width="100" fixed="left"/>
<el-table-column prop="director" width="50"/>
</el-table-column>
<el-table-column prop="runtime"/>
</el-table>
`,

created() {
this.testData = getTestData()
this.groupFixed = 'left'
tinyfind marked this conversation as resolved.
Show resolved Hide resolved
},
})

await doubleWait()
// 分层判断
// 1/2/2
const lfhcolumns = wrapper
.findAll('.el-table__header tr')
.map((item) => item.findAll('.el-table-fixed-column--left'))
const lfbcolumns = wrapper.findAll(
'.el-table__body .el-table-fixed-column--left'
)
const rfhcolumns = wrapper
.findAll('.el-table__header tr')
.map((item) => item.findAll('.el-table-fixed-column--right'))
const rfbcolumns = wrapper.findAll(
'.el-table__body .el-table-fixed-column--right'
)
expect(lfbcolumns).toHaveLength(15)
expect(rfbcolumns).toHaveLength(10)
expect(lfhcolumns.at(0).at(0).classes()).toContain('is-last-column')
expect(lfhcolumns.at(1).at(1).classes()).toContain('is-last-column')
expect(getComputedStyle(lfhcolumns.at(1).at(1).element).left).toBe(
'200px'
)
expect(getComputedStyle(lfhcolumns.at(2).at(1).element).left).toBe(
'100px'
)
expect(rfhcolumns.at(0).at(0).classes()).toContain('is-first-column')
expect(rfhcolumns.at(1).at(0).classes()).toContain('is-first-column')
expect(getComputedStyle(rfhcolumns.at(1).at(0).element).right).toBe(
'50px'
)
wrapper.unmount()
})

it('el-table-column should callback itself', async () => {
const TableColumn = {
name: 'TableColumn',
Expand Down
11 changes: 11 additions & 0 deletions packages/components/table/src/store/watcher.ts
Expand Up @@ -84,8 +84,19 @@ function useWatcher<T>() {
if (!rowKey.value) throw new Error('[ElTable] prop row-key is required')
}

// 更新 fixed
const updateChildFixed = (column: TableColumnCtx<T>) => {
column.children?.forEach((childColumn) => {
childColumn.fixed = column.fixed
updateChildFixed(childColumn)
})
}

// 更新列
const updateColumns = () => {
_columns.value.forEach((column) => {
updateChildFixed(column)
})
fixedColumns.value = _columns.value.filter(
(column) => column.fixed === true || column.fixed === 'left'
)
Expand Down
26 changes: 13 additions & 13 deletions packages/components/table/src/table-body/styles-helper.ts
Expand Up @@ -67,9 +67,11 @@ function useStyles<T>(props: Partial<TableBodyProps<T>>) {
column,
})
}
const fixedStyle = column.isSubColumn
? null
: getFixedColumnOffset(columnIndex, props?.fixed, props.store)
const fixedStyle = getFixedColumnOffset(
columnIndex,
props?.fixed,
props.store
)
ensurePosition(fixedStyle, 'left')
ensurePosition(fixedStyle, 'right')
return Object.assign({}, cellStyles, fixedStyle)
Expand All @@ -82,16 +84,14 @@ function useStyles<T>(props: Partial<TableBodyProps<T>>) {
column: TableColumnCtx<T>,
offset: number
) => {
const fixedClasses = column.isSubColumn
? []
: getFixedColumnsClass(
ns.b(),
columnIndex,
props?.fixed,
props.store,
undefined,
offset
)
const fixedClasses = getFixedColumnsClass(
ns.b(),
columnIndex,
props?.fixed,
props.store,
undefined,
offset
)
const classes = [column.id, column.align, column.className, ...fixedClasses]
const cellClassName = parent?.props.cellClassName
if (typeof cellClassName === 'string') {
Expand Down
30 changes: 13 additions & 17 deletions packages/components/table/src/table-header/style.helper.ts
Expand Up @@ -48,14 +48,12 @@ function useStyle<T>(props: TableHeaderProps<T>) {
column,
})
}
const fixedStyle = column.isSubColumn
? null
: getFixedColumnOffset<T>(
columnIndex,
column.fixed,
props.store,
row as unknown as TableColumnCtx<T>[]
)
const fixedStyle = getFixedColumnOffset<T>(
columnIndex,
column.fixed,
props.store,
row as unknown as TableColumnCtx<T>[]
)
ensurePosition(fixedStyle, 'left')
ensurePosition(fixedStyle, 'right')
return Object.assign({}, headerCellStyles, fixedStyle)
Expand All @@ -67,15 +65,13 @@ function useStyle<T>(props: TableHeaderProps<T>) {
row: T,
column: TableColumnCtx<T>
) => {
const fixedClasses = column.isSubColumn
? []
: getFixedColumnsClass<T>(
ns.b(),
columnIndex,
column.fixed,
props.store,
row as unknown as TableColumnCtx<T>[]
)
const fixedClasses = getFixedColumnsClass<T>(
ns.b(),
columnIndex,
column.fixed,
props.store,
row as unknown as TableColumnCtx<T>[]
)
const classes = [
column.id,
column.order,
Expand Down
59 changes: 36 additions & 23 deletions packages/components/table/src/util.ts
@@ -1,6 +1,6 @@
// @ts-nocheck
import { createPopper } from '@popperjs/core'
import { get } from 'lodash-unified'
import { flatMap, get } from 'lodash-unified'
import escapeHtml from 'escape-html'
import { hasOwn, throwError } from '@element-plus/utils'
import { useZIndex } from '@element-plus/hooks'
Expand Down Expand Up @@ -379,6 +379,18 @@ export function createTablePopper(
return popperInstance
}

function getCurrentColumns<T>(column: TableColumnCtx<T>): TableColumnCtx<T>[] {
if (column.children) {
return flatMap(column.children, getCurrentColumns)
} else {
return [column]
}
}

function getColSpan<T>(colSpan: number, column: TableColumnCtx<T>) {
return colSpan + column.colSpan
}

export const isFixedColumn = <T>(
index: number,
fixed: string | boolean,
Expand All @@ -387,21 +399,18 @@ export const isFixedColumn = <T>(
) => {
let start = 0
let after = index
const columns = store.states.columns.value
tinyfind marked this conversation as resolved.
Show resolved Hide resolved
if (realColumns) {
if (realColumns[index].colSpan > 1) {
// fixed column not supported in grouped header
return {}
}
// handle group
for (let i = 0; i < index; i++) {
start += realColumns[i].colSpan
}
after = start + realColumns[index].colSpan - 1
// fixed column supported in grouped header
const curColumns = getCurrentColumns(realColumns[index])
const preColumns = columns.slice(0, columns.indexOf(curColumns[0]))

start = preColumns.reduce(getColSpan, 0)
after = start + curColumns.reduce(getColSpan, 0) - 1
} else {
start = index
}
let fixedLayout
const columns = store.states.columns
switch (fixed) {
case 'left':
if (after < store.states.fixedLeafColumnsLength.value) {
Expand All @@ -411,7 +420,7 @@ export const isFixedColumn = <T>(
case 'right':
if (
start >=
columns.value.length - store.states.rightFixedLeafColumnsLength.value
columns.length - store.states.rightFixedLeafColumnsLength.value
) {
fixedLayout = 'right'
}
Expand All @@ -421,7 +430,7 @@ export const isFixedColumn = <T>(
fixedLayout = 'left'
} else if (
start >=
columns.value.length - store.states.rightFixedLeafColumnsLength.value
columns.length - store.states.rightFixedLeafColumnsLength.value
) {
fixedLayout = 'right'
}
Expand All @@ -444,13 +453,18 @@ export const getFixedColumnsClass = <T>(
offset = 0
) => {
const classes: string[] = []
const { direction, start } = isFixedColumn(index, fixed, store, realColumns)
const { direction, start, after } = isFixedColumn(
index,
fixed,
store,
realColumns
)
if (direction) {
const isLeft = direction === 'left'
classes.push(`${namespace}-fixed-column--${direction}`)
if (
isLeft &&
start + offset === store.states.fixedLeafColumnsLength.value - 1
after + offset === store.states.fixedLeafColumnsLength.value - 1
) {
classes.push('is-last-column')
} else if (
Expand Down Expand Up @@ -480,23 +494,22 @@ export const getFixedColumnOffset = <T>(
store: any,
realColumns?: TableColumnCtx<T>[]
) => {
const { direction, start = 0 } = isFixedColumn(
index,
fixed,
store,
realColumns
)
const {
direction,
start = 0,
after = 0,
} = isFixedColumn(index, fixed, store, realColumns)
if (!direction) {
return
}
const styles: any = {}
const isLeft = direction === 'left'
const columns = store.states.columns.value
if (isLeft) {
styles.left = columns.slice(0, index).reduce(getOffset, 0)
styles.left = columns.slice(0, start).reduce(getOffset, 0)
} else {
styles.right = columns
.slice(start + 1)
.slice(after + 1)
.reverse()
.reduce(getOffset, 0)
}
Expand Down