From 7ef19333f03beb8e49f64b9887de446d313e8501 Mon Sep 17 00:00:00 2001 From: Artem Alesenko <62352758+Artles777@users.noreply.github.com> Date: Mon, 14 Jul 2025 13:18:17 +0300 Subject: [PATCH] feat(Table): add support for `colspan` and `rowspan` (#4460) Co-authored-by: Benjamin Canac --- src/runtime/components/Table.vue | 33 +++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/runtime/components/Table.vue b/src/runtime/components/Table.vue index 082a9878..a766c254 100644 --- a/src/runtime/components/Table.vue +++ b/src/runtime/components/Table.vue @@ -49,6 +49,13 @@ declare module '@tanstack/table-core' { th?: string | Record | ((cell: Header) => string | Record) td?: string | Record | ((cell: Cell) => string | Record) } + colspan?: { + td?: string | ((cell: Cell) => string) + } + rowspan?: { + td?: string | ((cell: Cell) => string) + } + } interface TableMeta { @@ -376,6 +383,14 @@ function onRowContextmenu(e: Event, row: TableRow) { } } +function resolveValue(prop: T | ((arg: A) => T), arg?: A): T | undefined { + if (typeof prop === 'function') { + // @ts-expect-error: TS can't know if prop is a function here + return prop(arg) + } + return prop +} + watch( () => props.data, () => { data.value = props.data ? [...props.data] : [] @@ -405,10 +420,11 @@ defineExpose({ :data-pinned="header.column.getIsPinned()" :scope="header.colSpan > 1 ? 'colgroup' : 'col'" :colspan="header.colSpan > 1 ? header.colSpan : undefined" + :rowspan="header.rowSpan > 1 ? header.rowSpan : undefined" :class="ui.th({ class: [ props.ui?.th, - typeof header.column.columnDef.meta?.class?.th === 'function' ? header.column.columnDef.meta.class.th(header) : header.column.columnDef.meta?.class?.th + resolveValue(header.column.columnDef.meta?.class?.th, header) ], pinned: !!header.column.getIsPinned() })" @@ -436,10 +452,10 @@ defineExpose({ :class="ui.tr({ class: [ props.ui?.tr, - typeof tableApi.options.meta?.class?.tr === 'function' ? tableApi.options.meta.class.tr(row) : tableApi.options.meta?.class?.tr + resolveValue(tableApi.options.meta?.class?.tr, row) ] })" - :style="typeof tableApi.options.meta?.style?.tr === 'function' ? tableApi.options.meta?.style?.tr(row) : tableApi.options.meta?.style?.tr" + :style="resolveValue(tableApi.options.meta?.style?.tr, row)" @click="onRowSelect($event, row)" @pointerenter="onRowHover($event, row)" @pointerleave="onRowHover($event, null)" @@ -449,14 +465,16 @@ defineExpose({ v-for="cell in row.getVisibleCells()" :key="cell.id" :data-pinned="cell.column.getIsPinned()" + :colspan="resolveValue(cell.column.columnDef.meta?.colspan?.td, cell)" + :rowspan="resolveValue(cell.column.columnDef.meta?.rowspan?.td, cell)" :class="ui.td({ class: [ props.ui?.td, - typeof cell.column.columnDef.meta?.class?.td === 'function' ? cell.column.columnDef.meta.class.td(cell) : cell.column.columnDef.meta?.class?.td + resolveValue(cell.column.columnDef.meta?.class?.td, cell) ], pinned: !!cell.column.getIsPinned() })" - :style="typeof cell.column.columnDef.meta?.style?.td === 'function' ? cell.column.columnDef.meta.style.td(cell) : cell.column.columnDef.meta?.style?.td" + :style="resolveValue(cell.column.columnDef.meta?.style?.td, cell)" > @@ -497,14 +515,15 @@ defineExpose({ :key="header.id" :data-pinned="header.column.getIsPinned()" :colspan="header.colSpan > 1 ? header.colSpan : undefined" + :rowspan="header.rowSpan > 1 ? header.rowSpan : undefined" :class="ui.th({ class: [ props.ui?.th, - typeof header.column.columnDef.meta?.class?.th === 'function' ? header.column.columnDef.meta.class.th(header) : header.column.columnDef.meta?.class?.th + resolveValue(header.column.columnDef.meta?.class?.th, header) ], pinned: !!header.column.getIsPinned() })" - :style="typeof header.column.columnDef.meta?.style?.th === 'function' ? header.column.columnDef.meta.style.th(header) : header.column.columnDef.meta?.style?.th" + :style="resolveValue(header.column.columnDef.meta?.style?.th, header)" >