feat(components): [virtual-table] renderers (#7198)

- Add LeftFixedTable component renderer
- Render LeftFixedTable for table-v2
- Add style for left table
This commit is contained in:
JeremyWuuuuu 2022-04-17 19:28:44 +08:00 committed by GitHub
parent da88218821
commit e610149fb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 142 additions and 46 deletions

View File

@ -42,8 +42,10 @@ const HeaderCellRenderer: FunctionalComponent<HeaderCellRendererProps> = (
onColumnSorted,
} = props
const style = columnsStyles[column.key]
if (column.placeholderSign === placeholderSign) {
return
return <div class={ns.em('header-row-cell', 'placeholder')} style={style} />
}
const { headerCellRenderer, headerClass, sortable, resizable } = column
@ -88,7 +90,7 @@ const HeaderCellRenderer: FunctionalComponent<HeaderCellRendererProps> = (
...tryCall(headerCellProps, props),
onClick: column.sortable ? onColumnSorted : undefined,
class: cellKls,
style: columnsStyles[column.key],
style,
['data-key']: column.key,
}

View File

@ -0,0 +1,23 @@
import Table from '../table-grid'
import type { FunctionalComponent, Ref } from 'vue'
import type { TableV2GridProps } from '../grid'
import type { TableGridInstance } from '../table-grid'
type LeftTableProps = TableV2GridProps & {
leftTableRef: Ref<TableGridInstance | undefined>
}
const LeftTable: FunctionalComponent<LeftTableProps> = (props, { slots }) => {
if (!props.columns) return
const { leftTableRef, ...rest } = props
return (
<Table ref={leftTableRef} {...rest}>
{slots}
</Table>
)
}
export default LeftTable

View File

@ -7,7 +7,7 @@ import { isObject } from '@element-plus/utils'
import Header from './table-header'
import { TableV2InjectionKey } from './tokens'
import { tableV2GridProps } from './grid'
import { sumHeights } from './utils'
import { sum } from './utils'
import type { UnwrapRef } from 'vue'
import type {
@ -42,7 +42,7 @@ const useTableGrid = (props: TableV2GridProps) => {
return (fixedData?.length || 0) * rowHeight
})
const headerHeight = computed(() => sumHeights(props.headerHeight))
const headerHeight = computed(() => sum(props.headerHeight))
const gridHeight = computed(() => {
const { height } = props

View File

@ -90,6 +90,7 @@ const useTableRow = (props: TableV2RowProps) => {
{ name: 'onMouseenter', hovered: true },
] as const
).forEach(({ name, hovered }) => {
const existedHandler = eventHandlers[name]
eventHandlers[name] = (event: MouseEvent) => {
onRowHover({
event,
@ -99,7 +100,7 @@ const useTableRow = (props: TableV2RowProps) => {
rowKey,
})
eventHandlers[name]?.(event)
existedHandler?.(event)
}
})
}

View File

@ -6,13 +6,13 @@ import { TableV2InjectionKey } from './tokens'
import { tableV2Props } from './table'
// renderers
import MainTable from './renderers/main-table'
import LeftTable from './renderers/left-table'
import Row from './renderers/row'
import Cell from './renderers/cell'
import Header from './renderers/header'
import HeaderCell from './renderers/header-cell'
import type { CSSProperties } from 'vue'
import type { TableV2GridProps } from './grid'
import type { TableGridRowSlotParams } from './table-grid'
import type { TableV2RowCellRenderParam } from './table-row'
import type { TableV2HeaderRendererParams } from './table-header'
@ -29,16 +29,19 @@ const TableV2 = defineComponent({
const {
columnsStyles,
columnsTotalWidth,
// fixedColumnsOnLeft,
fixedColumnsOnLeft,
// fixedColumnOnRight,
mainColumns,
mainTableHeight,
fixedTableHeight,
leftTableWidth,
data,
depthMap,
expandedRowKeys,
hasFixedColumns,
hoveringRowKey,
mainTableRef,
leftTableRef,
isResetting,
isScrolling,
resizingKey,
@ -52,6 +55,7 @@ const TableV2 = defineComponent({
onRowExpanded,
onRowsRendered,
onScroll,
onVerticalScroll,
} = useTable(props)
const bodyWidth = computed(() => {
@ -116,27 +120,53 @@ const TableV2 = defineComponent({
width,
} = props
const mainTableProps: TableV2GridProps = {
const _data = unref(data)
const mainTableProps = {
cache,
class: ns.e('main'),
columns: unref(mainColumns),
data: unref(data),
data: _data,
fixedData,
estimatedRowHeight,
bodyWidth: unref(bodyWidth),
headerHeight,
headerWidth: unref(headerWidth),
rowHeight,
height: unref(mainTableHeight),
mainTableRef,
rowHeight,
useIsScrolling,
width,
onRowsRendered,
onScroll,
}
const leftColumnsWidth = unref(leftTableWidth)
const leftColumnsWidthWithScrollbar =
leftColumnsWidth + unref(vScrollbarSize)
const _fixedTableHeight = unref(fixedTableHeight)
const leftTableProps = {
cache,
class: ns.e('left'),
columns: unref(fixedColumnsOnLeft),
data: _data,
estimatedRowHeight,
leftTableRef,
rowHeight,
bodyWidth: leftColumnsWidthWithScrollbar,
headerWidth: leftColumnsWidthWithScrollbar,
headerHeight,
height: _fixedTableHeight,
useIsScrolling,
width: leftColumnsWidthWithScrollbar,
onScroll: onVerticalScroll,
}
const tableRowProps = {
ns,
depthMap: unref(depthMap),
expandColumnKey,
expandedRowKeys: unref(expandedRowKeys),
estimatedRowHeight,
hasFixedColumns: unref(hasFixedColumns),
@ -180,32 +210,33 @@ const TableV2 = defineComponent({
onColumnSorted,
}
return (
<div class={[ns.b(), ns.e('root')]} style={unref(rootStyle)}>
<MainTable mainTableRef={mainTableRef} {...mainTableProps}>
const tableSlots = {
row: (props: TableGridRowSlotParams) => (
<Row {...props} {...tableRowProps}>
{{
row: (props: TableGridRowSlotParams) => (
<Row {...props} {...tableRowProps}>
{{
row: slots.row,
cell: (props: TableV2RowCellRenderParam) => (
<Cell {...props} {...tableCellProps} />
),
}}
</Row>
),
header: (props: TableV2HeaderRendererParams) => (
<Header {...props} {...tableHeaderProps}>
{{
header: slots.header,
cell: (props: TableV2HeaderRowCellRendererParams) => (
<HeaderCell {...props} {...tableHeaderCellProps} />
),
}}
</Header>
row: slots.row,
cell: (props: TableV2RowCellRenderParam) => (
<Cell {...props} {...tableCellProps} />
),
}}
</MainTable>
</Row>
),
header: (props: TableV2HeaderRendererParams) => (
<Header {...props} {...tableHeaderProps}>
{{
header: slots.header,
cell: (props: TableV2HeaderRowCellRendererParams) => (
<HeaderCell {...props} {...tableHeaderCellProps} />
),
}}
</Header>
),
}
return (
<div class={[ns.b(), ns.e('root')]} style={unref(rootStyle)}>
<MainTable {...mainTableProps}>{tableSlots}</MainTable>
<LeftTable {...leftTableProps}>{tableSlots}</LeftTable>
</div>
)
}

View File

@ -7,8 +7,8 @@ import {
unref,
watch,
} from 'vue'
import { isObject, isUndefined } from '@element-plus/utils'
import { sumHeights } from './utils'
import { isNumber, isObject, isUndefined } from '@element-plus/utils'
import { sum } from './utils'
import { useColumns } from './use-columns'
import { SortOrder, oppositeOrderMap } from './constants'
@ -56,7 +56,6 @@ function useTable(props: TableV2Props) {
const scrollPos = ref<ScrollPos>({ scrollLeft: 0, scrollTop: 0 })
const lastRenderedRowIndex = ref(-1)
const rowsHeight = shallowRef(0)
const rowHeights = shallowRef<Heights>({})
const leftTableHeights = shallowRef<Heights>({})
const mainTableHeights = shallowRef<Heights>({})
@ -64,6 +63,16 @@ function useTable(props: TableV2Props) {
const hScrollbarSize = shallowRef(0)
const vScrollbarSize = shallowRef(0)
const rowsHeight = computed(() => {
const { rowHeight, estimatedRowHeight } = props
const _data = unref(data)
if (isNumber(estimatedRowHeight)) {
return _data.length * estimatedRowHeight
}
return _data.length * rowHeight
})
const flattenedData = computed(() => {
const depths = {}
const { data, rowKey } = props
@ -119,7 +128,23 @@ function useTable(props: TableV2Props) {
return height - footerHeight
})
const headerHeight = computed(() => sumHeights(props.headerHeight))
const fixedTableHeight = computed(() => {
const tableHeight =
unref(mainTableHeight) -
(unref(data).length > 0 ? unref(hScrollbarSize) : 0)
if (isNumber(props.maxHeight) && props.maxHeight > 0) return tableHeight
const totalHeight =
unref(rowsHeight) + unref(headerHeight) + unref(fixedRowsHeight)
return Math.min(tableHeight, totalHeight)
})
const leftTableWidth = computed(() =>
sum(unref(fixedColumnsOnLeft).map((column) => column.width))
)
const headerHeight = computed(() => sum(props.headerHeight))
const fixedRowsHeight = computed(() => {
return (props.fixedData?.length || 0) * props.rowHeight
@ -172,8 +197,6 @@ function useTable(props: TableV2Props) {
unref(lastRenderedRowIndex) >= 0 &&
_totalHeight !== unref(rowsHeight)
) {
rowsHeight.value = _totalHeight
onEndReached(heightUntilEnd)
}
}
@ -200,7 +223,7 @@ function useTable(props: TableV2Props) {
props.onScroll?.(params)
}
function onVerticalScroll(scrollTop: number) {
function onVerticalScroll({ scrollTop }: ScrollPos) {
const { scrollTop: currentScrollTop } = unref(scrollPos)
if (scrollTop !== currentScrollTop) scrollToTop(scrollTop)
}
@ -374,6 +397,8 @@ function useTable(props: TableV2Props) {
mainColumns,
// metadata
mainTableHeight,
fixedTableHeight,
leftTableWidth,
// methods
scrollTo,

View File

@ -2,10 +2,10 @@ import { addUnit, isArray, isFunction } from '@element-plus/utils'
import type { CSSProperties } from 'vue'
export const sumHeights = (heights: number | number[]) => {
return isArray(heights)
? heights.reduce((sum, height) => sum + height, 0)
: heights
const sumReducer = (sum: number, num: number) => sum + num
export const sum = (listLike: number | number[]) => {
return isArray(listLike) ? listLike.reduce(sumReducer, 0) : listLike
}
export const tryCall = <T>(

View File

@ -24,6 +24,16 @@
}
}
@mixin table-root() {
display: flex;
flex-direction: column-reverse;
position: absolute;
overflow: hidden;
left: 0;
top: 0;
background-color: getCssVar('bg', 'color');
}
@include b('table-v2') {
@include set-component-css-var('table', $table);
}
@ -38,8 +48,12 @@
}
@include e('main') {
display: flex;
flex-direction: column-reverse;
@include table-root;
}
@include e('left') {
@include table-root;
box-shadow: 2px 0 4px 0 rgb(0 0 0 / 6%);
}
@include e('header-wrapper') {