mirror of
https://github.com/element-plus/element-plus.git
synced 2025-01-24 11:05:17 +08:00
feat(components): [virtual-table] row (#7060)
- Implement TabelRow component
This commit is contained in:
parent
4442ca31ec
commit
1aba790bdb
@ -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)
|
||||
|
||||
|
204
packages/components/table-v2/src/table-row.tsx
Normal file
204
packages/components/table-v2/src/table-row.tsx
Normal 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
|
8
packages/components/table-v2/src/tokens.ts
Normal file
8
packages/components/table-v2/src/tokens.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import type { InjectionKey, Ref } from 'vue'
|
||||
|
||||
export type TableV2Context = {
|
||||
isScrolling: Ref<boolean>
|
||||
}
|
||||
|
||||
export const TableV2InjectionKey: InjectionKey<TableV2Context> =
|
||||
Symbol('tableV2')
|
Loading…
Reference in New Issue
Block a user