refactor(components): [table] remove @ts-nocheck (#21200)

* refactor(components): [table] remove `@ts-nocheck`

* chore: remove any wip

* chore: better types
This commit is contained in:
Noblet Ouways
2025-07-02 05:28:11 +02:00
committed by GitHub
parent 6c3967c546
commit 164a8a5ee5
15 changed files with 204 additions and 151 deletions

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
import { h, inject, ref } from 'vue'
import { debounce } from 'lodash-unified'
import { addClass, hasClass, removeClass } from '@element-plus/utils'
@@ -13,24 +12,25 @@ import { TABLE_INJECTION_KEY } from '../tokens'
import type { TableColumnCtx } from '../table-column/defaults'
import type { TableBodyProps } from './defaults'
import type { TableOverflowTooltipOptions } from '../util'
import type { DefaultRow } from '../table/defaults'
function isGreaterThan(a: number, b: number, epsilon = 0.03) {
return a - b > epsilon
}
function useEvents<T>(props: Partial<TableBodyProps<T>>) {
function useEvents<T extends DefaultRow>(props: Partial<TableBodyProps<T>>) {
const parent = inject(TABLE_INJECTION_KEY)
const tooltipContent = ref('')
const tooltipTrigger = ref(h('div'))
const handleEvent = (event: Event, row: T, name: string) => {
const table = parent
const cell = getCell(event)
let column: TableColumnCtx<T>
let column: TableColumnCtx<T> | null = null
const namespace = table?.vnode.el?.dataset.prefix
if (cell) {
column = getColumnByCell(
{
columns: props.store.states.columns.value,
columns: props.store?.states.columns.value ?? [],
},
cell,
namespace
@@ -45,17 +45,17 @@ function useEvents<T>(props: Partial<TableBodyProps<T>>) {
handleEvent(event, row, 'dblclick')
}
const handleClick = (event: Event, row: T) => {
props.store.commit('setCurrentRow', row)
props.store?.commit('setCurrentRow', row)
handleEvent(event, row, 'click')
}
const handleContextMenu = (event: Event, row: T) => {
handleEvent(event, row, 'contextmenu')
}
const handleMouseEnter = debounce((index: number) => {
props.store.commit('setHoverRow', index)
props.store?.commit('setHoverRow', index)
}, 30)
const handleMouseLeave = debounce(() => {
props.store.commit('setHoverRow', null)
props.store?.commit('setHoverRow', null)
}, 30)
const getPadding = (el: HTMLElement) => {
const style = window.getComputedStyle(el, null)
@@ -76,11 +76,12 @@ function useEvents<T>(props: Partial<TableBodyProps<T>>) {
event: MouseEvent,
toggle: (el: Element, cls: string) => void
) => {
let node = event.target.parentNode
let node: Node | null | undefined = (event?.target as Element | null)
?.parentNode
while (rowSpan > 1) {
node = node?.nextSibling
if (!node || node.nodeName !== 'TR') break
toggle(node, 'hover-row hover-fixed-row')
toggle(node as Element, 'hover-row hover-fixed-row')
rowSpan--
}
}
@@ -90,14 +91,15 @@ function useEvents<T>(props: Partial<TableBodyProps<T>>) {
row: T,
tooltipOptions: TableOverflowTooltipOptions
) => {
if (!parent) return
const table = parent
const cell = getCell(event)
const namespace = table?.vnode.el?.dataset.prefix
let column: TableColumnCtx<T>
let column: TableColumnCtx<T> | null = null
if (cell) {
column = getColumnByCell(
{
columns: props.store.states.columns.value,
columns: props.store?.states.columns.value ?? [],
},
cell,
namespace
@@ -108,7 +110,11 @@ function useEvents<T>(props: Partial<TableBodyProps<T>>) {
if (cell.rowSpan > 1) {
toggleRowClassByCell(cell.rowSpan, event, addClass)
}
const hoverState = (table.hoverState = { cell, column, row })
const hoverState = (table.hoverState = {
cell,
column: column as any,
row,
})
table?.emit(
'cell-mouse-enter',
hoverState.row,
@@ -162,7 +168,7 @@ function useEvents<T>(props: Partial<TableBodyProps<T>>) {
) {
createTablePopper(
tooltipOptions,
cell.innerText || cell.textContent,
(cell?.innerText || cell?.textContent) ?? '',
row,
column,
cell,
@@ -172,7 +178,7 @@ function useEvents<T>(props: Partial<TableBodyProps<T>>) {
removePopper?.()
}
}
const handleCellMouseLeave = (event) => {
const handleCellMouseLeave = (event: MouseEvent) => {
const cell = getCell(event)
if (!cell) return
if (cell.rowSpan > 1) {

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
import {
defineComponent,
getCurrentInstance,
@@ -28,8 +27,8 @@ export default defineComponent({
useRender(props)
const { onColumnsChange, onScrollableChange } = useLayoutObserver(parent!)
const hoveredCellList = []
watch(props.store.states.hoverRow, (newVal: any, oldVal: any) => {
const hoveredCellList: HTMLTableCellElement[] = []
watch(props.store?.states.hoverRow, (newVal: any, oldVal: any) => {
const el = instance?.vnode.el as HTMLElement
const rows = Array.from(el?.children || []).filter((e) =>
e?.classList.contains(`${ns.e('row')}`)
@@ -37,7 +36,8 @@ export default defineComponent({
// hover rowSpan > 1 choose the whole row
let rowNum = newVal
const childNodes = rows[rowNum]?.childNodes
const childNodes = rows[rowNum]
?.childNodes as NodeListOf<HTMLTableCellElement>
if (childNodes?.length) {
let control = 0
const indexes = Array.from(childNodes).reduce((acc, item, index) => {
@@ -50,13 +50,15 @@ export default defineComponent({
}
control > 0 && control--
return acc
}, [])
}, [] as number[])
indexes.forEach((rowIndex) => {
rowNum = newVal
while (rowNum > 0) {
// find from previous
const preChildNodes = rows[rowNum - 1]?.childNodes
const preChildNodes = rows[rowNum - 1]
?.childNodes as NodeListOf<HTMLTableCellElement>
if (
preChildNodes[rowIndex] &&
preChildNodes[rowIndex].nodeName === 'TD' &&
@@ -73,7 +75,7 @@ export default defineComponent({
hoveredCellList.forEach((item) => removeClass(item, 'hover-cell'))
hoveredCellList.length = 0
}
if (!props.store.states.isComplex.value || !isClient) return
if (!props.store?.states.isComplex.value || !isClient) return
rAF(() => {
// just get first level children; fix #9723
@@ -104,7 +106,7 @@ export default defineComponent({
},
render() {
const { wrappedRowRender, store } = this
const data = store.states.data.value || []
const data = store?.states.data.value || []
// Why do we need tabIndex: -1 ?
// If you set the tabindex attribute on an element ,
// then its child content cannot be scrolled with the arrow keys,
@@ -112,7 +114,7 @@ export default defineComponent({
// See https://github.com/facebook/react/issues/25462#issuecomment-1274775248 or https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes/tabindex
return h('tbody', { tabIndex: -1 }, [
data.reduce((acc: VNode[], row) => {
return acc.concat(wrappedRowRender(row, acc.length))
return acc.concat(wrappedRowRender(row, acc.length) as VNode[])
}, []),
])
},

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
import { computed, h, inject } from 'vue'
import { merge } from 'lodash-unified'
import { useNamespace } from '@element-plus/hooks'
@@ -10,10 +9,19 @@ import useStyles from './styles-helper'
import TdWrapper from './td-wrapper.vue'
import type { TableBodyProps } from './defaults'
import type { RenderRowData, TableProps, TreeNode } from '../table/defaults'
import type {
DefaultRow,
RenderRowData,
Table,
TableColumnCtx,
TableProps,
TreeNode,
} from '../table/defaults'
import type { TreeData } from '../store/tree'
import type { TableOverflowTooltipOptions } from '../util'
function useRender<T>(props: Partial<TableBodyProps<T>>) {
const parent = inject(TABLE_INJECTION_KEY)
function useRender<T extends DefaultRow>(props: Partial<TableBodyProps<T>>) {
const parent = inject(TABLE_INJECTION_KEY) as Table<T>
const ns = useNamespace('table')
const {
handleDoubleClick,
@@ -35,12 +43,12 @@ function useRender<T>(props: Partial<TableBodyProps<T>>) {
getColspanRealWidth,
} = useStyles(props)
const firstDefaultColumnIndex = computed(() => {
return props.store.states.columns.value.findIndex(
return props.store?.states.columns.value.findIndex(
({ type }) => type === 'default'
)
})
const getKeyOfRow = (row: T, index: number) => {
const rowKey = (parent.props as Partial<TableProps<T>>).rowKey
const rowKey = (parent?.props as Partial<TableProps<T>>)?.rowKey
if (rowKey) {
return getRowIdentity(row, rowKey)
}
@@ -53,12 +61,12 @@ function useRender<T>(props: Partial<TableBodyProps<T>>) {
expanded = false
) => {
const { tooltipEffect, tooltipOptions, store } = props
const { indent, columns } = store.states
const { indent, columns } = store!.states
const rowClasses = getRowClass(row, $index)
let display = true
if (treeRowData) {
rowClasses.push(ns.em('row', `level-${treeRowData.level}`))
display = treeRowData.display
display = !!treeRowData.display
}
const displayStyle = display ? null : { display: 'none' }
return h(
@@ -67,9 +75,9 @@ function useRender<T>(props: Partial<TableBodyProps<T>>) {
style: [displayStyle, getRowStyle(row, $index)],
class: rowClasses,
key: getKeyOfRow(row, $index),
onDblclick: ($event) => handleDoubleClick($event, row),
onClick: ($event) => handleClick($event, row),
onContextmenu: ($event) => handleContextMenu($event, row),
onDblclick: ($event: Event) => handleDoubleClick($event, row),
onClick: ($event: Event) => handleClick($event, row),
onContextmenu: ($event: Event) => handleContextMenu($event, row),
onMouseenter: () => handleMouseEnter($index),
onMouseleave: handleMouseLeave,
},
@@ -85,8 +93,8 @@ function useRender<T>(props: Partial<TableBodyProps<T>>) {
cellIndex
)
const data: RenderRowData<T> = {
store: props.store,
_self: props.context || parent,
store: store!,
_self: props.context || parent!,
column: columnData,
row,
$index,
@@ -95,7 +103,7 @@ function useRender<T>(props: Partial<TableBodyProps<T>>) {
}
if (cellIndex === firstDefaultColumnIndex.value && treeRowData) {
data.treeNode = {
indent: treeRowData.level * indent.value,
indent: treeRowData.level && treeRowData.level * indent.value,
level: treeRowData.level,
}
if (isBoolean(treeRowData.expanded)) {
@@ -128,8 +136,12 @@ function useRender<T>(props: Partial<TableBodyProps<T>>) {
key: `${patchKey}${baseKey}`,
rowspan,
colspan,
onMouseenter: ($event) =>
handleCellMouseEnter($event, row, mergedTooltipOptions),
onMouseenter: ($event: MouseEvent) =>
handleCellMouseEnter(
$event,
row,
mergedTooltipOptions as TableOverflowTooltipOptions
),
onMouseleave: handleCellMouseLeave,
},
{
@@ -139,12 +151,16 @@ function useRender<T>(props: Partial<TableBodyProps<T>>) {
})
)
}
const cellChildren = (cellIndex, column, data) => {
const cellChildren = <T extends DefaultRow>(
_cellIndex: number,
column: TableColumnCtx<T>,
data: RenderRowData<T>
) => {
return column.renderCell(data)
}
const wrappedRowRender = (row: T, $index: number) => {
const store = props.store
const store = props.store!
const { isRowExpanded, assertRowKey } = store
const { treeData, lazyTreeNodeMap, childrenColumnName, rowKey } =
store.states
@@ -153,7 +169,7 @@ function useRender<T>(props: Partial<TableBodyProps<T>>) {
if (hasExpandColumn) {
const expanded = isRowExpanded(row)
const tr = rowRender(row, $index, undefined, expanded)
const renderExpanded = parent.renderExpanded
const renderExpanded = parent?.renderExpanded
if (!renderExpanded) {
console.error('[Element Error]renderExpanded is required.')
return tr
@@ -200,26 +216,28 @@ function useRender<T>(props: Partial<TableBodyProps<T>>) {
expanded: cur.expanded,
level: cur.level,
display: true,
noLazyChildren: undefined as boolean | undefined,
loading: undefined as boolean | undefined,
}
if (isBoolean(cur.lazy)) {
if (isBoolean(cur.loaded) && cur.loaded) {
if (treeRowData && isBoolean(cur.loaded) && cur.loaded) {
treeRowData.noLazyChildren = !(cur.children && cur.children.length)
}
treeRowData.loading = cur.loading
}
}
const tmp = [rowRender(row, $index, treeRowData)]
const tmp = [rowRender(row, $index, treeRowData ?? undefined)]
// 渲染嵌套数据
if (cur) {
// currentRow 记录的是 index所以还需主动增加 TreeTable 的 index
let i = 0
const traverse = (children, parent) => {
const traverse = (children: T[], parent: TreeData) => {
if (!(children && children.length && parent)) return
children.forEach((node) => {
// 父节点的 display 状态影响子节点的显示状态
const innerTreeRowData = {
const innerTreeRowData: Partial<Record<string, any>> = {
display: parent.display && parent.expanded,
level: parent.level + 1,
level: parent.level! + 1,
expanded: false,
noLazyChildren: false,
loading: false,

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
import { inject } from 'vue'
import { useNamespace } from '@element-plus/hooks'
import { isArray, isFunction, isObject, isString } from '@element-plus/utils'
@@ -11,9 +10,10 @@ import { TABLE_INJECTION_KEY } from '../tokens'
import type { TableColumnCtx } from '../table-column/defaults'
import type { TableBodyProps } from './defaults'
import type { DefaultRow, Table } from '../table/defaults'
function useStyles<T>(props: Partial<TableBodyProps<T>>) {
const parent = inject(TABLE_INJECTION_KEY)
function useStyles<T extends DefaultRow>(props: Partial<TableBodyProps<T>>) {
const parent = inject(TABLE_INJECTION_KEY) as Table<T>
const ns = useNamespace('table')
const getRowStyle = (row: T, rowIndex: number) => {
@@ -31,7 +31,7 @@ function useStyles<T>(props: Partial<TableBodyProps<T>>) {
const classes = [ns.e('row')]
if (
parent?.props.highlightCurrentRow &&
row === props.store.states.currentRow.value
row === props.store?.states.currentRow.value
) {
classes.push('current-row')
}
@@ -143,7 +143,7 @@ function useStyles<T>(props: Partial<TableBodyProps<T>>) {
index: number
): number => {
if (colspan < 1) {
return columns[index].realWidth
return columns[index].realWidth!
}
const widthArr = columns
.map(({ realWidth, width }) => realWidth || width)

View File

@@ -65,7 +65,7 @@ type TableColumnCtx<T extends DefaultRow = DefaultRow> = {
filterClassName: string
index: number | ((index: number) => number)
sortOrders: (TableSortOrder | null)[]
renderCell: (data: any) => void
renderCell: (data: any) => VNode | VNode[]
colSpan: number
rowSpan: number
children?: TableColumnCtx<T>[]

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
import {
Fragment,
computed,
@@ -18,11 +17,13 @@ import useWatcher from './watcher-helper'
import useRender from './render-helper'
import defaultProps from './defaults'
import type { VNode } from 'vue'
import type { TableColumn, TableColumnCtx } from './defaults'
import type { DefaultRow } from '../table/defaults'
let columnIdSeed = 1
//TODO: when vue 3.3 we can set this component a generic: https://github.com/vuejs/core/pull/7963
export default defineComponent({
name: 'ElTableColumn',
components: {
@@ -56,16 +57,17 @@ export default defineComponent({
getColumnElIndex,
realAlign,
updateColumnOrder,
} = useRender(props as unknown as TableColumnCtx<unknown>, slots, owner)
} = useRender(props as unknown as TableColumnCtx<DefaultRow>, slots, owner)
const parent = columnOrTableParent.value
columnId.value = `${
parent.tableId || parent.columnId
('tableId' in parent && parent.tableId) ||
('columnId' in parent && parent.columnId)
}_column_${columnIdSeed++}`
onBeforeMount(() => {
isSubColumn.value = owner.value !== parent
const type = props.type || 'default'
const type = (props.type as keyof typeof cellStarts) || 'default'
const sortable = props.sortable === '' ? true : props.sortable
//The selection column should not be affected by `showOverflowTooltip`.
const showOverflowTooltip =
@@ -134,7 +136,7 @@ export default defineComponent({
setColumnWidth,
setColumnForcedProps
)
column = chains(column)
column = chains(column) as unknown as TableColumnCtx<DefaultRow>
columnConfig.value = column
// 注册 watcher
@@ -144,7 +146,7 @@ export default defineComponent({
onMounted(() => {
const parent = columnOrTableParent.value
const children = isSubColumn.value
? parent.vnode.el.children
? parent.vnode.el?.children
: parent.refs.hiddenColumns?.children
const getColumnIndex = () =>
getColumnElIndex(children || [], instance.vnode.el)
@@ -154,7 +156,9 @@ export default defineComponent({
owner.value.store.commit(
'insertColumn',
columnConfig.value,
isSubColumn.value ? parent.columnConfig.value : null,
isSubColumn.value
? 'columnConfig' in parent && parent.columnConfig.value
: null,
updateColumnOrder
)
})
@@ -165,13 +169,15 @@ export default defineComponent({
owner.value.store.commit(
'removeColumn',
columnConfig.value,
isSubColumn.value ? parent.columnConfig.value : null,
isSubColumn.value
? 'columnConfig' in parent && parent.columnConfig.value
: null,
updateColumnOrder
)
})
instance.columnId = columnId.value
instance.columnConfig = columnConfig
instance.columnConfig = columnConfig as any
return
},
render() {
@@ -185,7 +191,7 @@ export default defineComponent({
if (isArray(renderDefault)) {
for (const childNode of renderDefault) {
if (
childNode.type?.name === 'ElTableColumn' ||
(childNode.type as any)?.name === 'ElTableColumn' ||
childNode.shapeFlag & 2
) {
children.push(childNode)
@@ -195,7 +201,10 @@ export default defineComponent({
) {
childNode.children.forEach((vnode) => {
// No rendering when vnode is dynamic slot or text
if (vnode?.patchFlag !== 1024 && !isString(vnode?.children)) {
if (
(vnode as VNode)?.patchFlag !== 1024 &&
!isString((vnode as VNode)?.children)
) {
children.push(vnode)
}
})

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
import {
Comment,
computed,
@@ -19,19 +18,20 @@ import {
} from '../config'
import { parseMinWidth, parseWidth } from '../util'
import type { ComputedRef } from 'vue'
import type { ComputedRef, RendererNode, Slots, VNode } from 'vue'
import type { TableColumn, TableColumnCtx } from './defaults'
import type { DefaultRow, Table } from '../table/defaults'
function useRender<T>(
function useRender<T extends DefaultRow>(
props: TableColumnCtx<T>,
slots,
owner: ComputedRef<any>
slots: Slots,
owner: ComputedRef<Table<T>>
) {
const instance = getCurrentInstance() as TableColumn<T>
const columnId = ref('')
const isSubColumn = ref(false)
const realAlign = ref<string>()
const realHeaderAlign = ref<string>()
const realAlign = ref<string | null>()
const realHeaderAlign = ref<string | null | undefined>()
const ns = useNamespace('table')
watchEffect(() => {
realAlign.value = props.align ? `is-${props.align}` : null
@@ -45,7 +45,7 @@ function useRender<T>(
// nextline help render
realHeaderAlign.value
})
const columnOrTableParent = computed(() => {
const columnOrTableParent = computed<Table<T> | TableColumn<T>>(() => {
let parent: any = instance.vnode.vParent || instance.parent
while (parent && !parent.tableId && !parent.columnId) {
parent = parent.vnode.vParent || parent.parent
@@ -53,7 +53,7 @@ function useRender<T>(
return parent
})
const hasTreeColumn = computed<boolean>(() => {
const { store } = instance.parent
const { store } = (instance.parent as Table<T>)!
if (!store) return false
const { treeData } = store.states
const treeDataValue = treeData.value
@@ -81,11 +81,11 @@ function useRender<T>(
const setColumnForcedProps = (column: TableColumnCtx<T>) => {
// 对于特定类型的 column某些属性不允许设置
const type = column.type
const source = cellForced[type] || {}
const source = cellForced[type as keyof typeof cellForced] || {}
Object.keys(source).forEach((prop) => {
const value = source[prop]
const value = source[prop as keyof typeof source]
if (prop !== 'className' && !isUndefined(value)) {
column[prop] = value
;(column as any)[prop] = value
}
})
const className = getDefaultClassName(type)
@@ -98,13 +98,13 @@ function useRender<T>(
return column
}
const checkSubColumn = (children: TableColumn<T> | TableColumn<T>[]) => {
const checkSubColumn = (children: VNode | VNode[]) => {
if (isArray(children)) {
children.forEach((child) => check(child))
} else {
check(children)
}
function check(item: TableColumn<T>) {
function check(item: any) {
if (item?.type?.name === 'ElTableColumn') {
item.vParent = instance
}
@@ -112,6 +112,7 @@ function useRender<T>(
}
const setColumnRenders = (column: TableColumnCtx<T>) => {
// renderHeader 属性不推荐使用。
//@ts-expect-error
if (props.renderHeader) {
debugWarn(
'TableColumn',
@@ -149,14 +150,14 @@ function useRender<T>(
},
[originRenderCell(data)]
)
owner.value.renderExpanded = (data) => {
return slots.default ? slots.default(data) : slots.default
owner.value.renderExpanded = (row) => {
return slots.default ? slots.default(row) : slots.default
}
} else {
originRenderCell = originRenderCell || defaultRenderCell
// 对 renderCell 进行包装
column.renderCell = (data) => {
let children = null
let children: VNode | VNode[] | null = null
if (slots.default) {
const vnodes = slots.default(data)
children = vnodes.some((v) => v.type !== Comment)
@@ -191,17 +192,17 @@ function useRender<T>(
}
return column
}
const getPropsData = (...propsKey: unknown[]) => {
const getPropsData = (...propsKey: string[][]) => {
return propsKey.reduce((prev, cur) => {
if (isArray(cur)) {
cur.forEach((key) => {
prev[key] = props[key]
prev[key] = props[key as keyof TableColumnCtx<T>]
})
}
return prev
}, {})
}, {} as Record<string, any>)
}
const getColumnElIndex = (children, child) => {
const getColumnElIndex = (children: T[], child: RendererNode | null) => {
return Array.prototype.indexOf.call(children, child)
}

View File

@@ -1,25 +1,25 @@
// @ts-nocheck
import { getCurrentInstance, watch } from 'vue'
import { hasOwn } from '@element-plus/utils'
import { parseMinWidth, parseWidth } from '../util'
import type { ComputedRef } from 'vue'
import type { DefaultRow } from '../table/defaults'
import type { TableColumn, TableColumnCtx, ValueOf } from './defaults'
function getAllAliases(props, aliases) {
function getAllAliases(props: string[], aliases: Record<string, string>) {
return props.reduce((prev, cur) => {
prev[cur] = cur
prev[cur as keyof typeof prev] = cur
return prev
}, aliases)
}
function useWatcher<T>(
function useWatcher<T extends DefaultRow>(
owner: ComputedRef<any>,
props_: Partial<TableColumnCtx<T>>
) {
const instance = getCurrentInstance() as TableColumn<T>
const registerComplexWatchers = () => {
const props = ['fixed']
const aliases = {
const aliases: Record<string, string> = {
realWidth: 'width',
realMinWidth: 'minWidth',
}
@@ -37,8 +37,8 @@ function useWatcher<T>(
if (columnKey === 'minWidth' && key === 'realMinWidth') {
value = parseMinWidth(newVal)
}
instance.columnConfig.value[columnKey as any] = value
instance.columnConfig.value[key] = value
instance.columnConfig.value[columnKey as never] = value as never
instance.columnConfig.value[key as never] = value as never
const updateColumns = columnKey === 'fixed'
owner.value.store.scheduleLayout(updateColumns)
}
@@ -61,7 +61,7 @@ function useWatcher<T>(
'showOverflowTooltip',
'tooltipFormatter',
]
const aliases = {
const aliases: Record<string, string> = {
property: 'prop',
align: 'realAlign',
headerAlign: 'realHeaderAlign',
@@ -73,7 +73,7 @@ function useWatcher<T>(
watch(
() => props_[columnKey],
(newVal) => {
instance.columnConfig.value[key] = newVal
instance.columnConfig.value[key as never] = newVal
}
)
}

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
import { defineComponent, h, inject } from 'vue'
import { useNamespace } from '@element-plus/hooks'
import useLayoutObserver from '../layout-observer'
@@ -6,10 +5,10 @@ import { TABLE_INJECTION_KEY } from '../tokens'
import useStyle from './style-helper'
import type { Store } from '../store'
import type { PropType } from 'vue'
import type { DefaultRow, Sort, SummaryMethod } from '../table/defaults'
import type { PropType, VNode } from 'vue'
import type { DefaultRow, Sort, SummaryMethod, Table } from '../table/defaults'
export interface TableFooter<T> {
export interface TableFooter<T extends DefaultRow> {
fixed: string
store: Store<T>
summaryMethod: SummaryMethod<T>
@@ -28,15 +27,13 @@ export default defineComponent({
},
store: {
required: true,
type: Object as PropType<TableFooter<DefaultRow>['store']>,
type: Object as PropType<TableFooter<any>['store']>,
},
summaryMethod: Function as PropType<
TableFooter<DefaultRow>['summaryMethod']
>,
summaryMethod: Function as PropType<TableFooter<any>['summaryMethod']>,
sumText: String,
border: Boolean,
defaultSort: {
type: Object as PropType<TableFooter<DefaultRow>['defaultSort']>,
type: Object as PropType<TableFooter<any>['defaultSort']>,
default: () => {
return {
prop: '',
@@ -46,10 +43,10 @@ export default defineComponent({
},
},
setup(props) {
const parent = inject(TABLE_INJECTION_KEY)
const parent = inject(TABLE_INJECTION_KEY) as Table<any>
const ns = useNamespace('table')
const { getCellClasses, getCellStyles, columns } = useStyle(
props as TableFooter<DefaultRow>
props as TableFooter<any>
)
const { onScrollableChange, onColumnsChange } = useLayoutObserver(parent!)
@@ -66,7 +63,7 @@ export default defineComponent({
const { columns, getCellStyles, getCellClasses, summaryMethod, sumText } =
this
const data = this.store.states.data.value
let sums = []
let sums: (string | VNode | number | undefined)[] = []
if (summaryMethod) {
sums = summaryMethod({
columns,
@@ -79,7 +76,7 @@ export default defineComponent({
return
}
const values = data.map((item) => Number(item[column.property]))
const precisions = []
const precisions: number[] = []
let notNumber = true
values.forEach((value) => {
if (!Number.isNaN(+value)) {

View File

@@ -26,7 +26,7 @@ function useMapState() {
columnsCount,
leftFixedCount,
rightFixedCount,
columns: store?.states.columns ?? [],
columns: computed(() => store?.states.columns.value ?? []),
}
}

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
import { getCurrentInstance, inject, ref } from 'vue'
import { isNull } from 'lodash-unified'
import {
@@ -10,10 +9,15 @@ import {
} from '@element-plus/utils'
import { TABLE_INJECTION_KEY } from '../tokens'
import type { EmitFn } from '@element-plus/utils'
import type { TableHeaderProps } from '.'
import type { TableColumnCtx } from '../table-column/defaults'
import type { DefaultRow, TableSortOrder } from '../table/defaults'
function useEvent<T>(props: TableHeaderProps<T>, emit) {
function useEvent<T extends DefaultRow>(
props: TableHeaderProps<T>,
emit: EmitFn<string[]>
) {
const instance = getCurrentInstance()
const parent = inject(TABLE_INJECTION_KEY)
const handleFilterClick = (event: Event) => {
@@ -33,9 +37,14 @@ function useEvent<T>(props: TableHeaderProps<T>, emit) {
const handleHeaderContextMenu = (event: Event, column: TableColumnCtx<T>) => {
parent?.emit('header-contextmenu', column, event)
}
const draggingColumn = ref(null)
const draggingColumn = ref<TableColumnCtx<T> | null>(null)
const dragging = ref(false)
const dragState = ref({})
const dragState = ref<{
startMouseLeft: number
startLeft: number
startColumnLeft: number
tableLeft: number
}>()
const handleMouseDown = (event: MouseEvent, column: TableColumnCtx<T>) => {
if (!isClient) return
if (column.children && column.children.length > 0) return
@@ -46,8 +55,8 @@ function useEvent<T>(props: TableHeaderProps<T>, emit) {
const table = parent
emit('set-drag-visible', true)
const tableEl = table?.vnode.el
const tableLeft = tableEl.getBoundingClientRect().left
const columnEl = instance.vnode.el.querySelector(`th.${column.id}`)
const tableLeft = tableEl?.getBoundingClientRect().left
const columnEl = instance?.vnode?.el?.querySelector(`th.${column.id}`)
const columnRect = columnEl.getBoundingClientRect()
const minLeft = columnRect.left - tableLeft + 30
@@ -96,7 +105,7 @@ function useEvent<T>(props: TableHeaderProps<T>, emit) {
document.body.style.cursor = ''
dragging.value = false
draggingColumn.value = null
dragState.value = {}
dragState.value = undefined
emit('set-drag-visible', false)
}
@@ -136,7 +145,7 @@ function useEvent<T>(props: TableHeaderProps<T>, emit) {
if (hasClass(target, 'is-sortable')) {
target.style.cursor = 'col-resize'
}
draggingColumn.value = column
draggingColumn.value = column as any
} else if (!dragging.value) {
bodyStyle.cursor = ''
if (hasClass(target, 'is-sortable')) {
@@ -151,19 +160,20 @@ function useEvent<T>(props: TableHeaderProps<T>, emit) {
if (!isClient) return
document.body.style.cursor = ''
}
const toggleOrder = ({ order, sortOrders }) => {
if (order === '') return sortOrders[0]
const toggleOrder = ({ order, sortOrders }: TableColumnCtx<T>) => {
if ((order as string) === '') return sortOrders[0]
const index = sortOrders.indexOf(order || null)
return sortOrders[index > sortOrders.length - 2 ? 0 : index + 1]
}
const handleSortClick = (
event: Event,
column: TableColumnCtx<T>,
givenOrder: string | boolean
givenOrder?: TableSortOrder | boolean
) => {
event.stopPropagation()
const order =
const order = (
column.order === givenOrder ? null : givenOrder || toggleOrder(column)
) as TableSortOrder | null
const target = (event.target as HTMLElement)?.closest('th')
if (target) {
@@ -179,7 +189,9 @@ function useEvent<T>(props: TableHeaderProps<T>, emit) {
if (
['ascending', 'descending'].some(
(str) => hasClass(clickTarget, str) && !column.sortOrders.includes(str)
(str) =>
hasClass(clickTarget as Element, str) &&
!column.sortOrders.includes(str as TableSortOrder)
)
) {
return

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
import {
defineComponent,
getCurrentInstance,
@@ -15,6 +14,7 @@ import { useNamespace } from '@element-plus/hooks'
import FilterPanel from '../filter-panel.vue'
import useLayoutObserver from '../layout-observer'
import { TABLE_INJECTION_KEY } from '../tokens'
import TableLayout from '../table-layout'
import useEvent from './event-helper'
import useStyle from './style.helper'
import useUtils from './utils-helper'
@@ -25,12 +25,12 @@ import type { Store } from '../store'
export interface TableHeader extends ComponentInternalInstance {
state: {
onColumnsChange
onScrollableChange
onColumnsChange: (layout: TableLayout<any>) => void
onScrollableChange: (layout: TableLayout<any>) => void
}
filterPanels: Ref<DefaultRow>
}
export interface TableHeaderProps<T> {
export interface TableHeaderProps<T extends DefaultRow> {
fixed: string
store: Store<T>
border: boolean
@@ -50,11 +50,11 @@ export default defineComponent({
},
store: {
required: true,
type: Object as PropType<TableHeaderProps<DefaultRow>['store']>,
type: Object as PropType<TableHeaderProps<any>['store']>,
},
border: Boolean,
defaultSort: {
type: Object as PropType<TableHeaderProps<DefaultRow>['defaultSort']>,
type: Object as PropType<TableHeaderProps<any>['defaultSort']>,
default: () => {
return {
prop: '',
@@ -117,15 +117,15 @@ export default defineComponent({
handleMouseOut,
handleSortClick,
handleFilterClick,
} = useEvent(props as TableHeaderProps<unknown>, emit)
} = useEvent(props as TableHeaderProps<any>, emit)
const {
getHeaderRowStyle,
getHeaderRowClass,
getHeaderCellStyle,
getHeaderCellClass,
} = useStyle(props as TableHeaderProps<unknown>)
} = useStyle(props as TableHeaderProps<any>)
const { isGroup, toggleAllSelection, columnRows } = useUtils(
props as TableHeaderProps<unknown>
props as TableHeaderProps<any>
)
instance.state = {
@@ -220,16 +220,22 @@ export default defineComponent({
subColumns,
column
),
onClick: ($event) => {
if ($event.currentTarget.classList.contains('noclick')) {
onClick: ($event: Event) => {
if (
($event.currentTarget as Element)?.classList.contains(
'noclick'
)
) {
return
}
handleHeaderClick($event, column)
},
onContextmenu: ($event) =>
onContextmenu: ($event: MouseEvent) =>
handleHeaderContextMenu($event, column),
onMousedown: ($event) => handleMouseDown($event, column),
onMousemove: ($event) => handleMouseMove($event, column),
onMousedown: ($event: MouseEvent) =>
handleMouseDown($event, column),
onMousemove: ($event: MouseEvent) =>
handleMouseMove($event, column),
onMouseout: handleMouseOut,
},
[
@@ -256,17 +262,18 @@ export default defineComponent({
h(
'span',
{
onClick: ($event) => handleSortClick($event, column),
onClick: ($event: Event) =>
handleSortClick($event, column),
class: 'caret-wrapper',
},
[
h('i', {
onClick: ($event) =>
onClick: ($event: Event) =>
handleSortClick($event, column, 'ascending'),
class: 'sort-caret ascending',
}),
h('i', {
onClick: ($event) =>
onClick: ($event: Event) =>
handleSortClick($event, column, 'descending'),
class: 'sort-caret descending',
}),
@@ -274,13 +281,13 @@ export default defineComponent({
),
column.filterable &&
h(
FilterPanel,
FilterPanel as any,
{
store,
placement: column.filterPlacement || 'bottom-start',
appendTo: $parent.appendFilterPanelTo,
appendTo: ($parent as any)?.appendFilterPanelTo,
column,
upDataColumn: (key, value) => {
upDataColumn: (key: never, value: never) => {
column[key] = value
},
},

View File

@@ -1,11 +1,11 @@
// @ts-nocheck
import { computed, inject } from 'vue'
import { TABLE_INJECTION_KEY } from '../tokens'
import type { DefaultRow } from '../table/defaults'
import type { TableColumnCtx } from '../table-column/defaults'
import type { TableHeaderProps } from '.'
const getAllColumns = <T>(
const getAllColumns = <T extends DefaultRow>(
columns: TableColumnCtx<T>[]
): TableColumnCtx<T>[] => {
const result: TableColumnCtx<T>[] = []
@@ -21,11 +21,11 @@ const getAllColumns = <T>(
return result
}
export const convertToRows = <T>(
export const convertToRows = <T extends DefaultRow>(
originColumns: TableColumnCtx<T>[]
): TableColumnCtx<T>[] => {
): TableColumnCtx<T>[][] => {
let maxLevel = 1
const traverse = (column: TableColumnCtx<T>, parent: TableColumnCtx<T>) => {
const traverse = (column: TableColumnCtx<T>, parent?: TableColumnCtx<T>) => {
if (parent) {
column.level = parent.level + 1
if (maxLevel < column.level) {
@@ -49,7 +49,7 @@ export const convertToRows = <T>(
traverse(column, undefined)
})
const rows = []
const rows: TableColumnCtx<T>[][] = []
for (let i = 0; i < maxLevel; i++) {
rows.push([])
}
@@ -69,7 +69,7 @@ export const convertToRows = <T>(
return rows
}
function useUtils<T>(props: TableHeaderProps<T>) {
function useUtils<T extends DefaultRow>(props: TableHeaderProps<T>) {
const parent = inject(TABLE_INJECTION_KEY)
const columnRows = computed(() => {
return convertToRows(props.store.states.originColumns.value)

View File

@@ -64,7 +64,7 @@ type RenderExpanded<T extends DefaultRow> = ({
$index,
store,
expanded,
}: RIS<T>) => VNode
}: RIS<T>) => VNode[] | undefined
type SummaryMethod<T extends DefaultRow> = (data: {
columns: TableColumnCtx<T>[]
@@ -443,4 +443,5 @@ export type {
TreeProps,
TableTooltipData,
TableSortOrder,
RenderExpanded,
}

View File

@@ -10,7 +10,7 @@ import {
import { useEventListener, useResizeObserver } from '@vueuse/core'
import { useFormSize } from '@element-plus/components/form'
import type { DefaultRow, Table, TableProps } from './defaults'
import type { DefaultRow, RenderExpanded, Table, TableProps } from './defaults'
import type { Store } from '../store'
import type TableLayout from '../table-layout'
import type { TableColumnCtx } from '../table-column/defaults'
@@ -22,7 +22,7 @@ function useStyle<T extends DefaultRow>(
table: Table<T>
) {
const isHidden = ref(false)
const renderExpanded = ref(null)
const renderExpanded = ref<RenderExpanded<T> | null>(null)
const resizeProxyVisible = ref(false)
const setDragVisible = (visible: boolean) => {
resizeProxyVisible.value = visible