feat(components): [virtual-table] row (#7060)

- Implement TabelRow component
This commit is contained in:
JeremyWuuuuu 2022-04-08 17:57:36 +08:00 committed by GitHub
parent 4442ca31ec
commit 1aba790bdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 224 additions and 7 deletions

View File

@ -1,6 +1,7 @@
import { buildProps, definePropType } from '@element-plus/utils'
import { virtualizedGridProps } from '@element-plus/components/virtual-list'
import type { ExtractPropTypes, StyleValue } from 'vue'
import type { CSSProperties, ExtractPropTypes } from 'vue'
import type { Column, FixedDirection, KeyType, RowCommonParams } from './types'
export type RowExpandParams<T> = {
@ -10,6 +11,8 @@ export type RowExpandParams<T> = {
export type RowHoverParams<T> = {
event: MouseEvent
hovered: boolean
rowKey: KeyType
} & RowCommonParams<T>
export type RowEventHandlerParams<T> = {
@ -28,11 +31,11 @@ export type RowHeightChangeHandler = (
) => void
export type RowEventHandlers<T> = {
click?: RowEventHandler<T>
contextmenu?: RowEventHandler<T>
dblclick?: RowEventHandler<T>
mouseenter?: RowEventHandler<T>
mouseleave?: RowEventHandler<T>
onClick?: RowEventHandler<T>
onContextmenu?: RowEventHandler<T>
onDblclick?: RowEventHandler<T>
onMouseenter?: RowEventHandler<T>
onMouseleave?: RowEventHandler<T>
}
export const tableV2RowProps = buildProps({
@ -43,6 +46,7 @@ export const tableV2RowProps = buildProps({
},
depth: Number,
expandColumnKey: String,
estimatedRowHeight: virtualizedGridProps.estimatedRowHeight,
isScrolling: Boolean,
onRowExpand: {
type: definePropType<RowExpandHandler<any>>(Function),
@ -69,9 +73,10 @@ export const tableV2RowProps = buildProps({
*/
rowKey: {
type: definePropType<KeyType>([String, Number, Symbol]),
required: true,
},
style: {
type: definePropType<StyleValue>([String, Array, Object]),
type: definePropType<CSSProperties>(Object),
},
} as const)

View File

@ -0,0 +1,204 @@
import {
computed,
defineComponent,
inject,
nextTick,
onMounted,
ref,
unref,
} from 'vue'
import { isFunction } from '@element-plus/utils'
import { tableV2RowProps } from './row'
import { TableV2InjectionKey } from './tokens'
import type { CSSProperties, RendererElement, RendererNode, VNode } from 'vue'
import type { RowEventHandlers, TableV2RowProps } from './row'
type CustomizedCellsType = VNode<
RendererNode,
RendererElement,
{
[key: string]: any
}
>[]
type DefaultCellsType = VNode<
RendererNode,
RendererElement,
{
[key: string]: any
}
>[][]
type ColumnCellsType = DefaultCellsType | CustomizedCellsType
const useTableRow = (props: TableV2RowProps) => {
const { isScrolling } = inject(TableV2InjectionKey)!
const measured = ref(false)
const rowRef = ref<HTMLElement>()
const measurable = computed(() => {
return props.estimatedRowHeight && props.rowIndex >= 0
})
const doMeasure = (isInit = false) => {
const $rowRef = unref(rowRef)
if (!$rowRef) return
const { columns, onRowHeightChange, rowKey, rowIndex, style } = props
const { height } = $rowRef.getBoundingClientRect()
measured.value = true
nextTick(() => {
if (isInit || height !== (style as CSSProperties)?.height) {
const firstColumn = columns[0]
onRowHeightChange?.(
rowKey,
height,
rowIndex,
firstColumn && firstColumn.isPlaceholder && firstColumn.fixed
)
}
})
}
const eventHandlers = computed(() => {
const { rowData, rowIndex, rowKey, onRowHover } = props
const handlers = props.rowEventHandlers || ({} as RowEventHandlers<any>)
const eventHandlers = {}
Object.entries(handlers).forEach(([eventName, handler]) => {
if (isFunction(handler)) {
eventHandlers[eventName] = (event: Event) => {
handler({
event,
rowData,
rowIndex,
rowKey,
})
}
}
})
if (onRowHover) {
;(
[
{ name: 'onMouseleave', hovered: false },
{ name: 'onMouseenter', hovered: true },
] as const
).forEach(({ name, hovered }) => {
eventHandlers[name] = (event: MouseEvent) => {
onRowHover({
event,
hovered,
rowData,
rowIndex,
rowKey,
})
eventHandlers[name]?.(event)
}
})
}
return eventHandlers
})
const onExpand = (expanded: boolean) => {
const { onRowExpand, rowData, rowIndex, rowKey } = props
onRowExpand?.({
expanded,
rowData,
rowIndex,
rowKey,
})
}
onMounted(() => {
if (unref(measurable)) {
doMeasure()
}
})
return { isScrolling, measurable, measured, rowRef, eventHandlers, onExpand }
}
const TableV2Row = defineComponent({
name: '',
props: tableV2RowProps,
setup(props, { expose, slots }) {
const {
eventHandlers,
isScrolling,
measurable,
measured,
rowRef,
onExpand,
} = useTableRow(props)
expose({
/**
* @description manually dispatching expand action on row.
*/
onExpand,
})
return () => {
const { columns, expandColumnKey, depth, rowData, rowIndex, style } =
props
let ColumnCells: ColumnCellsType = columns.map((column, columnIndex) => {
return slots.cell!({
column,
columns,
columnIndex,
rowData,
rowIndex,
isScrolling: unref(isScrolling),
expandIconProps:
column.key === expandColumnKey
? {
depth,
rowData,
rowIndex,
onExpand,
}
: undefined,
})
})
if (slots.default) {
ColumnCells = slots.default({
cells: ColumnCells,
columns,
depth,
rowData,
rowIndex,
isScrolling: unref(isScrolling),
})
}
if (unref(measurable)) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { height, ...expectHeight } = style || {}
const _measured = unref(measured)
return (
<div
ref={rowRef}
class={props.class}
style={_measured ? style : expectHeight}
{...unref(eventHandlers)}
>
{ColumnCells}
</div>
)
}
return <div ref={rowRef}>{ColumnCells}</div>
}
},
})
export default TableV2Row

View File

@ -0,0 +1,8 @@
import type { InjectionKey, Ref } from 'vue'
export type TableV2Context = {
isScrolling: Ref<boolean>
}
export const TableV2InjectionKey: InjectionKey<TableV2Context> =
Symbol('tableV2')