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

- Fix custom renderer for column cell and header column cell.
- Add helper method for rendering cells.
- Update column interface for correctness.
- Remove some unused code in `use-columns`.
This commit is contained in:
JeremyWuuuuu
2022-04-18 23:32:10 +08:00
committed by GitHub
parent 8307d109fd
commit ed08609bd4
8 changed files with 72 additions and 62 deletions

View File

@@ -0,0 +1 @@
export type { Column } from './src/types'

View File

@@ -1,11 +1,13 @@
import { buildProps, definePropType } from '@element-plus/utils'
import type { CSSProperties, ExtractPropTypes } from 'vue'
import type { CSSProperties, Component, ExtractPropTypes } from 'vue'
import type {
Alignment,
CellRenderer,
CellRendererParams,
FixedDirection,
HeaderRenderer,
HeaderCellRenderer,
HeaderCellRendererParams,
} from './types'
const widthType = {
@@ -44,11 +46,15 @@ export const tableV2ColumnProps = buildProps({
// getters & renderers
cellRenderer: {
type: definePropType<CellRenderer<any>>(Function),
type: definePropType<
CellRenderer<any> | Component<CellRendererParams<any>>
>([Function, Object]),
},
headerRenderer: {
type: definePropType<HeaderRenderer<any>>(Function),
type: definePropType<
HeaderCellRenderer<any> | Component<HeaderCellRendererParams<any>>
>([Function, Object]),
},
} as const)

View File

@@ -4,9 +4,10 @@ import TableCell from '../table-cell'
import ExpandIcon from '../expand-icon'
import { Alignment } from '../constants'
import { placeholderSign } from '../private'
import { enforceUnit } from '../utils'
import { componentToSlot, enforceUnit } from '../utils'
import type { FunctionalComponent, UnwrapNestedRefs, VNode } from 'vue'
import type { CellRendererParams } from '../types'
import type { TableV2RowCellRenderParam } from '../table-row'
import type { UseNamespaceReturn } from '@element-plus/hooks'
import type { UseTableReturn } from '../use-table'
@@ -51,9 +52,15 @@ const CellRenderer: FunctionalComponent<CellRendererProps> = (
if (column.placeholderSign === placeholderSign) {
return <div class={ns.em('row-cell', 'placeholder')} style={cellStyle} />
}
const { dataKey, dataGetter } = column
const { cellRenderer, dataKey, dataGetter } = column
const columnCellRenderer = componentToSlot(cellRenderer)
const CellComponent =
columnCellRenderer ||
slots.default ||
((props: CellRendererParams<any>) => <TableCell {...props} />)
const CellComponent = slots.cell || ((props) => <TableCell {...props} />)
const cellData = isFunction(dataGetter)
? dataGetter({ columns, column, columnIndex, rowData, rowIndex })
: get(rowData, dataKey ?? '')

View File

@@ -3,7 +3,7 @@ import HeaderCell from '../table-header-cell'
import SortIcon from '../sort-icon'
import { Alignment, SortOrder, oppositeOrderMap } from '../constants'
import { placeholderSign } from '../private'
import { tryCall } from '../utils'
import { componentToSlot, tryCall } from '../utils'
import type { FunctionalComponent, UnwrapNestedRefs } from 'vue'
import type { UseNamespaceReturn } from '@element-plus/hooks'
@@ -28,7 +28,8 @@ type HeaderCellRendererProps = TableV2HeaderRowCellRendererParams &
}
const HeaderCellRenderer: FunctionalComponent<HeaderCellRendererProps> = (
props
props,
{ slots }
) => {
const {
column,
@@ -52,14 +53,18 @@ const HeaderCellRenderer: FunctionalComponent<HeaderCellRendererProps> = (
/**
* render Cell children
*/
const cellRenderer =
headerCellRenderer ||
((props: TableV2HeaderCell) => <HeaderCell {...props} />)
const Cell = cellRenderer({
const cellProps = {
...props,
class: ns.e('header-cell-text'),
})
}
const cellRenderer =
componentToSlot<typeof cellProps>(headerCellRenderer) ||
slots.default ||
((props: TableV2HeaderCell) => <HeaderCell {...props} />)
const Cell = cellRenderer(cellProps)
/**
* Render cell container and sort indicator
@@ -85,7 +90,7 @@ const HeaderCellRenderer: FunctionalComponent<HeaderCellRendererProps> = (
// column.key === resizingKey && ns.is('resizing'),
]
const cellProps = {
const cellWrapperProps = {
...tryCall(headerCellProps, props),
onClick: column.sortable ? onColumnSorted : undefined,
class: cellKls,
@@ -95,7 +100,7 @@ const HeaderCellRenderer: FunctionalComponent<HeaderCellRendererProps> = (
// For now we don't deliver resizable column feature since it has some UX issue.
return (
<div {...cellProps}>
<div {...cellWrapperProps}>
{Cell}
{sortable && (

View File

@@ -19,6 +19,7 @@ import type { TableV2HeaderRendererParams } from './table-header'
import type { TableV2HeaderRowCellRendererParams } from './table-header-row'
const COMPONENT_NAME = 'ElTableV2'
const TableV2 = defineComponent({
name: COMPONENT_NAME,
props: tableV2Props,
@@ -221,9 +222,14 @@ const TableV2 = defineComponent({
<Row {...props} {...tableRowProps}>
{{
row: slots.row,
cell: (props: TableV2RowCellRenderParam) => (
<Cell {...props} {...tableCellProps} />
),
cell: (props: TableV2RowCellRenderParam) =>
slots.cell ? (
<Cell {...props} {...tableCellProps}>
{slots.cell}
</Cell>
) : (
<Cell {...props} {...tableCellProps} />
),
}}
</Row>
),
@@ -231,9 +237,14 @@ const TableV2 = defineComponent({
<Header {...props} {...tableHeaderProps}>
{{
header: slots.header,
cell: (props: TableV2HeaderRowCellRendererParams) => (
<HeaderCell {...props} {...tableHeaderCellProps} />
),
cell: (props: TableV2HeaderRowCellRendererParams) =>
slots['header-cell'] ? (
<HeaderCell {...props} {...tableHeaderCellProps}>
{slots['header-cell']}
</HeaderCell>
) : (
<HeaderCell {...props} {...tableHeaderCellProps} />
),
}}
</Header>
),

View File

@@ -18,7 +18,7 @@ export type ColumnCommonParams<T> = {
columnIndex: number
}
export type HeaderRendererParams<T> = {
export type HeaderCellRendererParams<T> = {
headerIndex: number
} & ColumnCommonParams<T>
@@ -49,7 +49,9 @@ export type HeaderClassGetter<T> = (
*/
export type CellRenderer<T> = (params: CellRendererParams<T>) => VNode
export type HeaderCellRenderer<T> = (params: HeaderRendererParams<T>) => VNode
export type HeaderCellRenderer<T> = (
params: HeaderCellRendererParams<T>
) => VNode
export type Column<T = any> = {
key: KeyType
@@ -61,7 +63,7 @@ export type Column<T = any> = {
fixed?: true | FixedDirection
title?: string
hidden?: boolean
headerClass: HeaderClassGetter<T> | string
headerClass?: HeaderClassGetter<T> | string
maxWidth?: number
minWidth?: number
resizable?: boolean

View File

@@ -1,4 +1,4 @@
import { computed, ref, unref, watchEffect } from 'vue'
import { computed, unref } from 'vue'
import { placeholderSign } from './private'
import type { CSSProperties, Ref } from 'vue'
@@ -34,33 +34,9 @@ const calcColumnStyle = (
return style
}
const mapColumns = (columns: AnyColumn, currentColumns: AnyColumn) => {
return columns.map((column) => {
if (!column.resizable) return column
let { width } = column
if (column.resizable) {
const idx = columns.findIndex(
(predicated) => column.key === predicated.key
)
if (idx >= 0 && columns[idx].width === column.width) {
width = currentColumns[idx].width
}
}
return {
...column,
width,
}
})
}
function useColumns(columns: Ref<AnyColumn>, fixed: Ref<boolean>) {
let __columns: AnyColumn = []
const _columns = ref<AnyColumn>([])
const visibleColumns = computed(() => {
return unref(_columns).filter((column) => !column.hidden)
return unref(columns).filter((column) => !column.hidden)
})
const fixedColumnsOnLeft = computed(() =>
@@ -106,9 +82,9 @@ function useColumns(columns: Ref<AnyColumn>, fixed: Ref<boolean>) {
})
const columnsStyles = computed(() => {
const columns = unref(_columns)
const _columns = unref(columns)
return columns.reduce<Record<Column<any>['key'], CSSProperties>>(
return _columns.reduce<Record<Column<any>['key'], CSSProperties>>(
(style, column) => {
style[column.key] = calcColumnStyle(column, unref(fixed))
return style
@@ -124,14 +100,8 @@ function useColumns(columns: Ref<AnyColumn>, fixed: Ref<boolean>) {
)
})
watchEffect(() => {
const mappedColumns = mapColumns(unref(columns), __columns)
_columns.value = mappedColumns
__columns = mappedColumns
})
const getColumn = (key: KeyType) => {
return unref(_columns).find((column) => column.key === key)
return unref(columns).find((column) => column.key === key)
}
const getColumnStyle = (key: KeyType) => {
@@ -143,7 +113,7 @@ function useColumns(columns: Ref<AnyColumn>, fixed: Ref<boolean>) {
}
return {
columns: _columns,
columns,
columnsStyles,
columnsTotalWidth,
fixedColumnsOnLeft,

View File

@@ -1,6 +1,7 @@
import { h, isVNode } from 'vue'
import { addUnit, isArray, isFunction } from '@element-plus/utils'
import type { CSSProperties } from 'vue'
import type { CSSProperties, Component, Slot } from 'vue'
const sumReducer = (sum: number, num: number) => sum + num
@@ -23,3 +24,10 @@ export const enforceUnit = (style: CSSProperties) => {
return style
}
export const componentToSlot = <T>(
ComponentLike: JSX.Element | ((props: T) => Component<T>) | undefined
) =>
isVNode(ComponentLike)
? (props: T) => h(ComponentLike, props)
: (ComponentLike as Slot)