mirror of
https://github.com/element-plus/element-plus.git
synced 2025-03-07 15:47:57 +08:00
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:
parent
da88218821
commit
e610149fb7
@ -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,
|
||||
}
|
||||
|
||||
|
23
packages/components/table-v2/src/renderers/left-table.tsx
Normal file
23
packages/components/table-v2/src/renderers/left-table.tsx
Normal 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
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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>(
|
||||
|
@ -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') {
|
||||
|
Loading…
Reference in New Issue
Block a user